Runtime Enforcement of Noninterference by Duplicating Processes and their Memories

This paper presents a formal model for enforcing noninterference running one process of a program per level in the security lattice. The I/O effects of these processes are isolated from one another by restricting each processes to write only to output channels at the same or higher levels. This approach is intended to be implemented in general purpose operating systems. It is therefore more compatible with existing code. A Linux implementation is briefly described. 1 Pointers and Noninterference In [1] the authors survey the past three decades of information flow security, focusing on work that uses language-based techniques for the specification and enforcement of security policies for data confidentiality. They describe an approach based on type systems for information flow. More precisely, in a security-typed language, the types of program variables and expressions are augmented with annotations that specify policies on the use of typed data. Then, these policies are enforced by compile-time type checking, adding basically no run-time overhead. When we tried to develop a model based on those techniques for a general purpose operating system and existing applications, we found a problem when we faced programs like the one listed in Figure 1. The program would be used to read a secret value with the intention of disclosing it. To understand the program consider the following: sec_malloc() allocates memory in a secure way –for instance it writes zeroes on the memory–; a and b are pointers like in the C programming language [2]. The problem we found in applying traditional techniques to programs using low level pointers is that either (a) programs are rejected as insecure, or (b) they are accepted but are of reduced utility. Static techniques need to know in advance the security level from where read() reads and the ? Mr. Cristiá is also partially supported by CIFASIS – French Argentine International Center for Information Systems and Sciences – Rosario, Argentina a := sec_malloc(5); b : = a; read(x); for i := 0 to x 1 do *(a + i) := 1; endfor for j := 1 to 5 do print(*b); b := b + 1; endfor Figure 1: x stores a secret value between 1 and 5; the program leaks it. security level where print() prints. Then, if we assume that the latter is low (L) and the former is high (H), compile-time mechanisms will reject the program (i.e. they will not compile it) either because there is a flow of information from x into the cells pointed to by a –which, by the way, are the same as the ones pointed to by b–, or because they cannot deal with C-like pointers at all. Under the same assumptions, dynamic techniques must label a and b, and not the cells pointed to by them, with H, because otherwise there would be a leakage, making it impossible to output cells containing low level data using the same pointers. It is worth to mention that accurate labeling of memory cells arises only at the presence of pointers, and not with regular variables. The drawbacks we found in applying language-based techniques are along the same lines as the ones noted in [3] when it is necessary to enforce complex dynamic security policies. The goal of this paper is to present a model (section 3) that warranties classic noninterference [4] of terminating programs written in a high level programming language (section 2) including pointers, I/O, interactions with the underlying operating system (OS) and with a standard semantics, that overcome the problems mentioned in this section. As shown in section 3, noninterference is enforced by an operating system providing a pure runtime mechanism which, at the same time, does not suffer of level creep. Section 4 briefly introduces the implementation of the model in the Linux kernel as part of the Flowx project. The proof that the whole method enforces noninterference is included in the Appendix. 2 The Programming Language and the Operating System The language considered in this paper (Figure 2) has pointers, I/O interactions and system calls, besides the standard elements and structures Expr ::= N | var | ∗var | &var | Expr Expr