357 lines
9.5 KiB
Markdown
357 lines
9.5 KiB
Markdown
/¯¯¯¯/\¯¯¯¯\·. ''/¯¯¯¯/\¯¯¯¯\·. |¯¯¯¯|`·.' '/¯¯¯¯/\¯¯¯¯\`·.''/¯¯¯¯/\¯¯¯¯\·. |¯¯¯¯|`·.'/¯¯¯¯/`·.
|
||
|`·.·´`·|::'¯¯¯¯`·.\|·´`·.·´|:/____/`·.|`·.·´`·|:::' |·´`·.·´|::|`·.·´`·|:::|`·.·´`·|:::¯¯¯¯`·.\|.·´`·.·|:::|.·´`·.·|\¯¯¯¯\`·.
|
||
|`·.·´`·|\¯¯¯¯\·.¨ '|·´`·.·´|:\¯¯¯¯\·./|`·.·´`·|::|¯¯¯¯|`·.|·´`·.·´|::|`·.·´`·|:::|`·.·´`·|:\¯¯¯¯\·. ¨|.·´`·.·|:::|.·´`·.·|:|`·.·´`·|:::'
|
||
|____|::¯¯¯¯`·.\''\____\/____/`·.'\____\/____/`·.''\____\/____/`·.''\____\/____/`·. |____|:::|____|:|____|:::'
|
||
'`·.:::::`·.¨ ¨ ¨ ¨ ¨ ' `·.::::::::::::'`·./ `·.::::::::::::'`·./' '`·.:::::::::::::`·./' `·.::::::::::::'`·./ '`·.:::::`·.'`·.:::::`·`·.::::`·.
|
||
|
||
# fblogin — framebuffer login for tty1
|
||
|
||
Framebuffer greeter that:
|
||
- draws directly to **fbdev** (no X/Wayland),
|
||
- authenticates via **PAM** (optional fprintd),
|
||
- binds a **logind** session on **tty1**,
|
||
- execs a **real login shell** on the same VT.
|
||
|
||
---
|
||
|
||
## Quick start
|
||
|
||
```bash
|
||
# Debian/Ubuntu deps
|
||
sudo apt-get install -y build-essential libpam0g-dev libfprint-dev fprintd pkg-config
|
||
|
||
# build + install (binary, unit, pam, banner, manpage)
|
||
make
|
||
sudo make install-all
|
||
|
||
# logging (global file, debug on)
|
||
sudo install -d -m 0755 /var/log/fblogin
|
||
sudo install -m 0640 /dev/null /var/log/fblogin/fblogin.log
|
||
sudo chown root:adm /var/log/fblogin/fblogin.log
|
||
|
||
# unit env (drop-in)
|
||
sudo systemctl edit fblogin@tty1.service
|
||
# [Service]
|
||
# Environment=FBLOGIN_DEBUG=1
|
||
# Environment=FBLOGIN_LOG_FILE=/var/log/fblogin/fblogin.log
|
||
|
||
# logrotate (SIGUSR1 reopen)
|
||
sudo tee /etc/logrotate.d/fblogin >/dev/null <<'ROT'
|
||
/var/log/fblogin/fblogin.log {
|
||
rotate 7
|
||
daily
|
||
missingok
|
||
notifempty
|
||
compress
|
||
create 0640 root adm
|
||
postrotate
|
||
systemctl kill -s SIGUSR1 fblogin@tty1.service 2>/dev/null || true
|
||
endscript
|
||
}
|
||
ROT
|
||
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable --now fblogin@tty1.service
|
||
```
|
||
|
||
Switch to tty1, log in (fingerprint or password). You land in a login shell on tty1; start your session from there (e.g., `startx`).
|
||
|
||
---
|
||
|
||
## Contents
|
||
|
||
- [Scope](#scope)
|
||
- [Features](#features)
|
||
- [Repository layout](#repository-layout)
|
||
- [Build/install targets](#buildinstall-targets)
|
||
- [Systemd integration](#systemd-integration)
|
||
- [PAM stack](#pam-stack)
|
||
- [Authentication flow](#authentication-flow)
|
||
- [Session model](#session-model)
|
||
- [TTY/signals](#ttysignals)
|
||
- [Logging modes](#logging-modes)
|
||
- [Xauthority/startx notes](#xauthoritystartx-notes)
|
||
- [setup.sh](#setupsh)
|
||
- [Man page](#man-page)
|
||
- [Security](#security)
|
||
- [Troubleshooting](#troubleshooting)
|
||
- [Contributing / License / Docs](#contributing--license--docs)
|
||
|
||
---
|
||
|
||
## Scope
|
||
|
||
**Is**
|
||
- Greeter for **tty1** using fbdev.
|
||
- PAM client that opens a proper **logind** session.
|
||
- Hands control to a login shell; session startup remains user-controlled.
|
||
|
||
**Is not**
|
||
- Full display manager (no seat juggling, no VT switching).
|
||
- Compositor or X/Wayland launcher.
|
||
- Aggressive manager of `.Xauthority` (guidance only).
|
||
|
||
---
|
||
|
||
## Features
|
||
|
||
- **Env-aware logging**
|
||
- `FBLOGIN_DEBUG=1` toggles debug.
|
||
- `FBLOGIN_LOG_FILE=…` pins a global log file (no per-user switch).
|
||
- `SIGUSR1` reopens the forced log (for logrotate).
|
||
- **CLI**
|
||
- `--help`, `--version` (from `include/version.h`)
|
||
- `--log-file=/path` (implies debug; pins logging)
|
||
- `--no-fp`, `--fp-max=N` (UI hint; PAM remains authoritative)
|
||
- `--dev` (relax in-session guard), `--debug`
|
||
- **Greeter keeper**
|
||
- Greeter PAM session in a child, held open until auth, then closed.
|
||
- **sd-login diagnostics** (if linked with libsystemd)
|
||
- **Self-pipe signal wakeups** (no SA_RESTART surprises)
|
||
|
||
---
|
||
|
||
## Repository layout
|
||
|
||
```
|
||
include/
|
||
config.h fb.h input.h pam_auth.h ui.h version.h
|
||
man/
|
||
fblogin.1
|
||
src/
|
||
fb.c input.c pam_auth.c ui.c main.c
|
||
system/
|
||
systemd/fblogin@.service
|
||
pamd/fblogin
|
||
issue/issue.fblogin
|
||
docs/
|
||
ARCHITECTURE.md CHANGELOG.md Contributing.md TODO.md
|
||
Makefile
|
||
setup.sh
|
||
README.md
|
||
```
|
||
|
||
---
|
||
|
||
## Build/install targets
|
||
|
||
Variables:
|
||
- `PREFIX=/usr/local` (default)
|
||
- `BINDIR=$(PREFIX)/bin`
|
||
- `MANPREFIX=$(PREFIX)/share/man`, `MAN1DIR=$(MANPREFIX)/man1`
|
||
- `ISSUE_FILE=system/issue/issue.fblogin`
|
||
|
||
Targets:
|
||
```make
|
||
make # build
|
||
make debug|release|asan
|
||
|
||
sudo make install # binary (4755)
|
||
sudo make install-systemd # unit
|
||
sudo make install-pam # /etc/pam.d/fblogin
|
||
sudo make install-issue # /etc/issue.fblogin (optional)
|
||
sudo make install-man # manpage
|
||
sudo make install-all # all above
|
||
|
||
sudo make uninstall
|
||
sudo make uninstall-systemd
|
||
sudo make uninstall-pam
|
||
sudo make uninstall-issue
|
||
sudo make uninstall-man
|
||
sudo make uninstall-all
|
||
```
|
||
|
||
Uninstall is symmetric with defaults. If you changed `PREFIX`/`MAN*`, pass the same values to uninstall.
|
||
|
||
---
|
||
|
||
## Systemd integration
|
||
|
||
Installed unit (`system/systemd/fblogin@.service`):
|
||
|
||
```ini
|
||
[Unit]
|
||
Description=Framebuffer Login on %I
|
||
Documentation=man:systemd-logind.service(8) man:logind.conf(5)
|
||
After=systemd-user-sessions.service plymouth-quit-wait.service
|
||
Conflicts=getty@%i.service
|
||
ConditionPathExists=/dev/%I
|
||
|
||
[Service]
|
||
TTYPath=/dev/%I
|
||
TTYReset=yes
|
||
TTYVHangup=yes
|
||
StandardInput=tty
|
||
StandardOutput=tty
|
||
StandardError=tty
|
||
|
||
PAMName=fblogin
|
||
UtmpIdentifier=%I
|
||
UtmpMode=user
|
||
|
||
ExecStart=/usr/local/bin/fblogin
|
||
Type=simple
|
||
Restart=always
|
||
RestartSec=1
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
Recommended drop-in:
|
||
|
||
```ini
|
||
[Service]
|
||
Environment=FBLOGIN_DEBUG=1
|
||
Environment=FBLOGIN_LOG_FILE=/var/log/fblogin/fblogin.log
|
||
```
|
||
|
||
Logrotate:
|
||
|
||
```conf
|
||
/var/log/fblogin/fblogin.log {
|
||
rotate 7
|
||
daily
|
||
missingok
|
||
notifempty
|
||
compress
|
||
create 0640 root adm
|
||
postrotate
|
||
systemctl kill -s SIGUSR1 fblogin@tty1.service 2>/dev/null || true
|
||
endscript
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## PAM stack
|
||
|
||
`/etc/pam.d/fblogin`:
|
||
|
||
```
|
||
auth include common-auth
|
||
account include common-account
|
||
password include common-password
|
||
|
||
session required pam_env.so
|
||
session required pam_loginuid.so
|
||
session include common-session
|
||
session required pam_systemd.so
|
||
```
|
||
|
||
- `pam_loginuid.so` sets `/proc/self/loginuid`.
|
||
- `pam_systemd.so` registers the session with logind.
|
||
|
||
---
|
||
|
||
## Authentication flow
|
||
|
||
- Username field first; `Tab` toggles focus.
|
||
- If password is empty and fingerprints are enabled, try the **fingerprint** path via PAM.
|
||
- On success: welcome screen → stop greeter keeper → exec user’s login shell.
|
||
- On failure: prompt for password and proceed via PAM.
|
||
|
||
Disable fingerprints:
|
||
- `--no-fp` or `FBLOGIN_FINGERPRINT=0`.
|
||
|
||
---
|
||
|
||
## Session model
|
||
|
||
- Runs on **tty1** (`TTYPath` + `UtmpMode=user`).
|
||
- Opens the user **PAM** session; logind sees `CLASS=user TYPE=tty`.
|
||
- Drops to user’s uid/gid, sets `HOME/SHELL/USER/LOGNAME` (+ fallback `PATH`), execs the login shell with `-l`.
|
||
- Start the graphical session from that shell (e.g., `startx`).
|
||
|
||
---
|
||
|
||
## TTY/signals
|
||
|
||
- `SIGTERM`: graceful shutdown via self-pipe to break `poll(2)`.
|
||
- `SIGUSR1`: reopen forced log file.
|
||
- `SIGINT`, `SIGQUIT`, `SIGTSTP`: ignored.
|
||
|
||
Keys:
|
||
- `Tab` toggle focus, `Ctrl-U` clear focused field, `Ctrl-R` reset both, `Ctrl-D` restart greeter UI.
|
||
|
||
---
|
||
|
||
## Logging modes
|
||
|
||
- **Default**: pre-user logs to `/tmp/fblogin-$pid.log`; after username known, switch to `$HOME/.fblogin.log` (600, chowned).
|
||
- **Forced global**: set `FBLOGIN_LOG_FILE=/var/log/fblogin/fblogin.log` or pass `--log-file=/path`. Remains on that file and never switches. `SIGUSR1` triggers reopen. Requires `FBLOGIN_DEBUG=1` or `--debug` (implied by `--log-file`).
|
||
|
||
---
|
||
|
||
## Xauthority/startx notes
|
||
|
||
Do not export `XAUTHORITY` globally in `~/.zshenv`.
|
||
|
||
If keeping Xauthority under `XDG_RUNTIME_DIR` **only on tty1**, add to `~/.config/zsh/.zlogin`:
|
||
|
||
```zsh
|
||
if [[ -z "$DISPLAY" && -z "$SSH_TTY" && "$(tty)" == "/dev/tty1" ]]; then
|
||
if [[ -n "$XDG_RUNTIME_DIR" && -O "$XDG_RUNTIME_DIR" ]]; then
|
||
AUTH="${XDG_RUNTIME_DIR}/x11/Xauthority"
|
||
install -d -m 700 -- "${AUTH:h}"
|
||
if [[ ! -L "$HOME/.Xauthority" || "$(readlink -f "$HOME/.Xauthority" 2>/dev/null)" != "$AUTH" ]]; then
|
||
rm -f -- "$HOME/.Xauthority"
|
||
ln -s -- "$AUTH" "$HOME/.Xauthority"
|
||
fi
|
||
# exec startx # optional
|
||
fi
|
||
fi
|
||
```
|
||
|
||
`~/.xinitrc` should not hard-set `XAUTHORITY`; let `xinit/xauth` manage cookies. It can export env to user services if needed.
|
||
|
||
---
|
||
|
||
## setup.sh
|
||
|
||
Automates:
|
||
- PAM file write,
|
||
- unit install,
|
||
- removal of conflicting `getty@tty1` override,
|
||
- disabling getty on tty1/tty2,
|
||
- enabling `fblogin@tty1`,
|
||
- basic verification.
|
||
|
||
Run with root: `sudo ./setup.sh`.
|
||
|
||
---
|
||
|
||
## Man page
|
||
|
||
Install via `make install-man`, then `man fblogin`.
|
||
|
||
---
|
||
|
||
## Security
|
||
|
||
- Binary is **setuid root** (4755).
|
||
- PAM handles credentials; we don’t store them.
|
||
- Greeter session isolated; user session opened/closed explicitly.
|
||
- Minimal signal surface; blocking calls interrupted safely.
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
- **stdin is not a TTY**: start via `fblogin@tty1.service` on a VT.
|
||
- **PAM session failed (TTY busy / inside a session)**: use a free VT; don’t launch inside an existing session unless `--dev`.
|
||
- **polkit prompts missing**: ensure DE/WM started from the tty-backed session (`loginctl` should show `TYPE=tty`).
|
||
- **fingerprint never matches**: enroll with `fprintd-enroll`, confirm sensor support; disable with `--no-fp` if needed.
|
||
- **logs break after rotation**: use the provided logrotate and `SIGUSR1` reopen; ensure forced global logging is configured.
|
||
- **no /dev/fb0**: fbdev not exposed; current code targets fbdev, not DRM/KMS.
|
||
|
||
---
|
||
|
||
## Contributing / License / Docs
|
||
|
||
- Contribution guide: `docs/Contributing.md`
|
||
- Architecture notes: `docs/ARCHITECTURE.md`
|
||
- Changelog: `docs/CHANGELOG.md`
|
||
- License: `LICENSE`
|
||
|