Skip to main content

ad_acl/
catalog.rs

1//! Fixed, forest-independent GUIDs that appear in AD object ACEs.
2//!
3//! Only GUIDs that are identical in every forest live here. Anything whose `schemaIDGUID`
4//! is generated at schema-extension time — LAPS (`ms-Mcs-AdmPwd`, `msLAPS-*`), gMSA/dMSA
5//! attributes and classes — is deliberately absent: resolve those at runtime with
6//! [`crate::SchemaMap`], which reads them from the forest schema.
7
8use windows_sddl::sid::Guid;
9
10/// What an `object_type` GUID in an object-ACE denotes.
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum GuidClass {
13    /// Control-access right — meaningful with `CONTROL_ACCESS`.
14    ExtendedRight,
15    /// Single attribute — meaningful with `READ_PROP` / `WRITE_PROP`.
16    Attribute,
17    /// Validated write — meaningful with `SELF`.
18    ValidatedWrite,
19}
20
21/// One catalog entry: a GUID with a name and the mask bit it pairs with.
22pub struct KnownGuid {
23    pub name: &'static str,
24    pub class: GuidClass,
25    guid: &'static str,
26}
27
28impl KnownGuid {
29    const fn new(name: &'static str, class: GuidClass, guid: &'static str) -> Self {
30        KnownGuid { name, class, guid }
31    }
32    pub fn guid(&self) -> Guid {
33        Guid::parse(self.guid).expect("static catalog GUID is valid")
34    }
35    pub fn matches(&self, g: &Guid) -> bool {
36        self.guid() == *g
37    }
38}
39
40use GuidClass::{Attribute, ExtendedRight, ValidatedWrite};
41
42// ── extended rights ───────────────────────────────────────────────────────────
43
44/// `DS-Replication-Get-Changes` — half of DCSync.
45pub const REPL_GET_CHANGES: KnownGuid = KnownGuid::new(
46    "DS-Replication-Get-Changes",
47    ExtendedRight,
48    "1131f6aa-9c07-11d1-f79f-00c04fc2dcd2",
49);
50/// `DS-Replication-Get-Changes-All` — the half that carries secrets.
51pub const REPL_GET_CHANGES_ALL: KnownGuid = KnownGuid::new(
52    "DS-Replication-Get-Changes-All",
53    ExtendedRight,
54    "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2",
55);
56/// `DS-Replication-Get-Changes-In-Filtered-Set` — RODC-filtered replication.
57pub const REPL_GET_CHANGES_FILTERED: KnownGuid = KnownGuid::new(
58    "DS-Replication-Get-Changes-In-Filtered-Set",
59    ExtendedRight,
60    "89e95b76-444d-4c62-991a-0facbeda640c",
61);
62/// `User-Force-Change-Password` — reset a password without knowing the old one.
63pub const FORCE_CHANGE_PASSWORD: KnownGuid = KnownGuid::new(
64    "User-Force-Change-Password",
65    ExtendedRight,
66    "00299570-246d-11d0-a768-00aa006e0529",
67);
68/// `Reanimate-Tombstones` — resurrect deleted objects (revives stale privilege).
69pub const REANIMATE_TOMBSTONES: KnownGuid = KnownGuid::new(
70    "Reanimate-Tombstones",
71    ExtendedRight,
72    "45ec5156-db7e-47bb-b53f-dbeb2d03c40f",
73);
74/// `Certificate-Enrollment` on an AD CS template.
75pub const ENROLLMENT: KnownGuid = KnownGuid::new(
76    "Certificate-Enrollment",
77    ExtendedRight,
78    "0e10c968-78fb-11d2-90d4-00c04f79dc55",
79);
80/// `Certificate-AutoEnrollment` on an AD CS template.
81pub const AUTO_ENROLLMENT: KnownGuid = KnownGuid::new(
82    "Certificate-AutoEnrollment",
83    ExtendedRight,
84    "a05b8cc2-17bc-4802-a710-e7c15ab866a2",
85);
86
87// ── attributes ────────────────────────────────────────────────────────────────
88
89/// `member` — write it to add anyone to a group.
90pub const MEMBER: KnownGuid =
91    KnownGuid::new("member", Attribute, "bf9679c0-0de6-11d0-a285-00aa003049e2");
92/// `msDS-KeyCredentialLink` — write it for Shadow Credentials.
93pub const KEY_CREDENTIAL_LINK: KnownGuid = KnownGuid::new(
94    "msDS-KeyCredentialLink",
95    Attribute,
96    "5b47d60f-6090-40b2-9f37-2a4de88f3063",
97);
98/// `msDS-AllowedToActOnBehalfOfOtherIdentity` — write it for RBCD.
99pub const RBCD: KnownGuid = KnownGuid::new(
100    "msDS-AllowedToActOnBehalfOfOtherIdentity",
101    Attribute,
102    "3f78c3e5-f79a-46bd-a0b8-9d18116ddc79",
103);
104/// `servicePrincipalName` — write it to make an account Kerberoastable.
105pub const SPN: KnownGuid = KnownGuid::new(
106    "servicePrincipalName",
107    Attribute,
108    "f3a64788-5306-11d1-a9c5-0000f80367c1",
109);
110/// `altSecurityIdentities` — write it to bind an attacker certificate to the account.
111pub const ALT_SECURITY_IDENTITIES: KnownGuid = KnownGuid::new(
112    "altSecurityIdentities",
113    Attribute,
114    "00fbf30c-91fe-11d1-aebc-0000f80367c1",
115);
116/// `msDS-AllowedToDelegateTo` — write it for constrained delegation abuse.
117pub const ALLOWED_TO_DELEGATE_TO: KnownGuid = KnownGuid::new(
118    "msDS-AllowedToDelegateTo",
119    Attribute,
120    "800d94d7-b7a1-42a1-b14d-7cae1423d07f",
121);
122/// `gPLink` — write it on an OU to attach a hostile GPO.
123pub const GP_LINK: KnownGuid =
124    KnownGuid::new("gPLink", Attribute, "f30e3bbe-9ff0-11d1-b603-0000f80367c1");
125
126// ── validated writes ──────────────────────────────────────────────────────────
127
128/// `Self-Membership` — add *yourself* to a group (shares the `member` GUID).
129pub const SELF_MEMBERSHIP: KnownGuid = KnownGuid::new(
130    "Self-Membership",
131    ValidatedWrite,
132    "bf9679c0-0de6-11d0-a285-00aa003049e2",
133);
134/// `Validated-SPN` — set an SPN on yourself (shares the `servicePrincipalName` GUID).
135pub const VALIDATED_SPN: KnownGuid = KnownGuid::new(
136    "Validated-SPN",
137    ValidatedWrite,
138    "f3a64788-5306-11d1-a9c5-0000f80367c1",
139);
140
141/// Every fixed GUID this crate knows, for name resolution and reporting.
142pub const ALL: &[&KnownGuid] = &[
143    &REPL_GET_CHANGES,
144    &REPL_GET_CHANGES_ALL,
145    &REPL_GET_CHANGES_FILTERED,
146    &FORCE_CHANGE_PASSWORD,
147    &REANIMATE_TOMBSTONES,
148    &ENROLLMENT,
149    &AUTO_ENROLLMENT,
150    &MEMBER,
151    &KEY_CREDENTIAL_LINK,
152    &RBCD,
153    &SPN,
154    &ALT_SECURITY_IDENTITIES,
155    &ALLOWED_TO_DELEGATE_TO,
156    &GP_LINK,
157];
158
159/// Human-readable name for a GUID, or `None` if it is forest-specific.
160///
161/// `class` disambiguates the two GUIDs that mean different things depending on the
162/// mask bit (`member` / `Self-Membership`, `servicePrincipalName` / `Validated-SPN`).
163pub fn name_of(g: &Guid, class: GuidClass) -> Option<&'static str> {
164    if class == ValidatedWrite {
165        for k in [&SELF_MEMBERSHIP, &VALIDATED_SPN] {
166            if k.matches(g) {
167                return Some(k.name);
168            }
169        }
170    }
171    ALL.iter()
172        .find(|k| k.class == class && k.matches(g))
173        .map(|k| k.name)
174}
175
176/// True if the GUID is either DCSync half (or the filtered-set variant).
177pub fn is_replication_right(g: &Guid) -> bool {
178    REPL_GET_CHANGES.matches(g)
179        || REPL_GET_CHANGES_ALL.matches(g)
180        || REPL_GET_CHANGES_FILTERED.matches(g)
181}
182
183/// True if the GUID grants certificate enrollment (manual or auto).
184pub fn is_enrollment_right(g: &Guid) -> bool {
185    ENROLLMENT.matches(g) || AUTO_ENROLLMENT.matches(g)
186}