Section 5.4. Privilege Awareness: The Details


5.4. Privilege Awareness: The Details

In this section we present the precise semantics and describe the various features of our privilege model.

5.4.1. Per-Process State

We extend the per-process state from Section 5.3.2 with the privilege awareness property, pas, and the semantically void[2] privilege debugging flag, db, and get

[2] In Trusted Solaris, privilege debugging is a systemwide property that requires a reboot to switch on or off. It then allows applications in the Trusted Path to manipulate the privilege debugging flag for certain applications; those applications will then have all privilege checks succeed. This privilege debugging property is not semantically void.

  • C.I. The Inheritable set

  • C.P. The Permitted set

  • C.E. The Effective set

  • C.L. The Limit set

  • C.pas. The privilege-awareness property

  • C.db. The privilege debugging flag

The notion of observed Effective and Permitted set is used only in those cases in which we need to distinguish between the implementation sets EI and PI and the observed sets EO and PO. When talking about the semantics and the user-visible behavior, we use E and P for EO and PO. These are also the values returned by getppriv(2) and as shown by ppriv(1). For reference, these are the observation rules:


The initial process init starts off with EI = PI = I = IO and L = P. IO is the set of basic privileges and can be empty. This default process credential behaves exactly like the superuser model: When the effective uid is non-zero, the process has the default privileges; when the effective uid is 0, the process has all privileges.

5.4.2. Privilege Awareness State Transitions

In this section we describe the transitions between being privilege aware and not being privilege aware. This can only happen if the observed set invariance condition is met. That is, when a process transitions between PA and NPA, no changes to E and P are observed.

5.4.2.1. Becoming Privilege Aware

There are no restrictions on becoming privilege aware. Assuming pas = NPA, the following changes to the credential take place when transitioning to PA:


EO and PO are constant under this operation. This mechanism converts a uid 0 process that appears to follow the superuser model to a fully privileged process for which uid manipulations no longer have an effect on the privilege status.

5.4.2.2. Losing Privilege Awareness

There are several times when a process may want to lose the privilege awareness, for example, on exec(2). Several restrictions apply since not all combinations of privilege sets can successfully and safely be converted to an equivalent NPA process. We derived the following rules from the observed set invariance property:

  • If any of the uids is 0, PI must be equal to L.

  • If the effective uid is 0, EI must be equal to L.

  • If none of the uids is 0, there are no restrictions.

The last rule tells us that in a process without any 0 uids, pas can be changed at will.

When the conditions are met and a process transitions to NPA, C.pas is set to NPA and the implementation sets are conditionally changed as follows:


Again note that EO and PO are unchanged under these operations.

5.4.3. Privilege State Manipulation

The per-process state changes at various times, either as a result of explicit actions or as a side effect of other actions. In some cases the changes are merely observed and not reflected in the underlying data structures.

5.4.3.1. What Happens When User IDs Change?

In all circumstances, the actual implementation sets remain unchanged, but we can distinguish several cases. In privilege-aware processes, no changes are observed.

In processes not having any uids set to 0, no changes are observed, even if privileges are used to obtain uid 0.

In non-privilege-aware processes, EO alternates between L and EI depending on the value of the effective uid. PO appears as L until the last uid is set to non-zero; it then reverts to PI.

5.4.3.2. What Happens When a Process Is Created?

When a process is created by any of fork(2), vfork(2), or fork1(2), the full privilege state is inherited by the child process.

5.4.3.3. What Happens When a Program Is Exec'd?

When any of members of the exec(2) family is called, the following rules apply:


We include X.A and X.F in these equations both for use in future implementations and to keep open the option of per-mount point allowed sets and some form of forced privilege emulation.

At one stage the prototype emulated a single forced privilege by using the setgid bit. A single privilege appears to be sufficient for many Solaris applications; for example, commands using rcmd(3socket), such as rsh(1) and rlogin(1) as well as applications using raw sockets, such as ping(1m) and TRaceroute(1m).

The db property is inherited across exec(2); pas is adjusted according to the following rules:

  • If a process is NPA, it becomes PA only in the presence of forced privileges, taking the following rule into consideration.

  • If a process is PA, the privileges are examined before exec and a process becomes NPA if possible, according to the rules outlined in Chapter 3. If the process remains PA, a second attempt at reverting to NPA is attempted directly after the exec.

The changes to the implementation sets EI and PI are defined in such a way that the observed sets EO and PO remain the same. As a consequence of these rules, it is not always possible for PA processes to gain privileges by executing a set-uid root executable. It is possible to gain privileges in that way for NPA processes.

In addition to the current practice of marking a process NOCD|SUGID[3] if the real and effective IDs do not match, we add the marker if the permitted set of the process calling exec(2) is not a superset of the inheritable set. That is, when the child process runs with more privileges, it is awarded additional protection.

