Skip to main content

ms_pac/
kerb_validation_info.rs

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/// ```c
69/// typedef struct _KERB_VALIDATION_INFO {
70///   FILETIME LogonTime;
71///   FILETIME LogoffTime;
72///   FILETIME KickOffTime;
73///   FILETIME PasswordLastSet;
74///   FILETIME PasswordCanChange;
75///   FILETIME PasswordMustChange;
76///   RPC_UNICODE_STRING EffectiveName;
77///   RPC_UNICODE_STRING FullName;
78///   RPC_UNICODE_STRING LogonScript;
79///   RPC_UNICODE_STRING ProfilePath;
80///   RPC_UNICODE_STRING HomeDirectory;
81///   RPC_UNICODE_STRING HomeDirectoryDrive;
82///   USHORT LogonCount;
83///   USHORT BadPasswordCount;
84///   ULONG UserId;
85///   ULONG PrimaryGroupId;
86///   ULONG GroupCount;
87///   [size_is(GroupCount)] PGROUP_MEMBERSHIP GroupIds;
88///   ULONG UserFlags;
89///   USER_SESSION_KEY UserSessionKey;
90///   RPC_UNICODE_STRING LogonServer;
91///   RPC_UNICODE_STRING LogonDomainName;
92///   PISID LogonDomainId;
93///   ULONG Reserved1[2];
94///   ULONG UserAccountControl;
95///   ULONG SubAuthStatus;
96///   FILETIME LastSuccessfulILogon;
97///   FILETIME LastFailedILogon;
98///   ULONG FailedILogonCount;
99///   ULONG Reserved3;
100///   ULONG SidCount;
101///   [size_is(SidCount)] PKERB_SID_AND_ATTRIBUTES ExtraSids;
102///   PISID ResourceGroupDomainSid;
103///   ULONG ResourceGroupCount;
104///   [size_is(ResourceGroupCount)] PGROUP_MEMBERSHIP ResourceGroupIds;
105/// } KERB_VALIDATION_INFO;
106/// ```
107#[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    /// Creates a binary representation of KERB_VALIDATION_INFO
146    /// by following the indications of [MS-PAC]
147    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    /// Build a RPC header as specified in [MS-RPCE] section 2.2.6.
241    /// It also includes a reference to body data
242    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}