Sunday Mar 31, 2013

Permissive and Restricted Policies

Recently I posted two entries about the new Extended Policy functionality in Oracle Solaris 11.1. One demonstrated how to create application sandboxes, and the other how to confine services, like MySQL. Both of these are examples of restrictive policies, whereas privileges have traditionally been used to implement permissive policies, hence the term privilege escalation. The basic functionality and terminology first appeared in Trusted Solaris in 1991, and was later adopted by AIX and HP-UX. Unfortunately, a draft of the POSIX 1.e Security Specification (withdrawn), used the term capabilities for this functionality, which was subsequently used by Linux developers. Oracle Solaris privileges do share some common terminology with Linux capabilities , including the permitted, effective, and inheritable privilege sets. But almost everything else is different.

For example, the term unprivileged doesn't apply in Oracle Solaris, since every process has at least some privileges. Instead, we use the term basic privileges to refer to the set that are traditionally granted to all UNIX processes. This set includes:


Removing some of these privileges, or enumerating the objects to which they apply is a powerful containment mechanism which I've previously described. But even normal users may choose to limit their basic privileges. For example, my .bash_profile contains the following line, which limits my view of processes on the system to just the ones I own by removing PRIV_PROC_INFO from my login shell. It makes programs like ps(1) and prstat(1) more friendly.

ppriv -s EI-proc_info $$

Another difference is that recent versions of Linux allow the setting of file capabilities in the extended attributes of executable files, as an alternative to using the setuid-to-root permission bit. That's what Trusted Solaris did, too, but now Oracle Solaris uses a special rights profile called Forced Privilege, to specify the process privileges for setuid-to-root programs. The Forced Privilege profile is unique in that it is always in effect regardless of who is executing these programs.

Both the AppArmor and SELinux security modules plug into the LSM framework, so they can restrict access to specific objects. But these modules are not invoked when such access requires a capability (privilege) which was not already granted to the process, so a permissive mechanism is also needed. Although file capabilities can be used to elevate process privileges, they must be used carefully because they apply to any user who executes that file. So most Linux user's rely on sudo(1M), which grants them additional access.

Although Solaris supports sudo, it isn't hooked into the kernel like rights profiles are. The sudo command needs to run as root with all privileges so that it can grant the attributes specified for each program in the /etc/sudoers file for the current user. But it cannot specify the attributes of any subsequent child processes. The best it can do is block their execution. For example, the Solaris version of sudo prevents this by removing the PRIV_PROC_EXEC privilege from the process.

Instead Solaris provides an integrated execution environment in which every program invoked by the user runs with the process attributes specified in the user's hierarchical set of rights profiles. Following the principle of least privilege, both restrictive and permissive policies are implemented using a single framework. This environment can be made completely transparent to users when their accounts are created, in which case their available programs are determined by their rights profile assignments. Alternatively, users may explicitly request profile-based execution by prepending the prefix pf to their favorite shell names, or via pfexec. Unlike the sudo command, these shells do not run with elevated privileges. Instead, they set a process flag, PRIV_PFEXEC, which is inherited by their child processes.  When programs are executed the kernel checks this flag; if set it queries a policy server, pfexecd, for the appropriate process attributes and applies them to the new process credential.

Unlike AppArmor and SELinux, the entire policy is not loaded into the kernel. Instead, the policy is cached in user space by the Name Service Cache Daemon, nscd. So the policy can be maintained in an LDAP directory, and updated at any time. Those aspects of the policy that apply to currently executing processes are maintained in their kernel credential structures. When programs are executed for which no explicit attributes are specified, the parent's process credential structure is shared with the child.

Here's a flowchart of how it all works:

Monday Mar 25, 2013

Oracle Solaris Extended Policy and MySQL

Jeremy Smyth has posted two entries on his blog describing how the mandatory access controls in AppArmor and SELinux apply to MySQL. That provides me an opportunity to demonstrate the Extended Policy functionality in Oracle Solaris. While Solaris provides an equivalent level of policy granularity, it doesn't need a knob to disable enforcement; nor does it require relabeling the filesystem to make the policy effective. Note in the steps below, that we never need to inform the kernel that the policy is updated because the policy is maintained in each process credential, not in a system-wide kernel database.

Let's begin by installing MySQL

gfaden@solaris: pfexec pkg install mysql-51

Since I originally installed this system, I have the Software Installation rights profile,  so I didn't need to become root for this step. But some of the following steps require more privileges than I have been granted, so I will use the root role for the remainder of the procedure.

gfaden@solaris:svcs -a|grep -i mysql
gfaden@solaris:su -

Although the full FMRI name of the MySQL service is svc:/application/database/mysql:version_51, the last component is sufficient to uniquely specify the service. The service manifest specifies that the execution method is a shell script wrapper, /lib/svc/method/mysql_51. So this is the pathname that will be referenced in a new rights profile, called MySQL Service, created using the profiles(1) CLI.

