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