ironrdp_pdu/rdp/session_info/
logon_info.rs

1use ironrdp_core::{
2    cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, read_padding, write_padding, Decode,
3    DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor,
4};
5
6use crate::utils;
7
8const DOMAIN_NAME_SIZE_FIELD_SIZE: usize = 4;
9const DOMAIN_NAME_SIZE_V1: usize = 52;
10const USER_NAME_SIZE_FIELD_SIZE: usize = 4;
11const USER_NAME_SIZE_V1: usize = 512;
12const ID_SESSION_SIZE: usize = 4;
13
14const SAVE_SESSION_PDU_VERSION_ONE: u16 = 0x0001;
15const LOGON_INFO_V2_SIZE: usize = 18;
16const LOGON_INFO_V2_PADDING_SIZE: usize = 558;
17const DOMAIN_NAME_SIZE_V2: usize = 52;
18const USER_NAME_SIZE_V2: usize = 512;
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct LogonInfoVersion1 {
22    pub logon_info: LogonInfo,
23}
24
25impl LogonInfoVersion1 {
26    const NAME: &'static str = "LogonInfoVersion1";
27
28    const FIXED_PART_SIZE: usize = DOMAIN_NAME_SIZE_FIELD_SIZE
29        + DOMAIN_NAME_SIZE_V1
30        + USER_NAME_SIZE_FIELD_SIZE
31        + USER_NAME_SIZE_V1
32        + ID_SESSION_SIZE;
33}
34
35impl Encode for LogonInfoVersion1 {
36    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
37        ensure_size!(in: dst, size: self.size());
38
39        let mut domain_name_buffer = utils::to_utf16_bytes(self.logon_info.domain_name.as_ref());
40        domain_name_buffer.resize(DOMAIN_NAME_SIZE_V1 - 2, 0);
41        let mut user_name_buffer = utils::to_utf16_bytes(self.logon_info.user_name.as_ref());
42        user_name_buffer.resize(USER_NAME_SIZE_V1 - 2, 0);
43
44        dst.write_u32(cast_length!(
45            "domainNameSize",
46            (self.logon_info.domain_name.len() + 1) * 2
47        )?);
48        dst.write_slice(domain_name_buffer.as_ref());
49        dst.write_u16(0); // UTF-16 null terminator
50        dst.write_u32(cast_length!("userNameSize", (self.logon_info.user_name.len() + 1) * 2)?);
51        dst.write_slice(user_name_buffer.as_ref());
52        dst.write_u16(0); // UTF-16 null terminator
53        dst.write_u32(self.logon_info.session_id);
54
55        Ok(())
56    }
57
58    fn name(&self) -> &'static str {
59        Self::NAME
60    }
61
62    fn size(&self) -> usize {
63        Self::FIXED_PART_SIZE
64    }
65}
66
67impl<'de> Decode<'de> for LogonInfoVersion1 {
68    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
69        ensure_fixed_part_size!(in: src);
70
71        let domain_name_size: usize = cast_length!("domainNameSize", src.read_u32())?;
72        if domain_name_size > DOMAIN_NAME_SIZE_V1 {
73            return Err(invalid_field_err!("domainNameSize", "invalid domain name size"));
74        }
75
76        let domain_name =
77            utils::decode_string(src.read_slice(DOMAIN_NAME_SIZE_V1), utils::CharacterSet::Unicode, false)?;
78
79        let user_name_size: usize = cast_length!("userNameSize", src.read_u32())?;
80        if user_name_size > USER_NAME_SIZE_V1 {
81            return Err(invalid_field_err!("userNameSize", "invalid user name size"));
82        }
83
84        let user_name = utils::decode_string(src.read_slice(USER_NAME_SIZE_V1), utils::CharacterSet::Unicode, false)?;
85
86        let session_id = src.read_u32();
87
88        Ok(Self {
89            logon_info: LogonInfo {
90                session_id,
91                domain_name,
92                user_name,
93            },
94        })
95    }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
99pub struct LogonInfoVersion2 {
100    pub logon_info: LogonInfo,
101}
102
103impl LogonInfoVersion2 {
104    const NAME: &'static str = "LogonInfoVersion2";
105
106    const FIXED_PART_SIZE: usize = LOGON_INFO_V2_SIZE + LOGON_INFO_V2_PADDING_SIZE;
107}
108
109impl Encode for LogonInfoVersion2 {
110    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
111        ensure_size!(in: dst, size: self.size());
112
113        dst.write_u16(SAVE_SESSION_PDU_VERSION_ONE);
114        dst.write_u32(LOGON_INFO_V2_SIZE as u32);
115        dst.write_u32(self.logon_info.session_id);
116        dst.write_u32(cast_length!(
117            "domainNameSize",
118            (self.logon_info.domain_name.len() + 1) * 2
119        )?);
120        dst.write_u32(cast_length!("userNameSize", (self.logon_info.user_name.len() + 1) * 2)?);
121        write_padding!(dst, LOGON_INFO_V2_PADDING_SIZE);
122
123        utils::write_string_to_cursor(
124            dst,
125            self.logon_info.domain_name.as_ref(),
126            utils::CharacterSet::Unicode,
127            true,
128        )?;
129        utils::write_string_to_cursor(
130            dst,
131            self.logon_info.user_name.as_ref(),
132            utils::CharacterSet::Unicode,
133            true,
134        )?;
135
136        Ok(())
137    }
138
139    fn name(&self) -> &'static str {
140        Self::NAME
141    }
142
143    fn size(&self) -> usize {
144        Self::FIXED_PART_SIZE + (self.logon_info.domain_name.len() + 1) * 2 + (self.logon_info.user_name.len() + 1) * 2
145    }
146}
147
148impl<'de> Decode<'de> for LogonInfoVersion2 {
149    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
150        ensure_fixed_part_size!(in: src);
151
152        let version = src.read_u16();
153        if version != SAVE_SESSION_PDU_VERSION_ONE {
154            return Err(invalid_field_err!("version", "invalid logon version 2"));
155        }
156
157        let size: usize = cast_length!("LogonInfoSize", src.read_u32())?;
158        if size != LOGON_INFO_V2_SIZE {
159            return Err(invalid_field_err!("domainNameSize", "invalid logon info size"));
160        }
161
162        let session_id = src.read_u32();
163        let domain_name_size: usize = cast_length!("domainNameSize", src.read_u32())?;
164        if domain_name_size > DOMAIN_NAME_SIZE_V2 {
165            return Err(invalid_field_err!("domainNameSize", "invalid domain name size"));
166        }
167
168        let user_name_size: usize = cast_length!("userNameSize", src.read_u32())?;
169        if user_name_size > USER_NAME_SIZE_V2 {
170            return Err(invalid_field_err!("userNameSize", "invalid user name size"));
171        }
172
173        read_padding!(src, LOGON_INFO_V2_PADDING_SIZE);
174
175        ensure_size!(in: src, size: domain_name_size);
176        let domain_name = utils::decode_string(src.read_slice(domain_name_size), utils::CharacterSet::Unicode, false)?;
177
178        ensure_size!(in: src, size: user_name_size);
179        let user_name = utils::decode_string(src.read_slice(user_name_size), utils::CharacterSet::Unicode, false)?;
180
181        Ok(Self {
182            logon_info: LogonInfo {
183                session_id,
184                domain_name,
185                user_name,
186            },
187        })
188    }
189}
190
191#[derive(Debug, Clone, PartialEq, Eq)]
192pub struct LogonInfo {
193    pub session_id: u32,
194    pub user_name: String,
195    pub domain_name: String,
196}