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