Fun fact of the day: Ping

Pressing "Ctrl+\" while running ping will display the current statistics without quitting.

You may now return to your regularly scheduled programming...


The Design of Design

I recently finished reading The Design of Design: Essays from a Computer Scientist by Frederick P. Brooks, (better known for his classic text The Mythical Man-Month: Essays on Software Engineering).  I would highly recommend it for any computer science students, and even any other technical discipline that deals with design work.  While most of what he says about design isn't anything particularly new or sensational, he manages to cover the breadth of design wisdom and put it all into a coherent framework, with lot's of examples and stories to back it up.  It's also a fairly quick read.  My only complaint is that about half the book is an account of several iterations of him designing his family beach house.  That was boring enough that I skimmed or skipped most of it.  He used it mainly to illustrated some of his design principles in a way that was accessible to a general audience, and I'm sure it would be more useful to people who are hearing some of his design philosophy for the first time.  More useful, was his analysis of some of the early IBM computer architectures that he worked on, and some discussion on the constraints and trade-offs involved.

Another chunk of the book was devoted to discussing the design process as it interacts with an institutional framework such as a business.  Having recently started a full-time job as a computer programmer, I found this to be the most interesting part of the book.  He balanced theory with examples and lots of practical advice, which is fairly rare among most of the people I've seen try and cover the topic.

Of uids and shell scripts ...

So, I've got this extensive collection of shell scripts that I've written sitting in ~/bin, that I use all time.  In fact, I like them so much, that I wanted to be able to call them while  I was root.  But that's a huge security hole, because it means that anyone who has become me and can edit my scripts, now has a pretty good chance of me running them as root.  Granted, if someone actually owns my user account, I already have some serious problems, because in all reality I care about my data much more than I care about "root access" by itself.  But, seeing as I wrote all of these scripts myself, there's a reasonable chance that there's some egregious bug in them that I don't want to run as root anyway.

I used to just put my user scripts dir in my path as root, figuring that I'm the only one who's ever me, and I usually have physical security of my box.  But as I started copying my scripts to more and more computers (including my linode, which I don't  have physical control of), this started making me nervous.   So eventually I solved this problem by making two copies of my scripts, one to sit in /root and one to sit in ~/bin.  This worked great for security, but now I had to maintain and sync two copies of this stuff, and bug fixes weren't propagating quickly.  But I didn't have a better way to do things.

I was talking to one of my friends yesterday, who told me that he uses "su -c" for that problem.  That let's you run a command as another user, so you can drop root privileges.  If the syntax was a little different, this is almost exactly what I wanted, but unfortunately I play enough games with arguments, that I wanted to be able to pass ./script 1 2 3 'a b c' correctly, and I couldn't figure out how to do that on a shell level.  I also don't want to worry about sanitizing arguments with spaces or escape sequences or anything.

So I finally convinced myself that the right way to do this was in C (after a failed attempt to find perl commands that would let me do this).  It actually turned out to be way easier than I expected.  Essentially I can just call setuid, pull off the command line arguments, and pass them to exec, and I have a script which runs a command as the specified user.  I ended up pulling some library routines out of the "su" utility to do some environment sanitization , but other than that the code is a few system calls and some error checking.  If I ever clean it up, and make sure I've preserved all the copyright notices I'll stick it up here.

But here's the gist of it (error checking removed):
Called as "suwrap user command args"

int main(int argc, char ** argv)
    int x;
    uid_t uid;
    char **args;
    passwd *p;
    int argsize; 
    p = getpwnam(argv[1]);
    addenv("HOME", p->pw_dir);
    addenv("USER", p->pw_name);
    addenv("LOGNAME", p->pw_name);
    uid = p->pw_uid;
    argsize = argc-COMMAND_INDEX+1;
    args = malloc(argsize*sizeof(char*)); 
    for(x=COMMAND_INDEX;x<argc;x++) { 
        args[x-COMMAND_INDEX] = argv[x]; 
    args[argsize-1] = NULL; 
    execvpe(argv[COMMAND_INDEX], args, newenvp); 
    //No one will ever see this... 
    return 0;

I don't know if I should feel proud or disturbed that I've progressed from writing shell scripts to C code, but I definitely feel that I have crossed a Unix threshold here...