From 9dadca8993cdc279ec8eda5a8f00b59ade73383f Mon Sep 17 00:00:00 2001 From: Ben S Date: Tue, 26 Jan 2016 19:10:48 +0000 Subject: [PATCH] Move user-switching functions to their own module Remove `c_int` from the libc public exports list while I'm at it. This might break someone's code, but it shouldn't have been used from this library anyway (only for user switching functions!) --- src/base.rs | 106 +----------------------------------------------- src/lib.rs | 5 ++- src/switch.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 106 deletions(-) create mode 100644 src/switch.rs diff --git a/src/base.rs b/src/base.rs index a2d756d..dd643a4 100644 --- a/src/base.rs +++ b/src/base.rs @@ -1,11 +1,9 @@ -use std::io::{Error as IOError, Result as IOResult}; use std::ffi::{CStr, CString}; use std::ptr::read; use std::str::from_utf8_unchecked; use std::sync::Arc; - -use libc::{uid_t, gid_t, c_int}; +use libc::{uid_t, gid_t}; #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly"))] use libc::{c_char, time_t}; @@ -59,17 +57,8 @@ extern { fn getuid() -> uid_t; fn geteuid() -> uid_t; - fn setuid(uid: uid_t) -> c_int; - fn seteuid(uid: uid_t) -> c_int; - fn getgid() -> gid_t; fn getegid() -> gid_t; - - fn setgid(gid: gid_t) -> c_int; - fn setegid(gid: gid_t) -> c_int; - - fn setreuid(ruid: uid_t, euid: uid_t) -> c_int; - fn setregid(rgid: gid_t, egid: gid_t) -> c_int; } /// Information about a particular user. @@ -243,99 +232,6 @@ pub fn get_effective_groupname() -> Option { get_group_by_gid(gid).map(|g| Arc::try_unwrap(g.name).unwrap()) } -/// Sets current user for the running process, requires root priviledges. -pub fn set_current_uid(uid: uid_t) -> IOResult<()> { - match unsafe { setuid(uid) } { - 0 => Ok(()), - -1 => Err(IOError::last_os_error()), - _ => unreachable!() - } -} - -/// Set current group for the running process, requires root priviledges. -pub fn set_current_gid(gid: gid_t) -> IOResult<()> { - match unsafe { setgid(gid) } { - 0 => Ok(()), - -1 => Err(IOError::last_os_error()), - _ => unreachable!() - } -} - -/// Set effective user for the running process, requires root priviledges. -pub fn set_effective_uid(uid: uid_t) -> IOResult<()> { - match unsafe { seteuid(uid) } { - 0 => Ok(()), - -1 => Err(IOError::last_os_error()), - _ => unreachable!() - } -} - -/// Set effective user for the running process, requires root priviledges. -pub fn set_effective_gid(gid: gid_t) -> IOResult<()> { - match unsafe { setegid(gid) } { - 0 => Ok(()), - -1 => Err(IOError::last_os_error()), - _ => unreachable!() - } -} - -/// Atomically set current and effective user for the running process, requires root priviledges. -pub fn set_both_uid(ruid: uid_t, euid: uid_t) -> IOResult<()> { - match unsafe { setreuid(ruid, euid) } { - 0 => Ok(()), - -1 => Err(IOError::last_os_error()), - _ => unreachable!() - } -} - -/// Atomically set current and effective group for the running process, requires root priviledges. -pub fn set_both_gid(rgid: gid_t, egid: gid_t) -> IOResult<()> { - match unsafe { setregid(rgid, egid) } { - 0 => Ok(()), - -1 => Err(IOError::last_os_error()), - _ => unreachable!() - } -} - -pub struct SwitchUserGuard { - uid: uid_t, - gid: gid_t, -} - -impl Drop for SwitchUserGuard { - fn drop(&mut self) { - // Panic on error here, as failing to set values back - // is a possible security breach. - set_effective_uid(self.uid).unwrap(); - set_effective_gid(self.gid).unwrap(); - } -} - -/// Safely switch user and group for the current scope. -/// Requires root access. -/// -/// ```ignore -/// { -/// let _guard = switch_user_group(1001, 1001); -/// // current and effective user and group ids are 1001 -/// } -/// // back to the old values -/// ``` -/// -/// Use with care! Possible security issues can happen, as Rust doesn't -/// guarantee running the destructor! If in doubt run `drop()` method -/// on the guard value manually! -pub fn switch_user_group(uid: uid_t, gid: gid_t) -> Result { - let current_state = SwitchUserGuard { - uid: get_effective_uid(), - gid: get_effective_gid(), - }; - - try!(set_effective_uid(uid)); - try!(set_effective_gid(gid)); - Ok(current_state) -} - #[cfg(test)] mod test { diff --git a/src/lib.rs b/src/lib.rs index 5de5a6f..9950bce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,7 +109,7 @@ //! edge cases. extern crate libc; -pub use libc::{uid_t, gid_t, c_int}; +pub use libc::{uid_t, gid_t}; mod base; pub use base::*; @@ -118,5 +118,8 @@ pub mod mock; pub mod os; pub use os::OSUsers; +pub mod switch; +use switch::*; + mod traits; pub use traits::{Users, Groups}; diff --git a/src/switch.rs b/src/switch.rs new file mode 100644 index 0000000..4cceac0 --- /dev/null +++ b/src/switch.rs @@ -0,0 +1,110 @@ +use std::io::{Error as IOError, Result as IOResult}; +use libc::{uid_t, gid_t, c_int}; + +use base::{get_effective_uid, get_effective_gid}; + + +extern { + fn setuid(uid: uid_t) -> c_int; + fn seteuid(uid: uid_t) -> c_int; + + fn setgid(gid: gid_t) -> c_int; + fn setegid(gid: gid_t) -> c_int; + + fn setreuid(ruid: uid_t, euid: uid_t) -> c_int; + fn setregid(rgid: gid_t, egid: gid_t) -> c_int; +} + + +/// Sets current user for the running process, requires root priviledges. +pub fn set_current_uid(uid: uid_t) -> IOResult<()> { + match unsafe { setuid(uid) } { + 0 => Ok(()), + -1 => Err(IOError::last_os_error()), + _ => unreachable!() + } +} + +/// Set current group for the running process, requires root priviledges. +pub fn set_current_gid(gid: gid_t) -> IOResult<()> { + match unsafe { setgid(gid) } { + 0 => Ok(()), + -1 => Err(IOError::last_os_error()), + _ => unreachable!() + } +} + +/// Set effective user for the running process, requires root priviledges. +pub fn set_effective_uid(uid: uid_t) -> IOResult<()> { + match unsafe { seteuid(uid) } { + 0 => Ok(()), + -1 => Err(IOError::last_os_error()), + _ => unreachable!() + } +} + +/// Set effective user for the running process, requires root priviledges. +pub fn set_effective_gid(gid: gid_t) -> IOResult<()> { + match unsafe { setegid(gid) } { + 0 => Ok(()), + -1 => Err(IOError::last_os_error()), + _ => unreachable!() + } +} + +/// Atomically set current and effective user for the running process, requires root priviledges. +pub fn set_both_uid(ruid: uid_t, euid: uid_t) -> IOResult<()> { + match unsafe { setreuid(ruid, euid) } { + 0 => Ok(()), + -1 => Err(IOError::last_os_error()), + _ => unreachable!() + } +} + +/// Atomically set current and effective group for the running process, requires root priviledges. +pub fn set_both_gid(rgid: gid_t, egid: gid_t) -> IOResult<()> { + match unsafe { setregid(rgid, egid) } { + 0 => Ok(()), + -1 => Err(IOError::last_os_error()), + _ => unreachable!() + } +} + +pub struct SwitchUserGuard { + uid: uid_t, + gid: gid_t, +} + +impl Drop for SwitchUserGuard { + fn drop(&mut self) { + // Panic on error here, as failing to set values back + // is a possible security breach. + set_effective_uid(self.uid).unwrap(); + set_effective_gid(self.gid).unwrap(); + } +} + +/// Safely switch user and group for the current scope. +/// Requires root access. +/// +/// ```ignore +/// { +/// let _guard = switch_user_group(1001, 1001); +/// // current and effective user and group ids are 1001 +/// } +/// // back to the old values +/// ``` +/// +/// Use with care! Possible security issues can happen, as Rust doesn't +/// guarantee running the destructor! If in doubt run `drop()` method +/// on the guard value manually! +pub fn switch_user_group(uid: uid_t, gid: gid_t) -> Result { + let current_state = SwitchUserGuard { + uid: get_effective_uid(), + gid: get_effective_gid(), + }; + + try!(set_effective_uid(uid)); + try!(set_effective_gid(gid)); + Ok(current_state) +}