Repository navigation
Security
pam_usb is intended as an "user comfort" utility. While it can enhance security, if used as a second factor, it can also reduce it.
Make sure you are aware of how it works and what you combine it with (see other warnings).
Also I want to point it that this is barely audited. I've tried to raise funds for it but there was literally no interest in it seemingly...
pam_usb has gone through an ongoing security audit. The advisories below have been published and are fixed in the listed release — update to at least that version (ideally the latest) to be protected. Full write-ups (impact, affected versions, patches, credits) are available on the GitHub Security Advisories page.
CVSS scores are v3.1 base scores as recorded on the advisory. Advisories scoring
below CVSS 4.0 are not assigned a CVE (shown as — in the CVE column) and are
referenced by their GHSA identifier.
Note: Additional advisories from later audit rounds are being prepared and will be published together with the release that fixes them. Per our responsible disclosure policy, unfixed issues are not detailed publicly until a fix ships.
| CVE | GHSA | Severity | CVSS | Summary |
|---|---|---|---|---|
| CVE-2026-44713 | GHSA-822m-whrh-vrj8 | High | 8.8 | Command injection via $TMUX environment variable leads to RCE as root |
| CVE-2026-44712 | GHSA-jgv5-w6rm-7wxg | High | 8.2 | Shell injection via device UUID and username in pamusb-conf / pamusb-agent
|
| CVE-2026-44711 | GHSA-fjpm-p9pj-mp34 | High | 7.9 | Symlink attacks on the pad directory/files enable auth bypass and root file corruption |
| CVE-2026-44709 | GHSA-jxrj-q67x-wr4c | High | 7.8 |
PINENTRY_FALLBACK_APP allows arbitrary command execution; keyring password exposed in process args |
| CVE-2026-44710 | GHSA-j8cq-2gv6-gfwf | Medium | 4.6 | NULL pointer dereference from UDisks device fields causes PAM crash (login DoS) |
| CVE | GHSA | Severity | CVSS | Summary |
|---|---|---|---|---|
| CVE-2026-47269 | GHSA-jmmj-qhrq-w45g | High | 7.4 |
deny_remote incorrectly classifies IPv4-mapped IPv6 remote connections as local |
| — | GHSA-7cgr-4c38-59h2 | High | 7.4 | Local authentication check bypass via incorrect process ancestry and session identity parsing |
| CVE-2026-47272 | GHSA-vx6f-rrqr-j87c | High | 7.1 | OTP pad authentication bypass via missing system pad check and uninitialized RNG buffer |
| CVE-2026-47273 | GHSA-vfj3-5h5v-6g93 | Medium | 6.5 | XPath injection via PAM-supplied identifiers in configuration queries |
| CVE-2026-47274 | GHSA-pp29-w28g-r9h9 | Medium | 6.3 | Uncontrolled search path in the tools allows privilege escalation via PATH manipulation |
| CVE-2026-47270 | GHSA-j3xw-vc43-x7jg | Medium | 6.3 |
strtok() race condition in multi-threaded PAM hosts can corrupt the deny_remote result |
| CVE-2026-47271 | GHSA-7rvx-jcc6-7hqq | Medium | 5.1 | OOM guards removed by -DNDEBUG cause NULL dereference and auth process crash |
| CVE | GHSA | Severity | CVSS | Summary |
|---|---|---|---|---|
| CVE-2026-48064 | GHSA-w38v-cw9r-x9p6 | High | 8.1 |
PAM_RHOST check skipped when deny_remote=false allows XDMCP authentication bypass |
| CVE-2026-48065 | GHSA-24mw-m2vf-36vp | Medium | 6.7 | Unchecked integer multiplication before xmalloc() allows heap overflow on 32-bit targets |
| CVE-2026-48066 | GHSA-qg76-57wq-mpv6 | Medium | 5.7 | Thread-unsafe static pointer in log.c causes a data race under concurrent PAM auth |
| CVE-2026-48792 | GHSA-pvrg-chgw-x42c | Medium | 4.4 |
pusb_has_virtual_input_device() silently discards EACCES, disabling remote-desktop detection under non-root execution |
| CVE | GHSA | Severity | CVSS | Summary |
|---|---|---|---|---|
| CVE-2026-48981 | GHSA-96vv-r4wc-28c2 | Medium | 6.7 |
xmlReadFile with default flags permits XXE network entity fetching in conf.c
|
| CVE-2026-48980 | GHSA-qr83-mf3h-fvqr | Medium | 6.3 |
getenv() in PAM context allows environment variable injection into local-check logic |
| CVE-2026-48982 | GHSA-hxh6-9574-5vp6 | Medium | 5.8 | Missing O_EXCL on pad temp-file creation allows a concurrent update race |
| CVE-2026-48983 | GHSA-4j8q-67fq-3xc3 | Medium | 5.8 | TOCTOU race in pad directory creation allows symlink substitution |
| CVE-2026-48985 | GHSA-7j6h-wfc2-mg5q | Medium | 5.5 | NULL dereference crash in pusb_is_loginctl_local when loginctl returns an empty Remote field |
| CVE-2026-48986 | GHSA-h28h-9hc3-v595 | Medium | 4.7 | Infinite-loop DoS in the process-tree walk when the parent process exits during authentication |
| CVE-2026-48984 | GHSA-rmp6-wfrq-wrrc | Medium | 4.7 |
xfree() does not call explicit_bzero — sensitive cryptographic material may linger in freed heap |
| — | GHSA-64g7-3jrm-4jc3 | Low | 3.6 | Missing O_NOFOLLOW in evdev.c allows symlink following when opening device nodes |
| — | GHSA-vw5x-vxgv-fgxm | Low | 3.3 |
BUFSIZ truncation in pusb_get_process_envvar may cause false-negative TMUX detection |
| — | GHSA-xgfj-3wm2-gvx8 | Low | 2.5 |
fopen without O_CLOEXEC in process.c leaks /proc file descriptors into popen children |
| — | GHSA-rfcg-6wgv-77hw | Low | 1.9 | Integer overflow UB in pusb_xpath_get_time() via atoi and signed multiplication |
| — | GHSA-chgp-j28w-mx9q | Low | 1.8 | Per-device config elements were silently never applied during config parsing |
| CVE | GHSA | Severity | CVSS | Summary |
|---|---|---|---|---|
| — | GHSA-gc4c-v52c-2v34 | High | 7.4 |
deny_remote bypassed by compressed IPv6 addresses (e.g. ::1, fe80::1) in pusb_tmux_has_remote_clients
|
| — | GHSA-7583-9cqv-j9rf | High | 7.4 |
deny_remote bypassed by zone-indexed IPv6 addresses (e.g. fe80::1%eth0) in pusb_tmux_has_remote_clients
|
| — | GHSA-ffx6-xjp6-x33g | High | 7.1 |
getenv() in pusb_local_login allows the XRDP deny_remote block to be bypassed by unsetting XRDP_SESSION
|
| — | GHSA-gc6f-5hpq-ghxh | Medium | 4.4 | Heap out-of-bounds read in pusb_get_tty_from_display_server when /proc/ fills the read buffer |
This warning has been partially mitigated since 0.9.1 but XDMCP + pam_usb is still not recommended. Read carefully.
What changed in 0.9.1 (GHSA-w38v-cw9r-x9p6): The root cause of the original bypass was that the PAM_RHOST check was gated behind the deny_remote option. Display managers whitelisted with deny_remote=false (e.g. gdm-password, lightdm) also skipped the PAM_RHOST check entirely, so a remote XDMCP client was never identified as remote by pam_usb.
Since 0.9.1, the PAM_RHOST check runs unconditionally regardless of deny_remote. When a display manager correctly sets PAM_RHOST to the remote client's IP for XDMCP connections, pam_usb will detect it and return PAM_IGNORE.
Why this is still not a guarantee: PAM_RHOST is set by the display manager, not by the kernel or PAM itself. Whether a given DM sets it correctly for XDMCP depends on its implementation. Major display managers (GDM, LightDM) do set PAM_RHOST for remote sessions; less common or older DMs may not. If your DM does not set PAM_RHOST for XDMCP connections, the 0.9.1 fix offers no protection.
The remote_desktop_check option (enabled by default since 0.9.0) provides an additional layer via evdev virtual device detection, which XDMCP sessions typically trigger. However this too is not a guaranteed catch-all.
Recommendation: XDMCP is an unencrypted, largely deprecated protocol that should be avoided on general security grounds regardless of pam_usb. If you must use it, ensure you are on 0.9.1+ and verify that your specific display manager sets PAM_RHOST for XDMCP connections before relying on pam_usb as a meaningful security control.
Since v0.9.0, pam_usb ships built-in remote desktop detection controlled by the remote_desktop_check option (default: true). When enabled, authentication is denied if a known remote desktop service is detected as active. The check only runs when deny_remote is also enabled.
| Tool | Detection method |
|---|---|
| AnyDesk | Process presence alone — denied even if no remote session is active |
| VNC (x11vnc, Xvnc, GNOME Remote Desktop/VNC) | Process running and external established connection on port 5900 |
| RDP (GNOME Remote Desktop/RDP) | Process running and external established connection on port 3389 |
| TeamViewer | Process running and outbound connection to TeamViewer relay servers (port 5938) |
| Virtual input devices | Any tool that injects synthetic input via a BUS_VIRTUAL evdev device with no physical path |
All checks exclude loopback connections — a VNC or RDP session over localhost is not treated as remote.
- TeamViewer in idle state (daemon running but no active remote session)
- x11vnc / Xvnc with no client currently connected
- VNC or RDP servers listening on non-standard ports
- GNOME desktop sharing over WebRTC (newer GNOME Remote Desktop protocol)
- Parsec, Rustdesk, Chrome Remote Desktop, NoMachine, and other tools not listed above
There will likely always be some remote access software that can circumvent the check. If this is a concern, use pam_usb as a second factor (required instead of sufficient in your PAM stack) so a password is still required even when pam_usb grants access.
The virtual input device check (BUS_VIRTUAL evdev devices) can produce false positives for locally-running tools that happen to create synthetic input devices:
- Screen readers and on-screen keyboards
- Drawing tablet drivers (Wacom, Huion)
- Key remappers (keyd, xremap, input-remapper)
- Accessibility software
If pam_usb unexpectedly denies access and you do not use any remote desktop tools, set remote_desktop_check=false in the relevant section of /etc/security/pam_usb.conf to disable the check.
In the past there have been ways to circumvent the local check (see issue #51 and also the "cup of tee"). I'm confident that all known ways are fixed now.
But I need to underline "known"... I'm no security expert and it's very well possible that there are still ways to circumvent the checks.
Kudos to @Fuseteam for extensive testing, breaking and reporting.
If you use the ability to auto-unlock your keyring while using pam_usb you are exposing your keyring password to everyone being able to access the file containing it.
This means at least root (and users able to use sudo) can read your keyring password in cleartext. If you use the SAMBA feature to share your home directory you will also expose it to everyone allowed to access that share.
For most users this isn't an issue. But if you're sharing your machine with other users, or use it in a network, you should for sure keep it in mind.
In case you want to use multiple devices, and enable only one (or some) of them for superuser grants (su, sudo, polkit) you can do so with some manual configuration. You need to be sure about covering every mechanism/pam service used in your system though, in example if you enable it for sudo only but the user is able to run pkexec it will obviously fail to take effect.
Available from v0.8.7.
This feature allows you to designate specific USB devices as superuser-capable — valid for privilege-escalation services such as sudo, su, and polkit-1 — while other registered devices remain valid only for non-privileged services (login, screensaver unlock, etc.).
A single superuser="true" attribute on a element covers all services you mark as requiring it. No per-service device entries are needed.
Use case: In a shared-account environment (e.g. school lab), students and teachers use the same Unix account. Students carry a StudentUSB that lets them log in; teachers carry a TeacherUSB that additionally grants sudo access. The configuration enforces this without creating separate accounts.
Two things must be configured for the restriction to take effect:
-
Mark the device as superuser-capable in the
block, using thesuperuser="true"attribute on theelement. -
Mark the service as requiring a superuser device, by setting
inside the correspondingblock.
When a user authenticates against a service marked with superuser=true, any device in their list that does not carry the superuser="true" attribute is silently excluded for that authentication attempt. If no superuser-capable device is present, authentication is denied.
Services not marked with superuser=true are unaffected — all registered devices for the user remain valid (fully backward-compatible).
| Name | Type | Default | Description |
|---|---|---|---|
superuser |
Boolean | false |
When set on a , only devices with superuser="true" in the user's device list are accepted for that service. No effect when set on , , or . |
The element under accepts an optional superuser XML attribute:
| Name | Type | Description | Example |
|---|---|---|---|
superuser |
Attribute (optional) | Marks this device as valid for superuser services. Devices without this attribute are excluded when the service requires superuser. | superuser="true" |
<user id="alex">
<device>StudentUSBdevice>
<device superuser="true">TeacherUSBdevice>
user>A device with superuser="true" is not restricted to superuser services — it works for every service, including login. The attribute only adds eligibility for superuser-marked services.
Mark any service as requiring a superuser-capable device by adding the superuser option:
<service id="sudo">
<option name="superuser">trueoption>
service>
<service id="su">
<option name="superuser">trueoption>
service>
<service id="polkit-1">
<option name="superuser">trueoption>
<option name="deny_remote">falseoption>
service>You can apply the superuser option to any service name that appears in your PAM configuration — it is not limited to sudo, su, and polkit-1.
Scenario: A shared account labuser has two USB keys. The StudentUSB grants login only; the TeacherUSB grants login and sudo/pkexec.
<configuration>
<devices>
<device id="StudentUSB">
<vendor>SanDisk Corp.vendor>
<model>Cruzer Blademodel>
<serial>SNDKXXXXXXXXXXXXXXXXserial>
<volume_uuid>AAAA-1111volume_uuid>
device>
<device id="TeacherUSB">
<vendor>Kingstonvendor>
<model>DataTravelermodel>
<serial>KNGXXXXXXXXXXXXXXXXXserial>
<volume_uuid>BBBB-2222volume_uuid>
device>
devices>
<users>
<user id="labuser">
<device>StudentUSBdevice>
<device superuser="true">TeacherUSBdevice>
user>
users>
<services>
<service id="sudo">
<option name="superuser">trueoption>
service>
<service id="su">
<option name="superuser">trueoption>
service>
<service id="polkit-1">
<option name="superuser">trueoption>
<option name="deny_remote">falseoption>
service>
services>
configuration>Result:
| Device | Service | Outcome |
|---|---|---|
| StudentUSB | login, gdm, screensaver | Allowed |
| StudentUSB | sudo, su, polkit-1 |
Denied - no superuser attribute |
| TeacherUSB | login, gdm, screensaver | Allowed |
| TeacherUSB | sudo, su, polkit-1 | Allowed |
If all of a user's devices are excluded by the superuser filter, pam_usb emits an error visible in the system log even without debug mode (from v0.9.2):
Service "sudo" for user "alex" requires a superuser-capable device but none of the registered devices have the superuser attribute.
With , the per-device filtering decisions are also logged:
Service "sudo" requires superuser device. Filtering device list.
Device "StudentUSB" excluded for service "sudo" (no superuser attribute).
- The restriction is configured entirely in
/etc/security/pam_usb.conf— no PAM stack changes are needed. - Adding more superuser-granted services requires only a new
entry; no change to the device or user configuration is needed. - If a user has no device with
superuser="true"and authenticates against a superuser-marked service, pam_usb denies the request. The fallback PAM module (e.g.pam_unix) still runs if configured assufficientrather thanrequired. - Use
pamusb-conf --add-user=to set--superuser superuser="true"on theelement automatically (available from v0.9.0). The--superuserflag can only be combined with--add-user. Alternatively, add the attribute manually to theelement in the config file.