root@solaris# profiles -p "MySQL Service"
MySQL Service> set desc="Locking down the MySQL Service"
MySQL Service> add cmd=/lib/svc/method/mysql_51
MySQL Service:mysql_51> set privs=basic
MySQL Service:mysql_51> add privs={net_privaddr}:3306/tcp
MySQL Service:mysql_51> add privs={file_write}:/var/mysql/5.1/data/*
MySQL Service:mysql_51> add privs={file_write}:/tmp/mysql.sock
MySQL Service:mysql_51> add privs={file_write}:/var/tmp/ib*
MySQL Service:mysql_51> end
MySQL Service> set uid=mysql
MySQL Service> set gid=mysql
MySQL Service> exit

The file_write privilege is a basic privilege granted by default to all processes. By explicitly enumerating the writable pathnames, write access is restricted to just those pathnames. This constraint applies to the specified executable and its child processes.

The net_privaddr privilege is required to bind to a privilege port. In the case of MySQL, binding to the default port number, 3306, doesn't normally require this privilege, since it is greater than 1023. So the ipadm(1M) command is used to add it to the set of privileged ports.

root@solaris# ipadm set-prop -p extra_priv_ports+=3306 tcp
root@solaris# ipadm show-prop -p extra_priv_ports tcp
tcp   extra_priv_ports      rw   2049,4045,   --           2049,4045    1-65535

Next we assign the profile to the MySQL service using the svccfg(1M) CLI.

root@solaris# svccfg -s mysql:version_51
svc:/application/database/mysql:version_51> setprop method_context/profile="MySQL Service"
svc:/application/database/mysql:version_51> setprop method_context/use_profile=true
svc:/application/database/mysql:version_51> refresh
svc:/application/database/mysql:version_51> exit

Finally, we enable the service, using svcadm(1M).

root@solaris# svcadm enable mysql:version_51

To verify that the policy has been properly applied, we use the ppriv(1M) and pgrep(1) commands.

root@solaris# ppriv $(pgrep mysql)
103697: /usr/mysql/5.1/bin/mysqld --basedir=/usr/mysql/5.1 --datadir=/var/mysq
        Extended policies:
        E: basic,!file_write
        I: basic,!file_write
        P: basic,!file_write
        L: all
103609: /bin/sh /usr/mysql/5.1/bin/mysqld_safe --user=mysql --datadir=/var/mys
        Extended policies:
        E: basic,!file_write
        I: basic,!file_write
        P: basic,!file_write
        L: all

Sunday Mar 24, 2013

Application Containment via Sandboxing

Normally, the ability to specify process privileges is restricted to the root role to prevent privilege escalation. By default, root is all powerful, so it can delegate any of its privileges. For example, it can specify application-specific process privileges in Rights Profiles, and then assign them to users. But Oracle Solaris allows non-root users to delegate their process privileges, too.

Although it is possible to assign sufficient rights to users so they can manage their own Rights Profiles, that isn't necessary. Instead a normal user, with no special rights can create application sandboxes with shell script wrappers. That's because subsets of the basic privileges that users get by default, can be be removed or restricted by the users themselves.

Removing or restricting basic privileges from an application can be done using ppriv(1). However, determining which privileges to remove depends on what kind of behavior you are trying to restrict. For example, you may want to prevent an application from transmitting your files over the Internet, or simply from reading or writing files in directories where you have private information. This can't be prevented in traditional OS's because your applications are implicitly allowed such access (but some smartphones allow users to restrict access by their apps).

The following shell script provides an example of how application sandboxes can be created by normal users in Oracle Solaris. Note in the following line:

 50 ppriv -s I-$DENY -r $SANDBOX -De $program

that the ppriv(1) command is passed two privilege sets as shell variables, $DENY and $SANDBOX. The first set, $DENY,  prevents the process from reading or writing any file, executing any subprocess, observing other user's processes, and (conditionally) accessing the network. This is too much of a heavy hammer, so in the second set, $SANDBOX, we refine the policy by enumerating the directories which are available for reading, writing, and executing.

This shell script also demonstrates how the policy can be adjusted to permit specific applications more or less access. For example, in lines 38-42, firefox is granted write access to several dot files in the user's home directory, where session information is maintained. And firefox is not subject to line 46 where network access is removed. However, firefox is still restricted from reading arbitrary files in the user's home directory, and can only save files in its current directory.

As an extra level of paranoia,  the default program, at line 30, is a restricted bash shell which cannot change its current directory or execute the user's dot files. So any commands that are started from this shell are similarly locked into the sandbox.

Also note, in line 50, that the debug option, -D, is specified, so you can customize the policy based on whether you want to allow your applications additional access. Access failures are listed in realtime, and include the named object and the corresponding privilege that would be required for success.

  1 #!/bin/bash
  3 # Using bash because ksh misinterprets extended policy syntax
  5 PATH=/usr/bin:/usr/sbin:/usr/gnu/bin
  7 DENY=file_read,file_write,proc_exec,proc_info
  9 SANDBOX="\
 10 {file_read}:/dev/*,\
 11 {file_read}:/etc/*,\
 12 {file_read}:/lib/*,\
 13 {file_read,file_write}:/usr/*,\
 14 {file_read}:/proc,\
 15 {file_read,file_write}:/proc/*,\
 16 {file_read}:/system/volatile/*,\
 17 {file_read,file_write}:/tmp,\
 18 {file_read,file_write}:/tmp/*,\
 19 {file_read,file_write}:/var/*,\
 20 {file_write}:$HOME,\
 21 {file_read}:$HOME/.*,\
 22 {file_read,file_write}:$PWD,\
 23 {file_read,file_write}:$PWD/*,\
 24 {proc_exec}:/usr/*\
 25 "
 27 # Default program is restricted bash shell
 29 if [[ ! -n $1 ]]; then
 30     program="/usr/bin/bash --login --noprofile --restricted"
 31 else
 32     program="$@"
 33 fi
 36 # Firefox needs more file and network access
 37 if [[ "$program" =~ firefox ]]; then
 38     SANDBOX+=",\
 39 {file_read,file_write}:$HOME/.gnome*,\
 40 {file_read,file_write}:$HOME/.mozill*,\
 41 {file_read,file_write}:$HOME/.dbu*,\
 42 {file_read,file_write}:$HOME/.puls*\
 43 "
 45 else
 46     DENY+=",net_access"
 47 fi
 49 echo Starting $program in sandbox
 50 ppriv -s I-$DENY -r $SANDBOX -De $program


This blog explores some of the security features of Oracle Solaris. In particular, topics such as Role-Based Access Control and Labeled Security are my special interests.


« July 2016