Check out the new USENIX Web site. next up previous
Next: Attribute Mapping Up: Implementation Previous: Implementation


Gaining control

In order to provide protection, LOMAC must gain supervisory control over kernel operations - that is, LOMAC must be able to make access control decisions as described in section 2, and compel the kernel to enforce them. LOMAC achieves this control by interposing itself between processes and the kernel at the kernel's system call interface. LOMAC's kernel interface contains a series of functions called ``wrappers,'' due to their similarity to Generic Software Wrappers [10]. Ultimately, there will be one such wrapper for each security-relevant Linux system call; some wrappers have not yet been implemented in the present version of LOMAC. Each wrapper takes the same parameters as its corresponding system call. At initialization time, LOMAC traverses the kernel's system call vector, which is essentially an array of function pointers through which the kernel provides services to user processes. LOMAC replaces the addresses of security-relevant system calls with the addresses of the corresponding wrappers. Once done, calls made through the system call vector will call the wrappers, rather than the kernel's corresponding system call functions.

Wrappers follow the algorithm shown in figure 3. First, LOMAC performs mediation: it decides whether to allow or deny the calling process's request for service. It bases this decision on a comparison of the calling process's level and the levels of the arguments, as described in section 2. If LOMAC decides to deny, it returns an appropriate error code to the caller. Otherwise, LOMAC proceeds to the next step, where it calls the kernel's original system call function to provide the actual service. Finally, LOMAC monitors the completion of the kernel's original system call, updating its data structures to reflect changes in the system state. This is where LOMAC demotes processes, and marks the in-memory data structures representing open files (dentry structures) with the appropriate levels for future reference.

Figure 3: Wrapper Algorithm
\begin{figure}\begin{center}
\begin{verbatim}wrapper( arguments ){
Mediate: d...
...LOMAC's state
on successful completion;
}\end{verbatim}\end{center}\end{figure}

Figure 4: C source for LOMAC v1.1.0's wrapper for sys_open (run-time assertions and most comments removed).
\begin{figure*}\begin{verbatim}01: int wrap_open( const char *filename, int fl...
...e_s );
49: return( ret_val );
50: } /* wrap_open() */\end{verbatim}\end{figure*}

Viewed from a high level of abstraction, this interposition-based wrapper algorithm is not overly complex. However, implementing it in a manner that avoids Time-Of-Check, Time-Of-Use (TOCTOU) errors requires care [11,26]. Early versions of LOMAC had many TOCTOU errors: Wrappers would copy user-space pathname arguments into kernel-space, and make mediation decisions based on these copies. After positive decisions, the kernel's original system call functions would copy the pathnames into kernel-space a second time, and operate on this second copy. The potential existed for a user process to make a system call with an allowable pathname and change it to a non-allowable pathname after LOMAC had made its mediation decision, but before it called the kernel's original system call function. This ability to change pathnames between the time of LOMAC's check, and the time the kernel used the pathname gave user processes the opportunity to defeat LOMAC's protection.

Figure 4 illustrates the solution to the TOCTOU problem: copy pathname arguments into kernel-space at the beginning of the wrapper, and invoke the kernel's original system call with the address of this copy, rather than the address of the original user-space buffer. The figure contains the C source for LOMAC's open system call (sys_open) wrapper. The source shows the additional buffer-copying, as well as the unusual toggling of the Linux kernel's sense of the kernel-/user-space boundary required to make the its original system calls accept these copies.

In its first 18 lines, the wrapper examines its arguments, gathering the information it needs in later steps. Line 7 copies the filename argument into kernel-space to avoid TOCTOU errors. All subsequent operations are on this copy, rather than the user-space original. LOMAC determines the levels of files based on their absolute canonical-form pathnames using an algorithm discussed in the next subsection. Line 14 prepares the filename for its level determination by converting it into this form.

The nested if statements in lines 19 through 21 ensure that LOMAC performs mediation only when there is the potential for a file creation or truncation. LOMAC does not mediate writes to files in the open wrapper. This mediation is handled by other wrappers corresponding to the Linux kernel's various write system calls. The WRITE_EXEMPT macro on line 21 exists to allow harmless truncates of device special files such as serial lines and terminals. Similar exemptions exist in the write system call wrappers. These exemptions allow low-level processes to perform I/O on these devices, while keeping the device special files themselves in the high-level part of the system.

Lines 22 through 32 perform the actual mediation. Before allowing the open, LOMAC makes checks both on the file and on its parent directory, as traditional UNIX does. Line 22 ensures that the calling process has sufficiently high integrity level to modify the contents of the named file's parent directory. Line 26 ensures that the calling process has a sufficiently high integrity level to create or truncate the named file. These checks are handled by functions in the kernel-independent part of the LOMAC LKM.

Lines 33 through 36 invoke the kernel's original system call function using the wrapper's kernel-space copy of the filename argument. When serving user processes, the kernel's system calls expect to copy their pathname arguments from user-space. Before copying, the system calls execute a check to ensure that the pathname buffer address is indeed on the user side of the kernel-/user-space boundary - a check that will normally fail on the wrapper's kernel-space pathname buffers. Fortunately, the kernel provides a mechanism to disable this check on a per-process basis. The macros on lines 33 and 36 toggle this check off for the duration of the original system call function. For safety, the canonical-absolute pathname conversion function on line 14 performs the safety checks that LOMAC turns off in the original system call.

Lines 37 through 50 conclude the wrapper. If the open system call succeeded in opening a file, lines 37 though 41 call LOMAC's kernel-independent open monitoring function to label the file's in-memory data structure (dentry) with the appropriate level. The various read and write wrappers will subsequently use this label when they mediate and monitor operations on the file.

As shown in figure 4, it takes a considerable amount of wrapper code to support mediation and monitoring in an interposition-based scheme. The extra buffer copy to avoid TOCTOU errors adds overhead. Similarly, many wrappers contain nested if statements like those in lines 19 through 22 to predict, based on the arguments, what operation the kernel will eventually perform. The read and write wrappers require more extensive logic, because these system calls must handle operations on a variety of objects (files, pipes, sockets), each of which requires different mediation and monitoring.

An alternative approach to gaining control might be to patch the kernel source, placing mediation and monitoring further down in the kernel, at the point closer to where it operates on objects. This move would reduce overhead by eliminating the extra TOCTOU buffer copies and the need to predict the kernel's behavior ahead of time. However, this patching strategy is not presently an option for LOMAC, which must avoid modifying kernel source in order to maintain compatibility with existing kernels.


next up previous
Next: Attribute Mapping Up: Implementation Previous: Implementation
2001-04-30