One request that has come up a few times is the ability for snapped daemons to make use of polkit authorisation. This essentially lets the daemon consult a system policy to decide whether to allow a non-root user to perform a particular action. When the polkitd daemon is absent, such applications typically fall back to requiring that the user be root.
I’ve been doing some analysis to see what needs to be exposed to enable this feature, and what some of the potential security concerns are.
Communicating with polkitd
The polkitd
daemon resides on the D-Bus system bus under the name org.freedesktop.PolicyKit1
and exposes a number of methods on the org.freedesktop.PolicyKit1.Authority
interface. This is a mix of methods intended for use by daemons, user-side authentication agents, and potentially a control panel type UI.
Of particular concern, the EnumerateActions
method could potentially be used to determine the list of all polkit enabled daemons on the system.
Granting access to only CheckAuthorization
and CancelCheckAuthorization
should be mostly safe, but it could probably be used to probe for the presence of well known action IDs (e.g. request non-interactive auth of an action ID, and check what error is returned).
Determining the authorisation subject
The first argument to CheckAuthorization
is a “subject” structure identifying the client the daemon wants to check. The PolkitSubject
struct can take a number of forms:
-
PolkitSystemBusName
: system bus unique name -
PolkitUnixProcess
: pid, process start time, uid -
PolkitUnixSession
: ConsoleKit session ID (is this deprecated?)
In the first case, no additional permissions should be needed: the D-Bus daemon passes the client’s unique bus name in the message.
The UNIX process subject type is needed when the client communicates with the daemon over something other than the D-Bus system bus. Snapd is an example, where we use polkit to authorise commands sent over a UNIX domain socket.
To handle this case, the daemon needs to be able to read the SO_PEERCRED
socket option for the pid and uid, and read /proc/$pid/stat
for the process start time. This last one is used to detect pid reuse attacks. This effectively means that the daemon needs to be given read access to /proc/*/stat
, which grants access to far more information than just the start time of one particular process.
Given the size of this hole, it might make sense to leave ProcessUnixProcess
support out initially, or hide it behind an interface option that review-tools can gate access to. Supporting D-Bus system daemons alone would be a decent improvement.
Installing action descriptions
The CheckAuthorization
method takes an “action ID” string as an argument. These action IDs take the form of Java-style reversed DNS names and are chosen by the daemon author. In order to be usable though, it is necessary to publish a .policy
file describing those actions. These are XML files that provide human readable descriptions of the actions (for use in prompt dialogs) and the default policy for the actions. The files need to live in /usr/share/polkit-1/actions
.
So we need some way for a snap to install new policy files. This should only be done after validating the files to ensure it’s not something that will cause polkitd
to misbehave.
One concern I have is snaps attempting to provide default policy for a second daemon’s action IDs. I still need to check what polkit does in a situation like this, but the worst case scenario would be a snap changing the default policy for an action from auth_admin
to a simple yes
. If polkit does guard against duplicate default policy, it might simply turn into a denial of service for that action ID. Neither option is particularly appealing.
Requiring that actions contain the snap name is one approach, but since actions IDs are generally hard coded into daemons this would likely require code modification in most cases. I’m not sure if there is a better solution though.
I don’t think there is any need to restrict what the default policy actually is: if a client is allowed to talk to a daemon, then the daemon could easily act without consulting polkitd. So there isn’t any real security benefit to e.g. rejecting a default permissive policy for an action ID.
Proposed implementation
- add a new
polkit
interface whose slot is implicitly provided bysystem
on classic distros. Snaps that plug this interface are allowed to connect to the D-Bus system bus and issue theCheckAuthorization
andCancelCheckAuthorization
method calls topolkitd
. - snaps can provide a
meta/polkit-1.policy
file. On snap installation, this file will be validated to ensure it is valid XML and only provides actions namedsnap.$SNAP_NAME.*
, and then installed to/usr/share/polkit-1/actions/snap.$SNAP_NAME.policy
.
I think it should be safe to have these two parts operate independently: installing policy files for unused actions should effectively be a no-op. And if we don’t initially include support for PolkitUnixProcess
subjects, then it is probably safe to allow auto-connection.