Skip to content

Security

McDope edited this page Jul 2, 2026 · 18 revisions

General note about 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...

Known CVEs and security advisories

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.

Fixed in 0.8.7

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)

Fixed in 0.9.0

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

Fixed in 0.9.1

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

Fixed in 0.9.2

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

Fixed in 0.9.3

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//cmdline fills the read buffer

Warning about XDMCP

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.

Warning about remote desktop tools

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.

What is detected

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.

What is still NOT reliably detected

  • 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.

Known false positives

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.

Warning about remote access (SSH etc)

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.

Warning about the ability to unlock the GNOME keyring

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.

Require a specific device for superuser access

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.


How it works

Two things must be configured for the restriction to take effect:

  1. Mark the device as superuser-capable in the block, using the superuser="true" attribute on the element.
  2. Mark the service as requiring a superuser device, by setting inside the corresponding block.

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).


Option reference

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 .

User configuration

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.


Service configuration

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.


Complete example

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

Log output

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).

Notes

  • 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 as sufficient rather than required.
  • Use pamusb-conf --add-user= --superuser to set superuser="true" on the element automatically (available from v0.9.0). The --superuser flag can only be combined with --add-user. Alternatively, add the attribute manually to the element in the config file.