rusthound_ce/objects/
ou.rs

1use 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, GPOChange, Link, SPNTarget, Member};
9use crate::enums::acl::parse_ntsecuritydescriptor;
10use crate::enums::gplink::parse_gplink;
11use crate::enums::sid::decode_guid_le;
12use crate::utils::date::string_to_epoch;
13
14/// Ou structure
15#[derive(Debug, Clone, Deserialize, Serialize, Default)]
16pub struct Ou {
17    #[serde(rename = "GPOChanges")]
18    gpo_changes: GPOChange,
19    #[serde(rename = "ObjectIdentifier")]
20    object_identifier: String,
21    #[serde(rename = "Properties")]
22    properties: OuProperties,
23    #[serde(rename = "Aces")]
24    aces: Vec<AceTemplate>,
25    #[serde(rename = "Links")]
26    links: Vec<Link>,
27    #[serde(rename = "ChildObjects")]
28    child_objects: Vec<Member>,
29    #[serde(rename = "IsDeleted")]
30    is_deleted: bool,
31    #[serde(rename = "IsACLProtected")]
32    is_acl_protected: bool,
33    #[serde(rename = "ContainedBy")]
34    contained_by: Option<Member>,
35}
36
37impl Ou {
38    // New computer.
39    pub fn new() -> Self { 
40        Self { ..Default::default() } 
41    }
42
43    // Immutable access.
44    pub fn properties(&self) -> &OuProperties {
45        &self.properties
46    }
47
48    // Mutable access.
49    pub fn gpo_changes_mut(&mut self) -> &mut GPOChange {
50        &mut self.gpo_changes
51    }
52    pub fn child_objects_mut(&mut self) -> &mut Vec<Member> {
53        &mut self.child_objects
54    }
55
56    /// Function to parse and replace value for OU object.
57    /// <https://bloodhound.readthedocs.io/en/latest/further-reading/json.html#ous>
58    pub fn parse(
59        &mut self,
60        result: SearchEntry,
61        domain: &str,
62        dn_sid: &mut HashMap<String, String>,
63        sid_type: &mut HashMap<String, String>,
64        domain_sid: &str
65    ) -> Result<(), Box<dyn Error>> {
66        let result_dn: String = result.dn.to_uppercase();
67        let result_attrs: HashMap<String, Vec<String>> = result.attrs;
68        let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
69
70        // Debug for current object
71        debug!("Parse OU: {result_dn}");
72
73        // Trace all result attributes
74        for (key, value) in &result_attrs {
75            trace!("  {key:?}:{value:?}");
76        }
77        // Trace all bin result attributes
78        for (key, value) in &result_bin {
79            trace!("  {key:?}:{value:?}");
80        }
81
82        // Change all values...
83        self.properties.domain = domain.to_uppercase();
84        self.properties.distinguishedname = result_dn;
85        self.properties.domainsid = domain_sid.to_string();
86
87        // Check and replace value
88        for (key, value) in &result_attrs {
89             match key.as_str() {
90                 "name" => {
91                     let name = &value[0];
92                     let email = format!("{}@{}", name.to_owned(), domain);
93                     self.properties.name = email.to_uppercase();
94                 }
95                 "description" => {
96                     self.properties.description = value.first().cloned();
97                 }
98                 "whenCreated" => {
99                     let epoch = string_to_epoch(&value[0])?;
100                     if epoch.is_positive() {
101                          self.properties.whencreated = epoch;
102                     }
103                 }
104                 "gPLink" => {
105                     self.links = parse_gplink(value[0].to_string())?;
106                 }
107                 "gPOtions" => {
108                     self.properties.blocksinheritance = value[0].parse::<i64>().unwrap_or(0) == 1;
109                 }
110                 "IsDeleted" => {
111                     self.is_deleted = true;
112                 }
113                 _ => {}
114             }
115        }
116
117          // For all, bins attributes
118        for (key, value) in &result_bin {
119             match key.as_str() {
120                 "objectGUID" => {
121                     // objectGUID raw to string
122                     self.object_identifier = decode_guid_le(&value[0]).to_owned();
123                 }
124                 "nTSecurityDescriptor" => {
125                     // trace!("nTSecurityDescriptor ACES ACLS ?");
126                     // nTSecurityDescriptor raw to string
127                     let relations_ace = parse_ntsecuritydescriptor(
128                          self,
129                          &value[0],
130                          "OU",
131                          &result_attrs,
132                          &result_bin,
133                          domain,
134                     );
135                     self.aces = relations_ace;
136                 }
137                 _ => {}
138             }
139        }
140        // Push DN and SID in HashMap
141        dn_sid.insert(
142             self.properties.distinguishedname.to_string(),
143             self.object_identifier.to_string(),
144        );
145        // Push DN and Type
146        sid_type.insert(
147            self.object_identifier.to_string(),
148             "OU".to_string(),
149        );
150
151        // Trace and return Ou struct
152        // trace!("JSON OUTPUT: {:?}",serde_json::to_string(&self).unwrap());
153        Ok(())
154    }
155}
156
157impl LdapObject for Ou {
158    // To JSON
159    fn to_json(&self) -> Value {
160        serde_json::to_value(self).unwrap()
161    }
162    
163    // Get values
164    fn get_object_identifier(&self) -> &String {
165        &self.object_identifier
166    }
167    fn get_is_acl_protected(&self) -> &bool {
168        &self.is_acl_protected
169    }
170    fn get_aces(&self) -> &Vec<AceTemplate> {
171        &self.aces
172    }
173    fn get_spntargets(&self) -> &Vec<SPNTarget> {
174        panic!("Not used by current object.");
175    }
176    fn get_allowed_to_delegate(&self) -> &Vec<Member> {
177        panic!("Not used by current object.");
178    }
179    fn get_links(&self) -> &Vec<Link> {
180        &self.links
181    }
182    fn get_contained_by(&self) -> &Option<Member> {
183        &self.contained_by
184    }
185    fn get_child_objects(&self) -> &Vec<Member> {
186        &self.child_objects
187    }
188    fn get_haslaps(&self) -> &bool {
189        &false
190    }
191    
192    // Get mutable values
193    fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
194        &mut self.aces
195    }
196    fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
197        panic!("Not used by current object.");
198    }
199    fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
200        panic!("Not used by current object.");
201    }
202    
203    // Edit values
204    fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
205        self.is_acl_protected = is_acl_protected;
206        self.properties.isaclprotected = is_acl_protected;
207    }
208    fn set_aces(&mut self, aces: Vec<AceTemplate>) {
209        self.aces = aces;
210    }
211    fn set_spntargets(&mut self, _spn_targets: Vec<SPNTarget>) {
212        // Not used by current object.
213    }
214    fn set_allowed_to_delegate(&mut self, _allowed_to_delegate: Vec<Member>) {
215        // Not used by current object.
216    }
217    fn set_links(&mut self, links: Vec<Link>) {
218        self.links = links;
219    }
220    fn set_contained_by(&mut self, contained_by: Option<Member>) {
221        self.contained_by = contained_by;
222    }
223    fn set_child_objects(&mut self, child_objects: Vec<Member>) {
224        self.child_objects = child_objects
225    }
226}
227
228// Ou properties structure
229#[derive(Debug, Clone, Deserialize, Serialize, Default)]
230pub struct OuProperties {
231    domain: String,
232    name: String,
233    distinguishedname: String,
234    domainsid: String,
235    isaclprotected: bool,
236    highvalue: bool,
237    description: Option<String>,
238    whencreated: i64,
239    blocksinheritance: bool
240}
241
242impl OuProperties {
243    // Immutable access.
244    pub fn name(&self) -> &String {
245        &self.name
246    }
247    pub fn distinguishedname(&self) -> &String {
248        &self.distinguishedname
249    }
250
251
252    pub fn isaclprotected_mut(&mut self) -> &mut bool {
253        &mut self.isaclprotected
254    }
255}