1use crate::{
2 GROUP_MEMBERSHIP, KERB_SID_AND_ATTRIBUTES, PISID, USER_SESSION_KEY,
3};
4use ms_dtyp::{FILETIME, RPC_UNICODE_STRING, ULONG, USHORT};
5
6pub const NOT_EXPIRE_TIME: u64 = 0x7fffffffffffffff;
7pub const NOT_SET_TIME: u64 = 0x0;
8
9const RPC_LITTLE_ENDIAN: u8 = 0x10;
10const RPC_VERSION: u8 = 0x1;
11
12macro_rules! align {
13 ($data:expr, $alignment:expr) => {
14 while ($data.len() % $alignment) != 0 {
15 $data.push(00);
16 }
17 };
18}
19
20macro_rules! build_le_bytes {
21 ($data:expr, $int:expr) => {{
22 let mut bs = $int.to_le_bytes().to_vec();
23 align!($data, bs.len());
24 $data.append(&mut bs);
25 }};
26}
27
28macro_rules! build_filetime {
29 ($data:expr, $time:expr) => {
30 build_le_bytes!($data, $time.dwLowDateTime);
31 build_le_bytes!($data, $time.dwHighDateTime);
32 };
33}
34
35macro_rules! build_unistr {
36 ($data:expr, $str:expr, $ref_data:expr, $ref_id:expr) => {
37 build_le_bytes!($data, $str.Length);
38 build_le_bytes!($data, $str.MaximumLength);
39 $ref_id += 1;
40 build_le_bytes!($data, $ref_id);
41
42 build_le_bytes!($ref_data, ($str.Length / 2) as u32);
43 build_le_bytes!($ref_data, 0u32);
44 build_le_bytes!($ref_data, ($str.Length / 2) as u32);
45 $ref_data.append(&mut u16_array_to_le_bytes(&$str.Buffer));
46 };
47}
48
49macro_rules! build_group_membership {
50 ($data:expr, $group:expr) => {
51 build_le_bytes!($data, $group.RelativeId);
52 build_le_bytes!($data, $group.Attributes);
53 };
54}
55
56macro_rules! build_sid {
57 ($data:expr, $sid:expr) => {
58 build_le_bytes!($data, $sid.SubAuthority.len() as u32);
59 $data.push($sid.Revision);
60 $data.push($sid.SubAuthority.len() as u8);
61 $data.append(&mut $sid.IdentifierAuthority.to_vec());
62 for sub_auth in $sid.SubAuthority.iter() {
63 $data.append(&mut sub_auth.to_le_bytes().to_vec());
64 }
65 };
66}
67
68#[derive(Clone, Debug, PartialEq, Default)]
108pub struct KERB_VALIDATION_INFO {
109 pub LogonTime: FILETIME,
110 pub LogoffTime: FILETIME,
111 pub KickOffTime: FILETIME,
112 pub PasswordLastSet: FILETIME,
113 pub PasswordCanChange: FILETIME,
114 pub PasswordMustChange: FILETIME,
115 pub EfectiveName: RPC_UNICODE_STRING,
116 pub FullName: RPC_UNICODE_STRING,
117 pub LogonScript: RPC_UNICODE_STRING,
118 pub ProfilePath: RPC_UNICODE_STRING,
119 pub HomeDirectory: RPC_UNICODE_STRING,
120 pub HomeDirectoryDrive: RPC_UNICODE_STRING,
121 pub LogonCount: USHORT,
122 pub BadPasswordCount: USHORT,
123 pub UserId: ULONG,
124 pub PrimaryGroupId: ULONG,
125 pub GroupIds: Vec<GROUP_MEMBERSHIP>,
126 pub UserFlags: ULONG,
127 pub UserSessionKey: USER_SESSION_KEY,
128 pub LogonServer: RPC_UNICODE_STRING,
129 pub LogonDomainName: RPC_UNICODE_STRING,
130 pub LogonDomainId: PISID,
131 pub Reserved1: [u8; 8],
132 pub UserAccountControl: ULONG,
133 pub SubAuthStatus: ULONG,
134 pub LastSuccessfulILogon: FILETIME,
135 pub LastFailedILogon: FILETIME,
136 pub FailedILogonCount: ULONG,
137 pub Reserved3: ULONG,
138 pub ExtraSids: Vec<KERB_SID_AND_ATTRIBUTES>,
139 pub ResourceGroupDomainSid: Option<PISID>,
140 pub ResourceGroupIds: Vec<GROUP_MEMBERSHIP>,
141}
142
143impl KERB_VALIDATION_INFO {
144
145 pub fn build_data(&self) -> Vec<u8> {
148 let mut ref_id: u32 = 0x1110;
149 let mut data = Vec::new();
150 let mut ref_data = Vec::new();
151
152 build_filetime!(data, self.LogonTime);
153 build_filetime!(data, self.LogoffTime);
154 build_filetime!(data, self.KickOffTime);
155 build_filetime!(data, self.PasswordLastSet);
156 build_filetime!(data, self.PasswordCanChange);
157 build_filetime!(data, self.PasswordMustChange);
158
159 build_unistr!(data, self.EfectiveName, ref_data, ref_id);
160 build_unistr!(data, self.FullName, ref_data, ref_id);
161 build_unistr!(data, self.LogonScript, ref_data, ref_id);
162 build_unistr!(data, self.ProfilePath, ref_data, ref_id);
163 build_unistr!(data, self.HomeDirectory, ref_data, ref_id);
164 build_unistr!(data, self.HomeDirectoryDrive, ref_data, ref_id);
165
166 build_le_bytes!(data, self.LogonCount);
167 build_le_bytes!(data, self.BadPasswordCount);
168 build_le_bytes!(data, self.UserId);
169 build_le_bytes!(data, self.PrimaryGroupId);
170
171 let GroupCount = self.GroupIds.len() as ULONG;
172 build_le_bytes!(data, GroupCount);
173 ref_id += 1;
174 build_le_bytes!(data, ref_id);
175 build_le_bytes!(ref_data, GroupCount);
176 for group_id in self.GroupIds.iter() {
177 build_group_membership!(ref_data, group_id);
178 }
179
180 build_le_bytes!(data, self.UserFlags);
181 data.append(&mut self.UserSessionKey.to_bytes().to_vec());
182
183 build_unistr!(data, self.LogonServer, ref_data, ref_id);
184 build_unistr!(data, self.LogonDomainName, ref_data, ref_id);
185
186 ref_id += 1;
187 build_le_bytes!(data, ref_id);
188 build_sid!(ref_data, self.LogonDomainId);
189
190 data.append(&mut self.Reserved1.to_vec());
191 build_le_bytes!(data, self.UserAccountControl);
192 build_le_bytes!(data, self.SubAuthStatus);
193
194 build_filetime!(data, self.LastSuccessfulILogon);
195 build_filetime!(data, self.LastFailedILogon);
196 build_le_bytes!(data, self.FailedILogonCount);
197 build_le_bytes!(data, self.Reserved3);
198
199 let SidCount = self.ExtraSids.len() as ULONG;
200 build_le_bytes!(data, SidCount);
201
202 if SidCount != 0 {
203 ref_id += 1;
204 build_le_bytes!(data, ref_id);
205 build_le_bytes!(ref_data, SidCount);
206 for kerb_sid in self.ExtraSids.iter() {
207 build_sid!(ref_data, kerb_sid.Sid);
208 build_le_bytes!(ref_data, kerb_sid.Attributes);
209 }
210 } else {
211 build_le_bytes!(data, 0u32);
212 }
213
214 if let Some(ResourceGroupDomainSid) = &self.ResourceGroupDomainSid {
215 ref_id += 1;
216 build_le_bytes!(data, ref_id);
217 build_sid!(ref_data, ResourceGroupDomainSid);
218 } else {
219 build_le_bytes!(data, 0u32);
220 }
221
222 let ResourceGroupDomainCount = self.ResourceGroupIds.len() as ULONG;
223 build_le_bytes!(data, ResourceGroupDomainCount);
224 if ResourceGroupDomainCount != 0 {
225 ref_id += 1;
226 build_le_bytes!(data, ref_id);
227 build_le_bytes!(ref_data, ResourceGroupDomainCount);
228
229 for group_id in self.ResourceGroupIds.iter() {
230 build_group_membership!(ref_data, group_id);
231 }
232 } else {
233 build_le_bytes!(data, 0u32);
234 }
235
236 data.append(&mut ref_data);
237 return data;
238 }
239
240 fn build_rpc_header(&self, body_length: u32) -> Vec<u8> {
243 let header_len: u16 = 0x8;
244 let filler: &[u8] = &[0xcc, 0xcc, 0xcc, 0xcc];
245 let mut data = Vec::new();
246 data.push(RPC_VERSION);
247 data.push(RPC_LITTLE_ENDIAN);
248 data.append(&mut header_len.to_le_bytes().to_vec());
249 data.append(&mut filler.to_vec());
250
251 let body_plus_ref_size = body_length + 4;
252 data.append(&mut body_plus_ref_size.to_le_bytes().to_vec());
253 data.append(&mut filler.to_vec());
254
255 return data;
256 }
257
258 pub fn build(&self) -> Vec<u8> {
259 let ref_id: u32 = 0x2122;
260 let mut body_data = self.build_data();
261 let mut data = self.build_rpc_header(body_data.len() as u32);
262
263
264 data.append(&mut ref_id.to_le_bytes().to_vec());
265 data.append(&mut body_data);
266
267 return data;
268 }
269}
270
271fn u16_array_to_le_bytes(u16_array: &[u16]) -> Vec<u8> {
272 let mut u8_vec: Vec<u8> = Vec::with_capacity(u16_array.len() * 2);
273
274 for u16_item in u16_array.iter() {
275 let u8_min = *u16_item as u8;
276 let u8_max = (*u16_item >> 8) as u8;
277
278 u8_vec.push(u8_min);
279 u8_vec.push(u8_max);
280 }
281
282 return u8_vec;
283}
284
285#[cfg(test)]
286mod test {
287 use super::*;
288 use ms_samr::{
289 SE_GROUP_ENABLED, SE_GROUP_ENABLED_BY_DEFAULT, SE_GROUP_MANDATORY,
290 USER_DONT_EXPIRE_PASSWORD, USER_NORMAL_ACCOUNT,
291 };
292
293 use std::convert::TryFrom;
294
295 #[test]
296 fn test_build_kerb_validation_info() {
297 let timestamp = 0x01d63110a3b82380u64;
298 let name = "stegosaurus";
299 let user_id = 500;
300 let groups = vec![513, 512, 520, 518, 519];
301 let domain = "jurassic.park";
302 let domain_sid = "S-1-5-21-1339291983-1349129144-367733775";
303 let mut kvi = KERB_VALIDATION_INFO::default();
304
305 kvi.LogonTime = timestamp.into();
306 kvi.LogoffTime = NOT_EXPIRE_TIME.into();
307 kvi.KickOffTime = NOT_EXPIRE_TIME.into();
308 kvi.PasswordLastSet = timestamp.into();
309 kvi.PasswordCanChange = 0.into();
310 kvi.PasswordMustChange = NOT_EXPIRE_TIME.into();
311 kvi.EfectiveName = name.into();
312 kvi.LogonCount = 500;
313 kvi.BadPasswordCount = 0;
314 kvi.UserId = user_id;
315 kvi.PrimaryGroupId = 513;
316
317 for group_id in groups.iter() {
318 kvi.GroupIds.push(GROUP_MEMBERSHIP::new(
319 *group_id,
320 SE_GROUP_MANDATORY
321 | SE_GROUP_ENABLED
322 | SE_GROUP_ENABLED_BY_DEFAULT,
323 ));
324 }
325
326 kvi.LogonDomainName = domain.to_uppercase().as_str().into();
327 kvi.LogonDomainId = PISID::try_from(domain_sid).unwrap();
328 kvi.UserAccountControl =
329 USER_NORMAL_ACCOUNT | USER_DONT_EXPIRE_PASSWORD;
330
331 let raw = vec![
332 0x01, 0x10, 0x08, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xB8, 0x01, 0x00,
333 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x22, 0x21, 0x00, 0x00, 0x80, 0x23,
334 0xB8, 0xA3, 0x10, 0x31, 0xD6, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
335 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F,
336 0x80, 0x23, 0xB8, 0xA3, 0x10, 0x31, 0xD6, 0x01, 0x00, 0x00, 0x00,
337 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
338 0xFF, 0x7F, 0x16, 0x00, 0x16, 0x00, 0x11, 0x11, 0x00, 0x00, 0x00,
339 0x00, 0x00, 0x00, 0x12, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
340 0x13, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x11, 0x00,
341 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x11, 0x00, 0x00, 0x00, 0x00,
342 0x00, 0x00, 0x16, 0x11, 0x00, 0x00, 0xF4, 0x01, 0x00, 0x00, 0xF4,
343 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
344 0x17, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
345 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
346 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x11, 0x00, 0x00, 0x1A,
347 0x00, 0x1A, 0x00, 0x19, 0x11, 0x00, 0x00, 0x1A, 0x11, 0x00, 0x00,
348 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00,
349 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
350 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
351 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
352 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
353 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00,
354 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65,
355 0x00, 0x67, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x61, 0x00, 0x75, 0x00,
356 0x72, 0x00, 0x75, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
357 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
358 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
359 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
360 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
361 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
362 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x07,
363 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
364 0x08, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00,
365 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00, 0x07, 0x00,
366 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
367 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
368 0x0D, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x55, 0x00, 0x52, 0x00, 0x41,
369 0x00, 0x53, 0x00, 0x53, 0x00, 0x49, 0x00, 0x43, 0x00, 0x2E, 0x00,
370 0x50, 0x00, 0x41, 0x00, 0x52, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x04,
371 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
372 0x15, 0x00, 0x00, 0x00, 0x4F, 0xF9, 0xD3, 0x4F, 0xB8, 0x13, 0x6A,
373 0x50, 0x0F, 0x2C, 0xEB, 0x15,
374 ];
375 println!("{:02X?}", kvi.build());
376 assert_eq!(raw, kvi.build());
377 }
378}