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 let guid = decode_guid_le(&value[0]);
99 self.object_identifier = guid.to_owned();
100 self.properties.objectguid = guid;
101 }
102 "nTSecurityDescriptor" => {
103 let relations_ace = parse_ntsecuritydescriptor(
105 self,
106 &value[0],
107 "NtAuthStore",
108 &result_attrs,
109 &result_bin,
110 domain,
111 schema_guid_map,
112 );
113 self.aces = relations_ace;
114 }
115 "cACertificate" => {
116 self.properties.certthumbprints = vec![calculate_sha1(&value[0])];
118 }
119 _ => {}
120 }
121 }
122
123 if self.object_identifier != "SID" {
125 dn_sid.insert(
126 self.properties.distinguishedname.to_string(),
127 self.object_identifier.to_string()
128 );
129 sid_type.insert(
131 self.object_identifier.to_string(),
132 "NtAuthStore".to_string()
133 );
134 }
135
136 Ok(())
139 }
140}
141
142impl LdapObject for NtAuthStore {
143 fn to_json(&self) -> Value {
145 serde_json::to_value(self).unwrap()
146 }
147
148 fn get_object_identifier(&self) -> &String {
150 &self.object_identifier
151 }
152 fn get_is_acl_protected(&self) -> &bool {
153 &self.is_acl_protected
154 }
155 fn get_aces(&self) -> &Vec<AceTemplate> {
156 &self.aces
157 }
158 fn get_spntargets(&self) -> &Vec<SPNTarget> {
159 panic!("Not used by current object.");
160 }
161 fn get_allowed_to_delegate(&self) -> &Vec<Member> {
162 panic!("Not used by current object.");
163 }
164 fn get_links(&self) -> &Vec<Link> {
165 panic!("Not used by current object.");
166 }
167 fn get_contained_by(&self) -> &Option<Member> {
168 &self.contained_by
169 }
170 fn get_child_objects(&self) -> &Vec<Member> {
171 panic!("Not used by current object.");
172 }
173 fn get_haslaps(&self) -> &bool {
174 &false
175 }
176
177 fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
179 &mut self.aces
180 }
181 fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
182 panic!("Not used by current object.");
183 }
184 fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
185 panic!("Not used by current object.");
186 }
187
188 fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
190 self.is_acl_protected = is_acl_protected;
191 self.properties.isaclprotected = is_acl_protected;
192 }
193 fn set_aces(&mut self, aces: Vec<AceTemplate>) {
194 self.aces = aces;
195 }
196 fn set_spntargets(&mut self, _spn_targets: Vec<SPNTarget>) {
197 }
199 fn set_allowed_to_delegate(&mut self, _allowed_to_delegate: Vec<Member>) {
200 }
202 fn set_links(&mut self, _links: Vec<Link>) {
203 }
205 fn set_contained_by(&mut self, contained_by: Option<Member>) {
206 self.contained_by = contained_by;
207 }
208 fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
209 }
211 fn set_owner_rights_flags(&mut self, any: bool, any_inherited: bool) {
212 self.properties.doesanyacegrantownerrights = any;
213 self.properties.doesanyinheritedacegrantownerrights = any_inherited;
214 }
215}
216
217
218#[derive(Debug, Clone, Deserialize, Serialize, Default)]
220pub struct NtAuthStoreProperties {
221 domain: String,
222 name: String,
223 distinguishedname: String,
224 domainsid: String,
225 objectguid: String,
226 doesanyacegrantownerrights: bool,
227 doesanyinheritedacegrantownerrights: bool,
228 isaclprotected: bool,
229 certthumbprints: Vec<String>,
230 description: Option<String>,
231 whencreated: i64,
232}