BigAdmin System Administration Portal
Feature Article
Print-friendly VersionPrint-friendly Version

The Least Privilege Model in the Solaris 10 OS

Amy Rich, February, 2005

Contents:

Most UNIX operating systems run a large number of their system processes with root privileges, giving the program the capability to read and modify other processes, memory, I/O devices, and so on. While this gives the system processes the power needed to perform their tasks, it also provides them with unnecessary access to other protected parts of the system. Many software exploits count on this escalated privilege to gain superuser access to a machine via bugs like buffer overflows and data corruption. To combat this problem, the Solaris 10 Operating System includes a new least privilege model, which gives a specified process only a subset of the superuser powers and not full access to all privileges.

The least privilege model evolved from Sun's experiences with Trusted Solaris and the tighter security model used there. The Solaris 10 OS least privileged model conveniently enables normal users to do things like mount file systems, start daemon processes that bind to lower numbered ports, and change the ownership of files. On the other hand, it also protects the system against programs that previously ran with full root privileges because they needed limited access to things like binding to ports lower than 1024, reading from and writing to user home directories, or accessing the Ethernet device. Since setuid root binaries and daemons that run with full root privileges are rarely necessary under the least privilege model, an exploit in a program no longer means a full root compromise. Damage due to programming errors like buffer overflows can be contained to a non-root user, which has no access to critical abilities like reading or writing protected system files or halting the machine.

The Solaris 10 OS least privilege model includes nearly 50 fine-grained privileges as well as the basic privilege set. The defined privileges are broken into the groups contract, cpc, dtrace, file, ipc, net, proc, and sys. The basic privilege set includes all privileges granted to unprivileged processes under the traditional security model: proc_fork, proc_exec, proc_session, proc_info, and file_link_any.


Process Privilege Sets

Each process has four privilege sets in its kernel credentials:

  • The Inheritable set (I): The privileges inherited on exec.
  • The Permitted set (P): The maximum set of privileges for the process.
  • The Effective set (E): The privileges currently in effect, a subset of P.
  • The Limit set (L): The upper bound of the privileges a process and its children may obtain. Any changes to the set L take effect on the next exec.

Each process also has a privilege awareness state (PAS) which can be set to Privilege Aware (PA) or Not-Privilege Aware (NPA). Privilege awareness is a mechanism which allows legacy applications to retain full compatibility with the traditional full privilege model. Legacy applications that are NPA will appear to be granted all privileges in the set L if any of the EUID, RUID, or SUID are 0 (root).

When a process calls exec(2), the kernel tries to relinquish privilege awareness. For unprivileged processes, I, P, and E are generally the same as the basic set of privileges, and L is typically the full set of defined privileges. For a more detailed explanation of the theory behind the implementation, take a look at Casper Dik's weblog on blogs.sun.com and the Process Rights Management Tutorial.

Once launched, a process uses the privilege manipulation functions to add or remove privileges from the privilege sets. Privileges can always be removed, but only privileges found in the permitted set can be added to the effective and inheritable set. As a result, the inheritable set can be larger than the permitted set. The limit set can never grow.


The ppriv(1) Command

Process privilege sets and their attributes are viewed and modified by the program ppriv(1). The ppriv(1) program arguments follow the syntax:

  /usr/bin/ppriv -l [-v] [privilege-specification...]

  /usr/bin/ppriv [-v] [-S] [-D | -N] [-s spec] [pid | core]

  /usr/bin/ppriv -e [-D | -N] [-s spec] command [arg...]

The preceding options are defined as:

  • -D: Turns on privilege debugging for the processes or command supplied.
  • -e: Interprets the remainder of the arguments as a command line and runs the command line with specified privilege attributes and sets.
  • -l: Lists all currently defined privileges on stdout.
  • -N: Turns off privilege debugging for the processes or command supplied.
  • -s spec: Modifies a process's privilege sets according to spec. Modifying the same set with multiple -s options is possible as long as you make either precisely one assignment to an individual set or any number of additions and removals. That is, assignment and addition or removal for one set are mutually exclusive. spec is a specification with the format [AEILP][+-=]privsetspec, containing no spaces, where:
    • AEILP: Includes one or more letters indicating which privilege sets to change. These are case insensitive, for example, either a or A indicates all privilege sets.
    • +-=: Indicates a modifier to respectively add (+), remove (-), or assign (=) the listed privileges to the specified set(s) in privsetspec.
    • privsetspec: Indicates a comma-separated privilege set specification (priv1, priv2, and so on), as described in priv_str_to_set(3C).
  • -S: Short. Reports the shortest possible output strings for sets. The default is portable output. See priv_str_to_set(3C).
  • -v: Verbose. Reports privilege sets using privilege names.

