git.lucas.co / cce-display-manager
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.