Section 5.3. Process Privilege Models


5.3. Process Privilege Models

The Least Privilege model in Solaris followed a simple set of guidelines:

  • The new model should be completely transparent to legacy applications.

  • Applications expecting the old and new model should be able to coexist in the same Solaris instance.

  • It should be possible to restrict a process and its offspring to a certain set of privileges.

The Solaris Least Privilege implementation provides full backward compatibility while still allowing use of the new functionality in the system at all times. If the functionality were such that it could be turned off, Solaris itself would not be able to use it and we could not apply the principle of Least Privilege to Solaris system daemons. Acceptance by software vendors would also be impacted. The functionality would not be guaranteed to be configured on. Conversely, if traditional Solaris applications couldn't work in the new environment, we would need ugly knobs to turn the functionality off.

The third requirement follows naturally from the traditional UNIX capability of set-uid applications: applications that gain elevated privileges merely by being executed. By limiting the extent of privileges a process can gain even in the face of set-uid applications, we further our goal of improving overall system security. For example, one can imagine a system in which the privileges needed to configure network interfaces can only be obtained when logging in on the console. By extending privileges to cover actions that are not privileged at this time, we could disallow all outgoing network activity for sessions originating from the network, making it impossible to use a system as a stepping stone.

Compatibility, coexistence, and containment are the three guiding principles. In the remainder of this section, we continue with a high-level description of the current Solaris superuser model and the new Solaris privilege model, followed by an in-depth discussion of the Solaris privilege implementation.

5.3.1. The Traditional Solaris Superuser Model

The traditional Solaris Security model has seen only minor revisions since the first UNIX days.

Each process has a credential associated with it; the credential consists of an effective, saved, and real user and group IDs as well as a supplementary group list. With a few exceptions, a process is considered privileged if and only if the effective uid is 0. This test is generally performed in two kernel routines, the DDI-compliant interface drv_priv(9f) and the traditional UNIX kernel function suser(), although some code checks the credential against uid 0 directly.

The three uids fare usually exactly the same. When they differ, they allow a coarse-grained swapping between different privilege states.

In the context of our discussion, we define C, the process credential; the credential resulting from an operation; and X, the properties of an executable. The following are items of interest for our discussion:

  • C.e. Effective uid of a process

  • C.r. Real uid of a process

  • C.s. Saved uid of a process

  • X.u. uid of the user owning an executable

Both suser() and drv_priv() are essentially defined as C.e = 0.

There are several times at which uids can change:

  • When a non-set-uid program is executed, the saved uid is made equal to the effective uid. Typically, this doesn't cause a change to the process credential.


  • When a set-uid program is executed, the saved and effective uid are set to the uid of the program.


  • When a process manipulates uids by using any of the setuid(2) family of system calls.

    - setuid(u)Change C.e, r, s if we're superuser, else change C.e.


    - setreuid(u)Change


    - setreuid(r, e)Change C.e, C.r, update C.s if C.r changes and the new C.e is not equal to the new C.r.


In the Solaris kernel, the superuser policy is enforced with the following checks, in decreasing frequency of use:

  • Direct comparison of cr->cr_uid (the effective uid)

  • The traditional kernel function call suser(cred_t *)

  • The DDI-compliant function call drv_priv(cred_t *)

  • Direct comparison of cr->cr_ruid (the real uid, used in some resource limit situations)

Additionally, files and device nodes on the system are protected by file ownership and permissions bits, also known as discretionary access control (DAC).

5.3.2. Extending Solaris with Process Privileges

One of the major features of Solaris is continued backward binary compatibility, allowing applications to live on in binary form without change, hopefully forever.

Moving from the suser model to an exclusively fine-grained, privilege-based model would have broken quite a few application or deployment scenarios. Applications assuming they have powers with uid 0 and set-uid root binaries would suddenly break. The new Solaris model is one by which ordinary user IDs can have additional privileges and in which the user with uid 0 is no longer all-powerful. This means that we now check against the privilege sets rather than simply checking whether the uid is 0.

