The enforcement of security policies for computation

Security policies define who may use what information in a computer system. Protection mechanisms are built into a system to enforce security policies. In most systems, however, it is quite unclear what policies a mechanism can or does enforce. This paper defines security policies and protection mechanisms precisely and bridges the gap between them with the concept of soundness: whether a protection mechanism enforces a policy. Different sound protection mechanisms for the same policy can then be compared. We also show that the “union” of mechanisms for the same program produces a more “complete” mechanism. Although a “maximal” mechanism exists, it cannot necessarily be constructed.


i. Introduction
Society differentiates kinds of information and endeavors to control its use. Out of concern for privacy of individuals and the cost of information theft, society has techniques for controlling who can obtain certain information, when they can obtain it, what uses they can put it to, and even who can produce it. We now symbolically represent sensitive information within computer systems. The control of information dissemination has proven to be as difficult to implement in computer systems as in the rest of society.* It is currently the subject of study by many researchers: Bell  Both authors were supported in part by the Rand Summer Institute on Security.
The need for precise and complete understanding of the basic questions in the security area is mandatory.
To illustrate this, compare security enforcement flaws to compiler design flaws. When a compiler error occurs, the users complain and demand correction.
On the other hand, when a security error occurs, the violator does not disclose the system flaw that allowed him to perform prohibited actions.
Often in the case of information theft no trace remains to show that one user read information private to another. For these reasons precision and proofs are not a luxury but a necessity.
While precision and proofs are required, in order to be credible the basic framework must be simple and clear. No one will believe an unstructured system is secure, Just as no one will believe that a formal proof is correct if it is long, poorly structured, and based on imprecisely defined terms. We conclude that to be useful the basis of a theory in the security area must be simple.
This paper develops the basic notions relevant to security, providing a framework in which the underlying principles of security can be investigated.
We believe it to be precise, yet simple.
As we illustrate, a clear understanding of these basic notions makes evident the inadequacies of some of the proposed approaches to providing security.
The basic elements of our theory are precise definitions of programs, of protection mechanisms, and of security policies.
A security policy defines what information is to be protected; it has a non-procedural form.
For example, a security policy might state that a user is not to obtain "top secret" information.
In contrast, a protection mechanism defines how information is to be protected; it has a procedural form. For example, a protection mechanism might check each operation performed by a user.
Soundness defines the bridge between the nonprocedural security policy and the procedural protection mechanism.

