158 lines
7.6 KiB
Markdown
158 lines
7.6 KiB
Markdown
# fingerprint-switch
|
||
|
||
A Rust tool for selecting a fingerprint reader through **fprintd**, plus optional
|
||
lid-based routing for normal login, lock-screen and sudo authentication that
|
||
already uses fprintd. Uses the installed libfprint drivers and Polkit permissions.
|
||
|
||
Built for this laptop's two readers:
|
||
|
||
| Role | USB ID | libfprint driver | fprintd name |
|
||
| --- | --- | --- | --- |
|
||
| Internal | `27c6:609c` | `goodixmoc` | Goodix MOC Fingerprint Sensor |
|
||
| External | `3274:8012` | `mafpmoc` | MAFP MOC Fingerprint Sensor |
|
||
|
||
The USB sensor is already supported by libfprint **1.94.100**, installed on this
|
||
Aurora 44 machine. A new USB driver is unnecessary. These are match-on-chip
|
||
readers; this tool does not export fingerprint images or implement matching.
|
||
|
||
## Build and use
|
||
|
||
```sh
|
||
cargo build --release --locked
|
||
./target/release/fingerprint-switch list
|
||
./target/release/fingerprint-switch status
|
||
./target/release/fingerprint-switch probe --sensor external
|
||
```
|
||
|
||
Run these as your normal desktop user. `probe` opens and releases the reader
|
||
without enrolling. Commands that access fprintd need the host's system D-Bus;
|
||
run them outside a restricted sandbox. Polkit may request authentication.
|
||
|
||
Enroll one finger on the external sensor, then verify it:
|
||
|
||
```sh
|
||
./target/release/fingerprint-switch enroll --sensor external \
|
||
--finger right-index-finger --allow-template-reset
|
||
./target/release/fingerprint-switch verify --sensor external
|
||
```
|
||
|
||
**The template-reset flag acknowledges a real driver limitation.** In libfprint
|
||
1.94.100, the `mafpmoc` enrollment path unconditionally sends command `0x0d`, the
|
||
same command used to clear all device storage. Thus enrollment can erase all
|
||
previously stored templates on the external sensor, including other users' or
|
||
Windows templates. It does not erase the separate Goodix reader. This was
|
||
confirmed from source, not by erasing this device. Use one enrolled finger on the
|
||
external reader with this driver; enrolling a second can invalidate the first.
|
||
The tool requires the flag for MAFP/Microarray readers even when fprintd lists no
|
||
prints, because device storage may contain templates from another installation.
|
||
|
||
Follow the prompts and lift your finger between samples. Enrollment defaults to
|
||
120 seconds, verification to 30; use `--timeout SECONDS` (1–600) to change this.
|
||
Ctrl-C cancels and releases the reader. Only an explicit successful completion
|
||
produces exit code 0; no-match, cancellation and timeout produce nonzero exits.
|
||
|
||
Other examples:
|
||
|
||
```sh
|
||
./target/release/fingerprint-switch verify --sensor internal
|
||
./target/release/fingerprint-switch verify --sensor auto
|
||
./target/release/fingerprint-switch verify --sensor 'MAFP MOC Fingerprint Sensor'
|
||
```
|
||
|
||
`auto` reads the lid state at the start of the command: closed prefers external,
|
||
open prefers internal, and an absent preferred reader falls back to the other.
|
||
Explicit selection never silently falls back. Duplicate device names require a
|
||
D-Bus path from `list`; paths may change when fprintd restarts. Fingerprints must
|
||
be enrolled separately on each reader. `--user USER` selects another account
|
||
subject to fprintd's normal permissions; omitting it uses the calling user.
|
||
|
||
## Automatic routing for login and sudo
|
||
|
||
CLI selection affects only that invocation. Stock `pam_fprintd` chooses the reader
|
||
with the most enrolled fingers; it does not use the CLI's selected reader.
|
||
|
||
The optional system service checks the lid and USB presence every second and
|
||
writes a root-owned environment file under `/run/fingerprint-switch`. A fprintd
|
||
service drop-in reads `FP_DRIVERS_ALLOWLIST` from this file. It allows the internal
|
||
driver with the lid open and the external driver with it closed, falling back to
|
||
the connected reader if the preferred one is missing. With neither connected it
|
||
allows both drivers for hotplug discovery. If lid state is unavailable, it prefers
|
||
the internal reader and logs the condition.
|
||
|
||
Only the selected driver is exposed through fprintd in automatic mode. Both
|
||
devices remain physically connected. Changes queue a restart of an active
|
||
fprintd; they do not start an idle fprintd. **Switching can cancel an enrollment or
|
||
authentication already in progress.** The next attempt uses the new reader.
|
||
Keep the lid and USB connection stable while enrolling.
|
||
|
||
After enrolling and verifying both readers, install the optional integration:
|
||
|
||
```sh
|
||
sudo ./scripts/install.sh
|
||
fingerprint-switch status
|
||
journalctl -u fingerprint-switch -n 30 --no-pager
|
||
```
|
||
|
||
The installer copies the built binary to `/usr/local/bin`, installs its own
|
||
systemd service and fprintd drop-in, and enables the watcher. It does not replace
|
||
libfprint, edit PAM, change suspend behavior or delete enrollment data. Existing
|
||
fingerprint-enabled authentication flows use the selected reader. Applications
|
||
that access USB directly are outside this routing mechanism. The allowlist
|
||
operates per driver, so it is intended for this Goodix-plus-Microarray pair.
|
||
The installer restarts an already-running fprintd once to apply the new drop-in.
|
||
The runtime environment is preserved across watcher stops/restarts, and is
|
||
removed during uninstall or reboot.
|
||
If the watcher is interrupted between writing a selection and restarting
|
||
fprintd, force application with `sudo systemctl try-restart fprintd.service`.
|
||
|
||
Manual overrides, stored in `/etc/fingerprint-switch/mode`:
|
||
|
||
```sh
|
||
sudo fingerprint-switch mode external # only Microarray, regardless of lid
|
||
sudo fingerprint-switch mode internal # only Goodix, regardless of lid
|
||
sudo fingerprint-switch mode both # expose both for enrollment/CLI selection
|
||
sudo fingerprint-switch mode auto # return to lid-based routing
|
||
```
|
||
|
||
The installed watcher applies changes within a few seconds. In `both` mode PAM
|
||
returns to its usual enrollment-count selection; it does not listen on both
|
||
readers simultaneously. If the service is not installed/running, `mode` only
|
||
saves a preference. Automatic routing does not check whether the current user
|
||
has enrolled a finger on the selected reader; enroll first. It also cannot make
|
||
an inaccessible built-in reader usable when the lid is closed and USB unplugged;
|
||
use the normal password fallback in that situation.
|
||
|
||
Rollback:
|
||
|
||
```sh
|
||
sudo ./scripts/uninstall.sh
|
||
```
|
||
|
||
This removes the watcher and its drop-in, restarts an active fprintd to restore
|
||
stock discovery, and preserves all enrollments. No password/PAM settings change.
|
||
|
||
## Validation and sources
|
||
|
||
```sh
|
||
cargo test --locked
|
||
cargo clippy --all-targets --locked -- -D warnings
|
||
cargo fmt --check
|
||
bash -n scripts/install.sh scripts/uninstall.sh
|
||
```
|
||
|
||
Tests use `dbus-daemon` to run isolated mock buses and cover reader-selection
|
||
policy and D-Bus operation handling without storing biometrics. Run them outside
|
||
a sandbox that blocks local sockets. Full enrollment/verification needs a person touching the sensor;
|
||
lid-based system routing must also be tested after opting into installation.
|
||
See `VALIDATION.md` for the checks actually performed on this machine.
|
||
|
||
- [Official supported-device list](https://fprint.freedesktop.org/supported-devices.html)
|
||
- [libfprint v1.94.100 mafpmoc source](https://gitlab.freedesktop.org/libfprint/libfprint/-/blob/v1.94.100/libfprint/drivers/mafpmoc/mafpmoc.c)
|
||
- [Commit introducing unconditional enrollment clear](https://gitlab.freedesktop.org/libfprint/libfprint/-/commit/67649a0efde02211b89a8c8153d37d797bd2ca6f)
|
||
- [fprintd Device D-Bus API](https://fprint.freedesktop.org/fprintd-dev/Device.html)
|
||
- [PAM device-selection implementation](https://gitlab.freedesktop.org/libfprint/fprintd/-/blob/v1.94.5/pam/pam_fprintd.c)
|
||
- [UPower lid properties](https://upower.freedesktop.org/docs/UPower/)
|
||
|
||
`research/` contains ignored, unmodified upstream checkouts used during the
|
||
investigation; they are not dependencies of this program.
|