|
| 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. |
0 commit comments