Added chapter on secure programming in basics section and added a
bibliography.
This commit is contained in:
parent
87fddebcea
commit
a86ed65644
Notes:
svn2git
2020-12-08 03:00:23 +00:00
svn path=/head/; revision=8297
3 changed files with 1596 additions and 3 deletions
|
@ -1,7 +1,7 @@
|
|||
<!--
|
||||
The FreeBSD Documentation Project
|
||||
|
||||
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/book.sgml,v 1.3 2000/10/06 15:36:45 phantom Exp $
|
||||
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/book.sgml,v 1.4 2000/11/06 10:22:11 murray Exp $
|
||||
-->
|
||||
|
||||
<!DOCTYPE BOOK PUBLIC "-//FreeBSD//DTD DocBook V3.1-Based Extension//EN" [
|
||||
|
@ -214,6 +214,410 @@
|
|||
|
||||
<para></para>
|
||||
</chapter>
|
||||
|
||||
<chapter id="secure-programming">
|
||||
<title>Secure Programming</title>
|
||||
|
||||
<para>This chapter was written by Murray Stokely.</para>
|
||||
|
||||
<sect1><title>Synopsis</title>
|
||||
|
||||
<para>This chapter describes some of the security issues that
|
||||
have plagued Unix programmers for decades and some of the new
|
||||
tools available to help programmers avoid writing exploitable
|
||||
code.</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="secure-philosophy"><title>Secure Design
|
||||
Methodology</title>
|
||||
|
||||
<para>Writing secure applications takes a very scrutinous and
|
||||
pessimistic outlook on life. Applications should be run with
|
||||
the principle of <quote>least privilege</quote> so that no
|
||||
process is ever running than more with the bare minimum access
|
||||
that it needs to accomplish its function. Previously tested
|
||||
code should be reused whenever possible to avoid common
|
||||
mistakes that others may have already fixed.</para>
|
||||
|
||||
<para>One of the pitfalls of the Unix environment is how easy it
|
||||
is to make assumptions about the sanity of the environment.
|
||||
Applications should never trust user input (in all its forms),
|
||||
system resources, inter-process communication, or the timing of
|
||||
events. Unix processes do not execute synchronously so logical
|
||||
operations are rarely atomic.</para>
|
||||
</sect1>
|
||||
|
||||
<sect1><title>Buffer Overflows</title>
|
||||
|
||||
<para>Buffer Overflows have been around since the very
|
||||
beginnings of the Von-Neuman <xref linkend="COD"> architecture.
|
||||
They first gained widespread notoriety in 1988 with the Moorse
|
||||
Internet worm. Unfortunately, the same basic attack remains
|
||||
effective today. Of the 17 CERT security advisories of 1999, 10
|
||||
of them were directly caused by buffer-overflow software bugs.
|
||||
By far the most common type of buffer overflow attack is based
|
||||
on corrupting the stack.</para>
|
||||
|
||||
<para>Most modern computer systems use a stack to pass arguments
|
||||
to procedures and to store local variables. A stack is a last
|
||||
in first out (LIFO) buffer in the high memory area of a process
|
||||
image. When a program invokes a function a new "stack frame" is
|
||||
created. This stack frame consists of the arguments passed to
|
||||
the function as well as a dynamic amount of local variable
|
||||
space. The "stack pointer" is a register that holds the current
|
||||
location of the top of the stack. Since this value is
|
||||
constantly changing as new values are pushed onto the top of the
|
||||
stack, many implementations also provide a "frame pointer" that
|
||||
is located near the beginning of a stack frame so that local
|
||||
variables can more easily be addressed relative to this
|
||||
value. <xref linkend="COD"> The return address for function
|
||||
calls is also stored on the stack, and this is the cause of
|
||||
stack-overflow exploits since overflowing a local variable in a
|
||||
function can overwrite the return address of that function,
|
||||
potentially allowing a malicious user to execute any code he or
|
||||
she wants.</para>
|
||||
|
||||
<para>Although stack-based attacks are by far the most common,
|
||||
it would also be possible to overrun the stack with a heap-based
|
||||
(malloc/free) attack.</para>
|
||||
|
||||
<para>The C programming language does not perform automatic
|
||||
bounds checking on arrays or pointers as many other languages
|
||||
do. In addition, the standard C library is filled with a
|
||||
handful of very dangerous functions.</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols=2>
|
||||
<tbody>
|
||||
<row><entry><function>strcpy</function>(char *dest, const char
|
||||
*src)</entry>
|
||||
<entry><simpara>May overflow the dest buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>strcat</function>(char *dest, const char
|
||||
*src)</entry>
|
||||
<entry><simpara>May overflow the dest buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>getwd</function>(char *buf)</entry>
|
||||
<entry><simpara>May overflow the buf buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>gets</function>(char *s)</entry>
|
||||
<entry><simpara>May overflow the s buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>[vf]scanf</function>(const char *format,
|
||||
...)</entry>
|
||||
<entry><simpara>May overflow its arguments.</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>realpath</function>(char *path, char
|
||||
resolved_path[])</entry>
|
||||
<entry><simpara>May overflow the path buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>[v]sprintf</function>(char *str, const char
|
||||
*format, ...)</entry>
|
||||
<entry><simpara>May overflow the str buffer.</simpara></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
|
||||
<sect2><title>Example Buffer Overflow</title>
|
||||
|
||||
<para>The following example code contains a buffer overflow
|
||||
designed to overwrite the return address and skip the
|
||||
instruction immediately following the function call. (Inspired
|
||||
by <xref linkend="Phrack">)</para>
|
||||
|
||||
<programlisting>
|
||||
#include <sgmltag>stdio.h</sgmltag>
|
||||
|
||||
void manipulate(char *buffer) {
|
||||
char newbuffer[80];
|
||||
strcpy(newbuffer,buffer);
|
||||
}
|
||||
|
||||
int main() {
|
||||
char ch,buffer[4096];
|
||||
int i=0;
|
||||
|
||||
while ((buffer[i++] = getchar()) != '\n') {};
|
||||
|
||||
i=1;
|
||||
manipulate(buffer);
|
||||
i=2;
|
||||
printf("The value of i is : %d\n",i);
|
||||
return 0;
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>Let us examine what the memory image of this process would
|
||||
look like if we were to input 160 spaces into our little program
|
||||
before hitting return.</para>
|
||||
|
||||
<para>[XXX figure here!]</para>
|
||||
|
||||
<para>Obviously more malicious input can be devised to execute
|
||||
actual compiled instructions (such as exec(/bin/sh)).</para>
|
||||
</sect2>
|
||||
|
||||
<sect2><title>Avoiding Buffer Overflows</title>
|
||||
|
||||
<para>The most straightforward solution to the problem of
|
||||
stack-overflows is to always use length restricted memory and
|
||||
string copy functions. <function>strncpy</function> and
|
||||
<function>strncat</function> are part of the standard C library.
|
||||
These functions accept a length value as a parameter which
|
||||
should be no larger than the size of the destination buffer.
|
||||
These functions will then copy up to `length' bytes from the
|
||||
source to the destination. However there are a number of
|
||||
problems with these functions. Neither function guarantees NUL
|
||||
termination if the size of the input buffer is as large as the
|
||||
destination. The length parameter is also used inconsistently
|
||||
between strncpy and strncat so it is easy for programmers to get
|
||||
confused as to their proper usage. There is also a significant
|
||||
performance loss compared to <function>strcpy</function> when
|
||||
copying a short string into a large buffer since
|
||||
<function>strncpy</function> NUL fills up the the size
|
||||
specified.</para>
|
||||
|
||||
<para>In OpenBSD, another memory copy implementation has been
|
||||
created to get around these problem. The
|
||||
<function>strlcpy</function> and <function>strlcat</function>
|
||||
functions guarantee that they will always null terminate the
|
||||
destination string when given a non-zero length argument. For
|
||||
more information about these functions see <xref
|
||||
linkend="OpenBSD">. The OpenBSD <function>strlcpy</function> and
|
||||
<function>strlcat</function> instructions have been in FreeBSD
|
||||
since 3.5.</para>
|
||||
|
||||
<sect3><title>Compiler based run-time bounds checking</title>
|
||||
|
||||
<para>Unfortunately there is still a very large assortment of
|
||||
code in public use which blindly copies memory around without
|
||||
using any of the bounded copy routines we just discussed.
|
||||
Fortunately, there is another solution. Several compiler
|
||||
add-ons and libraries exist to do Run-time bounds checking in
|
||||
C/C++.</para>
|
||||
|
||||
<para>StackGuard is one such add-on that is implemented as a
|
||||
small patch to the gcc code generator. From the StackGuard
|
||||
website, http://immunix.org/stackguard.html :
|
||||
<blockquote><para>"StackGuard detects and defeats stack
|
||||
smashing attacks by protecting the return address on the stack
|
||||
from being altered. StackGuard places a "canary" word next to
|
||||
the return address when a function is called. If the canary
|
||||
word has been altered when the function returns, then a stack
|
||||
smashing attack has been attempted, and the program responds
|
||||
by emitting an intruder alert into syslog, and then
|
||||
halts."</para></blockquote>
|
||||
|
||||
<blockquote><para>"StackGuard is implemented as a small patch
|
||||
to the gcc code generator, specifically the function_prolog()
|
||||
and function_epilog() routines. function_prolog() has been
|
||||
enhanced to lay down canaries on the stack when functions
|
||||
start, and function_epilog() checks canary integrity when the
|
||||
function exits. Any attempt at corrupting the return address
|
||||
is thus detected before the function
|
||||
returns."</para></blockquote>
|
||||
</para>
|
||||
|
||||
<para>Recompiling your application with StackGuard is an
|
||||
effective means of stopping most buffer-overflow attacks, but
|
||||
it can still be compromised.</para>
|
||||
|
||||
</sect3>
|
||||
|
||||
<sect3><title>Library based run-time bounds checking</title>
|
||||
|
||||
<para>Compiler-based mechanisms are completely useless for
|
||||
binary-only software for which you cannot recompile. For
|
||||
these situations there are a number of libraries which
|
||||
re-implement the unsafe functions of the C-library
|
||||
(<function>strcpy</function>, <function>fscanf</function>,
|
||||
<function>getwd</function>, etc..) and ensure that these
|
||||
functions can never write past the stack pointer.</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><simpara>libsafe</simpara></listitem>
|
||||
<listitem><simpara>libverify</simpara></listitem>
|
||||
<listitem><simpara>libparnoia</simpara></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Unfortunately these library-based defenses have a number
|
||||
of shortcomings. These libraries only protect against a very
|
||||
small set of security related issues and they neglect to fix
|
||||
the actual problem. These defenses may fail if the
|
||||
application was compiled with -fomit-frame-pointer. Also, the
|
||||
LD_PRELOAD and LD_LIBRARY_PATH environment variables can be
|
||||
overwritten/unset by the user.</para>
|
||||
</sect3>
|
||||
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1><title>SetUID issues</title>
|
||||
|
||||
<para>There are at least 6 different IDs associated with any
|
||||
given process. Because of this you have to be very careful with
|
||||
the access that your process has at any given time. In
|
||||
particular, all seteuid applications should give up their
|
||||
privileges as soon as it is no longer required.</para>
|
||||
|
||||
<para>The real user ID can only be changed by a superuser
|
||||
process. The <application>login</application> program sets this
|
||||
when a user initially logs in and it is seldom changed.</para>
|
||||
|
||||
<para>The effective user ID is set by the
|
||||
<function>exec()</function> functions if a program has its
|
||||
seteuid bit set. An application can call
|
||||
<function>seteuid()</function> at any time to set the effective
|
||||
user ID to either the real user ID or the saved set-user-ID.
|
||||
When the effective user ID is set by <function>exec()</function>
|
||||
functions, the previous value is saved in the saved set-user-ID.</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="chroot"><title>Limiting your program's environment</title>
|
||||
|
||||
<para>The traditional method of restricting access to a process
|
||||
is with the <function>chroot()</function> system call. This
|
||||
system call changes the root directory from which all other
|
||||
paths are referenced for a process and any child processes. For
|
||||
this call to succeed the process must have execute (search)
|
||||
permission on the directory being referenced. The new
|
||||
environment does not actually take affect until you
|
||||
<function>chdir()</function> into your new environment. It
|
||||
should also be noted that a process can easily break out of a
|
||||
chroot environment if it has root privilege. This could be
|
||||
accomplished by creating device nodes to read kernel memory,
|
||||
attaching a debugger to a process outside of the jail, or in
|
||||
many other creative ways.</para>
|
||||
|
||||
<para>The behavior of the <function>chroot()</function> system
|
||||
call can be controlled somewhat with the
|
||||
kern.chroot_allow_open_directories <command>sysctl</command>
|
||||
variable. When this value is set to 0,
|
||||
<function>chroot()</function> will fail with EPERM if there are
|
||||
any directories open. If set to the default value of 1, then
|
||||
<function>chroot()</function> will fail with EPERM if there are
|
||||
any directories open and the process is already subject to a
|
||||
<function>chroot()</function> call. For any other value, the
|
||||
check for open directories will be bypassed completely.</para>
|
||||
|
||||
<sect2><title>FreeBSD's jail functionality</title>
|
||||
|
||||
<para>The concept of a Jail extends upon the
|
||||
<function>chroot()</function> by limiting the powers of the
|
||||
superuser to create a true `virtual server'. Once a prison is
|
||||
setup all network communication must take place through the
|
||||
specified IP address, and the power of "root privilege" in this
|
||||
jail is severely constrained.</para>
|
||||
|
||||
<para>While in a prison, any tests of superuser power within the
|
||||
kernel using the <function>suser()</function> call will fail.
|
||||
However, some calls to <function>suser()</function> have been
|
||||
changed to a new interface <function>suser_xxx()</function>.
|
||||
This function is responsible for recognizing or denying access
|
||||
to superuser power for imprisoned processes.</para>
|
||||
|
||||
<para>A superuser process within a jailed environment has the
|
||||
power to : </para>
|
||||
<itemizedlist>
|
||||
<listitem><simpara>Manipulate credential with
|
||||
<function>setuid</function>, <function>seteuid</function>,
|
||||
<function>setgid</function>, <function>setegid</function>,
|
||||
<function>setgroups</function>, <function>setreuid</function>,
|
||||
<function>setregid</function>, <function>setlogin</function></simpara></listitem>
|
||||
<listitem><simpara>Set resource limits with <function>setrlimit</function></simpara></listitem>
|
||||
<listitem><simpara>Modify some sysctl nodes
|
||||
(kern.hostname)</simpara></listitem>
|
||||
<listitem><simpara><function>chroot()</function></simpara></listitem>
|
||||
<listitem><simpara>Set flags on a vnode:
|
||||
<function>chflags</function>,
|
||||
<function>fchflags</function></simpara></listitem>
|
||||
<listitem><simpara>Set attributes of a vnode such as file
|
||||
permission, owner, group, size, access time, and modification
|
||||
time.</simpara></listitem>
|
||||
<listitem><simpara>Bind to privileged ports in the Internet
|
||||
domain (ports < 1024)</simpara></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para><function>Jail</function> is a very useful tool for
|
||||
running applications in a secure environment but it does have
|
||||
some shortcomings. Currently, the IPC mechanisms have not been
|
||||
converted to the <function>suser_xxx</function> so applications
|
||||
such as MySQL can not be run within a jail. Superuser access
|
||||
may have a very limited meaning within a jail, but there is
|
||||
no way to specify exactly what "very limited" means.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2><title>POSIX.1e Process Capabilities</title>
|
||||
|
||||
<para>Posix has released a working draft that adds event
|
||||
auditing, access control lists, fine grained privileges,
|
||||
information labeling, and mandatory access control.</para>
|
||||
<para>This is a work in progress and is the focus of the <ulink
|
||||
url="http://www.trustedbsd.org">TrustedBSD</ulink> project. Some
|
||||
of the initial work has been committed to FreeBSD-current
|
||||
(cap_set_proc(3)).</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1><title>Trust</title>
|
||||
|
||||
<para>An application should never assume that anything about the
|
||||
users environment is sane. This includes (but is certainly not
|
||||
limited to) : user input, signals, environment variables,
|
||||
resources, IPC, mmaps, the file system working directory, file
|
||||
descriptors, the # of open files, etc.</para>
|
||||
|
||||
<para>You should never assume that you can catch all forms of
|
||||
invalid input that a user might supply. Instead, your
|
||||
application should use positive filtering to only allow a
|
||||
specific subset of inputs that you deem safe. Improper data
|
||||
validation has been the cause of many exploits, especially with
|
||||
CGI scripts on the world wide web. For filenames you need to be
|
||||
extra careful about paths ("../", "/"), symbolic links, and
|
||||
shell escape characters.</para>
|
||||
|
||||
<para>Perl has a really cool feature called "Taint" mode which
|
||||
can be used to prevent scripts for using data derived outside
|
||||
the program in an unsafe way. This mode will check command line
|
||||
arguments, environment variables, locale information, the
|
||||
results of certain syscalls (<function>readdir()</function>,
|
||||
<function>readlink()</function>,
|
||||
<function>getpwxxx()</function>, and all file input.</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1><title>Race Conditions</title>
|
||||
|
||||
<para>A race condition is anomalous behavior caused by the
|
||||
unexpected dependence on the relative timing of events. In
|
||||
other words, a programmer incorrectly assumed that a particular
|
||||
event would always happen before another.</para>
|
||||
|
||||
<para>Some of the common causes of race conditions are signals,
|
||||
access checks, and file opens. Signals are asynchronous events
|
||||
by nature so special care must be taken in dealing with them.
|
||||
Checking access with <function>access(2)</function> then
|
||||
<function>open(2)</function> is clearly non-atomic. Users can
|
||||
move files in between the two calls. Instead, privileged
|
||||
applications should <function>seteuid()</function> and then call
|
||||
<function>open()</function> directly. Along the same lines, an
|
||||
application should always set a proper umask before
|
||||
<function>open()</function> to obviate the need for spurious
|
||||
<function>chmod()</function> calls.</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
</part>
|
||||
|
||||
<part id="kernel">
|
||||
|
@ -976,4 +1380,131 @@ DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);
|
|||
</chapter>
|
||||
</part>
|
||||
|
||||
<part id="appendices">
|
||||
<title>Appendices</title>
|
||||
|
||||
<bibliography>
|
||||
|
||||
<biblioentry id="COD" xreflabel="1">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Dave</firstname>
|
||||
<othername role="MI">A</othername>
|
||||
<surname>Patterson</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>John</firstname>
|
||||
<othername role="MI">L</othername>
|
||||
<surname>Hennessy</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<copyright><year>1998</year><holder>Morgan Kaufmann Publishers,
|
||||
Inc.</holder></copyright>
|
||||
<isbn>1-55860-428-6</isbn>
|
||||
<publisher>
|
||||
<publishername>Morgan Kaufmann Publishers, Inc.</publishername>
|
||||
</publisher>
|
||||
<title>Computer Organization and Design</title>
|
||||
<subtitle>The Hardware / Software Interface</subtitle>
|
||||
<pagenums>1-2</pagenums>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry xreflabel="2">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>W.</firstname>
|
||||
<othername role="Middle">Richard</othername>
|
||||
<surname>Stevens</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<copyright><year>1993</year><holder>Addison Wesley Longman,
|
||||
Inc.</holder></copyright>
|
||||
<isbn>0-201-56317-7</isbn>
|
||||
<publisher>
|
||||
<publishername>Addison Wesley Longman, Inc.</publishername>
|
||||
</publisher>
|
||||
<title>Advanced Programming in the Unix Environment</title>
|
||||
<pagenums>1-2</pagenums>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry xreflabel="3">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Marshall</firstname>
|
||||
<othername role="Middle">Kirk</othername>
|
||||
<surname>McKusick</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Keith</firstname>
|
||||
<surname>Bostic</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Michael</firstname>
|
||||
<othername role="MI">J</othername>
|
||||
<surname>Karels</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>John</firstname>
|
||||
<othername role="MI">S</othername>
|
||||
<surname>Quarterman</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<copyright><year>1996</year><holder>Addison-Wesley Publishing Company,
|
||||
Inc.</holder></copyright>
|
||||
<isbn>0-201-54979-4</isbn>
|
||||
<publisher>
|
||||
<publishername>Addison-Wesley Publishing Company, Inc.</publishername>
|
||||
</publisher>
|
||||
<title>The Design and Implementation of the 4.4 BSD Operating System</title>
|
||||
<pagenums>1-2</pagenums>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry id="Phrack" xreflabel="4">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Aleph</firstname>
|
||||
<surname>One</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<title>Phrack 49; "Smashing the Stack for Fun and Profit"</title>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry id="StackGuard" xreflabel="5">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Chrispin</firstname>
|
||||
<surname>Cowan</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Calton</firstname>
|
||||
<surname>Pu</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Dave</firstname>
|
||||
<surname>Maier</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<title>StackGuard; Automatic Adaptive Detection and Prevention of
|
||||
Buffer-Overflow Attacks</title>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry id="OpenBSD" xreflabel="6">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Todd</firstname>
|
||||
<surname>Miller</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Theo</firstname>
|
||||
<surname>de Raadt</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<title>strlcpy and strlcat -- consistent, safe string copy and
|
||||
concatenation.</title>
|
||||
</biblioentry>
|
||||
|
||||
</bibliography>
|
||||
|
||||
</part>
|
||||
|
||||
</book>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!--
|
||||
The FreeBSD Documentation Project
|
||||
|
||||
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/book.sgml,v 1.3 2000/10/06 15:36:45 phantom Exp $
|
||||
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/book.sgml,v 1.4 2000/11/06 10:22:11 murray Exp $
|
||||
-->
|
||||
|
||||
<!DOCTYPE BOOK PUBLIC "-//FreeBSD//DTD DocBook V3.1-Based Extension//EN" [
|
||||
|
@ -214,6 +214,410 @@
|
|||
|
||||
<para></para>
|
||||
</chapter>
|
||||
|
||||
<chapter id="secure-programming">
|
||||
<title>Secure Programming</title>
|
||||
|
||||
<para>This chapter was written by Murray Stokely.</para>
|
||||
|
||||
<sect1><title>Synopsis</title>
|
||||
|
||||
<para>This chapter describes some of the security issues that
|
||||
have plagued Unix programmers for decades and some of the new
|
||||
tools available to help programmers avoid writing exploitable
|
||||
code.</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="secure-philosophy"><title>Secure Design
|
||||
Methodology</title>
|
||||
|
||||
<para>Writing secure applications takes a very scrutinous and
|
||||
pessimistic outlook on life. Applications should be run with
|
||||
the principle of <quote>least privilege</quote> so that no
|
||||
process is ever running than more with the bare minimum access
|
||||
that it needs to accomplish its function. Previously tested
|
||||
code should be reused whenever possible to avoid common
|
||||
mistakes that others may have already fixed.</para>
|
||||
|
||||
<para>One of the pitfalls of the Unix environment is how easy it
|
||||
is to make assumptions about the sanity of the environment.
|
||||
Applications should never trust user input (in all its forms),
|
||||
system resources, inter-process communication, or the timing of
|
||||
events. Unix processes do not execute synchronously so logical
|
||||
operations are rarely atomic.</para>
|
||||
</sect1>
|
||||
|
||||
<sect1><title>Buffer Overflows</title>
|
||||
|
||||
<para>Buffer Overflows have been around since the very
|
||||
beginnings of the Von-Neuman <xref linkend="COD"> architecture.
|
||||
They first gained widespread notoriety in 1988 with the Moorse
|
||||
Internet worm. Unfortunately, the same basic attack remains
|
||||
effective today. Of the 17 CERT security advisories of 1999, 10
|
||||
of them were directly caused by buffer-overflow software bugs.
|
||||
By far the most common type of buffer overflow attack is based
|
||||
on corrupting the stack.</para>
|
||||
|
||||
<para>Most modern computer systems use a stack to pass arguments
|
||||
to procedures and to store local variables. A stack is a last
|
||||
in first out (LIFO) buffer in the high memory area of a process
|
||||
image. When a program invokes a function a new "stack frame" is
|
||||
created. This stack frame consists of the arguments passed to
|
||||
the function as well as a dynamic amount of local variable
|
||||
space. The "stack pointer" is a register that holds the current
|
||||
location of the top of the stack. Since this value is
|
||||
constantly changing as new values are pushed onto the top of the
|
||||
stack, many implementations also provide a "frame pointer" that
|
||||
is located near the beginning of a stack frame so that local
|
||||
variables can more easily be addressed relative to this
|
||||
value. <xref linkend="COD"> The return address for function
|
||||
calls is also stored on the stack, and this is the cause of
|
||||
stack-overflow exploits since overflowing a local variable in a
|
||||
function can overwrite the return address of that function,
|
||||
potentially allowing a malicious user to execute any code he or
|
||||
she wants.</para>
|
||||
|
||||
<para>Although stack-based attacks are by far the most common,
|
||||
it would also be possible to overrun the stack with a heap-based
|
||||
(malloc/free) attack.</para>
|
||||
|
||||
<para>The C programming language does not perform automatic
|
||||
bounds checking on arrays or pointers as many other languages
|
||||
do. In addition, the standard C library is filled with a
|
||||
handful of very dangerous functions.</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols=2>
|
||||
<tbody>
|
||||
<row><entry><function>strcpy</function>(char *dest, const char
|
||||
*src)</entry>
|
||||
<entry><simpara>May overflow the dest buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>strcat</function>(char *dest, const char
|
||||
*src)</entry>
|
||||
<entry><simpara>May overflow the dest buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>getwd</function>(char *buf)</entry>
|
||||
<entry><simpara>May overflow the buf buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>gets</function>(char *s)</entry>
|
||||
<entry><simpara>May overflow the s buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>[vf]scanf</function>(const char *format,
|
||||
...)</entry>
|
||||
<entry><simpara>May overflow its arguments.</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>realpath</function>(char *path, char
|
||||
resolved_path[])</entry>
|
||||
<entry><simpara>May overflow the path buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>[v]sprintf</function>(char *str, const char
|
||||
*format, ...)</entry>
|
||||
<entry><simpara>May overflow the str buffer.</simpara></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
|
||||
<sect2><title>Example Buffer Overflow</title>
|
||||
|
||||
<para>The following example code contains a buffer overflow
|
||||
designed to overwrite the return address and skip the
|
||||
instruction immediately following the function call. (Inspired
|
||||
by <xref linkend="Phrack">)</para>
|
||||
|
||||
<programlisting>
|
||||
#include <sgmltag>stdio.h</sgmltag>
|
||||
|
||||
void manipulate(char *buffer) {
|
||||
char newbuffer[80];
|
||||
strcpy(newbuffer,buffer);
|
||||
}
|
||||
|
||||
int main() {
|
||||
char ch,buffer[4096];
|
||||
int i=0;
|
||||
|
||||
while ((buffer[i++] = getchar()) != '\n') {};
|
||||
|
||||
i=1;
|
||||
manipulate(buffer);
|
||||
i=2;
|
||||
printf("The value of i is : %d\n",i);
|
||||
return 0;
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>Let us examine what the memory image of this process would
|
||||
look like if we were to input 160 spaces into our little program
|
||||
before hitting return.</para>
|
||||
|
||||
<para>[XXX figure here!]</para>
|
||||
|
||||
<para>Obviously more malicious input can be devised to execute
|
||||
actual compiled instructions (such as exec(/bin/sh)).</para>
|
||||
</sect2>
|
||||
|
||||
<sect2><title>Avoiding Buffer Overflows</title>
|
||||
|
||||
<para>The most straightforward solution to the problem of
|
||||
stack-overflows is to always use length restricted memory and
|
||||
string copy functions. <function>strncpy</function> and
|
||||
<function>strncat</function> are part of the standard C library.
|
||||
These functions accept a length value as a parameter which
|
||||
should be no larger than the size of the destination buffer.
|
||||
These functions will then copy up to `length' bytes from the
|
||||
source to the destination. However there are a number of
|
||||
problems with these functions. Neither function guarantees NUL
|
||||
termination if the size of the input buffer is as large as the
|
||||
destination. The length parameter is also used inconsistently
|
||||
between strncpy and strncat so it is easy for programmers to get
|
||||
confused as to their proper usage. There is also a significant
|
||||
performance loss compared to <function>strcpy</function> when
|
||||
copying a short string into a large buffer since
|
||||
<function>strncpy</function> NUL fills up the the size
|
||||
specified.</para>
|
||||
|
||||
<para>In OpenBSD, another memory copy implementation has been
|
||||
created to get around these problem. The
|
||||
<function>strlcpy</function> and <function>strlcat</function>
|
||||
functions guarantee that they will always null terminate the
|
||||
destination string when given a non-zero length argument. For
|
||||
more information about these functions see <xref
|
||||
linkend="OpenBSD">. The OpenBSD <function>strlcpy</function> and
|
||||
<function>strlcat</function> instructions have been in FreeBSD
|
||||
since 3.5.</para>
|
||||
|
||||
<sect3><title>Compiler based run-time bounds checking</title>
|
||||
|
||||
<para>Unfortunately there is still a very large assortment of
|
||||
code in public use which blindly copies memory around without
|
||||
using any of the bounded copy routines we just discussed.
|
||||
Fortunately, there is another solution. Several compiler
|
||||
add-ons and libraries exist to do Run-time bounds checking in
|
||||
C/C++.</para>
|
||||
|
||||
<para>StackGuard is one such add-on that is implemented as a
|
||||
small patch to the gcc code generator. From the StackGuard
|
||||
website, http://immunix.org/stackguard.html :
|
||||
<blockquote><para>"StackGuard detects and defeats stack
|
||||
smashing attacks by protecting the return address on the stack
|
||||
from being altered. StackGuard places a "canary" word next to
|
||||
the return address when a function is called. If the canary
|
||||
word has been altered when the function returns, then a stack
|
||||
smashing attack has been attempted, and the program responds
|
||||
by emitting an intruder alert into syslog, and then
|
||||
halts."</para></blockquote>
|
||||
|
||||
<blockquote><para>"StackGuard is implemented as a small patch
|
||||
to the gcc code generator, specifically the function_prolog()
|
||||
and function_epilog() routines. function_prolog() has been
|
||||
enhanced to lay down canaries on the stack when functions
|
||||
start, and function_epilog() checks canary integrity when the
|
||||
function exits. Any attempt at corrupting the return address
|
||||
is thus detected before the function
|
||||
returns."</para></blockquote>
|
||||
</para>
|
||||
|
||||
<para>Recompiling your application with StackGuard is an
|
||||
effective means of stopping most buffer-overflow attacks, but
|
||||
it can still be compromised.</para>
|
||||
|
||||
</sect3>
|
||||
|
||||
<sect3><title>Library based run-time bounds checking</title>
|
||||
|
||||
<para>Compiler-based mechanisms are completely useless for
|
||||
binary-only software for which you cannot recompile. For
|
||||
these situations there are a number of libraries which
|
||||
re-implement the unsafe functions of the C-library
|
||||
(<function>strcpy</function>, <function>fscanf</function>,
|
||||
<function>getwd</function>, etc..) and ensure that these
|
||||
functions can never write past the stack pointer.</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><simpara>libsafe</simpara></listitem>
|
||||
<listitem><simpara>libverify</simpara></listitem>
|
||||
<listitem><simpara>libparnoia</simpara></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Unfortunately these library-based defenses have a number
|
||||
of shortcomings. These libraries only protect against a very
|
||||
small set of security related issues and they neglect to fix
|
||||
the actual problem. These defenses may fail if the
|
||||
application was compiled with -fomit-frame-pointer. Also, the
|
||||
LD_PRELOAD and LD_LIBRARY_PATH environment variables can be
|
||||
overwritten/unset by the user.</para>
|
||||
</sect3>
|
||||
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1><title>SetUID issues</title>
|
||||
|
||||
<para>There are at least 6 different IDs associated with any
|
||||
given process. Because of this you have to be very careful with
|
||||
the access that your process has at any given time. In
|
||||
particular, all seteuid applications should give up their
|
||||
privileges as soon as it is no longer required.</para>
|
||||
|
||||
<para>The real user ID can only be changed by a superuser
|
||||
process. The <application>login</application> program sets this
|
||||
when a user initially logs in and it is seldom changed.</para>
|
||||
|
||||
<para>The effective user ID is set by the
|
||||
<function>exec()</function> functions if a program has its
|
||||
seteuid bit set. An application can call
|
||||
<function>seteuid()</function> at any time to set the effective
|
||||
user ID to either the real user ID or the saved set-user-ID.
|
||||
When the effective user ID is set by <function>exec()</function>
|
||||
functions, the previous value is saved in the saved set-user-ID.</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="chroot"><title>Limiting your program's environment</title>
|
||||
|
||||
<para>The traditional method of restricting access to a process
|
||||
is with the <function>chroot()</function> system call. This
|
||||
system call changes the root directory from which all other
|
||||
paths are referenced for a process and any child processes. For
|
||||
this call to succeed the process must have execute (search)
|
||||
permission on the directory being referenced. The new
|
||||
environment does not actually take affect until you
|
||||
<function>chdir()</function> into your new environment. It
|
||||
should also be noted that a process can easily break out of a
|
||||
chroot environment if it has root privilege. This could be
|
||||
accomplished by creating device nodes to read kernel memory,
|
||||
attaching a debugger to a process outside of the jail, or in
|
||||
many other creative ways.</para>
|
||||
|
||||
<para>The behavior of the <function>chroot()</function> system
|
||||
call can be controlled somewhat with the
|
||||
kern.chroot_allow_open_directories <command>sysctl</command>
|
||||
variable. When this value is set to 0,
|
||||
<function>chroot()</function> will fail with EPERM if there are
|
||||
any directories open. If set to the default value of 1, then
|
||||
<function>chroot()</function> will fail with EPERM if there are
|
||||
any directories open and the process is already subject to a
|
||||
<function>chroot()</function> call. For any other value, the
|
||||
check for open directories will be bypassed completely.</para>
|
||||
|
||||
<sect2><title>FreeBSD's jail functionality</title>
|
||||
|
||||
<para>The concept of a Jail extends upon the
|
||||
<function>chroot()</function> by limiting the powers of the
|
||||
superuser to create a true `virtual server'. Once a prison is
|
||||
setup all network communication must take place through the
|
||||
specified IP address, and the power of "root privilege" in this
|
||||
jail is severely constrained.</para>
|
||||
|
||||
<para>While in a prison, any tests of superuser power within the
|
||||
kernel using the <function>suser()</function> call will fail.
|
||||
However, some calls to <function>suser()</function> have been
|
||||
changed to a new interface <function>suser_xxx()</function>.
|
||||
This function is responsible for recognizing or denying access
|
||||
to superuser power for imprisoned processes.</para>
|
||||
|
||||
<para>A superuser process within a jailed environment has the
|
||||
power to : </para>
|
||||
<itemizedlist>
|
||||
<listitem><simpara>Manipulate credential with
|
||||
<function>setuid</function>, <function>seteuid</function>,
|
||||
<function>setgid</function>, <function>setegid</function>,
|
||||
<function>setgroups</function>, <function>setreuid</function>,
|
||||
<function>setregid</function>, <function>setlogin</function></simpara></listitem>
|
||||
<listitem><simpara>Set resource limits with <function>setrlimit</function></simpara></listitem>
|
||||
<listitem><simpara>Modify some sysctl nodes
|
||||
(kern.hostname)</simpara></listitem>
|
||||
<listitem><simpara><function>chroot()</function></simpara></listitem>
|
||||
<listitem><simpara>Set flags on a vnode:
|
||||
<function>chflags</function>,
|
||||
<function>fchflags</function></simpara></listitem>
|
||||
<listitem><simpara>Set attributes of a vnode such as file
|
||||
permission, owner, group, size, access time, and modification
|
||||
time.</simpara></listitem>
|
||||
<listitem><simpara>Bind to privileged ports in the Internet
|
||||
domain (ports < 1024)</simpara></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para><function>Jail</function> is a very useful tool for
|
||||
running applications in a secure environment but it does have
|
||||
some shortcomings. Currently, the IPC mechanisms have not been
|
||||
converted to the <function>suser_xxx</function> so applications
|
||||
such as MySQL can not be run within a jail. Superuser access
|
||||
may have a very limited meaning within a jail, but there is
|
||||
no way to specify exactly what "very limited" means.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2><title>POSIX.1e Process Capabilities</title>
|
||||
|
||||
<para>Posix has released a working draft that adds event
|
||||
auditing, access control lists, fine grained privileges,
|
||||
information labeling, and mandatory access control.</para>
|
||||
<para>This is a work in progress and is the focus of the <ulink
|
||||
url="http://www.trustedbsd.org">TrustedBSD</ulink> project. Some
|
||||
of the initial work has been committed to FreeBSD-current
|
||||
(cap_set_proc(3)).</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1><title>Trust</title>
|
||||
|
||||
<para>An application should never assume that anything about the
|
||||
users environment is sane. This includes (but is certainly not
|
||||
limited to) : user input, signals, environment variables,
|
||||
resources, IPC, mmaps, the file system working directory, file
|
||||
descriptors, the # of open files, etc.</para>
|
||||
|
||||
<para>You should never assume that you can catch all forms of
|
||||
invalid input that a user might supply. Instead, your
|
||||
application should use positive filtering to only allow a
|
||||
specific subset of inputs that you deem safe. Improper data
|
||||
validation has been the cause of many exploits, especially with
|
||||
CGI scripts on the world wide web. For filenames you need to be
|
||||
extra careful about paths ("../", "/"), symbolic links, and
|
||||
shell escape characters.</para>
|
||||
|
||||
<para>Perl has a really cool feature called "Taint" mode which
|
||||
can be used to prevent scripts for using data derived outside
|
||||
the program in an unsafe way. This mode will check command line
|
||||
arguments, environment variables, locale information, the
|
||||
results of certain syscalls (<function>readdir()</function>,
|
||||
<function>readlink()</function>,
|
||||
<function>getpwxxx()</function>, and all file input.</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1><title>Race Conditions</title>
|
||||
|
||||
<para>A race condition is anomalous behavior caused by the
|
||||
unexpected dependence on the relative timing of events. In
|
||||
other words, a programmer incorrectly assumed that a particular
|
||||
event would always happen before another.</para>
|
||||
|
||||
<para>Some of the common causes of race conditions are signals,
|
||||
access checks, and file opens. Signals are asynchronous events
|
||||
by nature so special care must be taken in dealing with them.
|
||||
Checking access with <function>access(2)</function> then
|
||||
<function>open(2)</function> is clearly non-atomic. Users can
|
||||
move files in between the two calls. Instead, privileged
|
||||
applications should <function>seteuid()</function> and then call
|
||||
<function>open()</function> directly. Along the same lines, an
|
||||
application should always set a proper umask before
|
||||
<function>open()</function> to obviate the need for spurious
|
||||
<function>chmod()</function> calls.</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
</part>
|
||||
|
||||
<part id="kernel">
|
||||
|
@ -976,4 +1380,131 @@ DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);
|
|||
</chapter>
|
||||
</part>
|
||||
|
||||
<part id="appendices">
|
||||
<title>Appendices</title>
|
||||
|
||||
<bibliography>
|
||||
|
||||
<biblioentry id="COD" xreflabel="1">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Dave</firstname>
|
||||
<othername role="MI">A</othername>
|
||||
<surname>Patterson</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>John</firstname>
|
||||
<othername role="MI">L</othername>
|
||||
<surname>Hennessy</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<copyright><year>1998</year><holder>Morgan Kaufmann Publishers,
|
||||
Inc.</holder></copyright>
|
||||
<isbn>1-55860-428-6</isbn>
|
||||
<publisher>
|
||||
<publishername>Morgan Kaufmann Publishers, Inc.</publishername>
|
||||
</publisher>
|
||||
<title>Computer Organization and Design</title>
|
||||
<subtitle>The Hardware / Software Interface</subtitle>
|
||||
<pagenums>1-2</pagenums>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry xreflabel="2">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>W.</firstname>
|
||||
<othername role="Middle">Richard</othername>
|
||||
<surname>Stevens</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<copyright><year>1993</year><holder>Addison Wesley Longman,
|
||||
Inc.</holder></copyright>
|
||||
<isbn>0-201-56317-7</isbn>
|
||||
<publisher>
|
||||
<publishername>Addison Wesley Longman, Inc.</publishername>
|
||||
</publisher>
|
||||
<title>Advanced Programming in the Unix Environment</title>
|
||||
<pagenums>1-2</pagenums>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry xreflabel="3">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Marshall</firstname>
|
||||
<othername role="Middle">Kirk</othername>
|
||||
<surname>McKusick</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Keith</firstname>
|
||||
<surname>Bostic</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Michael</firstname>
|
||||
<othername role="MI">J</othername>
|
||||
<surname>Karels</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>John</firstname>
|
||||
<othername role="MI">S</othername>
|
||||
<surname>Quarterman</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<copyright><year>1996</year><holder>Addison-Wesley Publishing Company,
|
||||
Inc.</holder></copyright>
|
||||
<isbn>0-201-54979-4</isbn>
|
||||
<publisher>
|
||||
<publishername>Addison-Wesley Publishing Company, Inc.</publishername>
|
||||
</publisher>
|
||||
<title>The Design and Implementation of the 4.4 BSD Operating System</title>
|
||||
<pagenums>1-2</pagenums>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry id="Phrack" xreflabel="4">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Aleph</firstname>
|
||||
<surname>One</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<title>Phrack 49; "Smashing the Stack for Fun and Profit"</title>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry id="StackGuard" xreflabel="5">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Chrispin</firstname>
|
||||
<surname>Cowan</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Calton</firstname>
|
||||
<surname>Pu</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Dave</firstname>
|
||||
<surname>Maier</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<title>StackGuard; Automatic Adaptive Detection and Prevention of
|
||||
Buffer-Overflow Attacks</title>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry id="OpenBSD" xreflabel="6">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Todd</firstname>
|
||||
<surname>Miller</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Theo</firstname>
|
||||
<surname>de Raadt</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<title>strlcpy and strlcat -- consistent, safe string copy and
|
||||
concatenation.</title>
|
||||
</biblioentry>
|
||||
|
||||
</bibliography>
|
||||
|
||||
</part>
|
||||
|
||||
</book>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!--
|
||||
The FreeBSD Documentation Project
|
||||
|
||||
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/book.sgml,v 1.3 2000/10/06 15:36:45 phantom Exp $
|
||||
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/book.sgml,v 1.4 2000/11/06 10:22:11 murray Exp $
|
||||
-->
|
||||
|
||||
<!DOCTYPE BOOK PUBLIC "-//FreeBSD//DTD DocBook V3.1-Based Extension//EN" [
|
||||
|
@ -214,6 +214,410 @@
|
|||
|
||||
<para></para>
|
||||
</chapter>
|
||||
|
||||
<chapter id="secure-programming">
|
||||
<title>Secure Programming</title>
|
||||
|
||||
<para>This chapter was written by Murray Stokely.</para>
|
||||
|
||||
<sect1><title>Synopsis</title>
|
||||
|
||||
<para>This chapter describes some of the security issues that
|
||||
have plagued Unix programmers for decades and some of the new
|
||||
tools available to help programmers avoid writing exploitable
|
||||
code.</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="secure-philosophy"><title>Secure Design
|
||||
Methodology</title>
|
||||
|
||||
<para>Writing secure applications takes a very scrutinous and
|
||||
pessimistic outlook on life. Applications should be run with
|
||||
the principle of <quote>least privilege</quote> so that no
|
||||
process is ever running than more with the bare minimum access
|
||||
that it needs to accomplish its function. Previously tested
|
||||
code should be reused whenever possible to avoid common
|
||||
mistakes that others may have already fixed.</para>
|
||||
|
||||
<para>One of the pitfalls of the Unix environment is how easy it
|
||||
is to make assumptions about the sanity of the environment.
|
||||
Applications should never trust user input (in all its forms),
|
||||
system resources, inter-process communication, or the timing of
|
||||
events. Unix processes do not execute synchronously so logical
|
||||
operations are rarely atomic.</para>
|
||||
</sect1>
|
||||
|
||||
<sect1><title>Buffer Overflows</title>
|
||||
|
||||
<para>Buffer Overflows have been around since the very
|
||||
beginnings of the Von-Neuman <xref linkend="COD"> architecture.
|
||||
They first gained widespread notoriety in 1988 with the Moorse
|
||||
Internet worm. Unfortunately, the same basic attack remains
|
||||
effective today. Of the 17 CERT security advisories of 1999, 10
|
||||
of them were directly caused by buffer-overflow software bugs.
|
||||
By far the most common type of buffer overflow attack is based
|
||||
on corrupting the stack.</para>
|
||||
|
||||
<para>Most modern computer systems use a stack to pass arguments
|
||||
to procedures and to store local variables. A stack is a last
|
||||
in first out (LIFO) buffer in the high memory area of a process
|
||||
image. When a program invokes a function a new "stack frame" is
|
||||
created. This stack frame consists of the arguments passed to
|
||||
the function as well as a dynamic amount of local variable
|
||||
space. The "stack pointer" is a register that holds the current
|
||||
location of the top of the stack. Since this value is
|
||||
constantly changing as new values are pushed onto the top of the
|
||||
stack, many implementations also provide a "frame pointer" that
|
||||
is located near the beginning of a stack frame so that local
|
||||
variables can more easily be addressed relative to this
|
||||
value. <xref linkend="COD"> The return address for function
|
||||
calls is also stored on the stack, and this is the cause of
|
||||
stack-overflow exploits since overflowing a local variable in a
|
||||
function can overwrite the return address of that function,
|
||||
potentially allowing a malicious user to execute any code he or
|
||||
she wants.</para>
|
||||
|
||||
<para>Although stack-based attacks are by far the most common,
|
||||
it would also be possible to overrun the stack with a heap-based
|
||||
(malloc/free) attack.</para>
|
||||
|
||||
<para>The C programming language does not perform automatic
|
||||
bounds checking on arrays or pointers as many other languages
|
||||
do. In addition, the standard C library is filled with a
|
||||
handful of very dangerous functions.</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols=2>
|
||||
<tbody>
|
||||
<row><entry><function>strcpy</function>(char *dest, const char
|
||||
*src)</entry>
|
||||
<entry><simpara>May overflow the dest buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>strcat</function>(char *dest, const char
|
||||
*src)</entry>
|
||||
<entry><simpara>May overflow the dest buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>getwd</function>(char *buf)</entry>
|
||||
<entry><simpara>May overflow the buf buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>gets</function>(char *s)</entry>
|
||||
<entry><simpara>May overflow the s buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>[vf]scanf</function>(const char *format,
|
||||
...)</entry>
|
||||
<entry><simpara>May overflow its arguments.</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>realpath</function>(char *path, char
|
||||
resolved_path[])</entry>
|
||||
<entry><simpara>May overflow the path buffer</simpara></entry>
|
||||
</row>
|
||||
|
||||
<row><entry><function>[v]sprintf</function>(char *str, const char
|
||||
*format, ...)</entry>
|
||||
<entry><simpara>May overflow the str buffer.</simpara></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
|
||||
<sect2><title>Example Buffer Overflow</title>
|
||||
|
||||
<para>The following example code contains a buffer overflow
|
||||
designed to overwrite the return address and skip the
|
||||
instruction immediately following the function call. (Inspired
|
||||
by <xref linkend="Phrack">)</para>
|
||||
|
||||
<programlisting>
|
||||
#include <sgmltag>stdio.h</sgmltag>
|
||||
|
||||
void manipulate(char *buffer) {
|
||||
char newbuffer[80];
|
||||
strcpy(newbuffer,buffer);
|
||||
}
|
||||
|
||||
int main() {
|
||||
char ch,buffer[4096];
|
||||
int i=0;
|
||||
|
||||
while ((buffer[i++] = getchar()) != '\n') {};
|
||||
|
||||
i=1;
|
||||
manipulate(buffer);
|
||||
i=2;
|
||||
printf("The value of i is : %d\n",i);
|
||||
return 0;
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>Let us examine what the memory image of this process would
|
||||
look like if we were to input 160 spaces into our little program
|
||||
before hitting return.</para>
|
||||
|
||||
<para>[XXX figure here!]</para>
|
||||
|
||||
<para>Obviously more malicious input can be devised to execute
|
||||
actual compiled instructions (such as exec(/bin/sh)).</para>
|
||||
</sect2>
|
||||
|
||||
<sect2><title>Avoiding Buffer Overflows</title>
|
||||
|
||||
<para>The most straightforward solution to the problem of
|
||||
stack-overflows is to always use length restricted memory and
|
||||
string copy functions. <function>strncpy</function> and
|
||||
<function>strncat</function> are part of the standard C library.
|
||||
These functions accept a length value as a parameter which
|
||||
should be no larger than the size of the destination buffer.
|
||||
These functions will then copy up to `length' bytes from the
|
||||
source to the destination. However there are a number of
|
||||
problems with these functions. Neither function guarantees NUL
|
||||
termination if the size of the input buffer is as large as the
|
||||
destination. The length parameter is also used inconsistently
|
||||
between strncpy and strncat so it is easy for programmers to get
|
||||
confused as to their proper usage. There is also a significant
|
||||
performance loss compared to <function>strcpy</function> when
|
||||
copying a short string into a large buffer since
|
||||
<function>strncpy</function> NUL fills up the the size
|
||||
specified.</para>
|
||||
|
||||
<para>In OpenBSD, another memory copy implementation has been
|
||||
created to get around these problem. The
|
||||
<function>strlcpy</function> and <function>strlcat</function>
|
||||
functions guarantee that they will always null terminate the
|
||||
destination string when given a non-zero length argument. For
|
||||
more information about these functions see <xref
|
||||
linkend="OpenBSD">. The OpenBSD <function>strlcpy</function> and
|
||||
<function>strlcat</function> instructions have been in FreeBSD
|
||||
since 3.5.</para>
|
||||
|
||||
<sect3><title>Compiler based run-time bounds checking</title>
|
||||
|
||||
<para>Unfortunately there is still a very large assortment of
|
||||
code in public use which blindly copies memory around without
|
||||
using any of the bounded copy routines we just discussed.
|
||||
Fortunately, there is another solution. Several compiler
|
||||
add-ons and libraries exist to do Run-time bounds checking in
|
||||
C/C++.</para>
|
||||
|
||||
<para>StackGuard is one such add-on that is implemented as a
|
||||
small patch to the gcc code generator. From the StackGuard
|
||||
website, http://immunix.org/stackguard.html :
|
||||
<blockquote><para>"StackGuard detects and defeats stack
|
||||
smashing attacks by protecting the return address on the stack
|
||||
from being altered. StackGuard places a "canary" word next to
|
||||
the return address when a function is called. If the canary
|
||||
word has been altered when the function returns, then a stack
|
||||
smashing attack has been attempted, and the program responds
|
||||
by emitting an intruder alert into syslog, and then
|
||||
halts."</para></blockquote>
|
||||
|
||||
<blockquote><para>"StackGuard is implemented as a small patch
|
||||
to the gcc code generator, specifically the function_prolog()
|
||||
and function_epilog() routines. function_prolog() has been
|
||||
enhanced to lay down canaries on the stack when functions
|
||||
start, and function_epilog() checks canary integrity when the
|
||||
function exits. Any attempt at corrupting the return address
|
||||
is thus detected before the function
|
||||
returns."</para></blockquote>
|
||||
</para>
|
||||
|
||||
<para>Recompiling your application with StackGuard is an
|
||||
effective means of stopping most buffer-overflow attacks, but
|
||||
it can still be compromised.</para>
|
||||
|
||||
</sect3>
|
||||
|
||||
<sect3><title>Library based run-time bounds checking</title>
|
||||
|
||||
<para>Compiler-based mechanisms are completely useless for
|
||||
binary-only software for which you cannot recompile. For
|
||||
these situations there are a number of libraries which
|
||||
re-implement the unsafe functions of the C-library
|
||||
(<function>strcpy</function>, <function>fscanf</function>,
|
||||
<function>getwd</function>, etc..) and ensure that these
|
||||
functions can never write past the stack pointer.</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><simpara>libsafe</simpara></listitem>
|
||||
<listitem><simpara>libverify</simpara></listitem>
|
||||
<listitem><simpara>libparnoia</simpara></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Unfortunately these library-based defenses have a number
|
||||
of shortcomings. These libraries only protect against a very
|
||||
small set of security related issues and they neglect to fix
|
||||
the actual problem. These defenses may fail if the
|
||||
application was compiled with -fomit-frame-pointer. Also, the
|
||||
LD_PRELOAD and LD_LIBRARY_PATH environment variables can be
|
||||
overwritten/unset by the user.</para>
|
||||
</sect3>
|
||||
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1><title>SetUID issues</title>
|
||||
|
||||
<para>There are at least 6 different IDs associated with any
|
||||
given process. Because of this you have to be very careful with
|
||||
the access that your process has at any given time. In
|
||||
particular, all seteuid applications should give up their
|
||||
privileges as soon as it is no longer required.</para>
|
||||
|
||||
<para>The real user ID can only be changed by a superuser
|
||||
process. The <application>login</application> program sets this
|
||||
when a user initially logs in and it is seldom changed.</para>
|
||||
|
||||
<para>The effective user ID is set by the
|
||||
<function>exec()</function> functions if a program has its
|
||||
seteuid bit set. An application can call
|
||||
<function>seteuid()</function> at any time to set the effective
|
||||
user ID to either the real user ID or the saved set-user-ID.
|
||||
When the effective user ID is set by <function>exec()</function>
|
||||
functions, the previous value is saved in the saved set-user-ID.</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="chroot"><title>Limiting your program's environment</title>
|
||||
|
||||
<para>The traditional method of restricting access to a process
|
||||
is with the <function>chroot()</function> system call. This
|
||||
system call changes the root directory from which all other
|
||||
paths are referenced for a process and any child processes. For
|
||||
this call to succeed the process must have execute (search)
|
||||
permission on the directory being referenced. The new
|
||||
environment does not actually take affect until you
|
||||
<function>chdir()</function> into your new environment. It
|
||||
should also be noted that a process can easily break out of a
|
||||
chroot environment if it has root privilege. This could be
|
||||
accomplished by creating device nodes to read kernel memory,
|
||||
attaching a debugger to a process outside of the jail, or in
|
||||
many other creative ways.</para>
|
||||
|
||||
<para>The behavior of the <function>chroot()</function> system
|
||||
call can be controlled somewhat with the
|
||||
kern.chroot_allow_open_directories <command>sysctl</command>
|
||||
variable. When this value is set to 0,
|
||||
<function>chroot()</function> will fail with EPERM if there are
|
||||
any directories open. If set to the default value of 1, then
|
||||
<function>chroot()</function> will fail with EPERM if there are
|
||||
any directories open and the process is already subject to a
|
||||
<function>chroot()</function> call. For any other value, the
|
||||
check for open directories will be bypassed completely.</para>
|
||||
|
||||
<sect2><title>FreeBSD's jail functionality</title>
|
||||
|
||||
<para>The concept of a Jail extends upon the
|
||||
<function>chroot()</function> by limiting the powers of the
|
||||
superuser to create a true `virtual server'. Once a prison is
|
||||
setup all network communication must take place through the
|
||||
specified IP address, and the power of "root privilege" in this
|
||||
jail is severely constrained.</para>
|
||||
|
||||
<para>While in a prison, any tests of superuser power within the
|
||||
kernel using the <function>suser()</function> call will fail.
|
||||
However, some calls to <function>suser()</function> have been
|
||||
changed to a new interface <function>suser_xxx()</function>.
|
||||
This function is responsible for recognizing or denying access
|
||||
to superuser power for imprisoned processes.</para>
|
||||
|
||||
<para>A superuser process within a jailed environment has the
|
||||
power to : </para>
|
||||
<itemizedlist>
|
||||
<listitem><simpara>Manipulate credential with
|
||||
<function>setuid</function>, <function>seteuid</function>,
|
||||
<function>setgid</function>, <function>setegid</function>,
|
||||
<function>setgroups</function>, <function>setreuid</function>,
|
||||
<function>setregid</function>, <function>setlogin</function></simpara></listitem>
|
||||
<listitem><simpara>Set resource limits with <function>setrlimit</function></simpara></listitem>
|
||||
<listitem><simpara>Modify some sysctl nodes
|
||||
(kern.hostname)</simpara></listitem>
|
||||
<listitem><simpara><function>chroot()</function></simpara></listitem>
|
||||
<listitem><simpara>Set flags on a vnode:
|
||||
<function>chflags</function>,
|
||||
<function>fchflags</function></simpara></listitem>
|
||||
<listitem><simpara>Set attributes of a vnode such as file
|
||||
permission, owner, group, size, access time, and modification
|
||||
time.</simpara></listitem>
|
||||
<listitem><simpara>Bind to privileged ports in the Internet
|
||||
domain (ports < 1024)</simpara></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para><function>Jail</function> is a very useful tool for
|
||||
running applications in a secure environment but it does have
|
||||
some shortcomings. Currently, the IPC mechanisms have not been
|
||||
converted to the <function>suser_xxx</function> so applications
|
||||
such as MySQL can not be run within a jail. Superuser access
|
||||
may have a very limited meaning within a jail, but there is
|
||||
no way to specify exactly what "very limited" means.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2><title>POSIX.1e Process Capabilities</title>
|
||||
|
||||
<para>Posix has released a working draft that adds event
|
||||
auditing, access control lists, fine grained privileges,
|
||||
information labeling, and mandatory access control.</para>
|
||||
<para>This is a work in progress and is the focus of the <ulink
|
||||
url="http://www.trustedbsd.org">TrustedBSD</ulink> project. Some
|
||||
of the initial work has been committed to FreeBSD-current
|
||||
(cap_set_proc(3)).</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1><title>Trust</title>
|
||||
|
||||
<para>An application should never assume that anything about the
|
||||
users environment is sane. This includes (but is certainly not
|
||||
limited to) : user input, signals, environment variables,
|
||||
resources, IPC, mmaps, the file system working directory, file
|
||||
descriptors, the # of open files, etc.</para>
|
||||
|
||||
<para>You should never assume that you can catch all forms of
|
||||
invalid input that a user might supply. Instead, your
|
||||
application should use positive filtering to only allow a
|
||||
specific subset of inputs that you deem safe. Improper data
|
||||
validation has been the cause of many exploits, especially with
|
||||
CGI scripts on the world wide web. For filenames you need to be
|
||||
extra careful about paths ("../", "/"), symbolic links, and
|
||||
shell escape characters.</para>
|
||||
|
||||
<para>Perl has a really cool feature called "Taint" mode which
|
||||
can be used to prevent scripts for using data derived outside
|
||||
the program in an unsafe way. This mode will check command line
|
||||
arguments, environment variables, locale information, the
|
||||
results of certain syscalls (<function>readdir()</function>,
|
||||
<function>readlink()</function>,
|
||||
<function>getpwxxx()</function>, and all file input.</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1><title>Race Conditions</title>
|
||||
|
||||
<para>A race condition is anomalous behavior caused by the
|
||||
unexpected dependence on the relative timing of events. In
|
||||
other words, a programmer incorrectly assumed that a particular
|
||||
event would always happen before another.</para>
|
||||
|
||||
<para>Some of the common causes of race conditions are signals,
|
||||
access checks, and file opens. Signals are asynchronous events
|
||||
by nature so special care must be taken in dealing with them.
|
||||
Checking access with <function>access(2)</function> then
|
||||
<function>open(2)</function> is clearly non-atomic. Users can
|
||||
move files in between the two calls. Instead, privileged
|
||||
applications should <function>seteuid()</function> and then call
|
||||
<function>open()</function> directly. Along the same lines, an
|
||||
application should always set a proper umask before
|
||||
<function>open()</function> to obviate the need for spurious
|
||||
<function>chmod()</function> calls.</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
</part>
|
||||
|
||||
<part id="kernel">
|
||||
|
@ -976,4 +1380,131 @@ DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);
|
|||
</chapter>
|
||||
</part>
|
||||
|
||||
<part id="appendices">
|
||||
<title>Appendices</title>
|
||||
|
||||
<bibliography>
|
||||
|
||||
<biblioentry id="COD" xreflabel="1">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Dave</firstname>
|
||||
<othername role="MI">A</othername>
|
||||
<surname>Patterson</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>John</firstname>
|
||||
<othername role="MI">L</othername>
|
||||
<surname>Hennessy</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<copyright><year>1998</year><holder>Morgan Kaufmann Publishers,
|
||||
Inc.</holder></copyright>
|
||||
<isbn>1-55860-428-6</isbn>
|
||||
<publisher>
|
||||
<publishername>Morgan Kaufmann Publishers, Inc.</publishername>
|
||||
</publisher>
|
||||
<title>Computer Organization and Design</title>
|
||||
<subtitle>The Hardware / Software Interface</subtitle>
|
||||
<pagenums>1-2</pagenums>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry xreflabel="2">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>W.</firstname>
|
||||
<othername role="Middle">Richard</othername>
|
||||
<surname>Stevens</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<copyright><year>1993</year><holder>Addison Wesley Longman,
|
||||
Inc.</holder></copyright>
|
||||
<isbn>0-201-56317-7</isbn>
|
||||
<publisher>
|
||||
<publishername>Addison Wesley Longman, Inc.</publishername>
|
||||
</publisher>
|
||||
<title>Advanced Programming in the Unix Environment</title>
|
||||
<pagenums>1-2</pagenums>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry xreflabel="3">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Marshall</firstname>
|
||||
<othername role="Middle">Kirk</othername>
|
||||
<surname>McKusick</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Keith</firstname>
|
||||
<surname>Bostic</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Michael</firstname>
|
||||
<othername role="MI">J</othername>
|
||||
<surname>Karels</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>John</firstname>
|
||||
<othername role="MI">S</othername>
|
||||
<surname>Quarterman</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<copyright><year>1996</year><holder>Addison-Wesley Publishing Company,
|
||||
Inc.</holder></copyright>
|
||||
<isbn>0-201-54979-4</isbn>
|
||||
<publisher>
|
||||
<publishername>Addison-Wesley Publishing Company, Inc.</publishername>
|
||||
</publisher>
|
||||
<title>The Design and Implementation of the 4.4 BSD Operating System</title>
|
||||
<pagenums>1-2</pagenums>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry id="Phrack" xreflabel="4">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Aleph</firstname>
|
||||
<surname>One</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<title>Phrack 49; "Smashing the Stack for Fun and Profit"</title>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry id="StackGuard" xreflabel="5">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Chrispin</firstname>
|
||||
<surname>Cowan</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Calton</firstname>
|
||||
<surname>Pu</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Dave</firstname>
|
||||
<surname>Maier</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<title>StackGuard; Automatic Adaptive Detection and Prevention of
|
||||
Buffer-Overflow Attacks</title>
|
||||
</biblioentry>
|
||||
|
||||
<biblioentry id="OpenBSD" xreflabel="6">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Todd</firstname>
|
||||
<surname>Miller</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Theo</firstname>
|
||||
<surname>de Raadt</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<title>strlcpy and strlcat -- consistent, safe string copy and
|
||||
concatenation.</title>
|
||||
</biblioentry>
|
||||
|
||||
</bibliography>
|
||||
|
||||
</part>
|
||||
|
||||
</book>
|
||||
|
|
Loading…
Reference in a new issue