Viewing Process Privileges

Let's take a look at a few processes with the ppriv(1) command to see how they differ, depending on whether or not they are PA and have had their privileges modified. In this first example, we examine the third-party user process screen which is not privilege-aware. The flags are not set to PRIV_AWARE, and P, I, and E are all identically defined as the basic privilege set. The set L is the entire defined privilege set:

ppriv -v 1746
1746: screen -R
flags = <none>
        E: file_link_any,proc_exec,proc_fork,proc_info,proc_session
        I: file_link_any,proc_exec,proc_fork,proc_info,proc_session
        P: file_link_any,proc_exec,proc_fork,proc_info,proc_session
        L: contract_event,contract_observer,cpc_cpu,dtrace_kernel, 
	dtrace_proc,dtrace_user,file_chown,file_chown_self,
	file_dac_execute,file_dac_read,file_dac_search,
	file_dac_write,file_link_any,file_owner,file_setid,
	ipc_dac_read,ipc_dac_write,ipc_owner,net_icmpaccess,
	net_privaddr,net_rawaccess,proc_audit,proc_chroot,
	proc_clock_highres,proc_exec,proc_fork,proc_info,
	proc_lock_memory,proc_owner,proc_priocntl,
	proc_session,proc_setid,proc_taskid,proc_zone,
	sys_acct,sys_admin,sys_audit,sys_config,sys_devices,
	sys_ipc_config,sys_linkdir,sys_mount,sys_net_config,
	sys_nfs,sys_res_config,sys_resource,sys_suser_compat,
	sys_time

In this second example we examine the inetd process. It is not privilege aware, but its P and E sets have been modified to match L because it's running with EUID/RUID/SUID 0:

pcred 193
193: e/r/suid=0 e/r/sgid=0

ppriv -v 193
193: /usr/lib/inet/inetd start
flags = <none>
        E: contract_event,contract_observer,cpc_cpu,dtrace_kernel,
	dtrace_proc,dtrace_user,file_chown,file_chown_self,
	file_dac_execute,file_dac_read,file_dac_search,
	file_dac_write,file_link_any,file_owner,file_setid,
	ipc_dac_read,ipc_dac_write,ipc_owner,net_icmpaccess,
	net_privaddr,net_rawaccess,proc_audit,proc_chroot,
	proc_clock_highres,proc_exec,proc_fork,proc_info,
	proc_lock_memory,proc_owner,proc_priocntl,proc_session,
	proc_setid,proc_taskid,proc_zone,sys_acct,sys_admin,
	sys_audit,sys_config,sys_devices,sys_ipc_config,
	sys_linkdirsys_mount,sys_net_config,sys_nfs,
	sys_res_config,sys_resource,sys_suser_compat,
	sys_time
        I: file_link_any,proc_exec,proc_fork,proc_info,proc_session
        P: contract_event,contract_observer,cpc_cpu,dtrace_kernel,
	dtrace_proc,dtrace_user,file_chown,file_chown_self,
	file_dac_execute,file_dac_read,file_dac_search,
	file_dac_write,file_link_any,file_owner,file_setid,
	ipc_dac_read,ipc_dac_write,ipc_owner,net_icmpaccess,
	net_privaddr,net_rawaccess,proc_audit,proc_chroot,
	proc_clock_highres,proc_exec,proc_fork,proc_info,
	proc_lock_memory,proc_owner,proc_priocntl,proc_session,
	proc_setid,proc_taskid,proc_zone,sys_acct,sys_admin,
	sys_audit,sys_config,sys_devices,sys_ipc_config,
	sys_linkdir,sys_mount,sys_net_config,sys_nfs,
	sys_res_config,sys_resource,sys_suser_compat,sys_time
        L: contract_event,contract_observer,cpc_cpu,dtrace_kernel,
	dtrace_proc,dtrace_user,file_chown,file_chown_self,
	file_dac_execute,file_dac_read,file_dac_search,
	file_dac_write,file_link_any,file_owner,file_setid,
	ipc_dac_read,ipc_dac_write,ipc_owner,net_icmpaccess,
	net_privaddr,net_rawaccess,proc_audit,proc_chroot,
	proc_clock_highres,proc_exec,proc_fork,proc_info,
	proc_lock_memory,proc_owner,proc_priocntl,proc_session,
	proc_setid,proc_taskid,proc_zone,sys_acct,sys_admin,
	sys_audit,sys_config,sys_devices,sys_ipc_config,sys_linkdir,
	sys_mount,sys_net_config,sys_nfs,sys_res_config,
	sys_resource,sys_suser_compat,sys_time

In this third example, we take a look at fmd(1M), a process that is privilege aware. Note the flags setting and the limitations on E, I, P, and L:

