Under the Domain Type
Enforcement (DTE) abstraction, each process executes within a domain and objects are assigned a type. A domain definition specifies the
rights each domain has to objects of a given type. All processes executing within the same
domain have the same set of access rights.
The assignment of
processes to domains provides a flexible mechanism to implement a security
policy. A domain might represent
all processes associated with ordinary users, or it might represent a system
daemon such as the network time protocol daemon. A domain need not be associated
with the rights of any single user.
Most other security models assign rights based on some characteristic of
a user, for example her security clearance (Multilevel Security), his role
(i.e., job) in the organization (Role Based Access Control), or his identity (standard
UNIX access control).
A primary advantage of
DTE is that it allows the administrator to find an appropriate balance between security
(POLP) and policy complexity. For
example, from the perspective of a system administrator, the access
requirements of all users may be sufficiently similar that they would be
collected into a single domain. While
this might violate the principle of least privilege, the domain definition
could disallow processes in this domain from modifying the files associated
with system configuration and operation.
This likely meets the overall security goals for a system in which user
data is not critical, but maintenance of a working platform is important. An educational setting might have these
security goals. If a more fine-grained approach is required, the domain could
be subdivided, for example into domains that correspond to each major area of
study: computer science, history, etc.
Domain and Type
Enforcement (DTE) was developed by Badger, et al. with the goal to improve
simplicity and compatibility of the flexible and strong type enforcement (TE)
access control mechanism
We will look at DTE by
example. Consider Policy 1 below. It gives each process all possible
permissions to every file on the system.[1]
The policy is written in Simplified Domain Type Enforcement Language (SDTEL).[2]
A DTE policy is composed
of four statement types: type,
domain, initial_domain and assign. At
least one statement of each of these four types must appear in the policy.
In SDTEL, the type statement
should appear first. Next the domain statement(s) should appear.
The initial_domain statement
comes next, followed by one or more assign
statements.
Policy 1: Unrestricted Access
A type statement enumerates the set of all types that are part of the policy. There is only one type in Policy 1: unrestricted_t.
The initial_domain
statement causes the initial process to run
in the specified domain, unrestricted_d
in this case.
A domain statement specifies one or more entry point programs as well as the access processes running in the domain have to objects of each type. The entry point program must be invoked in order for an executing process to enter the domain. In this policy there is one domain: unrestricted_d. The entry point program is /sbin/init.
Processes in the unrestricted_d
domain have c,d,r,w, and x permissions
to files of type unrestricted_t. The
d
permission applies to directories and is
similar to the x permission to directories on UNIX. The r, w, and x permissions
are similar to r, w, and
x
on UNIX. The c permission indicates the default type for objects created by
processes in the domain.
There is only one object
type in Policy 1, so the domain statement for unrestricted_d explicitly
specifies the access this domain has to all system objects. When there is more than one object type,
the default is for processes to have no access. A domain statement must explicitly grant
access to each type that is required by processes in that domain.
The assign statement specifies exactly one type for each file in the system. More specifically, it associates a type with a path. A recursive assignment, indicated by -r, applies to all files whose path has the indicated prefix. In Policy 1, there is only one assign statement. It assigns the type unrestricted_t to every file on the system whose path begins with /. Hence, all files in the directory tree rooted at / have the same type: unrestricted_t.
The single domain unrestricted_d
in Policy 1 is allowed all permissions to the only file type unrestricted_t. In implementation, DTE is layered
over the standard UNIX permissions.
Once a request passes the DTE restrictions, it will be subject to a UNIX
permissions check. Hence, under
Policy 1, the UNIX permissions contain all the restrictions on running processes. In order to simplify the discussion,
from this point we will not consider the UNIX restrictions imposed in addition
to a DTE policy.
Policy 1 illustrates important DTE and SDTEL basics, but does not impose any real restrictions on access to system objects. For our next example, we suppose that we want to restrict the files accessible to an application called simpleDaemon. The application is implemented by the binary /bin/simpleDaemon. This application only requires read and write access to the file /etc/simpleDaemon.txt. It does not require access to any other files on the system. We want to add these restrictions to those of Policy 1 without making any other changes to the access processes have to objects.
Abstractly, we can create a new domain for
the application. Processes in the
domain will only have read and write access to /etc/simpleDaemon.txt and /bin/simpleDaemon will run in this new domain.
Policy 2: Simple Daemon
Policy 2 implements
these additional restrictions.
(Line numbers have been
added as a
convenience.) Policy 2 contains an
additional type simpleDaemon_t (line 01).
This type is associated with the file that the simpleDaemon
application is allowed to access.
Notice that the assign statement (line 14) is not recursive. Longer path associations override
shorter ones. Hence, the assignment
to “/etc/simpleDaemon” will override the association to “/”. We
have also added a second domain, simpleDaemon_d
, which represents the simpleDaemon
application. This domain only has
read and write access (rw) to simpleDaemon_t.
Under DTE, the domain
determines the access that is allowed.
So we define multiple domains that grant access to different sets of
processes. A relevant question then
is how a process begins execution in a particular domain and whether a process
can move from one domain to another.
By default, when a process
in domain X creates a new process, the new process also runs on domain X. In order for a process to be created in DNEW
we specify a set of domain transitions.
The transition contains three parts: (1) the set of binaries that may be
executed in order to enter domain DNEW (the entry
point programs), (2) the set of domains from which DNEW
can be entered, and (3) whether a transition from DOLD to DNEW
is mandatory or optional. Policy 2
(line 08) specifies that domain simpleDaemon_d
can only be entered through execution of
the binary /bin/simpleDaemon. Components
(2) and (3) are defined via the subject-to-subject permissions definition within
a domain statement. An example is
line 6 of Policy 2. This statement
gives domain unrestricted_d the auto
permission to domain simpleDaemon_d. The auto permission indicates that when the entry point
program of the specified domain (simpleDaemon_d in line 6) is executed, the process will transition to that domain. Policy 2
specifies that whenever /bin/simpleDaemon
is executed from the unrestricted_d domain, the process will run in the simpleDaemon_d domain. The other subject-to-subject permission
is exec,
which specifies that the transition is
optional.
Figure 1: Example Transition into
simpleDaemon_d
Let’s look at domain
transitions in a little more detail.
Remember that on UNIX systems, a new process can only be created via the
fork system
call. When a process running under
DTE in domain X executes this call, the new process will also run in domain
X. The child process from a fork is
essentially a copy of the parent process at the time of the fork. In order to run a different binary, the
child must make a call to some variant of the exec system call. The point at which the call to exec is
executed is the point at which a decision is made on whether a domain
transition takes place. This is
illustrated in Figure 1. It shows
an example transition from unrestricted_d
into simpleDaemon_d.
Initially, a process created from /bin/tcsh is executing in unrestricted_d (part
(a)). This process calls fork, which
creates a child process executing in the same domain (part (b)). Finally, the child process calls execle
with parameters that specify execution of /bin/simpleDaemon.
Since this is the entry point program for simpleDaemon_d and the
subject-to-subject permissions for unrestricted_d specify auto for simpleDaemon_d, the child process executes in simpleDaemon_d. If the exec permission were specified instead of auto, then the child process could execute in either unrestricted_d
or simpleDaemon_d. Note that an additional parameter to exec would
be required to indicate the domain the process should execute within and, in
the case of an optional transition via the exec flag, whether the transition
should take place.[3]
Policy 3: User Account Restriction
Under Policy 2, the
binary /bin/simpleDaemon can only be executed from the unrestricted_d
domain. Whenever this binary is
executed, the process transitions to the simpleDaemon_d domain.
A process in simpleDaemon_d can only read and write the file /etc/simpleDaemon.txt. Hence, the only access
allowed by any process created from /bin/simpleDaemon is read and write to /etc/simpleDaemon.txt.
Notice that we added an
additional type access statement to the unrestricted_d domain definition (line 05). If we had not added this statement,
processes running in the unrestricted_d domain would have no access to /etc/simpleDaemon.txt. Recall that our goal was to restrict access by the simpleDaemon
application, but to leave all the other accesses allowed by Policy 1. If we had not added this to the domain
definition for unrestricted_d, then our policy would not be correct. We would
have removed access to /etc/simpleDaemon.txt that is given to processes other than /bin/simpleDaemon which is allowed within Policy 1.
Figure 2: Shell Execution Under Policy 3
Policy 3 aims to
disallow the system binaries from being written by certain accounts. This policy is taken from “Computer
Security: Art and Science”, by Matt Bishop. The first process created on UNIX
systems, /sbin/init, runs in daemon_d. All the daemons the system spawns during
startup will also run in daemon_d.
On a UNIX system, some variant of getty will run on specified terminals to present the
login prompt. When a user logs in, getty
will spawn login so that a user can authenticate. This policy assumes that users login via
the /usr/bin/login binary.
The auto
transition will cause this process to execute in the login_d
domain. When a user authenticates
successfully, the specified shell will be spawned. This policy assumes that one of sh, csh, or ksh will
be spawned. Through the exec subject-to-subject permission, the shell can run
in either user_d or admin_d.
This is depicted in Figure 2. The login process is trusted to run the
shell in the appropriate domain based on the user authentication.
Notice that once a process enters the user_d domain, neither the process or any of its
descendants can transition out of this domain. Further, this domain does not have write
permission to sysbin_t.
Hence users cannot write to the system binaries. Even if a process in this domain were to
acquire UID 0 or GID 0, it can only write to files to which the domain is
allowed write access. Notice that
processes in the daemon_d are also disallowed write access to the system
binaries. Hence, a compromised daemon
running as root in this domain could not insert a Trojan horse or virus into the
system binaries.
Badger, L.,
D.F. Sterne, D.L. Sherman, K.M. Walker, and S.A. Haghighat. "A Domain
and Type Enforcement UNIX prototype." Proceedings of the 5th USENIX
UNIX Seucrity Symposium. Berkeley, CA: USENIX Association, 1996. 127-140.
Walker, K.M., Sterne, D.F.,Badger, M.L., Petkac,
M.J.,Oostendorp, K.A. "Confining Root Programs with Domain Type
Enforcement (DTE)." Proceedings of the Sixth USENIX UNIX Security
Symposium. San Jose, CA: USENIX Association, 1996. 21-36.
[1] DTE is
typically used in conjunction with the UNIX access controls. A request is first
checked against the DTE policy. If
the DTE policy allows the request, then it is checked against the UNIX permissions. On a real system, the UNIX permissions
would likely impose additional constraints on every process.
[2] SDTEL
implements a subset of DTEL and is intended for use with the DTEvisual system
for learning DTE.
[3]
In
Policy 2, the source domain unrestricted_d has permission to execute the
entry point program /bin/simpleDaemon. The destination domain simpleDaemon_d does
not. An implementation could
require either or both the source and destination domains to have execute
permission. We assume that execute
permission is granted automatically to the source domain for any entry point
program without requiring explicit execute access to the type of the entry
point program binary.