Skip to main content

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::selinux::TaskAttrs;
9use crate::{gid_t, uapi, uid_t};
10use bitflags::bitflags;
11use std::ops;
12use std::sync::{Arc, LazyLock};
13
14// We don't use bitflags for this because capability sets can have bits set that don't have defined
15// meaning as capabilities. init has all 64 bits set, even though only 40 of them are valid.
16#[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    // rhs is the "right-hand side" of the expression `a & b`
90    fn bitand(self, rhs: Self) -> Self::Output {
91        Self { mask: self.mask & rhs.mask }
92    }
93}
94
95impl ops::BitAndAssign for Capabilities {
96    // rhs is the "right-hand side" of the expression `a & b`
97    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    /// See https://man7.org/linux/man-pages/man2/setfsuid.2.html
274    pub fsuid: uid_t,
275
276    /// See https://man7.org/linux/man-pages/man2/setfsgid.2.html
277    pub fsgid: gid_t,
278
279    /// From https://man7.org/linux/man-pages/man7/capabilities.7.html
280    ///
281    /// > This is a limiting superset for the effective capabilities that the thread may assume. It
282    /// > is also a limiting superset for the capabilities that may be added to the inheritable set
283    /// > by a thread that does not have the CAP_SETPCAP capability in its effective set.
284    ///
285    /// > If a thread drops a capability from its permitted set, it can never reacquire that
286    /// > capability (unless it execve(2)s either a set-user-ID-root program, or a program whose
287    /// > associated file capabilities grant that capability).
288    pub cap_permitted: Capabilities,
289
290    /// From https://man7.org/linux/man-pages/man7/capabilities.7.html
291    ///
292    /// > This is the set of capabilities used by the kernel to perform permission checks for the
293    /// > thread.
294    pub cap_effective: Capabilities,
295
296    /// From https://man7.org/linux/man-pages/man7/capabilities.7.html
297    ///
298    /// > This is a set of capabilities preserved across an execve(2).  Inheritable capabilities
299    /// > remain inheritable when executing any program, and inheritable capabilities are added to
300    /// > the permitted set when executing a program that has the corresponding bits set in the file
301    /// > inheritable set.
302    ///
303    /// > Because inheritable capabilities are not generally preserved across execve(2) when running
304    /// > as a non-root user, applications that wish to run helper programs with elevated
305    /// > capabilities should consider using ambient capabilities, described below.
306    pub cap_inheritable: Capabilities,
307
308    /// From https://man7.org/linux/man-pages/man7/capabilities.7.html
309    ///
310    /// > The capability bounding set is a mechanism that can be used to limit the capabilities that
311    /// > are gained during execve(2).
312    ///
313    /// > Since Linux 2.6.25, this is a per-thread capability set. In older kernels, the capability
314    /// > bounding set was a system wide attribute shared by all threads on the system.
315    pub cap_bounding: Capabilities,
316
317    /// From https://man7.org/linux/man-pages/man7/capabilities.7.html
318    ///
319    /// > This is a set of capabilities that are preserved across an execve(2) of a program that is
320    /// > not privileged.  The ambient capability set obeys the invariant that no capability can
321    /// > ever be ambient if it is not both permitted and inheritable.
322    ///
323    /// > Executing a program that changes UID or GID due to the set-user-ID or set-group-ID bits
324    /// > or executing a program that has any file capabilities set will clear the ambient set.
325    pub cap_ambient: Capabilities,
326
327    /// From https://man7.org/linux/man-pages/man7/capabilities.7.html
328    ///
329    /// > Starting with kernel 2.6.26, and with a kernel in which file capabilities are enabled,
330    /// > Linux implements a set of per-thread securebits flags that can be used to disable special
331    /// > handling of capabilities for UID 0 (root).
332    ///
333    /// > The securebits flags can be modified and retrieved using the prctl(2)
334    /// > PR_SET_SECUREBITS and PR_GET_SECUREBITS operations.  The CAP_SETPCAP capability is
335    /// > required to modify the flags.
336    pub securebits: SecureBits,
337
338    /// The SELinux security state of the task.
339    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    /// Creates a set of credentials with all possible permissions and capabilities.
361    pub fn root() -> Arc<Self> {
362        ROOT_CREDENTIALS.clone()
363    }
364
365    /// Creates a set of credentials with the given uid and gid. If the uid is 0, the credentials
366    /// will grant superuser access.
367    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    /// Updates the `securebits` field, taking into account *`_LOCKED` bits.
394    pub fn set_securebits(&mut self, securebits: SecureBits) -> Result<(), Errno> {
395        // If a lock bit is set then neither it nor the corresponding `SECBIT_*` can be changed.
396        let mut locked_bits = SecureBits::empty();
397        if self.securebits.contains(SecureBits::NOROOT_LOCKED) {
398            locked_bits |= SecureBits::NOROOT | SecureBits::NOROOT_LOCKED;
399        }
400        if self.securebits.contains(SecureBits::KEEP_CAPS_LOCKED) {
401            locked_bits |= SecureBits::KEEP_CAPS | SecureBits::KEEP_CAPS_LOCKED;
402        }
403        if self.securebits.contains(SecureBits::NO_SETUID_FIXUP_LOCKED) {
404            locked_bits |= SecureBits::NO_SETUID_FIXUP | SecureBits::NO_SETUID_FIXUP_LOCKED;
405        }
406        if self.securebits.contains(SecureBits::NO_CAP_AMBIENT_RAISE_LOCKED) {
407            locked_bits |=
408                SecureBits::NO_CAP_AMBIENT_RAISE | SecureBits::NO_CAP_AMBIENT_RAISE_LOCKED;
409        }
410
411        if securebits & locked_bits != self.securebits & locked_bits {
412            return error!(EPERM);
413        }
414
415        self.securebits = securebits;
416        Ok(())
417    }
418
419    pub fn exec(&mut self, prev: &Credentials) -> bool {
420        // From <https://man7.org/linux/man-pages/man7/credentials.7.html>:
421        //
422        //   On exec, the filesystem UIDs are always reset to the effective UIDs.
423        self.fsuid = self.euid;
424        self.fsgid = self.egid;
425
426        // From <https://man7.org/linux/man-pages/man2/execve.2.html>:
427        //
428        //   The effective user ID of the process is copied to the saved set-
429        //   user-ID; similarly, the effective group ID is copied to the saved
430        //   set-group-ID.  This copying takes place after any effective ID
431        //   changes that occur because of the set-user-ID and set-group-ID
432        //   mode bits.
433        self.saved_uid = self.euid;
434        self.saved_gid = self.egid;
435
436        // From <https://man7.org/linux/man-pages/man7/capabilities.7.html>:
437        //
438        //   During an execve(2), the kernel calculates the new capabilities
439        //   of the process using the following algorithm:
440        //   P'(ambient)     = (file is privileged) ? 0 : P(ambient)
441        //   P'(permitted)   = (P(inheritable) & F(inheritable)) |
442        //                     (F(permitted) & P(bounding)) | P'(ambient)
443        //   P'(effective)   = F(effective) ? P'(permitted) : P'(ambient)
444        //   P'(inheritable) = P(inheritable)    [i.e., unchanged]
445        //   P'(bounding)    = P(bounding)       [i.e., unchanged]
446        // where:
447        //   P()    denotes the value of a thread capability set before
448        //          the execve(2)
449        //   P'()   denotes the value of a thread capability set after the
450        //          execve(2)
451        //   F()    denotes a file capability set
452
453        // From <https://man7.org/linux/man-pages/man7/capabilities.7.html>:
454        //
455        //   A privileged file is one that has capabilities or has the set-user-ID
456        //   or set-group-ID bit set.
457        //
458        // TODO(https://fxbug.dev/328629782): Add support for file capabilities.
459        //
460        // In Linux, a SUID/SGID bit only makes the file privileged if it actually
461        // changes the effective UID/GID of the process.
462        let file_is_privileged = self.euid != prev.euid || self.egid != prev.egid;
463
464        // From <https://man7.org/linux/man-pages/man7/capabilities.7.html>:
465        //
466        //   After having performed any changes to the process effective ID
467        //   that were triggered by the set-user-ID mode bit of the binary—
468        //   e.g., switching the effective user ID to 0 (root) because a set-
469        //   user-ID-root program was executed—the kernel calculates the file
470        //   capability sets as follows:
471
472        // From <https://man7.org/linux/man-pages/man7/capabilities.7.html>:
473        //
474        //   (1)  If the real or effective user ID of the process is 0 (root),
475        //        then the file inheritable and permitted sets are ignored;
476        //        instead they are notionally considered to be all ones (i.e.,
477        //        all capabilities enabled).
478        let (file_permitted, file_inheritable) =
479            if (self.uid == 0 || self.euid == 0) && !self.securebits.contains(SecureBits::NOROOT) {
480                (Capabilities::all(), Capabilities::all())
481            } else {
482                (Capabilities::empty(), Capabilities::empty())
483            };
484
485        // From <https://man7.org/linux/man-pages/man7/capabilities.7.html>:
486        //
487        //   (2)  If the effective user ID of the process is 0 (root) or the
488        //        file effective bit is in fact enabled, then the file
489        //        effective bit is notionally defined to be one (enabled).
490        let file_effective = self.euid == 0 && !self.securebits.contains(SecureBits::NOROOT);
491
492        // TODO(https://fxbug.dev/328629782): File capabilities are honored for set-user-ID-root
493        // binaries with capabilities executed by non-root users. See "Set-user-ID-root programs
494        // that have file capabilities" in the man page.
495
496        // From <https://man7.org/linux/man-pages/man7/capabilities.7.html>:
497        //
498        //   Ambient capabilities are cleared on execve(2) if the process's real
499        //   or effective user ID is changed (e.g., executing a set-user-ID
500        //   program) or if the program has file capabilities.
501        //
502        // In addition to SUID/SGID or file capabilities, Linux clears the ambient set if the exec
503        // is "secure" (secureexec), which includes cases where the real and effective UIDs/GIDs
504        // are different (split UIDs/GIDs).
505        let is_secure_exec = file_is_privileged || self.uid != self.euid || self.gid != self.egid;
506
507        //   P'(ambient)     = (exec is secure) ? 0 : P(ambient)
508        self.cap_ambient = if is_secure_exec { Capabilities::empty() } else { self.cap_ambient };
509
510        //   P'(permitted)   = (P(inheritable) & F(inheritable)) |
511        //                     (F(permitted) & P(bounding)) | P'(ambient)
512        self.cap_permitted = (self.cap_inheritable & file_inheritable)
513            | (file_permitted & self.cap_bounding)
514            | self.cap_ambient;
515
516        //   P'(effective)   = F(effective) ? P'(permitted) : P'(ambient)
517        self.cap_effective = if file_effective { self.cap_permitted } else { self.cap_ambient };
518
519        self.securebits.remove(SecureBits::KEEP_CAPS);
520
521        is_secure_exec
522    }
523
524    pub fn as_fscred(&self) -> FsCred {
525        FsCred { uid: self.fsuid, gid: self.fsgid }
526    }
527
528    pub fn euid_as_fscred(&self) -> FsCred {
529        FsCred { uid: self.euid, gid: self.egid }
530    }
531
532    pub fn uid_as_fscred(&self) -> FsCred {
533        FsCred { uid: self.uid, gid: self.gid }
534    }
535
536    /// Adjusts the capability sets (permitted, effective, and ambient) of these credentials
537    /// to reflect changes in user IDs (UID, EUID, Saved UID, or FSUID) from a previous state.
538    ///
539    /// This method compares the current state of these credentials against the `prev`
540    /// credentials to implement the Linux security model rules for UID transitions (as described
541    /// in `capabilities(7)` under "Effect of user ID changes on capabilities"). It is typically
542    /// called when preparing a new set of `Credentials` during `setuid()` family syscalls or
543    /// during `exec` after UID/GID bits have been applied.
544    pub fn update_capabilities(&mut self, prev: &Credentials) {
545        // https://man7.org/linux/man-pages/man7/capabilities.7.html
546        // If one or more of the real, effective, or saved set user IDs
547        // was previously 0, and as a result of the UID changes all of
548        // these IDs have a nonzero value, then all capabilities are
549        // cleared from the permitted, effective, and ambient capability
550        // sets.
551        //
552        // SECBIT_KEEP_CAPS: Setting this flag allows a thread that has one or more 0
553        // UIDs to retain capabilities in its permitted set when it
554        // switches all of its UIDs to nonzero values.
555        // The setting of the SECBIT_KEEP_CAPS flag is ignored if the
556        // SECBIT_NO_SETUID_FIXUP flag is set.  (The latter flag
557        // provides a superset of the effect of the former flag.)
558        // SECBIT_NO_SETUID_FIXUP: Setting  this  flag  stops  the  kernel from adjusting
559        // the process's permitted, effective, and ambient capability sets when the thread's
560        // effective and filesystem UIDs are switched between zero and nonzero values.
561        if self.securebits.contains(SecureBits::NO_SETUID_FIXUP) {
562            return;
563        }
564        let was_any_zero = prev.uid == 0 || prev.euid == 0 || prev.saved_uid == 0;
565        let is_all_nonzero = self.uid != 0 && self.euid != 0 && self.saved_uid != 0;
566
567        if was_any_zero && is_all_nonzero {
568            if !self.securebits.contains(SecureBits::KEEP_CAPS) {
569                self.cap_permitted = Capabilities::empty();
570                self.cap_effective = Capabilities::empty();
571            }
572            self.cap_ambient = Capabilities::empty();
573        }
574        // If the effective user ID is changed from 0 to nonzero, then
575        // all capabilities are cleared from the effective set.
576        if prev.euid == 0 && self.euid != 0 {
577            self.cap_effective = Capabilities::empty();
578        } else if prev.euid != 0 && self.euid == 0 {
579            // If the effective user ID is changed from nonzero to 0, then
580            // the permitted set is copied to the effective set.
581            self.cap_effective = self.cap_permitted;
582        }
583
584        // If the filesystem user ID is changed from 0 to nonzero (see
585        // setfsuid(2)), then the following capabilities are cleared from
586        // the effective set: CAP_CHOWN, CAP_DAC_OVERRIDE,
587        // CAP_DAC_READ_SEARCH, CAP_FOWNER, CAP_FSETID,
588        // CAP_LINUX_IMMUTABLE (since Linux 2.6.30), CAP_MAC_OVERRIDE,
589        // and CAP_MKNOD (since Linux 2.6.30).
590        let fs_capabilities = CAP_CHOWN
591            | CAP_DAC_OVERRIDE
592            | CAP_DAC_READ_SEARCH
593            | CAP_FOWNER
594            | CAP_FSETID
595            | CAP_LINUX_IMMUTABLE
596            | CAP_MAC_OVERRIDE
597            | CAP_MKNOD;
598        if prev.fsuid == 0 && self.fsuid != 0 {
599            self.cap_effective &= !fs_capabilities;
600        } else if prev.fsuid != 0 && self.fsuid == 0 {
601            // If the filesystem UID is changed from nonzero to 0, then any
602            // of these capabilities that are enabled in the permitted set
603            // are enabled in the effective set.
604            self.cap_effective |= self.cap_permitted & fs_capabilities;
605        }
606    }
607}
608
609/// The owner and group of a file. Used as a parameter for functions that create files.
610#[derive(Debug, Clone, Copy)]
611pub struct FsCred {
612    pub uid: uid_t,
613    pub gid: gid_t,
614}
615
616impl FsCred {
617    pub const fn root() -> Self {
618        Self { uid: 0, gid: 0 }
619    }
620}
621
622impl From<Credentials> for FsCred {
623    fn from(c: Credentials) -> Self {
624        c.as_fscred()
625    }
626}
627
628#[derive(Debug, Default, Clone)]
629pub struct UserAndOrGroupId {
630    pub uid: Option<uid_t>,
631    pub gid: Option<gid_t>,
632}
633
634impl UserAndOrGroupId {
635    pub fn is_none(&self) -> bool {
636        self.uid.is_none() && self.gid.is_none()
637    }
638
639    pub fn is_some(&self) -> bool {
640        !self.is_none()
641    }
642
643    pub fn clear(&mut self) {
644        self.uid = None;
645        self.gid = None;
646    }
647}
648
649#[cfg(test)]
650mod tests {
651    use super::*;
652
653    #[::fuchsia::test]
654    fn test_empty() {
655        assert_eq!(Capabilities::empty().mask, 0);
656    }
657
658    #[::fuchsia::test]
659    fn test_all() {
660        // all() should be every bit set, not just all the CAP_* constants.
661        assert_eq!(Capabilities::all().mask, u64::MAX);
662    }
663
664    #[::fuchsia::test]
665    fn test_union() {
666        let expected = Capabilities { mask: CAP_BLOCK_SUSPEND.mask | CAP_AUDIT_READ.mask };
667        assert_eq!(CAP_BLOCK_SUSPEND.union(CAP_AUDIT_READ), expected);
668        assert_eq!(CAP_BLOCK_SUSPEND.union(CAP_BLOCK_SUSPEND), CAP_BLOCK_SUSPEND);
669    }
670
671    #[::fuchsia::test]
672    fn test_difference() {
673        let base = CAP_BPF | CAP_AUDIT_WRITE;
674        let expected = CAP_BPF;
675        assert_eq!(base.difference(CAP_AUDIT_WRITE), expected);
676        assert_eq!(base.difference(CAP_AUDIT_WRITE | CAP_BPF), Capabilities::empty());
677    }
678
679    #[::fuchsia::test]
680    fn test_contains() {
681        let base = CAP_BPF | CAP_AUDIT_WRITE;
682        assert!(base.contains(CAP_AUDIT_WRITE));
683        assert!(base.contains(CAP_BPF));
684        assert!(base.contains(CAP_AUDIT_WRITE | CAP_BPF));
685
686        assert!(!base.contains(CAP_AUDIT_CONTROL));
687        assert!(!base.contains(CAP_AUDIT_WRITE | CAP_BPF | CAP_AUDIT_CONTROL));
688    }
689
690    #[::fuchsia::test]
691    fn test_insert() {
692        let mut capabilities = CAP_BLOCK_SUSPEND;
693        capabilities.insert(CAP_BLOCK_SUSPEND);
694        assert_eq!(capabilities, CAP_BLOCK_SUSPEND);
695
696        capabilities.insert(CAP_AUDIT_READ);
697        let expected = Capabilities { mask: CAP_BLOCK_SUSPEND.mask | CAP_AUDIT_READ.mask };
698        assert_eq!(capabilities, expected);
699    }
700
701    #[::fuchsia::test]
702    fn test_remove() {
703        let mut capabilities = CAP_BLOCK_SUSPEND;
704        capabilities.remove(CAP_BLOCK_SUSPEND);
705        assert_eq!(capabilities, Capabilities::empty());
706
707        let mut capabilities = CAP_BLOCK_SUSPEND | CAP_AUDIT_READ;
708        capabilities.remove(CAP_AUDIT_READ);
709        assert_eq!(capabilities, CAP_BLOCK_SUSPEND);
710    }
711
712    #[::fuchsia::test]
713    fn test_try_from() {
714        let capabilities = CAP_BLOCK_SUSPEND;
715        assert_eq!(Capabilities::try_from(uapi::CAP_BLOCK_SUSPEND as u64), Ok(capabilities));
716
717        assert_eq!(Capabilities::try_from(200000), error!(EINVAL));
718    }
719}