This is a discussion on root daemon dropping privileges temporarily? within the Linux Security forums, part of the System Security and Security Related category; I'm writing a custom network application for use on my systems (Linux and FreeBSD), which has to run as ...
|
|||||||
| FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read |
|
|||
|
I'm writing a custom network application for use on my systems (Linux and
FreeBSD), which has to run as root because it does user authentication -- think along the lines of a pop3 daemon out of inetd. So I'm not worrying about the socket binding etc, just single program instances. Currently, the program is launched as root and does setuid() after it authenticates the connecting user. My concern is vulnerability while the software is running as root. Is it possible/portable to do setuid() to something like nobody and then later do a setuid() back up to root? Or should I be using seteuid() to set the effective user ID? -- Jem Berkes http://www.sysdesign.ca/ |
|
|||
|
Jem Berkes spilled the following:
> > Currently, the program is launched as root and does setuid() after it > authenticates the connecting user. My concern is vulnerability while the > software is running as root. > > Is it possible/portable to do setuid() to something like nobody and then > later do a setuid() back up to root? Or should I be using seteuid() to set > the effective user ID? > Is that going to add to the security? - if it is reversible by your code then it must also be reversible by someone else's exploit, all you've gained is more code and complexity. I think the only way to seperate privilege would be to run an application specific proxy locked down as tight as possible (e.g. uid=nobody) which communicates with a back-end which actually runs as root via a tightly defined protocol which is not accessible by other users processes (encryption? shared memory?), although again it has added a lot of complexity. HTH C. |
|
|||
|
>> Is it possible/portable to do setuid() to something like nobody and
>> then later do a setuid() back up to root? Or should I be using >> seteuid() to set the effective user ID? >> > Is that going to add to the security? - if it is reversible by your > code then it must also be reversible by someone else's exploit, all > you've gained is more code and complexity. I realized this too, it's reversible and if an attacker gets to the point where they can execute arbitrary code then they certainly could seteuid back to root and cause plenty trouble. However -- and this is why I think it's still worth doing -- not all exploits result in arbitrary command execution. Setting effective privileges to nobody would thwart many kinds of less serious attacks and also help prevent accidents. For instance, snooping in restricted file systems, viewing or deleting important files. > I think the only way to seperate privilege would be to run an > application specific proxy locked down as tight as possible (e.g. > uid=nobody) which communicates with a back-end which actually runs as > root via a tightly defined protocol which is not accessible by other > users processes (encryption? shared memory?), although again it has > added a lot of complexity. Yes, lots of complexity there and considering that my entire application is a light-weight proxy to begin with, I really think adding that kind of privilege separation would not be worth it in my case. Thinking about all this also gave me an interesting idea, maybe I could get your feedback on this. Using seteuid() to temporarily drop root privileges seems like a good idea, except that it's so easy to seteuid(0) again. but what if, when lowering privileges, the kernel assigned you some kind of a code/context (stored on the heap) specific to your app instance? Then to rise privileges you would have to supply this code back to the kernel. Since locations on the heap are very difficult for attackers to predict, perhaps this would thwart an attacker from changing privileges if they have gained code execution ability by smashing the stack. This rambling is all just theoretical, but it makes me wonder if instance-specific codes could somehow be used to distinguish attackers from valid programs. -- Jem Berkes http://www.sysdesign.ca/ |
|
|||
|
> Currently, the program is launched as root and does setuid() after it
> authenticates the connecting user. My concern is vulnerability while > the software is running as root. > > Is it possible/portable to do setuid() to something like nobody and > then later do a setuid() back up to root? Or should I be using > seteuid() to set the effective user ID? I ran into a different scheme, this one separates privileges and seems common. Although it's still not perfect (since the processes are related) I think it should be pretty good, unless anyone knows otherwise? 1) fork() and setgid, setuid nobody. Unprivileged child "faces the world" 2) parent retains root privileges, and code now ONLY does authentication 3) unprivileged child can now query the privileged parent only using IPC Thus if the network-facing child becomes compromised, the attacker is stuck at nobody's privileges, though they can still craft dangerous data to send to the root authenticator process via IPC. I think the only way the parent (root, authenticator) can be compromised is if it is vulnerable to maliciously crafted IPC queries. But still much better off then a root-privilege process doing socket I/O with the world. -- Jem Berkes http://www.sysdesign.ca/ |
|
|||
|
Jem Berkes spilled the following:
> Thinking about all this also gave me an interesting idea, maybe I could > get your feedback on this. Using seteuid() to temporarily drop root > privileges seems like a good idea, except that it's so easy to seteuid(0) > again. but what if, when lowering privileges, the kernel assigned you > some kind of a code/context (stored on the heap) specific to your app > instance? Then to rise privileges you would have to supply this code back > to the kernel. > > Since locations on the heap are very difficult for attackers to predict, > perhaps this would thwart an attacker from changing privileges if they > have gained code execution ability by smashing the stack. This rambling > is all just theoretical, but it makes me wonder if instance-specific > codes could somehow be used to distinguish attackers from valid programs. > Well if re-writing the kernel is not a problem for your application there are a few useful bits of software out there already which can improve the security - particularly the openwall kernel patch to disable execution of code within the stack, or the NSAs SELinux MAC stuff. Your proposal again relies on "security through obscurity" although IMHO, leaving aside the risks of increasing complexity (and therefore probability of vulnerability) this *IS* better than no security. Regarding the MAC stuff - its not something I'm overly familiar with but from what I know it would not depend on authentication information being stored within the memory space of your process. It sounds *similar* to waht you are suggesting - every entity in the system (files, programs etc) get allocated a unique ID and the admiistrator sets out a policy defining how they can relate to each other - the kernel then mediates interactions. It (I think) should be possible to build prototype system using sudo and chroot (maybe even a production solution) within the pain of installing SELinux and defining the policy, but you might want to read up on it if you haven't already done so. I did try to get my head round how to build a secure linux system sitting in a hostile envorinment (how do I stop someone from reading the hard disk on another machine...running arbitary code....booting it up from floppy without passwording the BIOS) and found myself going down the DRM road. This requires the hardware to establish trust with the bootloader, the bootloader with the kernel, the kernel with programs. And to get that working you really need an asymmetric cypher system. And how often are critical patches released for openSSL? Looks like we're back at the complexity issue - KISS. Best solution - write beautiful, secure code ;) C. |
|
|||
|
Colin McKinnon wrote:
> > Best solution - write beautiful, secure code ;) > > C. (this is me somewhere else) It occurred to me last night that the best solution to the problem would be to allow the kernel to change the real uid of the process from a user other than root. Obviously it would need the authentication token to permit this. It's not trivial since the kernel code to support this would have to be stateful as well as re-entrant. Unless you were to migrate PAM into the kernel, it would also require that the kernel be able to call out to user-space (isn't this how encrypted loop-back filesystems work?). Interestingly, if the token was passed as a hashed value using a challenge supplied by the kernel...actually, thinking about it...if the challenge was based on things already known at both ends, you could lose the stateful part. But now we have a system where your program does not even have to trust the kernel! e.g. (I won't pretend to write 'C' here): in program: unix_timestamp=time_now(); key=get_key_for_user(to_username); crypt_password=crypt(key, password); hash=md5(crypt_password + unix_timestamp + pid); raise_privilege(to_username, hash, unix_timestamp); in "kernel": function raise_privilege(to_user, hash_from_pid, time_in_hash) if (abs(time_now()-time_in_hash)<120) { myhash=md5(stored_password + time_in_hash + requesting_pid); if (hash_from_pid==myhash) { return( set_uid_for_process(to_user, requesting_pid) ); } } return(false); } ....just a thot. C. |
|
|||
|
> It occurred to me last night that the best solution to the problem
> would be to allow the kernel to change the real uid of the process > from a user other than root. I'm not sure if this could ever be a good idea... I like how dropping privileges with setuid() is a permanent thing, at least that's my understanding of it. > Interestingly, if the token was passed as a hashed value using a > challenge supplied by the kernel...actually, thinking about it...if > the challenge was based on things already known at both ends, you > could lose the stateful part. But now we have a system where your > program does not even have to trust the kernel! But I like this token method. Seems a nice way to form an agreement between a process and the kernel, where neither has to trust the other. -- Jem Berkes http://www.sysdesign.ca/ |