pub type Result<T> = std::result::Result<T, Error>;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[cfg(target_os = "linux")]
#[error("caps error: {0}")]
CapsError(#[from] caps::errors::CapsError),
#[cfg(windows)]
#[error("OpenProcessToken failed")]
OpenProcessTokenError,
#[cfg(windows)]
#[error("GetTokenInformation failed")]
GetTokenInformationError,
}
#[derive(Debug)]
pub struct Privilege {
has_privileges: bool,
needs_privileges: bool,
}
impl Privilege {
pub fn discover() -> Result<Self> {
let has_privileges = Self::check_has_privileges()?;
let needs_privileges = Self::check_needs_privileges();
Ok(Self {
has_privileges,
needs_privileges,
})
}
#[must_use]
pub const fn new(has_privileges: bool, needs_privileges: bool) -> Self {
Self {
has_privileges,
needs_privileges,
}
}
#[must_use]
pub const fn has_privileges(&self) -> bool {
self.has_privileges
}
#[must_use]
pub const fn needs_privileges(&self) -> bool {
self.needs_privileges
}
#[cfg(target_os = "linux")]
pub fn acquire_privileges() -> Result<Self> {
if caps::has_cap(None, caps::CapSet::Permitted, caps::Capability::CAP_NET_RAW)? {
caps::raise(None, caps::CapSet::Effective, caps::Capability::CAP_NET_RAW)?;
}
Self::discover()
}
#[cfg(target_os = "linux")]
fn check_has_privileges() -> Result<bool> {
Ok(caps::has_cap(
None,
caps::CapSet::Effective,
caps::Capability::CAP_NET_RAW,
)?)
}
#[cfg(target_os = "linux")]
pub fn drop_privileges() -> Result<()> {
caps::clear(None, caps::CapSet::Effective)?;
Ok(())
}
#[cfg(all(unix, not(target_os = "linux")))]
#[allow(clippy::unnecessary_wraps)]
pub fn acquire_privileges() -> Result<Self> {
Self::discover()
}
#[cfg(all(unix, not(target_os = "linux")))]
#[allow(clippy::unnecessary_wraps)]
fn check_has_privileges() -> Result<bool> {
Ok(nix::unistd::Uid::effective().is_root())
}
#[cfg(all(unix, not(target_os = "linux")))]
#[allow(clippy::unnecessary_wraps)]
pub const fn drop_privileges() -> Result<()> {
Ok(())
}
#[cfg(all(unix, not(target_os = "macos")))]
const fn check_needs_privileges() -> bool {
true
}
#[cfg(target_os = "macos")]
const fn check_needs_privileges() -> bool {
false
}
#[cfg(windows)]
#[allow(clippy::unnecessary_wraps)]
pub fn acquire_privileges() -> Result<Self> {
Self::discover()
}
#[cfg(windows)]
#[allow(clippy::unnecessary_wraps)]
fn check_has_privileges() -> Result<bool> {
macro_rules! syscall {
($p: path, $fn: ident ( $($arg: expr),* $(,)* ) ) => {{
#[allow(unsafe_code)]
unsafe { paste::paste!(windows_sys::Win32::$p::$fn) ($($arg, )*) }
}};
}
pub struct Privileged {
handle: windows_sys::Win32::Foundation::HANDLE,
}
impl Privileged {
pub fn current_process() -> Result<Self> {
use windows_sys::Win32::Security::TOKEN_QUERY;
let mut handle: windows_sys::Win32::Foundation::HANDLE = 0;
let current_process = syscall!(System::Threading, GetCurrentProcess());
let res = syscall!(
System::Threading,
OpenProcessToken(current_process, TOKEN_QUERY, std::ptr::addr_of_mut!(handle))
);
if res == 0 {
Err(Error::OpenProcessTokenError)
} else {
Ok(Self { handle })
}
}
pub fn is_elevated(&self) -> Result<bool> {
use windows_sys::Win32::Security::TokenElevation;
use windows_sys::Win32::Security::TOKEN_ELEVATION;
let mut elevation = TOKEN_ELEVATION { TokenIsElevated: 0 };
#[allow(clippy::cast_possible_truncation)]
let size = std::mem::size_of::<TOKEN_ELEVATION>() as u32;
let mut ret_size = 0u32;
let ret = syscall!(
Security,
GetTokenInformation(
self.handle,
TokenElevation,
std::ptr::addr_of_mut!(elevation).cast(),
size,
std::ptr::addr_of_mut!(ret_size),
)
);
if ret == 0 {
Err(Error::GetTokenInformationError)
} else {
Ok(elevation.TokenIsElevated != 0)
}
}
}
impl Drop for Privileged {
fn drop(&mut self) {
if self.handle != 0 {
syscall!(Foundation, CloseHandle(self.handle));
}
}
}
Privileged::current_process()?.is_elevated()
}
#[cfg(windows)]
#[allow(clippy::unnecessary_wraps)]
pub const fn drop_privileges() -> Result<()> {
Ok(())
}
#[cfg(target_os = "windows")]
const fn check_needs_privileges() -> bool {
true
}
}