[3] We introduced the NOCD flag when we realized that the current credentials of a process do not properly reflect the capability status of a process. That is, if a process has opened a restricted file or device or has acquired some other capability and afterwards dropped its extra privileges, the process still has some capabilities or data that should be restricted. The name originates from NO Core Dump but was later extended to deny process inspection through /proc and to restrict the use of certain environment variables in libraries. The SUGID flag is set when a process results from the exec of a set-uid or set-gid executable.

An implementation of Trusted Solaris might force most or all processes to PA.

5.4.3.4. Manipulating Privileges Directly

This project adds a system call family, setppriv(2), that allows a process to manipulate its privileges directly.

The function setppriv() takes three arguments: the operation to perform, which is one of PRIV_OFF, PRIV_ON, or PRIV_SET; the privilege set name; and a privilege set with privileges to switch off or on, or which can completely replace the current set.

The Effective set is most useful to perform privilege bracketing. In the superuser world, privilege bracketing looks like this:

/* program start */ uid = getuid(); seteuid(uid); /* privilege bracketing */ seteuid(0); /* code requiring super-user privileges */ .... seteuid(uid); /* ordinary code */ .... /* Permanently giving up root */ setreuid(uid, uid); 


With process privileges, the code (simplified) becomes

/* program start */ getppriv(PRIV_PERMITTED, pset); setppriv(PRIV_SET, PRIV_EFFECTIVE, emptyset); /* privilege bracketing */ setppriv(PRIV_SET, PRIV_EFFECTIVE, pset); /* code requiring privileges */ .... setppriv(PRIV_SET, PRIV_EFFECTIVE, emptyset); /* ordinary code */ .... /* Permanently giving up privileges */ setppriv(PRIV_SET, PRIV_PERMITTED, emptyset); 


The added advantage is that this manipulation can be done at the level of single privileges. For this purpose, a single convenience function, priv_set(3c), is provided. Removing privileges from E is not restricted; only privileges in P can be added to E. A process manipulating E needs to be PA; it will transition to PA following the rules in Section 5.4.2, if necessary. The permitted set can only be shrunk; a process should shrink its Permitted set if it does not have any future need for the privileges. Privileges removed from P are automatically removed from E, enforcing E P will automatically transition to PA.

The Inheritable set can be added to and removed from; privileges from P can be added freely to I. Privileges can be removed from I without restriction. Because changing I does not influence EO or PO, a process does not need to transition to PA when manipulating I but might transition to PA on subsequent exec(2). I is allowed to be a superset of P, that is, removing privileges from P does not affect I.

The Limit set cannot be added to by any mechanism. Privileges can be removed from the Limit set, but not without peril. Applications running under uid 0 usually assume full privileges on the OS; this often leads to no error checking for privileged operations or, worse, different behavior of certain operations whether you are privileged or not. For example, setuid(non-0) will reset all uids to non-0 if the effective uid is 0 but will only change the effective uid if a process is not privileged but can swap uids because of the value of the saved uid.

It was discovered[4] that removing selected privileges from a further privileged process could render it insecure. To rectify the problem, we defined the set of unsafe privileges, the privileges without which it is too risky to allow privilege elevation. A process that does not have in L all privileges from the unsafe set, currently PRIV_PROC_SETID, PRIV_SYS_RESOURCE, and PRIV_PROC_AUDIT, will fail to execute set-uid 0 processes.

[4] Linux capabilities introduce a similar feature: Unrestricted use triggered a problem in sendmail(1m) mailer. See http://www.sendmail.org/sendmail.8.10.1.LINUX-SECURITY.txt

Changing the limit set would influence PO and EO, so a process needs to transition to PA when it changes L.

A process that manipulates its privileges is marked NOCD, which awards the process additional protection.

5.4.3.5. Manipulating pas Directly

Using getpflags(2) and setpflags(2) processes can query and change pas. The latter function enforces the rules outlined in Section 5.4.2.

5.4.3.6. Manipulating Privileges through proc(4)

The proc(4) interface is extended to allow inspecting and changing privileges through the /proc file system. The modifications through this interface are severely restricted:

  • A process can only attach to a target process when all the uids and gids of the target process are equal to its effective uid and gid, respectively, and when the process does not have the NOCD flag set or when the process has the PRIV_PROC_OWNER privilege. The attaching process must also have all the privileges available in the target process in its E, and its Limit set must be a superset of the target's L. That is, CT .P A.E Λ CT.I CT.L CT.L cannot be grown, only shrunk (following the rules in Section 5.4.3.4).

  • Only privileges in E of the attaching process can be added to the other sets of the target process.

  • A process that has its privileges manipulated directly becomes PA unless it can swap to NPA mode following the rules in Section 5.4.2.