The implementation uses four privilege sets:

  • C.I. The Inheritable set

  • C.P. The Permitted set

  • C.E. The Effective set

  • C.L. The Limit set

The traditional components of C behave as in Solaris. The Effective set contains the privileges that are currently in effect; the Permitted set contains privileges that the process can put in the Effective set at will. The Inheritable set contains those privileges that are inherited across exec(2). The Limit set (L) is the upper bound of privileges that a process and its offspring can inherit. The Limit set is enforced only at exec time, allowing a process to drop privileges on exec, while still using them until that time. L can be seen as carrying A with a process; all executables will appear to have L as the allowed set for that particular process.

A process can manipulate the privilege sets E, P, and I in a restricted fashion by using the setppriv(2) system call, such that the following conditions are always met:


A process can add privileges from P to I and E; a process can remove privileges from I, P, and E, noting that when privileges are removed from P they are automatically removed from E as well but they are not removed from I. When a program X is executed, the following transformations take place:


The uids follow the same rules as in Solaris before Least Privilege.

In the Solaris kernel, the kernel policy is enforced by checking whether the required privilege p is a member of the Effective set, C E:

pC.E

As we have chosen to implement security policy enforcement solely based on the effective privileges of a process, superuser compatibility would appear to be a daunting task.

5.3.3. How the Solaris 10 Least Privilege Model Was Chosen

A wide range of options was available when we were enhancing Solaris with Least Privilege capability. The following were the primary driving reasons for selecting the current model:

  • Simplicity. Complexity is the enemy of security and usability; we want fewer privilege sets and few operations with unexpected or unintended side effects.

  • Compatibility. We want full compatibility for existing software, including programs customers are known to replace, such as daemons like telnetd(1m) and sshd(1m) and programs like login(1) and sendmail(1m). Installing an older version of Solaris user code on a privilege-aware kernel should work as it did before.

  • Least surprise. Compatibility features should not get in the way of programs using Least Privilege. It should be possible to completely separate user ID and privilege manipulation.

  • Permitted set bounding. The Permitted set should not gain privileges except when set-uid root.

  • Forward compatibility.

5.3.3.1. Trusted Solaris

Trusted Solaris was not developed with compatibility as an absolute goal; it assigns no special meaning to uid 0, and it is therefore incompatible with the Solaris superuser model.

5.3.3.2. Root Set

The Root Set proposal added a systemwide set R. This set would be added into P and E when executing a set-uid program. If a program performed the system call setuid(0), R could be added to I; once a process made all of its uids non-zero, R was subtracted from all privilege sets except L, or, in another variant, the other privilege sets were recovered from I. R suffers from several drawbacks: precarious privilege set manipulations were needed in order to retain privileges while getting rid of uid 0; adding R was easy enough, but removing R would be destructive; a single privilege awarded to a process could not survive any uid 0 transitions.

Privilege manipulations on transition from or to an effective uid of 0 were needed, but they made it impossible to run a process in a mode that only cared about privileges and not about uids.

Full compatibility wasn't achieved. While it might have gotten there, that would certainly have pushed the model's complexity to an unacceptable level. The Root Set model also included the future possibility of removing privileges from R to end up with an empty R in the far future.

Variants that used R as E when euid 0 were also discussed; such models disallow running a uid 0 process without privileges except with the help of L; that was considered too limiting for certain applications.

5.3.3.3. As Many Sets as It Takes

