Section 5.5.6. Library Interfaces


5.5.6. Library Interfaces

In this section we describe two different sets of interfaces: one set of interfaces for manipulating privilege set and a second set of interfaces that abstracts the kernel credentials (cred_t) into an opaque user credential (ucred_t) with a functional interface.

5.5.6.1. Privilege Specific Library Interfaces

The library interfaces are primarily for manipulating privilege sets and converting from privilege names to numbers and back. Since the implementation protects the programmer from the details of the implementation in the currently running kernel, functions are available to allocate sufficient memory for privilege sets as well as for functions that take privileges and privilege sets and do computations.

The library loads the privilege implementation details the first time it is required. These details are kept around for future calls to the library. When library calls query any part of the structure that may have been changed, the library checks with the kernel and updates the internal information if necessary. An application will learn about privileges added after it first caused the implementation details to be loaded by the C library. A Consolidation Private secondary set of functions that accepts the implementation details as arguments is provided for use by libproc(4) and other programs inspecting core dumps.

The manifest constants, defined by including <priv.h> in user code, map to strings only.

The manifest constants are present for early detection of typos in privilege names. The constants are cast to (const char *) to allow compilers to merge identical strings and to prevent accidental string concatenation.

#define PRIV_IPC_DAC_READ ((const char *)"ipc_dac_read") #define PRIV_IPC_DAC_WRITE ((const char *)"ipc_dac_write") #define PRIV_IPC_OWNER ((const char *)"ipc_owner") #define PRIV_SYS_IPC_CONFIG ((const char *)"sys_ipc_config") 


The privilege sets are represented as opaque pointers; applications should only declare pointers to priv_set_t and should allocate, free, and manipulate them by using appropriate functions, keeping the actual structure hidden from view. Utility functions that parse strings representing privilege sets and convert privilege sets back to strings are provided both for pretty printing, ease of input, and permanent storage of privilege sets. The strings can contain those privileges currently defined in the kernel and can also contain special tokens such as all for P, basic for B, none for no privileges. A single dash "-" or exclamation mark "!" preceding a privilege negates its presence, allowing shorthand notation for all privileges except.... The implementation prefers "!" as the negation character on output but will use "-" if the exclamation mark is specified as separator. Both can be used on input; the exclamation mark looks nicer but needs to be escaped by certain shells; the dash can be more convenient in such cases.

The special token all represents the set of all bits set, not just those privileges defined in the system. Similarly, tests against the full set and the set comparison and manipulation functions always take all bits of the underlying implementation into account, even those that do not represent valid privileges.

A full set of functions to manipulate sets in user code is contained in <priv.h>, including a number of functions that allow easy manipulation of individual privileges.

int setppriv(priv_op_t, priv_ptype_t, const priv_set_t *); int getppriv(priv_ptype_t, priv_set_t *); int setpflags(uint_t, uint_t); uint_t getpflags(uint_t); const priv_impl_info_t *getprivimplinfo(void); int priv_set(priv_op_t, priv_ptype_t, ...); boolean_t priv_ineffect(const char *); priv_set_t *priv_str_to_set(const char *, const char *, const char **); char *priv_set_to_str(const priv_set_t *, char, int); int priv_getbyname(const char *); const char *priv_getbynum(int); int priv_getsetbyname(const char *); const char *priv_getsetbynum(int); char *priv_gettext(const char *); priv_set_t *priv_allocset(void); void priv_freeset(priv_set_t *); void priv_emptyset(priv_set_t *); void priv_fillset(priv_set_t *); boolean_t priv_isemptyset(const priv_set_t *); boolean_t priv_isfullset(const priv_set_t *); boolean_t priv_isequalset(const priv_set_t *, const priv_set_t *); boolean_t priv_issubset(const priv_set_t *, const priv_set_t *); void priv_intersect(const priv_set_t *, priv_set_t *); void priv_union(const priv_set_t *, priv_set_t *); void priv_inverse(priv_set_t *); int priv_addset(priv_set_t *, const char *); void priv_copyset(const priv_set_t *, priv_set_t *); int priv_delset(priv_set_t *, const char *); boolean_t priv_ismember(const priv_set_t *, const char *); 


The mapping functions for name-to-number and back are included for completeness but they are not generally useful. The other interfaces discourage the use of privileges and sets as numbers; no function takes privileges or sets as numbers except the mapping functions. The actual size of privilege sets can only be determined with getprivimplinfo(3c); writing privilege sets to files as bits requires getting the set and obtaining the system-specific set size. The interface to convert a privilege set to a string is more convenient. The shorthand privilege bracketing functions let you switch privileges on or off without having to manually do the set allocation and other tedious work with variable argument functions with NULL as terminator.

priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_NET_PRIVADDR, NULL); bind(... reserved port ...); priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_NET_PRIVADDR, NULL); /* Permanently giving up our privilege */ priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_NET_PRIVADDR, NULL); 


5.5.6.2. User Credential Library Interfaces

Certain types of IPCs need the daemon handling the request of the client to establish the client's credentials. Several mechanisms have been tried. Mechanisms such as reserved ports whereby the client had to be a privileged process were discarded long ago. One newer mechanism used trusted kernel mechanisms, such as door_cred(3door), to forward an ad hoc subset of the kernel credential of the client process to the server process. Another method used private interfaces such as rpc_get_local_uid(), which was soon replaced with a more complete but still inextensible rpc_get_local_cred().

As with the extensions to the kernel credential, the userland representation of the kernel credential is made extensible. For obtain maximum flexibility, an opaque type ucred_t is defined, size unknown, and the only way to retrieve information from this opaque type is by using access functions as defined in <ucred.h>. The functions may return -1 or NULL if the underlying user credential is incomplete, since underlying interfaces may not provide a complete user credential. For completeness, a function ucred_get(3c) is available to retrieve the complete user credential from any process through /proc.

The old_door_cred(3door) interface is implemented on top of these new interfaces and mark it obsolete. The rpc functions are not extended; not only are they private, the rpc credentials include only user and group IDs across the board.

typedef struct ucred_s ucred_t; ucred_t *ucred_get(pid_t pid); void ucred_free(ucred_t *); uid_t ucred_geteuid(const ucred_t *); uid_t ucred_getruid(const ucred_t *); uid_t ucred_getsuid(const ucred_t *); gid_t ucred_getegid(const ucred_t *); gid_t ucred_getrgid(const ucred_t *); gid_t ucred_getsgid(const ucred_t *); int ucred_getgroups(const ucred_t *, const gid_t **); const priv_set_t *ucred_getprivset(const ucred_t *, priv_ptype_t); uint_t ucred_getpflags(const ucred_t *, uint_t); pid_t ucred_getpid(const ucred_t *); 


5.5.6.3. Private Daemon and Set-uid Interfaces

To get a higher return on the initial Least Privilege implementation, we added two private utility interfaces, init_daemon_priv() and init_suid_priv(). These interfaces allow daemons to specify which privileges they need on top of B. The library calls put those privileges in P and E and reset the effective uid for set-uid applications or set a specific daemon uid and clear the group list for a daemon. If requested, the library routine also clears I and L or sets I equal to P. The basic set is preserved insofar as present in the inheritable set so that code changes are not required for every basic privilege added. Utilities and daemons can revoke those basic privileges that existed at the time the code was written and aren't needed by the program.

In the following example, the set-uid program never needs to call fork(2) or exec(2), so it rescinds those privileges. The added benefit is that when the program is exploited, the exploit code will need to run in the original executable because execing a shell is not possible. The function is a no-op when run by a process that otherwise possesses all privileges, for example, processes started by the superuser.

The special-purpose function priv_bracket() switches on and off the privileges requested by the initial call to init_suid_priv() by adding them to E or removing them from E, depending on the argument; priv_relinquish() gives up the requested privileges permanently by removing them from P.

By making judicious use of priv_bracket() in the rcmd(3socket) implementation, set-uid root programs that need to use those functions can be adapted with ease: Add one call to init_suid_priv() and remove all the application's uid juggling code.

/* Returns with the required privilege in P but not in E */ __init_suid_priv(PRIVUTIL_CLEARLIMIT, PRIV_NET_RAWACCESS, NULL); /* Basic privileges not needed by this program */ priv_set(PRIV_OFF, PRIV_ALLSETS, PRIV_PROC_EXEC, PRIV_PROC_FORK, NULL); .... /* Need privilege for raw socket, adds it to E */ __priv_bracket(PRIV_ON); s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); /* Don't need it for a while, remove it from E */ __priv_bracket(PRIV_OFF); /* or don't need the privilege ever again, remove it from P and E */ __priv_relinquish(); 


Similarly, this daemon runs with as few privileges as required. A possible exploit also needs to run in the context of this process; it cannot escape and run a different executable.

/* NFS server: needs NFS privilege and access to the FX scheduler */ __init_daemon_priv(PRIVUTIL_CLEARLIMIT, DAEMON_UID, DAEMON_GID,                    PRIV_SYS_NFS, PRIV_PROC_PRIOCNTL, NULL); /* Basic privileges I do not need */ priv_set(PRIV_OFF, PRIV_ALLSETS, PRIV_PROC_EXEC, PRIV_PROC_FORK,          PRIV_PROC_SESSION, NULL); 





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