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 ) -> Result<(), Box<dyn Error>> {
84 let result_dn: String = result.dn.to_uppercase();
85 let result_attrs: HashMap<String, Vec<String>> = result.attrs;
86 let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
87
88 debug!("Parse user: {result_dn}");
90
91 for (key, value) in &result_attrs {
93 trace!(" {key:?}:{value:?}");
94 }
95 for (key, value) in &result_bin {
97 trace!(" {key:?}:{value:?}");
98 }
99
100 self.properties.domain = domain.to_uppercase();
102 self.properties.distinguishedname = result_dn;
103 self.properties.enabled = true;
104 self.domain_sid = domain_sid.to_string();
105
106 let mut group_id: String ="".to_owned();
108 for (key, value) in &result_attrs {
109 match key.as_str() {
110 "sAMAccountName" => {
111 let name = &value[0];
112 let email = format!("{}@{}",name.to_owned(),domain);
113 self.properties.name = email.to_uppercase();
114 self.properties.samaccountname = name.to_string();
115 }
116 "description" => {
117 self.properties.description = Some(value[0].to_owned());
118 }
119 "mail" => {
120 self.properties.email = value[0].to_owned();
121 }
122 "title" => {
123 self.properties.title = value[0].to_owned();
124 }
125 "userPassword" => {
126 self.properties.userpassword = value[0].to_owned();
127 }
128 "unixUserPassword" => {
129 self.properties.unixpassword = value[0].to_owned();
130 }
131 "unicodepwd" => {
132 self.properties.unicodepassword = value[0].to_owned();
133 }
134 "sfupassword" => {
135 }
137 "displayName" => {
138 self.properties.displayname = value[0].to_owned();
139 }
140 "adminCount" => {
141 let isadmin = &value[0];
142 let mut admincount = false;
143 if isadmin =="1" {
144 admincount = true;
145 }
146 self.properties.admincount = admincount;
147 }
148 "homeDirectory" => {
149 self.properties.homedirectory = value[0].to_owned();
150 }
151 "scriptpath" => {
152 self.properties.logonscript = value[0].to_owned();
153 }
154 "userAccountControl" => {
155 let uac = &value[0].parse::<u32>().unwrap_or(0);
156 self.properties.useraccountcontrol = *uac;
157 let uac_flags = get_flag(*uac);
158 for flag in uac_flags {
160 if flag.contains("AccountDisable") {
161 self.properties.enabled = false;
162 };
163 if flag.contains("PasswordNotRequired") {
165 self.properties.passwordnotreqd = true;
166 };
167 if flag.contains("DontExpirePassword") {
168 self.properties.pwdneverexpires = true;
169 };
170 if flag.contains("DontReqPreauth") {
171 self.properties.dontreqpreauth = true;
172 };
173 if flag.contains("TrustedForDelegation") {
175 self.properties.unconstraineddelegation = true;
176 self.unconstrained_delegation = true;
177 };
178 if flag.contains("NotDelegated") {
179 self.properties.sensitive = true;
180 };
181 if flag.contains("TrustedToAuthForDelegation") {
183 self.properties.trustedtoauth = true;
184 };
185 }
186 }
187 "msDS-AllowedToDelegateTo" => {
188 let mut vec_members2: Vec<Member> = Vec::new();
189 let mut seen = HashSet::<String>::new();
190
191 for spn_raw in value {
192 let spn = spn_raw.trim().replace('\\', "/");
194 let host_part = spn
196 .split_once('/') .map(|(_, rest)| rest)
198 .unwrap_or(spn.as_str());
199
200 let host = host_part.split(':').next().unwrap_or(host_part);
202
203 let fqdn_upper = host.trim().to_ascii_uppercase();
205 if fqdn_upper.is_empty() {
206 error!("Skipping empty host in SPN: {:?}", spn_raw);
207 continue;
208 }
209
210 if seen.insert(fqdn_upper.clone()) {
212 let mut m = Member::new();
213 *m.object_identifier_mut() = fqdn_upper; *m.object_type_mut() = "Computer".to_string();
215 vec_members2.push(m);
216 }
217 }
218
219 self.allowed_to_delegate = vec_members2;
220 }
221 "lastLogon" => {
222 let lastlogon = &value[0].parse::<i64>().unwrap_or(0);
223 if lastlogon.is_positive() {
224 let epoch = convert_timestamp(*lastlogon);
225 self.properties.lastlogon = epoch;
226 }
227 }
228 "lastLogonTimestamp" => {
229 let lastlogontimestamp = &value[0].parse::<i64>().unwrap_or(0);
230 if lastlogontimestamp.is_positive() {
231 let epoch = convert_timestamp(*lastlogontimestamp);
232 self.properties.lastlogontimestamp = epoch;
233 }
234 }
235 "pwdLastSet" => {
236 let pwdlastset = &value[0].parse::<i64>().unwrap_or(0);
237 if pwdlastset.is_positive() {
238 let epoch = convert_timestamp(*pwdlastset);
239 self.properties.pwdlastset = epoch;
240 }
241 }
242 "whenCreated" => {
243 let epoch = string_to_epoch(&value[0])?;
244 if epoch.is_positive() {
245 self.properties.whencreated = epoch;
246 }
247 }
248 "servicePrincipalName" => {
249 let mut targets: Vec<SPNTarget> = Vec::new();
251 let mut result: Vec<String> = Vec::new();
252 let mut added: bool = false;
253 for v in value {
254 result.push(v.to_owned());
255 let _target = match check_spn(v).to_owned() {
257 Some(_target) => {
258 if !added {
259 targets.push(_target.to_owned());
260 added = true;
261 }
262 },
263 None => {}
264 };
265 }
266 self.properties.serviceprincipalnames = result;
267 self.properties.hasspn = true;
268 self.spn_targets = targets;
269 }
270 "primaryGroupID" => {
271 group_id = value[0].to_owned();
272 }
273 "IsDeleted" => {
274 self.is_deleted = true;
278 }
279 "msDS-SupportedEncryptionTypes" => {
280 self.properties.supportedencryptiontypes = convert_encryption_types(value[0].parse::<i32>().unwrap_or(0));
281 }
282 _ => {}
283 }
284 }
285
286 let mut sid: String = "".to_owned();
288 for (key, value) in &result_bin {
289 match key.as_str() {
290 "objectSid" => {
291 sid = sid_maker(LdapSid::parse(&value[0]).unwrap().1, domain);
292 self.object_identifier = sid.to_owned();
293
294 for domain_sid in OBJECT_SID_RE1.captures_iter(&sid) {
295 self.properties.domainsid = domain_sid[0].to_owned().to_string();
296 }
297 }
298 "nTSecurityDescriptor" => {
299 let relations_ace = parse_ntsecuritydescriptor(
301 self,
302 &value[0],
303 "User",
304 &result_attrs,
305 &result_bin,
306 domain,
307 );
308 self.aces_mut().extend(relations_ace);
309 }
310 "sIDHistory" => {
311 let mut list_sid_history: Vec<String> = Vec::new();
314 for bsid in value {
315 debug!("sIDHistory: {:?}", &bsid);
316 list_sid_history.push(sid_maker(LdapSid::parse(bsid).unwrap().1, domain));
317 }
319 self.properties.sidhistory = list_sid_history;
320 }
321 "msDS-GroupMSAMembership" => {
322 let mut relations_ace = parse_ntsecuritydescriptor(
324 self,
325 &value[0],
326 "User",
327 &result_attrs,
328 &result_bin,
329 domain,
330 );
331 parse_gmsa(&mut relations_ace, self);
334 }
336 "userCertificate" => {
337 let res = X509Certificate::from_der(&value[0]);
340 match res {
341 Ok((_rem, _cert)) => {},
342 _ => error!("CA x509 certificate parsing failed: {:?}", res),
343 }
344 }
345 _ => {}
346 }
347 }
348
349 #[allow(irrefutable_let_patterns)]
351 if let id = group_id {
352 if let Some(part1) = SID_PART1_RE1.find(&sid) {
353 self.primary_group_sid = format!("{}{}", part1.as_str(), id);
354 } else {
355 eprintln!("[!] Regex did not match any part of the SID");
356 }
357 }
358
359 dn_sid.insert(
361 self.properties.distinguishedname.to_owned(),
362 self.object_identifier.to_owned(),
363 );
364 sid_type.insert(
366 self.object_identifier.to_owned(),
367 "User".to_string(),
368 );
369
370 Ok(())
373 }
374}
375
376impl LdapObject for User {
378 fn to_json(&self) -> Value {
380 serde_json::to_value(self).unwrap()
381 }
382
383 fn get_object_identifier(&self) -> &String {
385 &self.object_identifier
386 }
387 fn get_is_acl_protected(&self) -> &bool {
388 &self.is_acl_protected
389 }
390 fn get_aces(&self) -> &Vec<AceTemplate> {
391 &self.aces
392 }
393 fn get_spntargets(&self) -> &Vec<SPNTarget> {
394 &self.spn_targets
395 }
396 fn get_allowed_to_delegate(&self) -> &Vec<Member> {
397 &self.allowed_to_delegate
398 }
399 fn get_links(&self) -> &Vec<Link> {
400 panic!("Not used by current object.");
401 }
402 fn get_contained_by(&self) -> &Option<Member> {
403 &self.contained_by
404 }
405 fn get_child_objects(&self) -> &Vec<Member> {
406 panic!("Not used by current object.");
407 }
408 fn get_haslaps(&self) -> &bool {
409 &false
410 }
411
412 fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
414 &mut self.aces
415 }
416 fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
417 &mut self.spn_targets
418 }
419 fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
420 &mut self.allowed_to_delegate
421 }
422
423 fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
425 self.is_acl_protected = is_acl_protected;
426 self.properties.isaclprotected = is_acl_protected;
427 }
428 fn set_aces(&mut self, aces: Vec<AceTemplate>) {
429 self.aces = aces;
430 }
431 fn set_spntargets(&mut self, spn_targets: Vec<SPNTarget>) {
432 self.spn_targets = spn_targets;
433 }
434 fn set_allowed_to_delegate(&mut self, allowed_to_delegate: Vec<Member>) {
435 self.allowed_to_delegate = allowed_to_delegate;
436 }
437 fn set_links(&mut self, _links: Vec<Link>) {
438 }
440 fn set_contained_by(&mut self, contained_by: Option<Member>) {
441 self.contained_by = contained_by;
442 }
443 fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
444 }
446}
447
448#[derive(Debug, Clone, Deserialize, Serialize, Default)]
450pub struct UserProperties {
451 domain: String,
452 name: String,
453 domainsid: String,
454 isaclprotected: bool,
455 distinguishedname: String,
456 highvalue: bool,
457 description: Option<String>,
458 whencreated: i64,
459 sensitive: bool,
460 dontreqpreauth: bool,
461 passwordnotreqd: bool,
462 unconstraineddelegation: bool,
463 pwdneverexpires: bool,
464 enabled: bool,
465 trustedtoauth: bool,
466 lastlogon: i64,
467 lastlogontimestamp: i64,
468 pwdlastset: i64,
469 serviceprincipalnames: Vec<String>,
470 hasspn: bool,
471 displayname: String,
472 email: String,
473 title: String,
474 homedirectory: String,
475 logonscript: String,
476 useraccountcontrol: u32,
477 samaccountname: String,
478 userpassword: String,
479 unixpassword: String,
480 unicodepassword: String,
481 sfupassword: String,
482 admincount: bool,
483 supportedencryptiontypes: Vec<String>,
484 sidhistory: Vec<String>,
485 allowedtodelegate: Vec<String>
486}
487
488impl UserProperties {
489 pub fn name(&self) -> &String {
491 &self.name
492 }
493 pub fn domainsid(&self) -> &String {
494 &self.domainsid
495 }
496 pub fn isaclprotected(&self) -> &bool {
497 &self.isaclprotected
498 }
499
500 pub fn name_mut(&mut self) -> &mut String {
502 &mut self.name
503 }
504 pub fn domainsid_mut(&mut self) -> &mut String {
505 &mut self.domainsid
506 }
507 pub fn isaclprotected_mut(&mut self) -> &mut bool {
508 &mut self.isaclprotected
509 }
510}