Targeted administrative privileges with PolicyKit

The Bouncer


If Linux closes its doors to you, your first impulse might be to resort to su or sudo; PolicyKit provides a more flexible approach to assigning privileges.

By Tim Schürmann

typomaniac, Fotolia

By default, Linux delegates responsibilities: The only user permitted to modify system settings is the omnipotent root; normal users are restricted to their desktops.

In daily use, this restriction can be a nuisance, especially if you just want to mount a USB stick or set the system clock. If several users share a PC, the root user has to be sitting in front of the keyboard to make the changes.

A young project called PolicyKit [1] provides a more refined solution to the problem of setting access privileges. PolicyKit supports independent privilege management reminiscent of telephone directory assistance: Linux programs can "call" to ask whether a user is allowed to execute a specific system function.

Operator, Operator!

On Ubuntu, when you try to set the system clock, before the window hidden behind System | Administration | Time and Date lets you make any changes, you first have to click the padlock or shield icon. After doing so, the Time and Date Settings ask PolicyKit whether you are authorized to set the clock.

To answer this question, PolicyKit checks the ruleset, which says you are only allowed to change the time if you are a member of the Administrator group and can supply a matching password. PolicyKit then asks the Gnome desktop manager to prompt you for your password.

Gnome immediately does so and pops up the windows shown in Figure 1. Once PolicyKit has all the information it needs, it gives the thumbs up to the Time and Date Settings, which in turn unblock the full functionality of the dialog.

Figure 1: In Gnome, PolicyKit grants access to date and time settings.

By using this approach, PolicyKit can grant or revoke privileges in a targeted manner. For example, you could allow a user, who I will call carlo, to set the clock but not give him access to any other system functions. In contrast to su or sudo, the applications involved here are not given root privileges; that is, carlo would not be allowed to use the time and date window to set the time zone or access any other part of the system.

Number Puzzles

This brave new PolicyKit world has a couple of blemishes. For example, both the applications and the distribution need to support PolicyKit - at least on Ubuntu right now. OpenSUSE 11.2 and Fedora 12 still require the root password for most system settings. OpenSUSE uses PolicyKit to allow software updates by any user, but for nothing else.

On top of this, version 0.9.1 of the system was completely reworked; recent PolicyKit versions are no longer downwardly compatible. Therefore, developers have to modify or rework any applications that support PolicyKit, prompting distributors to take a fairly blunt approach of providing both the legacy and the latest versions. The latest version is often unofficially dubbed PolicyKit-1, or polkit-1 for short, to distinguish between the two.

The latest version doesn't support the graphical privilege manager, which will only work with legacy PolicyKit versions up to 0.9.0. For privilege management tasks, you have no alternative but to use your favorite editor, but this is actually less daunting than you might expect.

Lord of the Manor

PolicyKit-1 distinguishes between normal users and administrators. Administrators can tweak system settings by default just like root. The configuration files in the /etc/polkit-1/localauthority.conf.d define the members. Out of the box, PolicyKit has a single file, 50-localauthority.conf, with the entry

[Configuration]
AdminIdentities=unix-user:0

that tells PolicyKit to require the (unix-user) password for the user with ID 0 for all (AdminIdentities) that require administrative privileges. Of course, ID 0 designates the root user. In other words, installing PolicyKit doesn't change anything. On Ubuntu, a second configuration file, called 51-ubuntu-admin.conf, overwrites this rule with the following content:

[Configuration]
AdminIdentities=unix-group:admin

In line with this, all members of the admin user group are automatically privileged administrators. Although you can easily modify the defaults to suit your own needs, the next time you update your system, the file will revert to its original state and all your work will be lost. Fortunately, PolicyKit evaluates its configuration files in lexicographic order, so creating your own configuration file will override any other configuration files whose file names start with a smaller number.

To give the users adam and bonny administrative privileges, you would create a 60-myconfig.conf file in the /etc/polkit-1/localauthority.conf.d directory and add the following lines:

[Configuration]
AdminIdentities=unix-group:admin;unix-user:adam;unix-user:bonny

It doesn't really matter how you name the file, it just has to start with a higher number (60 in this example) than the others. The future admins are separated by colons and follow AdminIdentities=. Individuals adam and bonny thus need the unix-user: prefix. User groups are indicated by unix-group:.

Under the Hood

PolicyKit comprises a number of modules, and a request to it always triggers a kind of chain reaction. Initially, a non-privileged program, known as a client, triggers a function in a privileged program, or "mechanism." For example, a desktop applet (the client) could try to switch the computer to sleep mode with the use of DeviceKit (mechanism).

The mechanism checks with PolicyKit to determine whether the client is allowed to trigger this action. To do so, it sends a request to the org.freedesktop.PolicyKit1 D-Bus service. D-Bus then automatically launches the polkitd PolicyKit daemon, which checks its rules to see if the client is trustworthy. If this requires a password entry, PolicyKit then uses D-Bus to ask the desktop environment to launch an authentication agent. The agent is typically a small window that prompts the user for a password. The desktop environment developers get to decide what the authentication agent looks like.

