1use colored::Colorize;
2use serde_json::value::Value;
3use serde::{Deserialize, Serialize};
4use x509_parser::oid_registry::asn1_rs::oid;
5use x509_parser::prelude::*;
6
7use crate::enums::{decode_guid_le, parse_ntsecuritydescriptor, MaskFlags, SecurityDescriptor, AceFormat, Acl, sid_maker, parse_ca_security};
8use crate::json::checker::common::get_name_from_full_distinguishedname;
9use crate::utils::date::string_to_epoch;
10use crate::objects::common::{
11 LdapObject,
12 AceTemplate,
13 SPNTarget,
14 Link,
15 Member
16};
17use crate::utils::crypto::calculate_sha1;
18
19use ldap3::SearchEntry;
20use log::{debug, error, info, trace};
21use std::collections::HashMap;
22use std::error::Error;
23
24#[derive(Debug, Clone, Deserialize, Serialize, Default)]
26pub struct EnterpriseCA {
27 #[serde(rename = "Properties")]
28 properties: EnterpriseCAProperties,
29 #[serde(rename = "HostingComputer")]
30 hosting_computer: String,
31 #[serde(rename = "CARegistryData")]
32 ca_registry_data: CARegistryData,
33 #[serde(rename = "EnabledCertTemplates")]
34 enabled_cert_templates: Vec<Member>,
35 #[serde(rename = "Aces")]
36 aces: Vec<AceTemplate>,
37 #[serde(rename = "ObjectIdentifier")]
38 object_identifier: String,
39 #[serde(rename = "IsDeleted")]
40 is_deleted: bool,
41 #[serde(rename = "IsACLProtected")]
42 is_acl_protected: bool,
43 #[serde(rename = "ContainedBy")]
44 contained_by: Option<Member>,
45}
46
47impl EnterpriseCA {
48 pub fn new() -> Self {
50 Self { ..Default::default() }
51 }
52
53 pub fn enabled_cert_templates(&self) -> &Vec<Member> {
55 &self.enabled_cert_templates
56 }
57
58 pub fn enabled_cert_templates_mut(&mut self) -> &mut Vec<Member> {
60 &mut self.enabled_cert_templates
61 }
62
63 pub fn parse(
65 &mut self,
66 result: SearchEntry,
67 domain: &String,
68 dn_sid: &mut HashMap<String, String>,
69 sid_type: &mut HashMap<String, String>,
70 domain_sid: &String
71 ) -> Result<(), Box<dyn Error>> {
72 let result_dn: String = result.dn.to_uppercase();
73 let result_attrs: HashMap<String, Vec<String>> = result.attrs;
74 let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
75
76 debug!("Parse EnterpriseCA: {}", result_dn);
78 for (key, value) in &result_attrs {
80 trace!(" {:?}:{:?}", key, value);
81 }
82 for (key, value) in &result_bin {
84 trace!(" {:?}:{:?}", key, value);
85 }
86
87 self.properties.domain = domain.to_uppercase();
89 self.properties.distinguishedname = result_dn;
90 self.properties.domainsid = domain_sid.to_string();
91 let ca_name = get_name_from_full_distinguishedname(&self.properties.distinguishedname);
92 self.properties.caname = ca_name;
93
94 for (key, value) in &result_attrs {
96 match key.as_str() {
97 "name" => {
98 let name = format!("{}@{}", &value[0], domain);
99 self.properties.name = name.to_uppercase();
100 }
101 "description" => {
102 self.properties.description = Some(value[0].to_owned());
103 }
104 "dNSHostName" => {
105 self.properties.dnshostname = value[0].to_owned();
106 }
107 "certificateTemplates" => {
108 if value.len() <= 0 {
109 error!("No certificate templates enabled for {}", self.properties.caname);
110 } else {
111 info!("Found {} enabled certificate templates", value.len().to_string().bold());
113 trace!("Enabled certificate templates: {:?}", value);
114 let enabled_templates: Vec<Member> = value.iter().map(|template_name| {
115 let mut member = Member::new();
116 *member.object_identifier_mut() = template_name.to_owned();
117 *member.object_type_mut() = String::from("CertTemplate");
118
119 member
120 }).collect();
121 self.enabled_cert_templates = enabled_templates;
122 }
123 }
124 "whenCreated" => {
125 let epoch = string_to_epoch(&value[0])?;
126 if epoch.is_positive() {
127 self.properties.whencreated = epoch;
128 }
129 }
130 "IsDeleted" => {
131 self.is_deleted = true.into();
132 }
133 _ => {}
134 }
135 }
136
137 for (key, value) in &result_bin {
139 match key.as_str() {
140 "objectGUID" => {
141 let guid = decode_guid_le(&value[0]);
143 self.object_identifier = guid.to_owned().into();
144 }
145 "nTSecurityDescriptor" => {
146 let entry_type = "EnterpriseCA".to_string();
148 let relations_ace = parse_ntsecuritydescriptor(
150 self,
151 &value[0],
152 entry_type,
153 &result_attrs,
154 &result_bin,
155 &domain,
156 );
157 self.aces = relations_ace;
159 self.hosting_computer = Self::get_hosting_computer(&value[0], &domain);
161 let ca_security_data = parse_ca_security(&value[0], &self.hosting_computer, &domain);
163 if ca_security_data.len() != 0 {
164 let ca_security = CASecurity { data: ca_security_data, collected: true, failure_reason: None };
165 self.properties.casecuritycollected = true;
166 let ca_registry_data = CARegistryData::new(ca_security);
167 self.ca_registry_data = ca_registry_data;
168 } else {
169 let ca_security = CASecurity { data: Vec::new(), collected: false, failure_reason: Some(String::from("Failed to get CASecurity!")) };
170 self.properties.casecuritycollected = false;
171 let ca_registry_data = CARegistryData::new(ca_security);
172 self.ca_registry_data = ca_registry_data;
173 }
174 }
175 "cACertificate" => {
176 let certsha1: String = calculate_sha1(&value[0]);
178 self.properties.certthumbprint = certsha1.to_owned();
179 self.properties.certname = certsha1.to_owned();
180 self.properties.certchain = vec![certsha1.to_owned()];
181
182 let res = X509Certificate::from_der(&value[0]);
184 match res {
185 Ok((_rem, cert)) => {
186 for ext in cert.extensions() {
188 if &ext.oid == &oid!(2.5.29.19) {
190 if let ParsedExtension::BasicConstraints(basic_constraints) = &ext.parsed_extension() {
192 let _ca = &basic_constraints.ca;
193 let _path_len_constraint = &basic_constraints.path_len_constraint;
194 match _path_len_constraint {
197 Some(_path_len_constraint) => {
198 if _path_len_constraint > &0 {
199 self.properties.hasbasicconstraints = true;
200 self.properties.basicconstraintpathlength = _path_len_constraint.to_owned();
201
202 } else {
203 self.properties.hasbasicconstraints = false;
204 self.properties.basicconstraintpathlength = 0;
205 }
206 },
207 None => {
208 self.properties.hasbasicconstraints = false;
209 self.properties.basicconstraintpathlength = 0;
210 }
211 }
212 }
213 }
214 }
215 },
216 _ => error!("CA x509 certificate parsing failed: {:?}", res),
217 }
218 }
219 _ => {}
220 }
221 }
222
223 if self.object_identifier.to_string() != "SID" {
225 dn_sid.insert(
226 self.properties.distinguishedname.to_string(),
227 self.object_identifier.to_string()
228 );
229 sid_type.insert(
231 self.object_identifier.to_string(),
232 "EnterpriseCA".to_string()
233 );
234 }
235
236 Ok(())
239 }
240
241 fn get_hosting_computer(
243 nt: &Vec<u8>,
244 domain: &String,
245 ) -> String {
246 let mut hosting_computer = String::from("Not found");
247 let blacklist_sid = vec![
248 "-544", "-519", "-512", ];
253 let secdesc: SecurityDescriptor = SecurityDescriptor::parse(&nt).unwrap().1;
254 if secdesc.offset_dacl as usize != 0
255 {
256 let res = Acl::parse(&nt[secdesc.offset_dacl as usize..]);
257 match res {
258 Ok(_res) => {
259 let dacl = _res.1;
260 let aces = dacl.data;
261 for ace in aces {
262 if ace.ace_type == 0x00 {
263 let sid = sid_maker(AceFormat::get_sid(ace.data.to_owned()).unwrap(), domain);
264 let mask = match AceFormat::get_mask(ace.data.to_owned()) {
265 Some(mask) => mask,
266 None => continue,
267 };
268 if (MaskFlags::MANAGE_CERTIFICATES.bits() | mask) == mask
269 && !blacklist_sid.iter().any(|blacklisted| sid.ends_with(blacklisted))
270 {
271 hosting_computer = sid;
273 return hosting_computer
274 }
275 }
276 }
277 },
278 Err(err) => error!("Error. Reason: {err}")
279 }
280 }
281 hosting_computer
282 }
283}
284
285impl LdapObject for EnterpriseCA {
286 fn to_json(&self) -> Value {
288 serde_json::to_value(&self).unwrap()
289 }
290
291 fn get_object_identifier(&self) -> &String {
293 &self.object_identifier
294 }
295 fn get_is_acl_protected(&self) -> &bool {
296 &self.is_acl_protected
297 }
298 fn get_aces(&self) -> &Vec<AceTemplate> {
299 &self.aces
300 }
301 fn get_spntargets(&self) -> &Vec<SPNTarget> {
302 panic!("Not used by current object.");
303 }
304 fn get_allowed_to_delegate(&self) -> &Vec<Member> {
305 panic!("Not used by current object.");
306 }
307 fn get_links(&self) -> &Vec<Link> {
308 panic!("Not used by current object.");
309 }
310 fn get_contained_by(&self) -> &Option<Member> {
311 &self.contained_by
312 }
313 fn get_child_objects(&self) -> &Vec<Member> {
314 panic!("Not used by current object.");
315 }
316 fn get_haslaps(&self) -> &bool {
317 &false
318 }
319
320 fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
322 &mut self.aces
323 }
324 fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
325 panic!("Not used by current object.");
326 }
327 fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
328 panic!("Not used by current object.");
329 }
330
331 fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
333 self.is_acl_protected = is_acl_protected;
334 self.properties.isaclprotected = is_acl_protected;
335 }
336 fn set_aces(&mut self, aces: Vec<AceTemplate>) {
337 self.aces = aces;
338 }
339 fn set_spntargets(&mut self, _spn_targets: Vec<SPNTarget>) {
340 }
342 fn set_allowed_to_delegate(&mut self, _allowed_to_delegate: Vec<Member>) {
343 }
345 fn set_links(&mut self, _links: Vec<Link>) {
346 }
348 fn set_contained_by(&mut self, contained_by: Option<Member>) {
349 self.contained_by = contained_by;
350 }
351 fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
352 }
354}
355
356
357#[derive(Debug, Clone, Deserialize, Serialize)]
359pub struct EnterpriseCAProperties {
360 domain: String,
361 name: String,
362 distinguishedname: String,
363 domainsid: String,
364 isaclprotected: bool,
365 description: Option<String>,
366 whencreated: i64,
367 flags: String,
368 caname: String,
369 dnshostname: String,
370 certthumbprint: String,
371 certname: String,
372 certchain: Vec<String>,
373 hasbasicconstraints: bool,
374 basicconstraintpathlength: u32,
375 unresolvedpublishedtemplates: Vec<String>,
376 casecuritycollected: bool,
377 enrollmentagentrestrictionscollected: bool,
378 isuserspecifiessanenabledcollected: bool,
379 roleseparationenabledcollected: bool,
380}
381
382impl Default for EnterpriseCAProperties {
383 fn default() -> EnterpriseCAProperties {
384 EnterpriseCAProperties {
385 domain: String::from(""),
386 name: String::from(""),
387 distinguishedname: String::from(""),
388 domainsid: String::from(""),
389 isaclprotected: false,
390 description: None,
391 whencreated: -1,
392 flags: String::from(""),
393 caname: String::from(""),
394 dnshostname: String::from(""),
395 certthumbprint: String::from(""),
396 certname: String::from(""),
397 certchain: Vec::new(),
398 hasbasicconstraints: false,
399 basicconstraintpathlength: 0,
400 unresolvedpublishedtemplates: Vec::new(),
401 casecuritycollected: false,
402 enrollmentagentrestrictionscollected: false,
403 isuserspecifiessanenabledcollected: false,
404 roleseparationenabledcollected: false,
405 }
406 }
407 }
408
409#[derive(Debug, Clone, Deserialize, Serialize, Default)]
411pub struct CARegistryData {
412 #[serde(rename = "CASecurity")]
413 ca_security: CASecurity,
414 #[serde(rename = "EnrollmentAgentRestrictions")]
415 enrollment_agent_restrictions: EnrollmentAgentRestrictions,
416 #[serde(rename = "IsUserSpecifiesSanEnabled")]
417 is_user_specifies_san_enabled: IsUserSpecifiesSanEnabled,
418 #[serde(rename = "RoleSeparationEnabled")]
419 role_separation_enabled: RoleSeparationEnabled,
420}
421
422impl CARegistryData {
423 pub fn new(
424 ca_security: CASecurity,
425 ) -> Self {
426 Self {
427 ca_security,
428 ..Default::default()
429 }
430 }
431}
432
433#[derive(Debug, Clone, Deserialize, Serialize)]
435pub struct CASecurity {
436 #[serde(rename = "Data")]
437 data: Vec<AceTemplate>,
438 #[serde(rename = "Collected")]
439 collected: bool,
440 #[serde(rename = "FailureReason")]
441 failure_reason: Option<String>,
442}
443
444
445impl Default for CASecurity {
446 fn default() -> CASecurity {
447 CASecurity {
448 data: Vec::new(),
449 collected: true,
450 failure_reason: None,
451 }
452 }
453}
454
455#[derive(Debug, Clone, Deserialize, Serialize)]
457pub struct EnrollmentAgentRestrictions {
458 #[serde(rename = "Restrictions")]
459 restrictions: Vec<String>, #[serde(rename = "Collected")]
461 collected: bool,
462 #[serde(rename = "FailureReason")]
463 failure_reason: Option<String>,
464}
465
466impl Default for EnrollmentAgentRestrictions {
467 fn default() -> EnrollmentAgentRestrictions {
468 EnrollmentAgentRestrictions {
469 restrictions: Vec::new(),
470 collected: true,
471 failure_reason: None,
472 }
473 }
474}
475
476#[derive(Debug, Clone, Deserialize, Serialize)]
478pub struct IsUserSpecifiesSanEnabled {
479 #[serde(rename = "Value")]
480 value: bool,
481 #[serde(rename = "Collected")]
482 collected: bool,
483 #[serde(rename = "FailureReason")]
484 failure_reason: Option<String>,
485}
486
487impl Default for IsUserSpecifiesSanEnabled {
488 fn default() -> IsUserSpecifiesSanEnabled {
489 IsUserSpecifiesSanEnabled {
490 value: false,
491 collected: true,
492 failure_reason: None,
493 }
494 }
495}
496
497#[derive(Debug, Clone, Deserialize, Serialize)]
499pub struct RoleSeparationEnabled {
500 #[serde(rename = "Value")]
501 value: bool,
502 #[serde(rename = "Collected")]
503 collected: bool,
504 #[serde(rename = "FailureReason")]
505 failure_reason: Option<String>,
506}
507
508impl Default for RoleSeparationEnabled {
509 fn default() -> RoleSeparationEnabled {
510 RoleSeparationEnabled {
511 value: false,
512 collected: true,
513 failure_reason: None,
514 }
515 }
516}