In addition, if a process can modify the state of a process, it can always switch db on or off.

The above rules help enforce the fact that the special privilege PRIV_PROC_OWNER is not sufficient to gain control over a process when such control can lead to escalation of privileges.

In the special case in which any of the user IDs in the target process is zero, the full set of privileges is required. The privilege does give unrestricted read access.

5.4.4. Privilege Escalation Prevention

The privilege escalation is defined as the process whereby a subject can obtain one or more privileges by performing an action that does not require those privileges if that action is not specifically allowed by the security policy.

One of the often recognized problems with privilege implementation is that some privileges provide back doors to others. We can think of many cases where this can happen:

  • A privilege to modify all processes privileges would allow a process to obtain all privileges by modifying its own privileges.

  • A privilege sufficient to attach to all processes is equivalent to obtaining the superset of all privileges currently in permitted sets.

  • A privilege sufficient to modify the system or administration files could allow a process to assign all privileges to a user.

  • A privilege to write directly to /dev/kmem allows a process to change its own credentials, so this is equivalent to having all privileges.

  • A privilege to write directly to /dev/dsk/* allows a process full access over all file contents, so this is equivalent to having all the file * privileges or even all privileges if the disks in question contain system configuration files or allow set-uid execution.

While L could make certain that such privileges aren't accorded to ordinary users, such restrictions on L may preclude perfectly valid, controlled uses of such privileges by privileged programs, so we feel that we should put extra hurdles whenever we find a place where certain privileges can be leveraged to more privileges. Where we can identify such cases of privilege escalation, we require a process to either possess as many privileges as it would be able to obtain with the specific action or, in the specific instance of a process PS granting privileges to a process PO, we require that those privileges do not exceed PS.E 5.4.5. The Trouble with uid 0

The superuser's user ID is used not only for privilege checks but also for discretionary access control on files and devices, because the superuser owns most files and devices on the system. Even without any of the PRIV_FILE_DAC* discretionary access overrides, a process with uid 0 still has near complete reign of the system. It therefore stands to reason that we run as few system processes with uid 0 as possible. Rather, we run the daemons that need privileges as user daemon with some privileges rather than as root with most privileges removed.

When we look at the privileges (for the complete set used in our implementation see privileges(5)), we find that many privileges are potentially too power-fulthey make no distinction between objects owned by root and objects owned by ordinary users, and without such distinction, they can switch to just any uid or to root.

In that situation, MAC labeling would really help. In keeping with our Prevention of Privilege Escalation policy, we will require the full set of privileges if the effective uid of a process is not 0 when that process needs a privilege to modify an object owned by uid 0 or wants to control a process with any of its uids 0. If a process switches from any uid to 0 and none of its other uids is 0, it will also require all privileges.

Ordinary file, directory, and other permissions still apply to root-owned objects; it is not until a privilege is needed that the additional requirement comes into force. For all other intents and purposes, root-owned objects behave exactly the same as ordinary objects.

The more interesting applications of process privileges will be the use in processes not running with uid 0. Our implementation runs a few daemons with a very restricted set of privileges. We can run daemons under different uids, and we document which privileges we actually use at the same time. The daemons themselves have been changed to drop the excess privileges and run under a different uid.

# ppriv 'pgrep rpcbind' 'pgrep statd' 'pgrep nfsd' 'pgrep lockd' 170: /usr/sbin/rpcbind flags = 0x2         E: net_privaddr,proc_fork,sys_nfs         I: none         P: net_privaddr,proc_fork,sys_nfs         L: all 328: /usr/lib/nfs/statd flags = 0x2         E: proc_fork         I: none         P: proc_fork         L: all 335: /usr/lib/nfs/nfsd flags = 0x2         E: sys_nfs         I: none         P: sys_nfs         L: all 330: /usr/lib/nfs/lockd flags = 0x2         E: sys_nfs         I: none         P: sys_nfs 


5.4.6. Basic Privileges

In certain situations, you may think that the standard Solaris user has too many privileges enabled by default. Or you might want to enable unrestricted chown(2) for just a handful of users. Or make sure that a process never forks or execs, a feature that can be used to make any editor safe for use in a restricted menu environment. If you are using quotas, you may sometimes want to prevent users from creating hard links to files they do not own. To facilitate such features, we have implemented what we call basic privileges. Basic privileges are just like ordinary privileges except that the default I contains all basic privileges.

Administrators can assign different privileges to users or withdraw the basic privileges away from specific users. The initial set of privileges, I0, is assigned the full set of basic privileges, B. This initial set is inherited by all processes and also determines the privileges associated with ordinary users authenticated to the kernel services using a form of RPC, such as NFS.

The system(4) variable rstchown determines whether the privilege file_chown_self is present in I0; the ability to change ownership of files owned by the current effective uid is now per-process and can be awarded user by user.

The set of basic privileges is part of the information the kernel supplies about its configuration; it is, therefore, easy to determine whether a process runs with more than the basic set of privileges.

So that more standard functionality can migrate into the set of basic privileges, the functions that convert privilege sets to strings expand the basic privileges into the string "basic" by default. If one or more privileges from B are missing from a set, the expansion will read "basic,!basic missing1,!basic missing2", thus preserving the privileges the set conveys over future expansions of B. Options change the default behavior for those cases in which the literal privilege set is required or in which the shortest output form is wanted.

To write a process or daemon that uses privileges that need to be ported forward, programmers must take care that the process or daemon requires B in addition to the other privileges they may need. Individual basic privileges that are known not to be required can be removed from P. For example, if we were to make open(..., O *WR*) a basic privilege, such a daemon would retain that privilege simply by retaining the basic privilege set at startup. In the OS release with the additional basic privilege, the process or daemon would automatically get that additional privilege.

5.4.7. Privileges and the Runtime Environment

The Solaris runtime environment, consisting of the runtime linker and the runtime libraries, is user configurable to a considerable degree, mostly with environment variables. The runtime environment restricts the configurability for processes that are set-uid or set-gid.

Applications that completely assume different identities in subprocesses, for example, su(1m) and sendmail(1m), clean the environment before executing subprocesses that cannot determine that they run under an assumed uid, for example, because the effective and real uid are now identical.

The runtime environment does not impose restrictions on privileged processes merely because they are privileged; rather, it does so only when it knows that the privileged processes derive from unprivileged processes, for example, through set-uid.

In Solaris, this mechanism remains largely unchanged; it remains the responsibility of applications to vet the environment if privileges are moved to I. The kernel will take care of those cases in which extra privileges are gained through setuid or, in the future, forced privileges. In Solaris, this responsibility is handled by extending issetugid(2). In the initial implementation, this solution is unnecessary, since the only way to gain privileges is through set-uid 0 executables and those are already caught by the current mechanism.

The interface between the runtime linker and kernel is slightly changed; the kernel has better knowledge about how the process transitioned and how it was started; a new aux vector, AT_SUN_AUXFLAGS, is passed to the runtime linker. Its value is a bit mask for which we define a single bit, AF_SUN_SETUGID, which, when set, indicates that the runtime linker can trust only secure directories.

5.4.8. Privileges and NFS

The network file system, NFS, authorizes operations on files according to network credentials. In the insecure, default mode of operation, the NFS client sends the identity of the caller directly with each request, without giving the server any way to verify the credentials of the process on the NFS client. Proper authorizations schemes have been developed, but all of them focus on the "user" for granularity of access control. But because proper authorization is not widespread, NFS servers typically map requests by the superuser to a guest or nobody credential. Allowing privileged operations over NFS is atypical.

There is one use of NFS for which this approach is not fine: the use for diskless clients, including the case of network installation. Diskless clients are slightly more complicated because root write access is also required.

In both of the above cases, the NFS file systems are exported with full root access. We achieve compatibility by still having our system and install processes run as root; this gives those systems full file system access for those insecure exports. In those cases in which daemons are converted to run as a uid other than root, the appropriate ownership or permission changes are made.

In the future we can think of client-verified (for example, digitally signed) additional credentials that hold the effective privileges of a process. However, we believe that allowing clients to wield privileges on NFS servers is generally ill-advised.

It is NFS that prevents us from replacing the kernel tunable rstchown with the privilege PRIV_FILE_CHOWN_SELF.

Several kernel RPC functions had their implementation changed: the NFS-specific privilege check was moved to the nfssys system call because it was out of place in the RPC layer.

The NFS system generates kernel credentials from network credentials in several locations; we changed the code that generates credentials and the functions to generate full credentials. This changes the signatures of certain kernel rpc functions. The function authdes getucred is renamed as kauthdes_getucred because the former also exists in the rpc libraries. The signature of sec_svc_getcred just changes.

/* Old signatures */ int sec_svc_getcred(struct svc_req *req, uid_t *uid, gid_t *gid,                      short *ngroups, gid_t *groups, caddr_t *principal, int *secmod) int authdes_getucred(const struct authdes_cred *, uid_t *, gid_t *, short *,                      gid_t *); /* New signatures */ int sec_svc_getcred(struct svc_req *req, cred_t *cr, caddr_t *principal,                     int *secmod); int kauthdes_getucred(const struct authdes_cred *adc, cred_t *cr); 


5.4.9. Privileges and Third-Party File Systems

There are no changes to the file system interfaces, so preexisting file systems will continue to work unless they reference the cr_groups field directly. But unmodified file systems will not honor privileges; they most likely will still require uid 0 for privileged operations until they are converted to using the Least Privilege file system policy routines.




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