rusthound_ce/objects/
fsp.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::utils::date::string_to_epoch;
19use crate::enums::secdesc::LdapSid;
20use crate::enums::sid::{objectsid_to_vec8, sid_maker};
21
22/// FSP (ForeignSecurityPrincipal) structure
23#[derive(Debug, Clone, Deserialize, Serialize, Default)]
24pub struct Fsp {
25    #[serde(rename = "Properties")]
26    properties: FspProperties,
27    #[serde(rename = "Aces")]
28    aces: Vec<AceTemplate>,
29    #[serde(rename = "ObjectIdentifier")]
30    object_identifier: String,
31    #[serde(rename = "IsDeleted")]
32    is_deleted: bool,
33    #[serde(rename = "IsACLProtected")]
34    is_acl_protected: bool,
35    #[serde(rename = "ContainedBy")]
36    contained_by: Option<Member>,
37}
38
39impl Fsp {
40    // New FSP
41    pub fn new() -> Self { 
42        Self { ..Default::default() } 
43    }
44
45    /// Function to parse and replace value in json template for ForeignSecurityPrincipal object.
46    pub fn parse(
47        &mut self,
48        result: SearchEntry,
49        domain: &String,
50        dn_sid: &mut HashMap<String, String>,
51        sid_type: &mut HashMap<String, String>,
52    ) -> Result<(), Box<dyn Error>> {
53        let result_dn: String = result.dn.to_uppercase();
54        let result_attrs: HashMap<String, Vec<String>> = result.attrs;
55        let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
56        
57        // Debug for current object
58        debug!("Parse ForeignSecurityPrincipal: {}", result_dn);
59        // Trace all result attributes
60        for (key, value) in &result_attrs {
61            trace!("  {:?}:{:?}", key, value);
62        }
63        // Trace all bin result attributes
64        for (key, value) in &result_bin {
65            trace!("  {:?}:{:?}", key, value);
66        }
67        
68        // Change all values...
69        self.properties.domain = domain.to_uppercase();
70        self.properties.distinguishedname = result_dn;    
71        
72        #[allow(unused_assignments)]
73        let mut sid: String = "".to_owned();
74        let mut ftype: &str = "Base";
75        
76        // With a check
77        for (key, value) in &result_attrs {
78            match key.as_str() {
79                "name" => {
80                    let name = format!("{}-{}", domain, &value.get(0).unwrap_or(&"".to_owned()));
81                    self.properties.name = name.to_uppercase();
82        
83                    // Type for group Member maker
84                    // based on https://docs.microsoft.com/fr-fr/troubleshoot/windows-server/identity/security-identifiers-in-windows
85                    let split = value[0].split("-").collect::<Vec<&str>>();
86
87                    // Not currently used:
88                    //let last = split.iter().last().unwrap_or(&"0").parse::<i32>().unwrap_or(0);
89                    if split.len() >= 17 {
90                        ftype = "User";
91                    } else {
92                        ftype = "Group";
93                    }
94                }
95                "whenCreated" => {
96                    let epoch = string_to_epoch(&value[0])?;
97                    if epoch.is_positive() {
98                        self.properties.whencreated = epoch;
99                    }
100                }
101                "objectSid" => {
102                    //objectSid to vec and raw to string
103                    let vec_sid = objectsid_to_vec8(&value[0]);
104                    sid = sid_maker(LdapSid::parse(&vec_sid).unwrap().1, domain);
105                    self.object_identifier = sid.to_owned();
106        
107                    let re = Regex::new(r"^S-[0-9]{1}-[0-9]{1}-[0-9]{1,}-[0-9]{1,}-[0-9]{1,}-[0-9]{1,}")?;
108                    for domain_sid in re.captures_iter(&sid) 
109                    {
110                        self.properties.domainsid = domain_sid[0].to_owned().to_string();
111                    }
112                }
113                "IsDeleted" => {
114                    self.is_deleted = true.into();
115                }
116                _ => {}
117            }
118        }
119        
120        // Push DN and SID in HashMap
121        if self.object_identifier.to_string() != "SID" {
122            dn_sid.insert(
123                self.properties.distinguishedname.to_string(),
124                self.object_identifier.to_string()
125            );
126            // Push DN and Type
127            sid_type.insert(
128                self.object_identifier.to_string(),
129                ftype.to_string()
130            );
131        }
132        
133        // Trace and return Fsp struct
134        // trace!("JSON OUTPUT: {:?}",serde_json::to_string(&self).unwrap());
135        Ok(())
136    }
137}
138
139/// Default FSP properties structure
140#[derive(Debug, Clone, Deserialize, Serialize, Default)]
141pub struct FspProperties {
142   domain: String,
143   name: String,
144   distinguishedname: String,
145   domainsid: String,
146   isaclprotected: bool,
147   highvalue: bool,
148   description: Option<String>,
149   whencreated: i64,
150}
151
152impl FspProperties {
153   // New default properties.
154   pub fn new(domain: String) -> Self { 
155      Self { 
156         domain,
157         whencreated: -1,
158         ..Default::default() }
159   }
160
161   // Immutable access.
162   pub fn domain(&self) -> &String {
163      &self.domain
164   }
165   pub fn name(&self) -> &String {
166      &self.name
167   }
168   pub fn distinguishedname(&self) -> &String {
169      &self.distinguishedname
170   }
171   pub fn domainsid(&self) -> &String {
172      &self.domainsid
173   }
174   pub fn highvalue(&self) -> &bool {
175      &self.highvalue
176   }
177   pub fn description(&self) -> &Option<String> {
178      &self.description
179   }
180   pub fn whencreated(&self) -> &i64 {
181      &self.whencreated
182   }
183
184   // Mutable access.
185   pub fn domain_mut(&mut self) -> &mut String {
186      &mut self.domain
187   }
188   pub fn name_mut(&mut self) -> &mut String {
189      &mut self.name
190   }
191   pub fn distinguishedname_mut(&mut self) -> &mut String {
192      &mut self.distinguishedname
193   }
194   pub fn domainsid_mut(&mut self) -> &mut String {
195      &mut self.domainsid
196   }
197   pub fn highvalue_mut(&mut self) -> &mut bool {
198      &mut self.highvalue
199   }
200   pub fn description_mut(&mut self) -> &mut Option<String> {
201      &mut self.description
202   }
203   pub fn whencreated_mut(&mut self) -> &mut i64 {
204      &mut self.whencreated
205   }
206}
207
208impl LdapObject for Fsp {
209    // To JSON
210    fn to_json(&self) -> Value {
211        serde_json::to_value(&self).unwrap()
212    }
213
214    // Get values
215    fn get_object_identifier(&self) -> &String {
216        &self.object_identifier
217    }
218    fn get_is_acl_protected(&self) -> &bool {
219        &self.is_acl_protected
220    }
221    fn get_aces(&self) -> &Vec<AceTemplate> {
222        &self.aces
223    }
224    fn get_spntargets(&self) -> &Vec<SPNTarget> {
225        panic!("Not used by current object.");
226    }
227    fn get_allowed_to_delegate(&self) -> &Vec<Member> {
228        panic!("Not used by current object.");
229    }
230    fn get_links(&self) -> &Vec<Link> {
231        panic!("Not used by current object.");
232    }
233    fn get_contained_by(&self) -> &Option<Member> {
234        &self.contained_by
235    }
236    fn get_child_objects(&self) -> &Vec<Member> {
237        panic!("Not used by current object.");
238    }
239    fn get_haslaps(&self) -> &bool {
240        &false
241    }
242    
243    // Get mutable values
244    fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
245        &mut self.aces
246    }
247    fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
248        panic!("Not used by current object.");
249    }
250    fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
251        panic!("Not used by current object.");
252    }
253    
254    // Edit values
255    fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
256        self.is_acl_protected = is_acl_protected;
257        self.properties.isaclprotected = is_acl_protected;
258    }
259    fn set_aces(&mut self, aces: Vec<AceTemplate>) {
260        self.aces = aces;
261    }
262    fn set_spntargets(&mut self, _spn_targets: Vec<SPNTarget>) {
263        // Not used by current object.
264    }
265    fn set_allowed_to_delegate(&mut self, _allowed_to_delegate: Vec<Member>) {
266        // Not used by current object.
267    }
268    fn set_links(&mut self, _links: Vec<Link>) {
269        // Not used by current object.
270    }
271    fn set_contained_by(&mut self, contained_by: Option<Member>) {
272        self.contained_by = contained_by;
273    }
274    fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
275        // Not used by current object.
276    }
277}