Skip to content

fix(rdpdr): defer User Logged On that arrives before Client ID Confirm - #2079

Open
AKolenda wants to merge 2 commits into
Devolutions:masterfrom
AKolenda:fix/rdpdr-logon-before-client-id
Open

AKolenda wants to merge 2 commits into
Devolutions:masterfrom
AKolenda:fix/rdpdr-logon-before-client-id

Conversation

@AKolenda

@AKolenda AKolenda commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

After the user logs in, Windows re-announces the RDPDR channel and sends Server User Logged On before the new Server Core Capability Request and Server Client ID Confirm. Rdpdr treated that order as a protocol error, so a connection to a Windows 11 host ended a few seconds after login with:

PDU error: [ironrdp_rdpdr::Rdpdr::handle_user_logged_on] other (received RDPDR user logged on before client ID confirmation)

Observed sequence (ironrdp_rdpdr=debug):

ServerAnnounceRequest   client_id: 3
ServerCoreCapabilityRequest
ServerClientIdConfirm   client_id: 3
ServerAnnounceRequest   client_id: 2
UserLoggedon                          <- previously fatal
ServerCoreCapabilityRequest
ServerClientIdConfirm   client_id: 2

Rdpdr now remembers a User Logged On that arrives before the Client ID Confirm and handles it right after the confirmation, so the backend's handle_user_logged_on and the post-logon device announcement still run once the client ID is known. A new Server Announce clears the pending flag. FreeRDP accepts the same order: its state check allows PAKID_CORE_USER_LOGGEDON before PAKID_CORE_CLIENTID_CONFIRM.

Validation

  • New unit test user_logged_on_before_client_id_confirm_is_deferred: an early User Logged On yields no messages, the following Client ID Confirm announces the configured drive, and drive IRPs are dispatched afterwards.
  • cargo test -p ironrdp-rdpdr, cargo clippy -p ironrdp-rdpdr --all-targets -- -D warnings, cargo fmt --check.
  • Live run against the Windows 11 host that produced the error: the session stays connected past login, and the log shows the deferred logon being handled after the confirmation.

Copilot AI lite review requested due to automatic review settings October 6, 2026 04:52
@AKolenda
AKolenda deployed to llm-providers October 6, 2026 04:52 — with GitHub Actions Active
@github-actions github-actions Bot added automation-failed Exact-head automated classification or review failed or was unavailable risk/unknown Risk could not be determined automatically; needs maintainer-level scrutiny scope/core Touches the core architectural tier size/S Size: up to 199 counted lines and 5 files; exceeds XS in either measure labels Oct 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Add coverage for the backend callback and stale-state reset path.

Review effort: Lite
Findings: 1 Low severity

Open (1)
What changed in this PR

Fixes RDPDR handling when User Logged On arrives before Client ID Confirm.

Changes:

  • Defers early logon notifications until client confirmation.
  • Resets pending state on new server announcements.
  • Adds regression coverage for deferred device handling.
File Summary
crates/​ironrdp-rdpdr/​src/​lib.rs Implements deferred logon handling and tests the sequence.

