diff --git a/en_US.ISO8859-1/books/arch-handbook/book.sgml b/en_US.ISO8859-1/books/arch-handbook/book.sgml index 7f0c2f3b9a..af9c07821e 100644 --- a/en_US.ISO8859-1/books/arch-handbook/book.sgml +++ b/en_US.ISO8859-1/books/arch-handbook/book.sgml @@ -1,1503 +1,1101 @@ %bookinfo; %chapters; ]> FreeBSD Developers' Handbook The FreeBSD Documentation Project
doc@FreeBSD.org
August 2000 2000 The FreeBSD Documentation Project &bookinfo.legalnotice; Welcome to the Developers' Handbook.
Introduction Developing on FreeBSD This will need to discuss FreeBSD as a development platform, the vision of BSD, architectural overview, layout of /usr/src, history, etc. Thank you for considering FreeBSD as your development platform! We hope it will not let you down. The BSD Vision Architectural Overview The Layout of /usr/src The complete source code to FreeBSD is available from our public CVS repository. The source code is normally installed in /usr/src which contains the following subdirectories. Directory Description bin/ Source for files in /bin contrib/ Source for files from contribued software. crypto/ DES source etc/ Source for files in /etc games/ Source for files in /usr/games gnu/ Utilities covered by the GNU Public License include/ Source for files in /usr/include kerberosIV/ Source for Kerbereros version IV kerberos5/ Source for Kerbereros version 5 lib/ Source for files in /usr/lib libexec/ Source for files in /usr/libexec release/ Files required to produce a FreeBSD release sbin/ Source for files in /sbin secure/ FreeSec sources share/ Source for files in /sbin sys/ Kernel source files tools/ Tools used for maintenance and testing of FreeBSD usr.bin/ Source for files in /usr/bin usr.sbin/ Source for files in /usr/sbin Basics &chap.tools; + &chap.secure; - - Secure Programming - - This chapter was written by Murray Stokely. - - Synopsis - - 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. - - - Secure Design - Methodology - - Writing secure applications takes a very scrutinous and - pessimistic outlook on life. Applications should be run with - the principle of least privilege 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. - - 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. - - - Buffer Overflows - - Buffer Overflows have been around since the very - beginnings of the Von-Neuman 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. - - 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. 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. - - 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. - - 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. - - - - - strcpy(char *dest, const char - *src) - May overflow the dest buffer - - - strcat(char *dest, const char - *src) - May overflow the dest buffer - - - getwd(char *buf) - May overflow the buf buffer - - - gets(char *s) - May overflow the s buffer - - - [vf]scanf(const char *format, - ...) - May overflow its arguments. - - - realpath(char *path, char - resolved_path[]) - May overflow the path buffer - - - [v]sprintf(char *str, const char - *format, ...) - May overflow the str buffer. - - - - - - Example Buffer Overflow - - 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 ) - - -#include stdio.h - -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; -} - - - 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. - - [XXX figure here!] - - Obviously more malicious input can be devised to execute - actual compiled instructions (such as exec(/bin/sh)). - - - Avoiding Buffer Overflows - - The most straightforward solution to the problem of - stack-overflows is to always use length restricted memory and - string copy functions. strncpy and - strncat 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 strcpy when - copying a short string into a large buffer since - strncpy NUL fills up the the size - specified. - - In OpenBSD, another memory copy implementation has been - created to get around these problem. The - strlcpy and strlcat - 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 . The OpenBSD strlcpy and - strlcat instructions have been in FreeBSD - since 3.5. - - Compiler based run-time bounds checking - - 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++. - - 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 : -
"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."
- -
"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."
-
- - Recompiling your application with StackGuard is an - effective means of stopping most buffer-overflow attacks, but - it can still be compromised. - -
- - Library based run-time bounds checking - - 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 - (strcpy, fscanf, - getwd, etc..) and ensure that these - functions can never write past the stack pointer. - - - libsafe - libverify - libparnoia - - - 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. - - -
-
- - SetUID issues - - 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. - - The real user ID can only be changed by a superuser - process. The login program sets this - when a user initially logs in and it is seldom changed. - - The effective user ID is set by the - exec() functions if a program has its - seteuid bit set. An application can call - seteuid() 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 exec() - functions, the previous value is saved in the saved set-user-ID. - - - - Limiting your program's environment - - The traditional method of restricting access to a process - is with the chroot() 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 - chdir() 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. - - The behavior of the chroot() system - call can be controlled somewhat with the - kern.chroot_allow_open_directories sysctl - variable. When this value is set to 0, - chroot() will fail with EPERM if there are - any directories open. If set to the default value of 1, then - chroot() will fail with EPERM if there are - any directories open and the process is already subject to a - chroot() call. For any other value, the - check for open directories will be bypassed completely. - - FreeBSD's jail functionality - - The concept of a Jail extends upon the - chroot() 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. - - While in a prison, any tests of superuser power within the - kernel using the suser() call will fail. - However, some calls to suser() have been - changed to a new interface suser_xxx(). - This function is responsible for recognizing or denying access - to superuser power for imprisoned processes. - - A superuser process within a jailed environment has the - power to : - - Manipulate credential with - setuid, seteuid, - setgid, setegid, - setgroups, setreuid, - setregid, setlogin - Set resource limits with setrlimit - Modify some sysctl nodes - (kern.hostname) - chroot() - Set flags on a vnode: - chflags, - fchflags - Set attributes of a vnode such as file - permission, owner, group, size, access time, and modification - time. - Bind to privileged ports in the Internet - domain (ports < 1024) - - - Jail 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 suser_xxx 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. - - - POSIX.1e Process Capabilities - - Posix has released a working draft that adds event - auditing, access control lists, fine grained privileges, - information labeling, and mandatory access control. - This is a work in progress and is the focus of the TrustedBSD project. Some - of the initial work has been committed to FreeBSD-current - (cap_set_proc(3)). - - - - - - Trust - - 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. - - 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. - - 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 (readdir(), - readlink(), - getpwxxx(), and all file input. - - - - Race Conditions - - 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. - - 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 access(2) then - open(2) is clearly non-atomic. Users can - move files in between the two calls. Instead, privileged - applications should seteuid() and then call - open() directly. Along the same lines, an - application should always set a proper umask before - open() to obviate the need for spurious - chmod() calls. - - - -
Kernel History of the Unix Kernel Some history of the Unix/BSD kernel, system calls, how do processes work, blocking, scheduling, threads (kernel), context switching, signals, interrupts, modules, etc. Memory and Virtual Memory Virtual Memory VM, paging, swapping, allocating memory, testing for memory leaks, mmap, vnodes, etc. I/O System UFS UFS, FFS, Ext2FS, JFS, inodes, buffer cache, labeling, locking, metadata, soft-updates, LFS, portalfs, procfs, vnodes, memory sharing, memory objects, TLBs, caching Interprocess Communication Signals Signals, pipes, semaphores, message queues, shared memory, ports, sockets, doors Networking Sockets Sockets, bpf, IP, TCP, UDP, ICMP, OSI, bridging, firewalling, NAT, switching, etc Network Filesystems AFS AFS, NFS, SANs etc] Terminal Handling Syscons Syscons, tty, PCVT, serial console, screen savers, etc Sound OSS OSS, waveforms, etc Device Drivers Writing FreeBSD Device Drivers This chapter was written by Murray Stokely with selections from a variety of sources including the intro(4) man page by Joerg Wunsch. Introduction This chapter provides a brief introduction to writing device drivers for FreeBSD. A device in this context is a term used mostly for hardware-related stuff that belongs to the system, like disks, printers, or a graphics display with its keyboard. A device driver is the software component of the operating system that controls a specific device. There are also so-called pseudo-devices where a device driver emulates the behaviour of a device in software without any particular underlying hardware. Device drivers can be compiled into the system statically or loaded on demand through the dynamic kernel linker facility `kld'. Most devices in a Unix-like operating system are accessed through device-nodes, sometimes also called special files. These files are usually located under the directory /dev in the file system hierarchy. Until devfs is fully integrated into FreeBSD, each device node must be created statically and independent of the existence of the associated device driver. Most device nodes on the system are created by running MAKEDEV. Device drivers can roughly be broken down into three categories; character (unbuffered), block (buffered), and network drivers. Dynamic Kernel Linker Facility - KLD The kld interface allows system administrators to dynamically add and remove functionality from a running system. This allows device driver writers to load their new changes into a running kernel without constantly rebooting to test changes. The kld interface is used through the following administrator commands : kldload - loads a new kernel module kldunload - unloads a kernel module kldstat - lists the currently loadded modules Skeleton Layout of a kernel module /* * KLD Skeleton * Inspired by Andrew Reiter's Daemonnews article */ #include <sys/types.h> #include <sys/module.h> #include <sys/systm.h> /* uprintf */ #include <sys/errno.h> #include <sys/param.h> /* defines used in kernel.h */ #include <sys/kernel.h> /* types used in module initialization */ /* * Load handler that deals with the loading and unloading of a KLD. */ static int skel_loader(struct module *m, int what, void *arg) { int err = 0; switch (what) { case MOD_LOAD: /* kldload */ uprintf("Skeleton KLD loaded.\n"); break; case MOD_UNLOAD: uprintf("Skeleton KLD unloaded.\n"); break; default: err = EINVAL; break; } return(err); } /* Declare this module to the rest of the kernel */ DECLARE_MODULE(skeleton, skel_loader, SI_SUB_KLD, SI_ORDER_ANY); Makefile FreeBSD provides a makefile include that you can use to quickly compile your kernel addition. SRCS=skeleton.c KMOD=skeleton .include <bsd.kmod.mk> Simply running make with this makefile will create a file skeleton.ko that can be loaded into your system by typing : &prompt.root kldload -v ./skeleton.ko Accessing a device driver Unix provides a common set of system calls for user applications to use. The upper layers of the kernel dispatch these calls to the corresponding device driver when a user accesses a device node. The /dev/MAKEDEV script makes most of the device nodes for your system but if you are doing your own driver development it may be necessary to create your own device nodes with mknod Creating static device nodes The mknod command requires four arguments to create a device node. You must specify the name of this device node, the type of device, the major number of the device, and the minor number of the device. Dynamic device nodes The device filesystem, or devfs, provides access to the kernel's device namespace in the global filesystem namespace. This eliminates the problems of potentially having a device driver without a static device node, or a device node without an installed device driver. Unfortunately, devfs is still a work in progress. Character Devices A character device driver is one that transfers data directly to and from a user process. This is the most common type of device driver and there are plenty of simple examples in the source tree. This simple example pseudo-device remembers whatever values you write to it and can then supply them back to you when you read from it. /* * Simple `echo' pseudo-device KLD * * Murray Stokely */ #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #include <sys/types.h> #include <sys/module.h> #include <sys/systm.h> /* uprintf */ #include <sys/errno.h> #include <sys/param.h> /* defines used in kernel.h */ #include <sys/kernel.h> /* types used in module initialization */ #include <sys/conf.h> /* cdevsw struct */ #include <sys/uio.h> /* uio struct */ #include <sys/malloc.h> #define BUFFERSIZE 256 /* Function prototypes */ d_open_t echo_open; d_close_t echo_close; d_read_t echo_read; d_write_t echo_write; /* Character device entry points */ static struct cdevsw echo_cdevsw = { echo_open, echo_close, echo_read, echo_write, noioctl, nopoll, nommap, nostrategy, "echo", 33, /* reserved for lkms - /usr/src/sys/conf/majors */ nodump, nopsize, D_TTY, -1 }; typedef struct s_echo { char msg[BUFFERSIZE]; int len; } t_echo; /* vars */ static dev_t sdev; static int len; static int count; static t_echo *echomsg; MALLOC_DECLARE(M_ECHOBUF); MALLOC_DEFINE(M_ECHOBUF, "echobuffer", "buffer for echo module"); /* * This function acts is called by the kld[un]load(2) system calls to * determine what actions to take when a module is loaded or unloaded. */ static int echo_loader(struct module *m, int what, void *arg) { int err = 0; switch (what) { case MOD_LOAD: /* kldload */ sdev = make_dev(&echo_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "echo"); /* kmalloc memory for use by this driver */ /* malloc(256,M_ECHOBUF,M_WAITOK); */ MALLOC(echomsg, t_echo *, sizeof(t_echo), M_ECHOBUF, M_WAITOK); printf("Echo device loaded.\n"); break; case MOD_UNLOAD: destroy_dev(sdev); FREE(echomsg,M_ECHOBUF); printf("Echo device unloaded.\n"); break; default: err = EINVAL; break; } return(err); } int echo_open(dev_t dev, int oflags, int devtype, struct proc *p) { int err = 0; uprintf("Opened device \"echo\" successfully.\n"); return(err); } int echo_close(dev_t dev, int fflag, int devtype, struct proc *p) { uprintf("Closing device \"echo.\"\n"); return(0); } /* * The read function just takes the buf that was saved via * echo_write() and returns it to userland for accessing. * uio(9) */ int echo_read(dev_t dev, struct uio *uio, int ioflag) { int err = 0; int amt; /* How big is this read operation? Either as big as the user wants, or as big as the remaining data */ amt = MIN(uio->uio_resid, (echomsg->len - uio->uio_offset > 0) ? echomsg->len - uio->uio_offset : 0); if ((err = uiomove(echomsg->msg + uio->uio_offset,amt,uio)) != 0) { uprintf("uiomove failed!\n"); } return err; } /* * echo_write takes in a character string and saves it * to buf for later accessing. */ int echo_write(dev_t dev, struct uio *uio, int ioflag) { int err = 0; /* Copy the string in from user memory to kernel memory */ err = copyin(uio->uio_iov->iov_base, echomsg->msg, MIN(uio->uio_iov->iov_len,BUFFERSIZE)); /* Now we need to null terminate */ *(echomsg->msg + MIN(uio->uio_iov->iov_len,BUFFERSIZE)) = 0; /* Record the length */ echomsg->len = MIN(uio->uio_iov->iov_len,BUFFERSIZE); if (err != 0) { uprintf("Write failed: bad address!\n"); } count++; return(err); } DEV_MODULE(echo,echo_loader,NULL); To install this driver you will first need to make a node on your filesystem with a command such as : &prompt.root mknod /dev/echo c 33 0 With this driver loaded you should now be able to type something like : &prompt.root echo -n "Test Data" > /dev/echo &prompt.root cat /dev/echo Test Data Real hardware devices in the next chapter.. Additional Resources Dynamic Kernel Linker (KLD) Facility Programming Tutorial - Daemonnews October 2000 How to Write Kernel Drivers with NEWBUS - Daemonnews July 2000 Block Devices A block device driver transfers data to and from the operating system's buffer cache. They are solely intended to layer a file system on top of them. For this reason they are normally implemented for disks and disk-like devices only. Example test data generator ... Example ramdisk device ... Real hardware devices in the next chapter.. Network Drivers Drivers for network devices do not use device nodes in ord to be accessed. Their selection is based on other decisions made inside the kernel and instead of calling open(), use of a network device is generally introduced by using the system call socket(2). man ifnet(), loopback device, Bill Pauls drivers, etc.. PCI Devices This chapter will talk about the FreeBSD mechanisms for writing a device driver for a device on a PCI bus. Probe and Attach Information here about how the PCI bus code iterates through the unattached devices and see if a newly loaded kld will attach to any of them. /* * Simple KLD to play with the PCI functions. * * Murray Stokely */ #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #include <sys/types.h> #include <sys/module.h> #include <sys/systm.h> /* uprintf */ #include <sys/errno.h> #include <sys/param.h> /* defines used in kernel.h */ #include <sys/kernel.h> /* types used in module initialization */ #include <sys/conf.h> /* cdevsw struct */ #include <sys/uio.h> /* uio struct */ #include <sys/malloc.h> #include <sys/bus.h> /* structs, prototypes for pci bus stuff */ #include <pci/pcivar.h> /* For get_pci macros! */ /* Function prototypes */ d_open_t mypci_open; d_close_t mypci_close; d_read_t mypci_read; d_write_t mypci_write; /* Character device entry points */ static struct cdevsw mypci_cdevsw = { mypci_open, mypci_close, mypci_read, mypci_write, noioctl, nopoll, nommap, nostrategy, "mypci", 36, /* reserved for lkms - /usr/src/sys/conf/majors */ nodump, nopsize, D_TTY, -1 }; /* vars */ static dev_t sdev; /* We're more interested in probe/attach than with open/close/read/write at this point */ int mypci_open(dev_t dev, int oflags, int devtype, struct proc *p) { int err = 0; uprintf("Opened device \"mypci\" successfully.\n"); return(err); } int mypci_close(dev_t dev, int fflag, int devtype, struct proc *p) { int err=0; uprintf("Closing device \"mypci.\"\n"); return(err); } int mypci_read(dev_t dev, struct uio *uio, int ioflag) { int err = 0; uprintf("mypci read!\n"); return err; } int mypci_write(dev_t dev, struct uio *uio, int ioflag) { int err = 0; uprintf("mypci write!\n"); return(err); } /* PCI Support Functions */ /* * Return identification string if this is device is ours. */ static int mypci_probe(device_t dev) { uprintf("MyPCI Probe\n" "Vendor ID : 0x%x\n" "Device ID : 0x%x\n",pci_get_vendor(dev),pci_get_device(dev)); if (pci_get_vendor(dev) == 0x11c1) { uprintf("We've got the Winmodem, probe successful!\n"); return 0; } return ENXIO; } /* Attach function is only called if the probe is successful */ static int mypci_attach(device_t dev) { uprintf("MyPCI Attach for : deviceID : 0x%x\n",pci_get_vendor(dev)); sdev = make_dev(&mypci_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "mypci"); uprintf("Mypci device loaded.\n"); return ENXIO; } /* Detach device. */ static int mypci_detach(device_t dev) { uprintf("Mypci detach!\n"); return 0; } /* Called during system shutdown after sync. */ static int mypci_shutdown(device_t dev) { uprintf("Mypci shutdown!\n"); return 0; } /* * Device suspend routine. */ static int mypci_suspend(device_t dev) { uprintf("Mypci suspend!\n"); return 0; } /* * Device resume routine. */ static int mypci_resume(device_t dev) { uprintf("Mypci resume!\n"); return 0; } static device_method_t mypci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mypci_probe), DEVMETHOD(device_attach, mypci_attach), DEVMETHOD(device_detach, mypci_detach), DEVMETHOD(device_shutdown, mypci_shutdown), DEVMETHOD(device_suspend, mypci_suspend), DEVMETHOD(device_resume, mypci_resume), { 0, 0 } }; static driver_t mypci_driver = { "mypci", mypci_methods, 0, /* sizeof(struct mypci_softc), */ }; static devclass_t mypci_devclass; DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0); Additional Resources PCI Special Interest Group PCI System Architecture, Fourth Edition by Tom Shanley, et al. USB Devices This chapter will talk about the FreeBSD mechanisms for writing a device driver for a device on a USB bus. NewBus This chapter will talk about the FreeBSD NewBus architecture. Architectures IA-32 Talk about the architectural specifics of FreeBSD/x86. Alpha Talk about the architectural specifics of FreeBSD/alpha. Explanation of allignment errors, how to fix, how to ignore. Example assembly language code for FreeBSD/alpha. IA-64 Talk about the architectural specifics of FreeBSD/ia64. Debugging Truss various descriptions on how to debug certain aspects of the system using truss, ktrace, gdb, kgdb, etc Compatibility Layers Linux Linux, SVR4, etc Appendices Dave A Patterson John L Hennessy 1998Morgan Kaufmann Publishers, Inc. 1-55860-428-6 Morgan Kaufmann Publishers, Inc. Computer Organization and Design The Hardware / Software Interface 1-2 W. Richard Stevens 1993Addison Wesley Longman, Inc. 0-201-56317-7 Addison Wesley Longman, Inc. Advanced Programming in the Unix Environment 1-2 Marshall Kirk McKusick Keith Bostic Michael J Karels John S Quarterman 1996Addison-Wesley Publishing Company, Inc. 0-201-54979-4 Addison-Wesley Publishing Company, Inc. The Design and Implementation of the 4.4 BSD Operating System 1-2 Aleph One Phrack 49; "Smashing the Stack for Fun and Profit" Chrispin Cowan Calton Pu Dave Maier StackGuard; Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks Todd Miller Theo de Raadt strlcpy and strlcat -- consistent, safe string copy and concatenation.
diff --git a/en_US.ISO8859-1/books/developers-handbook/book.sgml b/en_US.ISO8859-1/books/developers-handbook/book.sgml index 7f0c2f3b9a..af9c07821e 100644 --- a/en_US.ISO8859-1/books/developers-handbook/book.sgml +++ b/en_US.ISO8859-1/books/developers-handbook/book.sgml @@ -1,1503 +1,1101 @@ %bookinfo; %chapters; ]> FreeBSD Developers' Handbook The FreeBSD Documentation Project
doc@FreeBSD.org
August 2000 2000 The FreeBSD Documentation Project &bookinfo.legalnotice; Welcome to the Developers' Handbook.
Introduction Developing on FreeBSD This will need to discuss FreeBSD as a development platform, the vision of BSD, architectural overview, layout of /usr/src, history, etc. Thank you for considering FreeBSD as your development platform! We hope it will not let you down. The BSD Vision Architectural Overview The Layout of /usr/src The complete source code to FreeBSD is available from our public CVS repository. The source code is normally installed in /usr/src which contains the following subdirectories. Directory Description bin/ Source for files in /bin contrib/ Source for files from contribued software. crypto/ DES source etc/ Source for files in /etc games/ Source for files in /usr/games gnu/ Utilities covered by the GNU Public License include/ Source for files in /usr/include kerberosIV/ Source for Kerbereros version IV kerberos5/ Source for Kerbereros version 5 lib/ Source for files in /usr/lib libexec/ Source for files in /usr/libexec release/ Files required to produce a FreeBSD release sbin/ Source for files in /sbin secure/ FreeSec sources share/ Source for files in /sbin sys/ Kernel source files tools/ Tools used for maintenance and testing of FreeBSD usr.bin/ Source for files in /usr/bin usr.sbin/ Source for files in /usr/sbin Basics &chap.tools; + &chap.secure; - - Secure Programming - - This chapter was written by Murray Stokely. - - Synopsis - - 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. - - - Secure Design - Methodology - - Writing secure applications takes a very scrutinous and - pessimistic outlook on life. Applications should be run with - the principle of least privilege 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. - - 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. - - - Buffer Overflows - - Buffer Overflows have been around since the very - beginnings of the Von-Neuman 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. - - 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. 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. - - 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. - - 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. - - - - - strcpy(char *dest, const char - *src) - May overflow the dest buffer - - - strcat(char *dest, const char - *src) - May overflow the dest buffer - - - getwd(char *buf) - May overflow the buf buffer - - - gets(char *s) - May overflow the s buffer - - - [vf]scanf(const char *format, - ...) - May overflow its arguments. - - - realpath(char *path, char - resolved_path[]) - May overflow the path buffer - - - [v]sprintf(char *str, const char - *format, ...) - May overflow the str buffer. - - - - - - Example Buffer Overflow - - 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 ) - - -#include stdio.h - -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; -} - - - 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. - - [XXX figure here!] - - Obviously more malicious input can be devised to execute - actual compiled instructions (such as exec(/bin/sh)). - - - Avoiding Buffer Overflows - - The most straightforward solution to the problem of - stack-overflows is to always use length restricted memory and - string copy functions. strncpy and - strncat 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 strcpy when - copying a short string into a large buffer since - strncpy NUL fills up the the size - specified. - - In OpenBSD, another memory copy implementation has been - created to get around these problem. The - strlcpy and strlcat - 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 . The OpenBSD strlcpy and - strlcat instructions have been in FreeBSD - since 3.5. - - Compiler based run-time bounds checking - - 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++. - - 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 : -
"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."
- -
"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."
-
- - Recompiling your application with StackGuard is an - effective means of stopping most buffer-overflow attacks, but - it can still be compromised. - -
- - Library based run-time bounds checking - - 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 - (strcpy, fscanf, - getwd, etc..) and ensure that these - functions can never write past the stack pointer. - - - libsafe - libverify - libparnoia - - - 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. - - -
-
- - SetUID issues - - 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. - - The real user ID can only be changed by a superuser - process. The login program sets this - when a user initially logs in and it is seldom changed. - - The effective user ID is set by the - exec() functions if a program has its - seteuid bit set. An application can call - seteuid() 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 exec() - functions, the previous value is saved in the saved set-user-ID. - - - - Limiting your program's environment - - The traditional method of restricting access to a process - is with the chroot() 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 - chdir() 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. - - The behavior of the chroot() system - call can be controlled somewhat with the - kern.chroot_allow_open_directories sysctl - variable. When this value is set to 0, - chroot() will fail with EPERM if there are - any directories open. If set to the default value of 1, then - chroot() will fail with EPERM if there are - any directories open and the process is already subject to a - chroot() call. For any other value, the - check for open directories will be bypassed completely. - - FreeBSD's jail functionality - - The concept of a Jail extends upon the - chroot() 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. - - While in a prison, any tests of superuser power within the - kernel using the suser() call will fail. - However, some calls to suser() have been - changed to a new interface suser_xxx(). - This function is responsible for recognizing or denying access - to superuser power for imprisoned processes. - - A superuser process within a jailed environment has the - power to : - - Manipulate credential with - setuid, seteuid, - setgid, setegid, - setgroups, setreuid, - setregid, setlogin - Set resource limits with setrlimit - Modify some sysctl nodes - (kern.hostname) - chroot() - Set flags on a vnode: - chflags, - fchflags - Set attributes of a vnode such as file - permission, owner, group, size, access time, and modification - time. - Bind to privileged ports in the Internet - domain (ports < 1024) - - - Jail 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 suser_xxx 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. - - - POSIX.1e Process Capabilities - - Posix has released a working draft that adds event - auditing, access control lists, fine grained privileges, - information labeling, and mandatory access control. - This is a work in progress and is the focus of the TrustedBSD project. Some - of the initial work has been committed to FreeBSD-current - (cap_set_proc(3)). - - - - - - Trust - - 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. - - 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. - - 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 (readdir(), - readlink(), - getpwxxx(), and all file input. - - - - Race Conditions - - 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. - - 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 access(2) then - open(2) is clearly non-atomic. Users can - move files in between the two calls. Instead, privileged - applications should seteuid() and then call - open() directly. Along the same lines, an - application should always set a proper umask before - open() to obviate the need for spurious - chmod() calls. - - - -
Kernel History of the Unix Kernel Some history of the Unix/BSD kernel, system calls, how do processes work, blocking, scheduling, threads (kernel), context switching, signals, interrupts, modules, etc. Memory and Virtual Memory Virtual Memory VM, paging, swapping, allocating memory, testing for memory leaks, mmap, vnodes, etc. I/O System UFS UFS, FFS, Ext2FS, JFS, inodes, buffer cache, labeling, locking, metadata, soft-updates, LFS, portalfs, procfs, vnodes, memory sharing, memory objects, TLBs, caching Interprocess Communication Signals Signals, pipes, semaphores, message queues, shared memory, ports, sockets, doors Networking Sockets Sockets, bpf, IP, TCP, UDP, ICMP, OSI, bridging, firewalling, NAT, switching, etc Network Filesystems AFS AFS, NFS, SANs etc] Terminal Handling Syscons Syscons, tty, PCVT, serial console, screen savers, etc Sound OSS OSS, waveforms, etc Device Drivers Writing FreeBSD Device Drivers This chapter was written by Murray Stokely with selections from a variety of sources including the intro(4) man page by Joerg Wunsch. Introduction This chapter provides a brief introduction to writing device drivers for FreeBSD. A device in this context is a term used mostly for hardware-related stuff that belongs to the system, like disks, printers, or a graphics display with its keyboard. A device driver is the software component of the operating system that controls a specific device. There are also so-called pseudo-devices where a device driver emulates the behaviour of a device in software without any particular underlying hardware. Device drivers can be compiled into the system statically or loaded on demand through the dynamic kernel linker facility `kld'. Most devices in a Unix-like operating system are accessed through device-nodes, sometimes also called special files. These files are usually located under the directory /dev in the file system hierarchy. Until devfs is fully integrated into FreeBSD, each device node must be created statically and independent of the existence of the associated device driver. Most device nodes on the system are created by running MAKEDEV. Device drivers can roughly be broken down into three categories; character (unbuffered), block (buffered), and network drivers. Dynamic Kernel Linker Facility - KLD The kld interface allows system administrators to dynamically add and remove functionality from a running system. This allows device driver writers to load their new changes into a running kernel without constantly rebooting to test changes. The kld interface is used through the following administrator commands : kldload - loads a new kernel module kldunload - unloads a kernel module kldstat - lists the currently loadded modules Skeleton Layout of a kernel module /* * KLD Skeleton * Inspired by Andrew Reiter's Daemonnews article */ #include <sys/types.h> #include <sys/module.h> #include <sys/systm.h> /* uprintf */ #include <sys/errno.h> #include <sys/param.h> /* defines used in kernel.h */ #include <sys/kernel.h> /* types used in module initialization */ /* * Load handler that deals with the loading and unloading of a KLD. */ static int skel_loader(struct module *m, int what, void *arg) { int err = 0; switch (what) { case MOD_LOAD: /* kldload */ uprintf("Skeleton KLD loaded.\n"); break; case MOD_UNLOAD: uprintf("Skeleton KLD unloaded.\n"); break; default: err = EINVAL; break; } return(err); } /* Declare this module to the rest of the kernel */ DECLARE_MODULE(skeleton, skel_loader, SI_SUB_KLD, SI_ORDER_ANY); Makefile FreeBSD provides a makefile include that you can use to quickly compile your kernel addition. SRCS=skeleton.c KMOD=skeleton .include <bsd.kmod.mk> Simply running make with this makefile will create a file skeleton.ko that can be loaded into your system by typing : &prompt.root kldload -v ./skeleton.ko Accessing a device driver Unix provides a common set of system calls for user applications to use. The upper layers of the kernel dispatch these calls to the corresponding device driver when a user accesses a device node. The /dev/MAKEDEV script makes most of the device nodes for your system but if you are doing your own driver development it may be necessary to create your own device nodes with mknod Creating static device nodes The mknod command requires four arguments to create a device node. You must specify the name of this device node, the type of device, the major number of the device, and the minor number of the device. Dynamic device nodes The device filesystem, or devfs, provides access to the kernel's device namespace in the global filesystem namespace. This eliminates the problems of potentially having a device driver without a static device node, or a device node without an installed device driver. Unfortunately, devfs is still a work in progress. Character Devices A character device driver is one that transfers data directly to and from a user process. This is the most common type of device driver and there are plenty of simple examples in the source tree. This simple example pseudo-device remembers whatever values you write to it and can then supply them back to you when you read from it. /* * Simple `echo' pseudo-device KLD * * Murray Stokely */ #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #include <sys/types.h> #include <sys/module.h> #include <sys/systm.h> /* uprintf */ #include <sys/errno.h> #include <sys/param.h> /* defines used in kernel.h */ #include <sys/kernel.h> /* types used in module initialization */ #include <sys/conf.h> /* cdevsw struct */ #include <sys/uio.h> /* uio struct */ #include <sys/malloc.h> #define BUFFERSIZE 256 /* Function prototypes */ d_open_t echo_open; d_close_t echo_close; d_read_t echo_read; d_write_t echo_write; /* Character device entry points */ static struct cdevsw echo_cdevsw = { echo_open, echo_close, echo_read, echo_write, noioctl, nopoll, nommap, nostrategy, "echo", 33, /* reserved for lkms - /usr/src/sys/conf/majors */ nodump, nopsize, D_TTY, -1 }; typedef struct s_echo { char msg[BUFFERSIZE]; int len; } t_echo; /* vars */ static dev_t sdev; static int len; static int count; static t_echo *echomsg; MALLOC_DECLARE(M_ECHOBUF); MALLOC_DEFINE(M_ECHOBUF, "echobuffer", "buffer for echo module"); /* * This function acts is called by the kld[un]load(2) system calls to * determine what actions to take when a module is loaded or unloaded. */ static int echo_loader(struct module *m, int what, void *arg) { int err = 0; switch (what) { case MOD_LOAD: /* kldload */ sdev = make_dev(&echo_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "echo"); /* kmalloc memory for use by this driver */ /* malloc(256,M_ECHOBUF,M_WAITOK); */ MALLOC(echomsg, t_echo *, sizeof(t_echo), M_ECHOBUF, M_WAITOK); printf("Echo device loaded.\n"); break; case MOD_UNLOAD: destroy_dev(sdev); FREE(echomsg,M_ECHOBUF); printf("Echo device unloaded.\n"); break; default: err = EINVAL; break; } return(err); } int echo_open(dev_t dev, int oflags, int devtype, struct proc *p) { int err = 0; uprintf("Opened device \"echo\" successfully.\n"); return(err); } int echo_close(dev_t dev, int fflag, int devtype, struct proc *p) { uprintf("Closing device \"echo.\"\n"); return(0); } /* * The read function just takes the buf that was saved via * echo_write() and returns it to userland for accessing. * uio(9) */ int echo_read(dev_t dev, struct uio *uio, int ioflag) { int err = 0; int amt; /* How big is this read operation? Either as big as the user wants, or as big as the remaining data */ amt = MIN(uio->uio_resid, (echomsg->len - uio->uio_offset > 0) ? echomsg->len - uio->uio_offset : 0); if ((err = uiomove(echomsg->msg + uio->uio_offset,amt,uio)) != 0) { uprintf("uiomove failed!\n"); } return err; } /* * echo_write takes in a character string and saves it * to buf for later accessing. */ int echo_write(dev_t dev, struct uio *uio, int ioflag) { int err = 0; /* Copy the string in from user memory to kernel memory */ err = copyin(uio->uio_iov->iov_base, echomsg->msg, MIN(uio->uio_iov->iov_len,BUFFERSIZE)); /* Now we need to null terminate */ *(echomsg->msg + MIN(uio->uio_iov->iov_len,BUFFERSIZE)) = 0; /* Record the length */ echomsg->len = MIN(uio->uio_iov->iov_len,BUFFERSIZE); if (err != 0) { uprintf("Write failed: bad address!\n"); } count++; return(err); } DEV_MODULE(echo,echo_loader,NULL); To install this driver you will first need to make a node on your filesystem with a command such as : &prompt.root mknod /dev/echo c 33 0 With this driver loaded you should now be able to type something like : &prompt.root echo -n "Test Data" > /dev/echo &prompt.root cat /dev/echo Test Data Real hardware devices in the next chapter.. Additional Resources Dynamic Kernel Linker (KLD) Facility Programming Tutorial - Daemonnews October 2000 How to Write Kernel Drivers with NEWBUS - Daemonnews July 2000 Block Devices A block device driver transfers data to and from the operating system's buffer cache. They are solely intended to layer a file system on top of them. For this reason they are normally implemented for disks and disk-like devices only. Example test data generator ... Example ramdisk device ... Real hardware devices in the next chapter.. Network Drivers Drivers for network devices do not use device nodes in ord to be accessed. Their selection is based on other decisions made inside the kernel and instead of calling open(), use of a network device is generally introduced by using the system call socket(2). man ifnet(), loopback device, Bill Pauls drivers, etc.. PCI Devices This chapter will talk about the FreeBSD mechanisms for writing a device driver for a device on a PCI bus. Probe and Attach Information here about how the PCI bus code iterates through the unattached devices and see if a newly loaded kld will attach to any of them. /* * Simple KLD to play with the PCI functions. * * Murray Stokely */ #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #include <sys/types.h> #include <sys/module.h> #include <sys/systm.h> /* uprintf */ #include <sys/errno.h> #include <sys/param.h> /* defines used in kernel.h */ #include <sys/kernel.h> /* types used in module initialization */ #include <sys/conf.h> /* cdevsw struct */ #include <sys/uio.h> /* uio struct */ #include <sys/malloc.h> #include <sys/bus.h> /* structs, prototypes for pci bus stuff */ #include <pci/pcivar.h> /* For get_pci macros! */ /* Function prototypes */ d_open_t mypci_open; d_close_t mypci_close; d_read_t mypci_read; d_write_t mypci_write; /* Character device entry points */ static struct cdevsw mypci_cdevsw = { mypci_open, mypci_close, mypci_read, mypci_write, noioctl, nopoll, nommap, nostrategy, "mypci", 36, /* reserved for lkms - /usr/src/sys/conf/majors */ nodump, nopsize, D_TTY, -1 }; /* vars */ static dev_t sdev; /* We're more interested in probe/attach than with open/close/read/write at this point */ int mypci_open(dev_t dev, int oflags, int devtype, struct proc *p) { int err = 0; uprintf("Opened device \"mypci\" successfully.\n"); return(err); } int mypci_close(dev_t dev, int fflag, int devtype, struct proc *p) { int err=0; uprintf("Closing device \"mypci.\"\n"); return(err); } int mypci_read(dev_t dev, struct uio *uio, int ioflag) { int err = 0; uprintf("mypci read!\n"); return err; } int mypci_write(dev_t dev, struct uio *uio, int ioflag) { int err = 0; uprintf("mypci write!\n"); return(err); } /* PCI Support Functions */ /* * Return identification string if this is device is ours. */ static int mypci_probe(device_t dev) { uprintf("MyPCI Probe\n" "Vendor ID : 0x%x\n" "Device ID : 0x%x\n",pci_get_vendor(dev),pci_get_device(dev)); if (pci_get_vendor(dev) == 0x11c1) { uprintf("We've got the Winmodem, probe successful!\n"); return 0; } return ENXIO; } /* Attach function is only called if the probe is successful */ static int mypci_attach(device_t dev) { uprintf("MyPCI Attach for : deviceID : 0x%x\n",pci_get_vendor(dev)); sdev = make_dev(&mypci_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "mypci"); uprintf("Mypci device loaded.\n"); return ENXIO; } /* Detach device. */ static int mypci_detach(device_t dev) { uprintf("Mypci detach!\n"); return 0; } /* Called during system shutdown after sync. */ static int mypci_shutdown(device_t dev) { uprintf("Mypci shutdown!\n"); return 0; } /* * Device suspend routine. */ static int mypci_suspend(device_t dev) { uprintf("Mypci suspend!\n"); return 0; } /* * Device resume routine. */ static int mypci_resume(device_t dev) { uprintf("Mypci resume!\n"); return 0; } static device_method_t mypci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mypci_probe), DEVMETHOD(device_attach, mypci_attach), DEVMETHOD(device_detach, mypci_detach), DEVMETHOD(device_shutdown, mypci_shutdown), DEVMETHOD(device_suspend, mypci_suspend), DEVMETHOD(device_resume, mypci_resume), { 0, 0 } }; static driver_t mypci_driver = { "mypci", mypci_methods, 0, /* sizeof(struct mypci_softc), */ }; static devclass_t mypci_devclass; DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0); Additional Resources PCI Special Interest Group PCI System Architecture, Fourth Edition by Tom Shanley, et al. USB Devices This chapter will talk about the FreeBSD mechanisms for writing a device driver for a device on a USB bus. NewBus This chapter will talk about the FreeBSD NewBus architecture. Architectures IA-32 Talk about the architectural specifics of FreeBSD/x86. Alpha Talk about the architectural specifics of FreeBSD/alpha. Explanation of allignment errors, how to fix, how to ignore. Example assembly language code for FreeBSD/alpha. IA-64 Talk about the architectural specifics of FreeBSD/ia64. Debugging Truss various descriptions on how to debug certain aspects of the system using truss, ktrace, gdb, kgdb, etc Compatibility Layers Linux Linux, SVR4, etc Appendices Dave A Patterson John L Hennessy 1998Morgan Kaufmann Publishers, Inc. 1-55860-428-6 Morgan Kaufmann Publishers, Inc. Computer Organization and Design The Hardware / Software Interface 1-2 W. Richard Stevens 1993Addison Wesley Longman, Inc. 0-201-56317-7 Addison Wesley Longman, Inc. Advanced Programming in the Unix Environment 1-2 Marshall Kirk McKusick Keith Bostic Michael J Karels John S Quarterman 1996Addison-Wesley Publishing Company, Inc. 0-201-54979-4 Addison-Wesley Publishing Company, Inc. The Design and Implementation of the 4.4 BSD Operating System 1-2 Aleph One Phrack 49; "Smashing the Stack for Fun and Profit" Chrispin Cowan Calton Pu Dave Maier StackGuard; Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks Todd Miller Theo de Raadt strlcpy and strlcat -- consistent, safe string copy and concatenation.
diff --git a/en_US.ISO8859-1/books/developers-handbook/secure/chapter.sgml b/en_US.ISO8859-1/books/developers-handbook/secure/chapter.sgml new file mode 100644 index 0000000000..805409b691 --- /dev/null +++ b/en_US.ISO8859-1/books/developers-handbook/secure/chapter.sgml @@ -0,0 +1,409 @@ + + + + Secure Programming + + This chapter was written by Murray Stokely. + + Synopsis + + 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. + + + Secure Design + Methodology + + Writing secure applications takes a very scrutinous and + pessimistic outlook on life. Applications should be run with + the principle of least privilege 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. + + 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. + + + Buffer Overflows + + Buffer Overflows have been around since the very + beginnings of the Von-Neuman 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. + + 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. 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. + + 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. + + 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. + + + + + strcpy(char *dest, const char + *src) + May overflow the dest buffer + + + strcat(char *dest, const char + *src) + May overflow the dest buffer + + + getwd(char *buf) + May overflow the buf buffer + + + gets(char *s) + May overflow the s buffer + + + [vf]scanf(const char *format, + ...) + May overflow its arguments. + + + realpath(char *path, char + resolved_path[]) + May overflow the path buffer + + + [v]sprintf(char *str, const char + *format, ...) + May overflow the str buffer. + + + + + + Example Buffer Overflow + + 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 ) + + +#include stdio.h + +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; +} + + + 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. + + [XXX figure here!] + + Obviously more malicious input can be devised to execute + actual compiled instructions (such as exec(/bin/sh)). + + + Avoiding Buffer Overflows + + The most straightforward solution to the problem of + stack-overflows is to always use length restricted memory and + string copy functions. strncpy and + strncat 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 strcpy when + copying a short string into a large buffer since + strncpy NUL fills up the the size + specified. + + In OpenBSD, another memory copy implementation has been + created to get around these problem. The + strlcpy and strlcat + 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 . The OpenBSD strlcpy and + strlcat instructions have been in FreeBSD + since 3.5. + + Compiler based run-time bounds checking + + 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++. + + 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 : +
"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."
+ +
"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."
+
+ + Recompiling your application with StackGuard is an + effective means of stopping most buffer-overflow attacks, but + it can still be compromised. + +
+ + Library based run-time bounds checking + + 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 + (strcpy, fscanf, + getwd, etc..) and ensure that these + functions can never write past the stack pointer. + + + libsafe + libverify + libparnoia + + + 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. + + +
+
+ + SetUID issues + + 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. + + The real user ID can only be changed by a superuser + process. The login program sets this + when a user initially logs in and it is seldom changed. + + The effective user ID is set by the + exec() functions if a program has its + seteuid bit set. An application can call + seteuid() 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 exec() + functions, the previous value is saved in the saved set-user-ID. + + + + Limiting your program's environment + + The traditional method of restricting access to a process + is with the chroot() 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 + chdir() 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. + + The behavior of the chroot() system + call can be controlled somewhat with the + kern.chroot_allow_open_directories sysctl + variable. When this value is set to 0, + chroot() will fail with EPERM if there are + any directories open. If set to the default value of 1, then + chroot() will fail with EPERM if there are + any directories open and the process is already subject to a + chroot() call. For any other value, the + check for open directories will be bypassed completely. + + FreeBSD's jail functionality + + The concept of a Jail extends upon the + chroot() 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. + + While in a prison, any tests of superuser power within the + kernel using the suser() call will fail. + However, some calls to suser() have been + changed to a new interface suser_xxx(). + This function is responsible for recognizing or denying access + to superuser power for imprisoned processes. + + A superuser process within a jailed environment has the + power to : + + Manipulate credential with + setuid, seteuid, + setgid, setegid, + setgroups, setreuid, + setregid, setlogin + Set resource limits with setrlimit + Modify some sysctl nodes + (kern.hostname) + chroot() + Set flags on a vnode: + chflags, + fchflags + Set attributes of a vnode such as file + permission, owner, group, size, access time, and modification + time. + Bind to privileged ports in the Internet + domain (ports < 1024) + + + Jail 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 suser_xxx 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. + + + POSIX.1e Process Capabilities + + Posix has released a working draft that adds event + auditing, access control lists, fine grained privileges, + information labeling, and mandatory access control. + This is a work in progress and is the focus of the TrustedBSD project. Some + of the initial work has been committed to FreeBSD-current + (cap_set_proc(3)). + + + + + + Trust + + 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. + + 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. + + 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 (readdir(), + readlink(), + getpwxxx(), and all file input. + + + + Race Conditions + + 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. + + 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 access(2) then + open(2) is clearly non-atomic. Users can + move files in between the two calls. Instead, privileged + applications should seteuid() and then call + open() directly. Along the same lines, an + application should always set a proper umask before + open() to obviate the need for spurious + chmod() calls. + + + +
diff --git a/en_US.ISO_8859-1/books/developers-handbook/book.sgml b/en_US.ISO_8859-1/books/developers-handbook/book.sgml index 7f0c2f3b9a..af9c07821e 100644 --- a/en_US.ISO_8859-1/books/developers-handbook/book.sgml +++ b/en_US.ISO_8859-1/books/developers-handbook/book.sgml @@ -1,1503 +1,1101 @@ %bookinfo; %chapters; ]> FreeBSD Developers' Handbook The FreeBSD Documentation Project
doc@FreeBSD.org
August 2000 2000 The FreeBSD Documentation Project &bookinfo.legalnotice; Welcome to the Developers' Handbook.
Introduction Developing on FreeBSD This will need to discuss FreeBSD as a development platform, the vision of BSD, architectural overview, layout of /usr/src, history, etc. Thank you for considering FreeBSD as your development platform! We hope it will not let you down. The BSD Vision Architectural Overview The Layout of /usr/src The complete source code to FreeBSD is available from our public CVS repository. The source code is normally installed in /usr/src which contains the following subdirectories. Directory Description bin/ Source for files in /bin contrib/ Source for files from contribued software. crypto/ DES source etc/ Source for files in /etc games/ Source for files in /usr/games gnu/ Utilities covered by the GNU Public License include/ Source for files in /usr/include kerberosIV/ Source for Kerbereros version IV kerberos5/ Source for Kerbereros version 5 lib/ Source for files in /usr/lib libexec/ Source for files in /usr/libexec release/ Files required to produce a FreeBSD release sbin/ Source for files in /sbin secure/ FreeSec sources share/ Source for files in /sbin sys/ Kernel source files tools/ Tools used for maintenance and testing of FreeBSD usr.bin/ Source for files in /usr/bin usr.sbin/ Source for files in /usr/sbin Basics &chap.tools; + &chap.secure; - - Secure Programming - - This chapter was written by Murray Stokely. - - Synopsis - - 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. - - - Secure Design - Methodology - - Writing secure applications takes a very scrutinous and - pessimistic outlook on life. Applications should be run with - the principle of least privilege 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. - - 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. - - - Buffer Overflows - - Buffer Overflows have been around since the very - beginnings of the Von-Neuman 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. - - 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. 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. - - 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. - - 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. - - - - - strcpy(char *dest, const char - *src) - May overflow the dest buffer - - - strcat(char *dest, const char - *src) - May overflow the dest buffer - - - getwd(char *buf) - May overflow the buf buffer - - - gets(char *s) - May overflow the s buffer - - - [vf]scanf(const char *format, - ...) - May overflow its arguments. - - - realpath(char *path, char - resolved_path[]) - May overflow the path buffer - - - [v]sprintf(char *str, const char - *format, ...) - May overflow the str buffer. - - - - - - Example Buffer Overflow - - 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 ) - - -#include stdio.h - -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; -} - - - 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. - - [XXX figure here!] - - Obviously more malicious input can be devised to execute - actual compiled instructions (such as exec(/bin/sh)). - - - Avoiding Buffer Overflows - - The most straightforward solution to the problem of - stack-overflows is to always use length restricted memory and - string copy functions. strncpy and - strncat 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 strcpy when - copying a short string into a large buffer since - strncpy NUL fills up the the size - specified. - - In OpenBSD, another memory copy implementation has been - created to get around these problem. The - strlcpy and strlcat - 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 . The OpenBSD strlcpy and - strlcat instructions have been in FreeBSD - since 3.5. - - Compiler based run-time bounds checking - - 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++. - - 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 : -
"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."
- -
"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."
-
- - Recompiling your application with StackGuard is an - effective means of stopping most buffer-overflow attacks, but - it can still be compromised. - -
- - Library based run-time bounds checking - - 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 - (strcpy, fscanf, - getwd, etc..) and ensure that these - functions can never write past the stack pointer. - - - libsafe - libverify - libparnoia - - - 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. - - -
-
- - SetUID issues - - 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. - - The real user ID can only be changed by a superuser - process. The login program sets this - when a user initially logs in and it is seldom changed. - - The effective user ID is set by the - exec() functions if a program has its - seteuid bit set. An application can call - seteuid() 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 exec() - functions, the previous value is saved in the saved set-user-ID. - - - - Limiting your program's environment - - The traditional method of restricting access to a process - is with the chroot() 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 - chdir() 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. - - The behavior of the chroot() system - call can be controlled somewhat with the - kern.chroot_allow_open_directories sysctl - variable. When this value is set to 0, - chroot() will fail with EPERM if there are - any directories open. If set to the default value of 1, then - chroot() will fail with EPERM if there are - any directories open and the process is already subject to a - chroot() call. For any other value, the - check for open directories will be bypassed completely. - - FreeBSD's jail functionality - - The concept of a Jail extends upon the - chroot() 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. - - While in a prison, any tests of superuser power within the - kernel using the suser() call will fail. - However, some calls to suser() have been - changed to a new interface suser_xxx(). - This function is responsible for recognizing or denying access - to superuser power for imprisoned processes. - - A superuser process within a jailed environment has the - power to : - - Manipulate credential with - setuid, seteuid, - setgid, setegid, - setgroups, setreuid, - setregid, setlogin - Set resource limits with setrlimit - Modify some sysctl nodes - (kern.hostname) - chroot() - Set flags on a vnode: - chflags, - fchflags - Set attributes of a vnode such as file - permission, owner, group, size, access time, and modification - time. - Bind to privileged ports in the Internet - domain (ports < 1024) - - - Jail 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 suser_xxx 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. - - - POSIX.1e Process Capabilities - - Posix has released a working draft that adds event - auditing, access control lists, fine grained privileges, - information labeling, and mandatory access control. - This is a work in progress and is the focus of the TrustedBSD project. Some - of the initial work has been committed to FreeBSD-current - (cap_set_proc(3)). - - - - - - Trust - - 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. - - 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. - - 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 (readdir(), - readlink(), - getpwxxx(), and all file input. - - - - Race Conditions - - 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. - - 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 access(2) then - open(2) is clearly non-atomic. Users can - move files in between the two calls. Instead, privileged - applications should seteuid() and then call - open() directly. Along the same lines, an - application should always set a proper umask before - open() to obviate the need for spurious - chmod() calls. - - - -
Kernel History of the Unix Kernel Some history of the Unix/BSD kernel, system calls, how do processes work, blocking, scheduling, threads (kernel), context switching, signals, interrupts, modules, etc. Memory and Virtual Memory Virtual Memory VM, paging, swapping, allocating memory, testing for memory leaks, mmap, vnodes, etc. I/O System UFS UFS, FFS, Ext2FS, JFS, inodes, buffer cache, labeling, locking, metadata, soft-updates, LFS, portalfs, procfs, vnodes, memory sharing, memory objects, TLBs, caching Interprocess Communication Signals Signals, pipes, semaphores, message queues, shared memory, ports, sockets, doors Networking Sockets Sockets, bpf, IP, TCP, UDP, ICMP, OSI, bridging, firewalling, NAT, switching, etc Network Filesystems AFS AFS, NFS, SANs etc] Terminal Handling Syscons Syscons, tty, PCVT, serial console, screen savers, etc Sound OSS OSS, waveforms, etc Device Drivers Writing FreeBSD Device Drivers This chapter was written by Murray Stokely with selections from a variety of sources including the intro(4) man page by Joerg Wunsch. Introduction This chapter provides a brief introduction to writing device drivers for FreeBSD. A device in this context is a term used mostly for hardware-related stuff that belongs to the system, like disks, printers, or a graphics display with its keyboard. A device driver is the software component of the operating system that controls a specific device. There are also so-called pseudo-devices where a device driver emulates the behaviour of a device in software without any particular underlying hardware. Device drivers can be compiled into the system statically or loaded on demand through the dynamic kernel linker facility `kld'. Most devices in a Unix-like operating system are accessed through device-nodes, sometimes also called special files. These files are usually located under the directory /dev in the file system hierarchy. Until devfs is fully integrated into FreeBSD, each device node must be created statically and independent of the existence of the associated device driver. Most device nodes on the system are created by running MAKEDEV. Device drivers can roughly be broken down into three categories; character (unbuffered), block (buffered), and network drivers. Dynamic Kernel Linker Facility - KLD The kld interface allows system administrators to dynamically add and remove functionality from a running system. This allows device driver writers to load their new changes into a running kernel without constantly rebooting to test changes. The kld interface is used through the following administrator commands : kldload - loads a new kernel module kldunload - unloads a kernel module kldstat - lists the currently loadded modules Skeleton Layout of a kernel module /* * KLD Skeleton * Inspired by Andrew Reiter's Daemonnews article */ #include <sys/types.h> #include <sys/module.h> #include <sys/systm.h> /* uprintf */ #include <sys/errno.h> #include <sys/param.h> /* defines used in kernel.h */ #include <sys/kernel.h> /* types used in module initialization */ /* * Load handler that deals with the loading and unloading of a KLD. */ static int skel_loader(struct module *m, int what, void *arg) { int err = 0; switch (what) { case MOD_LOAD: /* kldload */ uprintf("Skeleton KLD loaded.\n"); break; case MOD_UNLOAD: uprintf("Skeleton KLD unloaded.\n"); break; default: err = EINVAL; break; } return(err); } /* Declare this module to the rest of the kernel */ DECLARE_MODULE(skeleton, skel_loader, SI_SUB_KLD, SI_ORDER_ANY); Makefile FreeBSD provides a makefile include that you can use to quickly compile your kernel addition. SRCS=skeleton.c KMOD=skeleton .include <bsd.kmod.mk> Simply running make with this makefile will create a file skeleton.ko that can be loaded into your system by typing : &prompt.root kldload -v ./skeleton.ko Accessing a device driver Unix provides a common set of system calls for user applications to use. The upper layers of the kernel dispatch these calls to the corresponding device driver when a user accesses a device node. The /dev/MAKEDEV script makes most of the device nodes for your system but if you are doing your own driver development it may be necessary to create your own device nodes with mknod Creating static device nodes The mknod command requires four arguments to create a device node. You must specify the name of this device node, the type of device, the major number of the device, and the minor number of the device. Dynamic device nodes The device filesystem, or devfs, provides access to the kernel's device namespace in the global filesystem namespace. This eliminates the problems of potentially having a device driver without a static device node, or a device node without an installed device driver. Unfortunately, devfs is still a work in progress. Character Devices A character device driver is one that transfers data directly to and from a user process. This is the most common type of device driver and there are plenty of simple examples in the source tree. This simple example pseudo-device remembers whatever values you write to it and can then supply them back to you when you read from it. /* * Simple `echo' pseudo-device KLD * * Murray Stokely */ #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #include <sys/types.h> #include <sys/module.h> #include <sys/systm.h> /* uprintf */ #include <sys/errno.h> #include <sys/param.h> /* defines used in kernel.h */ #include <sys/kernel.h> /* types used in module initialization */ #include <sys/conf.h> /* cdevsw struct */ #include <sys/uio.h> /* uio struct */ #include <sys/malloc.h> #define BUFFERSIZE 256 /* Function prototypes */ d_open_t echo_open; d_close_t echo_close; d_read_t echo_read; d_write_t echo_write; /* Character device entry points */ static struct cdevsw echo_cdevsw = { echo_open, echo_close, echo_read, echo_write, noioctl, nopoll, nommap, nostrategy, "echo", 33, /* reserved for lkms - /usr/src/sys/conf/majors */ nodump, nopsize, D_TTY, -1 }; typedef struct s_echo { char msg[BUFFERSIZE]; int len; } t_echo; /* vars */ static dev_t sdev; static int len; static int count; static t_echo *echomsg; MALLOC_DECLARE(M_ECHOBUF); MALLOC_DEFINE(M_ECHOBUF, "echobuffer", "buffer for echo module"); /* * This function acts is called by the kld[un]load(2) system calls to * determine what actions to take when a module is loaded or unloaded. */ static int echo_loader(struct module *m, int what, void *arg) { int err = 0; switch (what) { case MOD_LOAD: /* kldload */ sdev = make_dev(&echo_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "echo"); /* kmalloc memory for use by this driver */ /* malloc(256,M_ECHOBUF,M_WAITOK); */ MALLOC(echomsg, t_echo *, sizeof(t_echo), M_ECHOBUF, M_WAITOK); printf("Echo device loaded.\n"); break; case MOD_UNLOAD: destroy_dev(sdev); FREE(echomsg,M_ECHOBUF); printf("Echo device unloaded.\n"); break; default: err = EINVAL; break; } return(err); } int echo_open(dev_t dev, int oflags, int devtype, struct proc *p) { int err = 0; uprintf("Opened device \"echo\" successfully.\n"); return(err); } int echo_close(dev_t dev, int fflag, int devtype, struct proc *p) { uprintf("Closing device \"echo.\"\n"); return(0); } /* * The read function just takes the buf that was saved via * echo_write() and returns it to userland for accessing. * uio(9) */ int echo_read(dev_t dev, struct uio *uio, int ioflag) { int err = 0; int amt; /* How big is this read operation? Either as big as the user wants, or as big as the remaining data */ amt = MIN(uio->uio_resid, (echomsg->len - uio->uio_offset > 0) ? echomsg->len - uio->uio_offset : 0); if ((err = uiomove(echomsg->msg + uio->uio_offset,amt,uio)) != 0) { uprintf("uiomove failed!\n"); } return err; } /* * echo_write takes in a character string and saves it * to buf for later accessing. */ int echo_write(dev_t dev, struct uio *uio, int ioflag) { int err = 0; /* Copy the string in from user memory to kernel memory */ err = copyin(uio->uio_iov->iov_base, echomsg->msg, MIN(uio->uio_iov->iov_len,BUFFERSIZE)); /* Now we need to null terminate */ *(echomsg->msg + MIN(uio->uio_iov->iov_len,BUFFERSIZE)) = 0; /* Record the length */ echomsg->len = MIN(uio->uio_iov->iov_len,BUFFERSIZE); if (err != 0) { uprintf("Write failed: bad address!\n"); } count++; return(err); } DEV_MODULE(echo,echo_loader,NULL); To install this driver you will first need to make a node on your filesystem with a command such as : &prompt.root mknod /dev/echo c 33 0 With this driver loaded you should now be able to type something like : &prompt.root echo -n "Test Data" > /dev/echo &prompt.root cat /dev/echo Test Data Real hardware devices in the next chapter.. Additional Resources Dynamic Kernel Linker (KLD) Facility Programming Tutorial - Daemonnews October 2000 How to Write Kernel Drivers with NEWBUS - Daemonnews July 2000 Block Devices A block device driver transfers data to and from the operating system's buffer cache. They are solely intended to layer a file system on top of them. For this reason they are normally implemented for disks and disk-like devices only. Example test data generator ... Example ramdisk device ... Real hardware devices in the next chapter.. Network Drivers Drivers for network devices do not use device nodes in ord to be accessed. Their selection is based on other decisions made inside the kernel and instead of calling open(), use of a network device is generally introduced by using the system call socket(2). man ifnet(), loopback device, Bill Pauls drivers, etc.. PCI Devices This chapter will talk about the FreeBSD mechanisms for writing a device driver for a device on a PCI bus. Probe and Attach Information here about how the PCI bus code iterates through the unattached devices and see if a newly loaded kld will attach to any of them. /* * Simple KLD to play with the PCI functions. * * Murray Stokely */ #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #include <sys/types.h> #include <sys/module.h> #include <sys/systm.h> /* uprintf */ #include <sys/errno.h> #include <sys/param.h> /* defines used in kernel.h */ #include <sys/kernel.h> /* types used in module initialization */ #include <sys/conf.h> /* cdevsw struct */ #include <sys/uio.h> /* uio struct */ #include <sys/malloc.h> #include <sys/bus.h> /* structs, prototypes for pci bus stuff */ #include <pci/pcivar.h> /* For get_pci macros! */ /* Function prototypes */ d_open_t mypci_open; d_close_t mypci_close; d_read_t mypci_read; d_write_t mypci_write; /* Character device entry points */ static struct cdevsw mypci_cdevsw = { mypci_open, mypci_close, mypci_read, mypci_write, noioctl, nopoll, nommap, nostrategy, "mypci", 36, /* reserved for lkms - /usr/src/sys/conf/majors */ nodump, nopsize, D_TTY, -1 }; /* vars */ static dev_t sdev; /* We're more interested in probe/attach than with open/close/read/write at this point */ int mypci_open(dev_t dev, int oflags, int devtype, struct proc *p) { int err = 0; uprintf("Opened device \"mypci\" successfully.\n"); return(err); } int mypci_close(dev_t dev, int fflag, int devtype, struct proc *p) { int err=0; uprintf("Closing device \"mypci.\"\n"); return(err); } int mypci_read(dev_t dev, struct uio *uio, int ioflag) { int err = 0; uprintf("mypci read!\n"); return err; } int mypci_write(dev_t dev, struct uio *uio, int ioflag) { int err = 0; uprintf("mypci write!\n"); return(err); } /* PCI Support Functions */ /* * Return identification string if this is device is ours. */ static int mypci_probe(device_t dev) { uprintf("MyPCI Probe\n" "Vendor ID : 0x%x\n" "Device ID : 0x%x\n",pci_get_vendor(dev),pci_get_device(dev)); if (pci_get_vendor(dev) == 0x11c1) { uprintf("We've got the Winmodem, probe successful!\n"); return 0; } return ENXIO; } /* Attach function is only called if the probe is successful */ static int mypci_attach(device_t dev) { uprintf("MyPCI Attach for : deviceID : 0x%x\n",pci_get_vendor(dev)); sdev = make_dev(&mypci_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "mypci"); uprintf("Mypci device loaded.\n"); return ENXIO; } /* Detach device. */ static int mypci_detach(device_t dev) { uprintf("Mypci detach!\n"); return 0; } /* Called during system shutdown after sync. */ static int mypci_shutdown(device_t dev) { uprintf("Mypci shutdown!\n"); return 0; } /* * Device suspend routine. */ static int mypci_suspend(device_t dev) { uprintf("Mypci suspend!\n"); return 0; } /* * Device resume routine. */ static int mypci_resume(device_t dev) { uprintf("Mypci resume!\n"); return 0; } static device_method_t mypci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mypci_probe), DEVMETHOD(device_attach, mypci_attach), DEVMETHOD(device_detach, mypci_detach), DEVMETHOD(device_shutdown, mypci_shutdown), DEVMETHOD(device_suspend, mypci_suspend), DEVMETHOD(device_resume, mypci_resume), { 0, 0 } }; static driver_t mypci_driver = { "mypci", mypci_methods, 0, /* sizeof(struct mypci_softc), */ }; static devclass_t mypci_devclass; DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0); Additional Resources PCI Special Interest Group PCI System Architecture, Fourth Edition by Tom Shanley, et al. USB Devices This chapter will talk about the FreeBSD mechanisms for writing a device driver for a device on a USB bus. NewBus This chapter will talk about the FreeBSD NewBus architecture. Architectures IA-32 Talk about the architectural specifics of FreeBSD/x86. Alpha Talk about the architectural specifics of FreeBSD/alpha. Explanation of allignment errors, how to fix, how to ignore. Example assembly language code for FreeBSD/alpha. IA-64 Talk about the architectural specifics of FreeBSD/ia64. Debugging Truss various descriptions on how to debug certain aspects of the system using truss, ktrace, gdb, kgdb, etc Compatibility Layers Linux Linux, SVR4, etc Appendices Dave A Patterson John L Hennessy 1998Morgan Kaufmann Publishers, Inc. 1-55860-428-6 Morgan Kaufmann Publishers, Inc. Computer Organization and Design The Hardware / Software Interface 1-2 W. Richard Stevens 1993Addison Wesley Longman, Inc. 0-201-56317-7 Addison Wesley Longman, Inc. Advanced Programming in the Unix Environment 1-2 Marshall Kirk McKusick Keith Bostic Michael J Karels John S Quarterman 1996Addison-Wesley Publishing Company, Inc. 0-201-54979-4 Addison-Wesley Publishing Company, Inc. The Design and Implementation of the 4.4 BSD Operating System 1-2 Aleph One Phrack 49; "Smashing the Stack for Fun and Profit" Chrispin Cowan Calton Pu Dave Maier StackGuard; Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks Todd Miller Theo de Raadt strlcpy and strlcat -- consistent, safe string copy and concatenation.
diff --git a/en_US.ISO_8859-1/books/developers-handbook/secure/chapter.sgml b/en_US.ISO_8859-1/books/developers-handbook/secure/chapter.sgml new file mode 100644 index 0000000000..805409b691 --- /dev/null +++ b/en_US.ISO_8859-1/books/developers-handbook/secure/chapter.sgml @@ -0,0 +1,409 @@ + + + + Secure Programming + + This chapter was written by Murray Stokely. + + Synopsis + + 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. + + + Secure Design + Methodology + + Writing secure applications takes a very scrutinous and + pessimistic outlook on life. Applications should be run with + the principle of least privilege 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. + + 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. + + + Buffer Overflows + + Buffer Overflows have been around since the very + beginnings of the Von-Neuman 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. + + 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. 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. + + 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. + + 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. + + + + + strcpy(char *dest, const char + *src) + May overflow the dest buffer + + + strcat(char *dest, const char + *src) + May overflow the dest buffer + + + getwd(char *buf) + May overflow the buf buffer + + + gets(char *s) + May overflow the s buffer + + + [vf]scanf(const char *format, + ...) + May overflow its arguments. + + + realpath(char *path, char + resolved_path[]) + May overflow the path buffer + + + [v]sprintf(char *str, const char + *format, ...) + May overflow the str buffer. + + + + + + Example Buffer Overflow + + 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 ) + + +#include stdio.h + +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; +} + + + 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. + + [XXX figure here!] + + Obviously more malicious input can be devised to execute + actual compiled instructions (such as exec(/bin/sh)). + + + Avoiding Buffer Overflows + + The most straightforward solution to the problem of + stack-overflows is to always use length restricted memory and + string copy functions. strncpy and + strncat 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 strcpy when + copying a short string into a large buffer since + strncpy NUL fills up the the size + specified. + + In OpenBSD, another memory copy implementation has been + created to get around these problem. The + strlcpy and strlcat + 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 . The OpenBSD strlcpy and + strlcat instructions have been in FreeBSD + since 3.5. + + Compiler based run-time bounds checking + + 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++. + + 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 : +
"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."
+ +
"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."
+
+ + Recompiling your application with StackGuard is an + effective means of stopping most buffer-overflow attacks, but + it can still be compromised. + +
+ + Library based run-time bounds checking + + 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 + (strcpy, fscanf, + getwd, etc..) and ensure that these + functions can never write past the stack pointer. + + + libsafe + libverify + libparnoia + + + 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. + + +
+
+ + SetUID issues + + 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. + + The real user ID can only be changed by a superuser + process. The login program sets this + when a user initially logs in and it is seldom changed. + + The effective user ID is set by the + exec() functions if a program has its + seteuid bit set. An application can call + seteuid() 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 exec() + functions, the previous value is saved in the saved set-user-ID. + + + + Limiting your program's environment + + The traditional method of restricting access to a process + is with the chroot() 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 + chdir() 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. + + The behavior of the chroot() system + call can be controlled somewhat with the + kern.chroot_allow_open_directories sysctl + variable. When this value is set to 0, + chroot() will fail with EPERM if there are + any directories open. If set to the default value of 1, then + chroot() will fail with EPERM if there are + any directories open and the process is already subject to a + chroot() call. For any other value, the + check for open directories will be bypassed completely. + + FreeBSD's jail functionality + + The concept of a Jail extends upon the + chroot() 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. + + While in a prison, any tests of superuser power within the + kernel using the suser() call will fail. + However, some calls to suser() have been + changed to a new interface suser_xxx(). + This function is responsible for recognizing or denying access + to superuser power for imprisoned processes. + + A superuser process within a jailed environment has the + power to : + + Manipulate credential with + setuid, seteuid, + setgid, setegid, + setgroups, setreuid, + setregid, setlogin + Set resource limits with setrlimit + Modify some sysctl nodes + (kern.hostname) + chroot() + Set flags on a vnode: + chflags, + fchflags + Set attributes of a vnode such as file + permission, owner, group, size, access time, and modification + time. + Bind to privileged ports in the Internet + domain (ports < 1024) + + + Jail 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 suser_xxx 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. + + + POSIX.1e Process Capabilities + + Posix has released a working draft that adds event + auditing, access control lists, fine grained privileges, + information labeling, and mandatory access control. + This is a work in progress and is the focus of the TrustedBSD project. Some + of the initial work has been committed to FreeBSD-current + (cap_set_proc(3)). + + + + + + Trust + + 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. + + 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. + + 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 (readdir(), + readlink(), + getpwxxx(), and all file input. + + + + Race Conditions + + 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. + + 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 access(2) then + open(2) is clearly non-atomic. Users can + move files in between the two calls. Instead, privileged + applications should seteuid() and then call + open() directly. Along the same lines, an + application should always set a proper umask before + open() to obviate the need for spurious + chmod() calls. + + + +