rusthound_ce/objects/
ntauthstore.rs1use serde_json::value::Value;
2use serde::{Deserialize, Serialize};
3use ldap3::SearchEntry;
4use log::{debug, trace};
5use std::collections::HashMap;
6use std::error::Error;
7
8use crate::objects::common::{LdapObject, AceTemplate, SPNTarget, Link, Member};
9use crate::enums::{decode_guid_le, parse_ntsecuritydescriptor};
10use crate::utils::date::string_to_epoch;
11use crate::utils::crypto::calculate_sha1;
12
13#[derive(Debug, Clone, Deserialize, Serialize, Default)]
15pub struct NtAuthStore {
16 #[serde(rename = "Properties")]
17 properties: NtAuthStoreProperties,
18 #[serde(rename = "DomainSID")]
19 domain_sid: String,
20 #[serde(rename = "Aces")]
21 aces: Vec<AceTemplate>,
22 #[serde(rename = "ObjectIdentifier")]
23 object_identifier: String,
24 #[serde(rename = "IsDeleted")]
25 is_deleted: bool,
26 #[serde(rename = "IsACLProtected")]
27 is_acl_protected: bool,
28 #[serde(rename = "ContainedBy")]
29 contained_by: Option<Member>,
30}
31
32impl NtAuthStore {
33 pub fn new() -> Self {
35 Self { ..Default::default() }
36 }
37
38 pub fn parse(
40 &mut self,
41 result: SearchEntry,
42 domain: &str,
43 dn_sid: &mut HashMap<String, String>,
44 sid_type: &mut HashMap<String, String>,
45 domain_sid: &str,
46 schema_guid_map: &HashMap<String, String>,
47 ) -> Result<(), Box<dyn Error>> {
48 let result_dn: String = result.dn.to_uppercase();
49 let result_attrs: HashMap<String, Vec<String>> = result.attrs;
50 let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
51
52 debug!("Parse NtAuthStore: {result_dn}");
54
55 for (key, value) in &result_attrs {
57 trace!(" {key:?}:{value:?}");
58 }
59 for (key, value) in &result_bin {
61 trace!(" {key:?}:{value:?}");
62 }
63
64 self.properties.domain = domain.to_uppercase();
66 self.properties.distinguishedname = result_dn;
67 self.properties.domainsid = domain_sid.to_string();
68 self.domain_sid = domain_sid.to_string();
69
70 for (key, value) in &result_attrs {
72 match key.as_str() {
73 "name" => {
74 let name = format!("{}@{}", &value[0], domain);
75 self.properties.name = name.to_uppercase();
76 }
77 "description" => {
78 self.properties.description = value.first().map(|s| s.to_owned());
79 }
80 "whenCreated" => {
81 let epoch = string_to_epoch(&value[0])?;
82 if epoch.is_positive() {
83 self.properties.whencreated = epoch;
84 }
85 }
86 "IsDeleted" => {
87 self.is_deleted = true;
88 }
89 _ => {}
90 }
91 }
92
93 for (key, value) in &result_bin {
95 match key.as_str() {
96 "objectGUID" => {
97 self.object_identifier = decode_guid_le(&value[0]).to_owned();
99 }
100 "nTSecurityDescriptor" => {
101 let relations_ace = parse_ntsecuritydescriptor(
103 self,
104 &value[0],
105 "NtAuthStore",
106 &result_attrs,
107 &result_bin,
108 domain,
109 schema_guid_map,
110 );
111 self.aces = relations_ace;
112 }
113 "cACertificate" => {
114 self.properties.certthumbprints = vec![calculate_sha1(&value[0])];
116 }
117 _ => {}
118 }
119 }
120
121 if self.object_identifier != "SID" {
123 dn_sid.insert(
124 self.properties.distinguishedname.to_string(),
125 self.object_identifier.to_string()
126 );
127 sid_type.insert(
129 self.object_identifier.to_string(),
130 "NtAuthStore".to_string()
131 );
132 }
133
134 Ok(())
137 }
138}
139
140impl LdapObject for NtAuthStore {
141 fn to_json(&self) -> Value {
143 serde_json::to_value(self).unwrap()
144 }
145
146 fn get_object_identifier(&self) -> &String {
148 &self.object_identifier
149 }
150 fn get_is_acl_protected(&self) -> &bool {
151 &self.is_acl_protected
152 }
153 fn get_aces(&self) -> &Vec<AceTemplate> {
154 &self.aces
155 }
156 fn get_spntargets(&self) -> &Vec<SPNTarget> {
157 panic!("Not used by current object.");
158 }
159 fn get_allowed_to_delegate(&self) -> &Vec<Member> {
160 panic!("Not used by current object.");
161 }
162 fn get_links(&self) -> &Vec<Link> {
163 panic!("Not used by current object.");
164 }
165 fn get_contained_by(&self) -> &Option<Member> {
166 &self.contained_by
167 }
168 fn get_child_objects(&self) -> &Vec<Member> {
169 panic!("Not used by current object.");
170 }
171 fn get_haslaps(&self) -> &bool {
172 &false
173 }
174
175 fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
177 &mut self.aces
178 }
179 fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
180 panic!("Not used by current object.");
181 }
182 fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
183 panic!("Not used by current object.");
184 }
185
186 fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
188 self.is_acl_protected = is_acl_protected;
189 self.properties.isaclprotected = is_acl_protected;
190 }
191 fn set_aces(&mut self, aces: Vec<AceTemplate>) {
192 self.aces = aces;
193 }
194 fn set_spntargets(&mut self, _spn_targets: Vec<SPNTarget>) {
195 }
197 fn set_allowed_to_delegate(&mut self, _allowed_to_delegate: Vec<Member>) {
198 }
200 fn set_links(&mut self, _links: Vec<Link>) {
201 }
203 fn set_contained_by(&mut self, contained_by: Option<Member>) {
204 self.contained_by = contained_by;
205 }
206 fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
207 }
209}
210
211
212#[derive(Debug, Clone, Deserialize, Serialize, Default)]
214pub struct NtAuthStoreProperties {
215 domain: String,
216 name: String,
217 distinguishedname: String,
218 domainsid: String,
219 isaclprotected: bool,
220 certthumbprints: Vec<String>,
221 description: Option<String>,
222 whencreated: i64,
223}