If the mechanism finally receives a positive response via D-Bus from PolicyKit, it will execute the requested function; otherwise, it cancels the action and issues an error message. Figure 2 illustrates the whole process.

Figure 2: The client first enables a system service (1). The service then uses D-Bus to ask PolicyKit for permission (2), and PolicyKit requests a password from the user (3).

Late for a Very Important Date?

Rules define who is permitted to call system functions. PolicyKit calls them "Authorization Entries" and groups them in the /etc/polkit-1/localauthority directory in its subfolders. Some rules belong in 50-local.d; Table 1 lists the others.

To allow the non-privileged user carlo to set the clock, you would create a new configuration file with the extension .pkla (PolicyKit Local Authority). Again, the file name doesn't really matter: PolicyKit simply evaluates all the .pkla files in this directory in ascending lexicographic order. However, it makes sense to choose an intuitive name. For clock-setter carlo, the file would look like Listing 1.

Listing 1: Set the Clock
01 [Carlo allowed to set the clock]
02 Identity=unix-user:carlo
03 Action=org.gnome.clockapplet.mechanism.settime
04 ResultActive=yes
05 ResultInactive=no
06 ResultAny=no

A free-text description in square brackets opens the file. It is followed by the Identity= keyword and the user or users to whom the following privilege changes apply. Multiple users and groups need to be comma separated (as mentioned earlier) and follow the familiar unix-users: or unix-group: keywords.

The next line contains the name of the system function or action in question, org.clockapplet.mechanism.settime, which refers to setting the clock in Gnome. Typing pkaction --verbose at the command line will tell you what other actions PolicyKit supports. The list can be fairly long depending on your distribution; the following command redirects output into the text file list.txt for easier inspection:

$ pkaction --verbose > list.txt

The password prompt dialog in Gnome shown in Figure 3 provides another useful piece of information. The Details section reveals the Action the user tried to perform.

Figure 3: The Password prompt in the Gnome Authenticate dialog reveals the action the user tried to perform.

In the .pkla file, you can use a wildcard (*) to group multiple actions. For example,

Action=org.gnome.clockapplet.mechanism.*

modifies all actions that start with org.gnome.clockapplet.mechanism. at the same time. These privileges mean that carlo can set the time and change the time zone.

Privileges

The last three lines in the rule in Listing 1 define the privileges. When carlo attempts to perform an action in the current session, PolicyKit references the ResultActive= setting: yes lets carlo change the time without a prompt; auth_self forces him to enter his own password, and auth_self_keep tells PolicyKit to remember the password for a couple of seconds. If carlo needs to change the time again during this period, he doesn't have to retype his password.

Figure 4: Assuming they have the required privileges, normal users can install programs. In this case, carlo only needs to type his own password to run apt-get with root privileges.

Finally, auth_admin prompts the user to enter the administrative password. This can be any user listed after AdminIdentities= in the /etc/polkit-1/localauthority.conf.d/60-myconfig.conf file - in this example, adam and bonny. Table 2 shows the other supported values for ResultActive. Following the same pattern, ResultInactive handles queries originating from inactive sessions; ResultAny doesn't distinguish between active and inactive sessions.

On the basis of the pattern shown earlier, you can now add more sections to your .pkla file and thus tweak your privilege assignments. In a production environment, rules for a single action normally will be grouped in a file, which is then named for the action. The changes for carlo would thus be saved in org.gnome.clockapplet.mechanism.pkla.

All or Nothing

PolicyKit applies new rules immediately without a reboot. On Ubuntu, however, the modifications don't change the user's ability to set the clock: Canonical seems to have redirected the system settings so that clicking on the padlock icon queries org.freedesktop.systemtoolsbackends.set. To allow bonny to set the clock, I need to modify line 3 in Listing 1 as follows:

Action=org.freedesktop.systemtoolsbackends.set

As the action name suggests, this would give bonny access to all other system settings. She would not just be able to set the time but to tamper with user management. On Ubuntu, it would thus be easier to add bonny to the Administrator group.

Start Ramp

With PolicyKit, you can let normal users launch system programs. To allow this to happen, pkexec replaces the familiar sudo tool. For example, the command

$ pkexec --user bonny /usr/bin/apt-get

launches the package manager in the context of the bonny user account (see also the "Password Pitfalls" box). The application is executed in a minimal and secure environment. Although this makes it impossible to inject malicious code, it also makes it impossible to launch X11 programs with another user's account.

By default, only administrators - that is, the users listed in the /etc/polkit-1/localauthority.conf.d/60-myconfig.conf configuration file - are allowed to use pkexec to launch a program. To allow non-privileged user carlo to launch programs, you simply create a new rule. The action for this is org.freedesktop.policykit.exec (Listing 2).

Listing 2: Non-Privileged Users Launch Programs
01 [Allow program execution via pkexec]
02 Identity=unix-user:carlo
03 Action=org.freedesktop.policykit.exec
04 ResultActive=yes
05 ResultInactive=no
06 ResultAny=no

The code in Listing 2 would allow carlo to launch any program he likes via pkexec. If you only want to let carlo launch apt-get after entering his password, another special configuration file is needed.

Password Pitfalls

