rusthound_ce/objects/
group.rs

1use serde_json::value::Value;
2use serde::{Deserialize, Serialize};
3
4use crate::objects::common::{
5    LdapObject,
6    AceTemplate,
7    SPNTarget,
8    Link,
9    Member
10};
11
12use ldap3::SearchEntry;
13use log::{debug, trace};
14use regex::Regex;
15use std::collections::HashMap;
16use std::error::Error;
17
18use crate::enums::acl::parse_ntsecuritydescriptor;
19use crate::enums::secdesc::LdapSid;
20use crate::enums::sid::{objectsid_to_vec8, sid_maker};
21use crate::utils::date::string_to_epoch;
22
23/// Group structure
24#[derive(Debug, Clone, Deserialize, Serialize, Default)]
25pub struct Group {
26    #[serde(rename = "ObjectIdentifier")]
27    object_identifier: String,
28    #[serde(rename = "IsDeleted")]
29    is_deleted: bool,
30    #[serde(rename = "IsACLProtected")]
31    is_acl_protected: bool,
32    #[serde(rename = "Properties")]
33    properties: GroupProperties,
34    #[serde(rename = "Members")]
35    members: Vec<Member>,
36    #[serde(rename = "Aces")]
37    aces: Vec<AceTemplate>,
38    #[serde(rename = "ContainedBy")]
39    contained_by: Option<Member>,
40}
41
42impl Group {
43    // New group.
44    pub fn new() -> Self { 
45        Self { ..Default::default() } 
46    }
47
48    // Immutable access.
49    pub fn members(&self) -> &Vec<Member> {
50        &self.members
51    }
52
53    // Mutable access.
54    pub fn properties_mut(&mut self) -> &mut GroupProperties {
55        &mut self.properties
56    }
57    pub fn object_identifier_mut(&mut self) -> &mut String {
58        &mut self.object_identifier
59    }
60    pub fn members_mut(&mut self) -> &mut Vec<Member> {
61        &mut self.members
62    }
63
64    /// Function to parse and replace value for group object.
65    /// <https://bloodhound.readthedocs.io/en/latest/further-reading/json.html#groups>
66    pub fn parse(
67        &mut self,
68        result: SearchEntry,
69        domain: &String,
70        dn_sid: &mut HashMap<String, String>,
71        sid_type: &mut HashMap<String, String>,
72        domain_sid: &String,
73    ) -> Result<(), Box<dyn Error>> {
74        let result_dn: String = result.dn.to_uppercase();
75        let result_attrs: HashMap<String, Vec<String>> = result.attrs;
76        let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
77        
78        debug!("Parse group: {}", result_dn);
79        // Trace all result attributes
80        for (key, value) in &result_attrs {
81            trace!("  {:?}:{:?}", key, value);
82        }
83        // Trace all bin result attributes
84        for (key, value) in &result_bin {
85            trace!("  {:?}:{:?}", key, value);
86        }
87        
88        // Some needed vectors.
89        let mut vec_members: Vec<Member> = Vec::new();
90        let mut member_template = Member::new();
91        
92        // Change all values...
93        self.properties.domain = domain.to_uppercase();
94        self.properties.distinguishedname = result_dn;
95        self.properties.domainsid = domain_sid.to_string();
96        
97        // With a check
98        for (key, value) in &result_attrs {
99            match key.as_str() {
100                "name" => {
101                    let name = &value[0];
102                    let email = format!("{}@{}",name.to_owned(),domain);
103                    self.properties.name = email.to_uppercase();
104                }
105                "description" => {
106                    self.properties.description = Some(value[0].to_owned());
107                }
108                "adminCount" => {
109                    let isadmin = &value[0];
110                    let mut admincount = false;
111                    if isadmin == "1" {
112                        admincount = true;
113                    }
114                    self.properties.admincount = admincount.into();
115                }
116                "sAMAccountName" => {
117                    self.properties.samaccountname = value[0].to_owned();
118                }
119                "member" => {
120                    if value.len() > 0 {
121                        for member in value {
122                            *member_template.object_identifier_mut() = member.to_owned().to_uppercase();
123                            if member_template.object_identifier() != "SID" {
124                                vec_members.push(member_template.to_owned());
125                            }
126                        }
127                        self.members = vec_members.to_owned();
128                    }
129                }
130                "objectSid" => {
131                    // objectSid to vec and raw to string
132                    let vec_sid = objectsid_to_vec8(&value[0]);
133                    let sid = sid_maker(LdapSid::parse(&vec_sid).unwrap().1, domain);
134                    self.object_identifier = sid.to_owned();
135        
136                    /*let re = Regex::new(r"^S-[0-9]{1}-[0-9]{1}-[0-9]{1,}-[0-9]{1,}-[0-9]{1,}-[0-9]{1,}").unwrap();
137                    for domain_sid in re.captures_iter(&sid) 
138                    {
139                        group_json["Properties"]["domainsid"] = domain_sid[0].to_owned().to_string().into();
140                    }*/
141        
142                    // highvalue
143                    if sid.ends_with("-512") 
144                    || sid.ends_with("-516") 
145                    || sid.ends_with("-519") 
146                    || sid.ends_with("-520") 
147                    {
148                        self.properties.highvalue = true;
149                    }
150                    else if sid.ends_with("S-1-5-32-544") 
151                    || sid.ends_with("S-1-5-32-548") 
152                    || sid.ends_with("S-1-5-32-549")
153                    || sid.ends_with("S-1-5-32-550") 
154                    || sid.ends_with("S-1-5-32-551") 
155                    {
156                        self.properties.highvalue = true;
157                    }
158                    else {
159                        self.properties.highvalue = false;
160                    }
161                }
162                "whenCreated" => {
163                    let epoch = string_to_epoch(&value[0])?;
164                    if epoch.is_positive() {
165                        self.properties.whencreated = epoch;
166                    }
167                }
168                "IsDeleted" => {
169                    self.is_deleted =true;
170                }
171                _ => {}
172            }
173        }
174        
175        // For all, bins attributs
176        for (key, value) in &result_bin {
177            match key.as_str() {
178                "objectSid" => {
179                    // objectSid raw to string
180                    let sid = sid_maker(LdapSid::parse(&value[0]).unwrap().1, domain);
181                    self.object_identifier = sid.to_owned();
182        
183                    let re = Regex::new(r"^S-[0-9]{1}-[0-9]{1}-[0-9]{1,}-[0-9]{1,}-[0-9]{1,}-[0-9]{1,}")?;
184                    for domain_sid in re.captures_iter(&sid) 
185                    {
186                        self.properties.domainsid = domain_sid[0].to_owned().to_string();
187                    }
188                    
189                    // highvalue
190                    if sid.ends_with("-512") 
191                    || sid.ends_with("-516") 
192                    || sid.ends_with("-519") 
193                    || sid.ends_with("-520") 
194                    {
195                        self.properties.highvalue = true;
196                    }
197                    else if sid.ends_with("S-1-5-32-544") 
198                    || sid.ends_with("S-1-5-32-548") 
199                    || sid.ends_with("S-1-5-32-549")
200                    || sid.ends_with("S-1-5-32-550") 
201                    || sid.ends_with("S-1-5-32-551") 
202                    {
203                        self.properties.highvalue = true;
204                    }
205                    else {
206                        self.properties.highvalue = false;
207                    }
208                }
209                "nTSecurityDescriptor" => {
210                    // Needed with acl
211                    let entry_type = "Group".to_string();
212                    // nTSecurityDescriptor raw to string
213                    let relations_ace = parse_ntsecuritydescriptor(
214                        self,
215                        &value[0],
216                        entry_type,
217                        &result_attrs,
218                        &result_bin,
219                        &domain,
220                    );
221                    self.aces = relations_ace;
222                }
223                _ => {}
224            }
225        }
226        
227        // Push DN and SID in HashMap
228        dn_sid.insert(
229            self.properties.distinguishedname.to_string(),
230            self.object_identifier.to_string(),
231        );
232        // Push DN and Type
233        sid_type.insert(
234            self.object_identifier.to_string(),
235            "Group".to_string(),
236        );
237        
238        // Trace and return Group struct
239        // trace!("JSON OUTPUT: {:?}",serde_json::to_string(&self).unwrap());
240        Ok(())
241    }
242}
243
244impl LdapObject for Group {
245    // To JSON
246    fn to_json(&self) -> Value {
247        serde_json::to_value(&self).unwrap()
248    }
249
250    // Get values
251    fn get_object_identifier(&self) -> &String {
252        &self.object_identifier
253    }
254    fn get_is_acl_protected(&self) -> &bool {
255        &self.is_acl_protected
256    }
257    fn get_aces(&self) -> &Vec<AceTemplate> {
258        &self.aces
259    }
260    fn get_spntargets(&self) -> &Vec<SPNTarget> {
261        panic!("Not used by current object.");
262    }
263    fn get_allowed_to_delegate(&self) -> &Vec<Member> {
264        panic!("Not used by current object.");
265    }
266    fn get_links(&self) -> &Vec<Link> {
267        panic!("Not used by current object.");
268    }
269    fn get_contained_by(&self) -> &Option<Member> {
270        &self.contained_by
271    }
272    fn get_child_objects(&self) -> &Vec<Member> {
273        panic!("Not used by current object.");
274    }
275    fn get_haslaps(&self) -> &bool {
276        &false
277    }
278    
279    // Get mutable values
280    fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
281        &mut self.aces
282    }
283    fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
284        panic!("Not used by current object.");
285    }
286    fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
287        panic!("Not used by current object.");
288    }
289    
290    // Edit values
291    fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
292        self.is_acl_protected = is_acl_protected;
293        self.properties.isaclprotected = is_acl_protected;
294    }
295    fn set_aces(&mut self, aces: Vec<AceTemplate>) {
296        self.aces = aces;
297    }
298    fn set_spntargets(&mut self, _spn_targets: Vec<SPNTarget>) {
299        // Not used by current object.
300    }
301    fn set_allowed_to_delegate(&mut self, _allowed_to_delegate: Vec<Member>) {
302        // Not used by current object.
303    }
304    fn set_links(&mut self, _links: Vec<Link>) {
305        // Not used by current object.
306    }
307    fn set_contained_by(&mut self, contained_by: Option<Member>) {
308        self.contained_by = contained_by;
309    }
310    fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
311        // Not used by current object.
312    }
313}
314
315// Group properties structure
316#[derive(Debug, Clone, Deserialize, Serialize, Default)]
317pub struct GroupProperties {
318    domain: String,
319    name: String,
320    distinguishedname: String,
321    domainsid: String,
322    isaclprotected: bool,
323    highvalue: bool,
324    samaccountname: String,
325    description: Option<String>,
326    whencreated: i64,
327    admincount: bool,
328}
329
330impl GroupProperties {
331   // Mutable access.
332   pub fn name_mut(&mut self) -> &mut String {
333      &mut self.name
334   }
335   pub fn highvalue_mut(&mut self) -> &mut bool {
336      &mut self.highvalue
337   }
338}