rusthound_ce/json/checker/
common.rs

1use std::collections::HashMap;
2use std::error::Error;
3
4use regex::Regex;
5use crate::enums::ldaptype::*;
6use crate::objects::common::Link;
7use crate::objects::{
8    user::User,
9    computer::Computer,
10    group::Group,
11    ou::Ou,
12    domain::Domain,
13    trust::Trust,
14    common::{Member, GPOChange, LdapObject}
15};
16//use log::{info,debug,trace};
17use crate::ldap::prepare_ldap_dc;
18use crate::utils::format::domain_to_dc;
19use crate::enums::regex::COMMON_RE1;
20use indicatif::ProgressBar;
21
22/// Function to add default groups
23/// <https://github.com/fox-it/BloodHound.py/blob/645082e3462c93f31b571db945cde1fd7b837fb9/bloodhound/enumeration/memberships.py#L411>
24pub fn add_default_groups(
25    vec_groups: &mut Vec<Group>,
26    vec_computers: &[Computer],
27    domain: String
28) -> Result<(), Box<dyn Error>> {
29    let mut domain_sid = "".to_owned();
30    let mut template_member = Member::new();
31    *template_member.object_type_mut() = "Computer".to_string();
32
33    // ENTERPRISE DOMAIN CONTROLLERS
34    let mut edc_group = Group::new();
35    let mut sid = domain.to_uppercase();
36    sid.push_str("-S-1-5-9");
37
38    let mut name = "ENTERPRISE DOMAIN CONTROLLERS@".to_owned();
39    name.push_str(&domain.to_uppercase());
40
41    let mut vec_members: Vec<Member> = Vec::new();
42    for computer in vec_computers {
43        if computer.properties().get_is_dc().to_owned()
44        {
45            // *template_member.object_identifier_mut() = computer.object_identifier().to_string();
46            // vec_members.push(template_member.to_owned());
47            // let re = Regex::new(r"^S-[0-9]{1}-[0-9]{1}-[0-9]{1,}-[0-9]{1,}-[0-9]{1,}-[0-9]{1,}")?;
48            // let mut sids: Vec<String> = Vec::new();
49            // for sid in re.captures_iter(&computer.object_identifier().to_string())
50            // {
51            //     sids.push(sid[0].to_owned().to_string());
52            // }
53            // domain_sid = sids[0].to_string();
54            *template_member.object_identifier_mut() = computer.object_identifier().clone();
55            vec_members.push(template_member.clone());
56            if let Some(capture) = COMMON_RE1.captures(computer.object_identifier()) {
57                domain_sid = capture.get(0).map(|m| m.as_str().to_string()).unwrap_or_default();
58            }
59        }
60    }
61
62    *edc_group.object_identifier_mut() = sid;
63    *edc_group.properties_mut().name_mut() = name;
64    *edc_group.members_mut() = vec_members;
65    vec_groups.push(edc_group);
66
67    // ACCOUNT OPERATORS
68    let mut account_operators_group = Group::new();
69    sid = domain.to_uppercase();
70    sid.push_str("-S-1-5-32-548");
71    let mut name = "ACCOUNT OPERATORS@".to_owned();
72    name.push_str(&domain.to_uppercase());
73    
74    *account_operators_group.object_identifier_mut() = sid;
75    *account_operators_group.properties_mut().name_mut() = name;
76    *account_operators_group.properties_mut().highvalue_mut() = true;
77    vec_groups.push(account_operators_group);
78
79    // WINDOWS AUTHORIZATION ACCESS GROUP
80    let mut waag_group = Group::new();
81    sid = domain.to_uppercase();
82    sid.push_str("-S-1-5-32-560");
83    let mut name = "WINDOWS AUTHORIZATION ACCESS GROUP@".to_owned();
84    name.push_str(&domain.to_uppercase());
85    *waag_group.object_identifier_mut() = sid;
86    *waag_group.properties_mut().name_mut() = name;
87    vec_groups.push(waag_group);
88
89    // EVERYONE
90    let mut everyone_group = Group::new();
91    sid = domain.to_uppercase();
92    sid.push_str("-S-1-1-0");
93    let mut name = "EVERYONE@".to_owned();
94    name.push_str(&domain.to_uppercase());
95
96    let mut vec_everyone_members: Vec<Member> = Vec::new();
97    let mut member_id = domain_sid.to_owned();
98    member_id.push_str("-515");
99    *template_member.object_identifier_mut() = member_id.to_owned();
100    *template_member.object_type_mut() = "Group".to_string();
101    vec_everyone_members.push(template_member.to_owned());
102
103    member_id = domain_sid.to_owned();
104    member_id.push_str("-513");
105    *template_member.object_identifier_mut() = member_id.to_owned();
106    *template_member.object_type_mut() = "Group".to_string();
107    vec_everyone_members.push(template_member.to_owned());
108
109    *everyone_group.object_identifier_mut() = sid;
110    *everyone_group.properties_mut().name_mut() = name;
111    *everyone_group.members_mut() = vec_everyone_members;
112    vec_groups.push(everyone_group);
113
114    // AUTHENTICATED USERS
115    let mut auth_users_group = Group::new();
116    sid = domain.to_uppercase();
117    sid.push_str("-S-1-5-11");
118    let mut name = "AUTHENTICATED USERS@".to_owned();
119    name.push_str(&domain.to_uppercase());
120
121    let mut vec_auth_users_members: Vec<Member> = Vec::new();
122    member_id = domain_sid.to_owned();
123    member_id.push_str("-515");
124    *template_member.object_identifier_mut() = member_id.to_owned();
125    *template_member.object_type_mut() = "Group".to_string();
126    vec_auth_users_members.push(template_member.to_owned());
127
128    member_id = domain_sid.to_owned();
129    member_id.push_str("-513");
130    *template_member.object_identifier_mut() = member_id.to_owned();
131    *template_member.object_type_mut() = "Group".to_string();
132    vec_auth_users_members.push(template_member.to_owned());
133
134    *auth_users_group.object_identifier_mut() = sid;
135    *auth_users_group.properties_mut().name_mut() = name;
136    *auth_users_group.members_mut() = vec_auth_users_members;
137    vec_groups.push(auth_users_group);
138
139    // ADMINISTRATORS
140    let mut administrators_group = Group::new();
141    sid = domain.to_uppercase();
142    sid.push_str("-S-1-5-32-544");
143    let mut name = "ADMINISTRATORS@".to_owned();
144    name.push_str(&domain.to_uppercase());
145
146    *administrators_group.object_identifier_mut() = sid;
147    *administrators_group.properties_mut().name_mut() = name;
148    *administrators_group.properties_mut().highvalue_mut() = true;
149    vec_groups.push(administrators_group);
150
151    // PRE-WINDOWS 2000 COMPATIBLE ACCESS
152    let mut pw2000ca_group = Group::new();
153    sid = domain.to_uppercase();
154    sid.push_str("-S-1-5-32-554");
155    let mut name = "PRE-WINDOWS 2000 COMPATIBLE ACCESS@".to_owned();
156    name.push_str(&domain.to_uppercase());
157            
158    *pw2000ca_group.object_identifier_mut() = sid;
159    *pw2000ca_group.properties_mut().name_mut() = name;
160    vec_groups.push(pw2000ca_group);    
161
162    // INTERACTIVE
163    let mut interactive_group = Group::new();
164    sid = domain.to_uppercase();
165    sid.push_str("-S-1-5-4");
166    let mut name = "INTERACTIVE@".to_owned();
167    name.push_str(&domain.to_uppercase());
168
169    *interactive_group.object_identifier_mut() = sid;
170    *interactive_group.properties_mut().name_mut() = name;
171    vec_groups.push(interactive_group);
172
173    // PRINT OPERATORS
174    let mut print_operators_group = Group::new();
175    sid = domain.to_uppercase();
176    sid.push_str("-S-1-5-32-550");
177    let mut name = "PRINT OPERATORS@".to_owned();
178    name.push_str(&domain.to_uppercase());
179            
180    *print_operators_group.object_identifier_mut() = sid;
181    *print_operators_group.properties_mut().name_mut() = name;
182    *print_operators_group.properties_mut().highvalue_mut() = true;
183    vec_groups.push(print_operators_group); 
184
185    // TERMINAL SERVER LICENSE SERVERS
186    let mut tsls_group = Group::new();
187    sid = domain.to_uppercase();
188    sid.push_str("-S-1-5-32-561");
189    let mut name = "TERMINAL SERVER LICENSE SERVERS@".to_owned();
190    name.push_str(&domain.to_uppercase());
191            
192    *tsls_group.object_identifier_mut() = sid;
193    *tsls_group.properties_mut().name_mut() = name;
194    vec_groups.push(tsls_group); 
195
196    // INCOMING FOREST TRUST BUILDERS
197    let mut iftb_group = Group::new();
198    sid = domain.to_uppercase();
199    sid.push_str("-S-1-5-32-557");
200    let mut name = "INCOMING FOREST TRUST BUILDERS@".to_owned();
201    name.push_str(&domain.to_uppercase());
202            
203    *iftb_group.object_identifier_mut() = sid;
204    *iftb_group.properties_mut().name_mut() = name;
205    vec_groups.push(iftb_group); 
206 
207    // THIS ORGANIZATION 
208    let mut this_organization_group = Group::new();
209    sid = domain.to_uppercase();
210    sid.push_str("-S-1-5-15");
211    let mut name = "THIS ORGANIZATION@".to_owned();
212    name.push_str(&domain.to_uppercase());
213            
214    *this_organization_group.object_identifier_mut() = sid;
215    *this_organization_group.properties_mut().name_mut() = name;
216    vec_groups.push(this_organization_group);
217    Ok(())
218}
219
220/// Function to add default user
221/// <https://github.com/fox-it/BloodHound.py/blob/645082e3462c93f31b571db945cde1fd7b837fb9/bloodhound/enumeration/memberships.py#L411>
222pub fn add_default_users(
223    vec_users: &mut Vec<User>,
224    domain: String
225) -> Result<(), Box<dyn Error>> {
226    // NT AUTHORITY
227    let mut ntauthority_user = User::new();
228    let mut sid = domain.to_uppercase();
229    sid.push_str("-S-1-5-20");
230    let mut name = "NT AUTHORITY@".to_owned();
231    name.push_str(&domain.to_uppercase());
232    *ntauthority_user.properties_mut().name_mut() = name;
233    *ntauthority_user.object_identifier_mut() = sid;
234    *ntauthority_user.properties_mut().domainsid_mut() = vec_users[0].properties().domainsid().to_string();
235    vec_users.push(ntauthority_user);
236    Ok(())
237}
238
239/// This function is to push user SID in ChildObjects v2
240pub fn add_childobjects_members<T: LdapObject>(
241    vec_replaced: &mut [T],
242    dn_sid: &HashMap<String, String>,
243    sid_type: &HashMap<String, String>,
244) -> Result<(), Box<dyn Error>> {
245    // Needed for progress bar stats
246    let total = vec_replaced.len();
247    let pb = ProgressBar::new(total as u64);
248
249    // Precompute "null" to avoid repeated allocations
250    let null: String = "NULL".to_string();
251
252    // Iterate over the objects
253    for (count, object) in vec_replaced.iter_mut().enumerate() {
254        // Update progress bar periodically
255        if count % (total / 100).max(1) == 0 {
256            pb.set_position(count as u64);
257        }
258
259        // Get the SID, DN, and name of the current object
260        let sid = object.get_object_identifier().to_uppercase();
261        let dn = dn_sid
262            .iter()
263            .find(|(_, v)| **v == sid)
264            .map(|(k, _)| k)
265            .unwrap_or(&null);
266        let name = get_name_from_full_distinguishedname(dn);
267        let _otype = sid_type.get(&sid).unwrap();
268
269        // Filter direct members from dn_sid
270        let direct_members: Vec<Member> = dn_sid
271            .iter()
272            .filter_map(|(dn_object, value_sid)| {
273                let dn_object_upper = dn_object.to_uppercase();
274
275                // Check if dn_object is related to the current object's DN
276                if dn_object_upper.contains(dn)
277                    && &dn_object_upper != dn
278                    && dn_object_upper.split(',')
279                        .nth(1)
280                        .and_then(|s| s.split('=').nth(1))
281                        == Some(&name)
282                {
283                    let mut member = Member::new();
284                    *member.object_identifier_mut() = value_sid.clone();
285                    *member.object_type_mut() = sid_type.get(value_sid).unwrap_or(&null).to_string();
286                    if !member.object_identifier().is_empty() {
287                        return Some(member);
288                    }
289                }
290                None
291            })
292            .collect();
293
294        // Set direct members for the object
295        object.set_child_objects(direct_members);
296    }
297
298    pb.finish_and_clear();
299    Ok(())
300}
301
302/// This function is to push user SID in ChildObjects for Ou v2
303pub fn add_childobjects_members_for_ou(
304    vec_replaced: &mut [Ou],
305    dn_sid: &HashMap<String, String>,
306    sid_type: &HashMap<String, String>,
307) -> Result<(), Box<dyn Error>> {
308    // Progress bar setup
309    let total = vec_replaced.len();
310    let pb = ProgressBar::new(total as u64);
311
312    // Cache common values to avoid repeated allocations
313    let null = "NULL".to_string();
314
315    for (count, object) in vec_replaced.iter_mut().enumerate() {
316        // Update progress bar periodically
317        if count % (total / 100).max(1) == 0 {
318            pb.set_position(count as u64);
319        }
320
321        let mut direct_members = Vec::new();
322        let mut affected_computers = Vec::new();
323
324        // Fetch properties of the current object
325        let dn = object.properties().distinguishedname();
326        let mut name = object.properties().name().to_owned();
327        let sid = dn_sid.get(dn).unwrap_or(&null);
328        let otype = sid_type.get(sid).unwrap_or(&null);
329
330        // Adjust the name if not a domain
331        if otype != "Domain" {
332            if let Some(first_part) = name.split('@').next() {
333                name = first_part.to_string();
334            }
335        }
336
337        // Process all dn_sid entries
338        for (dn_object, value_sid) in dn_sid {
339            let dn_object_upper = dn_object.to_uppercase();
340
341            // Parse the "first" component of the DN
342            let first = dn_object_upper
343                .split(',')
344                .nth(1)
345                .and_then(|part| part.split('=').nth(1))
346                .unwrap_or("");
347
348            if otype != "Domain" {
349                // For non-domain objects
350                if dn_object_upper.contains(dn) && &dn_object_upper != dn && first == name {
351                    let mut member = Member::new();
352                    *member.object_identifier_mut() = value_sid.clone();
353                    let object_type = sid_type.get(value_sid).unwrap_or(&null).to_string();
354                    *member.object_type_mut() = object_type.clone();
355
356                    direct_members.push(member.clone());
357
358                    // Add computers to affected_computers if applicable
359                    if object_type == "Computer" {
360                        affected_computers.push(member);
361                    }
362                }
363            } else {
364                // For domain objects
365                if let Some(cn) = name.split('.').next() {
366                    if first.contains(cn) {
367                        let mut member = Member::new();
368                        *member.object_identifier_mut() = value_sid.clone();
369                        *member.object_type_mut() = sid_type.get(value_sid).unwrap_or(&null).to_string();
370                        direct_members.push(member);
371                    }
372                }
373            }
374        }
375
376        // Set child objects and GPO changes for OUs
377        *object.child_objects_mut() = direct_members;
378        if otype == "OU" {
379            let mut gpo_changes = GPOChange::new();
380            *gpo_changes.affected_computers_mut() = affected_computers;
381            *object.gpo_changes_mut() = gpo_changes;
382        }
383    }
384
385    pb.finish_and_clear();
386    Ok(())
387}
388
389/// This function checks GUID for all Gplinks and replaces them with the correct GUIDs
390pub fn replace_guid_gplink<T: LdapObject>(
391    vec_replaced: &mut [T],
392    dn_sid: &HashMap<String, String>,
393) -> Result<(), Box<dyn Error>> {
394    // Progress bar setup
395    let total = vec_replaced.len();
396    let pb = ProgressBar::new(total as u64);
397
398    // Iterate over the objects
399    for (count, object) in vec_replaced.iter_mut().enumerate() {
400        // Update progress bar periodically
401        if count % (total / 100).max(1) == 0 {
402            pb.set_position(count as u64);
403        }
404
405        // Process links if they exist
406        if !object.get_links().is_empty() {
407            // Replace GUIDs in links
408            let updated_links: Vec<Link> = object
409                .get_links()
410                .iter()
411                .map(|link| {
412                    let mut new_link = link.clone(); // Clone the Link to create a new instance
413                    if let Some(new_guid) = dn_sid
414                        .iter()
415                        .find(|(key, _)| key.contains(link.guid()))
416                        .map(|(_, guid)| guid.to_owned())
417                    {
418                        *new_link.guid_mut() = new_guid;
419                    }
420                    new_link
421                })
422                .collect();
423
424            // Update the object's links
425            object.set_links(updated_links);
426        }
427    }
428
429    pb.finish_and_clear();
430    Ok(())
431}
432
433/// This function pushes computer SIDs into the domain's GPO changes v2
434pub fn add_affected_computers(
435    vec_domains: &mut [Domain],
436    sid_type: &HashMap<String, String>,
437) -> Result<(), Box<dyn Error>> {
438    // Filter only "Computer" SIDs and map them to Member objects
439    let vec_affected_computers: Vec<Member> = sid_type
440        .iter()
441        .filter(|&(_, obj_type)| obj_type == "Computer")
442        .map(|(sid, _)| {
443            let mut member = Member::new();
444            *member.object_type_mut() = "Computer".to_string();
445            *member.object_identifier_mut() = sid.clone();
446            member
447        })
448        .collect();
449
450    // Update the GPO changes of the first domain
451    if let Some(domain) = vec_domains.get_mut(0) {
452        let mut gpo_changes = GPOChange::new();
453        *gpo_changes.affected_computers_mut() = vec_affected_computers;
454        *domain.gpo_changes_mut() = gpo_changes;
455    }
456    Ok(())
457}
458
459/// This function pushes computer SIDs into GPO changes for each OU
460pub fn add_affected_computers_for_ou(
461    vec_ous: &mut [Ou],
462    dn_sid: &HashMap<String, String>,
463    sid_type: &HashMap<String, String>,
464) -> Result<(), Box<dyn Error>> {
465    // Filter all computers DN:SID in advance
466    let dn_sid_filtered: Vec<(&String, &String)> = dn_sid
467        .iter()
468        .filter(|(_, sid)| sid_type.get(*sid).map(|t| t == "Computer").unwrap_or(false))
469        .collect();
470
471    // Map each OU's identifier to its DN
472    let ou_dn_map: HashMap<String, String> = vec_ous
473        .iter()
474        .filter_map(|ou| {
475            dn_sid
476                .iter()
477                .find_map(|(dn, sid)| {
478                    if *sid == *ou.get_object_identifier() {
479                        Some((ou.get_object_identifier().to_owned(), dn.clone()))
480                    } else {
481                        None
482                    }
483                })
484        })
485        .collect();
486
487    // For each OU, add affected computers
488    for ou in vec_ous.iter_mut() {
489        if let Some(ou_dn) = ou_dn_map.get(ou.get_object_identifier()) {
490            let vec_affected_computers: Vec<Member> = dn_sid_filtered
491                .iter()
492                .filter_map(|(dn, sid)| {
493                    if get_contained_by_name_from_distinguishedname(
494                        &get_cn_object_name_from_full_distinguishedname(dn),
495                        dn,
496                    ) == *ou_dn
497                    {
498                        let mut member = Member::new();
499                        *member.object_identifier_mut() = sid.to_string();
500                        *member.object_type_mut() = "Computer".to_string();
501                        Some(member)
502                    } else {
503                        None
504                    }
505                })
506                .collect();
507
508            // Update GPO changes for the OU
509            let mut gpo_changes = GPOChange::new();
510            *gpo_changes.affected_computers_mut() = vec_affected_computers;
511            *ou.gpo_changes_mut() = gpo_changes;
512        }
513    }
514    Ok(())
515}
516
517/// This function replaces FQDN by SID in users' SPNTargets or computers' AllowedToDelegate
518pub fn replace_fqdn_by_sid<T: LdapObject>(
519    object_type: Type,
520    vec_src: &mut [T],
521    fqdn_sid: &HashMap<String, String>,
522) -> Result<(), Box<dyn Error>> {
523    // Progress bar setup
524    let total = vec_src.len();
525    let pb = ProgressBar::new(total as u64);
526
527    // Process based on the object type
528    match object_type {
529        Type::User => {
530            for (count, obj) in vec_src.iter_mut().enumerate() {
531                // Update progress bar
532                if count % (total / 100).max(1) == 0 {
533                    pb.set_position(count as u64);
534                }
535
536                // Process SPNTargets
537                for target in obj.get_spntargets_mut().iter_mut() {
538                    let sid = fqdn_sid
539                        .get(target.computer_sid())
540                        .unwrap_or_else(|| target.computer_sid());
541                    *target.computer_sid_mut() = sid.to_string();
542                }
543
544                // Process AllowedToDelegate
545                for target in obj.get_allowed_to_delegate_mut().iter_mut() {
546                    let sid = fqdn_sid
547                        .get(target.object_identifier())
548                        .unwrap_or_else(|| target.object_identifier());
549                    *target.object_identifier_mut() = sid.to_string();
550                }
551            }
552        }
553        Type::Computer => {
554            for (count, obj) in vec_src.iter_mut().enumerate() {
555                // Update progress bar
556                if count % (total / 100).max(1) == 0 {
557                    pb.set_position(count as u64);
558                }
559
560                // Process AllowedToDelegate
561                for delegate in obj.get_allowed_to_delegate_mut().iter_mut() {
562                    let sid = fqdn_sid
563                        .get(delegate.object_identifier())
564                        .unwrap_or_else(|| delegate.object_identifier());
565                    *delegate.object_identifier_mut() = sid.to_string();
566                }
567            }
568        }
569        _ => {}
570    }
571
572    pb.finish_and_clear();
573    Ok(())
574}
575
576/// This function checks and replaces object names by SIDs in group members v2
577pub fn replace_sid_members(
578    vec_groups: &mut [Group],
579    dn_sid: &HashMap<String, String>,
580    sid_type: &HashMap<String, String>,
581    vec_trusts: &[Trust],
582) -> Result<(), Box<dyn Error>> {
583    // Progress bar setup
584    let total = vec_groups.len();
585    let pb = ProgressBar::new(total as u64);
586
587    // Default values
588    let default_sid = "NULL".to_string();
589    let default_type = "Group".to_string();
590
591    // Iterate over groups
592    for (count, group) in vec_groups.iter_mut().enumerate() {
593        // Update progress bar periodically
594        if count % (total / 100).max(1) == 0 {
595            pb.set_position(count as u64);
596        }
597
598        // Process each member in the group
599        for member in group.members_mut() {
600            let member_dn = member.object_identifier();
601
602            // Get the SID from dn_sid or check in trusts
603            let sid = dn_sid.get(member_dn).unwrap_or(&default_sid);
604            if sid == "NULL" {
605                // Generate SID from another domain if not found
606                let generated_sid = sid_maker_from_another_domain(vec_trusts, member_dn)?;
607                *member.object_identifier_mut() = generated_sid.to_owned();
608                *member.object_type_mut() = default_type.clone();
609            } else {
610                // Use the existing SID
611                let type_object = sid_type.get(sid).unwrap_or(&default_type).to_owned();
612                *member.object_identifier_mut() = sid.to_owned();
613                *member.object_type_mut() = type_object;
614            }
615        }
616    }
617
618    pb.finish_and_clear();
619    Ok(())
620}
621
622/// Make the SID from domain present in trust v2
623fn sid_maker_from_another_domain(
624    vec_trusts: &[Trust],
625    object_identifier: &String,
626) -> Result<String, Box<dyn Error>> {
627    // Create the regex for SID matching
628    let sid_regex = Regex::new(r"S-[0-9]+-[0-9]+-[0-9]+(?:-[0-9]+)+")?;
629
630    // Check if the object_identifier matches any trusted domain
631    for trust in vec_trusts {
632        let ldap_dc = prepare_ldap_dc(trust.target_domain_name());
633        if object_identifier.contains(&ldap_dc[0]) {
634            let id = get_id_from_objectidentifier(object_identifier)?;
635            return Ok(format!("{}{}", trust.target_domain_name(), id))
636        }
637    }
638
639    // Check if object_identifier contains an SID
640    if object_identifier.contains("CN=S-") {
641        if let Some(capture) = sid_regex.captures(object_identifier).and_then(|cap| cap.get(0)) {
642            return Ok(capture.as_str().to_owned())
643        }
644    }
645
646    // Default case: return the object_identifier as-is
647    Ok(object_identifier.to_string())
648}
649
650// Get id from objectidentifier for all common group (Administrators ...) v2
651// https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers
652fn get_id_from_objectidentifier(
653    object_identifier: &str
654) -> Result<String, Box<dyn Error>> {
655
656    // Static mapping of group names to RIDs
657    const NAME_TO_RID: [(&str, &str); 16] = [
658        ("DOMAIN ADMINS", "-512"),
659        ("ADMINISTRATEURS DU DOMAINE", "-512"),
660        ("DOMAIN USERS", "-513"),
661        ("UTILISATEURS DU DOMAINE", "-513"),
662        ("DOMAIN GUESTS", "-514"),
663        ("INVITES DE DOMAINE", "-514"),
664        ("DOMAIN COMPUTERS", "-515"),
665        ("ORDINATEURS DE DOMAINE", "-515"),
666        ("DOMAIN CONTROLLERS", "-516"),
667        ("CONTRÔLEURS DE DOMAINE", "-516"),
668        ("CERT PUBLISHERS", "-517"),
669        ("EDITEURS DE CERTIFICATS", "-517"),
670        ("SCHEMA ADMINS", "-518"),
671        ("ADMINISTRATEURS DU SCHEMA", "-518"),
672        ("ENTERPRISE ADMINS", "-519"),
673        ("ADMINISTRATEURS DE L'ENTREPRISE", "-519"),
674    ];
675
676    // Iterate over the static array to find a match
677    for (name, rid) in NAME_TO_RID.iter() {
678        if object_identifier.contains(name) {
679            return Ok(rid.to_string())
680        }
681    }
682
683    // Default case if no match is found
684    Ok("NULL_ID1".to_string())
685}
686
687/// This function push trust domain values in domain
688pub fn add_trustdomain(
689    vec_domains: &mut Vec<Domain>,
690    vec_trusts: &mut [Trust]
691) -> Result<(), Box<dyn Error>> {
692    if !&vec_trusts[0].target_domain_sid().to_string().contains("SID") {
693        let mut trusts: Vec<Trust> = Vec::new();
694        for trust in vec_trusts {
695            trusts.push(trust.to_owned());
696            let mut new_domain = Domain::new();
697            *new_domain.object_identifier_mut() = trust.target_domain_sid().to_string();
698            *new_domain.properties_mut().name_mut() = trust.target_domain_name().to_string();
699            *new_domain.properties_mut().domain_mut() = trust.target_domain_name().to_string();
700            *new_domain.properties_mut().distinguishedname_mut() = domain_to_dc(trust.target_domain_name());
701            *new_domain.properties_mut().highvalue_mut() = true;
702            vec_domains.push(new_domain);
703        }
704        *vec_domains[0].trusts_mut() = trusts.to_owned();
705    }
706    Ok(())
707}
708
709/// This function checks PrincipalSID for all ACEs and adds the PrincipalType ("Group", "User", "Computer") v2
710pub fn add_type_for_ace<T: LdapObject>(
711    object: &mut [T],
712    sid_type: &HashMap<String, String>,
713) -> Result<(), Box<dyn Error>> {
714    // Progress bar setup
715    let total = object.len();
716    let pb = ProgressBar::new(total as u64);
717
718    // Default type for unmatched SIDs
719    let default_type = "Group".to_string();
720
721    // Iterate over each object
722    for (count, obj) in object.iter_mut().enumerate() {
723        // Update progress bar
724        if count % (total / 100).max(1) == 0 {
725            pb.set_position(count as u64);
726        }
727
728        // Get mutable reference to ACEs
729        for ace in obj.get_aces_mut() {
730            // Fetch the type from sid_type or use the default
731            let type_object = sid_type
732                .get(ace.principal_sid())
733                .unwrap_or(&default_type)
734                .clone();
735
736            // Update the principal type
737            *ace.principal_type_mut() = type_object;
738        }
739    }
740
741    pb.finish_and_clear();
742    Ok(())
743}
744
745/// This function checks PrincipalSID for all AllowedToAct objects and adds the PrincipalType ("Group", "User", "Computer") v2
746pub fn add_type_for_allowtedtoact(
747    computer: &mut [Computer],
748    sid_type: &HashMap<String, String>,
749) -> Result<(), Box<dyn Error>> {
750    // Progress bar setup
751    let total = computer.len();
752    let pb = ProgressBar::new(total as u64);
753
754    // Default type for unmatched SIDs
755    let default_type = "Computer".to_string();
756
757    // Iterate over all computers
758    for (count, comp) in computer.iter_mut().enumerate() {
759        // Update progress bar periodically
760        if count % (total / 100).max(1) == 0 {
761            pb.set_position(count as u64);
762        }
763
764        // Process all AllowedToAct objects
765        for allowed in comp.allowed_to_act_mut() {
766            let type_object = sid_type
767                .get(allowed.object_identifier())
768                .unwrap_or(&default_type)
769                .clone();
770
771            *allowed.object_type_mut() = type_object;
772        }
773    }
774
775    pb.finish_and_clear();
776    Ok(())
777}
778
779/// This function pushes user SID into ChildObjects for Ou v2
780pub fn add_contained_by_for<T: LdapObject>(
781    vec_replaced: &mut [T],
782    dn_sid: &HashMap<String, String>, 
783    sid_type: &HashMap<String, String>,
784) -> Result<(), Box<dyn Error>> {
785    // Progress bar setup
786    let total = vec_replaced.len();
787    let pb = ProgressBar::new(total as u64);
788
789    // Default type for unmatched SIDs
790    let default_type = "Group".to_string();
791
792    for (count, object) in vec_replaced.iter_mut().enumerate() {
793        // Update progress bar periodically
794        if count % (total / 100).max(1) == 0 {
795            pb.set_position(count as u64);
796        }
797
798        // Fetch SID and DN for the current object
799        let sid = object.get_object_identifier();
800        let dn = dn_sid.iter().find_map(|(key, value)| if value == sid { Some(key) } else { None });
801
802        if let Some(dn) = dn {
803            let otype = sid_type.get(sid).unwrap_or(&default_type);
804
805            if otype != "Domain" {
806                // Extract CN name and contained-by name
807                let cn_name = get_cn_object_name_from_full_distinguishedname(dn);
808                let contained_by_name = get_contained_by_name_from_distinguishedname(&cn_name, dn);
809
810                // Check if the contained-by name exists in dn_sid
811                if let Some(sid_contained_by) = dn_sid.get(&contained_by_name) {
812                    let type_contained_by = sid_type.get(sid_contained_by).unwrap_or(&default_type);
813
814                    // Create and set the contained_by Member
815                    let mut contained_by = Member::new();
816                    *contained_by.object_identifier_mut() = sid_contained_by.to_string();
817                    *contained_by.object_type_mut() = type_contained_by.to_string();
818                    object.set_contained_by(Some(contained_by));
819                }
820            }
821        }
822    }
823
824    pb.finish_and_clear();
825    Ok(())
826}
827
828/// Function to get name from DN
829pub fn get_name_from_full_distinguishedname(dn_object: &str) -> String {
830    // Example:
831    // dn_object = CN=G0H4N,CN=USERS,DC=ESSOS,DC=LOCAL
832    let split1 = dn_object.split(",");
833    let vec1 = split1.collect::<Vec<&str>>();
834    let split2 = vec1[0].split("=");
835    let vec2 = split2.collect::<Vec<&str>>();
836    let name = vec2[1].to_owned();
837    // name = G0H4N
838    name
839}
840
841/// Function to get CN=name from DN
842fn get_cn_object_name_from_full_distinguishedname(dn_object: &String) -> String {
843    // Example:
844    // dn_object = CN=G0H4N,CN=USERS,DC=ESSOS,DC=LOCAL
845    let name = dn_object.to_owned();
846    let split = name.split(",");
847    let vec = split.collect::<Vec<&str>>();
848    let name = vec[0].to_owned();
849    // name = CN=G0H4N
850    name
851}
852
853/// Function to get first degree contained by name from DN
854fn get_contained_by_name_from_distinguishedname(cn_name: &str, dn_object: &str) -> String {
855    // Example:
856    // dn_object = CN=G0H4N,CN=USERS,DC=ESSOS,DC=LOCAL
857    let name = format!("{},",cn_name);
858    let split = dn_object.split(&name);
859    let vec = split.collect::<Vec<&str>>();
860    let dn_contained_by = vec[1].to_owned();
861    // dn_contained_by = CN=USERS,DC=ESSOS,DC=LOCAL
862    dn_contained_by
863}
864
865//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
866
867#[cfg(test)]
868mod tests {
869    
870    use crate::json::checker::common::{
871        get_name_from_full_distinguishedname,
872        get_cn_object_name_from_full_distinguishedname,
873        get_contained_by_name_from_distinguishedname
874    };
875    
876    #[test]
877    #[rustfmt::skip]
878    pub fn test_get_name_from_full_distinguishedname() {
879        // Example:
880        // dn_object = CN=G0H4N,CN=USERS,DC=ESSOS,DC=LOCAL
881        let dn_object = "CN=G0H4N,CN=USERS,DC=ESSOS,DC=LOCAL".to_string();
882        let cn_name =  get_name_from_full_distinguishedname(&dn_object);
883        println!("dn_object: {:?}",dn_object);
884        println!("cn_name: {:?}",cn_name);
885        assert_eq!(cn_name, "G0H4N".to_string());
886    }
887
888    #[test]
889    #[rustfmt::skip]
890    pub fn test_get_cn_object_name_from_full_distinguishedname() {
891        // Example:
892        // dn_object = CN=G0H4N,CN=USERS,DC=ESSOS,DC=LOCAL
893        let dn_object = "CN=G0H4N,CN=USERS,DC=ESSOS,DC=LOCAL".to_string();
894        let cn_name =  get_cn_object_name_from_full_distinguishedname(&dn_object);
895        println!("dn_object: {:?}",dn_object);
896        println!("cn_name: {:?}",cn_name);
897        assert_eq!(cn_name, "CN=G0H4N".to_string());
898    }
899    
900    #[test]
901    #[rustfmt::skip]
902    pub fn test_get_contained_by_name_from_name() {
903        // Example:
904        // dn_object = CN=G0H4N,CN=USERS,DC=ESSOS,DC=LOCAL
905        let dn_object = "CN=G0H4N,CN=USERS,DC=ESSOS,DC=LOCAL".to_string();
906        let cn_name = "CN=G0H4N".to_string();
907        let contained_by_dn =  get_contained_by_name_from_distinguishedname(&cn_name, &dn_object);
908        println!("dn_object: {:?}",dn_object);
909        println!("contained_by_dn: {:?}",contained_by_dn);
910        assert_eq!(contained_by_dn, "CN=USERS,DC=ESSOS,DC=LOCAL".to_string());
911    }
912}