next up previous
Next: 2. Design Up: Stackable File Systems as Previous: Stackable File Systems as


1. Introduction

Security has become and integral part of computing in recent years. To provide the strongest possible security to users, security solutions sometimes consume a large amount of resources, and may inconvenience their users too much. In addition, developers find it difficult to port applications to many different security APIs. A file system can alleviate some of these problems as follows:

Performance: An in-kernel file system will perform better than an out of kernel one, because user-level file systems and applications suffer from context switches. Moreover, a file system could be tailored to perform specific intrusion detection actions faster than general purpose audit systems such as Solaris's BSM[25].

Flexibility: Applications and user-level file servers do not have easy access to all system resources. For example, it is easier and more secure to determine the effective user ID of a process inside the kernel than in a user-level NFS-based file server, where that information can be faked by spoofed RPC calls or trojanized shared libraries.

Scalability: When a facility as basic as the file system offers security features, existing user applications can automatically benefit from such features with little or no modification (e.g., text editors and encryption). In contrast, many applications now must implement their own security. New user applications also gain from such added file system security, rather than have it become a design afterthought.

Standardization: A file system provides a common API to all user applications, speeding up development and porting of new applications. Conversely, today's secure applications often use different security facilities.

Security: Code that runs in the kernel is generally harder to track, replace, or circumvent than user-level code. Access to kernel state and direct manipulation of kernel objects is often more difficult, and involves manipulation of kernel memory (e.g., via /dev/kmem). User processes are susceptible to root attacks, but the kernel runs in privileged mode and can even prevent the root user from certain actions until they are authorized. While address space separation does not immediately guarantee better security[19], kernel based file system security features may serve to slow attackers.

In addition, user-level file servers (such as those based on NFS) may be open to protocol vulnerabilities. That is, the protocol between the file server and the rest of the system may be compromised easily, especially if the protocol uses the network.

There are several areas where a kernel level file system may be more suitable for supporting security features than a user-level application or a user-level file system:

Cryptography: A file system can offer transparent encryption and decryption to all applications using it.

Access Control: Additional access methods such as ACLs can be added easily to file systems. The file system can take measures to protect against some root initiated attacks by declaring certain parts of a file system as read-only or immutable, or by requiring authentication before certain executables are modified.

Intrusion Detection: A file system can take measures to detect possible intrusions. For example, if a root process whose controlling terminal is not the console is trying to overwrite the /bin/login executable, the file system may elect to deny such a request on the assumption that this could indicate an intrusion. Also, if an attempt is made to turn the setuid bit on a root-owned binary, that request can be rejected by the file system. Moreover, the file system may decide to fake the success of such attempts and at the same time send an alert to a trusted user about a possible intrusion in progress.

Intrusion Avoidance: A file system can decide that no root owned or setuid system binaries can be modified from anywhere other than the console, and even then only after users authenticate themselves to the file system. It could further force the access to a subset of subdirectories in a given file system to read-only, without having to set the whole file system read-only.

Post Break-in Analysis: Attackers often cover their tracks by removing log files and tools used in the break-in. A file system can detect attempts to remove or truncate files and instead rename or make backup copies of them that are hidden from attackers' view.

Several extensible file system interfaces have been proposed in the past decade and a few of those were prototyped[8,17,23]. None of these proposals are available in commonly-used Unix operating systems, because of the significant changes that overhauling the file system interface would require, and the impact it would have on performance. A few small enhancements to kernel-based file systems were implemented over the years. These include immutable files in 4.4-BSD's FFS[12] and ACLs in Solaris's UFS[24]. However, none of these features are widely available. Furthermore, making such changes to existing file systems is often a costly proposal: source access is required, and the work involves deep understanding of that operating system's internals. Once the work is accomplished on one platform, a port to another is almost as difficult as the initial port.

Others have resorted to writing file systems at the user level. These file systems work similarly to the Amd automounter[16] or Blaze's Cryptographic file system CFS[2] and are based on an NFS[20] server. While it is easier to develop code for such user-level file servers, they suffer from poor performance due to the high number of context switches that take place in order to serve a user request, and because of limitations of the NFS (V.2) protocol such as synchronous writes. This limits the usefulness of such file systems. Worse, user-level NFS-based file servers are more vulnerable to protocol and network compromises, since they use RPC mechanisms and the network to communicate with the kernel and user processes.

We advocate a different solution to these problems: writing kernel-resident file systems that use existing unchanged native file systems, and expose to the user an interface (Vnode/VFS) that is generally similar even across different operating systems. Doing so results in performance comparable to that of kernel-resident systems, with development effort on par with user-level file systems.

Specifically, we provide a template Wrapper File System called Wrapfs. Wrapfs can wrap (mount) itself on top of one or more existing directories, and act as an intermediary between the user accessing the mount point and the lower level file system on which it was mounted. Wrapfs can then transparently change the behavior of the file system as seen by users, while keeping the underlying media unaware of the upper-level changes. The templates for Wrapfs take care of many file system internals and operating system bookkeeping, and provide the developer with simple hooks to manipulate or modify the data, names, and attributes of the lower file system's objects.

Wrapfs templates exist for several common operating systems: Solaris, Linux, and FreeBSD. Wrapfs can be ported to any operating system with a Vnode interface that provides a private opaque pointer in each of the data structures comprising the interface. The performance overhead imposed by Wrapfs is only 5-7%.

1.1 The Stackable Vnode Interface

Wrapfs is implemented as a stackable vnode interface. A Virtual Node or vnode is a data structure used within Unix-based operating systems to represent an open file, directory, device, or other entity (e.g., socket) that can appear in the file system name-space. A vnode does not expose what type of physical file system it implements. The vnode interface allows higher level operating system modules to perform operations on vnodes uniformly.

One improvement to the vnode concept is vnode stacking[8,17,23], a technique for modularizing file system functions by allowing one vnode interface to call another. Before stacking existed, there was only a single vnode interface; higher level operating system code called the vnode interface which in turn called code for a specific file system. With vnode stacking, several vnode interfaces may exist and may call each other in sequence: the code for a certain operation at stack level N typically calls the corresponding operation at level N-1, and so on. Before calling the next level, a layer may modify file objects and file attributes to provide added security. For example, it can encrypt data pages or check if a key was provided before writing certain files.

Figure 1: A Vnode Stackable File System
\epsfig{file=figures/wrapfs.eps, width=3in, height=2.1in}\vspace{-0.5em}

Figure 1 shows the structure for a simple, single-level stackable wrapper file system. System calls are translated into vnode level calls, and those invoke their Wrapfs equivalents. Wrapfs again invokes generic vnode operations, and the latter call their respective lower level file system specific operations such as UFS. Wrapfs can also call the lower level file system directly, by invoking the respective vnode operations of the lower vnode. It accomplishes that without knowing who or what type of lower file system it is calling.

The rest of this paper is organized as follows. Section 2 discusses the design of Wrapfs. Section 3 details Wrapfs's implementation, and issues relating to its portability to other platforms. Section 4 describes nine examples of security file systems written using the Wrapfs templates and illustrating features such as intrusion detection, intrusion avoidance, analysis, access control, and encryption. Section 5 evaluates the performance and portability of our example file system. We survey related works in Section 6 and offer concluding remarks in Section 7. Two appendices follow, covering implementation and portability issues.

next up previous
Next: 2. Design Up: Stackable File Systems as Previous: Stackable File Systems as
Erez Zadok