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 schema_guid_map: &HashMap<String, String>,
68 ) -> Result<(), Box<dyn Error>> {
69 let result_dn: String = result.dn.to_uppercase();
70 let result_attrs: HashMap<String, Vec<String>> = result.attrs;
71 let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
72
73 debug!("Parse EnterpriseCA: {result_dn}");
75
76 for (key, value) in &result_attrs {
78 trace!(" {key:?}:{value:?}");
79 }
80 for (key, value) in &result_bin {
82 trace!(" {key:?}:{value:?}");
83 }
84
85 self.properties.domain = domain.to_uppercase();
87 self.properties.distinguishedname = result_dn;
88 self.properties.domainsid = domain_sid.to_string();
89 let ca_name = get_name_from_full_distinguishedname(&self.properties.distinguishedname);
90 self.properties.caname = ca_name;
91
92 for (key, value) in &result_attrs {
94 match key.as_str() {
95 "name" => {
96 let name = format!("{}@{}", &value[0], domain);
97 self.properties.name = name.to_uppercase();
98 }
99 "description" => {
100 self.properties.description = Some(value[0].to_owned());
101 }
102 "dNSHostName" => {
103 self.properties.dnshostname = value[0].to_owned();
104 }
105 "certificateTemplates" => {
106 if value.is_empty() {
107 error!("No certificate templates enabled for {}", self.properties.caname);
108 } else {
109 info!("Found {} enabled certificate templates", value.len().to_string().bold());
111 trace!("Enabled certificate templates: {:?}", value);
112 let enabled_templates: Vec<Member> = value.iter().map(|template_name| {
113 let mut member = Member::new();
114 *member.object_identifier_mut() = template_name.to_owned();
115 *member.object_type_mut() = String::from("CertTemplate");
116
117 member
118 }).collect();
119 self.enabled_cert_templates = enabled_templates;
120 }
121 }
122 "whenCreated" => {
123 let epoch = string_to_epoch(&value[0])?;
124 if epoch.is_positive() {
125 self.properties.whencreated = epoch;
126 }
127 }
128 "IsDeleted" => {
129 self.is_deleted = true;
130 }
131 _ => {}
132 }
133 }
134
135 for (key, value) in &result_bin {
137 match key.as_str() {
138 "objectGUID" => {
139 let guid = decode_guid_le(&value[0]);
141 self.object_identifier = guid.to_owned();
142 }
143 "nTSecurityDescriptor" => {
144 let relations_ace = parse_ntsecuritydescriptor(
146 self,
147 &value[0],
148 "EnterpriseCA",
149 &result_attrs,
150 &result_bin,
151 domain,
152 schema_guid_map,
153 );
154 self.aces = relations_ace;
156 self.hosting_computer = Self::get_hosting_computer(&value[0], domain);
158 let ca_security_data = parse_ca_security(&value[0], &self.hosting_computer, domain);
160 if !ca_security_data.is_empty() {
161 let ca_security = CASecurity {
162 data: ca_security_data,
163 collected: true,
164 failure_reason: None,
165 };
166 self.properties.casecuritycollected = true;
167 let ca_registry_data = CARegistryData::new(ca_security);
168 self.ca_registry_data = ca_registry_data;
169 } else {
170 let ca_security = CASecurity {
171 data: Vec::new(),
172 collected: false,
173 failure_reason: Some(String::from("Failed to get CASecurity!"))
174 };
175 self.properties.casecuritycollected = false;
176 let ca_registry_data = CARegistryData::new(ca_security);
177 self.ca_registry_data = ca_registry_data;
178 }
179 }
180 "cACertificate" => {
181 let certsha1: String = calculate_sha1(&value[0]);
183 self.properties.certthumbprint = certsha1.to_owned();
184 self.properties.certname = certsha1.to_owned();
185 self.properties.certchain = vec![certsha1.to_owned()];
186
187 let res = X509Certificate::from_der(&value[0]);
189 match res {
190 Ok((_rem, cert)) => {
191 for ext in cert.extensions() {
193 if &ext.oid == &oid!(2.5.29.19) {
195 if let ParsedExtension::BasicConstraints(basic_constraints) = &ext.parsed_extension() {
197 let _ca = &basic_constraints.ca;
198 let _path_len_constraint = &basic_constraints.path_len_constraint;
199 match _path_len_constraint {
202 Some(_path_len_constraint) => {
203 if _path_len_constraint > &0 {
204 self.properties.hasbasicconstraints = true;
205 self.properties.basicconstraintpathlength = _path_len_constraint.to_owned();
206
207 } else {
208 self.properties.hasbasicconstraints = false;
209 self.properties.basicconstraintpathlength = 0;
210 }
211 }
212 None => {
213 self.properties.hasbasicconstraints = false;
214 self.properties.basicconstraintpathlength = 0;
215 }
216 }
217 }
218 }
219 }
220 },
221 _ => error!("CA x509 certificate parsing failed: {:?}", res),
222 }
223 }
224 _ => {}
225 }
226 }
227
228 if self.object_identifier != "SID" {
230 dn_sid.insert(
231 self.properties.distinguishedname.to_string(),
232 self.object_identifier.to_string(),
233 );
234 sid_type.insert(
236 self.object_identifier.to_string(),
237 "EnterpriseCA".to_string(),
238 );
239 }
240
241 Ok(())
244 }
245
246 fn get_hosting_computer(
248 nt: &[u8],
249 domain: &str,
250 ) -> String {
251 let mut hosting_computer = String::from("Not found");
252 let blacklist_sid = [
253 "-544", "-519", "-512", ];
258 let secdesc: SecurityDescriptor = SecurityDescriptor::parse(nt).unwrap().1;
259 if secdesc.offset_dacl as usize != 0
260 {
261 let res = Acl::parse(&nt[secdesc.offset_dacl as usize..]);
262 match res {
263 Ok(_res) => {
264 let dacl = _res.1;
265 let aces = dacl.data;
266 for ace in aces {
267 if ace.ace_type == 0x00 {
268 let sid = sid_maker(AceFormat::get_sid(ace.data.to_owned()).unwrap(), domain);
269 let mask = match AceFormat::get_mask(&ace.data) {
270 Some(mask) => mask,
271 None => continue,
272 };
273 if (MaskFlags::MANAGE_CERTIFICATES.bits() | mask) == mask
274 && !blacklist_sid.iter().any(|blacklisted| sid.ends_with(blacklisted))
275 {
276 hosting_computer = sid;
278 return hosting_computer
279 }
280 }
281 }
282 },
283 Err(err) => error!("Error. Reason: {err}")
284 }
285 }
286 hosting_computer
287 }
288}
289
290impl LdapObject for EnterpriseCA {
291 fn to_json(&self) -> Value {
293 serde_json::to_value(self).unwrap()
294 }
295
296 fn get_object_identifier(&self) -> &String {
298 &self.object_identifier
299 }
300 fn get_is_acl_protected(&self) -> &bool {
301 &self.is_acl_protected
302 }
303 fn get_aces(&self) -> &Vec<AceTemplate> {
304 &self.aces
305 }
306 fn get_spntargets(&self) -> &Vec<SPNTarget> {
307 panic!("Not used by current object.");
308 }
309 fn get_allowed_to_delegate(&self) -> &Vec<Member> {
310 panic!("Not used by current object.");
311 }
312 fn get_links(&self) -> &Vec<Link> {
313 panic!("Not used by current object.");
314 }
315 fn get_contained_by(&self) -> &Option<Member> {
316 &self.contained_by
317 }
318 fn get_child_objects(&self) -> &Vec<Member> {
319 panic!("Not used by current object.");
320 }
321 fn get_haslaps(&self) -> &bool {
322 &false
323 }
324
325 fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
327 &mut self.aces
328 }
329 fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
330 panic!("Not used by current object.");
331 }
332 fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
333 panic!("Not used by current object.");
334 }
335
336 fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
338 self.is_acl_protected = is_acl_protected;
339 self.properties.isaclprotected = is_acl_protected;
340 }
341 fn set_aces(&mut self, aces: Vec<AceTemplate>) {
342 self.aces = aces;
343 }
344 fn set_spntargets(&mut self, _spn_targets: Vec<SPNTarget>) {
345 }
347 fn set_allowed_to_delegate(&mut self, _allowed_to_delegate: Vec<Member>) {
348 }
350 fn set_links(&mut self, _links: Vec<Link>) {
351 }
353 fn set_contained_by(&mut self, contained_by: Option<Member>) {
354 self.contained_by = contained_by;
355 }
356 fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
357 }
359}
360
361
362#[derive(Debug, Clone, Deserialize, Serialize)]
364pub struct EnterpriseCAProperties {
365 domain: String,
366 name: String,
367 distinguishedname: String,
368 domainsid: String,
369 isaclprotected: bool,
370 description: Option<String>,
371 whencreated: i64,
372 flags: String,
373 caname: String,
374 dnshostname: String,
375 certthumbprint: String,
376 certname: String,
377 certchain: Vec<String>,
378 hasbasicconstraints: bool,
379 basicconstraintpathlength: u32,
380 unresolvedpublishedtemplates: Vec<String>,
381 casecuritycollected: bool,
382 enrollmentagentrestrictionscollected: bool,
383 isuserspecifiessanenabledcollected: bool,
384 roleseparationenabledcollected: bool,
385}
386
387impl Default for EnterpriseCAProperties {
388 fn default() -> EnterpriseCAProperties {
389 EnterpriseCAProperties {
390 domain: String::from(""),
391 name: String::from(""),
392 distinguishedname: String::from(""),
393 domainsid: String::from(""),
394 isaclprotected: false,
395 description: None,
396 whencreated: -1,
397 flags: String::from(""),
398 caname: String::from(""),
399 dnshostname: String::from(""),
400 certthumbprint: String::from(""),
401 certname: String::from(""),
402 certchain: Vec::new(),
403 hasbasicconstraints: false,
404 basicconstraintpathlength: 0,
405 unresolvedpublishedtemplates: Vec::new(),
406 casecuritycollected: false,
407 enrollmentagentrestrictionscollected: false,
408 isuserspecifiessanenabledcollected: false,
409 roleseparationenabledcollected: false,
410 }
411 }
412 }
413
414#[derive(Debug, Clone, Deserialize, Serialize, Default)]
416pub struct CARegistryData {
417 #[serde(rename = "CASecurity")]
418 ca_security: CASecurity,
419 #[serde(rename = "EnrollmentAgentRestrictions")]
420 enrollment_agent_restrictions: EnrollmentAgentRestrictions,
421 #[serde(rename = "IsUserSpecifiesSanEnabled")]
422 is_user_specifies_san_enabled: IsUserSpecifiesSanEnabled,
423 #[serde(rename = "RoleSeparationEnabled")]
424 role_separation_enabled: RoleSeparationEnabled,
425}
426
427impl CARegistryData {
428 pub fn new(
429 ca_security: CASecurity,
430 ) -> Self {
431 Self {
432 ca_security,
433 ..Default::default()
434 }
435 }
436}
437
438#[derive(Debug, Clone, Deserialize, Serialize)]
440pub struct CASecurity {
441 #[serde(rename = "Data")]
442 data: Vec<AceTemplate>,
443 #[serde(rename = "Collected")]
444 collected: bool,
445 #[serde(rename = "FailureReason")]
446 failure_reason: Option<String>,
447}
448
449
450impl Default for CASecurity {
451 fn default() -> CASecurity {
452 CASecurity {
453 data: Vec::new(),
454 collected: true,
455 failure_reason: None,
456 }
457 }
458}
459
460#[derive(Debug, Clone, Deserialize, Serialize)]
462pub struct EnrollmentAgentRestrictions {
463 #[serde(rename = "Restrictions")]
464 restrictions: Vec<String>, #[serde(rename = "Collected")]
466 collected: bool,
467 #[serde(rename = "FailureReason")]
468 failure_reason: Option<String>,
469}
470
471impl Default for EnrollmentAgentRestrictions {
472 fn default() -> EnrollmentAgentRestrictions {
473 EnrollmentAgentRestrictions {
474 restrictions: Vec::new(),
475 collected: true,
476 failure_reason: None,
477 }
478 }
479}
480
481#[derive(Debug, Clone, Deserialize, Serialize)]
483pub struct IsUserSpecifiesSanEnabled {
484 #[serde(rename = "Value")]
485 value: bool,
486 #[serde(rename = "Collected")]
487 collected: bool,
488 #[serde(rename = "FailureReason")]
489 failure_reason: Option<String>,
490}
491
492impl Default for IsUserSpecifiesSanEnabled {
493 fn default() -> IsUserSpecifiesSanEnabled {
494 IsUserSpecifiesSanEnabled {
495 value: false,
496 collected: true,
497 failure_reason: None,
498 }
499 }
500}
501
502#[derive(Debug, Clone, Deserialize, Serialize)]
504pub struct RoleSeparationEnabled {
505 #[serde(rename = "Value")]
506 value: bool,
507 #[serde(rename = "Collected")]
508 collected: bool,
509 #[serde(rename = "FailureReason")]
510 failure_reason: Option<String>,
511}
512
513impl Default for RoleSeparationEnabled {
514 fn default() -> RoleSeparationEnabled {
515 RoleSeparationEnabled {
516 value: false,
517 collected: true,
518 failure_reason: None,
519 }
520 }
521}