1#![allow(dead_code)]
6
7use crate::errors::{Errno, error};
8use crate::{gid_t, uapi, uid_t};
9use bitflags::bitflags;
10use std::ops;
11
12#[derive(Clone, Copy, Eq, PartialEq)]
15pub struct Capabilities {
16 mask: u64,
17}
18
19impl Capabilities {
20 pub fn empty() -> Self {
21 Self { mask: 0 }
22 }
23
24 pub fn all() -> Self {
25 Self { mask: u64::MAX }
26 }
27
28 pub fn union(&self, caps: Capabilities) -> Self {
29 let mut new_caps = *self;
30 new_caps.insert(caps);
31 new_caps
32 }
33
34 pub fn difference(&self, caps: Capabilities) -> Self {
35 let mut new_caps = *self;
36 new_caps.remove(caps);
37 new_caps
38 }
39
40 pub fn contains(self, caps: Capabilities) -> bool {
41 (self & caps) == caps
42 }
43
44 pub fn insert(&mut self, caps: Capabilities) {
45 *self |= caps;
46 }
47
48 pub fn remove(&mut self, caps: Capabilities) {
49 *self &= !caps;
50 }
51
52 pub fn as_abi_v1(self) -> u32 {
53 self.mask as u32
54 }
55
56 pub fn from_abi_v1(bits: u32) -> Self {
57 Self { mask: bits as u64 }
58 }
59
60 pub fn as_abi_v3(self) -> (u32, u32) {
61 (self.mask as u32, (self.mask >> 32) as u32)
62 }
63
64 pub fn from_abi_v3(u32s: (u32, u32)) -> Self {
65 Self { mask: u32s.0 as u64 | ((u32s.1 as u64) << 32) }
66 }
67}
68
69impl std::convert::TryFrom<u64> for Capabilities {
70 type Error = Errno;
71
72 fn try_from(capability_num: u64) -> Result<Self, Self::Error> {
73 match 1u64.checked_shl(capability_num as u32) {
74 Some(mask) => Ok(Self { mask }),
75 _ => error!(EINVAL),
76 }
77 }
78}
79
80impl ops::BitAnd for Capabilities {
81 type Output = Self;
82
83 fn bitand(self, rhs: Self) -> Self::Output {
85 Self { mask: self.mask & rhs.mask }
86 }
87}
88
89impl ops::BitAndAssign for Capabilities {
90 fn bitand_assign(&mut self, rhs: Self) {
92 self.mask &= rhs.mask;
93 }
94}
95
96impl ops::BitOr for Capabilities {
97 type Output = Self;
98
99 fn bitor(self, rhs: Self) -> Self::Output {
100 Self { mask: self.mask | rhs.mask }
101 }
102}
103
104impl ops::BitOrAssign for Capabilities {
105 fn bitor_assign(&mut self, rhs: Self) {
106 self.mask |= rhs.mask;
107 }
108}
109
110impl ops::Not for Capabilities {
111 type Output = Self;
112
113 fn not(self) -> Self::Output {
114 Self { mask: !self.mask }
115 }
116}
117
118impl std::fmt::Debug for Capabilities {
119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
120 write!(f, "Capabilities({:#x})", self.mask)
121 }
122}
123
124impl std::str::FromStr for Capabilities {
125 type Err = Errno;
126 fn from_str(s: &str) -> Result<Self, Self::Err> {
127 Ok(match s {
128 "CHOWN" => CAP_CHOWN,
129 "DAC_OVERRIDE" => CAP_DAC_OVERRIDE,
130 "DAC_READ_SEARCH" => CAP_DAC_READ_SEARCH,
131 "FOWNER" => CAP_FOWNER,
132 "FSETID" => CAP_FSETID,
133 "KILL" => CAP_KILL,
134 "SETGID" => CAP_SETGID,
135 "SETUID" => CAP_SETUID,
136 "SETPCAP" => CAP_SETPCAP,
137 "LINUX_IMMUTABLE" => CAP_LINUX_IMMUTABLE,
138 "NET_BIND_SERVICE" => CAP_NET_BIND_SERVICE,
139 "NET_BROADCAST" => CAP_NET_BROADCAST,
140 "NET_ADMIN" => CAP_NET_ADMIN,
141 "NET_RAW" => CAP_NET_RAW,
142 "IPC_LOCK" => CAP_IPC_LOCK,
143 "IPC_OWNER" => CAP_IPC_OWNER,
144 "SYS_MODULE" => CAP_SYS_MODULE,
145 "SYS_RAWIO" => CAP_SYS_RAWIO,
146 "SYS_CHROOT" => CAP_SYS_CHROOT,
147 "SYS_PTRACE" => CAP_SYS_PTRACE,
148 "SYS_PACCT" => CAP_SYS_PACCT,
149 "SYS_ADMIN" => CAP_SYS_ADMIN,
150 "SYS_BOOT" => CAP_SYS_BOOT,
151 "SYS_NICE" => CAP_SYS_NICE,
152 "SYS_RESOURCE" => CAP_SYS_RESOURCE,
153 "SYS_TIME" => CAP_SYS_TIME,
154 "SYS_TTY_CONFIG" => CAP_SYS_TTY_CONFIG,
155 "MKNOD" => CAP_MKNOD,
156 "LEASE" => CAP_LEASE,
157 "AUDIT_WRITE" => CAP_AUDIT_WRITE,
158 "AUDIT_CONTROL" => CAP_AUDIT_CONTROL,
159 "SETFCAP" => CAP_SETFCAP,
160 "MAC_OVERRIDE" => CAP_MAC_OVERRIDE,
161 "MAC_ADMIN" => CAP_MAC_ADMIN,
162 "SYSLOG" => CAP_SYSLOG,
163 "WAKE_ALARM" => CAP_WAKE_ALARM,
164 "BLOCK_SUSPEND" => CAP_BLOCK_SUSPEND,
165 "AUDIT_READ" => CAP_AUDIT_READ,
166 "PERFMON" => CAP_PERFMON,
167 "BPF" => CAP_BPF,
168 "CHECKPOINT_RESTORE" => CAP_CHECKPOINT_RESTORE,
169 _ => return error!(EINVAL),
170 })
171 }
172}
173
174pub const CAP_CHOWN: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_CHOWN };
175pub const CAP_DAC_OVERRIDE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_DAC_OVERRIDE };
176pub const CAP_DAC_READ_SEARCH: Capabilities =
177 Capabilities { mask: 1u64 << uapi::CAP_DAC_READ_SEARCH };
178pub const CAP_FOWNER: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_FOWNER };
179pub const CAP_FSETID: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_FSETID };
180pub const CAP_KILL: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_KILL };
181pub const CAP_SETGID: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SETGID };
182pub const CAP_SETUID: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SETUID };
183pub const CAP_SETPCAP: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SETPCAP };
184pub const CAP_LINUX_IMMUTABLE: Capabilities =
185 Capabilities { mask: 1u64 << uapi::CAP_LINUX_IMMUTABLE };
186pub const CAP_NET_BIND_SERVICE: Capabilities =
187 Capabilities { mask: 1u64 << uapi::CAP_NET_BIND_SERVICE };
188pub const CAP_NET_BROADCAST: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_NET_BROADCAST };
189pub const CAP_NET_ADMIN: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_NET_ADMIN };
190pub const CAP_NET_RAW: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_NET_RAW };
191pub const CAP_IPC_LOCK: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_IPC_LOCK };
192pub const CAP_IPC_OWNER: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_IPC_OWNER };
193pub const CAP_SYS_MODULE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_MODULE };
194pub const CAP_SYS_RAWIO: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_RAWIO };
195pub const CAP_SYS_CHROOT: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_CHROOT };
196pub const CAP_SYS_PTRACE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_PTRACE };
197pub const CAP_SYS_PACCT: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_PACCT };
198pub const CAP_SYS_ADMIN: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_ADMIN };
199pub const CAP_SYS_BOOT: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_BOOT };
200pub const CAP_SYS_NICE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_NICE };
201pub const CAP_SYS_RESOURCE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_RESOURCE };
202pub const CAP_SYS_TIME: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_TIME };
203pub const CAP_SYS_TTY_CONFIG: Capabilities =
204 Capabilities { mask: 1u64 << uapi::CAP_SYS_TTY_CONFIG };
205pub const CAP_MKNOD: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_MKNOD };
206pub const CAP_LEASE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_LEASE };
207pub const CAP_AUDIT_WRITE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_AUDIT_WRITE };
208pub const CAP_AUDIT_CONTROL: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_AUDIT_CONTROL };
209pub const CAP_SETFCAP: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SETFCAP };
210pub const CAP_MAC_OVERRIDE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_MAC_OVERRIDE };
211pub const CAP_MAC_ADMIN: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_MAC_ADMIN };
212pub const CAP_SYSLOG: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYSLOG };
213pub const CAP_WAKE_ALARM: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_WAKE_ALARM };
214pub const CAP_BLOCK_SUSPEND: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_BLOCK_SUSPEND };
215pub const CAP_AUDIT_READ: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_AUDIT_READ };
216pub const CAP_PERFMON: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_PERFMON };
217pub const CAP_BPF: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_BPF };
218pub const CAP_CHECKPOINT_RESTORE: Capabilities =
219 Capabilities { mask: 1u64 << uapi::CAP_CHECKPOINT_RESTORE };
220pub const CAP_LAST_CAP: u32 = uapi::CAP_LAST_CAP;
221
222bitflags! {
223 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
224 pub struct PtraceAccessMode: u32 {
225 const READ = 1 << 0;
226 const ATTACH = 1 << 1;
227 const FSCREDS = 1 << 2;
228 const REALCREDS = 1 << 3;
229 const NOAUDIT = 1 << 4;
230 }
231}
232
233pub const PTRACE_MODE_READ: PtraceAccessMode = PtraceAccessMode::READ;
234pub const PTRACE_MODE_ATTACH: PtraceAccessMode = PtraceAccessMode::ATTACH;
235pub const PTRACE_MODE_FSCREDS: PtraceAccessMode = PtraceAccessMode::FSCREDS;
236pub const PTRACE_MODE_REALCREDS: PtraceAccessMode = PtraceAccessMode::REALCREDS;
237pub const PTRACE_MODE_READ_FSCREDS: PtraceAccessMode = PtraceAccessMode::from_bits_truncate(
238 PtraceAccessMode::READ.bits() | PtraceAccessMode::FSCREDS.bits(),
239);
240pub const PTRACE_MODE_READ_REALCREDS: PtraceAccessMode = PtraceAccessMode::from_bits_truncate(
241 PtraceAccessMode::READ.bits() | PtraceAccessMode::REALCREDS.bits(),
242);
243pub const PTRACE_MODE_ATTACH_FSCREDS: PtraceAccessMode = PtraceAccessMode::from_bits_truncate(
244 PtraceAccessMode::ATTACH.bits() | PtraceAccessMode::FSCREDS.bits(),
245);
246pub const PTRACE_MODE_ATTACH_REALCREDS: PtraceAccessMode = PtraceAccessMode::from_bits_truncate(
247 PtraceAccessMode::ATTACH.bits() | PtraceAccessMode::REALCREDS.bits(),
248);
249pub const PTRACE_MODE_NOAUDIT: PtraceAccessMode = PtraceAccessMode::NOAUDIT;
250
251#[derive(Debug, Clone)]
252pub struct Credentials {
253 pub uid: uid_t,
254 pub gid: gid_t,
255 pub euid: uid_t,
256 pub egid: gid_t,
257 pub saved_uid: uid_t,
258 pub saved_gid: gid_t,
259 pub groups: Vec<gid_t>,
260
261 pub fsuid: uid_t,
263
264 pub fsgid: gid_t,
266
267 pub cap_permitted: Capabilities,
277
278 pub cap_effective: Capabilities,
283
284 pub cap_inheritable: Capabilities,
295
296 pub cap_bounding: Capabilities,
304
305 pub cap_ambient: Capabilities,
314
315 pub securebits: SecureBits,
325}
326
327bitflags! {
328 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
329 pub struct SecureBits: u32 {
330 const KEEP_CAPS = 1 << uapi::SECURE_KEEP_CAPS;
331 const KEEP_CAPS_LOCKED = 1 << uapi::SECURE_KEEP_CAPS_LOCKED;
332 const NO_SETUID_FIXUP = 1 << uapi::SECURE_NO_SETUID_FIXUP;
333 const NO_SETUID_FIXUP_LOCKED = 1 << uapi::SECURE_NO_SETUID_FIXUP_LOCKED;
334 const NOROOT = 1 << uapi::SECURE_NOROOT;
335 const NOROOT_LOCKED = 1 << uapi::SECURE_NOROOT_LOCKED;
336 const NO_CAP_AMBIENT_RAISE = 1 << uapi::SECURE_NO_CAP_AMBIENT_RAISE;
337 const NO_CAP_AMBIENT_RAISE_LOCKED = 1 << uapi::SECURE_NO_CAP_AMBIENT_RAISE_LOCKED;
338 }
339}
340
341impl Credentials {
342 pub fn root() -> Self {
344 Self::with_ids(0, 0)
345 }
346
347 pub fn with_ids(uid: uid_t, gid: gid_t) -> Credentials {
350 let caps = if uid == 0 { Capabilities::all() } else { Capabilities::empty() };
351 Credentials {
352 uid,
353 gid,
354 euid: uid,
355 egid: gid,
356 saved_uid: uid,
357 saved_gid: gid,
358 groups: vec![],
359 fsuid: uid,
360 fsgid: gid,
361 cap_permitted: caps,
362 cap_effective: caps,
363 cap_inheritable: Capabilities::empty(),
364 cap_bounding: Capabilities::all(),
365 cap_ambient: Capabilities::empty(),
366 securebits: SecureBits::empty(),
367 }
368 }
369
370 pub fn is_superuser(&self) -> bool {
371 self.euid == 0
372 }
373
374 pub fn is_in_group(&self, gid: gid_t) -> bool {
375 self.egid == gid || self.groups.contains(&gid)
376 }
377
378 pub fn has_capability(&self, capability: Capabilities) -> bool {
380 self.cap_effective.contains(capability)
381 }
382
383 fn apply_suid_and_sgid(&mut self, maybe_set: UserAndOrGroupId) {
384 if maybe_set.is_none() {
385 return;
386 }
387
388 let prev = self.copy_user_credentials();
389
390 if let Some(uid) = maybe_set.uid {
391 self.euid = uid;
392 self.fsuid = uid;
393 }
394
395 if let Some(gid) = maybe_set.gid {
396 self.egid = gid;
397 self.fsgid = gid;
398 }
399
400 self.update_capabilities(prev);
401 }
402
403 pub fn exec(&mut self, maybe_set: UserAndOrGroupId) {
404 let is_suid_or_sgid = maybe_set.is_some();
405 self.apply_suid_and_sgid(maybe_set);
414
415 self.saved_uid = self.euid;
423 self.saved_gid = self.egid;
424
425 let file_is_privileged = is_suid_or_sgid;
446
447 let (file_permitted, file_inheritable) = if self.uid == 0 || self.euid == 0 {
458 (Capabilities::all(), Capabilities::all())
459 } else {
460 (Capabilities::empty(), Capabilities::empty())
461 };
462
463 let file_effective = self.euid == 0;
467
468 self.cap_ambient =
474 if file_is_privileged { Capabilities::empty() } else { self.cap_ambient };
475
476 self.cap_permitted = (self.cap_inheritable & file_inheritable)
479 | (file_permitted & self.cap_bounding)
480 | self.cap_ambient;
481
482 self.cap_effective = if file_effective { self.cap_permitted } else { self.cap_ambient };
484
485 self.securebits.remove(SecureBits::KEEP_CAPS);
486 }
487
488 pub fn as_fscred(&self) -> FsCred {
489 FsCred { uid: self.fsuid, gid: self.fsgid }
490 }
491
492 pub fn euid_as_fscred(&self) -> FsCred {
493 FsCred { uid: self.euid, gid: self.egid }
494 }
495
496 pub fn uid_as_fscred(&self) -> FsCred {
497 FsCred { uid: self.uid, gid: self.gid }
498 }
499
500 pub fn copy_user_credentials(&self) -> UserCredentials {
501 UserCredentials {
502 uid: self.uid,
503 euid: self.euid,
504 fsuid: self.fsuid,
505 saved_uid: self.saved_uid,
506 }
507 }
508
509 pub fn update_capabilities(&mut self, prev: UserCredentials) {
510 if self.securebits.contains(SecureBits::NO_SETUID_FIXUP) {
527 return;
528 }
529 if !self.securebits.contains(SecureBits::KEEP_CAPS)
530 && (prev.uid == 0 || prev.euid == 0 || prev.saved_uid == 0)
531 && (self.uid != 0 && self.euid != 0 && self.saved_uid != 0)
532 {
533 self.cap_permitted = Capabilities::empty();
534 self.cap_effective = Capabilities::empty();
535 self.cap_ambient = Capabilities::empty();
536 }
537 if prev.euid == 0 && self.euid != 0 {
540 self.cap_effective = Capabilities::empty();
541 } else if prev.euid != 0 && self.euid == 0 {
542 self.cap_effective = self.cap_permitted;
545 }
546
547 let fs_capabilities = CAP_CHOWN
554 | CAP_DAC_OVERRIDE
555 | CAP_DAC_READ_SEARCH
556 | CAP_FOWNER
557 | CAP_FSETID
558 | CAP_LINUX_IMMUTABLE
559 | CAP_MAC_OVERRIDE
560 | CAP_MKNOD;
561 if prev.fsuid == 0 && self.fsuid != 0 {
562 self.cap_effective &= !fs_capabilities;
563 } else if prev.fsuid != 0 && self.fsuid == 0 {
564 self.cap_effective |= self.cap_permitted & fs_capabilities;
568 }
569 }
570}
571
572#[derive(Debug, Clone, Copy)]
574pub struct FsCred {
575 pub uid: uid_t,
576 pub gid: gid_t,
577}
578
579impl FsCred {
580 pub const fn root() -> Self {
581 Self { uid: 0, gid: 0 }
582 }
583}
584
585impl From<Credentials> for FsCred {
586 fn from(c: Credentials) -> Self {
587 c.as_fscred()
588 }
589}
590
591#[derive(Debug, Clone, Copy)]
592pub struct UserCredentials {
593 pub uid: uid_t,
594 pub euid: uid_t,
595 pub saved_uid: uid_t,
596 pub fsuid: uid_t,
597}
598
599#[derive(Debug, Default, Clone)]
600pub struct UserAndOrGroupId {
601 pub uid: Option<uid_t>,
602 pub gid: Option<gid_t>,
603}
604
605impl UserAndOrGroupId {
606 pub fn is_none(&self) -> bool {
607 self.uid.is_none() && self.gid.is_none()
608 }
609
610 pub fn is_some(&self) -> bool {
611 !self.is_none()
612 }
613
614 pub fn clear(&mut self) {
615 self.uid = None;
616 self.gid = None;
617 }
618}
619
620#[cfg(test)]
621mod tests {
622 use super::*;
623
624 #[::fuchsia::test]
625 fn test_empty() {
626 assert_eq!(Capabilities::empty().mask, 0);
627 }
628
629 #[::fuchsia::test]
630 fn test_all() {
631 assert_eq!(Capabilities::all().mask, u64::MAX);
633 }
634
635 #[::fuchsia::test]
636 fn test_union() {
637 let expected = Capabilities { mask: CAP_BLOCK_SUSPEND.mask | CAP_AUDIT_READ.mask };
638 assert_eq!(CAP_BLOCK_SUSPEND.union(CAP_AUDIT_READ), expected);
639 assert_eq!(CAP_BLOCK_SUSPEND.union(CAP_BLOCK_SUSPEND), CAP_BLOCK_SUSPEND);
640 }
641
642 #[::fuchsia::test]
643 fn test_difference() {
644 let base = CAP_BPF | CAP_AUDIT_WRITE;
645 let expected = CAP_BPF;
646 assert_eq!(base.difference(CAP_AUDIT_WRITE), expected);
647 assert_eq!(base.difference(CAP_AUDIT_WRITE | CAP_BPF), Capabilities::empty());
648 }
649
650 #[::fuchsia::test]
651 fn test_contains() {
652 let base = CAP_BPF | CAP_AUDIT_WRITE;
653 assert!(base.contains(CAP_AUDIT_WRITE));
654 assert!(base.contains(CAP_BPF));
655 assert!(base.contains(CAP_AUDIT_WRITE | CAP_BPF));
656
657 assert!(!base.contains(CAP_AUDIT_CONTROL));
658 assert!(!base.contains(CAP_AUDIT_WRITE | CAP_BPF | CAP_AUDIT_CONTROL));
659 }
660
661 #[::fuchsia::test]
662 fn test_insert() {
663 let mut capabilities = CAP_BLOCK_SUSPEND;
664 capabilities.insert(CAP_BLOCK_SUSPEND);
665 assert_eq!(capabilities, CAP_BLOCK_SUSPEND);
666
667 capabilities.insert(CAP_AUDIT_READ);
668 let expected = Capabilities { mask: CAP_BLOCK_SUSPEND.mask | CAP_AUDIT_READ.mask };
669 assert_eq!(capabilities, expected);
670 }
671
672 #[::fuchsia::test]
673 fn test_remove() {
674 let mut capabilities = CAP_BLOCK_SUSPEND;
675 capabilities.remove(CAP_BLOCK_SUSPEND);
676 assert_eq!(capabilities, Capabilities::empty());
677
678 let mut capabilities = CAP_BLOCK_SUSPEND | CAP_AUDIT_READ;
679 capabilities.remove(CAP_AUDIT_READ);
680 assert_eq!(capabilities, CAP_BLOCK_SUSPEND);
681 }
682
683 #[::fuchsia::test]
684 fn test_try_from() {
685 let capabilities = CAP_BLOCK_SUSPEND;
686 assert_eq!(Capabilities::try_from(uapi::CAP_BLOCK_SUSPEND as u64), Ok(capabilities));
687
688 assert_eq!(Capabilities::try_from(200000), error!(EINVAL));
689 }
690}