ironrdp_pdu/rdp/session_info/
logon_info.rs1use 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); 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); 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}