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