Unix has wired into it an assumption that the programmer knows best. It doesn’t stop you or request confirmation when you do dangerous things with your own data, like issuing rm -rf *. On the other hand, Unix is rather careful about not letting you step on other people’s data. In fact, Unix encourages you to have multiple accounts, each with its own attached and possibly differing privileges, to help you protect yourself from misbehaving programs.[22] System programs often have their own pseudo-user accounts to confer access to special system files without requiring unlimited (or superuser) access.
Unix has at least three levels of internal boundaries that guard against malicious users or buggy programs. One is memory management; Unix uses its hardware’s memory management unit (MMU) to ensure that separate processes are prevented from intruding on the others’ memory-address spaces. A second is the presence of true privilege groups for multiple users — an ordinary (nonroot) user’s processes cannot alter or read another user’s files without permission. A third is the confinement of security-critical functions to the smallest possible pieces of trusted code. Under Unix, even the shell (the system command interpreter) is not a privileged program.
The strength of an operating system’s internal boundaries is not merely an abstract issue of design: It has important practical consequences for the security of the system.
To design the perfect anti-Unix, discard or bypass memory management so that a runaway process can crash, subvert, or corrupt any running program. Have weak or nonexistent privilege groups, so users can readily alter each others’ files and the system’s critical data (e.g., a macro virus, having seized control of your word processor, can format your hard drive). And trust large volumes of code, like the entire shell and GUI, so that any bug or successful attack on that code becomes a threat to the entire system.
File Attributes and Record Structures
Unix files have neither record structure nor attributes. In some operating systems, files have an associated record structure; the operating system (or its service libraries) knows about files with a fixed record length, or about text line termination and whether CR/LF is to be read as a single logical character.
In other operating systems, files and directories can have name/attribute pairs associated with them — out-of-band data used (for example) to associate a document file with an application that understands it. (The classic Unix way to handle these associations is to have applications recognize ‘magic numbers’, or other type data within the file itself.)
OS-level record structures are generally an optimization hack, and do little more than complicate APIs and programmers’ lives. They encourage the use of opaque record-oriented file formats that generic tools like text editors cannot read properly.
File attributes can be useful, but (as we will see in Chapter 20) can raise some awkward semantic issues in a world of byte-stream-oriented tools and pipes. When file attributes are supported at the operating-system level, they predispose programmers to use opaque formats and lean on the file attributes to tie them to the specific applications that interpret them.
To design the perfect anti-Unix, have a cumbersome set of record structures that make it a hit-or-miss proposition whether any given tool will be able to even read a file as the writer intended it. Add file attributes and have the system depend on them heavily, so that the semantics of a file will not be determinable by looking at the data within it.