Skip to main content

rusthound_ce/objects/
certtemplate.rs

1use serde_json::value::Value;
2use serde::{Deserialize, Serialize};
3use ldap3::SearchEntry;
4use log::{debug, trace};
5use std::collections::HashMap;
6use std::error::Error;
7
8use crate::objects::common::{LdapObject, AceTemplate, SPNTarget, Link, Member};
9use crate::enums::{decode_guid_le, get_pki_cert_name_flags, get_pki_enrollment_flags, parse_ntsecuritydescriptor};
10use crate::json::checker::common::get_name_from_full_distinguishedname;
11use crate::utils::date::{filetime_to_span, span_to_string, string_to_epoch};
12
13/// CertTemplate structure
14#[derive(Debug, Clone, Deserialize, Serialize, Default)]
15pub struct CertTemplate {
16    #[serde(rename = "Properties")]
17    properties: CertTemplateProperties,
18    #[serde(rename = "Aces")]
19    aces: Vec<AceTemplate>,
20    #[serde(rename = "ObjectIdentifier")]
21    object_identifier: String,
22    #[serde(rename = "IsDeleted")]
23    is_deleted: bool,
24    #[serde(rename = "IsACLProtected")]
25    is_acl_protected: bool,
26    #[serde(rename = "ContainedBy")]
27    contained_by: Option<Member>,
28}
29
30impl CertTemplate {
31    // New CertTemplate
32    pub fn new() -> Self { 
33        Self { ..Default::default() } 
34    }
35
36    // Immutable access.
37    pub fn properties(&self) -> &CertTemplateProperties {
38        &self.properties
39    }
40    pub fn object_identifier(&self) -> &String {
41        &self.object_identifier
42    }
43
44    /// Function to parse and replace value in json template for Certificate Template object.
45    pub fn parse(
46        &mut self,
47        result: SearchEntry,
48        domain: &str,
49        dn_sid: &mut HashMap<String, String>,
50        sid_type: &mut HashMap<String, String>,
51        domain_sid: &str,
52        schema_guid_map: &HashMap<String, String>,
53    ) -> Result<(), Box<dyn Error>> {
54        let result_dn: String = result.dn.to_uppercase();
55        let result_attrs: HashMap<String, Vec<String>> = result.attrs;
56        let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
57
58        // Debug for current object
59        debug!("Parse CertTemplate: {result_dn}");
60
61        // Trace all result attributes
62        for (key, value) in &result_attrs {
63            trace!("  {key:?}:{value:?}");
64        }
65        // Trace all bin result attributes
66        for (key, value) in &result_bin {
67            trace!("  {key:?}:{value:?}");
68        }
69
70        // Change all values...
71        self.properties.domain = domain.to_uppercase();
72        self.properties.distinguishedname = result_dn;    
73        self.properties.domainsid = domain_sid.to_string();
74        let _ca_name = get_name_from_full_distinguishedname(&self.properties.distinguishedname);
75
76        // With a check
77        for (key, value) in &result_attrs {
78            match key.as_str() {
79                "name" => {
80                    let name = format!("{}@{}",&value[0],domain);
81                    self.properties.name = name.to_uppercase();
82                }
83                "description" => {
84                    self.properties.description = Some(value[0].to_owned());
85                }
86                "displayName" => {
87                    self.properties.displayname = value[0].to_owned();
88                }
89                "msPKI-Certificate-Name-Flag" => {
90                    if !value.is_empty() {
91                        self.properties.certificatenameflag = get_pki_cert_name_flags(value[0].parse::<i64>().unwrap_or(0) as u64);
92                        self.properties.enrolleesuppliessubject = self.properties.certificatenameflag.contains("ENROLLEE_SUPPLIES_SUBJECT");
93                        self.properties.subjectaltrequireupn = self.properties.certificatenameflag.contains("SUBJECT_ALT_REQUIRE_UPN");
94                    }
95                }
96                "msPKI-Enrollment-Flag" => {
97                    if !value.is_empty() {
98                        self.properties.enrollmentflag = get_pki_enrollment_flags(value[0].parse::<i64>().unwrap_or(0) as u64);
99                        self.properties.requiresmanagerapproval = self.properties.enrollmentflag.contains("PEND_ALL_REQUESTS");
100                        self.properties.nosecurityextension = self.properties.enrollmentflag.contains("NO_SECURITY_EXTENSION");
101                    }
102                }
103                "msPKI-Private-Key-Flag" => {
104                    // if !value.is_empty() {
105                    //     self.properties.() = get_pki_private_flags(value[0].parse::<i64>().unwrap_or(0) as u64);
106                    // }
107                }
108                "msPKI-RA-Signature" => {
109                    if !value.is_empty() {
110                        self.properties.authorizedsignatures = value.first().unwrap_or(&"0".to_string()).parse::<i64>().unwrap_or(0);
111                    }
112                }
113                "msPKI-RA-Application-Policies" => {
114                    if !value.is_empty() {
115                        self.properties.applicationpolicies = value.to_owned();
116                    }
117                }
118                "msPKI-Certificate-Application-Policy" => {
119                    if !value.is_empty() {
120                        self.properties.certificateapplicationpolicy = value.to_owned();
121                    }
122                }
123                "msPKI-RA-Policies" => {
124                    if !value.is_empty() {
125                        self.properties.issuancepolicies = value.to_owned();
126                    }
127                }
128                "msPKI-Cert-Template-OID" => {
129                    if !value.is_empty() {
130                        self.properties.oid = value[0].to_owned();
131                    }
132                }
133                "pKIExtendedKeyUsage" => {
134                    if !value.is_empty() {
135                        self.properties.ekus = value.to_owned();
136                    }
137                }
138                "msPKI-Template-Schema-Version" => {
139                    self.properties.schemaversion = value[0].parse::<i64>().unwrap_or(0);
140                }
141                "whenCreated" => {
142                    let epoch = string_to_epoch(&value[0])?;
143                    if epoch.is_positive() {
144                        self.properties.whencreated = epoch;
145                    }
146                }
147                "IsDeleted" => {
148                    self.is_deleted = true;
149                }
150                _ => {}
151            }
152        }
153
154        // For all, bins attributs
155        for (key, value) in &result_bin {
156            match key.as_str() {
157                "objectGUID" => {
158                    // objectGUID raw to string
159                    let guid = decode_guid_le(&value[0]);
160                    self.object_identifier = guid.to_owned();
161                }
162                "nTSecurityDescriptor" => {
163                    // nTSecurityDescriptor raw to string
164                    let relations_ace =  parse_ntsecuritydescriptor(
165                        self,
166                        &value[0],
167                        "CertTemplate",
168                        &result_attrs,
169                        &result_bin,
170                        domain,
171                        schema_guid_map,
172                    );
173                    self.aces = relations_ace;
174                }
175                "pKIExpirationPeriod" => {
176                    self.properties.validityperiod = span_to_string(filetime_to_span(value[0].to_owned())?);
177                }
178                "pKIOverlapPeriod" => {
179                    self.properties.renewalperiod = span_to_string(filetime_to_span(value[0].to_owned())?);
180                }
181                _ => {}
182            }
183        }
184
185        // Get all effective ekus.
186        self.properties.effectiveekus = Self::get_effectiveekus(
187            &self.properties.schemaversion,
188            &self.properties.ekus,
189            &self.properties.certificateapplicationpolicy,
190        );
191
192        // Check if authentication is enabled or not for this template.
193        self.properties.authenticationenabled = Self::authentication_is_enabled(self);
194
195        // Push DN and SID in HashMap
196        if self.object_identifier != "SID" {
197            dn_sid.insert(
198                self.properties.distinguishedname.to_string(),
199                self.object_identifier.to_string()
200            );
201            // Push DN and Type
202            sid_type.insert(
203                self.object_identifier.to_string(),
204                "CertTemplate".to_string()
205            );
206        }
207
208        // Trace and return CertTemplate struct
209        // trace!("JSON OUTPUT: {:?}",serde_json::to_string(&self).unwrap());
210        Ok(())
211    }
212
213    /// Function to get effective ekus for one template.
214    fn get_effectiveekus(
215        schema_version: &i64,
216        ekus: &[String],
217        certificateapplicationpolicy: &[String],
218    ) -> Vec<String> {
219        if schema_version == &1 && !ekus.is_empty() {
220            ekus.to_vec()
221        } else {
222            certificateapplicationpolicy.to_vec()
223        }
224    }
225
226    /// Function to check if authentication is enabled or not.
227    fn authentication_is_enabled(&mut self) -> bool {
228        let authentication_oids = [
229            "1.3.6.1.5.5.7.3.2", // ClientAuthentication,
230            "1.3.6.1.5.2.3.4", // PKINITClientAuthentication
231            "1.3.6.1.4.1.311.20.2.2", // SmartcardLogon
232            "2.5.29.37.0", // AnyPurpose
233        ];
234        self.properties.effectiveekus.iter()
235            .any(|eku| authentication_oids.contains(&eku.as_str()))
236            || self.properties.effectiveekus.is_empty()
237    }
238}
239
240impl LdapObject for CertTemplate {
241    // To JSON
242    fn to_json(&self) -> Value {
243        serde_json::to_value(self).unwrap()
244    }
245
246    // Get values
247    fn get_object_identifier(&self) -> &String {
248        &self.object_identifier
249    }
250    fn get_is_acl_protected(&self) -> &bool {
251        &self.is_acl_protected
252    }
253    fn get_aces(&self) -> &Vec<AceTemplate> {
254        &self.aces
255    }
256    fn get_spntargets(&self) -> &Vec<SPNTarget> {
257        panic!("Not used by current object.");
258    }
259    fn get_allowed_to_delegate(&self) -> &Vec<Member> {
260        panic!("Not used by current object.");
261    }
262    fn get_links(&self) -> &Vec<Link> {
263        panic!("Not used by current object.");
264    }
265    fn get_contained_by(&self) -> &Option<Member> {
266        &self.contained_by
267    }
268    fn get_child_objects(&self) -> &Vec<Member> {
269        panic!("Not used by current object.");
270    }
271    fn get_haslaps(&self) -> &bool {
272        &false
273    }
274    
275    // Get mutable values
276    fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
277        &mut self.aces
278    }
279    fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
280        panic!("Not used by current object.");
281    }
282    fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
283        panic!("Not used by current object.");
284    }
285    
286    // Edit values
287    fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
288        self.is_acl_protected = is_acl_protected;
289        self.properties.isaclprotected = is_acl_protected;
290    }
291    fn set_aces(&mut self, aces: Vec<AceTemplate>) {
292        self.aces = aces;
293    }
294    fn set_spntargets(&mut self, _spn_targets: Vec<SPNTarget>) {
295        // Not used by current object.
296    }
297    fn set_allowed_to_delegate(&mut self, _allowed_to_delegate: Vec<Member>) {
298        // Not used by current object.
299    }
300    fn set_links(&mut self, _links: Vec<Link>) {
301        // Not used by current object.
302    }
303    fn set_contained_by(&mut self, contained_by: Option<Member>) {
304        self.contained_by = contained_by;
305    }
306    fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
307        // Not used by current object.
308    }
309}
310
311
312// CertTemplate properties structure
313#[derive(Debug, Clone, Deserialize, Serialize)]
314pub struct CertTemplateProperties {
315   domain: String,
316   name: String,
317   distinguishedname: String,
318   domainsid: String,
319   isaclprotected: bool,
320   description: Option<String>,
321   whencreated: i64,
322   validityperiod: String,
323   renewalperiod: String,
324   schemaversion: i64,
325   displayname: String,
326   oid: String,
327   enrollmentflag: String,
328   requiresmanagerapproval: bool,
329   nosecurityextension: bool,
330   certificatenameflag: String,
331   enrolleesuppliessubject: bool,
332   subjectaltrequireupn: bool,
333   ekus: Vec<String>,
334   certificateapplicationpolicy: Vec<String>,
335   authorizedsignatures: i64,
336   applicationpolicies: Vec<String>,
337   issuancepolicies: Vec<String>,
338   effectiveekus: Vec<String>,
339   authenticationenabled: bool,
340}
341
342impl Default for CertTemplateProperties {
343    fn default() -> CertTemplateProperties {
344        CertTemplateProperties {
345            domain: String::from(""),
346            name: String::from(""),
347            distinguishedname: String::from(""),
348            domainsid: String::from(""),
349            isaclprotected: false,
350            description: None,
351            whencreated: -1,
352            validityperiod: String::from(""),
353            renewalperiod: String::from(""),
354            schemaversion: 1,
355            displayname: String::from(""),
356            oid: String::from(""),
357            enrollmentflag: String::from(""),
358            requiresmanagerapproval: false,
359            nosecurityextension: false,
360            certificatenameflag: String::from(""),
361            enrolleesuppliessubject: false,
362            subjectaltrequireupn: true,
363            ekus: Vec::new(),
364            certificateapplicationpolicy: Vec::new(),
365            authorizedsignatures: 0,
366            applicationpolicies: Vec::new(),
367            issuancepolicies: Vec::new(),
368            effectiveekus: Vec::new(),
369            authenticationenabled: false,
370       }
371    }
372 }
373
374impl CertTemplateProperties {
375    // Immutable access.
376    pub fn name(&self) -> &String {
377        &self.name
378    }
379}