self.client_id_confirmed = true;
self.post_logon_devices_announced = announce_all_devices;
if core::mem::take(&mut self.user_logged_on_pending) {
messages.extend(self.handle_user_logged_on()?);

@AKolenda AKolenda Oct 6, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in bc64c6a: TrackingBackend now counts handle_user_logged_on calls. The test asserts zero calls while the logon is deferred and exactly one after Client ID Confirm. A second test, server_announce_discards_a_deferred_user_logged_on, checks that a new Server Announce drops a pending logon (no backend call, no post-logon announcement). I checked that each test fails if the corresponding line in the fix is removed.

@AKolenda
AKolenda deployed to llm-providers October 6, 2026 06:06 — with GitHub Actions Active
@CBenoit

Benoît Cortier (CBenoit) commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

PR automation is failing because of a picky-krb 0.12.5 incompatibility, fixed on master by #2074. Please rebase on master to fix it.

Update: no rebase needed anymore. picky-krb 0.12.5 was yanked from crates.io (re-released as 0.13.0), so the API check builds again without changes to this branch. PR automation has been re-run here and passes.

@github-actions github-actions Bot added kind/protocol Affects RDP or related protocol behavior risk/medium Behavioral change that does not substantially alter a core public API and removed risk/unknown Risk could not be determined automatically; needs maintainer-level scrutiny automation-failed Exact-head automated classification or review failed or was unavailable labels Oct 7, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #2079 defers an RDPDR Server User Logged On that arrives before Server Client ID Confirm (as Windows 11 sends after re-announcing the channel) instead of terminating the connection with a PDU error. Verified in pr-head: a single user_logged_on_pending flag is set in handle_user_logged_on when client_id_confirmed is false, cleared on Server Announce in lockstep with the other sequence state, and consumed once via core::mem::take at the end of handle_client_id_confirm, so the backend callback and post-logon device announcements still run after confirmation. This matches MS-RDPEFS sequencing and the FreeRDP-precedent claim. Two new tests cover deferral and discard-on-reannounce, and the implementation is minimal with no compression concerns. The only valid specialist finding is a low-severity state/message coupling: within handle_client_id_confirm, device announcements are registered in pending_device_announcements and their messages built before the deferred backend handle_user_logge…

Comment on lines +444 to +446
if core::mem::take(&mut self.user_logged_on_pending) {
messages.extend(self.handle_user_logged_on()?);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[skeptical] Backend error during deferred User Logged On drops already-built device announcements — low 🟡 — The deferral folds User Logged On handling into the Client ID Confirm process() call. By the time the flushed backend.handle_user_logged_on runs, announce_devices has already registered the device IDs in pending_device_announcements and built the ClientDeviceListAnnounce messages. If the backend callback returns Err, the error propagates out of process() and the built messages are never returned to the transport, leaving pending_device_announcements stale relative to what the server actually received; the affected devices are never activated and re-announcing them later trips the announced-more-than-once check. Not a regression for the misordered flow (it was previously fatal) and requires a backend error at exactly this point; detaching the announcement messages before the flush or registering them only after the deferred call would avoid the coupling.

@github-actions github-actions Bot added ai-reviewed/1 One automated review completed needs-author-action The pull request author is the current next actor labels Oct 7, 2026
AKolenda added 2 commits October 9, 2026 23:16
After logon, Windows re-announces the RDPDR channel and sends User Logged
On before the new capability request and Client ID Confirm. Treating that
as a protocol error ended the whole session a few seconds after login.
Remember the logon and handle it once the client ID is confirmed, as
FreeRDP tolerates the same order.
Count RdpdrBackend::handle_user_logged_on calls in the tracking backend:
the deferred logon must invoke it exactly once after Client ID Confirm,
and a new Server Announce must discard a logon that is still pending.
@AKolenda
AKolenda force-pushed the fix/rdpdr-logon-before-client-id branch from bc64c6a to 20c88f6 Compare October 10, 2026 05:17
@AKolenda
AKolenda deployed to llm-providers October 10, 2026 05:18 — with GitHub Actions Active
@github-actions github-actions Bot removed the needs-author-action The pull request author is the current next actor label Oct 10, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 PR #2079 is a minimal, well-scoped fix in crates/ironrdp-rdpdr/src/lib.rs: a Server User Logged On PDU arriving before Server Client ID Confirm no longer terminates the connection. Instead, a single user_logged_on_pending bool records the event; handle_client_id_confirm replays it via core::mem::take after setting client_id_confirmed = true, so the backend's handle_user_logged_on and post-logon device announcements run exactly once with the same semantics as the non-deferred path (the RDP51 post_logon_devices_announced guard in announce_post_logon_devices behaves identically). Resetting the flag in handle_server_announce is consistent with the existing full channel reset there, and Windows re-sends UserLoggedon per re-announce cycle, so no event is lost; repeated early PDUs coalesce harmlessly since the PDU is payload-free. All modified lines verified in pr-head: the field/init/reset, the deferred branch in handle_user_logged_on, the replay block in handle_client_id_confirm, and two t…

@github-actions github-actions Bot added ai-reviewed/2 Two automated reviews completed needs-review A human reviewer is the current next actor and removed ai-reviewed/1 One automated review completed labels Oct 10, 2026

This branch was successfully deployed

1 active deployment
llm-providers — 20c88f65 Deployed Oct 10, 2026 by AKolenda via Classify pull request #2405
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-reviewed/2 Two automated reviews completed kind/protocol Affects RDP or related protocol behavior needs-review A human reviewer is the current next actor risk/medium Behavioral change that does not substantially alter a core public API scope/core Touches the core architectural tier size/S Size: up to 199 counted lines and 5 files; exceeds XS in either measure

Development

Successfully merging this pull request may close these issues.

3 participants