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:
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:
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 and ACLs in Solaris's UFS. 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 or Blaze's Cryptographic file system CFS and are based on an NFS 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%.
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 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.