https://bugs.kde.org/show_bug.cgi?id=522093

            Bug ID: 522093
           Summary: KRDP no longer works after upgrading to 6.7.0
    Classification: Applications
           Product: krdc
      Version First unspecified
       Reported In:
          Platform: Other
                OS: Linux
            Status: REPORTED
          Severity: normal
          Priority: NOR
         Component: RDP
          Assignee: [email protected]
          Reporter: [email protected]
  Target Milestone: ---

# Bug report for bugs.kde.org

File at: https://bugs.kde.org/enter_bug.cgi?product=KRdp

- **Product:** KRdp
- **Component:** general
- **Version:** 6.7.0
- **Severity:** normal (regression)
- **OS:** Linux (reproducible on any distro; specifics below are Fedora 44)

---

## Summary

`NoNewPrivileges=true`, added to `app-org.kde.krdpserver.service` in commit
`8e1de600a6ea4fcb1662508626da2cf0499f9734` ("Set NoNewPrivileges on the
krdpserver systemd service"), breaks the `SystemUserEnabled=true` ("log in with
your Linux account password") authentication path entirely. Every connection
attempt fails PAM authentication regardless of whether the correct password is
supplied, because the kernel-enforced `no_new_privs` flag prevents the
setuid-root `unix_chkpwd` helper (which `pam_unix` depends on) from escalating
privilege to read `/etc/shadow`.

This is a regression introduced between krdp 6.6.5 and 6.7.0.
`SystemUserEnabled=true` worked correctly on 6.6.5 and has not changed; only
the systemd unit changed.

## Environment

- krdp / krdp-libs: 6.7.0-1.fc44 (regression; 6.6.5-1.fc44 was the last
known-working version)
- plasma-workspace: 6.7.0-1.fc44
- Distro: Fedora 44 (KDE Plasma 6.7.0 spin)
- systemd: 259.6-1.fc44
- pam: 1.7.2-1.fc44
- Desktop session: Wayland, KWin
- `/etc/shadow` permissions: `----------` (mode 000), owner root:root —
standard Fedora default, not customized
- `/usr/bin/unix_chkpwd` permissions: `-rwsr-xr-x root root` — setuid-root,
standard package default, not customized

## Steps to Reproduce

1. Install/upgrade to krdp 6.7.0 (e.g. via a routine `dnf upgrade` that pulls
in plasma-workspace/krdp 6.7.0 together, as happens on Fedora).
2. In `~/.config/krdpserverrc`, set `SystemUserEnabled=true` under `[General]`
(this is the "use my system account password" toggle, settable via the Remote
Desktop KCM or directly).
3. Restart the server: `systemctl --user restart
app-org.kde.krdpserver.service`.
4. From another machine, connect with an RDP client (tested with KDE's RDP
client) using the correct username and the correct, current account password.
5. Observe the connection is rejected during authentication, every time, with
the correct password.

## Observed Result

Every attempt fails identically. Relevant `journalctl --user -u
app-org.kde.krdpserver.service` output for a single connection attempt:

```
New client connected:  Unspecified platform Unspecified version
unix_chkpwd[<pid>]: check pass; user unknown
unix_chkpwd[<pid>]: password check failed for user (james)
krdpserver[<pid>]: pam_unix(login:auth): authentication failure; logname=james
uid=1000 euid=1000 tty= ruser= rhost=  user=james
krdpserver[<pid>]: pam_authenticate failure: Authentication failure
krdpserver[<pid>]: [ERROR][com.freerdp.api] - [rdp_peer_handle_state_active]:
PostConnect for peer 0x... failed
krdpserver[<pid>]: [ERROR][com.freerdp.core.transport] - [transport_check_fds]:
transport_check_fds: transport->ReceiveCallback() - STATE_RUN_FAILED [-1]
```

The `unix_chkpwd: check pass; user unknown` line is notable: the account
(`james`) unquestionably exists (`getent passwd james` resolves it fine; the
same account is simultaneously logged in locally and over SSH at the time of
the failed RDP attempt). This message corresponds to the helper's early-exit
path, not a genuine "no such user" condition.

We confirmed this is reproducible across 5 separate connection attempts
spanning two reboots, with the identical failure signature every time, ruling
out a transient/network issue.

## Expected Result

Connecting with the correct system account username and password should succeed
and authenticate the RDP session, as it did on krdp 6.6.5 under the identical
account/PAM/shadow configuration.

## Root Cause Analysis

1. `app-org.kde.krdpserver.service` runs as a **per-user** systemd unit
(`systemctl --user`), so `krdpserver` always runs as the unprivileged user (UID
1000 here), never as root. (This is necessarily true given krdpserver's job: it
has to run inside the user's own graphical session to capture/control it.)
2. `SystemUserEnabled=true` authenticates via PAM: `src/RdpConnection.cpp`
calls `pam_start("login", ...)` → `pam_authenticate()`.
3. Because the calling process (`krdpserver`) is not root, `pam_unix` cannot
read `/etc/shadow` (mode 000, root-only) directly. Its normal fallback is to
fork/exec `/usr/bin/unix_chkpwd`, a small setuid-root helper whose only purpose
is to perform this one check on behalf of unprivileged callers — this is
standard, expected `pam_unix` behavior on every distro, not a configuration
quirk.
4. `NoNewPrivileges=true` is a kernel-enforced flag
(`prctl(PR_SET_NO_NEW_PRIVS)`) that is inherited by the unit's main process and
**all of its children**, and it unconditionally prevents privilege gain via
`execve()` — including via setuid/setgid bits — for the lifetime of that
process tree. This is explicitly what the directive is documented to do (and is
quoted, accurately, in the commit message that added it).
5. So when `krdpserver` execs `unix_chkpwd`, the kernel refuses to honor the
setuid-root bit. The helper runs as plain UID 1000, can't open `/etc/shadow`,
and fails — before ever reaching the actual password comparison. This produces
the generic, deliberately non-specific denial message (`check pass; user
unknown`) that `unix_chkpwd`/`pam_unix` use to avoid distinguishing "no such
user" from "couldn't check" to an unprivileged caller.

We verified this mechanism by diffing the unit file directly between releases:

- `v6.6.5`:
https://github.com/KDE/krdp/blob/v6.6.5/server/app-org.kde.krdpserver.service.in
— no `NoNewPrivileges` line.
- `v6.7.0`:
https://github.com/KDE/krdp/blob/v6.7.0/server/app-org.kde.krdpserver.service.in
— adds `NoNewPrivileges=true`.
- Introduced by commit
https://github.com/KDE/krdp/commit/8e1de600a6ea4fcb1662508626da2cf0499f9734
(2026-05-15, no associated BUG: tag), whose own message states the goal is
specifically to block setuid-based privilege escalation (citing `su`/"Copy
Fail"-style exploit chains as the motivating threat) in case of a future
FreeRDP RCE.

The hardening goal is legitimate and well-reasoned on its own; it simply
doesn't appear to have been checked against the `SystemUserEnabled=true` code
path, which depends on exactly the privilege-escalation mechanism it now
blocks.

### Isolated mechanism test (confirmed)

To isolate the `NoNewPrivileges` mechanism from krdp itself (no krdp process,
no real account password, no config changes), we invoked the `unix_chkpwd`
helper directly, twice, with an intentionally wrong test password, with and
without `no_new_privs` applied via `setpriv`:

```
$ printf 'isolatedtestbadpassword\0' | /usr/bin/unix_chkpwd james nonull
exit code: 7   (PAM_AUTH_ERR — "Authentication failure")
journal:   unix_chkpwd[6350]: password check failed for user (james)

$ printf 'isolatedtestbadpassword\0' | /usr/bin/setpriv --no-new-privs --
/usr/bin/unix_chkpwd james nonull
exit code: 9   (PAM_AUTHINFO_UNAVAIL — "Underlying authentication service can
not retrieve authentication information")
journal:   unix_chkpwd[6404]: check pass; user unknown
           unix_chkpwd[6404]: password check failed for user (james)
```

The `setpriv --no-new-privs` run reproduces the exact journal signature seen in
the live krdp failure (`check pass; user unknown`), and the PAM return code
changes from a genuine "wrong password" result (`PAM_AUTH_ERR`) to "couldn't
even check" (`PAM_AUTHINFO_UNAVAIL`) — with the only variable changed being the
`no_new_privs` flag, nothing krdp- or config-related. This confirms the
mechanism in isolation, independent of krdp's own code or our local
`SystemUserEnabled` setting.

### Confirmed local workaround

Adding a systemd user-unit drop-in overriding the directive restores the
pre-6.7.0 behavior:

```ini
# ~/.config/systemd/user/app-org.kde.krdpserver.service.d/override.conf
[Service]
NoNewPrivileges=false
```
followed by `systemctl --user daemon-reload && systemctl --user restart
app-org.kde.krdpserver.service`.

This is offered as supporting evidence of the mechanism, not as a request to
simply revert the hardening — see "Why this matters" below for why we think
this needs an upstream decision rather than a per-user workaround.

## Why this isn't just "use the other auth mode instead"

KRdp's other authentication mode — a dedicated RDP-only username/password,
stored via QtKeychain (which in practice resolves to KWallet/`ksecretd` on most
KDE systems) — is not a substitute for `SystemUserEnabled=true` on a
headless/unattended/autologin machine, for a structural reason:

- KWallet requires an interactive GUI prompt to unlock a locked wallet. There
is no `org.kde.KWallet` D-Bus method that accepts a password to unlock
non-interactively — by design.
- On a machine configured for unattended startup (no password typed at boot —
e.g. SDDM/plasmalogin autologin), nothing ever supplies that unlock, so the
wallet stays locked after every reboot.
- Since RDP is the remote-GUI-access method being relied on, and the wallet can
only be unlocked via a prompt rendered on the graphical session, this is
circular: you cannot unlock the wallet without GUI access, and you cannot get
GUI access (via RDP) without the wallet already unlocked. Only physical
presence at the console breaks the loop.
- We also confirmed current KWallet (`kwalletwizardpage*.ui` in the KDE/kwallet
repo) offers no "no encryption / no prompt" wallet option — only
Blowfish-with-password or GPG-key-backed wallets — so there is no supported way
to configure the credential-store path to avoid this either.

So as of 6.7.0, there is no KRdp configuration — system-user or
RDP-only-credential — that supports "reboot the machine remotely, then
reconnect via RDP with nobody physically present," which we believe was a
supported, intentional use case prior to this regression (it's explicitly
accommodated by KRdp's own README, which documents SDDM autologin specifically
for this purpose).

## Suggested directions for a fix (for maintainers to evaluate, not
prescriptive)

- Don't apply `NoNewPrivileges=true` when `SystemUserEnabled=true` is in effect
(runtime-conditional sandboxing), if that's mechanically feasible given the
directive is normally fixed at unit-definition time.
- Or, move the privileged PAM check out of the sandboxed `krdpserver` process
entirely — e.g. a small separate root-owned helper/D-Bus service that performs
the `pam_authenticate()` call on krdpserver's behalf, so the hardening on the
main (network-facing, untrusted-input-parsing) process can stay fully intact.
- At minimum, document the incompatibility so users relying on
`SystemUserEnabled=true` for unattended access know this combination is
currently non-functional, rather than discovering it via silent,
mysteristically-worded authentication failures.

## Additional notes

- This is reproducible purely from the unit file change and standard
Linux/systemd/PAM semantics (`/etc/shadow` mode 000, setuid `unix_chkpwd`,
`NoNewPrivileges` semantics per `systemd.exec(5)`:
https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#NoNewPrivileges=)
— we'd expect it to reproduce identically on any distro, not just Fedora.
- The isolated `unix_chkpwd`/`setpriv` test above (separate from the full local
workaround, which bundles `SystemUserEnabled=true` together with the systemd
override) confirms the mechanism on its own; raw output available on request.
- Full `journalctl --user -u app-org.kde.krdpserver.service` output and `dnf
history` showing the exact 6.6.5→6.7.0 transition timestamp are available on
request.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to