rusthound_ce/
api.rs

1use std::{collections::HashMap, error::Error};
2
3use indicatif::ProgressBar;
4use ldap3::SearchEntry;
5
6use crate::{
7    args::Options, banner::progress_bar, enums::{get_type, Type, PARSER_MOD_RE1, PARSER_MOD_RE2}, json::{
8        checker::check_all_result,
9    }, 
10    objects::{
11        aiaca::AIACA, certtemplate::CertTemplate, common::parse_unknown, computer::Computer, container::Container, domain::Domain, enterpriseca::EnterpriseCA, fsp::Fsp, gpo::Gpo, group::Group, inssuancepolicie::IssuancePolicie, ntauthstore::NtAuthStore, ou::Ou, rootca::RootCA, trust::Trust, user::User
12    }, 
13    storage::{EntrySource}
14};
15
16#[derive(Default)]
17pub struct ADResults {
18    pub users: Vec<User>,
19    pub groups: Vec<Group>,
20    pub computers: Vec<Computer>,
21    pub ous: Vec<Ou>,
22    pub domains: Vec<Domain>,
23    pub gpos: Vec<Gpo>,
24    pub fsps: Vec<Fsp>,
25    pub containers: Vec<Container>,
26    pub trusts: Vec<Trust>,
27    pub ntauthstores: Vec<NtAuthStore>,
28    pub aiacas: Vec<AIACA>,
29    pub rootcas: Vec<RootCA>,
30    pub enterprisecas: Vec<EnterpriseCA>,
31    pub certtemplates: Vec<CertTemplate>,
32    pub issuancepolicies: Vec<IssuancePolicie>,
33
34    pub mappings: DomainMappings,
35}
36
37#[derive(Default)]
38pub struct DomainMappings {
39    /// DN to SID
40    pub dn_sid: HashMap<String, String>,
41    ///  DN to Type
42    pub sid_type: HashMap<String, String>,
43    /// FQDN to SID
44    pub fqdn_sid: HashMap<String, String>,
45    /// fqdn to an ip address
46    pub fqdn_ip: HashMap<String, String>,
47}
48
49impl ADResults {
50    pub fn new() -> Self {
51        Self::default()
52    }
53}
54
55pub async fn prepare_results_from_source<S: EntrySource>(
56    source: S,
57    options: &Options,
58    total_objects: Option<usize>,
59) -> Result<ADResults, Box<dyn std::error::Error>> {
60    let mut ad_results = parse_result_type_from_source(options, source, total_objects)?;
61
62    // Functions to replace and add missing values
63    check_all_result(
64        options,
65        &mut ad_results.users,
66        &mut ad_results.groups,
67        &mut ad_results.computers,
68        &mut ad_results.ous,
69        &mut ad_results.domains,
70        &mut ad_results.gpos,
71        &mut ad_results.fsps,
72        &mut ad_results.containers,
73        &mut ad_results.trusts,
74        &mut ad_results.ntauthstores,
75        &mut ad_results.aiacas,
76        &mut ad_results.rootcas,
77        &mut ad_results.enterprisecas,
78        &mut ad_results.certtemplates,
79        &mut ad_results.issuancepolicies,
80        &ad_results.mappings.dn_sid,
81        &ad_results.mappings.sid_type,
82        &ad_results.mappings.fqdn_sid,
83        &ad_results.mappings.fqdn_ip,
84    )?;
85
86    Ok(ad_results)
87}
88
89// for `total_objects`, the total number of objects may not be known if the ldap query was never run
90// (e.g run was resumed from cached results)
91pub fn parse_result_type_from_source(
92    common_args: &Options,
93    source: impl EntrySource,
94    total_objects: Option<usize>,
95) -> Result<ADResults, Box<dyn Error>> {
96    let mut results = ADResults::default();
97    // Domain name
98    let domain = &common_args.domain;
99
100    // Needed for progress bar stats
101    let pb = ProgressBar::new(1);
102    let mut count = 0;
103    let total = total_objects;
104    let mut domain_sid: String = "DOMAIN_SID".to_owned();
105
106    log::info!("Starting the LDAP objects parsing...");
107
108    let dn_sid = &mut results.mappings.dn_sid;
109    let sid_type = &mut results.mappings.sid_type;
110    let fqdn_sid = &mut results.mappings.fqdn_sid;
111    let fqdn_ip = &mut results.mappings.fqdn_ip;
112
113    for entry in source.into_entry_iter() {
114        let entry: SearchEntry = entry?.into();
115        // Start parsing with Type matching
116        let atype = get_type(&entry).unwrap_or(Type::Unknown);
117        match atype {
118            Type::User => {
119                let mut user: User = User::new();
120                user.parse(entry, domain, dn_sid, sid_type, &domain_sid)?;
121                results.users.push(user);
122            }
123            Type::Group => {
124                let mut group = Group::new();
125                group.parse(entry, domain, dn_sid, sid_type, &domain_sid)?;
126                results.groups.push(group);
127            }
128            Type::Computer => {
129                let mut computer = Computer::new();
130                computer.parse(
131                    entry,
132                    domain,
133                    dn_sid,
134                    sid_type,
135                    fqdn_sid,
136                    fqdn_ip,
137                    &domain_sid,
138                )?;
139                results.computers.push(computer);
140            }
141            Type::Ou => {
142                let mut ou = Ou::new();
143                ou.parse(entry, domain, dn_sid, sid_type, &domain_sid)?;
144                results.ous.push(ou);
145            }
146            Type::Domain => {
147                let mut domain_object = Domain::new();
148                let domain_sid_from_domain =
149                    domain_object.parse(entry, domain, dn_sid, sid_type)?;
150                domain_sid = domain_sid_from_domain;
151                results.domains.push(domain_object);
152            }
153            Type::Gpo => {
154                let mut gpo = Gpo::new();
155                gpo.parse(entry, domain, dn_sid, sid_type, &domain_sid)?;
156                results.gpos.push(gpo);
157            }
158            Type::ForeignSecurityPrincipal => {
159                let mut security_principal = Fsp::new();
160                security_principal.parse(entry, domain, dn_sid, sid_type)?;
161                results.fsps.push(security_principal);
162            }
163            Type::Container => {
164                if PARSER_MOD_RE1.is_match(&entry.dn.to_uppercase())
165                    || PARSER_MOD_RE2.is_match(&entry.dn.to_uppercase())
166                {
167                    //trace!("Container not to add: {}",&cloneresult.dn.to_uppercase());
168                    continue;
169                }
170
171                //trace!("Container: {}",&entry.dn.to_uppercase());
172                let mut container = Container::new();
173                container.parse(entry, domain, dn_sid, sid_type, &domain_sid)?;
174                results.containers.push(container);
175            }
176            Type::Trust => {
177                let mut trust = Trust::new();
178                trust.parse(entry, domain)?;
179                results.trusts.push(trust);
180            }
181            Type::NtAutStore => {
182                let mut nt_auth_store = NtAuthStore::new();
183                nt_auth_store.parse(entry, domain, dn_sid, sid_type, &domain_sid)?;
184                results.ntauthstores.push(nt_auth_store);
185            }
186            Type::AIACA => {
187                let mut aiaca = AIACA::new();
188                aiaca.parse(entry, domain, dn_sid, sid_type, &domain_sid)?;
189                results.aiacas.push(aiaca);
190            }
191            Type::RootCA => {
192                let mut root_ca = RootCA::new();
193                root_ca.parse(entry, domain, dn_sid, sid_type, &domain_sid)?;
194                results.rootcas.push(root_ca);
195            }
196            Type::EnterpriseCA => {
197                let mut enterprise_ca = EnterpriseCA::new();
198                enterprise_ca.parse(entry, domain, dn_sid, sid_type, &domain_sid)?;
199                results.enterprisecas.push(enterprise_ca);
200            }
201            Type::CertTemplate => {
202                let mut cert_template = CertTemplate::new();
203                cert_template.parse(entry, domain, dn_sid, sid_type, &domain_sid)?;
204                results.certtemplates.push(cert_template);
205            }
206            Type::IssuancePolicie => {
207                let mut issuance_policie = IssuancePolicie::new();
208                issuance_policie.parse(entry, domain, dn_sid, sid_type, &domain_sid)?;
209                results.issuancepolicies.push(issuance_policie);
210            }
211            Type::Unknown => {
212                let _unknown = parse_unknown(entry, domain);
213            }
214        }
215        // Manage progress bar
216        // Pourcentage (%) = 100 x Valeur partielle/Valeur totale
217        if let Some(total) = total {
218            count += 1;
219            let pourcentage = 100 * count / total;
220            progress_bar(
221                pb.to_owned(),
222                "Parsing LDAP objects".to_string(),
223                pourcentage.try_into()?,
224                "%".to_string(),
225            );
226        }
227    }
228
229    pb.finish_and_clear();
230    log::info!("Parsing LDAP objects finished!");
231    Ok(results)
232}
233