ppriv -v 306
306: /usr/lib/fm/fmd/fmd
flags = PRIV_AWARE
        E: file_dac_execute,file_dac_read,file_dac_search,
	file_dac_write,file_link_any,file_owner,proc_exec,
	proc_fork,proc_info,proc_owner,proc_priocntl,proc_session,
	sys_admin,sys_config,sys_devices,sys_res_config
        I: file_dac_execute,file_dac_read,file_dac_search,
	file_dac_write,file_link_any,file_owner,proc_exec,
	proc_fork,proc_info,proc_owner,proc_priocntl,proc_session,
	sys_admin,sys_config,sys_devices,sys_res_config
        P: file_dac_execute,file_dac_read,file_dac_search,
	file_dac_write,file_link_any,file_owner,proc_exec,
	proc_fork,proc_info,proc_owner,proc_priocntl,proc_session,
	sys_admin,sys_config,sys_devices,sys_res_config
        L: file_dac_execute,file_dac_read,file_dac_search,
	file_dac_write,file_link_any,file_owner,proc_exec,
	proc_fork,proc_info,proc_owner,proc_priocntl,proc_session,
	sys_admin,sys_config,sys_devices,sys_res_config

In this final example, we take a look at the lockd(1M) process which is PRIV_AWARE and extremely limited in its access:

ppriv -v 161
161: /usr/lib/nfs/lockd
flags = PRIV_AWARE
        E: sys_nfs
        I: none
        P: sys_nfs
        L: none

Debugging Process Privileges

Often system administrators wish to grant certain users select privileges so that they may perform system tasks. If the user does not have the correct privileges, then the system outputs an error and the task is not performed. Say that the SUID bit has been removed from the file /usr/sbin/traceroute so that normal users cannot use it. Any system administrator should be able to use traceroute from his or her personal account without needing to be root, though. Without the correct privileges, a user will see the following error when trying to traceroute to the host www:

traceroute www
traceroute: icmp socket: Permission denied

To determine which privilege is missing from various commands, use the debugging functionality of ppriv(1) in the shell:

ppriv -D $$
traceroute www
traceroute[2885]: missing privilege "net_icmpaccess" (euid = 1001,
syscall = 230) for "devpolicy" needed at so_socket+0xa4
traceroute: icmp socket: Permission denied

Now that it's clear that the person with the UID 1001 is missing the PRIV_NET_ICMPACCESS privilege, it can be granted so that UID 1001 may successfully run traceroute. Be sure to turn off debugging in that shell after diagnosing the problem:

ppriv -N $$

Instead of debugging every command in the shell, the user can also debug just one process at a time:

ppriv -D -e dtrace -D test.d

Privileges and Role Based Access Control (RBAC)

The RBAC facility, present in the Solaris OS since version 8, is used to assign specific privileges to roles or users. Solaris RBAC configuration is controlled through four main files, /etc/security/exec_attr, /etc/security/prof_attr, /etc/security/auth_attr, and /etc/user_attr. exec_attr(4) specifies the execution attributes associated with profiles. This generally includes the user and group IDs, commands, and default/limit privileges. prof_attr(4) contains a collection of execution profile names, descriptions, and other attributes. auth_attr(4) contains authorization definitions and descriptions. user_attr(4) contains user and role definitions along with their assigned authorizations, profiles, and projects. For a better understanding of how RBAC operates, read the above-mentioned man pages along with rbac(5), policy.conf(4), chkauthattr(3SECDB) man pages, and the Roles, Rights Profiles, and Privileges section of the Solaris 10 System Administrator Collection.

To allow a group of users to use DTrace, the system administrator would either create a role that had access to the DTrace privileges or assign the privilege directly to a user. The following would create a "debug" role and grant it the appropriate privileges:

roleadd -u 201 -d /export/home/debug -P "Process Management" debug
rolemod -K defaultpriv=basic,dtrace_kernel,dtrace_proc,dtrace_user debug

Now add the necessary users to the debug role with usermod:

usermod -R debug username

The users with the role debug can now use su to access debug, providing the appropriate password, and run the necessary DTrace commands.

Instead of adding roles and making the users access the role via su, the system administrator can also directly assign privileges to a user. The user must be logged out in order for the following command to succeed:

usermod -K defaultpriv=basic,dtrace_kernel,dtrace_proc,dtrace_user username

If additional privileges are required, pinpoint them by running dtrace command ppriv again.

RBAC can also be used in conjunction with the least privilege model to more securely run daemons, like httpd, that need to bind to privileged ports. Many such programs do not actually need root access for anything other than listening on a port below 1024, so granting the role/user that runs the process net_privaddr would remove the need for ever running the process with EUID 0.


The Defined Privilege Set

