blob: 4cb169d9674ee7d696ce927c28ffd4ef7bb47610 [file] [log] [blame] [edit]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use bitflags::bitflags;
use crate::types::*;
#[derive(Debug, Clone)]
pub struct Credentials {
pub uid: uid_t,
pub gid: gid_t,
pub euid: uid_t,
pub egid: gid_t,
pub saved_uid: uid_t,
pub saved_gid: gid_t,
pub groups: Vec<gid_t>,
/// See https://man7.org/linux/man-pages/man2/setfsuid.2.html
pub fsuid: uid_t,
/// See https://man7.org/linux/man-pages/man2/setfsgid.2.html
pub fsgid: gid_t,
/// From https://man7.org/linux/man-pages/man7/capabilities.7.html
///
/// > This is a limiting superset for the effective capabilities that the thread may assume. It
/// > is also a limiting superset for the capabilities that may be added to the inheritable set
/// > by a thread that does not have the CAP_SETPCAP capability in its effective set.
///
/// > If a thread drops a capability from its permitted set, it can never reacquire that
/// > capability (unless it execve(2)s either a set-user-ID-root program, or a program whose
/// > associated file capabilities grant that capability).
pub cap_permitted: Capabilities,
/// From https://man7.org/linux/man-pages/man7/capabilities.7.html
///
/// > This is the set of capabilities used by the kernel to perform permission checks for the
/// > thread.
pub cap_effective: Capabilities,
/// From https://man7.org/linux/man-pages/man7/capabilities.7.html
///
/// > This is a set of capabilities preserved across an execve(2). Inheritable capabilities
/// > remain inheritable when executing any program, and inheritable capabilities are added to
/// > the permitted set when executing a program that has the corresponding bits set in the file
/// > inheritable set.
///
/// > Because inheritable capabilities are not generally preserved across execve(2) when running
/// > as a non-root user, applications that wish to run helper programs with elevated
/// > capabilities should consider using ambient capabilities, described below.
pub cap_inheritable: Capabilities,
/// From https://man7.org/linux/man-pages/man7/capabilities.7.html
///
/// > The capability bounding set is a mechanism that can be used to limit the capabilities that
/// > are gained during execve(2).
///
/// > Since Linux 2.6.25, this is a per-thread capability set. In older kernels, the capability
/// > bounding set was a system wide attribute shared by all threads on the system.
pub cap_bounding: Capabilities,
/// From https://man7.org/linux/man-pages/man7/capabilities.7.html
///
/// > This is a set of capabilities that are preserved across an execve(2) of a program that is
/// > not privileged. The ambient capability set obeys the invariant that no capability can
/// > ever be ambient if it is not both permitted and inheritable.
///
/// > Executing a program that changes UID or GID due to the set-user-ID or set-group-ID bits
/// > or executing a program that has any file capabilities set will clear the ambient set.
pub cap_ambient: Capabilities,
/// From https://man7.org/linux/man-pages/man7/capabilities.7.html
///
/// > Starting with kernel 2.6.26, and with a kernel in which file capabilities are enabled,
/// > Linux implements a set of per-thread securebits flags that can be used to disable special
/// > handling of capabilities for UID 0 (root).
///
/// > The securebits flags can be modified and retrieved using the prctl(2)
/// > PR_SET_SECUREBITS and PR_GET_SECUREBITS operations. The CAP_SETPCAP capability is
/// > required to modify the flags.
pub securebits: SecureBits,
}
bitflags! {
pub struct SecureBits: u32 {
const KEEP_CAPS = 1 << uapi::SECURE_KEEP_CAPS;
const KEEP_CAPS_LOCKED = 1 << uapi::SECURE_KEEP_CAPS_LOCKED;
const NO_SETUID_FIXUP = 1 << uapi::SECURE_NO_SETUID_FIXUP;
const NO_SETUID_FIXUP_LOCKED = 1 << uapi::SECURE_NO_SETUID_FIXUP_LOCKED;
const NOROOT = 1 << uapi::SECURE_NOROOT;
const NOROOT_LOCKED = 1 << uapi::SECURE_NOROOT_LOCKED;
const NO_CAP_AMBIENT_RAISE = 1 << uapi::SECURE_NO_CAP_AMBIENT_RAISE;
const NO_CAP_AMBIENT_RAISE_LOCKED = 1 << uapi::SECURE_NO_CAP_AMBIENT_RAISE_LOCKED;
}
}
impl Credentials {
/// Creates a set of credentials with all possible permissions and capabilities.
pub fn root() -> Self {
Self::with_ids(0, 0)
}
/// Creates a set of credentials with the given uid and gid. If the uid is 0, the credentials
/// will grant superuser access.
pub fn with_ids(uid: uid_t, gid: gid_t) -> Credentials {
let caps = if uid == 0 { Capabilities::all() } else { Capabilities::empty() };
Credentials {
uid,
gid,
euid: uid,
egid: gid,
saved_uid: uid,
saved_gid: gid,
groups: vec![],
fsuid: uid,
fsgid: gid,
cap_permitted: caps,
cap_effective: caps,
cap_inheritable: caps,
cap_bounding: Capabilities::all(),
cap_ambient: Capabilities::empty(),
securebits: SecureBits::empty(),
}
}
/// Compares the user ID of `self` to that of `other`.
///
/// Used to check whether a task can signal another.
///
/// From https://man7.org/linux/man-pages/man2/kill.2.html:
///
/// > For a process to have permission to send a signal, it must either be
/// > privileged (under Linux: have the CAP_KILL capability in the user
/// > namespace of the target process), or the real or effective user ID of
/// > the sending process must equal the real or saved set- user-ID of the
/// > target process.
///
/// Returns true if the credentials are considered to have the same user ID.
pub fn has_same_uid(&self, other: &Credentials) -> bool {
self.euid == other.saved_uid
|| self.euid == other.uid
|| self.uid == other.uid
|| self.uid == other.saved_uid
}
pub fn is_superuser(&self) -> bool {
self.euid == 0
}
pub fn is_in_group(&self, gid: gid_t) -> bool {
self.egid == gid || self.groups.contains(&gid)
}
/// Returns whether or not the task has the given `capability`.
pub fn has_capability(&self, capability: Capabilities) -> bool {
self.cap_effective.contains(capability)
}
pub fn exec(&mut self) {
// > Ambient capabilities are added to the permitted set and assigned to the effective set
// > when execve(2) is called.
// https://man7.org/linux/man-pages/man7/capabilities.7.html
// When a process with nonzero UIDs execve(2)s a set-user-ID-
// root program that does not have capabilities attached, or when a
// process whose real and effective UIDs are zero execve(2)s a
// program, the calculation of the process's new permitted
// capabilities simplifies to: inheritable | bounding.
if self.uid == 0 && self.euid == 0 {
self.cap_permitted = self.cap_inheritable | self.cap_bounding;
} else {
// TODO(security): This should take file capabilities into account.
// (inheritable & file.inheritable) | (file.permitted & bounding) | ambient
self.cap_permitted = self.cap_inheritable | self.cap_ambient;
}
// TODO(security): This should take file capabilities into account.
// if file.effective { permitted | ambient } else { 0 }
self.cap_effective = self.cap_permitted;
self.securebits.remove(SecureBits::KEEP_CAPS);
}
pub fn as_fscred(&self) -> FsCred {
FsCred { uid: self.fsuid, gid: self.fsgid }
}
pub fn update_capabilities(
&mut self,
prev_uid: uid_t,
prev_euid: uid_t,
prev_fsuid: uid_t,
prev_saved_uid: uid_t,
) {
// https://man7.org/linux/man-pages/man7/capabilities.7.html
// If one or more of the real, effective, or saved set user IDs
// was previously 0, and as a result of the UID changes all of
// these IDs have a nonzero value, then all capabilities are
// cleared from the permitted, effective, and ambient capability
// sets.
//
// SECBIT_KEEP_CAPS: Setting this flag allows a thread that has one or more 0
// UIDs to retain capabilities in its permitted set when it
// switches all of its UIDs to nonzero values.
// The setting of the SECBIT_KEEP_CAPS flag is ignored if the
// SECBIT_NO_SETUID_FIXUP flag is set. (The latter flag
// provides a superset of the effect of the former flag.)
if !self.securebits.contains(SecureBits::KEEP_CAPS)
&& !self.securebits.contains(SecureBits::NO_SETUID_FIXUP)
&& (prev_uid == 0 || prev_euid == 0 || prev_saved_uid == 0)
&& (self.uid != 0 && self.euid != 0 && self.saved_uid != 0)
{
self.cap_permitted = Capabilities::empty();
self.cap_effective = Capabilities::empty();
self.cap_ambient = Capabilities::empty();
}
// If the effective user ID is changed from 0 to nonzero, then
// all capabilities are cleared from the effective set.
if prev_euid == 0 && self.euid != 0 {
self.cap_effective = Capabilities::empty();
} else if prev_euid != 0 && self.euid == 0 {
// If the effective user ID is changed from nonzero to 0, then
// the permitted set is copied to the effective set.
self.cap_effective = self.cap_permitted;
}
// If the filesystem user ID is changed from 0 to nonzero (see
// setfsuid(2)), then the following capabilities are cleared from
// the effective set: CAP_CHOWN, CAP_DAC_OVERRIDE,
// CAP_DAC_READ_SEARCH, CAP_FOWNER, CAP_FSETID,
// CAP_LINUX_IMMUTABLE (since Linux 2.6.30), CAP_MAC_OVERRIDE,
// and CAP_MKNOD (since Linux 2.6.30).
let fs_capabilities = CAP_CHOWN
| CAP_DAC_OVERRIDE
| CAP_DAC_READ_SEARCH
| CAP_FOWNER
| CAP_FSETID
| CAP_LINUX_IMMUTABLE
| CAP_MAC_OVERRIDE
| CAP_MKNOD;
if prev_fsuid == 0 && self.fsuid != 0 {
self.cap_effective &= !fs_capabilities;
} else if prev_fsuid != 0 && self.fsuid == 0 {
// If the filesystem UID is changed from nonzero to 0, then any
// of these capabilities that are enabled in the permitted set
// are enabled in the effective set.
self.cap_effective |= self.cap_permitted & fs_capabilities;
}
}
}
/// The owner and group of a file. Used as a parameter for functions that create files.
#[derive(Debug, Clone, Copy)]
pub struct FsCred {
pub uid: uid_t,
pub gid: gid_t,
}
impl FsCred {
pub fn root() -> Self {
Self { uid: 0, gid: 0 }
}
}
impl From<Credentials> for FsCred {
fn from(c: Credentials) -> Self {
c.as_fscred()
}
}