Basic Model
Security enforcement involves restrictions on the behavior of computer programs.
These restrictions involve determining whether or not the output of a program encodes the proper information.
This determination clearly depends only on the functional behavior of the program: If two programs are functionally equivalent, then their outputs certainly encode the same information. This observation leads to our view of programs simply as functions from some set of inputs to some set of outputs.
Definition: Define Q to be a program provided where Q is a total function and D i is the range of the ith input and E is the range of the output.
As demonstrated by Parnas [9] and extended by Neumann et al. [7], programs can have two distinct types of functions.
They can be used as "V functions," i.e. they can be used to "view" their "inputs." On the other hand, they can be used as "0 operators," i.e. they can be used to "operate" on their inputs.
(Here input corresponds to the current state of the system.) Consequently, there are two types of security questions.
Suppose that Q is a program, i.e.
If Q is used as a view function, then the security question is: Does the value of Q(d I ..... ~) contain any information that it should not?
This is the so-called "confinement" or "memoryless subsystem" question studied by Lampson [6], Schroeder [12], and Fenton [4]. On the other hand, if Q is used as an operator function, then the security question is: Has the function Q altered any information that it should not?
This second question has sometimes been called "data security" (Popek [I0]). It concerns itself with whether or not information, such as a system table, has been illegally altered and hence lost.
In the rest of this paper we will study the first security question, i.e. the case where Q is used as a view function. We do, however, assert without proof that the same methods used here to study this case can also be used to study the second case. Let us now examine some examples.
E~nrple I: Fenton [4] studies programs Q of the form Q: D 1 × ... × D k + E where D. and E are the set of natural numbers. i The value Q(dl,...,dk) is the value obtained by the computation of some given Minsky-machlne that was starte@ with its ith register containing d i.
We will return again to this example.
Example 2: Consider a program Q of the form Here D i is the set of possible values for the ith "directory"; F i is the set of values for the ith "file." The value of Q(dl,...,dk, fl,...,fk) is the result of some file manipulation program. In this example the ith directory will contain information about who can access the ith file.
We wish, therefore, to know whether or not Q(dl,...,dk, fl,...,fk) contains any information about a file that was to be denied to us. We will return again to this example. This is elaborated later on.
We now turn our attention to the study of protection mechanisms.
A protection mechanism acts as a "gatekeeper": It suppresses or alters the output of the program it is protecting. In an operating system the protection mechanism is usually interleaved with the execution of the program being protected. This is not, however, necessary.
One can imagine a static protection mechanism that is implemented at compile time. Therefore, our definition of protection mechanism must be able to accommodate a wide variety of possible types of mechanisms. A protection mechanism acts as follows: Suppose that (dl,...,dk) is some possible input.
Then the protection mechanism can either give Q(dl,...,dk) to the user or return a violation notice.
The user is to interpret the violation notice as: "It looks as if you (the user) have attempted to view information that is to be denied to you; hence, I (the protection mechanism) am giving you this message." Then there are two trivial protection mechanisms for Q. The first is the program Q itself.
This corresponds, of course, to no protection at all.
The second is the program Second, Fenton allows an unusual type of violation notice. In his case the violation notices (the set F) and the possible output of the original program Q (the set E) need not be disjoint.
The set F includes the results of partial computations of the program Q. Thus it may be difficult for a user to determine whether or not he is getting Q(d I .... ,dk) ; (d I .... ,dk) is, after all, both the input and a violation notice.
Practically speaking, this difficulty may make it particularly hard to find program bugs that cause violation notices.

Exe~ple 2 continued:
A violation notice in our simple file system might be a message of the form "Illegal access attempted, run aborted." The purpose of a protection mechanism is to control information; in our framework this leads to the notions of security policy and soundness.
Definition: A security policy I for the program Q: D 1 × ... × D k + E is a function from D I × ... × D k to ~ where D is a new set.
The key relation between a protection mechanism and a security policy is whether the mechanism enforces the policy.
This relation is called "soundness." A security policy, therefore, acts as an "information filter." A mechanism M is sound provided it behaves as if it received as input not (dl,...,dk) but rather l(dl,...,dk). The value of l(dl,...,dk) has presumably filtered out all the information that was to be denied to the user. Let us now examine some security policies. Each of these security policies is characterized by either allowing information or allowing no information about some input. This type of security policy is the type that is studied in detail here.

Definition:
These pollcles are the ones most commonly supplied in real systems.
For example, after a user logs on to a system, the files in the system can be divided into two classes: those he should be able to access and those he should not, (This partition of files may change with time, but at any instant such a partition exists.) Because of the importance of these policies we introduce a shorthand for them: Our definition of security policy is oriented towards obtaining a simple framework.
The definition of security policy as a function, even with the allow shorthand, is not suitable for use in a real system. In such a system much more powerful shorthands are needed.
For example, the "*" = all convention used in Multics (Organick [8]). These issues are not addressed here.
We will now present a series of examples. These examples should help in understanding the basic notions of security polley and soundness.
Example 3 continued: Clearly the protection mechanism that always outputs ^ is sound for any security policy.
A program as its own protection may or may not be sound. Then Q as its own protection mechanism (see example 3) is unsound.
The reason this program is workable in practice is that the amount of information obtained by the user is "small." Example 6: The security policies studied here are "information control" policies.
They must be distinguished from "access control" policies. For example, enforcing an access control policy that specifies that the operation READFILE(A) cannot be performed is not the same as insuring that information about A is not extracted.
There may be a sequence of operations excluding READFILE(A) that has the same effect as READFILE{A).