Pkexec's behavior with password prompts is logical but confusing at first glance. If you enter the command

$ pkexec --user bonny apt-get install gnuchess

you launch pkexec yourself. PolicyKit thus prompts you for your own password - not Bonny's. Because it does not have access to the /var/lib/dpkg directory, apt-get also refuses to install. To install gnuchess as bonny, you first have to log in as Bonny and then type the command

pkexec apt-get install gnuchess

PolicyKit will now prompt for Bonny's password, launch apt-get with root privileges, and install gnuchess - assuming no PolicyKit rules prevent this.

Group Action

PolicyKit can only respond to a request if it knows the action in question. Applications thus first have to tell PolicyKit the system functions they offer. For this to happen, you package the required information in one or multiple XML files that reside in the /usr/share/polkit-1/actions subdirectory, which is also where you will find org.gnome.clockapplet.mechanism.policy with the actions for the Gnome clock applet that require authentication via PolicyKit.

Launching the /usr/bin/apt-get program is just another action. To apply access controls, you need to add another XML file (see Listing 3).

Listing 3: Applying Access Controls
01 <?xml version="1.0" encoding="UTF-8"?>
02 <!DOCTYPE policyconfig PUBLIC
03  "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
04  "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
05 <policyconfig>
06
07   <vendor>Linux New Media AG</vendor>
08   <vendor_url>http://www.linuxnewmedia.de</vendor_url>
09
10   <action id="de.linuxnewmedia.example.run-apt-get">
11     <description>run apt-get</description>
12     <description xml:lang="en">run apt-get</description>
13     <message>You need to authenticate to modify the system configuration</message>
14     <message xml:lang="en">You must identify yourself to run the program apt-get</message>
15     <defaults>
16       <allow_any>no</allow_any>
17       <allow_inactive>no</allow_inactive>
18       <allow_active>auth_self_keep</allow_active>
19     </defaults>
20     <annotate key="org.freedesktop.policykit.exec.path">/usr/bin/apt-get</annotate>
21   </action>
22
23 </policyconfig>

The structure is more complex than the configuration files we have looked at thus far. The cryptic characters at the start are a must for any XML file. The publisher or developer is revealed between the <vendor> and </vendor> tags, and the address is shown in <vendor_url>. The definition of the action to be executed, which starts with <action id=, and a unique name, which PolicyKit also refers to as an Action ID, follows. Any name is fair game, as long as it is made up of numbers, lowercase letters, dots, and dashes. The convention is to use the vendor's URL back-to-front and append the action name.

A description of the action is given between the <description> and </description> tags; <description xml:lang="en"> is used for the English translation. You can add descriptions for other languages in the same way; the org.gnome.clockapplet.mechanism.policy gives you a good example. The password prompt window displays the message text, and <message xml:lang="en"> provides the English version. The defaults section defines the default privileges. The command

<allow_active>auth_admin</allow_active>

tells pkexec not to run the program until the user in the active session (allow_active) has entered an administrative password (auth_admin). Later on, you can overwrite these settings individually with a .pkla file in /etc/polkit-1/localauthority/50-local.d.

All values from Table 2 are permitted between allow_active tags. allow_inactive handles requests from inactive sessions in a similar way (and corresponds to ResultInactive), whereas allow_any (as a counterpart to ResultAny) does not worry about origins.

Finally, the path to the program is specified in <annotate key="org.freedesktop.policykit.exec.path">, which is the Apt package manager in this example.

Although you need to save the action description with a .policy extension, the file name itself is again unimportant; the convention is to follow the action ID.

Extras

Now you need to set up a special (exception) rule for user carlo in the /etc/polkit-1/localauthority/50-local.d (Listing 4) that allows carlo to launch the apt-get program via pkexec without entering a password. Unfortunately, pkexec doesn't check the parameters that a user passes in with the program. In the example here, carlo could install an arbitrary (malicious) package.

Listing 4: Launching Without a Password
01 [Release apt-get program for Carlo]
02 Identity=unix-user:carlo
03 Action=de.linuxnewmedia.example.run-apt-get
04 ResultActive=yes
05 ResultInactive=no
06 ResultAny=no

Conclusions

PolicyKit gives administrators an extremely flexible tool for tailoring access profiles. In contrast to su and sudo, PolicyKit doesn't just give the user a "get out of jail free" card for the whole application; rather, it restricts the user to individual system functions. Additionally, users don't need to detour to the command line; at most, they will be prompted to enter their password.

In more complex scenarios, the rules can easily become cluttered, and using a text editor to create and maintain them isn't exactly convenient. Additionally, PolicyKit drops another privilege management system on top of Linux's own system.

Even if you use PolicyKit to keep carlo from running a program, he might still be able to run the program directly or by using sudo. This means that you need to keep an eye on both your PolicyKit settings and legacy privileges.

For PolicyKit to succeed, developers must support it explicitly in their applications; desktop systems have to provide a password dialog, and distributions have to be more consistent in their use of PolicyKit. The current versions of openSUSE, Fedora, and Ubuntu show that there is still much ground to cover.

INFO
[1] PolicyKit: http://www.freedesktop.org/wiki/Software/PolicyKit