Table of Contents
Table of Contents
PolicyKit is a toolkit for defining and handling the policy that allows unprivileged processes to speak to privileged processes: It is a framework for centralizing the decision making process with respect to granting access to privileged operations for unprivileged applications. PolicyKit is specifically targeting applications in rich desktop environments on multi-user UNIX-like operating systems.
Traditionally UNIX-like operating systems have a clear distinction between ordinary unprivileged users and the almight and powerful super user 'root'. However, in order for a user to access and configure hardware additional privileges and rights are needed. Hitherto, this have been done in a number of often OS-specific ways. For example, Red Hat based systems usually grant access to devices to a user if, and only if, the user is logged in at a local console. In contrast, Debian-based systems often relies on group membership, e.g. users in the 'cdrom' group can access optical drives, users in the 'plugdev' group can mount removable media and so on.
In addition, access was not only granted to devices; Red Hat-based systems, for example, provides a mechanism to allow a user at a local system to run certain applications (such as the system-config-* family) as the super user provided they could authenticate as the super user (typically by entering the root password using a graphical utility). Other distributions rely on sudo (with various graphical frontends) to provide similar functionality. Both the pam-console and sudo approaches doesn't require applications to be modified.
Finally, some classes of software (such as HAL, NetworkManager and gnome-system-tools) utilizes IPC mechanism (typically D-Bus) to provide a very narrow and well-defined subset of privileged operations to unprivileged desktop applications. It varies what mechanism is used to deny users.
There's a couple of problems with the status quo
Mechanisms are coarsely grained: either you're at the console or you're not (pam_console). Either you're a member of a group or you're not (Debian). There is no easy way to specify that only a subset of privileged operations should be available for a given user (e.g. it's hard to express "it's fine to mount removable media; it's not fine to mount fixed media; it's not fine to change the timezone" in a coherent way).
The way most people use pam-console and sudo is fundamentally broken. Full-fledged GTK+ or Qt applications as the super user which means that millions of line of code (including code such as image loaders that historically have lots of security problems) runs privileged. This is in direct violation of the well-known "least privilege" principle. In addition, often applications look out of place because settings in such programs now read per-user settings from root's home directory.
UNIX group membership have always been problematic; if a
user is a member of a group once, he can always become
member of the group again
(copy /bin/bash to $HOME; chown to
group, set the setgid bit, done).
It is difficult for upstream projects (such as GNOME or KDE) to implement features that requires administrative privileges because most downstream consumers (e.g. operating systems) have different ways of implementing access control. As a result most of these features are punted to OS distributors who have their own code for doing the same thing e.g. setting the date/timezone etc.; there is no way for file sharing applications (such as gnome-user-share, Banshee, Rhythmbox) to punch a hole in the firewall.
Without a centralized framework, access control configuration is often scattered throughout the system which makes it hard for system administrators to grasp how to configure the system.
Table of Contents
PolicyKit assumes a model where a program is split into two parts. One part, the Mechanism, runs privileged (with no user interface elements) and the other part, the policy agent, runs unprivileged. The two parts of the program are in different processes and communicate through some IPC mechanism such as pipes or the system message bus (D-Bus). In some instances the Mechanism can be considered part of the core OS and the policy agent part of the desktop stack.
A Mechanism should never trust any application that tries to use; it needs to carefully verify all data and requests passed to it from the application. This is the model employed by HAL and NetworkManager:
(TODO: diagram showing g-p-m, g-v-m, nm-applet, HAL and NM)
This model also applies to other security sensitive applications:
(TODO: diagram showing 1) gnome-screensaver / PAM-stack + /sbin/unix_chkpwd; and 2) gdm + gdm-greeter; 3) mount(8); 4) other setuid examples)
In general, such an architecture is thought of as secure as long as the Mechanism (and it's dependent libraries) have been verified to be secure.
Typically the entities that a Mechanism cares about can be split into three groups:
Subject: the entity requesting the Action; ie. an unprivileged application. To make a decision about whether to carry out the Action, the Mechanism needs to know as much about the Subject as possible, e.g. UNIX user id, UNIX process id, possible security attributes (such as SELinux security context) and other data such as if the Subject is a participant in a local or remote desktop session, whether said desktop session is currently active and so forth.
Object: some canonical representation of the Object; some Objects represent tangible things such as a UNIX device file, other Objects can be more abstract and represent e.g. a network connection to a specific destination, a reference to the power management subsystem, a reference to a piece of software tracked by the native package manager.
Action: what the Subject is attempting to do to the Object; this depends of the nature of the Object and examples include mounting a block device, formatting a block device with a file system, establishing a dial-up connection to connect to private or public networks, putting the system into a suspended state, installing an unsigned piece of software, updating the system with signed software, changing the timezone, gaining access to a webcam and so forth.
(TODO: mention that libpolkit represents the Subject as either a Caller (e.g. a process) or a Session (e.g. a group of processes in a desktop session) and what the implications are here; e.g. for granting/removing ACL's on device nodes. etc. etc. etc.)
One way to think about a Mechanism is that the Mechanism is split into an enforcer and a decider component. When an application attempts to access the Mechanism, the enforcer component will only carry out the Action if the decider component (supplied with the appropriate input parameters about the Subject, Object and Action) says it's OK.
The core of PolicyKit is implemented as a shared library that
Mechanisms can link to and use as the decider component. There's
a small set of (extensible) data structures that establish a
vocabulary for libpolkit and the Mechanism to
describe the Subject and Action in question. The Mechanism
should think about libpolkit as a black box;
it's sole purpose is to answer whether a given Subject is
permitted to do a specific Action. The answer, obviously, comes
from a configuration source read by the library and maintained
by the system administrator; see Chapter 3, PolicyKit configuration
for details on PolicyKit configuration.
The answer from libpolkit is not limited to a
boolean value; essentially the following values can be returned
Yes: It is ok for the Mechanism to carry out the Action requsted by the given Subject.
No: The Mechanism should not carry out the Action requested by the given Subject.
Require authentication: The Subject (e.g. the UI application) needs to ask the user to authenticate in order for the Mechanism to carry out this Action.
In addition,
Authentication can be specified (in the return value
from libpolkit) as either user
authentication (user puts in his own password) or super
user authentication (user puts in the root password or a
user in an administrator group authenticates).
The authorization can be kept (this is also specified in
the return value from libpolkit) either
1) indefinitely (e.g. it persists across reboots and
different desktop sessions); 2) for the remainer of the
desktop session the Subject is part of; or 3) confined to
the process life-time of the Subject.
To facilitate the authentication step, there's a shared library
called libpolkit-grant. Given an Action, this
library uses a privileged helper (as in it's a setgid
polkit application) to authenticate the user (using
PAM) and upon successful authentication leave a cookie
specifying that the given Action can be carried out. It is the
presence and contents of this cookie that will
allow libpolkit to
return Yes when the Subject asks the
Mechanism to carry out the Action again.
In order to keep the PolicyKit model reasonably simple, there is no representation of the Object. Instead, a Mechanism that cares about Objects (and many don't; for example, Mechanisms to change the timezone, punch a hole in the firewall or add a user all operate on a singleton Object: the system as a whole) must instead divide a given Action into multiple sub-Actions depending on the nature of the Object.
For example, consider a Mechanism for dial-up networking. Here,
the Subject is a UI applet running in a desktop session, the
Object is the phone number to dial and the Action is to
establish the connection (another Action could be to hang-up an
existing connection). Suppose that the Mechanism has a
white-list of phone numbers that are trusted; this could simply
be a
directory /var/lib/dialup-helper/trusted-dialup.d
where the system administrator can drop simple text or XML files
with phone numbers that are considered safe to dial. If the
phone number given by the client matches this white-list, the
Mechanism chooses the Action to
be dialup-connect-trusted. If it's not in the
white-list, the Action will be
dialup-connect-untrusted. Hence, depending
on how PolicyKit is configured it may return different answers
since these are different Actions; one sensible thing in a
default desktop rollout would be to always allow the
Action dialup-connect-trusted for local
active sessions and always require authentication for the Action
dialup-connect-untrusted.
When authentication is involved, the interaction diagram for having a Mechanism carry out an Action on behalf of a Subject looks roughly like this
TODO: include diagram
Table of Contents
A Mechanism needs to declare what Actions it supports. This is
achieved by dropping one or more XML files with the suffix .policy
into the /usr/share/PolicyKit/policy directory. An example:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
<policyconfig>
<action id="org.gnome.policykit.examples.frobnicate">
<description>Frobnicate</description>
<description xml:lang="da">Frobniker</description>
<description xml:lang="en_CA">Frobnicate, Aye!</description>
<message>System policy prevents the PolicyKit-gnome example helper from Frobnicating</message>
<message xml:lang="da">System indstillinger forhindrer PolicyKit-gnome eksempel hjælper i at Frobnikere!</message>
<message xml:lang="en_CA">System policy prevents the PolicyKit-gnome example helper from Frobnicating, Aye!</message>
<defaults>
<allow_inactive>no</allow_inactive>
<allow_active>auth_self</allow_active>
</defaults>
</action>
<action id="org.gnome.policykit.examples.tweak">
<description>Tweak</description>
<description xml:lang="da">Tvæk</description>
<description xml:lang="en_CA">Tweak, Aye!</description>
<message>System policy prevents the PolicyKit-gnome example helper from Tweaking</message>
<message xml:lang="da">System indstillinger forhindrer PolicyKit-gnome eksempel hjælper i at Tvække!</message>
<message xml:lang="en_CA">System policy prevents the PolicyKit-gnome example helper from Tweaking, Aye!</message>
<defaults>
<allow_inactive>no</allow_inactive>
<allow_active>auth_admin</allow_active>
</defaults>
</action>
</policyconfig>
The policy declaration includes:
Action Identifier: This identifies
the action and can only contain the
characters [a-z][0-9].-,
e.g. lower-case ASCII, digits, period and hyphen. In
addition the identifier needs to start with a lower-case
ASCII character. The rationale for having everything is
lower case is to make it easy to make a distinction
between PolicyKit actions and D-Bus methods / interfaces
as the latter is normally using CamelCase.
In order for the identifier to be unique, it is
recommended that a revser domain name is chosen, for
example if the company Acme Inc. has a product called
Frakker that exports two Actions Blit and Blop the action
names should be chosen
as com.acme.frakker.blit
and com.acme.frakker.blop.
Defaults:
The allow_inactive
and allow_active specify the default
answer that libpolkit will return for
respectively inactive and active sessions. See below for
valid values and their meaning.
Textual descriptions: Simply included
for convenience and organizational
purposes. Standard xml:lang mechnanisms
are used to convey localized strings (note that intltool
0.36 or greater includes native support for
handling .policy files).
The following values for the defaults are
no
auth_self
auth_self_keep_session
auth_self_keep_always
auth_admin
auth_admin_keep_session
auth_admin_keep_always
yes
The main point here is that individual upstream software
projects can provide sensible defaults, e.g. it's sensible for
the example with a dial-up mechanism to configure
the org.freedesktop.networkmanager.dialup-trusted Action to
return yes for local active sessions and
the Action
org.freedesktop.networkmanager.dialup-untrusted to perhaps
return auth_admin_keep_session. See
the section called “Beyond the Defaults” for how individual machines
and sites can customize this.
The polkit-list-actions(1) tool will list all
the Actions known to libpolkit in a
convenient
format. The polkit-policy-file-validate(1)
tool can be used to check policy files as part of the software
release and installation process.
When declaring an Action, one can also annotate it with one or more key/value pairs:
<action id="com.example.blahblaster.run-as-root">
<description>Run the graphical BlahBlaster application as the super user</description>
<message>System policy prevents the BlahBlaster application</message>
<defaults>
<allow_inactive>no</allow_inactive>
<allow_active>auth_admin</allow_active>
</defaults>
<annotate key="org.freedesktop.PolicyKit.run-as-superuser.path">/usr/bin/BlahBlaster</annotate>
</action>
This is useful when writing an extensible Mechanism that other
applications wants to use. The example declaration above is
dealing with an (hypothetical and setuid root) mechanism,
let's call it
run-as-superuser, that can start graphical
applications as uid 0. Suppose the user invokes it like this
run-as-superuser /usr/bin/BlahBlaster
Now, the run-as-superuser mechanism is only
passed a path to the application to start. In order to
determine if the calling user is allowed to run the given
application as root, we need to determine the PolicyKit Action
and then use libpolkit as usual to get an answer (and possibly
make the user authenticate to gain the privilege to run the
application). By using annotations,
the run-as-superuser mechanism can query
what the action is simply by searching for the Action that has
an annotation
where org.freedesktop.PolicyKit.run-as-superuser.path
equals the given path,
e.g. /usr/bin/BlahBlaster. It then becomes
part of the documentation for
the run-as-superuser program to specify
that applications wanting to use it, simply just needs to
provide a PolicyKit .policy file that
declares an Action with an
annotation org.freedesktop.PolicyKit.run-as-superuser.path
whose value is the path to the binary.
System administrators and sites can tweak what
answer libpolkit returns depending on the
Action and Subject and other factors through the configuration
file /etc/PolicyKit/PolicyKit.conf. The
configuration file format, along with examples, is described in
the associated manual page of the same name. Note that this file
is not supposed to be modified by individual packages, it is
solely the responsibility of the system administrator to make
changes to this file.