ExcmTple I continued:
Fenton's protection mechanism i~ not completely defined.
Indeed one reasonable interpretation of it is unsound.
The difficulty is the halt statement: if P = null then halt.
What happens if P # null? One possibility is that in this case an error message (i.e. a violation notice) is output. This is, however, unsound, as the following program demonstrates: y := 0; if x = 0 then begin y := i; halt end; halt This program will output an error message if and only if x = 0. If the security policy wishes to deny information about x, then this is unsound. Intuitively, the difficulty here is what we call "negative inference." When the program behaves correctly (i.e. does not try to halt when it should not) everything is fine; it fails when the program behaves incorrectly.
A classic negative inference is due to A. C. Doyle [3]: Inspector: Is there any other point to which you would wish to draw my attention? Holmes: To the curious incident of the dog in the nighttime.
Inspector: The dog did nothing in the nighttime.

Holmes:
That was the curious incident.
We next relate the observabillty postulate and the concept of soundness.
Consider the following program Q: We consider x as the input and y as the output; hence, for any x, Q(x) = i. For the security policy allow(), i.e. allow no information about x, the program Q as its own protection mechanism is sound. This follows trivially since Q is a constant function.
We can, however, simply observe the running time of Q to determine whether or not x = 0. This difficulty stems not from the definition of soundness but rather from a violation of the observability postulate: The running time of Q was an implicit output.
This example is compatible with our framework as follows: Let a program's output be not Just its computed output value hut also its running time, which records the elapsed real time, the elapsed compute time, or the number of steps executed. Now returning to program Q the observability postulate is valid.
In particular Q(x) = (i,T), where T is the number of steps executed.
Clearly this program is not sound for the policy allow().
This method is used in section 3 to show how our surveillance protection mechanism can handle running time.
One further example should reinforce the subtlety and importance of the relation between soundness and the observability postulate.
Let programs have inputs that are placed on a linear one-way read-only tape with the head initially at the leftmost character: where each z i is a block of characters. Consider a security policy allow(2), i.e. allow information only about the second block. Then we claim that no program Q can read z 2 and also be sound, provided running time is observable.
This follows since, in order for Q to get to the part of the tape where z 2 is stored, it must move across z I.
Even if Q does not "look" at Zl, it will encode the length of z I into the computation of Q; hence, Q will not be sound.
How can we avoid this problem?
One answer is to add a new operation, say tab (i).
This operation in one step causes the read head to Jump directly to the ith block. Now Q can read z 2 and he sound. But a new problem arises: Is the observability postulate valid? Perhaps tab(i) takes time dependent on the length of Zl,...,Zi_l? This is the crux of the problem, and there seem to be two answers: Either (i) run tab(i) so that it uses constant time, or (2) apply our methods recursively to tab(i).
While soundness is the most important bridge between protection mechanisms and security policies, the central issue is not just to construct sound protection mechanisms.
The protection mechanism that always outputs some fixed violation notice is certainly sound -recall example 3. It is also useless.
(It's equivalent to pulling the computer's plug out of the wall.) We are therefore led to consider the concept of how "complete" a protection mechanism is. The relation ~ is a partial ordering on the protection mechanisms for a given program and policy.

