Secure UNIX Programming FAQ

Version 0.5

Sun May 16 21:31:40 PDT 1999

The master copy of this FAQ is currently kept at

http://www.whitefang.com/sup/

The webpage has a more spiffy version of the FAQ in html.

This FAQ is also posted to comp.security.unix (c.s.u) , comp.answers , news.answers.

Please do not mirror this FAQ without prior permission. Due to the high volume of readers I'm worried that old versions of the FAQ are left to grow stale, consequently receive email based on fixed errors/omissions.

Additional Resources

After receiving many comments, and suggestions I decided to make some more SUP FAQ related resources available.

A change log can be found at: http://www.whitefang.com/sup/sec-changes.txt

A moderated mailing list has been setup for the discussion of Secure UNIX programming. You can find a copy of the announcement at:

http://www.whitefang.com/sup/announcement.txt

I'm currently working on a terse reference guide. It will be made available Real Soon Now in PostScript format. The reference can be printed out and kept handy next to your can of cola. It contains, tables, diagrams, and step by step instructions for various operations mentioned in the FAQ. It will not be posted to Usenet, and downloadable from the FAQ's website. Its "Real Soon Now" status is very Real Soon Now.

Copyright

I, Thamer Al-Herbish reserve a collective copyright on this FAQ. Individual contributions made to this FAQ are the intellectual property of the contributor.

I am responsible for the validity of all information found in this FAQ.

This FAQ may contain errors, or inaccurate material. Use it at your own risk. Although an effort is made to keep all the material presented here accurate, the contributors and maintainer of this FAQ will not be held responsible for any damage -- direct or indirect -- which may result from inaccuracies.

You may redistribute this document as long as you keep it in its current form, without any modifications. Read -- keep it up to date please!! :-)

Introduction

This FAQ answers questions about secure programming in the UNIX environment. It is a guide for programmers and not administrators. Keep this in mind because I do not tackle any administrative issues. Try to read it as a guide if possible. I'm sorry it sounds like a bad day on jeopardy.

At the risk of sounding too philosophical, this FAQ is also a call to arms. Over almost the last decade, a good six years, a movement took place where security advisories would hit mailing lists and other forums at astonishing speed. I think the veterans are all to familiar with the repetitive nature of these security advisories, and the small amount of literature that has been published to help avoid insecure programming. This text is a condensation of this movement and a contribution made to it, placed in a technical context to better serve the UNIX security community. As the Usenet phrase goes: "Hope this helps."

Additions and Contributions

The current FAQ is not complete. I will continue to work on it as I find time. Feel free to send in material for the Todo sections, and for the small notes I've left around. Also, compatibility is an issue I struggle with sometimes. The best I can do for some UNIX flavors is read man pages. Corrections/addendums for compatibility notes is greatly appreciated, and easily done as a collective effort. All contributions, comments, and criticisms can be sent to:

Secure UNIX Programming FAQ <sup@whitefang.com>

Please don't send them to my personal mailbox, because I can keep things organized better with the above e-mail address. Also please try to be as concise as possible. Remember I will usually quote you directly if you have something to add.

Finally, although the contributors list is currently short, the material in this FAQ did not pop out of my head in a pig-flying fashion. Attribution is given where applicable. If you feel any of this is unfair to something you have published, do let me know. The bibliography is found at the end.

Special thanks to John W. Temples, Darius Bacon, Brian Spilsbury, Elias Levy, who had looked at some of the drafts of past material that made it into this FAQ. As usual, all mistakes are mine and only mine.

Also kudos to the people at netspace.org for hosting Bugtraq all these years. The archive is invaluable to my research.

