22.1. When Security Matters
Computer programs are complicated things. Even the most simple "Hello World" program is surprisingly involved. Ignoring everything that happens in the kernel, the C library has to find the right shared libraries, load them into the system, and initialize the standard I/O routines. On the computer used to write this chapter, the full program took 25 system calls, only one of which was the write() call used to print the words "Hello World."
As programs get larger, complexity grows quite rapidly. Most real-world programs accept input from multiple locations (such as the command line, configuration files, and the terminal) and manipulate that input in complicated ways. Any mistakes in this process provide for unexpected behavior, and those surprises can often be manipulated by a savvy programmer with undesirable consequences. Add a few complicated libraries into this mix, and it is very difficult for any programmer (let alone a team of programmers) to fully understand just how a program will react to any particular set of inputs.
While most programs are carefully tested to make sure they give the proper results for correct input sequences, most are poorly tested for unexpected ones. While the kernel has been carefully designed to prevent failures in user-space programs from compromising the system, mistakes in some types of programs can affect the system's integrity.
 A popular way of testing early Linux kernels was to generate a sequence of completely random bytes and start executing them as a program. While this would never do anything particularly useful (let alone write Hamlet), it would quite often cause the kernel to completely lock up. While trying to execute completely random code sequences is not in the kernel's job description, user-space programs should never cause the kernel to stop running properly, so this technique found a large number of bugs that needed to be fixed.
There are three main types of programs whose programmers need to constantly think about security.
Programs that handle data that could come from an untrustworthy source are very prone to vulnerabilities. That data can have attacks hidden in it that exploit bugs in programs, causing those programs to behave unexpectedly; they can often be manipulated into allowing anonymous users full access to the machine. Any program that accesses data across a network (both clients and servers) is an obvious candidate for attacks, but programs as innocuous as word processors can also be attacked through corrupted data files.
 Attacking application software is now the primary method viruses use to spread.
Any program that switches user or group context when it is run (via the setuid or setgid bits on the executable) has the potential of being tricked into doing things as the other (privileged) user, things that the unpriviliged user is probably not allowed to do.
Any program that runs as a system daemon can be a security problem. Those programs are running as a privileged user on the system (quite often they are run as root), and any interaction they have with normal users provides the opportunity for system compromises.
22.1.1. When Security Fails
Security bugs in programs lead to four broad categories of attacks: remote exploits, local exploits, remote denial-of-service attacks, and local denial-of-service attacks. Remote exploits let users who can access the machine's network services run arbitrary code on that machine. For a remote exploit to occur, there must be a failure in a program that accesses the network. Traditionally, these mistakes have been in network servers, allowing a remote attacker to trick that server into giving him access to the system. More recently, bugs in network clients have been exploited. For example, if a web browser had a flaw in how it parsed HTML data, a web page loaded by that browser could cause the browser to run an arbitrary code sequence.
Local exploits let users perform action they do not normally have permission to perform (and are commonly referred to as allowing local privilege escalation, such as masquerading as other users on the system. This type of exploit typically targets local daemons (such as the cron or sendmail server) and setuid programs (such as mount or passwd).
Denial-of-service attacks do not let the attacker gain control of a system, but they allow them to prevent legitimate uses of that system. These are the most insidious bugs, and many of these can be very difficult to eliminate. Many programs that use lock files are subject to these, for example, as an attacker can create the lock file by hand and no program will ever remove it. One of the simplest denial-of-service attacks is for users to fill their home directories with unnecessary files, preventing other users on that file system from creating new files. As a general rule, there are many more opportunities for local denial-of-service attacks than remote denial-of-service attacks. We do not talk much about denial-of-service attacks here, as they are often the result of program architecture rather than any single flaw.
 System quotas prevent this attack from working.