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; setuid(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... printf("\nDone!\n"); 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...
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.