1use bitflags::bitflags;
32
33pub mod rights;
34pub mod sid;
35
36pub use sid::{Guid, Sid};
37
38pub fn sid_to_bytes(sid: &Sid) -> Vec<u8> {
40 sid.to_bytes()
41}
42
43pub fn build_rbcd_sd(trustee: &Sid) -> Vec<u8> {
47 let owner = Sid {
48 revision: 1,
49 identifier_authority: 5,
50 sub_authorities: vec![32, 544],
51 };
52 let ownerb = owner.to_bytes();
53 let trusteeb = trustee.to_bytes();
54
55 let ace_size = (4 + 4 + trusteeb.len()) as u16;
57 let mut ace = vec![0x00u8, 0x00];
58 ace.extend_from_slice(&ace_size.to_le_bytes());
59 ace.extend_from_slice(&0x000F_01FFu32.to_le_bytes());
60 ace.extend_from_slice(&trusteeb);
61
62 let dacl_size = (8 + ace.len()) as u16;
64 let mut dacl = vec![0x02u8, 0x00];
65 dacl.extend_from_slice(&dacl_size.to_le_bytes());
66 dacl.extend_from_slice(&1u16.to_le_bytes());
67 dacl.extend_from_slice(&0u16.to_le_bytes());
68 dacl.extend_from_slice(&ace);
69
70 let owner_off = 20u32;
72 let group_off = 20 + ownerb.len() as u32;
73 let dacl_off = group_off + ownerb.len() as u32;
74 let mut sd = vec![1u8, 0];
75 sd.extend_from_slice(&0x8004u16.to_le_bytes()); sd.extend_from_slice(&owner_off.to_le_bytes());
77 sd.extend_from_slice(&group_off.to_le_bytes());
78 sd.extend_from_slice(&0u32.to_le_bytes()); sd.extend_from_slice(&dacl_off.to_le_bytes());
80 sd.extend_from_slice(&ownerb); sd.extend_from_slice(&ownerb); sd.extend_from_slice(&dacl);
83 sd
84}
85
86#[derive(Debug, thiserror::Error)]
87pub enum SddlError {
88 #[error("buffer too short at {0}")]
89 Truncated(&'static str),
90 #[error("bad ACE sid")]
91 BadSid,
92}
93
94type Result<T> = std::result::Result<T, SddlError>;
95
96bitflags! {
97 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
99 pub struct AccessMask: u32 {
100 const CREATE_CHILD = 0x0000_0001;
101 const DELETE_CHILD = 0x0000_0002;
102 const LIST_CHILDREN = 0x0000_0004;
103 const SELF = 0x0000_0008; const READ_PROP = 0x0000_0010; const WRITE_PROP = 0x0000_0020; const DELETE_TREE = 0x0000_0040;
107 const LIST_OBJECT = 0x0000_0080;
108 const CONTROL_ACCESS = 0x0000_0100; const DELETE = 0x0001_0000;
110 const READ_CONTROL = 0x0002_0000;
111 const WRITE_DAC = 0x0004_0000;
112 const WRITE_OWNER = 0x0008_0000;
113 const SYNCHRONIZE = 0x0010_0000;
114 const ACCESS_SYSTEM_SECURITY = 0x0100_0000;
115 const GENERIC_ALL = 0x1000_0000;
116 const GENERIC_EXECUTE = 0x2000_0000;
117 const GENERIC_WRITE = 0x4000_0000;
118 const GENERIC_READ = 0x8000_0000;
119 }
120}
121
122#[derive(Clone, Copy, Debug, PartialEq, Eq)]
125pub enum AceType {
126 AccessAllowed,
127 AccessDenied,
128 AccessAllowedObject,
129 AccessDeniedObject,
130 Other(u8),
131}
132
133#[derive(Clone, Debug)]
134pub struct Ace {
135 pub ace_type: AceType,
136 pub flags: u8,
137 pub mask: AccessMask,
138 pub trustee: Sid,
139 pub object_type: Option<Guid>,
141 pub inherited_object_type: Option<Guid>,
142}
143
144impl Ace {
145 pub fn is_allow(&self) -> bool {
146 matches!(
147 self.ace_type,
148 AceType::AccessAllowed | AceType::AccessAllowedObject
149 )
150 }
151}
152
153#[derive(Clone, Debug, Default)]
154pub struct Acl {
155 pub aces: Vec<Ace>,
156}
157
158#[derive(Clone, Debug, Default)]
159pub struct SecurityDescriptor {
160 pub owner: Option<Sid>,
161 pub group: Option<Sid>,
162 pub dacl: Option<Acl>,
163}
164
165fn u16le(b: &[u8], o: usize) -> Result<u16> {
166 Ok(u16::from_le_bytes(
167 b.get(o..o + 2)
168 .ok_or(SddlError::Truncated("u16"))?
169 .try_into()
170 .unwrap(),
171 ))
172}
173fn u32le(b: &[u8], o: usize) -> Result<u32> {
174 Ok(u32::from_le_bytes(
175 b.get(o..o + 4)
176 .ok_or(SddlError::Truncated("u32"))?
177 .try_into()
178 .unwrap(),
179 ))
180}
181fn sid_at(b: &[u8], o: usize) -> Result<Sid> {
182 let count = *b.get(o + 1).ok_or(SddlError::Truncated("sid"))? as usize;
183 let end = o + 8 + count * 4;
184 Sid::from_bytes(b.get(o..end).ok_or(SddlError::Truncated("sid"))?).ok_or(SddlError::BadSid)
185}
186
187pub fn parse(b: &[u8]) -> Result<SecurityDescriptor> {
190 if b.len() < 20 {
191 return Err(SddlError::Truncated("sd header"));
192 }
193 let owner_off = u32le(b, 4)? as usize;
194 let group_off = u32le(b, 8)? as usize;
195 let dacl_off = u32le(b, 16)? as usize;
196
197 let owner = (owner_off != 0).then(|| sid_at(b, owner_off)).transpose()?;
198 let group = (group_off != 0).then(|| sid_at(b, group_off)).transpose()?;
199 let dacl = (dacl_off != 0)
200 .then(|| parse_acl(b, dacl_off))
201 .transpose()?;
202
203 Ok(SecurityDescriptor { owner, group, dacl })
204}
205
206fn parse_acl(b: &[u8], off: usize) -> Result<Acl> {
207 let ace_count = u16le(b, off + 4)? as usize;
209 let mut cur = off + 8;
210 let mut aces = Vec::with_capacity(ace_count);
211 for _ in 0..ace_count {
212 let ace_type_byte = *b.get(cur).ok_or(SddlError::Truncated("ace type"))?;
213 let flags = *b.get(cur + 1).ok_or(SddlError::Truncated("ace flags"))?;
214 let size = u16le(b, cur + 2)? as usize;
215 let ace_type = match ace_type_byte {
216 0x00 => AceType::AccessAllowed,
217 0x01 => AceType::AccessDenied,
218 0x05 => AceType::AccessAllowedObject,
219 0x06 => AceType::AccessDeniedObject,
220 x => AceType::Other(x),
221 };
222 let mask = AccessMask::from_bits_truncate(u32le(b, cur + 4)?);
223
224 let (object_type, inherited_object_type, sid_off) = match ace_type {
225 AceType::AccessAllowedObject | AceType::AccessDeniedObject => {
226 let obj_flags = u32le(b, cur + 8)?;
228 let mut p = cur + 12;
229 let mut ot = None;
230 let mut iot = None;
231 if obj_flags & 0x1 != 0 {
232 ot = b.get(p..p + 16).and_then(Guid::from_bytes);
233 p += 16;
234 }
235 if obj_flags & 0x2 != 0 {
236 iot = b.get(p..p + 16).and_then(Guid::from_bytes);
237 p += 16;
238 }
239 (ot, iot, p)
240 }
241 _ => (None, None, cur + 8),
242 };
243
244 let trustee = sid_at(b, sid_off)?;
245 aces.push(Ace {
246 ace_type,
247 flags,
248 mask,
249 trustee,
250 object_type,
251 inherited_object_type,
252 });
253 if size == 0 {
254 break;
255 }
256 cur += size;
257 }
258 Ok(Acl { aces })
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn rbcd_sd_roundtrips_through_parser() {
267 let sid = Sid::parse("S-1-5-21-1-2-3-1104").unwrap();
268 let sd = build_rbcd_sd(&sid);
269 let parsed = parse(&sd).expect("parse our own SD");
270 let aces = &parsed.dacl.expect("dacl").aces;
271 assert_eq!(aces.len(), 1);
272 assert!(aces[0].is_allow());
273 assert_eq!(aces[0].trustee, sid);
274 assert!(aces[0].mask.contains(AccessMask::WRITE_DAC));
275 }
276
277 #[test]
279 fn truncated_object_ace_does_not_panic() {
280 let mut sd = vec![1, 0, 0, 0];
281 sd.extend_from_slice(&0u32.to_le_bytes()); sd.extend_from_slice(&0u32.to_le_bytes()); sd.extend_from_slice(&0u32.to_le_bytes()); sd.extend_from_slice(&20u32.to_le_bytes()); sd.extend_from_slice(&[2, 0, 0x30, 0, 1, 0, 0, 0]); sd.extend_from_slice(&[0x05, 0, 0x20, 0]); sd.extend_from_slice(&0u32.to_le_bytes()); sd.extend_from_slice(&1u32.to_le_bytes()); let _ = parse(&sd); }
291
292 #[test]
294 fn fuzz_parse_never_panics() {
295 let mut seed = vec![1, 0, 0, 0];
296 seed.extend_from_slice(&0u32.to_le_bytes());
297 seed.extend_from_slice(&0u32.to_le_bytes());
298 seed.extend_from_slice(&0u32.to_le_bytes());
299 seed.extend_from_slice(&20u32.to_le_bytes());
300 seed.extend_from_slice(&[2, 0, 0x30, 0, 1, 0, 0, 0]);
301 seed.extend_from_slice(&[0x05, 0, 0x2c, 0]);
302 seed.extend_from_slice(&[0u8; 40]);
303
304 let mut s: u64 = 0xDEAD_BEEF_CAFE_F00D;
305 let mut rng = || {
306 s ^= s >> 12;
307 s ^= s << 25;
308 s ^= s >> 27;
309 s.wrapping_mul(0x2545_F491_4F6C_DD1D)
310 };
311 let prev = std::panic::take_hook();
312 std::panic::set_hook(Box::new(|_| {}));
313 let mut fail = None;
314 for _ in 0..200_000 {
315 let mut buf = if rng() & 1 == 0 {
316 seed.clone()
317 } else {
318 let n = (rng() as usize) % 80;
319 (0..n).map(|_| rng() as u8).collect::<Vec<u8>>()
320 };
321 for _ in 0..(rng() as usize % 6) {
322 if !buf.is_empty() {
323 let i = (rng() as usize) % buf.len();
324 buf[i] = rng() as u8;
325 }
326 }
327 let b = buf.clone();
328 if std::panic::catch_unwind(|| {
329 let _ = parse(&b);
330 })
331 .is_err()
332 {
333 fail = Some(buf);
334 break;
335 }
336 }
337 std::panic::set_hook(prev);
338 if let Some(buf) = fail {
339 panic!(
340 "parse panicked on {} bytes: {}",
341 buf.len(),
342 buf.iter().map(|x| format!("{x:02x}")).collect::<String>()
343 );
344 }
345 }
346}