Definition:
Also, ~ is a practically motivated ordering: Consider a single output program with two protection mechanisms M I and M 2. M I > M 2 implies that M I never gives a violation notice when M 2 does not.
This implies that the utility of M I is at least as high as that of M2, for one is interestedonly in getting non-violation notices.
We can show how to "join" two sound protection mechanisms to form a new sound one that is as complete as each of the other two.  (Note that M, while well defined as a function, may not he recursive --even if Q is.) We have established that the maximal protection mechanism exists, but as we shall show later, it cannot always be constructed.
We have now completed the development of our security model.
The value of any model is in its useful application.
We find that having the model's precise definitions, which are independent of any particular model of computation or any mechanics of protection mechanism implementation, aids in understanding and appraising work in the security area.
For example, we have already noted that if all pertinent observables cannot be specified then the "forgotten" observables provide a means for leaking i~formation.
Such was the case for abe PDP-iO Tenex system where the presence or absence of page faults could be used to ob~aln passwords.
In sun~nary, we should point out that we have informally used words like "information flow" and "dependence." What these terms mean is precisely captured by the definition of soundness. Though our security model can be easily understood informally, it is mandatory that it be precisely defined as well.
Without a precise basis on which to build, one will never be able to make progress in convincing others about the security properties of a system.

Surveillance Protection Mechanism
This section both illustrates a new protection mechanism and develops the framework put forth in the preceding section.
In order todefine this mechanism we will restrict our programs to be flowcharts with a single output and policies of the form allow(...).
We will then show how to assign to each flowchart and security policy a protection mechanism called the surveillance protection mechanism, This mechanism and a modified version that ignores runtime are then proved to be sound.

De~n~tion: A flowchart F with input variables
Xl,...,x k and with program variables ri,...,r m and with output variable y (i.e. those variables used for temporary values) is a finite connected directed graph whose nodes are boxes of the form:

