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.
+
+
+
+