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