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:
<policyconfig> <group id="polkit-gnome-examples"> <description>PolicyKit examples for PolicyKit-gnome</description> <policy id="polkit-gnome-examples-frobnicate"> <description>Let the example Frobnicate</description> <defaults> <allow_inactive>no</allow_inactive> <allow_active>auth_self</allow_active> </defaults> </policy> <policy id="polkit-gnome-examples-tweak"> <description>Let the example Tweak</description> <defaults> <allow_inactive>no</allow_inactive> <allow_active>auth_admin</allow_active> </defaults> </policy> <policy id="polkit-gnome-examples-twiddle"> <description>Twiddle</description> <defaults> <allow_inactive>no</allow_inactive> <allow_active>auth_admin_keep_always</allow_active> </defaults> </policy> <policy id="polkit-gnome-examples-punch"> <description>Punch</description> <defaults> <allow_inactive>no</allow_inactive> <allow_active>auth_self_keep_session</allow_active> </defaults> </policy> </group> </policyconfig>
Here's a more real-world example from HAL:
<policyconfig> <group id="hal-storage"> <description>Storage Drives and Media</description> <policy id="hal-storage-mount-fixed"> <description>Mount file systems from internal drives</description> <defaults> <allow_inactive>no</allow_inactive> <allow_active>auth_admin_keep_always</allow_active> </defaults> </policy> <policy id="hal-storage-unmount-others"> <description>Unmount file systems mounted by other users</description> <defaults> <allow_inactive>no</allow_inactive> <allow_active>auth_admin_keep_always</allow_active> </defaults> </policy> </group> </policyconfig>
The policy declaration includes:
Action Identifier: This identifies
the action - it needs to be namespaced accordingly using
some unique name of the mechanism. This could be
e.g. dialup-connect-trusted
or dialup-connect-untrusted
.
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.
Grouping: This is purely for organizational purposes. The group identifier needs to be properly namespaced as well.
Textual descriptions: Simply included for convenience and organizational purposes (TODO: think about i18n).
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 dialup-connect-trusted
Action to
return yes for local active sessions and
the Action
dialup-connect-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.
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.