Skip to content

Commit

Permalink
Move user-switching functions to their own module
Browse files Browse the repository at this point in the history
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!)
  • Loading branch information
ogham committed Jan 26, 2016
1 parent 80e0c7d commit 9dadca8
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 106 deletions.
106 changes: 1 addition & 105 deletions src/base.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -243,99 +232,6 @@ pub fn get_effective_groupname() -> Option<String> {
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<SwitchUserGuard, IOError> {
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 {
Expand Down
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -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};
110 changes: 110 additions & 0 deletions src/switch.rs
Original file line number Diff line number Diff line change
@@ -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<SwitchUserGuard, IOError> {
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)
}

0 comments on commit 9dadca8

Please sign in to comment.