Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Solaris Developer Chat Sessions

 
 

Solaris Live Transcripts Index

June 21, 2001

Chat Title: Secure C
Guest Speaker: Rich Teer

This is a moderated forum

LizA: Hello and welcome to Solaris Live! Our guest today is Rich Teer. Rich has over 10 years of industry experience with UNIX® systems and C programming. He lives in Kelowna, BC, Canada, where he runs his own Solaris consultancy and web hosting company, Rite Online Inc. He is currently writing a book titled "Solaris Systems Programming", which will be published by Addison-Wesley in 2002. Let me start the questions by asking, "What is a buffer overflow?"

Rich T: Very briefly, a buffer overflow is when a program (or the user of a program) tries to store more data in a variable than it has been allocated space for. An example that comes to mind is file pathnames.

Rich T: What happens is that the data over flows the assigned buffer, and smashes the stack, possibly corrupting data, or worse.

Rich T: Luckily, root shell compromises that rely on buffer overflows are very platform specific: one written for Solaris on x86 won't work for Solaris on SPARC. Odds are, it won't even work for Linux on x86.

LizA: Why do so many programs seem to suffer from buffer overflow problems?

Rich T: That's a good question! ...

Rich T: There are a couple of reasons why they are so prevalent. One is that a lot of functions in the C library, and the C language itself, don't do any bounds checking. So, if you (as a programmer) write some code to put something in the 100th element of an array, ...

Rich T: which only has space for 50 elements, nothing will stop you from doing that. C lets you shoot yourself in the foot...

Rich T: More recently, alternate versions of some standard library functions have been released. For example, in addtion to sprintf(), we can also now use snprintf()...

Rich T: snprintf() works the same as sprintf(), except that it lets you put an upper limit on the number of characters that get copied. Because these functions are relatively new, not that much code is using them. Historical code that is years old will be vulnerable, unfortunately, as will new code that doesn't take advantage of these facilities.

LizA: Tell me more about how I can avoid buffer overflows in my program.

Rich T: LizA, another way to avoid the side effects of buffer overflows is to disable code execution on the stack. While this method won't stop remote Denial of Service exploits by crashing your program, it can stop the sort of exploit that can do the most damage: getting the attacker a (root) shell...

Rich T: The way to do that is to put the following into /etc/system:

    set noexec_user_stack = 1

and reboot...

Rich T: But to actually avoid buffer overflows in the first place, I think the best thing to do is to make sure that your programs always use the version of functions that let you specify a maximum number of characters to transfer...

Rich T: As well as the snprintf() example I gave earlier, there are also strncat(), strncpy(), and many more. Perhaps another way to avoid them would be to make more use of dynamically allocated buffers (malloc), rather than static ones (char foo [100])...

Rich T: One last tip for avoiding buffer overflows is to test thoroughly, especially boundary conditions. Make sure that you've allowed room for the terminating null at the end of a string, for example.

LizA: What would be a good way for my program to start another program?

Rich T: LizA, there are two ways to start another program. The most obvious, and easiest, way is to use the system() function. Although easy to use, it is potentially dangerous...

Rich T: The trouble with the system function is that (behind the scenes) it invokes a shell to run the program you want. Anything that is in your program's environment, (open files, shell environment variables, etc.) will get passed to the new program, through the shell.

Rich T: A better way would be to use fork and exec (the system function also uses these functions, but in a manner that you can't control). You first fork a new process (very briefly: fork makes a copy of your process). In the child, you'd clean up your environment, before calling exec() directly.

Rich T: This way, you call your desired program without an intervening shell - which will probably have performance benefits too. When you exec the new program, make sure that you call one of the variants that lets you specify the new program by the complete pathname: relying on the user's $PATH is incredibly dangerous! Even better is to use execle() or execve(), which lets you specify the environment variables you pass to the new program.

Khirmint: When starting another program, what is the easiest and most secure way to communicate with that program, sending information back and forth?

Rich T: Hi Khirmint. Unfortunately, easiest and most secure are (almost) mutually exclusive! The easiest way to start a new process and talk to it is to use the popen() function, but that works in a similar manner to system...

Rich T: Depending on what sort of info you want to pass to and fro, you have a couple of options: 1) you could manually do what popen() does by using fork, pipe, and exec...

Rich T: 2) You could use one of the other standard IPC mechanisms, like shared memory, message queues, or (on Solaris and Linux) doors. By rolling everything yourself, you have complete control over what your program does...

Rich T: Although this involves more work [so it's great if you get paid by the line! :-)], it's more secure. Does that answer your question?

Khirmint: Aside from buffer overflows, what other sort of pitfalls need to be avoided?

Rich T: Khirmint, there are a lot of pitfalls to avoid!

Rich T: Some that come to mind are:

  • Keep your code as simple as you can. Code that is simple tends to be easier to read, and hence find bugs in.
  • Always use full path names for files (especially any programs you exec). Basically, you can't trust users! For example, if you want to run the ls command, use /bin/ls to run it, rather than just ls. It's too easy for $PATH to be maliciously changed...
  • Program defensively. By this I mean assume the worst: if you write a function that expects its arguments to be in a certain range, check them to make sure. Also, check the return value from any system functions you call, especially for errors. Don't assume things will work (even malloc on a machine with gigabytes of virtual memory).
  • If the confidentiality of your data is important, make sure your program can't produce a core dump.
  • In a S[GU]ID program, maintain the principle of least privilege...By this I mean don't let the whole program run as root (say). Do what must be done as root, and then relinquish root's privileges. Try to use a SGID program, rather than a SUID one.

LizA: Do you have tips for writing secure SUID or SGID programs?

Rich T: LizA, the most secure way of writing S[GU]ID programs is to not write them in the first place!

Rich T: But in UNIX programming, S[GU]ID programs are almost unavoidable. Following on from the least privilege axiom, if you need a program that needs to run as a privileged user, you should evaluate if that user NEEDS to be root. Can you do what you want by using some other application specific UID or GID?...

Rich T: Let's say you have a game that keeps track of high scores. You want to prevent people from writing directly to the file, but you still want people's high scores to be recorded. One way would be to make root own the file (and have the file only writable by root), and make the game SUID root...

Rich T: That's way too much privilege! A better approach would be to create a new user and group specific for the game, and then have the game set it's GID to the one specific for it (the high scores file would only be writable by that group). That way, you get the protection the file needs, without risking a root shell attack if the program gets compromised in some way...

Rich T: Everything we've talked about in this session is doubly important for S[GU]ID programs - especially if they SUID root...

Rich T: Unfortunately, we're running out of time here, so I can't give you a complete answer. The Solaris Developer Connection will be posting an article I wrote about Secure C Programming soon (probably by the end of next week). You will be able to find it at: http://soldc.sun.com/articles. The article goes into a bit more detail than we've had time for today.

LizA: Thank you, Rich. And thanks to the rest of you for joining us today. Join us again on July 19 at 9:30 a.m. PDT when our guest will be Massimo Lanfranconi, answering your questions about OpenOffice.org...an open source community project (http://openoffice.org).

June 21, 2001


Back to Top