Table of Contents


  • 4) Local Process Interaction:

  • Now for the list of process attributes and how to go about avoiding any security holes.

    With all of this SUID/SGID programming is daunting at best. A better technique (proposed by Thomas Ptacek on the newsgroup) would be to use a server-client model where the server is privileged but does not inherit the environment of its parent process -- a would be attacker. That way the client runs with no privileges, connects to the privileged server and passes the relevant information through the IPC channel.

    Back to Top

  • 4.2) How can I limit access to a SUID/SGID process-image safely?

    The question may seem vague but sometimes it would seem attractive to have a SUID process that is only executable by a particular set of users. Some time ago I implemented a distributed network monitoring package that had java clients talk to it remotely, and sniffers running on different servers (a very ambitious undertaking). The actual servers where run under a special group called "sly" that would in turn have access to a SUID process-image to do all the sniffing. The child process ran as root, but could only be executed by users in the group "sly."

    At first this looks good. The actual server does not run with special privileges, and it would seem that if it got exploited the attacker would not gain root privileges. However, he would gain privileges for the group "sly" that would let him sniff the local network. If he was then able to exploit the sniffer, he would gain root privileges. But he needs to exploit the server to the point of executing arbitrary code on the machine. Creating a file, or tricking it into sending privileged information would not equate to gaining the privileges of group "sly" necessarily. So this does not lessen the need for secure code, but it _could_ in the long run lessen the chances of complete compromise.

    I'm going to call this technique, at the risk of coining yet another term, privilege segmentation. The attacker may gain privileges to a specific group but has more work to do in order to gain higher privileges.

    Fair warning that security via "chance" or "hope" is not good. I don't particularly like hearing about "risk management," and the above technique is just that: risk management.

    Back to Top

  • 4.3) How do I authenticate a parent process?

    Since the child process inherits the parent's real user ID, a call to getuid() does the trick. Unfortunately due to a misconception, some programmers are led to believe that getuid() is not sufficient. This stems from the thinking that if a user managed in exploiting a SUID process into running another process, the child would have a real user ID matching the parent process' effective user ID. This is not true, because the real user ID keeps propagating from parent to child regardless of the SUID feature. As mentioned in 2.1, the child process inherits the credentials directly, a perfect copy with the exception of the effective credentials if the SGID or SUID feature. The saved credentials are also reset. But this exception does not extend to the real credentials which are directly inherited.

    However, if the exploited SUID process was to change its real user ID to match its effective user ID, which is easily done with setuid(), then getuid() is not sufficient. There is a logical fallacy here: if the parent process has already been exploited to the point where the attacker can cause it to switch credentials what's the point in faking it anymore? Nonetheless, additional steps can be taken but not portably. On systems where login information is stored in the kernel, and not on the file system, by setlogin(), getlogin() will always return the username associated with the session. [ FreeBSD stores login information in kernel. ]

    On OpenBSD, and FreeBSD the issetugid() system calls can be used to find out if the current process is a SUID or GUID process. This propagation continues unless a child process clears all its privileges, to quote the OpenBSD man page "uid != euid or gid != egid". So this system call may be used in conjunction with getuid to be even more paranoid.

    A good suggestion is to do a getuid(), check getlogin() if the information is stored in the kernel, and finally do a issetugid(). If all tests pass, you know you are talking to the Real McCoy. In saying that, caution should not be thrown to the wind. Using passwords, cookies, insert-fancy-authentication-mechanism-here etc. is always recommended, but the previous approach is the more light weight kernel supported method.

    Back to Top

  • 4.4) How do I authenticate a non-parent process?

    [ I could use writing on SO_PEERCRED (Linux) doors (Solaris) and LOCAL_CREDS (NetBSD). Also I have some example code for the techniques discussed below. If you tackle the any of the issues above, you would be a very nice contributor to provide example source. Also this is advanced stuff so I'll elaborate more when I fix it up]

    It is possible to authenticate processes via IPC channels (Bernstein 1999). However the methods differ on different UNIX flavors making it a very non-portable mess to write.

    BSD derived systems support the concept of access rights (Stevens 1990). The facility allows a process to pass a file descriptor through a UNIX domain socket, and with a small hack it can be used to authenticate a local process. The term hack is only used because the facility was not intended for authentication. However, if the client sends a descriptor referencing a file that has read permissions only for its owner,the receiver knows the sender is the owner. Thus the file acts as an identifier. However, on systems where a user may give away files with chown() this method cannot be used. The attacker can simply create a file with read permissions only for himself, open it, and then chown it to another user. Fortunately this is a System V "feature," and on many systems can be turned off as a kernel configuration option.

    A similar technique is found under System V derived systems. This is done by receiving file descriptors via a STREAMS file. Only the file descriptor is discarded because the UID is passed along with it. This is done by using an ioctl call with the I_SENDFD and I_RECVFD flags on a streams file used as an IPC channel. This technique does not suffer from the chown attack because the credentials are passed _along_ with the descriptor.

    BSD/OS, FreeBSD and other BSD derived operating systems also have SCM_CREDS that sends credential information through a UNIX domain socket. [ Ok, someone point me to some standard that documents the semantics. Every BSD camp is doing it differently ":( ]

    Back to Top

  • 5) Using The File System Securely

  • [ The first contributor to find a better solution to 5.2 gets a donut ]

    Sadly too many past security holes show that the average programmer fails to note that the file system is a database with links pointing to resources, and that filenames act only as identifiers. File names, which are stored in directories (considered special files), point to inodes. Indeed that is how we get race condition attacks, and symlink attacks. Both are given treatment below, but keep in mind that the principles are open to other databases that follow the same model as the file system. In particular race conditions may occur in non-file-system related operations.

  • 6) Handling Input

  • This section should be read with section 2 "The Flow Of Information"

  • 8) Bibliography

  • 9) List of Contributors

  • Back to Top

    "Youth, Nature, and relenting Jove, 
    To keep my lamp _in_ strongly strove,
    But Romanelli was so stout, 
    He beat all three -- _and blew it out_."
    
    -- George Gordon Byron "My Epitaph" From "Occasional Pieces"