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::enums::decode_guid_le;
9use crate::enums::regex::OBJECT_SID_RE1;
10use crate::objects::common::{LdapObject, AceTemplate, SPNTarget, Link, Member};
11use crate::utils::date::string_to_epoch;
12use crate::enums::secdesc::LdapSid;
13use crate::enums::sid::{objectsid_to_vec8, sid_maker};
14
15#[derive(Debug, Clone, Deserialize, Serialize, Default)]
17pub struct Fsp {
18 #[serde(rename = "Properties")]
19 properties: FspProperties,
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 Fsp {
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 ) -> Result<(), Box<dyn Error>> {
47 let result_dn: String = result.dn.to_uppercase();
48 let result_attrs: HashMap<String, Vec<String>> = result.attrs;
49 let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
50
51 debug!("Parse ForeignSecurityPrincipal: {result_dn}");
53
54 for (key, value) in &result_attrs {
56 trace!(" {key:?}:{value:?}");
57 }
58 for (key, value) in &result_bin {
60 trace!(" {key:?}:{value:?}");
61 }
62
63 self.properties.domain = domain.to_uppercase();
65 self.properties.distinguishedname = result_dn;
66 self.properties.domainsid = domain_sid.to_string();
67
68 #[allow(unused_assignments)]
69 let mut sid: String = "".to_owned();
70 let mut ftype: &str = "Base";
71
72 for (key, value) in &result_attrs {
74 match key.as_str() {
75 "name" => {
76 let name = format!("{}-{}", domain, &value.first().unwrap_or(&"".to_owned()));
77 self.properties.name = name.to_uppercase();
78
79 let split = value[0].split("-").collect::<Vec<&str>>();
82
83 if split.len() >= 17 {
86 ftype = "User";
87 } else {
88 ftype = "Group";
89 }
90 }
91 "whenCreated" => {
92 let epoch = string_to_epoch(&value[0])?;
93 if epoch.is_positive() {
94 self.properties.whencreated = epoch;
95 }
96 }
97 "objectSid" => {
98 let vec_sid = objectsid_to_vec8(&value[0]);
100 sid = sid_maker(LdapSid::parse(&vec_sid).unwrap().1, domain);
101 self.object_identifier = sid.to_owned();
102
103 for domain_sid in OBJECT_SID_RE1.captures_iter(&sid) {
104 self.properties.domainsid = domain_sid[0].to_owned().to_string();
105 }
106 }
107 "isDeleted" => {
108 self.is_deleted = true;
109 }
110 _ => {}
111 }
112 }
113
114
115 for (key, value) in &result_bin {
117 match key.as_str() {
118 "objectGUID" => {
119 let guid = decode_guid_le(&value[0]);
121 self.properties.objectguid = guid;
122 }
123 "objectSid" => {
124 let sid = sid_maker(LdapSid::parse(&value[0]).unwrap().1, domain);
127 self.object_identifier = sid.to_owned();
128
129 for domain_sid in OBJECT_SID_RE1.captures_iter(&sid) {
130 self.properties.domainsid = domain_sid[0].to_owned().to_string();
131 }
132 }
133 _ => {}
134 }
135 }
136
137 if self.object_identifier != "SID" {
139 dn_sid.insert(
140 self.properties.distinguishedname.to_string(),
141 self.object_identifier.to_string()
142 );
143 sid_type.insert(self.object_identifier.to_string(), ftype.to_string());
145 }
146
147 Ok(())
150 }
151}
152
153#[derive(Debug, Clone, Deserialize, Serialize, Default)]
155pub struct FspProperties {
156 domain: String,
157 name: String,
158 distinguishedname: String,
159 domainsid: String,
160 objectguid: String,
161 doesanyacegrantownerrights: bool,
162 doesanyinheritedacegrantownerrights: bool,
163 isaclprotected: bool,
164 highvalue: bool,
165 description: Option<String>,
166 whencreated: i64,
167}
168
169impl FspProperties {
170 pub fn new(domain: String) -> Self {
172 Self {
173 domain,
174 whencreated: -1,
175 ..Default::default() }
176 }
177
178 pub fn domain(&self) -> &String {
180 &self.domain
181 }
182 pub fn name(&self) -> &String {
183 &self.name
184 }
185 pub fn distinguishedname(&self) -> &String {
186 &self.distinguishedname
187 }
188 pub fn domainsid(&self) -> &String {
189 &self.domainsid
190 }
191 pub fn highvalue(&self) -> &bool {
192 &self.highvalue
193 }
194 pub fn description(&self) -> &Option<String> {
195 &self.description
196 }
197 pub fn whencreated(&self) -> &i64 {
198 &self.whencreated
199 }
200
201 pub fn domain_mut(&mut self) -> &mut String {
203 &mut self.domain
204 }
205 pub fn name_mut(&mut self) -> &mut String {
206 &mut self.name
207 }
208 pub fn distinguishedname_mut(&mut self) -> &mut String {
209 &mut self.distinguishedname
210 }
211 pub fn domainsid_mut(&mut self) -> &mut String {
212 &mut self.domainsid
213 }
214 pub fn highvalue_mut(&mut self) -> &mut bool {
215 &mut self.highvalue
216 }
217 pub fn description_mut(&mut self) -> &mut Option<String> {
218 &mut self.description
219 }
220 pub fn whencreated_mut(&mut self) -> &mut i64 {
221 &mut self.whencreated
222 }
223}
224
225impl LdapObject for Fsp {
226 fn to_json(&self) -> Value {
228 serde_json::to_value(self).unwrap()
229 }
230
231 fn get_object_identifier(&self) -> &String {
233 &self.object_identifier
234 }
235 fn get_is_acl_protected(&self) -> &bool {
236 &self.is_acl_protected
237 }
238 fn get_aces(&self) -> &Vec<AceTemplate> {
239 &self.aces
240 }
241 fn get_spntargets(&self) -> &Vec<SPNTarget> {
242 panic!("Not used by current object.");
243 }
244 fn get_allowed_to_delegate(&self) -> &Vec<Member> {
245 panic!("Not used by current object.");
246 }
247 fn get_links(&self) -> &Vec<Link> {
248 panic!("Not used by current object.");
249 }
250 fn get_contained_by(&self) -> &Option<Member> {
251 &self.contained_by
252 }
253 fn get_child_objects(&self) -> &Vec<Member> {
254 panic!("Not used by current object.");
255 }
256 fn get_haslaps(&self) -> &bool {
257 &false
258 }
259
260 fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
262 &mut self.aces
263 }
264 fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
265 panic!("Not used by current object.");
266 }
267 fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
268 panic!("Not used by current object.");
269 }
270
271 fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
273 self.is_acl_protected = is_acl_protected;
274 self.properties.isaclprotected = is_acl_protected;
275 }
276 fn set_aces(&mut self, aces: Vec<AceTemplate>) {
277 self.aces = aces;
278 }
279 fn set_spntargets(&mut self, _spn_targets: Vec<SPNTarget>) {
280 }
282 fn set_allowed_to_delegate(&mut self, _allowed_to_delegate: Vec<Member>) {
283 }
285 fn set_links(&mut self, _links: Vec<Link>) {
286 }
288 fn set_contained_by(&mut self, contained_by: Option<Member>) {
289 self.contained_by = contained_by;
290 }
291 fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
292 }
294 fn set_owner_rights_flags(&mut self, any: bool, any_inherited: bool) {
295 self.properties.doesanyacegrantownerrights = any;
296 self.properties.doesanyinheritedacegrantownerrights = any_inherited;
297 }
298}
299
300#[cfg(test)]
301mod tests {
302 use super::Fsp;
303 use crate::json::checker::common::replace_sid_members;
304 use crate::objects::{common::Member, group::Group};
305 use ldap3::SearchEntry;
306 use std::collections::HashMap;
307
308 const GUID: [u8; 16] = [
309 0x58, 0xEC, 0x7B, 0xF7, 0xA7, 0x73, 0x8D, 0x40,
310 0xAF, 0x0D, 0x42, 0xF0, 0xD7, 0x2C, 0x71, 0x14,
311 ];
312 const GUID_STR: &str = "F77BEC58-73A7-408D-AF0D-42F0D72C7114";
313
314 fn sid_bytes(sub_authorities: &[u32]) -> Vec<u8> {
316 let mut b = vec![1, sub_authorities.len() as u8, 0, 0, 0, 0, 0, 5];
317 for s in sub_authorities {
318 b.extend_from_slice(&s.to_le_bytes());
319 }
320 b
321 }
322
323 fn parse(entry: SearchEntry) -> (Fsp, HashMap<String, String>) {
324 let mut fsp = Fsp::new();
325 let mut dn_sid = HashMap::new();
326 let mut sid_type = HashMap::new();
327 fsp.parse(entry, "example.local", &mut dn_sid, &mut sid_type, "S-1-5-21-1-2-3")
328 .unwrap();
329 (fsp, dn_sid)
330 }
331
332 fn foreign_principal() -> (String, SearchEntry) {
335 let sid = "S-1-5-21-1111111111-2222222222-3333333333-1108";
336 let bytes = sid_bytes(&[21, 1111111111, 2222222222, 3333333333, 1108]);
337 assert!(std::str::from_utf8(&bytes).is_err(), "delivered in bin_attrs");
338 let dn = format!("CN={sid},CN=ForeignSecurityPrincipals,DC=example,DC=local");
339 let entry = SearchEntry {
340 dn: dn.clone(),
341 attrs: HashMap::from([("name".to_string(), vec![sid.to_string()])]),
342 bin_attrs: HashMap::from([
343 ("objectSid".to_string(), vec![bytes]),
344 ("objectGUID".to_string(), vec![GUID.to_vec()]),
345 ]),
346 };
347 (dn.to_uppercase(), entry)
348 }
349
350 #[test]
351 fn domain_sid_from_bin_attrs_is_the_identifier() {
352 let (dn, entry) = foreign_principal();
353 let (fsp, dn_sid) = parse(entry);
354 let sid = "S-1-5-21-1111111111-2222222222-3333333333-1108";
355 assert_eq!(fsp.object_identifier, sid);
356 assert_eq!(fsp.properties.objectguid, GUID_STR);
357 assert_eq!(fsp.properties.domainsid, "S-1-5-21-1111111111-2222222222-3333333333");
358 assert_eq!(dn_sid.get(&dn).map(String::as_str), Some(sid));
359 }
360
361 #[test]
362 fn well_known_sid_from_attrs_is_not_overwritten_by_the_guid() {
363 let raw = String::from_utf8(sid_bytes(&[11])).unwrap();
366 let entry = SearchEntry {
367 dn: "CN=S-1-5-11,CN=ForeignSecurityPrincipals,DC=example,DC=local".to_string(),
368 attrs: HashMap::from([
369 ("name".to_string(), vec!["S-1-5-11".to_string()]),
370 ("objectSid".to_string(), vec![raw]),
371 ]),
372 bin_attrs: HashMap::from([("objectGUID".to_string(), vec![GUID.to_vec()])]),
373 };
374 let (fsp, _) = parse(entry);
375 assert_eq!(fsp.object_identifier, "EXAMPLE.LOCAL-S-1-5-11");
376 assert_eq!(fsp.properties.objectguid, GUID_STR);
377 }
378
379 #[test]
380 fn group_member_from_a_trusted_forest_resolves_to_its_sid() {
381 let (dn, entry) = foreign_principal();
382 let mut fsp = Fsp::new();
383 let mut dn_sid = HashMap::new();
384 let mut sid_type = HashMap::new();
385 fsp.parse(entry, "example.local", &mut dn_sid, &mut sid_type, "S-1-5-21-1-2-3")
386 .unwrap();
387
388 let mut group = Group::new();
389 let mut member = Member::new();
390 *member.object_identifier_mut() = dn;
391 group.members_mut().push(member);
392 let mut groups = vec![group];
393 replace_sid_members(&mut groups, &dn_sid, &sid_type, &[]).unwrap();
394
395 assert_eq!(
396 groups[0].members()[0].object_identifier(),
397 "S-1-5-21-1111111111-2222222222-3333333333-1108"
398 );
399 }
400}