use crate::oslib;
use crate::passthrough::util::einval;
use crate::soft_idmap::{HostGid, HostUid, Id};
use std::io;
pub struct UnixCredentials {
uid: HostUid,
gid: HostGid,
sup_gid: Option<HostGid>,
keep_capability: bool,
}
impl UnixCredentials {
pub fn new(uid: HostUid, gid: HostGid) -> Self {
UnixCredentials {
uid,
gid,
sup_gid: None,
keep_capability: false,
}
}
pub fn supplementary_gid(self, supported_extension: bool, sup_gid: Option<HostGid>) -> Self {
UnixCredentials {
uid: self.uid,
gid: self.gid,
sup_gid,
keep_capability: !supported_extension,
}
}
pub fn set(self) -> io::Result<Option<UnixCredentialsGuard>> {
let current_uid = HostUid::from(unsafe { libc::geteuid() });
let current_gid = HostGid::from(unsafe { libc::getegid() });
let change_uid = !self.uid.is_root() && self.uid != current_uid;
let change_gid = !self.gid.is_root() && self.gid != current_gid;
if let Some(sup_gid) = self.sup_gid {
oslib::setsupgroup(sup_gid)?;
}
if change_gid {
oslib::seteffgid(self.gid)?;
}
if change_uid {
oslib::seteffuid(self.uid)?;
}
if change_uid && self.keep_capability {
if let Err(e) = crate::util::add_cap_to_eff("DAC_OVERRIDE") {
warn!("failed to add 'DAC_OVERRIDE' to the effective set of capabilities: {e}");
}
}
if !change_uid && !change_gid {
return Ok(None);
}
Ok(Some(UnixCredentialsGuard {
reset_uid: change_uid.then_some(current_uid),
reset_gid: change_gid.then_some(current_gid),
drop_sup_gid: self.sup_gid.is_some(),
}))
}
}
pub struct UnixCredentialsGuard {
reset_uid: Option<HostUid>,
reset_gid: Option<HostGid>,
drop_sup_gid: bool,
}
impl Drop for UnixCredentialsGuard {
fn drop(&mut self) {
if let Some(uid) = self.reset_uid {
oslib::seteffuid(uid).unwrap_or_else(|e| {
error!("failed to change uid back to {uid}: {e}");
});
}
if let Some(gid) = self.reset_gid {
oslib::seteffgid(gid).unwrap_or_else(|e| {
error!("failed to change gid back to {gid}: {e}");
});
}
if self.drop_sup_gid {
oslib::dropsupgroups().unwrap_or_else(|e| {
error!("failed to drop supplementary groups: {e}");
});
}
}
}
pub struct ScopedCaps {
cap: capng::Capability,
}
impl ScopedCaps {
fn new(cap_name: &str) -> io::Result<Option<Self>> {
use capng::{Action, CUpdate, Set, Type};
let cap = capng::name_to_capability(cap_name).map_err(|_| {
let err = io::Error::last_os_error();
error!("couldn't get the capability id for name {cap_name}: {err:?}");
err
})?;
if capng::have_capability(Type::EFFECTIVE, cap) {
let req = vec![CUpdate {
action: Action::DROP,
cap_type: Type::EFFECTIVE,
capability: cap,
}];
capng::update(req).map_err(|e| {
error!("couldn't drop {cap} capability: {e:?}");
einval()
})?;
capng::apply(Set::CAPS).map_err(|e| {
error!("couldn't apply capabilities after dropping {cap}: {e:?}");
einval()
})?;
Ok(Some(Self { cap }))
} else {
Ok(None)
}
}
}
impl Drop for ScopedCaps {
fn drop(&mut self) {
use capng::{Action, CUpdate, Set, Type};
let req = vec![CUpdate {
action: Action::ADD,
cap_type: Type::EFFECTIVE,
capability: self.cap,
}];
if let Err(e) = capng::update(req) {
panic!("couldn't restore {} capability: {:?}", self.cap, e);
}
if let Err(e) = capng::apply(Set::CAPS) {
panic!(
"couldn't apply capabilities after restoring {}: {:?}",
self.cap, e
);
}
}
}
pub fn drop_effective_cap(cap_name: &str) -> io::Result<Option<ScopedCaps>> {
ScopedCaps::new(cap_name)
}