login greeter
git clone https://git.lucas.co/cce-display-manager.git
README.md (6.3K)
1 # cce-display-manager
2
3 The login screen of the cce desktop: a root daemon on a VT that shows a
4 `cce-ui` greeter under [cage](https://github.com/cage-kiosk/cage), checks the
5 user's password or fingerprint against PAM, and starts their session —
6 normally `startcce`, the cce compositor.
7
8 It runs as `cce-display-manager@tty1.service` (enabled; `getty@tty1` is its
9 conflict). The machine's own session list comes from
10 `/usr/share/wayland-sessions` and `/usr/share/xsessions`, plus a built-in
11 **Bash Shell** entry for a console login on the tty.
12
13 ## Using the greeter
14
15 - Type the username (the last user is filled in) and the password, then
16 Enter — from either field; Enter in the username with no password yet moves
17 to the password.
18 - **Fingerprint**: with the username filled in and fprintd enabled for
19 `cce-display-manager-fprint`, a scan starts on its own; Enter with an empty
20 password starts one again. Submitting a password cancels a scan in
21 progress.
22 - **Up / Down** or **Ctrl+P / Ctrl+N** pick the session; **Tab** moves between
23 the two fields.
24 - **F1** powers the machine off and **F2** reboots it, at once (ly's keys,
25 the login screen this one replaced). The footer lists the keys.
26 - **F5** restarts the display manager daemon from `/usr/bin` — how a newly
27 installed version takes effect without a reboot.
28 - **Ctrl+C** exits the display manager; its unit (`Restart=always`) starts it
29 again a second later — a full restart, where F5 only re-execs the daemon.
30
31 ## How it is put together
32
33 One binary, four roles, each its own process:
34
35 | Role | Started as | Runs as | Job |
36 |---|---|---|---|
37 | daemon | `cce-display-manager` (the unit) | root | owns the tty; loops: greeter → session → greeter |
38 | greeter | `cage -s -- cce-display-manager --greeter` | root | the login screen; verifies the user, tells the daemon |
39 | fingerprint helper | `cce-display-manager --fprint-auth <user>` | root | one fingerprint attempt, killable (it holds the sensor) |
40 | session worker | `cce-display-manager --session-worker …` | root, then the user | opens the PAM/logind session, runs it as the user, closes it |
41
42 When the session ends the daemon ends its logind session too — a session's
43 leftover processes do not outlive it — and shows the greeter again. A
44 compositor restart (`ccectl restart-compositor`) relaunches the same session
45 straight away, without the greeter.
46
47 ## Installing
48
49 ```bash
50 make install
51 ```
52
53 That is two installs, and both matter:
54
55 - `ccebuild install cce-display-manager` — user-level: the keyring helper
56 scripts into `~/.local/bin` and the Secret Service D-Bus file.
57 - `ccebuild install-system` — root: `/usr/bin/cce-display-manager`, the
58 systemd units and the PAM stacks in `/etc/pam.d`. This is what the login
59 runs. It asks for sudo once and backs up everything it replaces as
60 `*.bak-<date>`.
61
62 A new binary takes effect for the **greeter** at the next login, but the
63 **daemon** keeps running the code it started with — press F5 at the greeter,
64 or reboot.
65
66 **If a login breaks** (the password is accepted, then no session): Ctrl+Alt+F2
67 gives a text login; restore `/usr/bin/cce-display-manager.bak-<date>` over
68 `/usr/bin/cce-display-manager` (and any `/etc/pam.d/cce-display-manager*.bak-<date>`
69 you changed) and reboot.
70
71 ## Logs
72
73 - The daemon, greeter, cage and session worker:
74 `journalctl -u cce-display-manager@tty1 -b`. (Under an older unit, or after an
75 F5 restart of a daemon started by one, they go to
76 `/var/log/cce-display-manager-tty1.log` instead.)
77 - The session's own output: `/run/user/<uid>/cce-session.log`, with the
78 previous session's as `cce-session.log.old`. The compositor also keeps its
79 own logs in `/run/user/<uid>/cce/`.
80
81 ## Login keyring
82
83 Login here is often by fingerprint, so PAM never sees a password and
84 `pam_gnome_keyring` cannot unlock anything. The keyring password is instead
85 sealed to the machine's TPM and fed to the daemon at startup — and
86 `pam_gnome_keyring` is deliberately **not** in the login stacks: it only
87 started a second daemon at every login that could not unlock the keyring and
88 raced this one.
89
90 - `scripts/cce-gnome-keyring-enroll` — one-time: seals a random password with
91 **tpm2-tools** into `~/.config/cce/keyring-seal.{pub,priv}` and creates the
92 gnome-keyring `login` keyring with it. Needs the `tss` group and
93 `tpm2-tools`.
94 - `scripts/cce-gnome-keyring-start` — unseals and pipes the password into
95 `gnome-keyring-daemon --foreground --unlock`, as a single process.
96 - `systemd/gnome-keyring-daemon.service.d/tpm-unlock.conf` — points the stock
97 unit at that script and sets `Restart=no`.
98 - `dbus/org.freedesktop.secrets.service` — routes bus activation to the same
99 unit, so there is only ever one provider.
100 - `scripts/cce-keyring-selftest` — one-command verdict on whether this login's
101 keyring chain worked.
102
103 **`ccebuild` does not install drop-ins** — `unit_files()` matches only
104 `.service/.target/.timer/.socket/.path`. Install this one by hand:
105
106 ```bash
107 install -Dm644 systemd/gnome-keyring-daemon.service.d/tpm-unlock.conf \
108 ~/.config/systemd/user/gnome-keyring-daemon.service.d/tpm-unlock.conf
109 ```
110
111 Three traps, each of which broke a previous attempt:
112
113 - **`systemd-creds` is not usable here.** Run by a non-root user it does not
114 touch the TPM; it delegates to a polkit-gated root service. It succeeds in an
115 interactive session and fails at login with
116 `io.systemd.InteractiveAuthenticationRequired`. tpm2-tools talks to
117 `/dev/tpmrm0` directly via the `tss` group, so it needs no agent.
118 - **Never validate this from an interactive shell** — it has a polkit agent and
119 a TTY that the login path does not. Use
120 `systemd-run --user --pipe --wait --setenv=PATH=...`, which reproduces the
121 login environment and the failure above.
122 - **`Restart=no` is load-bearing.** A drop-in replacing `ExecStart` inherits the
123 stock `Restart=on-failure`; with a credential that could not decrypt at login
124 that produced 99 restarts in ~90s and hung the greeter. Losing secrets is
125 recoverable, an unusable login is not.
126
127 Clients need `--password-store=gnome-libsecret`: Chromium picks its backend
128 from `XDG_CURRENT_DESKTOP`, does not recognise `cce`, and silently falls back
129 to plaintext even when the keyring is healthy.
130
131 ## Configuration
132
133 `/etc/cce/cce.json` — `{"scale": 2.0}` scales the greeter for a HiDPI panel
134 (cage reports a scale-1 output). The repo's `cce.json` is the one this machine
135 uses.