use std::os::unix::fs::MetadataExt;
use std::path::PathBuf;
use log::{error, warn};
use super::sysexits::*;
use super::system_config::SecurityConfig;
pub fn assume_system(
security: &SecurityConfig,
users_root: &mut PathBuf,
) -> Result<(), Sysexit> {
macro_rules! fatal {
($sysexit:expr, $($stuff:tt)*) => {{
error!($($stuff)*);
return Err($sysexit)
}}
}
let system_user = if security.system_user.is_empty() {
None
} else {
match nix::unistd::User::from_name(&security.system_user) {
Ok(Some(user)) => Some(user),
Ok(None) => fatal!(
EX_NOUSER,
"system_user '{}' does not exist!",
security.system_user
),
Err(e) => fatal!(
EX_OSFILE,
"Unable to look up system_user '{}': {}",
security.system_user,
e
),
}
};
if let Some(ref system_user) = system_user {
if system_user.uid != nix::unistd::getuid() {
if let Err(e) = nix::unistd::initgroups(
&std::ffi::CString::new(system_user.name.clone()).unwrap(),
system_user.gid,
) {
fatal!(
EX_OSERR,
"Unable to set up groups for system user: {}",
e
);
}
}
}
if security.chroot_system {
if let Err(e) =
nix::unistd::chroot(users_root)
.and_then(|_| nix::unistd::chdir("/"))
{
fatal!(
EX_OSERR,
"Failed to chroot to '{}': {}",
users_root.display(),
e
);
}
users_root.push("/");
}
if let Some(system_user) = system_user {
if system_user.uid != nix::unistd::getuid() {
if let Err(e) = nix::unistd::setgroups(&[system_user.gid]) {
fatal!(
EX_OSERR,
"Failed to set groups for UID {}: {}",
system_user.uid,
e
);
}
if let Err(e) = nix::unistd::setgid(system_user.gid)
.and_then(|_| nix::unistd::setuid(system_user.uid))
{
fatal!(
EX_OSERR,
"Failed to set UID:GID to {}:{}: {}",
system_user.uid,
system_user.gid,
e
);
}
}
}
Ok(())
}
pub fn assume_user_privileges(
log_prefix: &str,
chroot_system: bool,
user_dir: &mut PathBuf,
effective_only: bool,
) -> Result<(), Sysexit> {
if nix::unistd::ROOT != nix::unistd::getuid() {
return Ok(());
}
let md = match user_dir.metadata() {
Ok(md) => md,
Err(e) => {
error!(
"{} Failed to stat '{}': {}",
log_prefix,
user_dir.display(),
e
);
return Err(EX_NOUSER);
}
};
let target_uid = nix::unistd::Uid::from_raw(md.uid() as nix::libc::uid_t);
let (has_user_groups, target_gid) =
match nix::unistd::User::from_uid(target_uid) {
Ok(Some(user)) => {
if effective_only {
(false, user.gid)
} else {
match nix::unistd::initgroups(
&std::ffi::CString::new(user.name.to_owned())
.expect("Got UNIX user name with NUL?"),
user.gid,
) {
Ok(()) => (true, user.gid),
Err(e) => {
warn!(
"{} Failed to init groups for user: {}",
log_prefix, e
);
(false, user.gid)
}
}
}
}
Ok(None) => {
if !chroot_system {
warn!(
"{} No passwd entry for UID {}, assuming GID {}",
log_prefix,
target_uid,
md.gid()
);
}
(
false,
nix::unistd::Gid::from_raw(md.gid() as nix::libc::gid_t),
)
}
Err(e) => {
if !chroot_system {
warn!(
"{} Failed to look up passwd entry for UID {}, \
assuming GID {}: {}",
log_prefix,
target_uid,
md.gid(),
e
);
}
(
false,
nix::unistd::Gid::from_raw(md.gid() as nix::libc::gid_t),
)
}
};
if !effective_only {
if let Err(e) = nix::unistd::chdir(user_dir)
.and_then(|()| nix::unistd::chroot(user_dir))
{
error!(
"{} Chroot (forced because Crymap is running as root) \
into '{}' failed: {}",
log_prefix,
user_dir.display(),
e
);
return Err(EX_OSERR);
}
user_dir.push("/"); }
if let Err(e) = if has_user_groups {
Ok(())
} else {
nix::unistd::setgroups(&[target_gid])
}
.and_then(|()| {
if effective_only {
nix::unistd::setegid(target_gid)
} else {
nix::unistd::setgid(target_gid)
}
})
.and_then(|()| {
if effective_only {
nix::unistd::seteuid(target_uid)
} else {
nix::unistd::setuid(target_uid)
}
}) {
error!(
"{} Failed to drop privileges to {}:{}: {}",
log_prefix, target_uid, target_gid, e
);
return Err(EX_OSERR);
}
if nix::unistd::ROOT == nix::unistd::geteuid() {
error!(
"{} Crymap is still root! You must either \
(a) Run Crymap as a non-root user; \
(b) Set [security].system_user in crymap.toml; \
(c) Ensure that user directories are not owned by root.",
log_prefix
);
return Err(EX_USAGE);
}
Ok(())
}