#![allow(clippy::needless_return)]
use crate::utils::zwarnnam;
use std::cell::UnsafeCell;
#[inline]
const fn broken_setregid() -> bool {
cfg!(target_os = "netbsd")
}
#[inline]
const fn broken_setreuid() -> bool {
cfg!(target_os = "netbsd")
}
#[inline]
const fn have_native_setregid() -> bool {
cfg!(unix)
}
#[inline]
const fn have_native_setreuid() -> bool {
cfg!(unix)
}
#[inline]
const fn seteuid_breaks_setuid() -> bool {
false
}
#[cfg(unix)]
pub unsafe fn setresgid(rgid: libc::gid_t, egid: libc::gid_t, sgid: libc::gid_t) -> libc::c_int {
let mut ret: libc::c_int = 0;
let mut saved_errno: libc::c_int;
if rgid != sgid {
errno_set(libc::ENOSYS);
return -1;
}
if have_native_setregid() && !broken_setregid() {
if libc::setregid(rgid, egid) < 0 {
saved_errno = errno_get();
zwarnnam("setregid", &format!("to gid {}: {}", rgid as i64, errno_str(saved_errno)));
errno_set(saved_errno);
ret = -1;
}
} else {
if libc::setegid(egid) < 0 {
saved_errno = errno_get();
zwarnnam("setegid", &format!("to gid {}: {}", egid as i64, errno_str(saved_errno)));
errno_set(saved_errno);
ret = -1;
}
if libc::setgid(rgid) < 0 {
saved_errno = errno_get();
zwarnnam("setgid", &format!("to gid {}: {}", rgid as i64, errno_str(saved_errno)));
errno_set(saved_errno);
ret = -1;
}
}
ret
}
#[cfg(unix)]
pub unsafe fn setresuid(ruid: libc::uid_t, euid: libc::uid_t, suid: libc::uid_t) -> libc::c_int {
let mut ret: libc::c_int = 0;
let mut saved_errno: libc::c_int;
if ruid != suid {
errno_set(libc::ENOSYS);
return -1;
}
if have_native_setreuid() && !broken_setreuid() {
if libc::setreuid(ruid, euid) < 0 {
saved_errno = errno_get();
zwarnnam("setreuid", &format!("to uid {}: {}", ruid as i64, errno_str(saved_errno)));
errno_set(saved_errno);
ret = -1;
}
} else {
if !seteuid_breaks_setuid() {
if libc::seteuid(euid) < 0 {
saved_errno = errno_get();
zwarnnam("seteuid", &format!("to uid {}: {}", euid as i64, errno_str(saved_errno)));
errno_set(saved_errno);
ret = -1;
}
}
if libc::setuid(ruid) < 0 {
saved_errno = errno_get();
zwarnnam("setuid", &format!("to uid {}: {}", ruid as i64, errno_str(saved_errno)));
errno_set(saved_errno);
ret = -1;
}
}
ret
}
#[cfg(unix)]
#[inline]
fn errno_get() -> libc::c_int {
std::io::Error::last_os_error().raw_os_error().unwrap_or(0)
}
#[cfg(unix)]
#[inline]
fn errno_set(e: libc::c_int) {
unsafe {
let p: *mut libc::c_int = {
#[cfg(any(target_os = "linux", target_os = "android"))]
{ libc::__errno_location() }
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly"))]
{ libc::__error() }
#[cfg(any(target_os = "openbsd", target_os = "netbsd"))]
{
extern "C" {
fn __errno() -> *mut libc::c_int;
}
__errno()
}
#[cfg(not(any(
target_os = "linux",
target_os = "android",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "netbsd",
)))]
{
thread_local! {
static ERRNO: UnsafeCell<libc::c_int> = const { UnsafeCell::new(0) };
}
ERRNO.with(|c| c.get())
}
};
*p = e;
}
}
#[cfg(unix)]
#[inline]
fn errno_str(e: libc::c_int) -> String {
std::io::Error::from_raw_os_error(e).to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(unix)]
fn setresgid_rejects_split_real_saved() {
unsafe {
let r = setresgid(1, 2, 3);
assert_eq!(r, -1);
assert_eq!(errno_get(), libc::ENOSYS);
}
}
#[test]
#[cfg(unix)]
fn setresuid_rejects_split_real_saved() {
unsafe {
let r = setresuid(1, 2, 3);
assert_eq!(r, -1);
assert_eq!(errno_get(), libc::ENOSYS);
}
}
#[test]
#[cfg(unix)]
fn setresuid_noop_succeeds() {
unsafe {
let me = libc::getuid();
let r = setresuid(me, me, me);
assert_eq!(r, 0);
}
}
#[test]
#[cfg(unix)]
fn setresgid_noop_succeeds() {
unsafe {
let me = libc::getgid();
let r = setresgid(me, me, me);
assert_eq!(r, 0);
}
}
}