polkit applications are applications using the polkit authority
as a decider component. They do this by installing a .policy
file into the /usr/share/polkit-1/actions
directory and communicating with the polkit authority at runtime
(either via the D-Bus API or
indirectly through the libpolkit-gobject-1 library or the
pkcheck command).
DO use polkit if you are writing a privileged mechanism (that is, running as root or otherwise has special permissions) that is intended to be used by unprivileged programs.
DO carefully consider what actions to define. In many cases there isn't a 1:1 mapping between operations and polkit actions. Often a polkit action has more to do with the object the operation is acting on than the operation itself. It is important to strike the right balance between too fine-grained and too coarse-grained.
DO try to pick actions and implicit authorizations so applications using your mechanism will work out-of-the box for users logged in at the console. Not interrupting console users with authentication dialogs should be considered a priority. For example, it is not wise to require console users to authenticate for such mundane tasks as adding a printer queue (if the administrator really wants the OS to act this way, he can always deploy suitable authorization rules).
DO consider the impact of the
chosen implicit authorizations on multi-user systems. Generally,
ordinary users should be able to neither modify important system's
behavior for other users, nor view other users' private data. If
your application needs an authorization framework at all, it is
fairly likely that the default configuration should deny
authorization in at least some cases. Default to using
auth_admin
* instead of
auth_self
*. (On single-user desktops, the
single user is typically configured as a polkit administrator, so
the two variants behave equally. On multi-user systems,
non-administrator users will be restricted by the default
configuration.)
DO pass polkit variables along with CheckAuthorization() requests so it's possible to write authorization rules matching on these. Also document these variables in your documentation (for example, see the udisks2 actions and variables).
DO pass a customized
authentication message (using the
polkit.message
and
polkit.gettext_domain
variables) that
include more detailed information about the request than
whatever is declared in the .policy
file's
message
element. For example, it's
better to show “Authentication is needed to format
INTEL SSDSA2MH080G1GC (/dev/sda)” than just
“Authentication is needed to format the
device”.
DO make sure
your application works even when the
org.freedesktop.PolicyKit1
D-Bus service is not available (this can
happen if
polkitd(8)
is not installed or if the polkit.service systemd unit/service has been
masked). If you are using the
libpolkit-gobject-1 library this
means handling
polkit_authority_get_sync()
or
polkit_authority_get_finish()
returning NULL
or
polkit_authority_check_authorization() /
polkit_authority_check_authorization_sync()
failing with an error not in the
POLKIT_ERROR
domain.
An appropriate way of dealing with the polkit authority
not being available, could be to allow only uid 0 to
perform operations, forbid all operations or something
else.
DON'T use polkit if your program isn't intended to be used by unprivileged programs. For example, if you are writing developer tools or low-level core OS command-line tools it's fine to just require the user to be root. Users can always run your tool through e.g. sudo(8), pkexec(1) or write a simple polkit-using mechanism that allows access to a (safe) subset of your tool.
DON'T use polkit unless you actually have to. In other words, not every single privileged program providing service to unprivileged programs has to use polkit. For example, if you have a small well-written setuid helper to help deal with some implementation-detail of the OS (such as elevating the priority of the sound server process to real-time for console users) it's not really helpful to define a polkit action for this since, realistically, no-one is going to choose to not grant the privilege. Remember, a secure program is often one with little amount of code and few dependencies.
DON'T call CheckAuthorization() for all your actions every time the authority emits the Changed signal. Not only is this a waste of resources, the result may also be inaccurate as authorization rules can return whatever they want, whenever they want.
DON'T block the main thread in your mechanism (e.g. the one used to service IPC requests from unprivileged programs) while waiting for the authority to reply - calls to CheckAuthorization() may take a very long time (seconds, even minutes) to complete as user interaction may be involved. Instead, use either the asynchronous API or a dedicated thread with the synchronous API.
DON'T include any authorization rules with your application as this is only intended for administrators and special-purpose operating systems / environments. See the section called “AUTHORIZATION RULES” for more information.
An unprivileged program normally does not use polkit directly - it simply calls into a privileged mechanism and the mechanism either renders service (or refuses the request) after checking with polkit (which may include presenting an authentication dialog). In this setup, the unprivileged program is oblivious to the fact that polkit is being used - it simply just waits for the privileged mechanism to carry out the request (which, if authentication dialogs are involved may take many seconds). This is a good thing because not worrying about implementation details like polkit, helps simplify the unprivileged program.
Occasionally unprivileged programs need to disable, modify or
remove UI elements to convey to the user that a certain action
cannot be carried out (because e.g. the user is not
authorized) or authentication is needed (by e.g. displaying a
padlock icon in the UI). In this case, the best approach is
usually to have the unprivileged program get this information
from the privileged mechanism instead of polkit. This is
especially true because often there is no reliable way that
the unprivileged program can know what polkit action is going
to be used. In general, there is no guarantee that operations
(such as D-Bus methods) map 1:1: to polkit action - for
example, a disk manager service's Format()
method may check for the action
net.company.diskmanager.format-removable
if
the disk is removable and
net.company.diskmanager.format-fixed
otherwise.
However, in certain cases, for example when using the
org.freedesktop.policykit.imply annotation
(see the
polkit(8) man page),
it is meaningful for an unprivileged program to query the
polkit authority (to e.g. update UI elements) and it is
in fact allowed to do so as long as the unprivileged program doesn't pass any variables along with the
CheckAuthorization()
call (otherwise it would be easy to spoof authentication dialogs and bypass authorization rules).
In fact, since this use-case is so common,
libpolkit-gobject-1 provides the
PolkitPermission type (which is derived from
GPermission)
that can be used together with
GtkLockButton.
Note that for GtkLockButton to work well, the
polkit action backing it should use auth_admin_keep
for its implicit authorizations (or more rarely
auth_self_keep
for services which don't affect other
users).
This is often used to implement an instant
apply paradigm whereby the user
unlocks (by authenticating) e.g. a
preference pane window and is then free to change settings
until the authorization expires or is revoked.
If a polkit application wants to handle the case where no authentication agent exists (for example if the app is launched via a ssh(1) login), the application can use the PolkitAgentTextListener type to spawn its own authentication agent as needed. Alternatively, the pkttyagent(1) helper can be used to do this.