|
Softpanorama
(slightly skeptical)
Open Source Software Educational Society |
May the
source be with you,
but remember the KISS principle ;-)
|
Solaris Privilege Sets
Quotes for
The Least Privilege Model in the Solaris ...
... 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.
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.
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
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.
Notes:
- This is a Spartan WHYFF (We Help
You For Free) site written by people for whom English
is not a native language.
Some amount of grammar and spelling errors should be
expected.
- The site contain some broken links
as it develops like a living tree...
Please try to use Google, Open directory,
etc. to find a replacement link (see
HOWTO search the WEB for details). We would appreciate
if you can
mail us a correct link.
|
|
|
|
Feb 12, 2006 (Glenn Brunette's Security Weblog)
Just this month,
Darren Moffat
and I have published a
Sun
BluePrint article and opened a
OpenSolaris Security Project on privilege debugging which
includes a cool, new tool, called
privdebug.
Together these resources can help you quickly and easily
determine which privileges are used by any process, service or
application. With this information, you can configure SMF to limit
the privileges granted to it using the approach described
here.
Everything is freely available. So why not give it a try! We
would love to hear what you think!
Take care,
Glenn
Introduction
This Tech Tip explores using Process Rights Management (PRM) in the
Solaris 10 OS, and how PRM enables us to execute setuid and
setgid commands without the
setuid or setgid
flags.
Objective
PRM in the Solaris 10 OS allows us to remove setuid/setgid flags from executables
that would normally have them set, while also allowing a selected set of
non-privileged users to execute them.
In the Solaris 10 OS, although most of the "normal"
setuid/setgid
executables have been re-written to be privilege aware (PA), they still
have their setuid/setgid
flags set. This is necessary for the program to first gain the
appropriate root privilege and then drop the unnecessary ones. However,
this would require the program to be fully privilege aware and some
setuid/setgid
programs out there might not have been ported as yet.
This Tech Tip suggests a method to remove the setuid/setgid flag, while allowing a
selected non-root user to execute the program appropriately.
Example Using ping
A good example of a setuid binary would be
the ping program.
$ ls -al /usr/sbin/ping
-r-sr-xr-x 1 root bin 45016 Apr 26 2005 /usr/sbin/ping
Now rewritten to be PA, the ping program
drops unnecessary root privileges immediately upon startup:
root@solaris # ppriv -v 1325
1325: ping -s 192.168.0.1
flags = PRIV_AWARE
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: none
Let's say we now remove the setuid flag
from /usr/sbin/ping:
root@solaris # ls -al /usr/sbin/ping
-r-xr-xr-x 1 root bin 45016 Apr 26 2005 /usr/sbin/ping
Subsequently, a normal non-root user would no longer be able to
properly execute ping:
$ ping -s 192.168.0.1
ping: socket Permission denied
Why ping Failed
ping failed because it is now missing the
net_icmpaccess privilege. To illustrate this,
we run ping with the Solaris 10 OS privilege
inspection and debugging feature (using ppriv):
$ ppriv -e -D ping -s 192.168.0.1
ping[1391]: missing privilege "net_icmpaccess" (euid = 100, syscall = 230) for
"devpolicy" needed at so_socket+0x9d
ping: socket Permission denied
The above occurs because the setuid flag
has been removed from ping.
Let's look at the privilege of the parent user shell executing the
ping command:
root@solaris # ppriv -v 955
955: -ksh
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
This clearly is missing the net_icmpaccess
privilege from its E, I, and P privilege set.
The Parent Shell Privileges
Let's assign the net_icmpaccess privilege
to the parent shell process (pid 955):
#ppriv -s PEI-net_icmpaccess 955
Once again, let's look at the shell privileges:
root@solaris # ppriv -v 955
955: -ksh
flags = <none>
E: file_link_any,net_icmpaccess,proc_exec,proc_fork,proc_info,proc_session
I: file_link_any,net_icmpaccess,proc_exec,proc_fork,proc_info,proc_session
P: file_link_any,net_icmpaccess,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
We now try a ping as a non-root user from shell process 955:
$ ping -s 192.168.0.1
PING 192.168.0.1: 56 data bytes
64 bytes from silence.mshome.net (192.168.0.1): icmp_seq=0. time=0.313 ms
64 bytes from silence.mshome.net (192.168.0.1): icmp_seq=1. time=0.607 ms
64 bytes from silence.mshome.net (192.168.0.1): icmp_seq=2. time=0.566 ms
Success!
Automatic Assignment
But how do we assign individual shells the necessary privileges? Do
we have to assign them every time the user logs in or requires them?
Well, one way is to add the following user entry to
/etc/user_attr, like so:
johndoe::::defaultpriv=basic,net_icmpaccess
The above would automatically provide user johndoe (a
non-root privilege user) the appropriate
privileges to run ping without requiring that
ping have the setuid/setgid
flag set on its executable binary.
The above example illustrates a method to allow us to clear
setuid/setgid
flags from non-PA executables, which would normally require them.
Flaw
There is a flaw in using /etc/user_attr to
set the default privilege, of course: All the processes of user
admin would now also have net_icmpaccess, even though they might not require this
particular privilege.
a Sun BluePrints article, describes how to profile applications and services to
determine which Solaris 10 privileges they attempt to use. Organizations can
then restrict those applications and services so that they are granted only the
absolutely necessary privileges that they need to fulfill their intended
purpose.
Learn how to use Role-Based Access Control to enforce the "Two Man Rule",
which restricts access so that two people must work in concert to execute
highly privileged operations.
This module provides wrappers for the
Privilege-related system and library calls. Also provided are constants from
the various Privilege-related headers and dynamically generated constants for
all the privileges and privilege sets.
So what makes Solaris Privileges different?
Why didn't we copy something else like Trusted Solaris Privileges or "POSIX"
capabilities?
Let's start from what we formulated as our requirements near the beginning of
our project.
One of the important features of Solaris is complete binary backward
compatibility; in order to offer that we needed to design the privilege
subsystem in such a manner that current practices, binaries and products
would continue to work. Of course, some have solved this issue by providing a
system wide knob to turn: root / root + privileges / just privileges. We
don't like knobs in our OS; specifically not ones which drastically alter the
behaviour of a system. It makes it harder to develop software; it needs to
work for all settings. Certain products may require conflicting settings, and
so on. So we decided on a "per-process" knob which is largely automatic
With backward compatibility comes the onus on the software developer to
develop future proof interfaces; that ruled out all other interfaces as they
all have fixed bitmaps and fixed privilege/capability numbers, fixed
structure sizes in the programmer visible parts of the system. Solaris
Privileges have none of that. And while we could savely reuse the names of
the Trusted Solaris interfaces we can not redefine interfaces even from a
defunct standard. So we have interfaces which smell like Trusted Solaris but
with a completely new userland representation of privileges and privilege
sets. We can never have more signals; but we can have more privileges and
more privilege sets!
The privileges and privilege sets in Solaris 10 are represented to userland
processes and non-core kernel modules as strings; privilege sets are bitmasks
of undetermined size; they can only be allocated through the C library
routines. Privilege set names are also strings and not plain integer indices;
this gives us even more flexibility. A Solaris binary compiled for 4
privilege sets of each 32 privileges will continue to work on a Solaris
system with 5 privilege sets each of which can contain 64 privileges and with
all the privileges having their internal representation renumbered.
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.
cool feature i am
using (Score:5, Interesting)
by Anonymous Coward on Friday February 20, @08:08AM (#8338473)
|
i try with solaris express and I
find a cool feature called "ppriv" like this:
gta3# ppriv $$
1124: bash
flags = 0x0
E: all
I: basic
P: all
L: all
Ok, so I am root I have all privileges I think
but now look at rpcbind, it is running as daemon but has less
priviliges even than normal processes
gta3# ppriv 100182
100182:
/usr/sbin/rpcbind
flags = 0x2
E: net_privaddr,proc_fork,sys_nfs
I: none
P: net_privaddr,proc_fork,sys_nfs
L: all
see, it does not have privilege to do 'exec'... there are 30 or more
privileges and it has only 3. So i guess this means some stack attack
will not work against it like exec shell
also i can run and see privileges like thids
gta3$ ppriv -D -e cat /etc/shadow
cat[100619]: missing privilege "file_dac_read" (euid = 77293, syscall =
225) needed at ufs_iaccess+0xd2
cat: cannot open /etc/shadow
not sure what this means?
|
In case of broken links
please try to use Google search. If you find the page please notify
us about new location
General info:
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
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
[May 20, 2004]
Secure programmer Minimizing privileges David A. Wheeler (dwheelerNOSPAM@dwheeler.com)
Research Staff Member, Institute for Defense Analyses.
Secure programs must
minimize privileges so that any bugs are less likely to be become security
vulnerabilities. This article discusses how to minimize privileges by minimizing
the privileged modules, the privileges granted, and the time the privileges are
active. The article discusses not only some of the traditional UNIX-like
mechanisms for privileges, but some of the newer mechanisms like the FreeBSD
jail(), the Linux Security Modules (LSM) framework, and
Security-Enhanced Linux (SELinux).
On March 3rd, 2003, Internet Security Systems warned of a serious
vulnerability in Sendmail. All electronic mail is transferred using a mail
transfer agent (MTA), and Sendmail is the most popular MTA, so this warning
affected many organizations worldwide. The problem was that an e-mail message
with a carefully-crafted "from," "to," or "cc" field could give the sender
complete (root) control over any machine running Sendmail as it's commonly
configured. Even worse, typical firewalls would
not protect interior machines from this attack.
The immediate cause of the vulnerability was that one of Sendmail's
security checks was flawed, permitting a buffer overflow. But a significant
contributing factor is that Sendmail is often installed as a monolithic "setuid
root" program, with complete control over the system it runs on. Thus, any
flaw in Sendmail can give an attacker immediate control over the entire
system.
Is this design necessary? No; a popular competing MTA is Wietse Venema's
Postfix. Postfix, like Sendmail, does a number of security checks, but
Postfix is also designed as a set of modules that
minimize privilege. As a result, Postfix is generally accepted as a more
secure program than Sendmail. This article discusses how to minimize
privileges, so you can apply the same ideas to your programs.
Basics of minimizing privileges
Real-world programs have bugs in them. It's not what we want, but it's
certainly what we get. Complicated requirements, schedule pressure, and
changing environments all conspire to make useful bugless programs unlikely.
Even programs formally proved correct using sophisticated mathematical
techniques can have bugs. Why? One reason is that proofs must make many
assumptions, and usually some of those assumptions aren't completely true.
Most programs aren't examined that rigorously anyway, for a variety of
reasons. And even if there are no bugs today (unlikely), a maintenance change
or a change in the environment may introduce a bug later on. So, to handle
the real world, we have to somehow develop secure programs
in spite of the bugs in our programs.
One of the most important ways to secure programs, in spite of these bugs,
is to
minimize privileges. A privilege is simply permission to do something
that not
everyone is allowed to do. On a UNIX-like system, having the privileges
of the "root" user, of another user, or being a member of a group are some of
the most common kinds of privileges. Some systems let you give privileges to
read or write a specific file. But no matter what, to minimize privileges:
- Give a privilege to only the parts of the program needing it
- Grant only the specific privileges that part absolutely requires
- Limit the time those privileges are active or can be activated to the
absolute minimum
These are really goals, not hard absolutes. Your infrastructure (such as
your operating system or virtual machine) may not make this easy to do
precisely, or the effort to do it precisely may be so complicated that you'll
introduce more bugs trying to do it precisely. But the closer you get to
these goals, the less likely it will be that bugs will cause a security
problem. Even if a bug causes a security problem, the problems it causes are
likely to be less severe. And if you can ensure that only a tiny part of the
program has special privileges, you can spend a lot of extra time making sure
that one part resists attacks. This idea isn't new; the excellent 1975
paper by Saltzer and Schroeder discussing security principles specifically
identifies minimizing privileges as a principle (see
Resources). Some ideas, such as minimizing privileges, are timeless.
The next three sections discuss these goals in turn, including how to
implement them on UNIX-like systems. After that we'll discuss some of the
special mechanisms available in FreeBSD and Linux, including a discussion
about NSA's Security-Enhanced Linux (SELinux).
Minimize privileged modules
As noted earlier, only the parts of the program that need a privilege
should have the privilege. This means that when you're designing your
program, try to break the program into separate parts so that only small and
independent parts require special privileges.
If different parts must run concurrently, use processes (not threads) on
UNIX-like systems. Threads share their security privileges, and a
malfunctioning thread can interfere with all the other threads in a process.
Write the privileged parts as though the rest of the program was attacking
it: it might, someday! Make sure that the privileged part only does as little
as possible; limited functionality means there's less to exploit.
One common approach is to create a command-line tool with special
privileges (such as being setuid or setgid) that has an extremely limited
function. The UNIX passwd command is an example; it's a
command-line tool with special privileges to change the password (setuid
root), but the only thing it can do is change passwords. Various GUI tools
can then ask passwd to do the actual changing. Where possible,
try to avoid creating setuid or setgid programs at all, because it's very
difficult to make sure that you're really protecting all inputs.
Nevertheless, sometimes you need to create setuid/setgid programs, so when
it's necessary, make the program as small and as limited as possible.
There are many other approaches. For example, you could have a small
"server" process that has special privileges; that server allows only certain
requests, and only after verifying that the requester is allowed to make the
request. Another common approach is to start a program with privileges, which
then forks a second process that gives up all privileges and then does most
of the work.
Be careful how these modules communicate with each other. On many
UNIX-like systems, the command-line values and environment variable values
can be viewed by other users, so they aren't a good way to privately send
data between processes. Pipes work well, but be careful to avoid deadlock (a
simple request/response protocol, with flushing on both sides, works well).
Minimize privileges granted
Ensure that you only grant the privileges a program actually needs -- and no
more. The primary way that UNIX processes get privileges are the user and
groups they can run as. Normally, processes run as the user and groups of
their user, but a "setuid" or "setgid" program picks up the privileges of the
user or group that owns the program.
Sadly, there are still developers on UNIX-like systems that reflexively
give programs "setuid root" privileges. These developers think that they've
made things "easy" for themselves, because now they don't have to think hard
about exactly what privileges their programs need. The problem is that, since
these programs can do literally anything on most UNIX-like systems, any bugs
can quickly become a security disaster.
Don't give all possible privileges just because you need one simple task
done. Instead, give programs only the privileges they need. If you can, run
them as setgid not setuid -- setgid gives fewer privileges. Create special
users and groups (don't use root), and use those for what you need. Make sure
your executables are owned by root and only writeable by root, so others
can't change them. Set very restrictive file permissions -- don't let anyone
read or write files unless absolutely necessary, and use those special users
and groups. An example of all this might be the standard conventions for game
"top ten" scores. Many programs are "setgid games" so that only the game
programs can modify the "top ten" scores, and the files storing the scores
are owned by the group games (and only writeable by that group). Even if an
attacker broke into a game program, all he could do would be to change the
score files. Game developers still need to write their programs to protect
against malicious score files, however.
One useful tool -- that unfortunately is a little hard to use -- is the
chroot() system call. This system call changes what the process
views when it views the "root" of the filesystem. If you plan to use this --
and it can be useful -- be prepared to take time to use it well. The "new
root" has to be carefully prepared, which is complicated because correct
application depends on the specifics of the platform and of the application.
You
must be root to make the chroot() call, and you
should quickly change to non-root (a root user can escape a chroot
environment, so if it's to be effective, you need to drop that privilege).
And chroot doesn't change the network access. This can be a
useful system call, so it's sometimes necessary to consider it, but be
prepared for effort.
One often-forgotten tool is to limit resources, both for storage and for
processes. This can be especially useful for limiting denial-of-service
attacks:
- For storage, you can set quotas (limits) for the amount of
storage or the number of files, per user and per group, for every mounted
filesystem. On GNU/Linux systems see quota(1), quotactl(2), and
quotaon(8) for more about this, but although they're not quite everywhere,
quota systems are included in most UNIX-like systems. On GNU/Linux and
many other systems, you can set "hard" limits (never to exceed) and "soft"
limits (which can be temporarily exceeded).
- For processes, you can set a number of limits such as the number
of open files, number of processes, and so on. Such capabilities
are actually part of standards (such as the Single UNIX Specification), so
they've nearly ubiquitous on UNIX-like systems; for more information, see
getrlimit(2), setrlimit(2), and getrusage(2), sysconf(3), and ulimit(1).
Processes can never exceed the "current limit," but they can raise the
current limits all the way up to the "upper limit." Unfortunately, there's
a weird terminology problem here that can trip you up. The "current limit"
is also called the "soft" limit, and the upper limit is also called the
"hard" limit. Thus, you have the bizarre situation that processes can
never exceed the soft (current) limit of the process limits -- while
for quotas you can exceed the soft limits. I suggest using the
terms "current limit" and "upper limit" for process limits (never using
the terms "soft" and "hard") so there's no confusion.
Minimize privileges' time
Give privileges only when they're needed -- and not a moment longer.
Where possible, use whatever privileges you need immediately and then
permanently give them up. Once they're permanently given up, an attack
later on can't try to exploit those privileges in novel ways. For example, a
program that needs a single root privilege may get started as root (say, by
being setuid root) and then switch to running as a less-privileged user. This
is the approach taken by many Internet servers (including the Apache Web
server). UNIX-like systems don't let just any program open up the TCP/IP
ports 0 through 1023; you have to have root privileges. But most servers only
need to open the port when they first start up, and after that they don't
need the privilege any more. One approach is to run as root, open the
privileged port as soon as possible, and then permanently drop root
privileges (including any privileged groups the process belongs to). Try to
drop all other derived privileges too; for example, close files requiring
special privileges to open as soon as you can.
If you can't permanently give up the privilege, then you can at least
temporarily drop the privilege as often as possible. This isn't as good as
permanently dropping the privilege, since if an attacker can take control of
your program, the attacker can re-enable the privilege and exploit it. Still,
it's worth doing. Many attacks only work if they trick the privileged program
into doing something unintended while its privileges are enabled (for
example, by creating weird symbolic links and hard links). If the program
doesn't normally have its privileges enabled, it's harder for an attacker to
exploit the program.
Newer mechanisms
The principles we've discussed up to this point are actually true for just
about any operating system, and the general mechanisms have been very similar
between just about all UNIX-like systems since the 1970s. That doesn't mean
they're useless; simplicity and the test of time have their own advantages.
But some newer UNIX-like systems have added mechanisms to support least
privilege that are worth knowing about. While it's easy to find out about the
time-tested mechanisms, information about the newer mechanisms isn't as
widely known. So, here I'll discuss a few selected worthies: the FreeBSD
jail(), the Linux Security Modules (LSM) framework, and
Security-Enhanced Linux (SELinux).
FreeBSD jail()
The system call chroot() has a number of problems, as noted
above. For example, it's hard to use correctly, root users can still escape
from it, and it doesn't control network access at all. The FreeBSD developers
decided to add a new system call to counteract these problems, named
jail(). This call is similar to chroot(), but strives to
be both easier to use and more effective. Inside a jail, all requests (even
root's) are limited to the jail, processes can only communicate with other
processes in that jail, and the system blocks the typical ways root users try
to escape from the jail. A jail is assigned a specific IP address, and can't
use any others as its own address.
The jail() call is unique to FreeBSD, which currently limits
its utility. But, there's a lot of cross-pollination between the various OSS/FS
kernels. For example, a version of this jail has been developed for Linux
using the Linux Security Framework. And FreeBSD 5 has added a flexible MAC
framework (from the TrustedBSD project), including a module with
functionality essentially like SELinux's. So don't be surprised to see more
of this in the future.
Linux Security Modules (LSM)
At the 2001 Linux Kernel Summit, Linus Torvalds had a problem. Several
different security projects, including the Security-Enhanced Linux (SELinux)
project, had asked him to add their security approach to the Linux kernel.
Problem was, these different approaches were often incompatible. Torvalds
didn't have an easy way to determine which was best, so instead he asked the
projects to work together to create some sort of general security framework
for Linux. That way, administrators could install whichever security approach
they wanted on their particular system. After some discussion with Torvalds,
Crispin Cowan formed a group to create a general security framework. This
framework was named the Linux Security Modules (LSM) framework, and is now
part of the standard Linux kernel (as of kernel version 2.6).
Conceptually, the LSM framework is very simple. The Linux kernel still
does its normal security checks; for example, if you want to write to a file,
you still need write permission to it. However, any time that the Linux
kernel needs to decide if access should be granted, it also checks -- asks a
security module via a "hook" -- to determine whether or not the action is
okay. This way, an administrator can simply pick the security module he wants
to use and insert it like any other Linux kernel module. From then on, that
security module decides what's allowed.
The LSM framework was designed to be so flexible that it can implement
many different kinds of security policies. In fact, several different
projects worked together to make sure that the LSM framework is sufficient
for real work. For example, the LSM framework includes several calls when
internal objects are created and deleted -- not because those operations
might get stopped, but so that the security module can keep track of critical
data. Several different analysis tools have been used to make sure that the
LSM framework didn't miss any important hooks for its purposes. This project
turned out to be harder than many imagined, and its success was hard-won.
The LSM made a fundamental design decision that's worth understanding.
Fundamentally, the LSM framework was intentionally designed so that almost
all of its hooks would be restrictive, not authoritative. An
authoritative hook makes the absolute final decision: if the hook says a
request should be granted, then it's granted no matter what. In contrast, a
restrictive hook can only add additional restrictions; it can't grant new
permissions. In theory, if all LSM hooks were authoritative, the LSM
framework would be more flexible. One hook, named capable(), is
authoritative -- but only because it it has to be to support normal POSIX
capabilities. But making
all the hooks authoritative would have involved many radical changes to
the Linux kernel, and there was doubt that such changes would be accepted.
There were also many concerns that even the smallest bugs would be
disastrous if most hooks were authoritative; while making the hooks
restrictive meant that users would be unsurprised (no matter what, the
original UNIX permissions would still normally work). So the LSM framework
developers intentionally chose the restrictive approach, and most of its
developers decided that they could work within the framework.
It's important to understand some of the LSM framework's other
limitations, too. The LSM framework is designed to support only access
control, not other security issues such as auditing. By themselves LSM
modules can't log all requests or their results, because they won't see them
all. Why? One reason is because the kernel might reject a request without
even calling an LSM module; a problem if you wanted to audit the rejection.
Also, due to concerns about performance, some proposed LSM hooks and data
fields for networks were rejected for the mainline kernel. It's possible to
control some network accesses, but it's not enough to support "labelled"
network flows (where different packets have different security labels handled
by the operating system). These are unfortunate limitations, and not
fundamental to the general idea; hopefully the LSM framework will be extended
someday to eliminate these limitations.
Still, even with these limitations, the LSM framework can be very useful
for adding limits to privileges. Torvalds' goals were essentially met by the
LSM framework: "I'm not interested in the fight between different security
people. I want the indirection that gets
me out of that picture, and then the market can fight out which policy
and implementation actually ends up getting
used."
So, if you want to limit the privileges you give your programs on Linux,
you could create your very own Linux security module. If you want to impose
truly exotic limitations, that may be necessary -- and the nice thing is that
it's possible. However, this isn't trivial; no matter what, you're still
writing kernel code. If possible, you're better off using one of the existing
Linux security modules than trying to write your own. There are several LSM
modules available, but one of the most mature of the Linux security modules
is the Security-Enhanced Linux (SELinux) module, so let's look at that.
Copyright © 1996-2008 by Dr. Nikolai Bezroukov.
www.softpanorama.org was
created as a service to the UN Sustainable Development Networking Programme (SDNP)
in the author free time.
Submit
comments This document is an industrial compilation designed and created
exclusively for educational use and is placed under the copyright of the
Open Content License(OPL).
Original materials copyright belong to respective owners. Quotes are made
for educational purposes only in compliance with the fair use doctrine.
Standard disclaimer: The statements, views and opinions presented on
this web page are those of the author and are not endorsed by, nor do they necessarily
reflect, the opinions of the author present and former employers, SDNP or any other
organization the author may be associated with. We do not warrant the correctness
of the information provided or its fitness for any purpose.
Last modified:
June 05, 2008