The defined privileges under the new least privilege model are listed as in the privileges(5) man page, but here are some that system administrators are more likely to use:

PRIV_CPC_CPU
Allow a process to access per-CPU hardware performance counters.
PRIV_DTRACE_PROC
Allow DTrace process-level tracing. Allow process-level tracing probes to be placed and enabled in processes to which the user has permissions.
PRIV_DTRACE_USER
Allow DTrace user-level tracing. Allow use of the syscall and profile DTrace providers to examine processes to which the user has permissions.
PRIV_DTRACE_KERNEL
Allow DTrace kernel-level tracing.
PRIV_FILE_CHOWN
Allow a process to change a file's owner user ID. Allow a process to change a file's group ID to one other than the process's effective group ID or one of the process's supplemental group IDs.
PRIV_FILE_CHOWN_SELF
Allow a process to give away its files. A process with this privilege will run as if {_POSIX_CHOWN_RESTRICTED} is not in effect.
PRIV_FILE_DAC_READ
Allow a process to read a file or directory whose permission bits or ACL would otherwise disallow the process read permission.
PRIV_FILE_DAC_SEARCH
Allow a process to search a directory whose permission bits or ACL would not otherwise allow the process search permission.
PRIV_FILE_DAC_WRITE
Allow a process to write a file or directory whose permission bits or ACL do not allow the process write permission. All privileges are required to write files owned by UID 0 in the absence of an effective UID of 0.
PRIV_FILE_OWNER
Allow a process that is not the owner of a file to modify that file's access and modification times. Allow a process that is not the owner of a directory to modify that directory's access and modification times. Allow a process that is not the owner of a file or directory to remove or rename a file or directory whose parent directory has the "save text image after execution" (sticky) bit set. Allow a process that is not the owner of a file to mount a namefs upon that file. Allow a process that is not the owner of a file or directory to modify that file's or directory's permission bits or ACL.
PRIV_NET_ICMPACCESS
Allow a process to send and receive ICMP packets.
PRIV_NET_PRIVADDR
Allow a process to bind to a privileged port number. The privilege port numbers are 1-1023 (the traditional UNIX privileged ports) as well as those ports marked as "udp/tcp_extra_priv_ports" with the exception of the ports reserved for use by NFS.
PRIV_PROC_SETID
Allow a process to set its UIDs at will, assuming UID 0 requires all privileges to be asserted.
PRIV_PROC_ZONE
Allow a process to trace or send signals to processes in other zones. See zones(5).
PRIV_SYS_ADMIN
Allow a process to perform system administration tasks such as setting node and domain name and specifying coreadm(1M) and nscd(1M) settings.
PRIV_SYS_CONFIG
Allow a process to perform various system configuration tasks. Allow file system-specific administrative procedures, such as file system configuration ioctls, quota calls, creation and deletion of snapshots, and manipulating the PCFS boot sector.
PRIV_SYS_DEVICES
Allow a process to create device special files. Allow a process to successfully call a kernel module that calls the kernel drv_priv(9F) function to check for allowed access. Allow a process to open the real console device directly. Allow a process to open devices that have been exclusively opened.
PRIV_SYS_MOUNT
Allow a process to mount and unmount file systems that would otherwise be restricted (that is, most file systems except namefs). Allow a process to add and remove swap devices.

Privilege Set C Functions

Various C function calls are used to view and modify privileges. See the following man pages for more information about adding privilege awareness to your own code:

  • getpflags(2), setpflags(2): get or set process flags
  • getppriv(2), setppriv(2): get or set a privilege set
  • priv_addset(3C) priv_allocset(3C), priv_copyset(3C), priv_delset(3C), priv_emptyset(3C), priv_fillset(3C), priv_freeset(3C), priv_intersect(3C), priv_inverse(3C), priv_isemptyset(3C), priv_isequalset(3C), priv_isfullset(3C), priv_ismember(3C), priv_issubset(3C), priv_union(3C): privilege set manipulation functions
  • priv_set(3C), priv_ineffect(3C): change privilege sets and check whether privileges are set
  • priv_str_to_set(3C), priv_set_to_str(3C), priv_getbyname(3C), priv_getbynum(3C), priv_getsetbyname(3C), priv_getsetbynum(3C), priv_gettext(3C): privilege name functions
  • priv_getbyname(9F): map a privilege name to a number
  • priv_policy(9F), priv_policy_only(9F), priv_policy_choice(9F): check, report, and audit privileges

Resources

Comments (latest comments first)

Discuss and comment on this resource in the BigAdmin Wiki

Unless otherwise licensed, code in all technical manuals herein (including articles, FAQs, samples) is provided under this License.


BigAdmin
  
 
Solaris 10 System Administration Essentials