1use serde::{Deserialize, Serialize};
2use serde_json::value::Value;
3use ldap3::SearchEntry;
4use log::{debug, trace};
5use std::collections::HashMap;
6use std::error::Error;
7
8use crate::enums::{decode_guid_le, group_scope};
9use crate::enums::regex::OBJECT_SID_RE1;
10use crate::objects::common::{LdapObject, AceTemplate, SPNTarget, Link, Member};
11use crate::enums::acl::parse_ntsecuritydescriptor;
12use crate::enums::secdesc::LdapSid;
13use crate::enums::sid::{objectsid_to_vec8, sid_maker};
14use crate::utils::date::string_to_epoch;
15
16#[derive(Debug, Clone, Deserialize, Serialize, Default)]
18pub struct Group {
19 #[serde(rename = "ObjectIdentifier")]
20 object_identifier: String,
21 #[serde(rename = "IsDeleted")]
22 is_deleted: bool,
23 #[serde(rename = "IsACLProtected")]
24 is_acl_protected: bool,
25 #[serde(rename = "Properties")]
26 properties: GroupProperties,
27 #[serde(rename = "Members")]
28 members: Vec<Member>,
29 #[serde(rename = "HasSIDHistory")]
30 has_sid_history: Vec<Member>,
31 #[serde(rename = "Aces")]
32 aces: Vec<AceTemplate>,
33 #[serde(rename = "ContainedBy")]
34 contained_by: Option<Member>,
35}
36
37impl Group {
38 pub fn new() -> Self {
40 Self { ..Default::default() }
41 }
42
43 pub fn members(&self) -> &Vec<Member> {
45 &self.members
46 }
47 pub fn properties(&self) -> &GroupProperties {
48 &self.properties
49 }
50 pub fn object_identifier(&self) -> &String {
51 &self.object_identifier
52 }
53
54 pub fn properties_mut(&mut self) -> &mut GroupProperties {
56 &mut self.properties
57 }
58 pub fn object_identifier_mut(&mut self) -> &mut String {
59 &mut self.object_identifier
60 }
61 pub fn members_mut(&mut self) -> &mut Vec<Member> {
62 &mut self.members
63 }
64 pub fn has_sid_history_mut(&mut self) -> &mut Vec<Member> {
65 &mut self.has_sid_history
66 }
67
68 pub fn parse(
71 &mut self,
72 result: SearchEntry,
73 domain: &str,
74 dn_sid: &mut HashMap<String, String>,
75 sid_type: &mut HashMap<String, String>,
76 domain_sid: &str,
77 schema_guid_map: &HashMap<String, String>,
78 ) -> Result<(), Box<dyn Error>> {
79 let result_dn: String = result.dn.to_uppercase();
80 let result_attrs: HashMap<String, Vec<String>> = result.attrs;
81 let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
82
83 debug!("Parse group: {result_dn}");
84
85 for (key, value) in &result_attrs {
87 trace!(" {key:?}:{value:?}");
88 }
89 for (key, value) in &result_bin {
91 trace!(" {key:?}:{value:?}");
92 }
93
94 self.properties.domain = domain.to_uppercase();
96 self.properties.distinguishedname = result_dn;
97 self.properties.domainsid = domain_sid.to_string();
98
99 for (key, value) in &result_attrs {
101 match key.as_str() {
102 "name" => {
103 let name = &value[0];
104 let email = format!("{}@{}", name.to_owned(), domain);
105 self.properties.name = email.to_uppercase();
106 }
107 "description" => {
108 self.properties.description = Some(value[0].to_owned());
109 }
110 "adminCount" => {
111 let admincount = value[0].parse::<i32>().unwrap_or(0) != 0;
115 self.properties.admincount = admincount;
116 self.properties.adminsdholderprotected = admincount;
117 }
118 "groupType" => {
119 self.properties.groupscope = group_scope(value[0].parse::<i64>().unwrap_or(0));
120 }
121 "sAMAccountName" => {
122 self.properties.samaccountname = value[0].to_owned();
123 }
124 "member" => {
125 if !value.is_empty() {
126 let mut _vec_members: Vec<Member> = Vec::new();
127
128 for member in value {
129 let _member = member.trim();
130 if _member.is_empty() {
131 continue;
132 }
133 if _member.eq_ignore_ascii_case("SID") {
134 continue;
135 }
136
137 let mut m = Member::new();
138 *m.object_identifier_mut() = _member.to_uppercase();
139 _vec_members.push(m);
140 }
141
142 self.members = _vec_members;
143 }
144 }
145 "objectSid" => {
146 let vec_sid = objectsid_to_vec8(&value[0]);
148 let sid = sid_maker(LdapSid::parse(&vec_sid).unwrap().1, domain);
149 self.object_identifier = sid.to_owned();
150
151 if sid.ends_with("-512")
159 || sid.ends_with("-516")
160 || sid.ends_with("-519")
161 || sid.ends_with("-520")
162 {
163 self.properties.highvalue = true;
164 } else if sid.ends_with("S-1-5-32-544")
165 || sid.ends_with("S-1-5-32-548")
166 || sid.ends_with("S-1-5-32-549")
167 || sid.ends_with("S-1-5-32-550")
168 || sid.ends_with("S-1-5-32-551")
169 {
170 self.properties.highvalue = true;
171 } else {
172 self.properties.highvalue = false;
173 }
174 }
175 "whenCreated" => {
176 let epoch = string_to_epoch(&value[0])?;
177 if epoch.is_positive() {
178 self.properties.whencreated = epoch;
179 }
180 }
181 "isDeleted" => {
182 self.is_deleted = true;
183 }
184 _ => {}
185 }
186 }
187
188 for (key, value) in &result_bin {
190 match key.as_str() {
191 "objectGUID" => {
192 let guid = decode_guid_le(&value[0]);
194 self.properties.objectguid = guid;
195 }
196 "objectSid" => {
197 let sid = sid_maker(LdapSid::parse(&value[0]).unwrap().1, domain);
199 self.object_identifier = sid.to_owned();
200
201 for domain_sid in OBJECT_SID_RE1.captures_iter(&sid) {
202 self.properties.domainsid = domain_sid[0].to_owned().to_string();
203 }
204
205 if sid.ends_with("-512")
207 || sid.ends_with("-516")
208 || sid.ends_with("-519")
209 || sid.ends_with("-520")
210 {
211 self.properties.highvalue = true;
212 }
213 else if sid.ends_with("S-1-5-32-544")
214 || sid.ends_with("S-1-5-32-548")
215 || sid.ends_with("S-1-5-32-549")
216 || sid.ends_with("S-1-5-32-550")
217 || sid.ends_with("S-1-5-32-551")
218 {
219 self.properties.highvalue = true;
220 }
221 else {
222 self.properties.highvalue = false;
223 }
224 }
225 "nTSecurityDescriptor" => {
226 let relations_ace = parse_ntsecuritydescriptor(
228 self,
229 &value[0],
230 "Group",
231 &result_attrs,
232 &result_bin,
233 domain,
234 schema_guid_map,
235 );
236 self.aces = relations_ace;
237 }
238 "sIDHistory" => {
239 let mut list_sid_history: Vec<String> = Vec::new();
240 let mut has_sid_history: Vec<Member> = Vec::new();
241 for bsid in value {
242 debug!("sIDHistory: {:?}", &bsid);
243 let sid = sid_maker(LdapSid::parse(bsid).unwrap().1, domain);
244 let mut member = Member::new();
245 *member.object_identifier_mut() = sid.clone();
246 has_sid_history.push(member);
247 list_sid_history.push(sid);
248 }
249 self.properties.sidhistory = list_sid_history;
250 self.has_sid_history = has_sid_history;
251 }
252 _ => {}
253 }
254 }
255
256 dn_sid.insert(
258 self.properties.distinguishedname.to_string(),
259 self.object_identifier.to_string(),
260 );
261 sid_type.insert(
263 self.object_identifier.to_string(),
264 "Group".to_string(),
265 );
266
267 Ok(())
270 }
271}
272
273impl LdapObject for Group {
274 fn to_json(&self) -> Value {
276 serde_json::to_value(self).unwrap()
277 }
278
279 fn get_object_identifier(&self) -> &String {
281 &self.object_identifier
282 }
283 fn get_is_acl_protected(&self) -> &bool {
284 &self.is_acl_protected
285 }
286 fn get_aces(&self) -> &Vec<AceTemplate> {
287 &self.aces
288 }
289 fn get_spntargets(&self) -> &Vec<SPNTarget> {
290 panic!("Not used by current object.");
291 }
292 fn get_allowed_to_delegate(&self) -> &Vec<Member> {
293 panic!("Not used by current object.");
294 }
295 fn get_links(&self) -> &Vec<Link> {
296 panic!("Not used by current object.");
297 }
298 fn get_contained_by(&self) -> &Option<Member> {
299 &self.contained_by
300 }
301 fn get_child_objects(&self) -> &Vec<Member> {
302 panic!("Not used by current object.");
303 }
304 fn get_haslaps(&self) -> &bool {
305 &false
306 }
307
308 fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
310 &mut self.aces
311 }
312 fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
313 panic!("Not used by current object.");
314 }
315 fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
316 panic!("Not used by current object.");
317 }
318
319 fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
321 self.is_acl_protected = is_acl_protected;
322 self.properties.isaclprotected = is_acl_protected;
323 }
324 fn set_aces(&mut self, aces: Vec<AceTemplate>) {
325 self.aces = aces;
326 }
327 fn set_spntargets(&mut self, _spn_targets: Vec<SPNTarget>) {
328 }
330 fn set_allowed_to_delegate(&mut self, _allowed_to_delegate: Vec<Member>) {
331 }
333 fn set_links(&mut self, _links: Vec<Link>) {
334 }
336 fn set_contained_by(&mut self, contained_by: Option<Member>) {
337 self.contained_by = contained_by;
338 }
339 fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
340 }
342 fn set_owner_rights_flags(&mut self, any: bool, any_inherited: bool) {
343 self.properties.doesanyacegrantownerrights = any;
344 self.properties.doesanyinheritedacegrantownerrights = any_inherited;
345 }
346}
347
348#[derive(Debug, Clone, Deserialize, Serialize, Default)]
350pub struct GroupProperties {
351 domain: String,
352 name: String,
353 distinguishedname: String,
354 domainsid: String,
355 objectguid: String,
356 doesanyacegrantownerrights: bool,
357 doesanyinheritedacegrantownerrights: bool,
358 isaclprotected: bool,
359 highvalue: bool,
360 samaccountname: String,
361 description: Option<String>,
362 whencreated: i64,
363 admincount: bool,
364 adminsdholderprotected: bool,
365 groupscope: String,
366 sidhistory: Vec<String>,
367}
368
369impl GroupProperties {
370 pub fn name(&self) -> &String {
372 &self.name
373 }
374
375 pub fn name_mut(&mut self) -> &mut String {
377 &mut self.name
378 }
379 pub fn domain_mut(&mut self) -> &mut String {
380 &mut self.domain
381 }
382 pub fn domainsid_mut(&mut self) -> &mut String {
383 &mut self.domainsid
384 }
385 pub fn highvalue_mut(&mut self) -> &mut bool {
386 &mut self.highvalue
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393
394 #[test]
395 fn group_type_maps_to_bloodhound_scope() {
396 assert_eq!(group_scope(-2147483646), "Global"); assert_eq!(group_scope(-2147483643), "DomainLocal"); assert_eq!(group_scope(-2147483644), "DomainLocal"); assert_eq!(group_scope(-2147483640), "Universal"); assert_eq!(group_scope(2), "Global"); assert_eq!(group_scope(8), "Universal"); assert_eq!(group_scope(0), "");
404 }
405}