starnix_uapi/
auth.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![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// We don't use bitflags for this because capability sets can have bits set that don't have defined
14// meaning as capabilities. init has all 64 bits set, even though only 40 of them are valid.
15#[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    // rhs is the "right-hand side" of the expression `a & b`
89    fn bitand(self, rhs: Self) -> Self::Output {
90        Self { mask: self.mask & rhs.mask }
91    }
92}
93
94impl ops::BitAndAssign for Capabilities {
95    // rhs is the "right-hand side" of the expression `a & b`
96    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    /// See https://man7.org/linux/man-pages/man2/setfsuid.2.html
273    pub fsuid: uid_t,
274
275    /// See https://man7.org/linux/man-pages/man2/setfsgid.2.html
276    pub fsgid: gid_t,
277
278    /// From https://man7.org/linux/man-pages/man7/capabilities.7.html
279    ///
280    /// > This is a limiting superset for the effective capabilities that the thread may assume. It
281    /// > is also a limiting superset for the capabilities that may be added to the inheritable set
282    /// > by a thread that does not have the CAP_SETPCAP capability in its effective set.
283    ///
284    /// > If a thread drops a capability from its permitted set, it can never reacquire that
285    /// > capability (unless it execve(2)s either a set-user-ID-root program, or a program whose
286    /// > associated file capabilities grant that capability).
287    pub cap_permitted: Capabilities,
288
289    /// From https://man7.org/linux/man-pages/man7/capabilities.7.html
290    ///
291    /// > This is the set of capabilities used by the kernel to perform permission checks for the
292    /// > thread.
293    pub cap_effective: Capabilities,
294
295    /// From https://man7.org/linux/man-pages/man7/capabilities.7.html
296    ///
297    /// > This is a set of capabilities preserved across an execve(2).  Inheritable capabilities
298    /// > remain inheritable when executing any program, and inheritable capabilities are added to
299    /// > the permitted set when executing a program that has the corresponding bits set in the file
300    /// > inheritable set.
301    ///
302    /// > Because inheritable capabilities are not generally preserved across execve(2) when running
303    /// > as a non-root user, applications that wish to run helper programs with elevated
304    /// > capabilities should consider using ambient capabilities, described below.
305    pub cap_inheritable: Capabilities,
306
307    /// From https://man7.org/linux/man-pages/man7/capabilities.7.html
308    ///
309    /// > The capability bounding set is a mechanism that can be used to limit the capabilities that
310    /// > are gained during execve(2).
311    ///
312    /// > Since Linux 2.6.25, this is a per-thread capability set. In older kernels, the capability
313    /// > bounding set was a system wide attribute shared by all threads on the system.
314    pub cap_bounding: Capabilities,
315
316    /// From https://man7.org/linux/man-pages/man7/capabilities.7.html
317    ///
318    /// > This is a set of capabilities that are preserved across an execve(2) of a program that is
319    /// > not privileged.  The ambient capability set obeys the invariant that no capability can
320    /// > ever be ambient if it is not both permitted and inheritable.
321    ///
322    /// > Executing a program that changes UID or GID due to the set-user-ID or set-group-ID bits
323    /// > or executing a program that has any file capabilities set will clear the ambient set.
324    pub cap_ambient: Capabilities,
325
326    /// From https://man7.org/linux/man-pages/man7/capabilities.7.html
327    ///
328    /// > Starting with kernel 2.6.26, and with a kernel in which file capabilities are enabled,
329    /// > Linux implements a set of per-thread securebits flags that can be used to disable special
330    /// > handling of capabilities for UID 0 (root).
331    ///
332    /// > The securebits flags can be modified and retrieved using the prctl(2)
333    /// > PR_SET_SECUREBITS and PR_GET_SECUREBITS operations.  The CAP_SETPCAP capability is
334    /// > required to modify the flags.
335    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    /// Creates a set of credentials with all possible permissions and capabilities.
357    pub fn root() -> Arc<Self> {
358        ROOT_CREDENTIALS.clone()
359    }
360
361    /// Creates a set of credentials with the given uid and gid. If the uid is 0, the credentials
362    /// will grant superuser access.
363    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    /// Returns whether or not the task has the given `capability`.
393    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        // From <https://man7.org/linux/man-pages/man2/execve.2.html>:
420        //
421        //   If the set-user-ID bit is set on the program file referred to by
422        //   pathname, then the effective user ID of the calling process is
423        //   changed to that of the owner of the program file.  Similarly, if
424        //   the set-group-ID bit is set on the program file, then the
425        //   effective group ID of the calling process is set to the group of
426        //   the program file.
427        self.apply_suid_and_sgid(maybe_set);
428
429        // From <https://man7.org/linux/man-pages/man2/execve.2.html>:
430        //
431        //   The effective user ID of the process is copied to the saved set-
432        //   user-ID; similarly, the effective group ID is copied to the saved
433        //   set-group-ID.  This copying takes place after any effective ID
434        //   changes that occur because of the set-user-ID and set-group-ID
435        //   mode bits.
436        self.saved_uid = self.euid;
437        self.saved_gid = self.egid;
438
439        // From <https://man7.org/linux/man-pages/man7/capabilities.7.html>:
440        //
441        //   During an execve(2), the kernel calculates the new capabilities
442        //   of the process using the following algorithm:
443        //   P'(ambient)     = (file is privileged) ? 0 : P(ambient)
444        //   P'(permitted)   = (P(inheritable) & F(inheritable)) |
445        //                     (F(permitted) & P(bounding)) | P'(ambient)
446        //   P'(effective)   = F(effective) ? P'(permitted) : P'(ambient)
447        //   P'(inheritable) = P(inheritable)    [i.e., unchanged]
448        //   P'(bounding)    = P(bounding)       [i.e., unchanged]
449        // where:
450        //   P()    denotes the value of a thread capability set before
451        //          the execve(2)
452        //   P'()   denotes the value of a thread capability set after the
453        //          execve(2)
454        //   F()    denotes a file capability set
455
456        // a privileged file is one that has capabilities or
457        // has the set-user-ID or set-group-ID bit set.
458        // TODO(https://fxbug.dev/328629782): Add support for file capabilities.
459        let file_is_privileged = is_suid_or_sgid;
460
461        // After having performed any changes to the process effective ID
462        // that were triggered by the set-user-ID mode bit of the binary—
463        // e.g., switching the effective user ID to 0 (root) because a set-
464        // user-ID-root program was executed—the kernel calculates the file
465        // capability sets as follows:
466
467        // (1)  If the real or effective user ID of the process is 0 (root),
468        //  then the file inheritable and permitted sets are ignored;
469        //  instead they are notionally considered to be all ones (i.e.,
470        //  all capabilities enabled).
471        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        // (2)  If the effective user ID of the process is 0 (root) or the
478        //  file effective bit is in fact enabled, then the file
479        //  effective bit is notionally defined to be one (enabled).
480        let file_effective = self.euid == 0;
481
482        // TODO(https://fxbug.dev/328629782): File capabilities are honored for set-user-ID-root
483        // binaries with capabilities executed by non-root users. See "Set-user-ID-root programs
484        // that have file capabilities" in the man page.
485
486        //   P'(ambient)     = (file is privileged) ? 0 : P(ambient)
487        self.cap_ambient =
488            if file_is_privileged { Capabilities::empty() } else { self.cap_ambient };
489
490        //   P'(permitted)   = (P(inheritable) & F(inheritable)) |
491        //                     (F(permitted) & P(bounding)) | P'(ambient)
492        self.cap_permitted = (self.cap_inheritable & file_inheritable)
493            | (file_permitted & self.cap_bounding)
494            | self.cap_ambient;
495
496        //   P'(effective)   = F(effective) ? P'(permitted) : P'(ambient)
497        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        // https://man7.org/linux/man-pages/man7/capabilities.7.html
525        // If one or more of the real, effective, or saved set user IDs
526        // was previously 0, and as a result of the UID changes all of
527        // these IDs have a nonzero value, then all capabilities are
528        // cleared from the permitted, effective, and ambient capability
529        // sets.
530        //
531        // SECBIT_KEEP_CAPS: Setting this flag allows a thread that has one or more 0
532        // UIDs to retain capabilities in its permitted set when it
533        // switches all of its UIDs to nonzero values.
534        // The setting of the SECBIT_KEEP_CAPS flag is ignored if the
535        // SECBIT_NO_SETUID_FIXUP flag is set.  (The latter flag
536        // provides a superset of the effect of the former flag.)
537        // SECBIT_NO_SETUID_FIXUP: Setting  this  flag  stops  the  kernel from adjusting
538        // the process's permitted, effective, and ambient capability sets when the thread's
539        // effective and filesystem UIDs are switched between zero and nonzero values.
540        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 the effective user ID is changed from 0 to nonzero, then
552        // all capabilities are cleared from the effective set.
553        if prev.euid == 0 && self.euid != 0 {
554            self.cap_effective = Capabilities::empty();
555        } else if prev.euid != 0 && self.euid == 0 {
556            // If the effective user ID is changed from nonzero to 0, then
557            // the permitted set is copied to the effective set.
558            self.cap_effective = self.cap_permitted;
559        }
560
561        // If the filesystem user ID is changed from 0 to nonzero (see
562        // setfsuid(2)), then the following capabilities are cleared from
563        // the effective set: CAP_CHOWN, CAP_DAC_OVERRIDE,
564        // CAP_DAC_READ_SEARCH, CAP_FOWNER, CAP_FSETID,
565        // CAP_LINUX_IMMUTABLE (since Linux 2.6.30), CAP_MAC_OVERRIDE,
566        // and CAP_MKNOD (since Linux 2.6.30).
567        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            // If the filesystem UID is changed from nonzero to 0, then any
579            // of these capabilities that are enabled in the permitted set
580            // are enabled in the effective set.
581            self.cap_effective |= self.cap_permitted & fs_capabilities;
582        }
583    }
584}
585
586/// The owner and group of a file. Used as a parameter for functions that create files.
587#[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        // all() should be every bit set, not just all the CAP_* constants.
646        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}