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, Serialize, Deserialize, Default)]
23pub struct WebEnrollmentResult {
24 #[serde(rename = "Url")]
25 pub url: String,
26 #[serde(rename = "Type")]
27 pub enrollment_type: String,
28 #[serde(rename = "Status")]
29 pub status: String,
30 #[serde(rename = "ADCSWebEnrollmentHTTP")]
31 pub adcs_web_enrollment_http: bool,
32 #[serde(rename = "ADCSWebEnrollmentHTTPS")]
33 pub adcs_web_enrollment_https: bool,
34 #[serde(rename = "ADCSWebEnrollmentEPA")]
35 pub adcs_web_enrollment_epa: bool,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize, Default)]
39pub struct WebEnrollmentEndpoint {
40 #[serde(rename = "Result")]
41 pub result: Option<WebEnrollmentResult>,
42 #[serde(rename = "Collected")]
43 pub collected: bool,
44 #[serde(rename = "FailureReason")]
45 pub failure_reason: Option<String>,
46}
47
48#[derive(Debug, Clone, Deserialize, Serialize, Default)]
50pub struct EnterpriseCA {
51 #[serde(rename = "Properties")]
52 properties: EnterpriseCAProperties,
53 #[serde(rename = "HostingComputer")]
54 hosting_computer: String,
55 #[serde(rename = "CARegistryData")]
56 ca_registry_data: CARegistryData,
57 #[serde(rename = "EnabledCertTemplates")]
58 enabled_cert_templates: Vec<Member>,
59 #[serde(rename = "HttpEnrollmentEndpoints")]
60 http_enrollment_endpoints: Vec<WebEnrollmentEndpoint>,
61 #[serde(rename = "Aces")]
62 aces: Vec<AceTemplate>,
63 #[serde(rename = "ObjectIdentifier")]
64 object_identifier: String,
65 #[serde(rename = "IsDeleted")]
66 is_deleted: bool,
67 #[serde(rename = "IsACLProtected")]
68 is_acl_protected: bool,
69 #[serde(rename = "ContainedBy")]
70 contained_by: Option<Member>,
71}
72
73impl EnterpriseCA {
74 pub fn new() -> Self {
76 Self { ..Default::default() }
77 }
78
79 pub fn enabled_cert_templates(&self) -> &Vec<Member> {
81 &self.enabled_cert_templates
82 }
83
84 pub fn enabled_cert_templates_mut(&mut self) -> &mut Vec<Member> {
86 &mut self.enabled_cert_templates
87 }
88
89 pub fn dns_host(&self) -> &str {
91 &self.properties.dnshostname
92 }
93
94 pub fn caname(&self) -> &str {
96 &self.properties.caname
97 }
98
99 pub fn apply_esc8(&mut self, endpoints: Vec<WebEnrollmentEndpoint>) {
101 self.http_enrollment_endpoints = endpoints;
102 }
103
104 pub fn parse(
106 &mut self,
107 result: SearchEntry,
108 domain: &str,
109 dn_sid: &mut HashMap<String, String>,
110 sid_type: &mut HashMap<String, String>,
111 domain_sid: &str,
112 schema_guid_map: &HashMap<String, String>,
113 ) -> Result<(), Box<dyn Error>> {
114 let result_dn: String = result.dn.to_uppercase();
115 let result_attrs: HashMap<String, Vec<String>> = result.attrs;
116 let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
117
118 debug!("Parse EnterpriseCA: {result_dn}");
120
121 for (key, value) in &result_attrs {
123 trace!(" {key:?}:{value:?}");
124 }
125 for (key, value) in &result_bin {
127 trace!(" {key:?}:{value:?}");
128 }
129
130 self.properties.domain = domain.to_uppercase();
132 self.properties.distinguishedname = result_dn;
133 self.properties.domainsid = domain_sid.to_string();
134 let ca_name = get_name_from_full_distinguishedname(&self.properties.distinguishedname);
135 self.properties.caname = ca_name;
136
137 for (key, value) in &result_attrs {
139 match key.as_str() {
140 "name" => {
141 let name = format!("{}@{}", &value[0], domain);
142 self.properties.name = name.to_uppercase();
143 }
144 "description" => {
145 self.properties.description = Some(value[0].to_owned());
146 }
147 "dNSHostName" => {
148 self.properties.dnshostname = value[0].to_owned();
149 }
150 "certificateTemplates" => {
151 if value.is_empty() {
152 error!("No certificate templates enabled for {}", self.properties.caname);
153 } else {
154 info!("Found {} enabled certificate templates", value.len().to_string().bold());
156 trace!("Enabled certificate templates: {:?}", value);
157 let enabled_templates: Vec<Member> = value.iter().map(|template_name| {
158 let mut member = Member::new();
159 *member.object_identifier_mut() = template_name.to_owned();
160 *member.object_type_mut() = String::from("CertTemplate");
161
162 member
163 }).collect();
164 self.enabled_cert_templates = enabled_templates;
165 }
166 }
167 "whenCreated" => {
168 let epoch = string_to_epoch(&value[0])?;
169 if epoch.is_positive() {
170 self.properties.whencreated = epoch;
171 }
172 }
173 "isDeleted" => {
174 self.is_deleted = true;
175 }
176 _ => {}
177 }
178 }
179
180 for (key, value) in &result_bin {
182 match key.as_str() {
183 "objectGUID" => {
184 let guid = decode_guid_le(&value[0]);
186 self.object_identifier = guid.to_owned();
187 self.properties.objectguid = guid;
188 }
189 "nTSecurityDescriptor" => {
190 let relations_ace = parse_ntsecuritydescriptor(
192 self,
193 &value[0],
194 "EnterpriseCA",
195 &result_attrs,
196 &result_bin,
197 domain,
198 schema_guid_map,
199 );
200 self.aces = relations_ace;
202 self.hosting_computer = Self::get_hosting_computer(&value[0], domain);
204 let ca_security_data = parse_ca_security(&value[0], &self.hosting_computer, domain);
206 if !ca_security_data.is_empty() {
207 let ca_security = CASecurity {
208 data: ca_security_data,
209 collected: true,
210 failure_reason: None,
211 };
212 self.properties.casecuritycollected = true;
213 let ca_registry_data = CARegistryData::new(ca_security);
214 self.ca_registry_data = ca_registry_data;
215 } else {
216 let ca_security = CASecurity {
217 data: Vec::new(),
218 collected: false,
219 failure_reason: Some(String::from("Failed to get CASecurity!"))
220 };
221 self.properties.casecuritycollected = false;
222 let ca_registry_data = CARegistryData::new(ca_security);
223 self.ca_registry_data = ca_registry_data;
224 }
225 }
226 "cACertificate" => {
227 let certsha1: String = calculate_sha1(&value[0]);
229 self.properties.certthumbprint = certsha1.to_owned();
230 self.properties.certname = certsha1.to_owned();
231 self.properties.certchain = vec![certsha1.to_owned()];
232
233 let res = X509Certificate::from_der(&value[0]);
235 match res {
236 Ok((_rem, cert)) => {
237 for ext in cert.extensions() {
239 if &ext.oid == &oid!(2.5.29.19) {
241 if let ParsedExtension::BasicConstraints(basic_constraints) = &ext.parsed_extension() {
243 let _ca = &basic_constraints.ca;
244 let _path_len_constraint = &basic_constraints.path_len_constraint;
245 match _path_len_constraint {
248 Some(_path_len_constraint) => {
249 if _path_len_constraint > &0 {
250 self.properties.hasbasicconstraints = true;
251 self.properties.basicconstraintpathlength = _path_len_constraint.to_owned();
252
253 } else {
254 self.properties.hasbasicconstraints = false;
255 self.properties.basicconstraintpathlength = 0;
256 }
257 }
258 None => {
259 self.properties.hasbasicconstraints = false;
260 self.properties.basicconstraintpathlength = 0;
261 }
262 }
263 }
264 }
265 }
266 },
267 _ => error!("CA x509 certificate parsing failed: {:?}", res),
268 }
269 }
270 _ => {}
271 }
272 }
273
274 if self.object_identifier != "SID" {
276 dn_sid.insert(
277 self.properties.distinguishedname.to_string(),
278 self.object_identifier.to_string(),
279 );
280 sid_type.insert(
282 self.object_identifier.to_string(),
283 "EnterpriseCA".to_string(),
284 );
285 }
286
287 Ok(())
290 }
291
292 fn get_hosting_computer(
294 nt: &[u8],
295 domain: &str,
296 ) -> String {
297 let mut hosting_computer = String::from("Not found");
298 let blacklist_sid = [
299 "-544", "-519", "-512", ];
304 let secdesc: SecurityDescriptor = SecurityDescriptor::parse(nt).unwrap().1;
305 if secdesc.offset_dacl as usize != 0
306 {
307 let res = Acl::parse(&nt[secdesc.offset_dacl as usize..]);
308 match res {
309 Ok(_res) => {
310 let dacl = _res.1;
311 let aces = dacl.data;
312 for ace in aces {
313 if ace.ace_type == 0x00 {
314 let sid = sid_maker(AceFormat::get_sid(ace.data.to_owned()).unwrap(), domain);
315 let mask = match AceFormat::get_mask(&ace.data) {
316 Some(mask) => mask,
317 None => continue,
318 };
319 if (MaskFlags::MANAGE_CERTIFICATES.bits() | mask) == mask
320 && !blacklist_sid.iter().any(|blacklisted| sid.ends_with(blacklisted))
321 {
322 hosting_computer = sid;
324 return hosting_computer
325 }
326 }
327 }
328 },
329 Err(err) => error!("Error. Reason: {err}")
330 }
331 }
332 hosting_computer
333 }
334}
335
336impl LdapObject for EnterpriseCA {
337 fn to_json(&self) -> Value {
339 serde_json::to_value(self).unwrap()
340 }
341
342 fn get_object_identifier(&self) -> &String {
344 &self.object_identifier
345 }
346 fn get_is_acl_protected(&self) -> &bool {
347 &self.is_acl_protected
348 }
349 fn get_aces(&self) -> &Vec<AceTemplate> {
350 &self.aces
351 }
352 fn get_spntargets(&self) -> &Vec<SPNTarget> {
353 panic!("Not used by current object.");
354 }
355 fn get_allowed_to_delegate(&self) -> &Vec<Member> {
356 panic!("Not used by current object.");
357 }
358 fn get_links(&self) -> &Vec<Link> {
359 panic!("Not used by current object.");
360 }
361 fn get_contained_by(&self) -> &Option<Member> {
362 &self.contained_by
363 }
364 fn get_child_objects(&self) -> &Vec<Member> {
365 panic!("Not used by current object.");
366 }
367 fn get_haslaps(&self) -> &bool {
368 &false
369 }
370
371 fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
373 &mut self.aces
374 }
375 fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
376 panic!("Not used by current object.");
377 }
378 fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
379 panic!("Not used by current object.");
380 }
381
382 fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
384 self.is_acl_protected = is_acl_protected;
385 self.properties.isaclprotected = is_acl_protected;
386 }
387 fn set_aces(&mut self, aces: Vec<AceTemplate>) {
388 self.aces = aces;
389 }
390 fn set_spntargets(&mut self, _spn_targets: Vec<SPNTarget>) {
391 }
393 fn set_allowed_to_delegate(&mut self, _allowed_to_delegate: Vec<Member>) {
394 }
396 fn set_links(&mut self, _links: Vec<Link>) {
397 }
399 fn set_contained_by(&mut self, contained_by: Option<Member>) {
400 self.contained_by = contained_by;
401 }
402 fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
403 }
405 fn set_owner_rights_flags(&mut self, any: bool, any_inherited: bool) {
406 self.properties.doesanyacegrantownerrights = any;
407 self.properties.doesanyinheritedacegrantownerrights = any_inherited;
408 }
409}
410
411
412#[derive(Debug, Clone, Deserialize, Serialize)]
414pub struct EnterpriseCAProperties {
415 domain: String,
416 name: String,
417 distinguishedname: String,
418 domainsid: String,
419 objectguid: String,
420 doesanyacegrantownerrights: bool,
421 doesanyinheritedacegrantownerrights: bool,
422 isaclprotected: bool,
423 description: Option<String>,
424 whencreated: i64,
425 flags: String,
426 caname: String,
427 dnshostname: String,
428 certthumbprint: String,
429 certname: String,
430 certchain: Vec<String>,
431 hasbasicconstraints: bool,
432 basicconstraintpathlength: u32,
433 unresolvedpublishedtemplates: Vec<String>,
434 casecuritycollected: bool,
435 enrollmentagentrestrictionscollected: bool,
436 isuserspecifiessanenabledcollected: bool,
437 roleseparationenabledcollected: bool,
438}
439
440impl Default for EnterpriseCAProperties {
441 fn default() -> EnterpriseCAProperties {
442 EnterpriseCAProperties {
443 domain: String::from(""),
444 name: String::from(""),
445 distinguishedname: String::from(""),
446 domainsid: String::from(""),
447 objectguid: String::from(""),
448 doesanyacegrantownerrights: false,
449 doesanyinheritedacegrantownerrights: false,
450 isaclprotected: false,
451 description: None,
452 whencreated: -1,
453 flags: String::from(""),
454 caname: String::from(""),
455 dnshostname: String::from(""),
456 certthumbprint: String::from(""),
457 certname: String::from(""),
458 certchain: Vec::new(),
459 hasbasicconstraints: false,
460 basicconstraintpathlength: 0,
461 unresolvedpublishedtemplates: Vec::new(),
462 casecuritycollected: false,
463 enrollmentagentrestrictionscollected: false,
464 isuserspecifiessanenabledcollected: false,
465 roleseparationenabledcollected: false,
466 }
467 }
468 }
469
470#[derive(Debug, Clone, Deserialize, Serialize, Default)]
472pub struct CARegistryData {
473 #[serde(rename = "CASecurity")]
474 ca_security: CASecurity,
475 #[serde(rename = "EnrollmentAgentRestrictions")]
476 enrollment_agent_restrictions: EnrollmentAgentRestrictions,
477 #[serde(rename = "IsUserSpecifiesSanEnabled")]
478 is_user_specifies_san_enabled: IsUserSpecifiesSanEnabled,
479 #[serde(rename = "RoleSeparationEnabled")]
480 role_separation_enabled: RoleSeparationEnabled,
481}
482
483impl CARegistryData {
484 pub fn new(
485 ca_security: CASecurity,
486 ) -> Self {
487 Self {
488 ca_security,
489 ..Default::default()
490 }
491 }
492}
493
494#[derive(Debug, Clone, Deserialize, Serialize)]
496pub struct CASecurity {
497 #[serde(rename = "Data")]
498 data: Vec<AceTemplate>,
499 #[serde(rename = "Collected")]
500 collected: bool,
501 #[serde(rename = "FailureReason")]
502 failure_reason: Option<String>,
503}
504
505
506impl Default for CASecurity {
507 fn default() -> CASecurity {
508 CASecurity {
509 data: Vec::new(),
510 collected: true,
511 failure_reason: None,
512 }
513 }
514}
515
516#[derive(Debug, Clone, Deserialize, Serialize)]
518pub struct EnrollmentAgentRestrictions {
519 #[serde(rename = "Restrictions")]
520 restrictions: Vec<String>, #[serde(rename = "Collected")]
522 collected: bool,
523 #[serde(rename = "FailureReason")]
524 failure_reason: Option<String>,
525}
526
527impl Default for EnrollmentAgentRestrictions {
528 fn default() -> EnrollmentAgentRestrictions {
529 EnrollmentAgentRestrictions {
530 restrictions: Vec::new(),
531 collected: true,
532 failure_reason: None,
533 }
534 }
535}
536
537#[derive(Debug, Clone, Deserialize, Serialize)]
539pub struct IsUserSpecifiesSanEnabled {
540 #[serde(rename = "Value")]
541 value: bool,
542 #[serde(rename = "Collected")]
543 collected: bool,
544 #[serde(rename = "FailureReason")]
545 failure_reason: Option<String>,
546}
547
548impl Default for IsUserSpecifiesSanEnabled {
549 fn default() -> IsUserSpecifiesSanEnabled {
550 IsUserSpecifiesSanEnabled {
551 value: false,
552 collected: true,
553 failure_reason: None,
554 }
555 }
556}
557
558#[derive(Debug, Clone, Deserialize, Serialize)]
560pub struct RoleSeparationEnabled {
561 #[serde(rename = "Value")]
562 value: bool,
563 #[serde(rename = "Collected")]
564 collected: bool,
565 #[serde(rename = "FailureReason")]
566 failure_reason: Option<String>,
567}
568
569impl Default for RoleSeparationEnabled {
570 fn default() -> RoleSeparationEnabled {
571 RoleSeparationEnabled {
572 value: false,
573 collected: true,
574 failure_reason: None,
575 }
576 }
577}