[ LT 3
Here B(W) is a predicate and E(W) is an expression.
We ascribe to the flowchart F the usual semantics : The values of the variables (input, program, and output) are the integers. There is exactly one start box; execution begins there by initializing all the program variables and the output variables to 0. Since we view the flowchart F as computing a program Q with domain Z × ... x ~ (~ = integers), we assume that each input variable is initialized to the value that corresponds to the current input.
Thus if (al,...,ak) is the input value, then x i is initialized "to a i. Execution then follows the logic of the flowchart; at a decision box, the path that corresponds to the predicate's truth value is taken. Please note that no specific assumptions are made about what predicates or expressions are allowed: Any reasonable choice is allowed (i.e. so long as predicates and expressions are recursive there is no difficulty.) The observability postulate makes it necessary to state exactly what the range of Q (the program computed by F) is. Here we will assert that we will study these flowcharts under two distinct assumptions.
First, we will study the case where the range of Q is ~ x ~. That is, the value of Q(~) has two components.
The first is ~he value of y when the flowchart halts; the second is the number of steps executed by the flowchart, i.e. the "time" used by the flowchart in computing the value of y. Therefore, we will be encoding the running time of our flowcharts. (Note that we might have chosen any of a number of attributes other than running time, but we feel that it is representative.) Second, we will also study the case where the range of Q is just ~ In this case the value of Q(a) has only one component: the value of y when the flowchart execution halts. This second case is the one where we assume that running time is not observable to a user.
We next describe the surveillance protection mechanism.
It is based on the idea of keeping track of what inputs a variable may "depend on." Essentially we will associate with each variable in the program a new variable v. The value of v will be the set of indices of input variables that may have affected the current value of v in some way.
A key point here is that we must keep track of v not only for input, program, and output variables but also for the program counter of the program.
The need to do this for the program counter is independently illustrated in Fenton [4]. Therefore, let the program counter of Q be denoted by C. We will now construct the surveillance # We will now identify programs with flowcharts.

Def~n~.~on:
protection mechanism for the case where time is not part of the output (i.e. when the range of Q is just Z). Then the surveillance protection mechanism M corresponding to the program Q and some security policy I = allow(J) is constructed as follows.
The variables of M are the variables of Q plus the surveillance variables. M is obtained from Q by applying the following transformations: Here ^ is a symbol that is not a normal output of Q; it is used as a violation notice. Recall that I = allow(J). Clearly, given a program Q and a security policy I, M is indeed a protection mechanism. Indeed, M is always sound: f~eorem $: If M is the surveillance protection mechanism for the program Q and the security policy I, then M is sound for Q and I provided running times are not observable.
In contrast, it is easy to see that M is unsound when running time is observable. Indeed, both Fenton [4] and Denning [2] point out that it is an interesting problem of how to handle running time. We will now show how our surveillance protection mechanism can be modified to handle running time and still remain sound.
Definition: Suppose that Q is a program and I is a security policy. Then construct the surveillance protection mechanism M' for the case where running time is observable as follows:

Comparison of Protection Mechanisms
In the last section, the surveillance protection mechanism was presented. It is sound under the assumption that running time is not observable (Theorem 3). As noted earlier, soundness is only part of the story; in this section we consider the comparison of protection mechanisms. While soundness is all or none, completeness provides a measure with which to order these mechanisms.
A possible alternative protection me~anism is the hish-water mark protection mechanism. It is related to Me mechanism in Weissman [14]. It is based on the same transformations as we used in constructing the surveillance mechanism except Sat (2) is changed to: Intuitively, the hlgh-water mark mechanism is based on the assumption that there exists an ordered sequence of security classifications, e.g.
public < confidential < secret < top secret" The classification of each variable is initially known and is recorded in an associated surveillance variable. If a variable ever assumes a classification, its future classification is always at least as high.
We will now compare these two mechanisms. Suppose that M is the surveillance protection S mechanism for some program Q and security policy I and M h is the high-water protection mechanism for Q and I. It is easy to see that M s ~Mh, i.e. M s is at least as complete as M h. The following simple flowchart and the policy allow (2) shows that M s ~ M h is possible: In this case ~ always outputs ^; on the other hand, M s outputs ^ only when x 2 ~ 0. Intuitively, surveillance is better here, since it allows "forgetting" while high-water mark does not.
While surveillance is more complete than hlgh-water mark, it is not maximal, i.e. it is not the mechanism that produces the fewest violation notices.
In order to see this, consider the following program Q:

/\
Let I = allow(2) be the security policy.
Then the surveillance protection mechanism M for Q and I S always outputs A. Consider, however, the protection mechanism M = Q. It is easy to see max that M x is sound for Q~x and I. Since M ~ M we see that the surveillance protection max mechanism is not maximal.
The reason that the surveillance protection mechanism perfo~ed poorly on Q is that, once the br~ch on x there was taken, the surveillance mechanism was un~le to detect that ~e assi~ment to y was ~dependent of x I. For ~e remaining part of this section we will investigate how to modify surveillance so as to m~e it more complete.
(We will continue to assume that programs do not have their running times as observ~les.) As a step ~ this direction, suppose that we modify surveillance so that it can detect flowchart occurrences of the fo~: f For these if then else constructs, could we make all future computations independent of the test that determined whether the then or the else path was to be taken so ~at the result~g protection mechanism is still sound?
The answer is yes ~d is demonstrated by looking first at the example if then else of Q:

' I | w ÷ f(xl) I
Now the surveillance protection mechanism for Q' and I = allow(2) a~ays gives the output 1; clearly it is maximal.
This example is just an instance of a general w~ to generate many different protection mechanisms: Given a program Q, transfo~ it to Q' where Q and Q' are functionally equivalent.
Then apply the surveillance protection mechanism to Q' to yield a sound protection mechanism for Q. The if then else transform above is but one of many.
For instance, we could create a for loop transform that operates on FALSE in a way analogous to the if then else transform. Indeed, transforms can be created for all single-entry and single-exit structures.
Is the application of such transformations always advisable?
Unfortunately, the answer is no. Consider the program Q: _ Let I = allow(2) be the security policy again. Let M be the surveillance protection mechanism for Q and I; also let M' be the protection mechanism that corresponds to using the if then else transform on Q. M' is the surveillance protection mechanism for the following program: The danger is that since one does not know which branch is to be taken one must assume the worst case.
In summary, whether to apply a transform or not is seldom a clearcut decision.
But the optimal strategy for deciding is not, as the next theorem shows, computable.