Skip to content

Instantly share code, notes, and snippets.

@dvdhrm
Created August 14, 2014 10:55
Show Gist options
  • Select an option

  • Save dvdhrm/0c0dba4f5786cf20413a to your computer and use it in GitHub Desktop.

Select an option

Save dvdhrm/0c0dba4f5786cf20413a to your computer and use it in GitHub Desktop.
authorityd
Authority
=========
The systemd authority infrastructure provides authentication and authorization
services to a system via dbus. Privileged processes can query the
authority-daemon to request authorization on behalf of someone else. In other
words, processes that provide services to unprivileged processes can ask for
authorization of that unprivileged process. Furthermore, in case the
authorization is not granted, you might optionally ask for authentication as a
user with sufficient privileges.
Both, authorization and authentication, can be used independently of each other
and can optionally operate with user-interaction. If you request
user-interaction, the authentication-agents serving the unprivileged process are
asked to allow challenge/response interaction with a user. This can be
graphical, text-based or even external. The restrictions placed on
authentication agents are kept minimal.
As authentication on linux used to be bound to linux-PAM, and PAM is accessible
from root only (via a questionable API that needs multiple threads/processes to
handle multiple authentication methods at once) we also provide a generic API
for providers of authentication methods. The authority-daemon enumerates
available providers and wraps them on the bus. That means, whenever
authentication is requested, the authority-daemon queries available providers
and manages the challenge/response interface between the provider and running
agents. To support more than text-based communication like PAM, an agent has to
explicitly attach to a provider to allow using it. Thus, if you don't have an
agent installed that supports a given provider, this provider will not be used
for any challenge/response interfaces.
The authority infrastructure is modelled around a single daemon, called
"systemd-authorityd". This daemon reads configuration files from the system and
provides an API on the bus. The daemon can run multiple times, once for each bus
you want to provide its services on. The privilege levels managed by the daemon
depend on the bus you run on. That is, if you provide an API on the user-bus and
you ask the local authority-daemon on the same bus to authorize an action
requring root-privileges, you cannot rely on the response to be authoritative.
Problem is, the user-bus is managed and owned by a user. Hence, this user can
control this bus and perform any imaginable attack. Therefore, asking for a
privilege level higher than that of the user is not reliable. Fortunately, if
you provide services on the user-bus, you should never request root-privileges
for those, anyway. Privileged processes should never talk to unprivileged buses
if reliable information is needed. The bus-owner could always spoof the
sender-credentials and thus pretend to be someone else.
Overview
========
Involved Actors:
* authorityd: The systemd-authorityd daemon provides authentication and
authorization services via a bus API.
* service-daemon: A service-daemon is a program that provides services to
other programs. Whenever such a service is used by an
untrusted program, it asks authorityd for authorization of
that untrusted program. The API that is used to offer the
service is not bound to dbus. However, the service-daemon
must be able to authoritatively identify the untrusted
program. That is, it has to know the UID, GID, PID or some
other credentials of it. Depending on which credentials it
can reliably retrieve, authorityd can apply different
authorization rules.
* service-client: A service-client is a program that accesses the service
offered by a service-daemon. There is no restriction made
on the privilege level of the service-client, nor on the
communication channel between service-client and
service-daemon.
* agent: Agents provide interactive authentication and/or authorization.
Whenever a request cannot be satisfied immediately, all agents
registered for the given subject are queried to interactively
satisfy the requests.
+--------------------+ +----------------+
| Unprivileged Agent | | Service Client |
+--------------------+ +----------------+
^ ^
Unprivileged Context | |
=================================|============================|========
Privileged Context | |
| V
+------------------+ | +----------------+
| Privileged Agent |<---------+ | +--------------->| Service Daemon |
+------------------+ | | | +----------------+
| | |
V V V
/-----\
* Bus *
\-----/
^
|
|
|
V
+------------+
| authorityd |
+------------+
API Overview
============
The authority-daemon claims the name "org.freedesktop.authority1" on the bus it
is connected to. On this bus, it provides a singleton object reachable as
"/org/freedesktop/authority1" and accessible via the manager interface
"org.freedesktop.authority1.Manager".
Furthermore, the object-manager interface of DBus is used to advertise all
available objects, their supproted interface and their properties. Use it to
enumerate objects, no separate notifications and lists are provided.
The Manager-interface is accessible to unprivileged clients. Depending on the
method and the passed arguments, access may be denied to unprivileged clients.
Usually, you can only invoke methods that only affect your local
session/process/user, in case you're unprivileged. Everyone, who does not have
the same, or a superset of the, privileges of the bus-owner, is considered
unprivileged.
org.freedesktop.authority1.Manager:
* RequestAuthentication(...)
@arg[subject]: A description of the service-client that you want to
request authentication of.
@arg[object]: The object you want the subject to authenticate as.
@arg[flags]: An array of flags modifying the behavior of this call.
@returns: Object-path to an in-flight Authentication object.
This call requests a service-client @subject to authenticate. In case
you are only interested in specific authentications, you can pass it
as @object.
For instance, if @subject is set to a UID and @object is set to
"admin", the given user is asked to authenticate as administrator.
The @flags argument allows to modify the behavior of this call:
"interactive": In case the authentication cannot be performed
automatically, the agents of the user are queried to
perform interactive authentication.
This call always returns immediately and provides an object-path to
an Authentication-object freshly created with a unique name in:
/org/freedesktop/authority1/authentication/<unique-name>
supporting an interface of name:
org.freedesktop.authority1.Authentication
This authentication object is also announced via the object-manager.
If you disabled interactivity, this object may be destroyed
immediately after it was created. Therefore, you must handle the
announcements of the object-manager (which includes properties) if you
want the information provided by that object.
* RequestAuthorization(...)
@arg[subject]: A description of the service-client that you want to
request authorization for.
@arg[action]: The action you want the subject to be authorized for.
@arg[flags]: An array of flags modifying the behavior of this call.
@returns: Object-path to an in-flight Authorization object.
This is very similar to RequestAuthentication(), but instead of asking
@subject to authenticate, it asks, whether @subject is authorized to
do the action specified by @action.
The @flags argument allows to modify the behavior of this call:
"interactive": In case the subject is not authorized to perform the
action, but the action itself defines a required
authentication level, this will query the agents of
the user to perform interactive authentication to
gain required privilege levels.
This call always returns immediately and provides an object-path to
an Authorization-object freshly created with a unique name in:
/org/freedesktop/authority1/authorization/<unique-name>
supporting an interface of name:
org.freedesktop.authority1.Authorization
* AddAgent(...)
TBD
* RemoveAgent(...)
TBD
Whenever you perform an authentication, we create an in-flight "Authentication"
object. It lives as long as the authentication takes. So in case it's
non-interactive, it is usually destroyed before the call to
RequestAuthentication() returns. This is fine, though. The object is only used
as transmitter of data for such authentications. Therefore, the object-manager
makes sure all properties are advertised with their value included. No need for
clients to ever query any properties.
However, if you use interactive authentication, the object will be kept until
authentication is done (or canceled). Thus, you can query the object (or even
call methods on it) while it's in-flight. Same applies to objects of type
"Authorization".
org.freedesktop.authority1.Authentication:
* Property: Subject
The subject describes who is performing this authentication.
* Property: Object
The Object defines what you're authenticating as. It's copied
unchanged from the RequestAuthentication() call.
* Property: Authorization
This contains the object path of the authorization this is running
for. In case this is not part of an authorization, this is an empty
path.
* Cancel(...)
This cancels an interactive authentication with immediate effect.
* Signal: Done(...)
This is sent once the authentication is done. The object is destroyed
immediately after this signal is generated. The signal carries
information about success/failure of the authentication. The actual
object you authenticated as, can be found in @object and is kept
up-to-date by authorityd.
org.freedesktop.authority1.Authorization:
* Cancel(...)
This cancels an interactive authorization with immediate effect.
* Signal: Done(...)
This is sent once the authorization is done. The object is destroyed
immediately after this signal is generated. The signal carries
information about success/failure.
Whenever an authorization cannot be granted immediately, we optionally perform
an authentication of the user in question to grant more privileges. Furthermore,
clients can request authentications without any previous authorization request.
All those authentications usually require interactivity. In those cases, agents
perform the interaction with users.
Agents can be registered privileged or unprivileged:
* If there is an authority instance running on the system bus, each user
session usually registers their own agent so password-prompts can be shown
on the screen. Those agents run as the user, thus, they are less privileged
than the system-bus authorityd instance. Therefore, they're considered
unprivileged clients.
Those agents can be used for authentication methods that can be verified by
the authority daemon. For instance, password-entries are just fine as those
passwords are verified by the authority. However, an authentication method
just just shows two buttons with "Allow this action? Yes/No" cannot be used
with unprivileged agents as the authority daemon cannot verify which button
was actually pressed.
Agents are always limited to a subject. That means, you specify which subjects
you serve and then the agent is only used for requests from that subject. This
guarantees that an unprivileged user agent is only ever asked for
authentications performed by exactly that user.
Agents do not perform any authentication themselves. They only serve as
interface to interact with users and/or hardware. The actual authentication
methods are defined by providers. An agent needs to attach to providers it wants
to support. Once an authentication is started, those providers will send
notifications to the agent, what to do. Usually, this involves
challenge/response kinda things, but is not limited to that. The agent performs
the action and then forwards the response to the provider.
The interface between authorityd and those providers is not part of this
specification. For now, providers are built-in. One provider just uses PAM as
backend, so you can use your known PAM modules. We might want to allow external
providers at some point, though.
org.freedesktop.authority1.Agent:
* Property: Subject
The subject defines who this agent is running for and which subjects
serving. Only matching authentication requests are forwarded to this
agent.
* Property: Privileged
This property specifies the privilege level of the agent. Usually,
agents need to run privileged, however, there are some exceptions
where agents might run unprivileged. Note that some providers require
privileged agents.
* Property: Language
This is a writable property. Whenever an agent changes it's UI
language, it should adjust this property so providers can resend
information in translated form.
* Signal: StartAuthentication(...)
This is sent whenever a new authentication is started. The payload
contains the object-path of the authentication object.
* Signal: StopAuthentication()
This is sent whenever a new authentication is stopped. The payload
contains the object-path of the authentication object. Note that only
one authentication can be active at a time on a single agent.
org.freedesktop.authority1.Provider:
* Property: Name
A name (usually an interface-name) that identifies the type of this
provider. It also defines the API used to communicate with this
provider.
* Attach(...)
All available providers can be enumerated by registered agents. As an
agent often does not know how to talk to a given provider, they are
not enabled automatically. Instead, an agent needs to attach to a
provider in case it knows how to talk to it.
Once attached, the provider will send ShowMessage/Challenge signals
to the agent.
* Detach(...)
This detaches an agent from a provider again.
* Respond(...)
Once a challenge-message was printed and a user reacted to it, the
agent has to forward that response to the provider. The exact format
depends on the type of the provider.
* Signal: ShowChallenge(...)
This asks an agent to show a challenge-message to the user. The exact
format of the message depends on the provider. It may be a string, but
may also be an ENUM that is left to the interpretation of the agent.
The format is part of the provider's API and it's their job to not
break it.
* Signal: ShowMessage(...)
This asks an agent to show a message to the user. The exact format of
the message depends on the provider. It may be a string, but may also
be an ENUM that is left to the interpretation of the agent.
The format is part of the provider's API and it's their job to not
break it.
Examples
========
1) Desktop-Session
On desktop-sessions, you can simply register your unprivileged agent as you
did with polkit. It will attach to the PAM provider and simply shows requests
on the screen and asks for input.
2) Password Provider
The default provider uses PAM as backend. We forward the challenge/response
strings to the agent so they can deal with this crap. We use a pam-policy
similar to polkit-1. For all advanced stuff which is not user/password, I
want separate providers as PAM really sucks for those.
3) Fingerprint
A simple provider that directly talks to the fingerprint deamon (see fdo). It
does *not* do challenge/response but only forwards status messages to the
agent. In case the agent supports this provider, it can print messages like
"finger moved to fast", "finger moved in wrong direction", and so on. And it
can also show some UI images instead of just text-messages.
Note that this provider works even if an agent is not attached. This is
really handy as you can authenticate without the agent knowing you used
fingerprint. Obviously, for status messages the agent really *should* support
it, though.
4) SAK
If we want secure-attention-key support, we need *privileged* agents. That
is, the usual user-session agent does not work for that. Instead, there's a
separate system agent which runs privileged. Whenever the SAK key is pressed
we force-switch to it. This allows users to use their session-level agent if
they don't care, but once the SAK key is pressed, we switch to the privileged
system agent.
For now, the system-agent will have a hard time looking good as it has no way
to access the session content. Therefore, it will probably much look like
switching to gdm.
5) Apps
We want to support authorization on multiple buses. Apps are one example.
Therefore, if you run the authority daemon on the user-bus and Apps ask for
authorization to access user-privileged data, we can tell agents to print
dialogs like "Allow this App to do this? Yes/No".
In this case, the agent runs in the user-session itself and it is considered
PRIVILEGED! It runs as the same privilege level as the authority daemon so it
is privileged. Therefore, the authority daemon can rely on its responses. A
simply Yes/No question is thus allowed.
Note that this only works because this is the authority daemon running on the
user-bus.
An unprivileged agent in this case would be an agent registered by the App
itself. This agent obviously cannot be trusted, so it can *not* be asked to
perform Yes/No questions. However, in case the App talks to the system-bus
authority daemon (eg., to change hostnames), it can register it's own agent
to perform fingerprint based authentication. An unprivileged agent is just
fine. Note that we probably don't want to allow doing password-based
authentication for unprivileged agents on that level. The App would gain
access to the plaintext password of the user, which is Bad.
On the user-session level, this is bad, too, but we usually expect the user
to trust itself, so we allow it so far. But maybe we should rethink that,
too.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment