rusthound_ce/objects/
certtemplate.rs

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