1use serde_json::value::Value;
2use serde::{Deserialize, Serialize};
3use ldap3::SearchEntry;
4use log::{debug, error, trace};
5use std::collections::HashMap;
6use std::error::Error;
7use std::collections::HashSet;
8use x509_parser::prelude::*;
9
10use crate::enums::regex::{OBJECT_SID_RE1, SID_PART1_RE1};
11use crate::objects::common::{LdapObject, AceTemplate, SPNTarget, Link, Member};
12use crate::utils::date::{convert_timestamp, string_to_epoch};
13use crate::utils::crypto::convert_encryption_types;
14use crate::enums::acl::{parse_ntsecuritydescriptor, parse_gmsa};
15use crate::enums::secdesc::LdapSid;
16use crate::enums::sid::sid_maker;
17use crate::enums::spntasks::check_spn;
18use crate::enums::uacflags::get_flag;
19
20#[derive(Debug, Clone, Deserialize, Serialize, Default)]
22pub struct User {
23 #[serde(rename ="ObjectIdentifier")]
24 object_identifier: String,
25 #[serde(rename ="IsDeleted")]
26 is_deleted: bool,
27 #[serde(rename ="IsACLProtected")]
28 is_acl_protected: bool,
29 #[serde(rename ="Properties")]
30 properties: UserProperties,
31 #[serde(rename ="PrimaryGroupSID")]
32 primary_group_sid: String,
33 #[serde(rename ="SPNTargets")]
34 spn_targets: Vec<SPNTarget>,
35 #[serde(rename ="UnconstrainedDelegation")]
36 unconstrained_delegation: bool,
37 #[serde(rename ="DomainSID")]
38 domain_sid: String,
39 #[serde(rename ="Aces")]
40 aces: Vec<AceTemplate>,
41 #[serde(rename ="AllowedToDelegate")]
42 allowed_to_delegate: Vec<Member>,
43 #[serde(rename ="HasSIDHistory")]
44 has_sid_history: Vec<String>,
45 #[serde(rename ="ContainedBy")]
46 contained_by: Option<Member>,
47}
48
49impl User {
50 pub fn new() -> Self {
52 Self { ..Default::default()}
53 }
54
55 pub fn properties(&self) -> &UserProperties {
57 &self.properties
58 }
59 pub fn aces(&self) -> &Vec<AceTemplate> {
60 &self.aces
61 }
62
63 pub fn properties_mut(&mut self) -> &mut UserProperties {
65 &mut self.properties
66 }
67 pub fn aces_mut(&mut self) -> &mut Vec<AceTemplate> {
68 &mut self.aces
69 }
70 pub fn object_identifier_mut(&mut self) -> &mut String {
71 &mut self.object_identifier
72 }
73
74 pub fn parse(
77 &mut self,
78 result: SearchEntry,
79 domain: &str,
80 dn_sid: &mut HashMap<String, String>,
81 sid_type: &mut HashMap<String, String>,
82 domain_sid: &str,
83 schema_guid_map: &HashMap<String, String>,
84 ) -> Result<(), Box<dyn Error>> {
85 let result_dn: String = result.dn.to_uppercase();
86 let result_attrs: HashMap<String, Vec<String>> = result.attrs;
87 let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
88
89 debug!("Parse user: {result_dn}");
91
92 for (key, value) in &result_attrs {
94 trace!(" {key:?}:{value:?}");
95 }
96 for (key, value) in &result_bin {
98 trace!(" {key:?}:{value:?}");
99 }
100
101 self.properties.domain = domain.to_uppercase();
103 self.properties.distinguishedname = result_dn;
104 self.properties.enabled = true;
105 self.domain_sid = domain_sid.to_string();
106
107 let mut group_id: String ="".to_owned();
109 for (key, value) in &result_attrs {
110 match key.as_str() {
111 "sAMAccountName" => {
112 let name = &value[0];
113 let email = format!("{}@{}",name.to_owned(),domain);
114 self.properties.name = email.to_uppercase();
115 self.properties.samaccountname = name.to_string();
116 }
117 "description" => {
118 self.properties.description = Some(value[0].to_owned());
119 }
120 "mail" => {
121 self.properties.email = value[0].to_owned();
122 }
123 "title" => {
124 self.properties.title = value[0].to_owned();
125 }
126 "userPassword" => {
127 self.properties.userpassword = value[0].to_owned();
128 }
129 "unixUserPassword" => {
130 self.properties.unixpassword = value[0].to_owned();
131 }
132 "unicodepwd" => {
133 self.properties.unicodepassword = value[0].to_owned();
134 }
135 "sfupassword" => {
136 }
138 "displayName" => {
139 self.properties.displayname = value[0].to_owned();
140 }
141 "adminCount" => {
142 let isadmin = &value[0];
143 let mut admincount = false;
144 if isadmin =="1" {
145 admincount = true;
146 }
147 self.properties.admincount = admincount;
148 }
149 "homeDirectory" => {
150 self.properties.homedirectory = value[0].to_owned();
151 }
152 "scriptpath" => {
153 self.properties.logonscript = value[0].to_owned();
154 }
155 "userAccountControl" => {
156 let uac = &value[0].parse::<u32>().unwrap_or(0);
157 self.properties.useraccountcontrol = *uac;
158 let uac_flags = get_flag(*uac);
159 for flag in uac_flags {
161 if flag.contains("AccountDisable") {
162 self.properties.enabled = false;
163 };
164 if flag.contains("PasswordNotRequired") {
166 self.properties.passwordnotreqd = true;
167 };
168 if flag.contains("DontExpirePassword") {
169 self.properties.pwdneverexpires = true;
170 };
171 if flag.contains("DontReqPreauth") {
172 self.properties.dontreqpreauth = true;
173 };
174 if flag.contains("TrustedForDelegation") {
176 self.properties.unconstraineddelegation = true;
177 self.unconstrained_delegation = true;
178 };
179 if flag.contains("NotDelegated") {
180 self.properties.sensitive = true;
181 };
182 if flag.contains("TrustedToAuthForDelegation") {
184 self.properties.trustedtoauth = true;
185 };
186 }
187 }
188 "msDS-AllowedToDelegateTo" => {
189 let mut vec_members2: Vec<Member> = Vec::new();
190 let mut seen = HashSet::<String>::new();
191
192 for spn_raw in value {
193 let spn = spn_raw.trim().replace('\\', "/");
195 let host_part = spn
197 .split_once('/') .map(|(_, rest)| rest)
199 .unwrap_or(spn.as_str());
200
201 let host = host_part.split(':').next().unwrap_or(host_part);
203
204 let fqdn_upper = host.trim().to_ascii_uppercase();
206 if fqdn_upper.is_empty() {
207 error!("Skipping empty host in SPN: {:?}", spn_raw);
208 continue;
209 }
210
211 if seen.insert(fqdn_upper.clone()) {
213 let mut m = Member::new();
214 *m.object_identifier_mut() = fqdn_upper; *m.object_type_mut() = "Computer".to_string();
216 vec_members2.push(m);
217 }
218 }
219
220 self.allowed_to_delegate = vec_members2;
221 }
222 "lastLogon" => {
223 let lastlogon = &value[0].parse::<i64>().unwrap_or(0);
224 if lastlogon.is_positive() {
225 let epoch = convert_timestamp(*lastlogon);
226 self.properties.lastlogon = epoch;
227 }
228 }
229 "lastLogonTimestamp" => {
230 let lastlogontimestamp = &value[0].parse::<i64>().unwrap_or(0);
231 if lastlogontimestamp.is_positive() {
232 let epoch = convert_timestamp(*lastlogontimestamp);
233 self.properties.lastlogontimestamp = epoch;
234 }
235 }
236 "pwdLastSet" => {
237 let pwdlastset = &value[0].parse::<i64>().unwrap_or(0);
238 if pwdlastset.is_positive() {
239 let epoch = convert_timestamp(*pwdlastset);
240 self.properties.pwdlastset = epoch;
241 }
242 }
243 "whenCreated" => {
244 let epoch = string_to_epoch(&value[0])?;
245 if epoch.is_positive() {
246 self.properties.whencreated = epoch;
247 }
248 }
249 "servicePrincipalName" => {
250 let mut targets: Vec<SPNTarget> = Vec::new();
252 let mut result: Vec<String> = Vec::new();
253 let mut added: bool = false;
254 for v in value {
255 result.push(v.to_owned());
256 let _target = match check_spn(v).to_owned() {
258 Some(_target) => {
259 if !added {
260 targets.push(_target.to_owned());
261 added = true;
262 }
263 },
264 None => {}
265 };
266 }
267 self.properties.serviceprincipalnames = result;
268 self.properties.hasspn = true;
269 self.spn_targets = targets;
270 }
271 "primaryGroupID" => {
272 group_id = value[0].to_owned();
273 }
274 "IsDeleted" => {
275 self.is_deleted = true;
279 }
280 "msDS-SupportedEncryptionTypes" => {
281 self.properties.supportedencryptiontypes = convert_encryption_types(value[0].parse::<i32>().unwrap_or(0));
282 }
283 _ => {}
284 }
285 }
286
287 let mut sid: String = "".to_owned();
289 for (key, value) in &result_bin {
290 match key.as_str() {
291 "objectSid" => {
292 sid = sid_maker(LdapSid::parse(&value[0]).unwrap().1, domain);
293 self.object_identifier = sid.to_owned();
294
295 for domain_sid in OBJECT_SID_RE1.captures_iter(&sid) {
296 self.properties.domainsid = domain_sid[0].to_owned().to_string();
297 }
298 }
299 "nTSecurityDescriptor" => {
300 let relations_ace = parse_ntsecuritydescriptor(
302 self,
303 &value[0],
304 "User",
305 &result_attrs,
306 &result_bin,
307 domain,
308 schema_guid_map,
309 );
310 self.aces_mut().extend(relations_ace);
311 }
312 "sIDHistory" => {
313 let mut list_sid_history: Vec<String> = Vec::new();
316 for bsid in value {
317 debug!("sIDHistory: {:?}", &bsid);
318 list_sid_history.push(sid_maker(LdapSid::parse(bsid).unwrap().1, domain));
319 }
321 self.properties.sidhistory = list_sid_history;
322 }
323 "msDS-GroupMSAMembership" => {
324 let mut relations_ace = parse_ntsecuritydescriptor(
326 self,
327 &value[0],
328 "User",
329 &result_attrs,
330 &result_bin,
331 domain,
332 schema_guid_map,
333 );
334 parse_gmsa(&mut relations_ace, self);
337 }
339 "userCertificate" => {
340 let res = X509Certificate::from_der(&value[0]);
343 match res {
344 Ok((_rem, _cert)) => {},
345 _ => error!("CA x509 certificate parsing failed: {:?}", res),
346 }
347 }
348 _ => {}
349 }
350 }
351
352 #[allow(irrefutable_let_patterns)]
354 if let id = group_id {
355 if let Some(part1) = SID_PART1_RE1.find(&sid) {
356 self.primary_group_sid = format!("{}{}", part1.as_str(), id);
357 } else {
358 eprintln!("[!] Regex did not match any part of the SID");
359 }
360 }
361
362 dn_sid.insert(
364 self.properties.distinguishedname.to_owned(),
365 self.object_identifier.to_owned(),
366 );
367 sid_type.insert(
369 self.object_identifier.to_owned(),
370 "User".to_string(),
371 );
372
373 Ok(())
376 }
377}
378
379impl LdapObject for User {
381 fn to_json(&self) -> Value {
383 serde_json::to_value(self).unwrap()
384 }
385
386 fn get_object_identifier(&self) -> &String {
388 &self.object_identifier
389 }
390 fn get_is_acl_protected(&self) -> &bool {
391 &self.is_acl_protected
392 }
393 fn get_aces(&self) -> &Vec<AceTemplate> {
394 &self.aces
395 }
396 fn get_spntargets(&self) -> &Vec<SPNTarget> {
397 &self.spn_targets
398 }
399 fn get_allowed_to_delegate(&self) -> &Vec<Member> {
400 &self.allowed_to_delegate
401 }
402 fn get_links(&self) -> &Vec<Link> {
403 panic!("Not used by current object.");
404 }
405 fn get_contained_by(&self) -> &Option<Member> {
406 &self.contained_by
407 }
408 fn get_child_objects(&self) -> &Vec<Member> {
409 panic!("Not used by current object.");
410 }
411 fn get_haslaps(&self) -> &bool {
412 &false
413 }
414
415 fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
417 &mut self.aces
418 }
419 fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
420 &mut self.spn_targets
421 }
422 fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
423 &mut self.allowed_to_delegate
424 }
425
426 fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
428 self.is_acl_protected = is_acl_protected;
429 self.properties.isaclprotected = is_acl_protected;
430 }
431 fn set_aces(&mut self, aces: Vec<AceTemplate>) {
432 self.aces = aces;
433 }
434 fn set_spntargets(&mut self, spn_targets: Vec<SPNTarget>) {
435 self.spn_targets = spn_targets;
436 }
437 fn set_allowed_to_delegate(&mut self, allowed_to_delegate: Vec<Member>) {
438 self.allowed_to_delegate = allowed_to_delegate;
439 }
440 fn set_links(&mut self, _links: Vec<Link>) {
441 }
443 fn set_contained_by(&mut self, contained_by: Option<Member>) {
444 self.contained_by = contained_by;
445 }
446 fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
447 }
449}
450
451#[derive(Debug, Clone, Deserialize, Serialize, Default)]
453pub struct UserProperties {
454 domain: String,
455 name: String,
456 domainsid: String,
457 isaclprotected: bool,
458 distinguishedname: String,
459 highvalue: bool,
460 description: Option<String>,
461 whencreated: i64,
462 sensitive: bool,
463 dontreqpreauth: bool,
464 passwordnotreqd: bool,
465 unconstraineddelegation: bool,
466 pwdneverexpires: bool,
467 enabled: bool,
468 trustedtoauth: bool,
469 lastlogon: i64,
470 lastlogontimestamp: i64,
471 pwdlastset: i64,
472 serviceprincipalnames: Vec<String>,
473 hasspn: bool,
474 displayname: String,
475 email: String,
476 title: String,
477 homedirectory: String,
478 logonscript: String,
479 useraccountcontrol: u32,
480 samaccountname: String,
481 userpassword: String,
482 unixpassword: String,
483 unicodepassword: String,
484 sfupassword: String,
485 admincount: bool,
486 supportedencryptiontypes: Vec<String>,
487 sidhistory: Vec<String>,
488 allowedtodelegate: Vec<String>
489}
490
491impl UserProperties {
492 pub fn name(&self) -> &String {
494 &self.name
495 }
496 pub fn domainsid(&self) -> &String {
497 &self.domainsid
498 }
499 pub fn isaclprotected(&self) -> &bool {
500 &self.isaclprotected
501 }
502
503 pub fn name_mut(&mut self) -> &mut String {
505 &mut self.name
506 }
507 pub fn domainsid_mut(&mut self) -> &mut String {
508 &mut self.domainsid
509 }
510 pub fn isaclprotected_mut(&mut self) -> &mut bool {
511 &mut self.isaclprotected
512 }
513}