Skip to content

Commit 469dfff

Browse files
authored
Merge pull request #92 from linux-credentials/architecture-docs
Add architecture docs
2 parents bb238e7 + e17d0c9 commit 469dfff

9 files changed

Lines changed: 360 additions & 74 deletions

ARCHITECTURE.md

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
This document describes the high-level architecture of `credentialsd`. If you
2+
want to familiarize yourself with the code base, you are just in the right
3+
place!
4+
5+
# High-level Overview
6+
7+
There are three APIs defined in [doc/api.md](/doc/api.md). This repository contains
8+
two services that implement the three APIs defined by the specification:
9+
10+
- `credentialsd`: Implements the Gateway API and Flow Control API
11+
- `credentialsd-ui`: Implements the UI Control API.
12+
13+
These two services communicate with each other over D-Bus IPC.
14+
15+
The **Gateway** is the entrypoint for clients to interact with. The Flow
16+
Controler and UI Controller work together to guide the user through the
17+
process of selecting an appropriate credential based on the request received by
18+
the Gateway.
19+
20+
The **UI Controller** is used to launch a UI for the user to respond to
21+
authenticator requests for user interaction. The **Flow Controller** interacts
22+
with the OS and hardware, like detecting available transports and
23+
authenticators. It then relays the information needed for the UI to guide the
24+
user through the authentication flow, like prompts for a user to enter their PIN
25+
or touch the device. The UI Controller takes user input and responds back to the
26+
Flow Controller.
27+
28+
The UI Controller and Flow Controller pass user interaction request and action
29+
messages back and forth until the authenticator releases the credential. Then,
30+
the Flow Controller sends the credential to the Gateway, which relays the
31+
credential to the client.
32+
33+
Here is a diagram of the intended usage and interactions between the APIs.
34+
35+
```mermaid
36+
sequenceDiagram
37+
participant C as Client
38+
participant G as Gateway
39+
participant U as UI Controller
40+
participant F as Flow Controller
41+
participant A as Authenticator
42+
43+
C ->> +G: Initiate request
44+
G ->> U: Launch UI
45+
U ->> F: Subscribe to events
46+
loop
47+
F ->> +A: Send control messages
48+
A ->> F: Request user interaction
49+
F ->> U: Request user interaction
50+
U ->> F: Respond with user interaction
51+
end
52+
A ->> -F: Release credential
53+
F ->> G: Respond with credential
54+
G ->> -C: Respond with credential
55+
```
56+
57+
The division into multiple services and APIs has two purposes:
58+
59+
- least privilege: if there is a vulnerability in the UI process, it shouldn't
60+
have access to interact with the credential service memory directly. Also,
61+
hosting separate D-Bus services allows us to set different access control
62+
policies on the bus, restricting communication on the Flow Control and UI
63+
Control APIs to the `credentialsd` and `credentialsd-ui` services.
64+
65+
- flexibility: UIs are very specific to their desktop environment, so it would
66+
be impossible to satisfy the style and requirements for all the various Linux
67+
desktop environments out there. This separation allows desktop environments to
68+
create their own UIs targeted for their users.
69+
70+
# Code Map
71+
72+
## credentialsd
73+
74+
A Rust binary project for the service hosting the Gateway and Flow Control APIs.
75+
Interacts with authenticators and clients/user agents.
76+
77+
`credentialsd` does not start the UI directly; it sends a request to start the
78+
UI via D-Bus. It relies on D-Bus service activation or some other external
79+
method to start the process that hosts the UI Control API.
80+
81+
Credential requests on the Gateway are handled one at a time; if a new request
82+
comes in, it is immediately rejected, and the client is expected to retry if
83+
necessary. Currently, clients cannot cancel their own requests; the user has to
84+
do that via the UI.
85+
86+
### `credentialsd/src/credential_service/`
87+
88+
`CredentialService` is the main component that interacts with authenticators. It
89+
also holds request context to return back to the Gateway for request completion
90+
or when the Flow Controller notifies it that the request is cancelled.
91+
92+
Various authenticator transports are handled in sub-modules, for now USB and
93+
hybrid transports are supported. Each handler starts a `Stream` of events that
94+
represents requests from the authenticator for user interaction. If a response
95+
is required from the user, the event should contain a channel for the credential
96+
service to send the response after it receives user input.
97+
98+
The credential service mostly just forwards events over to the UI service, minus
99+
any details that are not necessary for the UI to know (like the response
100+
channels mentioned above, which cannot be serialized over D-Bus anyway).
101+
102+
Actual interaction I/O is performed in the [libwebauthn][libwebauthn] library.
103+
104+
[libwebauthn]: https://github.com/linux-credentials/libwebauthn
105+
106+
### `credentialsd/src/dbus/`
107+
108+
D-Bus clients and services.
109+
110+
The Gateway and Flow Controller services are defined here, as well as a client
111+
for the UI Controller.
112+
113+
The `model` module contains some methods to convert from D-Bus types to internal
114+
credential service types. (These types don't need to be made known to the UI, so
115+
they do not live in `credentialsd-common`).
116+
117+
### `credentialsd/src/webauthn.rs`
118+
119+
Types and functions needed to repackage requests from and responses to
120+
JSON-strings according to the [WebAuthn spec](webauthn-3).
121+
122+
There is one notable deviation from the spec: since we use JSON strings for
123+
requests and responses, raw binary fields need to be base64url-encoded strings.
124+
(See the note on [D-Bus/JSON serialization][dbus-json-serialization] in the API
125+
docs.) It is the responsibility of the application using this service to
126+
de-/construct the field accordingly.
127+
128+
Re-exports many types from `libwebauthn`.
129+
130+
[webauthn-3]: https://www.w3.org/TR/webauthn-3
131+
[dbus-json-serialization]: /doc/api.md#d-busjson-serialization
132+
133+
### `credentialsd/tests`
134+
135+
The `tests/` directory contains a setup for integration tests, allowing
136+
`credentialsd` to connect to a test D-Bus instance. There is currently only a
137+
few tests there; this should be expanded in the future.
138+
139+
## `credentialsd-common/`
140+
141+
Rust types shared between `credentialsd` and `credentials-ui`.
142+
143+
Most of the types live in `src/model.rs`, and some are duplciated in
144+
`src/server.rs`. The duplicates in the `server` module have tweaks that make it
145+
easier to serialize over D-Bus, but more difficult to work with in Rust. So
146+
conversion methods are provided between the two modules.
147+
148+
## `credentialsd-ui/`
149+
150+
A reference implementation for the UI Control API.
151+
152+
This is a GTK4 implementation of a UI Controller. We don't intend this to fully
153+
polish the UI, but it is provided as a reference for other desktop environment
154+
developers to understand how to work with the API.
155+
156+
### `credentialsd-ui/src/view_model/mod.rs`
157+
158+
This contains an event loop that listens for events from the Flow Control API
159+
and forwards them to the GTK UI.
160+
161+
The view model is written in a way that is GUI framework-agnostic (except for
162+
the fact that it uses async-std, and some frameworks may prefer Tokio or another
163+
async runtime.) This is intended to aid other GUI developers using other
164+
frameworks to set up their projects. For a given framework, it may be more
165+
intuitive not to have this separation or to structure the code differently, but
166+
the separation fo concerns here makes it clear what the developer needs to do.
167+
168+
### `credentialsd-ui/src/view_model/gtk/`
169+
170+
Contains GTK-specific code for drawing the UI.
171+
172+
## `doc/`
173+
174+
Contains spec-level documentation.
175+
176+
Some of this is leftover from the first prototype. The documentation to pay
177+
attention to is `api.md` which describes the D-Bus API and expected patterns.
178+
179+
## `dbus/`
180+
181+
Contains D-Bus service description files for D-Bus service activation, as well
182+
as service policy files.
183+
184+
During development, changes to these require pushing to the correct directory
185+
`/usr/local/share/dbus-1/services` (or `system-services` if running on the
186+
system bus), and a restart.
187+
188+
## `systemd/`
189+
190+
systemd service definition files for managing D-Bus service activation via systemd.
191+
192+
During development, changes to these require pushing to the correct directory
193+
`/usr/local/lib/systemd/user` or `system` if running as a system service and
194+
reloading systemd's configuration with `systemctl daemon-reload`.
195+
196+
## `webext/`
197+
198+
A web extension that uses native messaging features to interact with the D-Bus
199+
API via the browser Credentials Management/WebAuthn API.
200+
201+
The `add-on/` directory contains the JavaScript and manifest that is loaded into
202+
the browser, and the `app/` directory contains a Python script that proxies
203+
messages between the browser and the D-Bus service.
204+
205+
This is intended to be temporary; eventually we would like support for this API
206+
to be upstreamed into Firefox and Chromium.
207+
208+
## `demo_client/`
209+
210+
A demo RP client that can be used for testing during development.
211+
212+
This is a Python client that mimics an RP, saving the created public keys to a
213+
local `user.json` file and verifying assertions against it.
214+
215+
You can use it like this:
216+
217+
```shell
218+
cd demo_client/
219+
./main.py create
220+
./main.py get
221+
```
222+
223+
# Future Goals
224+
225+
## LSM Hardening
226+
227+
We should use all the LSM features possible, including SELinux, AppArmor and
228+
Landlock. We should also try to use `seccomp` where those fall short.
229+
230+
## Application Identity
231+
232+
Currently, credentialsd assumes that clients have the ability to request any
233+
origin. We should reduce this to only allowing some preconfigured list of
234+
trusted or "privileged clients" to request any origin.
235+
236+
What "trusted clients" means is not yet defined. This could be something like
237+
LSM labels, or even just a list of paths.
238+
239+
This list will need to be configurable by distros at least at install time, if
240+
not compile time. Steps must be taken to prevent modification of that list at runtime.
241+
242+
## Origin Binding
243+
244+
Eventually, if the application identity problem above is solved, then we would
245+
also like to allow certain clients access to request preconfigured origins based
246+
on a correlation between the application identity and the origin. We call these
247+
"unprivileged clients".
248+
249+
Potentially, this may mean hard-coding a list of trusted proxies, like the
250+
Flatpak or Snap daemons, to be trusted to set application identity properly. The
251+
applications they proxy access for would then be considered unprivileged
252+
clients.
253+
254+
(Whether this preconfiguration means that clients not on the list cannot call
255+
the D-Bus service at all, or whether they just have no binding to the origin is
256+
probably an implementation detail, or defense in depth.)
257+
258+
For more discussion on application identity and origin, see the
259+
[Origin Checking discussion][origin-discussion] on GitHub.
260+
261+
[origin-discussion]: https://github.com/linux-credentials/credentialsd/discussions/11
262+
263+
## Sandboxing
264+
265+
Eventually, we would like to sandbox I/O, like USB and Bluetooth, into separate
266+
processes so that malicious or buggy authenticators cannot corrupt the main
267+
service's memory or access privileged files. For now, all authenticator I/O and
268+
D-Bus service logic happens in the same process.

README.md

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,6 @@ with `credentialsd` directly without an add-on. You can access a
5757

5858
[firefox-patch-flatpak]: https://download.opensuse.org/repositories/home:/MSirringhaus:/webauthn_devel/openSUSE_Factory_flatpak/
5959

60-
## Clients
61-
62-
There is a demo client in the `demo_client`. It mimics an RP, saving the created public keys to a local file and verifying assertions against it.
63-
64-
```shell
65-
cd demo_client/
66-
./main.py create
67-
./main.py get
68-
```
69-
70-
There is also a demo web extension that can be used to test the service in Firefox. Instructions are in [/webext/README.md]().
71-
7260
## Mockups
7361

7462
Here are some mockups of what this would look like for a user:

demo_client/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
async def run(cmd):
1919
bus = await MessageBus().connect()
2020

21-
with open("../contrib/xyz.iinuwa.credentialsd.Credentials.xml", "r") as f:
21+
with open("../doc/xyz.iinuwa.credentialsd.Credentials.xml", "r") as f:
2222
introspection = f.read()
2323

2424
proxy_object = bus.get_proxy_object(

0 commit comments

Comments
 (0)