1#![allow(dead_code)]
6
7use crate::errors::{Errno, error};
8use crate::selinux::TaskAttrs;
9use crate::{gid_t, uapi, uid_t};
10use bitflags::bitflags;
11use std::ops;
12use std::sync::{Arc, LazyLock};
13
14#[derive(Clone, Copy, Eq, PartialEq)]
17pub struct Capabilities {
18 mask: u64,
19}
20
21impl Capabilities {
22 pub const fn empty() -> Self {
23 Self { mask: 0 }
24 }
25
26 pub const fn all() -> Self {
27 const fn make_mask(cap: u32) -> u64 {
28 let mask = if cap > 0 { make_mask(cap - 1) << 1 } else { 0u64 };
29 mask | 1u64
30 }
31 const ALL_CAPS_MASK: u64 = make_mask(CAP_LAST_CAP);
32 Self { mask: ALL_CAPS_MASK }
33 }
34
35 pub fn union(&self, caps: Capabilities) -> Self {
36 let mut new_caps = *self;
37 new_caps.insert(caps);
38 new_caps
39 }
40
41 pub fn difference(&self, caps: Capabilities) -> Self {
42 let mut new_caps = *self;
43 new_caps.remove(caps);
44 new_caps
45 }
46
47 pub fn contains(self, caps: Capabilities) -> bool {
48 (self & caps) == caps
49 }
50
51 pub fn insert(&mut self, caps: Capabilities) {
52 *self |= caps;
53 }
54
55 pub fn remove(&mut self, caps: Capabilities) {
56 *self &= !caps;
57 }
58
59 pub const fn as_abi_v1(self) -> u32 {
60 self.mask as u32
61 }
62
63 pub fn from_abi_v1(bits: u32) -> Self {
64 Self { mask: bits as u64 } & Self::all()
65 }
66
67 pub const fn as_abi_v3(self) -> (u32, u32) {
68 (self.mask as u32, (self.mask >> 32) as u32)
69 }
70
71 pub fn from_abi_v3(u32s: (u32, u32)) -> Self {
72 Self { mask: u32s.0 as u64 | ((u32s.1 as u64) << 32) } & Self::all()
73 }
74}
75
76impl std::convert::TryFrom<u64> for Capabilities {
77 type Error = Errno;
78
79 fn try_from(capability_num: u64) -> Result<Self, Self::Error> {
80 match 1u64.checked_shl(capability_num as u32) {
81 Some(mask) => Ok(Self { mask }),
82 _ => error!(EINVAL),
83 }
84 }
85}
86
87impl ops::BitAnd for Capabilities {
88 type Output = Self;
89
90 fn bitand(self, rhs: Self) -> Self::Output {
92 Self { mask: self.mask & rhs.mask }
93 }
94}
95
96impl ops::BitAndAssign for Capabilities {
97 fn bitand_assign(&mut self, rhs: Self) {
99 self.mask &= rhs.mask;
100 }
101}
102
103impl ops::BitOr for Capabilities {
104 type Output = Self;
105
106 fn bitor(self, rhs: Self) -> Self::Output {
107 Self { mask: self.mask | rhs.mask }
108 }
109}
110
111impl ops::BitOrAssign for Capabilities {
112 fn bitor_assign(&mut self, rhs: Self) {
113 self.mask |= rhs.mask;
114 }
115}
116
117impl ops::Not for Capabilities {
118 type Output = Self;
119
120 fn not(self) -> Self::Output {
121 Self { mask: !self.mask }
122 }
123}
124
125impl std::fmt::Debug for Capabilities {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
127 write!(f, "Capabilities({:#x})", self.mask)
128 }
129}
130
131impl std::fmt::LowerHex for Capabilities {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
133 std::fmt::LowerHex::fmt(&self.mask, f)
134 }
135}
136
137impl std::str::FromStr for Capabilities {
138 type Err = Errno;
139 fn from_str(s: &str) -> Result<Self, Self::Err> {
140 Ok(match s {
141 "CHOWN" => CAP_CHOWN,
142 "DAC_OVERRIDE" => CAP_DAC_OVERRIDE,
143 "DAC_READ_SEARCH" => CAP_DAC_READ_SEARCH,
144 "FOWNER" => CAP_FOWNER,
145 "FSETID" => CAP_FSETID,
146 "KILL" => CAP_KILL,
147 "SETGID" => CAP_SETGID,
148 "SETUID" => CAP_SETUID,
149 "SETPCAP" => CAP_SETPCAP,
150 "LINUX_IMMUTABLE" => CAP_LINUX_IMMUTABLE,
151 "NET_BIND_SERVICE" => CAP_NET_BIND_SERVICE,
152 "NET_BROADCAST" => CAP_NET_BROADCAST,
153 "NET_ADMIN" => CAP_NET_ADMIN,
154 "NET_RAW" => CAP_NET_RAW,
155 "IPC_LOCK" => CAP_IPC_LOCK,
156 "IPC_OWNER" => CAP_IPC_OWNER,
157 "SYS_MODULE" => CAP_SYS_MODULE,
158 "SYS_RAWIO" => CAP_SYS_RAWIO,
159 "SYS_CHROOT" => CAP_SYS_CHROOT,
160 "SYS_PTRACE" => CAP_SYS_PTRACE,
161 "SYS_PACCT" => CAP_SYS_PACCT,
162 "SYS_ADMIN" => CAP_SYS_ADMIN,
163 "SYS_BOOT" => CAP_SYS_BOOT,
164 "SYS_NICE" => CAP_SYS_NICE,
165 "SYS_RESOURCE" => CAP_SYS_RESOURCE,
166 "SYS_TIME" => CAP_SYS_TIME,
167 "SYS_TTY_CONFIG" => CAP_SYS_TTY_CONFIG,
168 "MKNOD" => CAP_MKNOD,
169 "LEASE" => CAP_LEASE,
170 "AUDIT_WRITE" => CAP_AUDIT_WRITE,
171 "AUDIT_CONTROL" => CAP_AUDIT_CONTROL,
172 "SETFCAP" => CAP_SETFCAP,
173 "MAC_OVERRIDE" => CAP_MAC_OVERRIDE,
174 "MAC_ADMIN" => CAP_MAC_ADMIN,
175 "SYSLOG" => CAP_SYSLOG,
176 "WAKE_ALARM" => CAP_WAKE_ALARM,
177 "BLOCK_SUSPEND" => CAP_BLOCK_SUSPEND,
178 "AUDIT_READ" => CAP_AUDIT_READ,
179 "PERFMON" => CAP_PERFMON,
180 "BPF" => CAP_BPF,
181 "CHECKPOINT_RESTORE" => CAP_CHECKPOINT_RESTORE,
182 _ => return error!(EINVAL),
183 })
184 }
185}
186
187pub const CAP_CHOWN: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_CHOWN };
188pub const CAP_DAC_OVERRIDE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_DAC_OVERRIDE };
189pub const CAP_DAC_READ_SEARCH: Capabilities =
190 Capabilities { mask: 1u64 << uapi::CAP_DAC_READ_SEARCH };
191pub const CAP_FOWNER: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_FOWNER };
192pub const CAP_FSETID: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_FSETID };
193pub const CAP_KILL: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_KILL };
194pub const CAP_SETGID: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SETGID };
195pub const CAP_SETUID: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SETUID };
196pub const CAP_SETPCAP: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SETPCAP };
197pub const CAP_LINUX_IMMUTABLE: Capabilities =
198 Capabilities { mask: 1u64 << uapi::CAP_LINUX_IMMUTABLE };
199pub const CAP_NET_BIND_SERVICE: Capabilities =
200 Capabilities { mask: 1u64 << uapi::CAP_NET_BIND_SERVICE };
201pub const CAP_NET_BROADCAST: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_NET_BROADCAST };
202pub const CAP_NET_ADMIN: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_NET_ADMIN };
203pub const CAP_NET_RAW: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_NET_RAW };
204pub const CAP_IPC_LOCK: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_IPC_LOCK };
205pub const CAP_IPC_OWNER: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_IPC_OWNER };
206pub const CAP_SYS_MODULE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_MODULE };
207pub const CAP_SYS_RAWIO: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_RAWIO };
208pub const CAP_SYS_CHROOT: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_CHROOT };
209pub const CAP_SYS_PTRACE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_PTRACE };
210pub const CAP_SYS_PACCT: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_PACCT };
211pub const CAP_SYS_ADMIN: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_ADMIN };
212pub const CAP_SYS_BOOT: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_BOOT };
213pub const CAP_SYS_NICE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_NICE };
214pub const CAP_SYS_RESOURCE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_RESOURCE };
215pub const CAP_SYS_TIME: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYS_TIME };
216pub const CAP_SYS_TTY_CONFIG: Capabilities =
217 Capabilities { mask: 1u64 << uapi::CAP_SYS_TTY_CONFIG };
218pub const CAP_MKNOD: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_MKNOD };
219pub const CAP_LEASE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_LEASE };
220pub const CAP_AUDIT_WRITE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_AUDIT_WRITE };
221pub const CAP_AUDIT_CONTROL: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_AUDIT_CONTROL };
222pub const CAP_SETFCAP: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SETFCAP };
223pub const CAP_MAC_OVERRIDE: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_MAC_OVERRIDE };
224pub const CAP_MAC_ADMIN: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_MAC_ADMIN };
225pub const CAP_SYSLOG: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_SYSLOG };
226pub const CAP_WAKE_ALARM: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_WAKE_ALARM };
227pub const CAP_BLOCK_SUSPEND: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_BLOCK_SUSPEND };
228pub const CAP_AUDIT_READ: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_AUDIT_READ };
229pub const CAP_PERFMON: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_PERFMON };
230pub const CAP_BPF: Capabilities = Capabilities { mask: 1u64 << uapi::CAP_BPF };
231pub const CAP_CHECKPOINT_RESTORE: Capabilities =
232 Capabilities { mask: 1u64 << uapi::CAP_CHECKPOINT_RESTORE };
233pub const CAP_LAST_CAP: u32 = uapi::CAP_LAST_CAP;
234
235bitflags! {
236 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
237 pub struct PtraceAccessMode: u32 {
238 const READ = 1 << 0;
239 const ATTACH = 1 << 1;
240 const FSCREDS = 1 << 2;
241 const REALCREDS = 1 << 3;
242 const NOAUDIT = 1 << 4;
243 }
244}
245
246pub const PTRACE_MODE_READ: PtraceAccessMode = PtraceAccessMode::READ;
247pub const PTRACE_MODE_ATTACH: PtraceAccessMode = PtraceAccessMode::ATTACH;
248pub const PTRACE_MODE_FSCREDS: PtraceAccessMode = PtraceAccessMode::FSCREDS;
249pub const PTRACE_MODE_REALCREDS: PtraceAccessMode = PtraceAccessMode::REALCREDS;
250pub const PTRACE_MODE_READ_FSCREDS: PtraceAccessMode = PtraceAccessMode::from_bits_truncate(
251 PtraceAccessMode::READ.bits() | PtraceAccessMode::FSCREDS.bits(),
252);
253pub const PTRACE_MODE_READ_REALCREDS: PtraceAccessMode = PtraceAccessMode::from_bits_truncate(
254 PtraceAccessMode::READ.bits() | PtraceAccessMode::REALCREDS.bits(),
255);
256pub const PTRACE_MODE_ATTACH_FSCREDS: PtraceAccessMode = PtraceAccessMode::from_bits_truncate(
257 PtraceAccessMode::ATTACH.bits() | PtraceAccessMode::FSCREDS.bits(),
258);
259pub const PTRACE_MODE_ATTACH_REALCREDS: PtraceAccessMode = PtraceAccessMode::from_bits_truncate(
260 PtraceAccessMode::ATTACH.bits() | PtraceAccessMode::REALCREDS.bits(),
261);
262pub const PTRACE_MODE_NOAUDIT: PtraceAccessMode = PtraceAccessMode::NOAUDIT;
263
264#[derive(Debug, Clone)]
265pub struct Credentials {
266 pub uid: uid_t,
267 pub gid: gid_t,
268 pub euid: uid_t,
269 pub egid: gid_t,
270 pub saved_uid: uid_t,
271 pub saved_gid: gid_t,
272 pub groups: Vec<gid_t>,
273
274 pub fsuid: uid_t,
276
277 pub fsgid: gid_t,
279
280 pub cap_permitted: Capabilities,
290
291 pub cap_effective: Capabilities,
296
297 pub cap_inheritable: Capabilities,
308
309 pub cap_bounding: Capabilities,
317
318 pub cap_ambient: Capabilities,
327
328 pub securebits: SecureBits,
338
339 pub security_state: TaskAttrs,
341}
342
343bitflags! {
344 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
345 pub struct SecureBits: u32 {
346 const KEEP_CAPS = 1 << uapi::SECURE_KEEP_CAPS;
347 const KEEP_CAPS_LOCKED = 1 << uapi::SECURE_KEEP_CAPS_LOCKED;
348 const NO_SETUID_FIXUP = 1 << uapi::SECURE_NO_SETUID_FIXUP;
349 const NO_SETUID_FIXUP_LOCKED = 1 << uapi::SECURE_NO_SETUID_FIXUP_LOCKED;
350 const NOROOT = 1 << uapi::SECURE_NOROOT;
351 const NOROOT_LOCKED = 1 << uapi::SECURE_NOROOT_LOCKED;
352 const NO_CAP_AMBIENT_RAISE = 1 << uapi::SECURE_NO_CAP_AMBIENT_RAISE;
353 const NO_CAP_AMBIENT_RAISE_LOCKED = 1 << uapi::SECURE_NO_CAP_AMBIENT_RAISE_LOCKED;
354 }
355}
356
357static ROOT_CREDENTIALS: LazyLock<Arc<Credentials>> =
358 LazyLock::new(|| Arc::new(Credentials::with_ids(0, 0)));
359
360impl Credentials {
361 pub fn root() -> Arc<Self> {
363 ROOT_CREDENTIALS.clone()
364 }
365
366 pub fn with_ids(uid: uid_t, gid: gid_t) -> Credentials {
369 let caps = if uid == 0 { Capabilities::all() } else { Capabilities::empty() };
370 Credentials {
371 uid,
372 gid,
373 euid: uid,
374 egid: gid,
375 saved_uid: uid,
376 saved_gid: gid,
377 groups: vec![],
378 fsuid: uid,
379 fsgid: gid,
380 cap_permitted: caps,
381 cap_effective: caps,
382 cap_inheritable: Capabilities::empty(),
383 cap_bounding: Capabilities::all(),
384 cap_ambient: Capabilities::empty(),
385 securebits: SecureBits::empty(),
386 security_state: TaskAttrs::for_kernel(),
387 }
388 }
389
390 pub fn is_in_group(&self, gid: gid_t) -> bool {
391 self.egid == gid || self.groups.contains(&gid)
392 }
393
394 pub fn set_securebits(&mut self, securebits: SecureBits) -> Result<(), Errno> {
396 let mut locked_bits = SecureBits::empty();
398 if self.securebits.contains(SecureBits::NOROOT_LOCKED) {
399 locked_bits |= SecureBits::NOROOT | SecureBits::NOROOT_LOCKED;
400 }
401 if self.securebits.contains(SecureBits::KEEP_CAPS_LOCKED) {
402 locked_bits |= SecureBits::KEEP_CAPS | SecureBits::KEEP_CAPS_LOCKED;
403 }
404 if self.securebits.contains(SecureBits::NO_SETUID_FIXUP_LOCKED) {
405 locked_bits |= SecureBits::NO_SETUID_FIXUP | SecureBits::NO_SETUID_FIXUP_LOCKED;
406 }
407 if self.securebits.contains(SecureBits::NO_CAP_AMBIENT_RAISE_LOCKED) {
408 locked_bits |=
409 SecureBits::NO_CAP_AMBIENT_RAISE | SecureBits::NO_CAP_AMBIENT_RAISE_LOCKED;
410 }
411
412 if securebits & locked_bits != self.securebits & locked_bits {
413 return error!(EPERM);
414 }
415
416 self.securebits = securebits;
417 Ok(())
418 }
419
420 pub fn as_fscred(&self) -> FsCred {
421 FsCred { uid: self.fsuid, gid: self.fsgid }
422 }
423
424 pub fn euid_as_fscred(&self) -> FsCred {
425 FsCred { uid: self.euid, gid: self.egid }
426 }
427
428 pub fn uid_as_fscred(&self) -> FsCred {
429 FsCred { uid: self.uid, gid: self.gid }
430 }
431
432 pub fn update_capabilities(&mut self, prev: &Credentials) {
441 if self.securebits.contains(SecureBits::NO_SETUID_FIXUP) {
458 return;
459 }
460 let was_any_zero = prev.uid == 0 || prev.euid == 0 || prev.saved_uid == 0;
461 let is_all_nonzero = self.uid != 0 && self.euid != 0 && self.saved_uid != 0;
462
463 if was_any_zero && is_all_nonzero {
464 if !self.securebits.contains(SecureBits::KEEP_CAPS) {
465 self.cap_permitted = Capabilities::empty();
466 self.cap_effective = Capabilities::empty();
467 }
468 self.cap_ambient = Capabilities::empty();
469 }
470 if prev.euid == 0 && self.euid != 0 {
473 self.cap_effective = Capabilities::empty();
474 } else if prev.euid != 0 && self.euid == 0 {
475 self.cap_effective = self.cap_permitted;
478 }
479
480 let fs_capabilities = CAP_CHOWN
487 | CAP_DAC_OVERRIDE
488 | CAP_DAC_READ_SEARCH
489 | CAP_FOWNER
490 | CAP_FSETID
491 | CAP_LINUX_IMMUTABLE
492 | CAP_MAC_OVERRIDE
493 | CAP_MKNOD;
494 if prev.fsuid == 0 && self.fsuid != 0 {
495 self.cap_effective &= !fs_capabilities;
496 } else if prev.fsuid != 0 && self.fsuid == 0 {
497 self.cap_effective |= self.cap_permitted & fs_capabilities;
501 }
502 }
503}
504
505#[derive(Debug, Clone, Copy)]
507pub struct FsCred {
508 pub uid: uid_t,
509 pub gid: gid_t,
510}
511
512impl FsCred {
513 pub const fn root() -> Self {
514 Self { uid: 0, gid: 0 }
515 }
516}
517
518impl From<Credentials> for FsCred {
519 fn from(c: Credentials) -> Self {
520 c.as_fscred()
521 }
522}
523
524#[derive(Debug, Default, Clone)]
525pub struct UserAndOrGroupId {
526 pub uid: Option<uid_t>,
527 pub gid: Option<gid_t>,
528}
529
530impl UserAndOrGroupId {
531 pub fn is_none(&self) -> bool {
532 self.uid.is_none() && self.gid.is_none()
533 }
534
535 pub fn is_some(&self) -> bool {
536 !self.is_none()
537 }
538
539 pub fn clear(&mut self) {
540 self.uid = None;
541 self.gid = None;
542 }
543}
544
545#[cfg(test)]
546mod tests {
547 use super::*;
548
549 #[::fuchsia::test]
550 fn test_empty() {
551 assert_eq!(Capabilities::empty().mask, 0);
552 }
553
554 #[::fuchsia::test]
555 fn test_union() {
556 let expected = Capabilities { mask: CAP_BLOCK_SUSPEND.mask | CAP_AUDIT_READ.mask };
557 assert_eq!(CAP_BLOCK_SUSPEND.union(CAP_AUDIT_READ), expected);
558 assert_eq!(CAP_BLOCK_SUSPEND.union(CAP_BLOCK_SUSPEND), CAP_BLOCK_SUSPEND);
559 }
560
561 #[::fuchsia::test]
562 fn test_difference() {
563 let base = CAP_BPF | CAP_AUDIT_WRITE;
564 let expected = CAP_BPF;
565 assert_eq!(base.difference(CAP_AUDIT_WRITE), expected);
566 assert_eq!(base.difference(CAP_AUDIT_WRITE | CAP_BPF), Capabilities::empty());
567 }
568
569 #[::fuchsia::test]
570 fn test_contains() {
571 let base = CAP_BPF | CAP_AUDIT_WRITE;
572 assert!(base.contains(CAP_AUDIT_WRITE));
573 assert!(base.contains(CAP_BPF));
574 assert!(base.contains(CAP_AUDIT_WRITE | CAP_BPF));
575
576 assert!(!base.contains(CAP_AUDIT_CONTROL));
577 assert!(!base.contains(CAP_AUDIT_WRITE | CAP_BPF | CAP_AUDIT_CONTROL));
578 }
579
580 #[::fuchsia::test]
581 fn test_insert() {
582 let mut capabilities = CAP_BLOCK_SUSPEND;
583 capabilities.insert(CAP_BLOCK_SUSPEND);
584 assert_eq!(capabilities, CAP_BLOCK_SUSPEND);
585
586 capabilities.insert(CAP_AUDIT_READ);
587 let expected = Capabilities { mask: CAP_BLOCK_SUSPEND.mask | CAP_AUDIT_READ.mask };
588 assert_eq!(capabilities, expected);
589 }
590
591 #[::fuchsia::test]
592 fn test_remove() {
593 let mut capabilities = CAP_BLOCK_SUSPEND;
594 capabilities.remove(CAP_BLOCK_SUSPEND);
595 assert_eq!(capabilities, Capabilities::empty());
596
597 let mut capabilities = CAP_BLOCK_SUSPEND | CAP_AUDIT_READ;
598 capabilities.remove(CAP_AUDIT_READ);
599 assert_eq!(capabilities, CAP_BLOCK_SUSPEND);
600 }
601
602 #[::fuchsia::test]
603 fn test_try_from() {
604 let capabilities = CAP_BLOCK_SUSPEND;
605 assert_eq!(Capabilities::try_from(uapi::CAP_BLOCK_SUSPEND as u64), Ok(capabilities));
606
607 assert_eq!(Capabilities::try_from(200000), error!(EINVAL));
608 }
609}