Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions crates/ironrdp-pdu/src/rdp/client_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,13 @@ impl<'de> Decode<'de> for ClientInfo {
let code_page = src.read_u32();
let flags_with_compression_type = src.read_u32();

let flags = ClientInfoFlags::from_bits(flags_with_compression_type & !COMPRESSION_TYPE_MASK)
.ok_or_else(|| invalid_field_err!("flags", "invalid ClientInfoFlags", in: src))?;
// [MS-RDPBCGR] 3.3.5.3.11 asks the server to validate lengths and to
// test the UNICODE flag, never to validate this bit set, and the
// INFO_* list has grown several times; refusing unknown bits would
// reject newer clients at the login step. Retain them instead
// (crate-wide policy since #1144) so re-encoding preserves the wire
// value.
let flags = ClientInfoFlags::from_bits_retain(flags_with_compression_type & !COMPRESSION_TYPE_MASK);
let compression_type = CompressionType::from_u32((flags_with_compression_type & COMPRESSION_TYPE_MASK) >> 9)
.ok_or_else(|| invalid_field_err!("flags", "invalid CompressionType", in: src))?;

Expand Down
21 changes: 21 additions & 0 deletions crates/ironrdp-testsuite-core/tests/pdu/rdp.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use ironrdp_core::{Encode as _, decode, encode_vec};
use ironrdp_pdu::rdp::client_info::ClientInfo;
use ironrdp_testsuite_core::capsets::*;
use ironrdp_testsuite_core::client_info::*;
use ironrdp_testsuite_core::rdp::*;
Expand Down Expand Up @@ -308,6 +309,26 @@ fn from_buffer_correct_parses_client_info_pdu_unicode() {
);
}

#[test]
fn client_info_with_undefined_flag_bits_decodes_and_retains_them() {
// [MS-RDPBCGR] 2.2.1.11.1.1's INFO_* list keeps growing; a client setting
// a bit this library does not know yet must not be refused at the login
// step (3.3.5.3.11 mandates no validation of this field). The unknown bit
// is retained rather than dropped so re-encoding preserves the wire value.
const UNDEFINED_BIT: u32 = 0x0000_0004; // undefined in 2.2.1.11.1.1

let mut buffer = CLIENT_INFO_BUFFER_UNICODE.to_vec();
let flags = u32::from_le_bytes(buffer[4..8].try_into().unwrap()) | UNDEFINED_BIT;
buffer[4..8].copy_from_slice(&flags.to_le_bytes());

let client_info: ClientInfo = decode(buffer.as_slice()).expect("undefined INFO bits are not fatal");

assert_ne!(client_info.flags.bits() & UNDEFINED_BIT, 0);

let reencoded = encode_vec(&client_info).unwrap();
assert_eq!(reencoded, buffer);
}

#[test]
fn from_buffer_correct_parses_client_info_pdu_unicode_without_optional_fields() {
assert_eq!(
Expand Down
Loading