Skip to main content

adhammer_core/
object.rs

1//! A generic collected directory object: DN + multi-valued string attributes,
2//! plus the raw binary blobs we need to parse ourselves (objectSid, nTSecurityDescriptor).
3
4use std::collections::HashMap;
5
6/// userAccountControl bit flags (subset we act on).
7pub mod uac {
8    pub const ACCOUNTDISABLE: u32 = 0x0002;
9    pub const HOMEDIR_REQUIRED: u32 = 0x0008;
10    pub const PASSWD_NOTREQD: u32 = 0x0020;
11    pub const NORMAL_ACCOUNT: u32 = 0x0200;
12    pub const DONT_EXPIRE_PASSWORD: u32 = 0x0001_0000;
13    pub const TRUSTED_FOR_DELEGATION: u32 = 0x0008_0000; // unconstrained delegation
14    pub const NOT_DELEGATED: u32 = 0x0010_0000; // "sensitive, cannot be delegated"
15    pub const USE_DES_KEY_ONLY: u32 = 0x0020_0000;
16    pub const DONT_REQ_PREAUTH: u32 = 0x0040_0000; // AS-REP roastable
17    pub const TRUSTED_TO_AUTH_FOR_DELEGATION: u32 = 0x0100_0000; // constrained w/ protocol transition
18    pub const USE_AES: u32 = 0; // placeholder; AES lives in msDS-SupportedEncryptionTypes
19}
20
21#[derive(Clone, Debug, Default)]
22pub struct AdObject {
23    pub dn: String,
24    pub attrs: HashMap<String, Vec<String>>,
25    /// Raw binary attributes (objectSid, nTSecurityDescriptor, objectGUID, ...).
26    pub bin: HashMap<String, Vec<Vec<u8>>>,
27}
28
29impl AdObject {
30    pub fn one(&self, attr: &str) -> Option<&str> {
31        self.attrs
32            .get(attr)
33            .and_then(|v| v.first())
34            .map(String::as_str)
35    }
36
37    pub fn all(&self, attr: &str) -> &[String] {
38        self.attrs.get(attr).map(Vec::as_slice).unwrap_or(&[])
39    }
40
41    pub fn int(&self, attr: &str) -> Option<i64> {
42        self.one(attr).and_then(|s| s.parse().ok())
43    }
44
45    pub fn bin1(&self, attr: &str) -> Option<&[u8]> {
46        self.bin
47            .get(attr)
48            .and_then(|v| v.first())
49            .map(Vec::as_slice)
50    }
51
52    /// All values of a multi-valued binary attribute (e.g. sIDHistory).
53    pub fn bin_all(&self, attr: &str) -> &[Vec<u8>] {
54        self.bin.get(attr).map(Vec::as_slice).unwrap_or(&[])
55    }
56
57    pub fn uac(&self) -> u32 {
58        self.int("userAccountControl").unwrap_or(0) as u32
59    }
60
61    pub fn has_class(&self, class: &str) -> bool {
62        self.all("objectClass")
63            .iter()
64            .any(|c| c.eq_ignore_ascii_case(class))
65    }
66
67    /// FILETIME attribute (pwdLastSet, lastLogonTimestamp) as raw 100ns ticks since 1601.
68    pub fn filetime(&self, attr: &str) -> Option<i64> {
69        self.int(attr)
70    }
71}