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