Extend the model with as many sets as necessary to achieve the desired result was also tried.

  • ER. The effective set if C.e = 0

  • PR. The permitted set if C.e = 0 v C.s = 0 v C.r = 0 (any of the process's uids is 0)

With ER PR P, this does give full compatibility, but ER and PR are visible to applications. When an application needs to have its privileges independent from uid manipulations, it must make sure that ER = E and PR = P. More complicated is the behavior on exec, especially in the light of set-uid root applications: Should ER and PR be inherited, or are they reset to either L or a systemwide R on exec?

The main problem with this model is the fact that the application needs to be aware that uid manipulations affect E and P and that the application needs to manipulate both in order to achieve uid independence. Another is the counterintuitive behavior that a process can have fewer privileges when the effective uid is 0 than when it is non-zero. The different privilege sets might also need to be manipulated directly, which would require changes to applications if we were to adopt a privilege-only model in future. Or there would need to be a mode bit to indicate that the sets for uid 0 and non-0 are now connected.

5.3.3.4. Privilege Awareness

The final compatibility model is the Privilege Awareness model; the privilege state of the program is extended with a privilege-aware state (pas), which can take the values PA (privilege aware) and NPA (not privilege aware). We also introduce the notion of observed Effective and Permitted sets, EO and PO, respectively. These are the E and P as observed by the code that enforces the kernel security policy, the process itself, and other processes.

A process running in NPA mode behaves almost exactly like a traditional process. It may have privileges in E and P, and the kernel will honor those. But if the process set its effective uid to 0, the upper bound of that process's privileges, L, is used as the Effective set instead. Similarly, if any of the process's uids become 0, L is used as the observed P. An observer will see E follow the transition of the effective uid and will see P revert when all of the process's uids are no longer 0. In contrast, the implementation sets PI and EI remain unchanged during uid manipulations. The observer will not see E and P change when a process in PA mode changes its uids around.

Summarizing, the observer sees E and P behave as follows:


Changing pas is mostly automatic and restricted; a process that manipulates E or P automatically becomes privilege aware because EO and PO must be decoupled from L; similarly, if L is changed, EO and PO must remain unchanged, which can only be achieved by a change to PA. A change to I only has effect after the subsequent exec(2) and does not affect pas until that time. A process can only become NPA if E, P can be adjusted in a manner that keeps EO and PO unchanged and when such a change cannot lead to the process leveraging uid 0 into full privileges.

While we admit that a state bit such as pas doesn't deserve any credit toward the software-engineering-of-the-month award, it has some benefits over the other proposals:

  • It is immediately obvious that a process is fully backward compatible with the Solaris superuser model when it is NPA; that is, unless an application chooses to be incompatible, it will not notice the existence of privileges.

  • It is immediately obvious that when a process is PA, no manipulation of uids will ever affect the privilege sets.

  • There is no need to keep and administer alternative copies of E or other sets with all the possible resynchronization trouble that brings.

Some or most of the shortcomings of the other models could also be addressed with a pure privilege state bit. We believe, however, that this model offers best compatibility; the ability to switch to a pure privilege model on a per-process basis; easy extensibility with the ability to force all processes to be privilege aware, which allows, for example, for a Trusted Solaris implementation; and relatively easy understandability.

5.3.4. Other UNIX Implementations

A long time ago, a POSIX standard, 1003.1e, was in development. The standard was abandoned for various reasons. Some blame lack of interest by the industry at the time. Others blame the scope: POSIX 1003.1e tried to standardize mandatory access controls, access control lists, information labels, security auditing, and capabilities. It was abandoned in January 1998.

Even though the defunct draft standard uses the term privileges throughout the document, the term capabilities was substituted for most uses of privileges in the standard shortly before the standard floundered. The rationale given for this was that "It has been pointed out that the term privilege has been commonly used for a mechanism that achieves the above stated goals. However, the term privilege is also commonly used in the international community to mean something else entirely. It is felt that the confusion that would result from using the term privilege would not serve this standard well."

It's not well understood what this confusion is about. Moreover, the term capability is already well established in computer science and has a completely different meaning. The choice of terminology has made the documentation more confusing, because capabilities and privileges are used almost interchangeably but are not quite the same.

Various drafts of the POSIX standard are being used in various forms in both IRIX and Linux. The IRIX implementation appears to allow developers to hard-code fixed-size bit sets in executables. Both implementations make numerical capability values visible as manifest constants. Differences exist in the precise rules for process inheritance. Linux appears to have started with draft 17; IRIX capabilities have their roots in draft 16.

Each process and each executable file have associated capabilities; each capability has three flags associated with it, named Inheritable, Permitted, and Effective. We can think of this in terms of three sets of capabilities associated with each executable and process.

The exec(2) rules are defined as follows in Linux, with the restriction that X.E is either Ø or P:


and as follows in SGI IRIX:


and different again in POS I X 1003.1e, draft 17:


where XX is an additional implementation-specific restriction. The last definition is much like our privilege sets: C.I remains constant, X.I is virtually identical to the Allowed set, and X.P behaves like our Forced set. X.E should contain all privileges for applications not aware of privileges and none for those applications that know how to turn privileges on. XX fits our Limit set.

IRIX 6.5 can be configured either to use capabilities exclusively or to also allow uid 0 to be honored. IRIX has support for capabilities in the file system, as long as you use XFS.

Linux 2.4.16 has various settings; the default assigns all capabilities to uid 0 and removes them on set*uid(2) calls that do away with the effective uid. The system supports a per-process flag that lets you keep privileges when getting rid of uid 0; Linux supports a global flag that causes uid juggling not to affect privileges at all.

But it was found that such solutions are problematic. It was, therefore, no surprise that the direction experimental Linux patches take is making sets and uids orthogonal, introducing a per-process flag that determines whether uid 0 is special or not. Unprivileged processes, however, cannot affect this state and it must be changed explicitly. A further set-uid 0 not allowed flag exists. Both flags are inherited.

Additionally, patches add basic privileges to Linux. In the first Linux model these patches need to be special-cased on all points where uid 0 causes capability sets to be changed.

Analogous to the Solaris per-process Limit set, Linux has a systemwide capability bound. Later patches have made the bound per-process, though strangely not part of the capability state proper. It is unclear to us why the flags and per-process capability bound are not part of the capability state.

The main reason Solaris did not go with an implementation based on the POSIX 1003.1e draft is that not only is it an unapproved standard, it is even withdrawn. What exists in implementation space is very much a moving target. Some of the features now found in Linux and in our proposal did not exist when we started our project. The standard took more than 10 years to develop and then failed. The terminology chosen is often confusing; all known implementations use some form of sets, but the standard tries hard not to speak of three capability sets. Using the same three sets for executables and processes doesn't do wonders for clarity either. The Linux capability FAQ[1] explains this as follows: "Now to make sense of the equations think of X.P as the Forced set of the executable, and X.I as the Allowed set of the executable."

[1] http://www.kernel.org/pub/linux/libs/security/linux-privs/kernel-2.4/capfaq-0.2.txt

The current Linux implementation is very much in flux; it has gone through several iterations trying to achieve sufficient uid 0 compatibility.

By choosing a completely separate namespace, PRIV over CAP, Solaris keeps open the option of implementing 1003.1e-compatible interfaces on top of the existing implementation in future, once those are sufficiently established.

Both the IRIX and Linux implementation address few of our forward binary compatibility constraints by externalizing numerical constants and binary representation in file systems.

IRIX appears to make the size of a capability set a first-class citizen for much of the code, whereas Linux appears to limit the visibility of the data structure to the kernel. Since binary device drivers are a necessity for Solaris, we cannot use numeric constants or bit test macros outside the core kernel.

Linux device access is arranged ad hoc inside the kernel. Support for capabilities in the file system is only available as an experimental patch at the time of this writing (2005).




SolarisT Internals. Solaris 10 and OpenSolaris Kernel Architecture
Solaris Internals: Solaris 10 and OpenSolaris Kernel Architecture (2nd Edition)
ISBN: 0131482092
EAN: 2147483647
Year: 2004
Pages: 244

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net