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