1use super::{AccessMask, Ace, AceType, Dacl, PermissionTarget, SecurityDescriptor, Sid};
4use crate::Result;
5use crate::error::{Error, PermissionEditError, SecurityError, SecurityUnsupportedError};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum ApplyMode {
10 ValidateOnly,
12 Apply,
14 DryRunDiff,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct PermissionEditPolicy {
21 pub preserve_inherited: bool,
23 pub reject_conflicting_changes: bool,
25}
26
27impl Default for PermissionEditPolicy {
28 fn default() -> Self {
29 Self {
30 preserve_inherited: true,
31 reject_conflicting_changes: true,
32 }
33 }
34}
35
36#[derive(Debug, Clone, PartialEq, Eq)]
37enum PermissionOperation {
38 Grant {
39 trustee: Sid,
40 access: AccessMask,
41 },
42 Deny {
43 trustee: Sid,
44 access: AccessMask,
45 },
46 Revoke {
47 trustee: Sid,
48 access: Option<AccessMask>,
49 },
50 ReplaceTrustee {
51 old_trustee: Sid,
52 new_trustee: Sid,
53 },
54}
55
56#[derive(Debug, Clone)]
58pub struct PermissionEditor {
59 policy: PermissionEditPolicy,
60 operations: Vec<PermissionOperation>,
61}
62
63impl PermissionEditor {
64 pub fn new() -> Self {
66 Self {
67 policy: PermissionEditPolicy::default(),
68 operations: Vec::new(),
69 }
70 }
71
72 pub fn policy(mut self, policy: PermissionEditPolicy) -> Self {
74 self.policy = policy;
75 self
76 }
77
78 pub fn grant(mut self, trustee: Sid, access: AccessMask) -> Self {
80 self.operations
81 .push(PermissionOperation::Grant { trustee, access });
82 self
83 }
84
85 pub fn deny(mut self, trustee: Sid, access: AccessMask) -> Self {
87 self.operations
88 .push(PermissionOperation::Deny { trustee, access });
89 self
90 }
91
92 pub fn revoke(mut self, trustee: Sid, access: Option<AccessMask>) -> Self {
96 self.operations
97 .push(PermissionOperation::Revoke { trustee, access });
98 self
99 }
100
101 pub fn replace_trustee(mut self, old_trustee: Sid, new_trustee: Sid) -> Self {
103 self.operations.push(PermissionOperation::ReplaceTrustee {
104 old_trustee,
105 new_trustee,
106 });
107 self
108 }
109
110 pub fn build(self) -> Result<PermissionEditPlan> {
112 if self.policy.reject_conflicting_changes {
113 for op in &self.operations {
114 if let PermissionOperation::Grant { trustee, access } = op {
115 let conflict = self.operations.iter().any(|other| {
116 matches!(other, PermissionOperation::Deny { trustee: t, access: a } if t == trustee && a == access)
117 });
118
119 if conflict {
120 return Err(Error::Security(SecurityError::PermissionEdit(
121 PermissionEditError::new(
122 "grant/deny",
123 "conflicting allow and deny operation for same trustee and mask",
124 ),
125 )));
126 }
127 }
128 }
129 }
130
131 Ok(PermissionEditPlan {
132 policy: self.policy,
133 operations: self.operations,
134 })
135 }
136}
137
138impl Default for PermissionEditor {
139 fn default() -> Self {
140 Self::new()
141 }
142}
143
144#[derive(Debug, Clone)]
146pub struct PermissionEditPlan {
147 policy: PermissionEditPolicy,
148 operations: Vec<PermissionOperation>,
149}
150
151impl PermissionEditPlan {
152 pub fn execute(&self, current: &Dacl, mode: ApplyMode) -> PermissionEditResult {
154 let mut next = current.clone();
155
156 if matches!(mode, ApplyMode::ValidateOnly) {
157 return PermissionEditResult {
158 mode,
159 updated_dacl: None,
160 diff: PermissionDiff::between(current, current),
161 };
162 }
163
164 for op in &self.operations {
165 match op {
166 PermissionOperation::Grant { trustee, access } => {
167 next.entries_mut()
168 .push(Ace::new(trustee.clone(), AceType::Allow, *access));
169 }
170 PermissionOperation::Deny { trustee, access } => {
171 next.entries_mut()
172 .push(Ace::new(trustee.clone(), AceType::Deny, *access));
173 }
174 PermissionOperation::Revoke { trustee, access } => {
175 next.entries_mut().retain(|ace| {
176 if self.policy.preserve_inherited && ace.inherited {
177 return true;
178 }
179
180 if &ace.trustee != trustee {
181 return true;
182 }
183
184 match access {
185 Some(mask) => ace.access_mask != *mask,
186 None => false,
187 }
188 });
189 }
190 PermissionOperation::ReplaceTrustee {
191 old_trustee,
192 new_trustee,
193 } => {
194 for ace in next.entries_mut().iter_mut() {
195 if self.policy.preserve_inherited && ace.inherited {
196 continue;
197 }
198 if ace.trustee == *old_trustee {
199 ace.trustee = new_trustee.clone();
200 }
201 }
202 }
203 }
204 }
205
206 next.canonicalize();
207
208 PermissionEditResult {
209 mode,
210 updated_dacl: if matches!(mode, ApplyMode::Apply) {
211 Some(next.clone())
212 } else {
213 None
214 },
215 diff: PermissionDiff::between(current, &next),
216 }
217 }
218
219 pub fn execute_on_descriptor(
221 &self,
222 current: &SecurityDescriptor,
223 mode: ApplyMode,
224 ) -> DescriptorEditResult {
225 let dacl_result = self.execute(current.dacl(), mode);
226 let mut updated = None;
227
228 if let Some(next_dacl) = dacl_result.updated_dacl.clone() {
229 let mut descriptor = current.clone();
230 *descriptor.dacl_mut() = next_dacl;
231 updated = Some(descriptor);
232 }
233
234 DescriptorEditResult {
235 mode: dacl_result.mode,
236 updated_descriptor: updated,
237 diff: dacl_result.diff,
238 }
239 }
240
241 pub fn execute_for_target(
245 &self,
246 target: &PermissionTarget,
247 current: &SecurityDescriptor,
248 mode: ApplyMode,
249 ) -> Result<DescriptorEditResult> {
250 let result = self.execute_on_descriptor(current, mode);
251
252 if matches!(mode, ApplyMode::Apply) {
253 if let Some(updated) = &result.updated_descriptor {
254 target.write_descriptor(updated)?;
255 } else {
256 return Err(Error::Security(SecurityError::Unsupported(
257 SecurityUnsupportedError::new(
258 "permission_target",
259 "apply_without_updated_descriptor",
260 ),
261 )));
262 }
263 }
264
265 Ok(result)
266 }
267
268 pub fn execute_against_target(
270 &self,
271 target: &PermissionTarget,
272 mode: ApplyMode,
273 ) -> Result<DescriptorEditResult> {
274 let current = target.read_descriptor()?;
275 self.execute_for_target(target, ¤t, mode)
276 }
277}
278
279#[derive(Debug, Clone)]
281pub struct PermissionEditResult {
282 pub mode: ApplyMode,
284 pub updated_dacl: Option<Dacl>,
286 pub diff: PermissionDiff,
288}
289
290#[derive(Debug, Clone)]
292pub struct DescriptorEditResult {
293 pub mode: ApplyMode,
295 pub updated_descriptor: Option<SecurityDescriptor>,
297 pub diff: PermissionDiff,
299}
300
301#[derive(Debug, Clone, Default)]
303pub struct PermissionDiff {
304 pub added: Vec<Ace>,
306 pub removed: Vec<Ace>,
308}
309
310impl PermissionDiff {
311 fn between(before: &Dacl, after: &Dacl) -> Self {
312 let mut added = Vec::new();
313 let mut removed = Vec::new();
314
315 for ace in after.entries() {
316 if !before.entries().contains(ace) {
317 added.push(ace.clone());
318 }
319 }
320
321 for ace in before.entries() {
322 if !after.entries().contains(ace) {
323 removed.push(ace.clone());
324 }
325 }
326
327 Self { added, removed }
328 }
329}
330
331#[cfg(test)]
332mod tests {
333 use super::{AccessMask, ApplyMode, PermissionEditor};
334 use crate::security::{Ace, AceType, Dacl, SecurityDescriptor, Sid};
335
336 #[test]
337 fn build_rejects_conflicting_grant_and_deny() {
338 let sid = Sid::parse("S-1-5-32-545").expect("valid sid");
339 let err = PermissionEditor::new()
340 .grant(sid.clone(), AccessMask::from_bits(0x1))
341 .deny(sid, AccessMask::from_bits(0x1))
342 .build()
343 .expect_err("expected conflict");
344
345 assert!(err.to_string().contains("conflicting allow and deny"));
346 }
347
348 #[test]
349 fn dry_run_diff_reports_added_ace() {
350 let sid = Sid::parse("S-1-5-32-545").expect("valid sid");
351 let dacl = Dacl::new();
352
353 let plan = PermissionEditor::new()
354 .grant(sid.clone(), AccessMask::from_bits(0x2))
355 .build()
356 .expect("build plan");
357
358 let result = plan.execute(&dacl, ApplyMode::DryRunDiff);
359 assert_eq!(result.diff.added.len(), 1);
360 assert!(result.updated_dacl.is_none());
361 assert_eq!(
362 result.diff.added[0],
363 Ace::new(sid, AceType::Allow, AccessMask::from_bits(0x2))
364 );
365 }
366
367 #[test]
368 fn execute_on_descriptor_updates_descriptor_dacl() {
369 let sid = Sid::parse("S-1-5-32-545").expect("valid sid");
370 let descriptor = SecurityDescriptor::new().with_dacl(Dacl::new());
371
372 let plan = PermissionEditor::new()
373 .grant(sid, AccessMask::from_bits(0x4))
374 .build()
375 .expect("build plan");
376
377 let result = plan.execute_on_descriptor(&descriptor, ApplyMode::Apply);
378 assert!(result.updated_descriptor.is_some());
379 assert_eq!(result.diff.added.len(), 1);
380 }
381}