Assigning Privileges with sudo and PolicyKit
If you give users who are usually supervised more scope to help themselves, they will need additional privileges. The sudo tool and the PolicyKit authorization service can control who does what on Linux.
For admins, it would be a relief if regular users were able to handle minor management tasks, such as updating software, themselves. The only problem is that apt-get requires administrative privileges, and you would not typically want to grant those to a regular user. Fortunately, admins can work with sudo or the PolicyKit authorization service to allow specific actions in a targeted way.
Sudo
The sudo tools runs commands with a different user’s account. For apt-get upgrade, this would be the all-powerful root. Which users are allowed to use sudo with what programs is defined in the /etc/sudoers configuration file. For example, to allow user klaus to update his own computer named marvin, you would add the following line to the /etc/sudoers file:
klaus marvin=(ALL) NOPASSWD:/usr/bin/apt-get upgrade
The line starts with the username and is followed by the host on which klaus is allowed to run the command. In this case, he must be working on his own computer, marvin.
If Klaus sees an error message to the effect that klaus is not allowed to run sudo on É when running apt-get upgrade, this likely has something to do with name resolution. The administrator should check that the specified hostname matches the output from the hostname command and cross-check against the content of the /etc/hosts file.
Specifying ALL instead of the hostname means that klaus can run apt-get upgrade on any machine. As an alternative to the hostname, you can also specify the IP address. For an address range, just specify the subnet:
klaus 192.168.2.0/255.255.255.0=(ALL)NOPASSWD:/usr/bin/apt-get upgrade
In this case, the user klaus can only run the command if he is working on a machine with an IP address in the range 192.168.2.1 through 192.168.2.254.
Sudo executes a command under a different user account. The details in the parentheses specify from which accounts klaus is allowed to choose. Specifying (tim) would mean that klaus could only launch the program as user tim. (ALL) allows klaus to choose any user account, including the root user required for apt-get:
sudo -u root apt-get upgrade
The -u option for the desired user is optional in the case of root. Before sudo runs the apt-get upgrade command, klaus normally needs to enter his password. This is not required if you put NOPASSWD: in front of the program name in the configuration file. The command to be executed comes at the end of the line. If you want to let klaus run multiple programs or commands, just add a comma-separated list:
klaus marvin=(ALL) NOPASSWD:/usr/bin/apt-get update,/usr/bin/apt-get upgrade
In this case, klaus can update the package database and install updates but cannot install any new programs. The universal ALL can also be used for other parameters. The following line equates klaus with the root user:
klaus ALL=(ALL) ALL
As an alternative to individual users, the sudoers file can also grant access to all the members of a group, such as admin:
%admin ALL=(ALL) ALL
The leading percent sign tells sudo that this is a group and not an individual user, which is how Ubuntu grants all administrators access to system functions.
Listing 1 shows the sudoers configuration file for Ubuntu; I’ve left out the comments for easier reading: root, the users from the admin group and sudo, can do everything, and the remaining users can do nothing.
Listing 1: Ubuntu 12.04 /etc/sudoers file
# Redefine environmental variables: Defaults env_reset Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" # Permit access: root ALL=(ALL:ALL) ALL %admin ALL=(ALL) ALL %sudo ALL=(ALL:ALL) ALL # Add more rules here: #includedir /etc/sudoers.d
The second ALL in the parentheses indicates the allowed group name; thus, klaus can run the command as any user from any group.
Every line that begins with Defaults changes a default setting in sudo. In Listing 1, env_reset resets the environmental variables, and secure_path defines the PATH variable (i.e., the directories in which Linux can find the programs to be executed). Both of these measures are designed to make attacks more difficult. Normally, sudo retains the password, once it has been entered. Also useful is the following line:
Defaults timestamp_timeout=15
It tells sudo to forget the password for all users after 15 seconds. If you set this to 0, users have to type their passwords every time they run sudo. For the other defaults, check out the man page (man 5 sudoers).
Within /etc/sudoers, an exclamation mark negates a setting. This lets the admin prevent klaus from running a specific program. To do this, just add a leading exclamation mark to the program name. In the following example, klaus is not allowed to reboot the computer:
klaus marvin=(ALL) !/sbin/reboot
All failed sudo calls usually end up in the syslog, whereas Ubuntu uses the /var/log/auth.log file. In this way, you can find out which user has been meddling.
Dancing with the Devil
Sudo is a dangerous beast in several respects. For example, just one small typo or an ! in the wrong place can completely lock you out of the system. Note that this can happen with rules that you might not expect to have such an effect.
Therefore, admins should only edit the /etc/sudoers file with the visudo tool created expressly for this purpose. Among other things, this editor checks the syntax of the configuration file. Contrary to what the vi in the name implies, the underlying editor in some distributions today is nano (Figures 1 and 2). On Ubuntu, the tool let through obvious typos in our lab, so you should not rely too heavily on it.
In many Linux distributions, it is standard practice to shift written rules to a separate configuration file and store them in the /etc/sudoers.d directory. The line
#includedir /etc/sudoers.d
in /etc/sudoers ensures that sudo automatically reads and evaluates all the text files in the /etc/sudoers.d directory. The leading # does not introduce a comment; it is part of the command. By outsourcing, an administrator can simply delete the additional configuration files to restore the old state in the event of an error.
However, caution is also advised with these additional rules. If klaus in the following example is also a member of the admin group, he can run any system tool – not just /sbin/reboot:
klaus marvin=(ALL) NOPASSWD:/sbin/reboot %admin ALL=(ALL) ALL
In a production environment, the dependencies to which the rules are subject are not always so evident.
Mitigating Risk
To access system tools, sudo uses the setuid bit. The command therefore always runs with root privileges. Only sudo itself prevents an ordinary user from running wild on the system. To discover what rights sudo are granted, you can simply type sudo -l.
Doing without a password, by specifying NOPASSWD, is convenient for users. However, the practice is also risky because a user could unthinkingly run commands that compromise the system, and malware could launch system tools in the background without the admin noticing.
Finally, you should always specify the full path to the programs; otherwise, sudo might not run the original apt-get but a malicious program of the same name that has injected itself into the search path.
PolicyKit Authorization Service
PolicyKit cleverly works its way around permissions problems and security risks with a completely different approach: If the user klaus wants to install a package, his package manager first asks PolicyKit whether this is a permitted action. PolicyKit can then immediately give klaus the go-ahead or prompt for a password. In contrast to sudo, PolicyKit regulates individual (system) functions. Thus, the admin can allow klaus to update packages and keep him from installing new packages.
PolicyKit has been through a paradigm change in the course of its development. The current version is typically referred to as PolicyKit-1, or polkit for short. Confusingly, it has not reached version 1; the latest version when this magazine went to press was 0.107. Despite the zero in front of the dot, the PolicyKit system is stable on distributions such as Ubuntu, Fedora, and openSUSE – and has been for several years.
The core of PolicyKit is the polkitd daemon. Because it only needs to respond to requests from system tools, it runs on a restricted account. Therefore no one can use it to hijack the system. polkitd provides its functions via the D-Bus communication system. Alternatively, programs can also use the libpolkit-gobject-1 library, which encapsulates the corresponding D-Bus calls.
A LocalAuthority component checks whether the action requested from PolicyKit is allowed. As the name implies, its decision takes into account the user accounts and groups that exist locally on the computer. If a password entry is needed, PolicyKit calls an authentication agent. This agent basically consists of an input mask (Figure 3). Each desktop environment can supply its own authentication agent.
Regulator
To grant a user access to a system function, the administrator needs to create a corresponding rule. All PolicyKit rules are gathered in the /etc/polkit-1/localauthority directory. Here, you also will find five subdirectories across which the rules are distributed, depending on their origins.
Table 1 shows you who needs to park which rules in which directory. Administrators can focus on the 50-local.d directory. LocalAuthority reads all the files stored there with a .pkla suffix in ascending alphabetical order.
Neatly Sorted
To stay on track when you are writing rules, you should always start the file name with a number in ascending order. The rule files themselves are simple text files, and you can group multiple rules in a file. Note that the .pkla files need unique names. PolicyKit refers to the access rules as authorization entries.
Listing 2 shows a rule that lets user klaus change the global network settings in Network Manager.
Listing 2: Rules for LocalAuthority
[Klaus is allowed to modify network settings] Identity=unix-user:klaus Action=org.freedesktop.NetworkManager.settings.modify.system ResultInactive=no ResultActive=auth_self
A comment in square brackets introduces the rule. Identity= shows which users or user groups are affected by the rule. In line 2, this is someone with a local user account (unix-user:) and the username klaus. Multiple users or groups are separated in the file by semicolons. The line
Identity=unix-user:klaus;unix-group:networkers
would allow the entire networkers user group, not just klaus, to modify network settings.
The Action= line in Listing 2 is followed by the name of the permitted action. The pkaction command reveals what actions PolicyKit knows. Be careful; the list output by this command can be long, depending on the distribution (Figure 4).
Typically, the action name tells you what the action does. If you want to know exactly what happens, run pkaction with the name of the action (Figure 5):
pkaction --verbose --action-id org.freedesktop.NetworkManager.settings.modify.system
It makes sense to redirect the complete, detailed list into a file with, for example:
pkaction --verbose > actions.txt
The available actions depend on the distribution and the installed programs (see the “PolicyKit’s Action Program” box). Multiple actions can be combined in a single rule with the * wildcard. The line
Identity=org.freedesktop.NetworkManager.*
would let klaus run all of the functions offered by Network Manager.
What Am I?
PolicyKit distinguishes between requests that come from an active session and those that originate from an inactive session. What to do with requests from the active session is defined by ResultActive=. In Listing 2, klaus then has to type in his own password (auth_self). In the case of an inactive session, the rules after ResultInactive= apply. Line 4 contains a no, which prohibits changing the network settings in Network Manager. In addition to auth_self and no, Table 2 summarizes some other possible values.
As an alternative to ResultInactive and ResultActive, you can specify ResultAny, which is the same for active and inactive sessions. Only one of these three needs to occur in the .pkla file; the rules specified by the corresponding action then apply for anything that is missing (see the “PolicyKit’s Action Program” box).
If you were to save Listing 2 as 51-org.freedesktop.NetworkManager.pkla in the /etc/polkit-1/localauthority/50-local.d directory, PolicyKit would automatically apply the new rules without having to restart the daemon.
Listing 3: Action File (Curtailed)
0102 Modify network connections for all users 03System policies prevent you from modifying network settings for all users 0405 08no 06auth_admin_keep 07
For more examples of rules, check out the subdirectory /var/lib/polkit-1/localauthority, which is only accessible to root. This directory stores all of the rules that came with the distribution. For details of the structure of the .pkla files, check out the man page (man pklocalauthority).
Omnipotent
Some system functions are so dangerous or critical that only a genuine administrator is allowed to run them; thus, LocalAuthority distinguishes between normal users and administrators, who have the same authority as root.
Members of the elite group of administrators are defined by the configuration files in /etc/polkit-1/localauthority.conf.d. Again, LocalAuthority parses all the files in alphabetical order; settings in later files overwrite previous files. The system administrator should therefore not change existing files but add a file of their own with a higher number, which also means the distribution would not overwrite the file during a system update. By default, a configuration file comprises just two lines; for example, in Ubuntu:
[Configuration] AdminIdentities=unix-group:sudo;unix-group:admin
AdminIdentities= is followed by all users and groups who have the same rights as root from PolicyKit’s point of view. To add klaus to this elite group in Ubuntu, the administrative user must create a new file named 99-Klaus.conf and add:
[Configuration] AdminIdentities=unix-group:sudo;unix-group:admin;unix-user:klaus
The two groups, sudo and admin, come from existing configuration files, so you need to add them; otherwise, only klaus has root privileges.
Little Support
PolicyKit provides an additional security layer on top of the existing Unix authorization system, but it does not replace it. Additionally, the system programs involved must support PolicyKit or request the service. Right now, only a few selected GUI tools, such as Network Manager, do this.
All console applications, including apt-get, ignore PolicyKit. The problems this mess can cause are shown in the “Executed” box.
However, even if an application supports PolicyKit, it doesn’t need to respect its instructions; thus, an attacker can easily work around PolicyKit with malicious code. More useful facts about PolicyKit are also to be found in the “PolicyKit Workshop” article [1].
Conclusions
Both sudo and PolicyKit have their advantages and disadvantages. If you want to permit an action that PolicyKit has in its repertoire, you should create a corresponding .pkla file, which means you are only allowing the necessary functions.
Admins should not allow regular users to use sudo unless they have carefully considered the consequences or in case of an emergency: the risk is too great that one incorrect rule could allow users to do more than you intend or even lock you out of the system itself.
Linux still lacks a granular permissions system. For cases in which administrators want to allow access to command-line programs, it is especially a good idea to look into alternative techniques, such as Access Control Lists (ACLs).
Info
[1] “PolicyKit Workshop” by Tim Schürmann, Linux Magazine, September 2010, pg. 36
[2] Aptdaemon