From 825062139b721cb4bea3a693574549d1c6c78a0c Mon Sep 17 00:00:00 2001 From: Konstantin Stepanov Date: Tue, 21 Jul 2015 11:07:56 +0300 Subject: [PATCH 1/9] import other gid/uid functions --- src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index e108db1..88882b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -196,6 +196,19 @@ extern { fn getgrnam(group_name: *const c_char) -> *const c_group; 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() -> git_t; + + fn setgid(gid: gid_t) -> c_int; + fn setegid(gid: git_t) -> c_int; + + fn setreuid(ruid: uid_t, euid: uid_t) -> c_int; + fn setregid(rgid: git_t, egid: git_t) -> c_int; } #[derive(Clone)] From f5d3838e750d2ecfdbcddd06cde175983c82d315 Mon Sep 17 00:00:00 2001 From: Konstantin Stepanov Date: Tue, 21 Jul 2015 11:22:58 +0300 Subject: [PATCH 2/9] add methods to get effective user and group info --- src/lib.rs | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 88882b0..f02a228 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -151,6 +151,24 @@ pub trait Users { /// Return the username of the user running the process. fn get_current_username(&mut self) -> Option; + + /// Return the group ID for the user running the process. + fn get_current_gid(&mut self) -> gid_t; + + /// Return the group name of the user running the process. + fn get_current_groupname(&mut self) -> Option; + + /// Return the effective user id. + fn get_effective_uid(&mut self) -> uid_t; + + /// Return the effective group id. + fn get_effective_gid(&mut self) -> gid_t; + + /// Return the effective username. + fn get_effective_username(&mut self) -> Option; + + /// Return the effective group name. + fn get_effective_groupname(&mut self) -> Option; } #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly"))] @@ -255,6 +273,9 @@ pub struct OSUsers { groups_back: HashMap>, uid: Option, + gid: Option, + euid: Option, + egid: Option, } unsafe fn from_raw_buf(p: *const i8) -> String { @@ -437,6 +458,54 @@ impl Users for OSUsers { let uid = self.get_current_uid(); self.get_user_by_uid(uid).map(|u| u.name) } + + fn get_current_gid(&mut self) -> gid_t { + match self.gid { + Some(gid) => gid, + None => { + let gid = unsafe { getgid() }; + self.gid = Some(gid); + gid + } + } + } + + fn get_current_groupname(&mut self) -> Option { + let gid = self.get_current_gid(); + self.get_group_by_gid(gid).map(|g| g.name) + } + + fn get_effective_gid(&mut self) -> gid_t { + match self.egid { + Some(gid) => gid, + None => { + let gid = unsafe { getegid() }; + self.egid = Some(gid); + gid + } + } + } + + fn get_effective_groupname(&mut self) -> Option { + let gid = self.get_effective_gid(); + self.get_group_by_gid(gid).map(|g| g.name) + } + + fn get_effective_uid(&mut self) -> uid_t { + match self.euid { + Some(uid) => uid, + None => { + let uid = unsafe { geteuid() }; + self.euid = Some(uid); + uid + } + } + } + + fn get_effective_username(&mut self) -> Option { + let uid = self.get_effective_uid(); + self.get_user_by_uid(uid).map(|u| u.name) + } } impl OSUsers { @@ -482,6 +551,36 @@ pub fn get_current_username() -> Option { OSUsers::empty_cache().get_current_username() } +/// Return the user ID for the effective user running the process. +pub fn get_effective_uid() -> uid_t { + OSUsers::empty_cache().get_effective_uid() +} + +/// Return the username of the effective user running the process. +pub fn get_effective_username() -> Option { + OSUsers::empty_cache().get_effective_username() +} + +/// Return the group ID for the user running the process. +pub fn get_current_gid() -> gid_t { + OSUsers::empty_cache().get_current_gid() +} + +/// Return the groupname of the user running the process. +pub fn get_current_groupname() -> Option { + OSUsers::empty_cache().get_current_groupname() +} + +/// Return the group ID for the effective user running the process. +pub fn get_effective_gid() -> gid_t { + OSUsers::empty_cache().get_effective_gid() +} + +/// Return the groupname of the effective user running the process. +pub fn get_effective_groupname() -> Option { + OSUsers::empty_cache().get_effective_groupname() +} + #[cfg(test)] mod test { use super::{Users, OSUsers, get_current_username}; From 6e2e00f2877ff0343285e272ef0f7d83250637db Mon Sep 17 00:00:00 2001 From: Konstantin Stepanov Date: Tue, 21 Jul 2015 11:32:22 +0300 Subject: [PATCH 3/9] add functions to set user and group --- src/lib.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index f02a228..5035f0b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,6 +127,7 @@ use std::ptr::read; use std::str::from_utf8_unchecked; use std::collections::HashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; +use std::io::Error as IoError; pub mod mock; @@ -581,6 +582,60 @@ pub fn get_effective_groupname() -> Option { OSUsers::empty_cache().get_effective_groupname() } +/// Set current user for the running process, requires root priviledges. +pub fn set_current_uid(uid: uid_t) -> Result<(), io::Error> { + match unsafe { setuid(uid) } { + 0 => Ok(()), + -1 => Err(io::Error::last_os_error()) + _ => unreachable!() + } +} + +/// Set current group for the running process, requires root priviledges. +pub fn set_current_gid(gid: gid_t) -> Result<(), io::Error> { + match unsafe { setgid(gid) } { + 0 => Ok(()), + -1 => Err(io::Error::last_os_error()) + _ => unreachable!() + } +} + +/// Set effective user for the running process, requires root priviledges. +pub fn set_effective_uid(uid: uid_t) -> Result<(), io::Error> { + match unsafe { seteuid(uid) } { + 0 => Ok(()), + -1 => Err(io::Error::last_os_error()) + _ => unreachable!() + } +} + +/// Set effective user for the running process, requires root priviledges. +pub fn set_effective_gid(gid: gid_t) -> Result<(), io::Error> { + match unsafe { setegid(gid) } { + 0 => Ok(()), + -1 => Err(io::Error::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) -> Result<(), io::Error> { + match unsafe { setreuid(ruid, euid) } { + 0 => Ok(()), + -1 => Err(io::Error::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: git_t) -> Result<(), io::Error> { + match unsafe { setregid(rgid, egid) } { + 0 => Ok(()), + -1 => Err(io::Error::last_os_error()) + _ => unreachable!() + } +} + #[cfg(test)] mod test { use super::{Users, OSUsers, get_current_username}; From 0cabf243bd6984f99c211448c5a54ef8819d8676 Mon Sep 17 00:00:00 2001 From: Konstantin Stepanov Date: Tue, 21 Jul 2015 11:39:32 +0300 Subject: [PATCH 4/9] add switch user/group functionality --- src/lib.rs | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 5035f0b..23e79c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,7 +127,7 @@ use std::ptr::read; use std::str::from_utf8_unchecked; use std::collections::HashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; -use std::io::Error as IoError; +use std::io; pub mod mock; @@ -636,6 +636,42 @@ pub fn set_both_gid(rgid: gid_t, egid: git_t) -> Result<(), io::Error> { } } +pub struct SwitchUserGuard { + uid: uid_t, + gid: gid_t, + euid: euid_t, + egid: egid_t, +} + +impl Drop for SwitchUserGuard { + fn drop(&mut self) { + set_both_uid(self.uid, self.euid); + set_both_gid(self.gid, self.egid); + } +} + +/// Safely switch user and group for the current scope. +/// Requires root access. +/// +/// ```rust +/// { +/// let guard = switch_user_group(1001, 1001, 1001, 1001); +/// // current and effective user and group ids are 1001 +/// } +/// // back to the old values +pub fn switch_user_group(uid: uid_t, gid: git_t, euid: uid_t, egid: gid_t) -> Result { + let current_state = SwitchUserGuard { + uid: get_current_uid(), + gid: get_current_gid(), + euid: get_effective_uid(), + egid: get_effective_gid(), + }; + + try!(set_both_uid(uid, euid)); + try!(set_both_gid(gid, egid)); + Ok(current_state) +} + #[cfg(test)] mod test { use super::{Users, OSUsers, get_current_username}; From fd73810619118d7a13e379bc0deaa66af6fc03f3 Mon Sep 17 00:00:00 2001 From: Konstantin Stepanov Date: Tue, 21 Jul 2015 11:43:10 +0300 Subject: [PATCH 5/9] security warnings --- src/lib.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 23e79c1..ba511f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -645,8 +645,10 @@ pub struct SwitchUserGuard { impl Drop for SwitchUserGuard { fn drop(&mut self) { - set_both_uid(self.uid, self.euid); - set_both_gid(self.gid, self.egid); + // Panic on error here, as failing to set values back + // is a possible security breach. + set_both_uid(self.uid, self.euid).unwrap(); + set_both_gid(self.gid, self.egid).unwrap(); } } @@ -659,6 +661,11 @@ impl Drop for SwitchUserGuard { /// // 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: git_t, euid: uid_t, egid: gid_t) -> Result { let current_state = SwitchUserGuard { uid: get_current_uid(), From d52a2bcd8cf9867ded48440ea06e632d63e72b72 Mon Sep 17 00:00:00 2001 From: Konstantin Stepanov Date: Tue, 21 Jul 2015 11:48:49 +0300 Subject: [PATCH 6/9] update mock --- src/mock.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/mock.rs b/src/mock.rs index e3025da..3856d62 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -106,6 +106,30 @@ impl Users for MockUsers { fn get_current_username(&mut self) -> Option { self.users.get(&self.uid).map(|u| u.name.clone()) } + + fn get_current_gid(&mut self) -> uid_t { + self.uid + } + + fn get_current_groupname(&mut self) -> Option { + self.groups.get(&self.uid).map(|u| u.name.clone()) + } + + fn get_effective_uid(&mut self) -> uid_t { + self.uid + } + + fn get_effective_username(&mut self) -> Option { + self.users.get(&self.uid).map(|u| u.name.clone()) + } + + fn get_effective_gid(&mut self) -> uid_t { + self.uid + } + + fn get_effective_groupname(&mut self) -> Option { + self.groups.get(&self.uid).map(|u| u.name.clone()) + } } #[cfg(test)] From 184bd434e1861890dbe91b539d388c3e5c19761d Mon Sep 17 00:00:00 2001 From: Konstantin Stepanov Date: Tue, 21 Jul 2015 12:19:34 +0300 Subject: [PATCH 7/9] fixes --- src/lib.rs | 59 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ba511f8..bfb2a19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,7 +113,7 @@ //! edge cases. extern crate libc; -pub use libc::{uid_t, gid_t}; +pub use libc::{uid_t, gid_t, c_int}; #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly"))] use libc::{c_char, time_t}; #[cfg(target_os = "linux")] @@ -152,22 +152,22 @@ pub trait Users { /// Return the username of the user running the process. fn get_current_username(&mut self) -> Option; - + /// Return the group ID for the user running the process. fn get_current_gid(&mut self) -> gid_t; - + /// Return the group name of the user running the process. fn get_current_groupname(&mut self) -> Option; - + /// Return the effective user id. fn get_effective_uid(&mut self) -> uid_t; - + /// Return the effective group id. fn get_effective_gid(&mut self) -> gid_t; - + /// Return the effective username. fn get_effective_username(&mut self) -> Option; - + /// Return the effective group name. fn get_effective_groupname(&mut self) -> Option; } @@ -216,18 +216,18 @@ 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() -> git_t; + fn getegid() -> gid_t; fn setgid(gid: gid_t) -> c_int; - fn setegid(gid: git_t) -> c_int; + fn setegid(gid: gid_t) -> c_int; fn setreuid(ruid: uid_t, euid: uid_t) -> c_int; - fn setregid(rgid: git_t, egid: git_t) -> c_int; + fn setregid(rgid: gid_t, egid: gid_t) -> c_int; } #[derive(Clone)] @@ -459,7 +459,7 @@ impl Users for OSUsers { let uid = self.get_current_uid(); self.get_user_by_uid(uid).map(|u| u.name) } - + fn get_current_gid(&mut self) -> gid_t { match self.gid { Some(gid) => gid, @@ -470,12 +470,12 @@ impl Users for OSUsers { } } } - + fn get_current_groupname(&mut self) -> Option { let gid = self.get_current_gid(); self.get_group_by_gid(gid).map(|g| g.name) } - + fn get_effective_gid(&mut self) -> gid_t { match self.egid { Some(gid) => gid, @@ -486,7 +486,7 @@ impl Users for OSUsers { } } } - + fn get_effective_groupname(&mut self) -> Option { let gid = self.get_effective_gid(); self.get_group_by_gid(gid).map(|g| g.name) @@ -502,7 +502,7 @@ impl Users for OSUsers { } } } - + fn get_effective_username(&mut self) -> Option { let uid = self.get_effective_uid(); self.get_user_by_uid(uid).map(|u| u.name) @@ -518,6 +518,9 @@ impl OSUsers { groups: HashMap::new(), groups_back: HashMap::new(), uid: None, + gid: None, + euid: None, + egid: None, } } } @@ -586,7 +589,7 @@ pub fn get_effective_groupname() -> Option { pub fn set_current_uid(uid: uid_t) -> Result<(), io::Error> { match unsafe { setuid(uid) } { 0 => Ok(()), - -1 => Err(io::Error::last_os_error()) + -1 => Err(io::Error::last_os_error()), _ => unreachable!() } } @@ -595,7 +598,7 @@ pub fn set_current_uid(uid: uid_t) -> Result<(), io::Error> { pub fn set_current_gid(gid: gid_t) -> Result<(), io::Error> { match unsafe { setgid(gid) } { 0 => Ok(()), - -1 => Err(io::Error::last_os_error()) + -1 => Err(io::Error::last_os_error()), _ => unreachable!() } } @@ -604,7 +607,7 @@ pub fn set_current_gid(gid: gid_t) -> Result<(), io::Error> { pub fn set_effective_uid(uid: uid_t) -> Result<(), io::Error> { match unsafe { seteuid(uid) } { 0 => Ok(()), - -1 => Err(io::Error::last_os_error()) + -1 => Err(io::Error::last_os_error()), _ => unreachable!() } } @@ -613,7 +616,7 @@ pub fn set_effective_uid(uid: uid_t) -> Result<(), io::Error> { pub fn set_effective_gid(gid: gid_t) -> Result<(), io::Error> { match unsafe { setegid(gid) } { 0 => Ok(()), - -1 => Err(io::Error::last_os_error()) + -1 => Err(io::Error::last_os_error()), _ => unreachable!() } } @@ -622,16 +625,16 @@ pub fn set_effective_gid(gid: gid_t) -> Result<(), io::Error> { pub fn set_both_uid(ruid: uid_t, euid: uid_t) -> Result<(), io::Error> { match unsafe { setreuid(ruid, euid) } { 0 => Ok(()), - -1 => Err(io::Error::last_os_error()) + -1 => Err(io::Error::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: git_t) -> Result<(), io::Error> { +pub fn set_both_gid(rgid: gid_t, egid: gid_t) -> Result<(), io::Error> { match unsafe { setregid(rgid, egid) } { 0 => Ok(()), - -1 => Err(io::Error::last_os_error()) + -1 => Err(io::Error::last_os_error()), _ => unreachable!() } } @@ -639,8 +642,8 @@ pub fn set_both_gid(rgid: gid_t, egid: git_t) -> Result<(), io::Error> { pub struct SwitchUserGuard { uid: uid_t, gid: gid_t, - euid: euid_t, - egid: egid_t, + euid: uid_t, + egid: gid_t, } impl Drop for SwitchUserGuard { @@ -666,14 +669,14 @@ impl Drop for SwitchUserGuard { /// 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: git_t, euid: uid_t, egid: gid_t) -> Result { +pub fn switch_user_group(uid: uid_t, gid: gid_t, euid: uid_t, egid: gid_t) -> Result { let current_state = SwitchUserGuard { uid: get_current_uid(), gid: get_current_gid(), euid: get_effective_uid(), egid: get_effective_gid(), }; - + try!(set_both_uid(uid, euid)); try!(set_both_gid(gid, egid)); Ok(current_state) From 7c19c92fd74ed7e2114df476fbd56edf4f361805 Mon Sep 17 00:00:00 2001 From: Konstantin Stepanov Date: Tue, 21 Jul 2015 12:55:44 +0300 Subject: [PATCH 8/9] switch effective uid only --- src/lib.rs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index bfb2a19..6f04c84 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -642,16 +642,14 @@ pub fn set_both_gid(rgid: gid_t, egid: gid_t) -> Result<(), io::Error> { pub struct SwitchUserGuard { uid: uid_t, gid: gid_t, - euid: uid_t, - egid: 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_both_uid(self.uid, self.euid).unwrap(); - set_both_gid(self.gid, self.egid).unwrap(); + set_effective_uid(self.uid).unwrap(); + set_effective_gid(self.gid).unwrap(); } } @@ -660,7 +658,7 @@ impl Drop for SwitchUserGuard { /// /// ```rust /// { -/// let guard = switch_user_group(1001, 1001, 1001, 1001); +/// let guard = switch_user_group(1001, 1001); /// // current and effective user and group ids are 1001 /// } /// // back to the old values @@ -669,16 +667,14 @@ impl Drop for SwitchUserGuard { /// 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, euid: uid_t, egid: gid_t) -> Result { +pub fn switch_user_group(uid: uid_t, gid: gid_t) -> Result { let current_state = SwitchUserGuard { - uid: get_current_uid(), - gid: get_current_gid(), - euid: get_effective_uid(), - egid: get_effective_gid(), + uid: get_effective_uid(), + gid: get_effective_gid(), }; - try!(set_both_uid(uid, euid)); - try!(set_both_gid(gid, egid)); + try!(set_effective_uid(uid)); + try!(set_effective_gid(gid)); Ok(current_state) } From 5cab91cf739356db8e05da79c72c87854c58a879 Mon Sep 17 00:00:00 2001 From: Konstantin Stepanov Date: Thu, 23 Jul 2015 01:40:53 +0300 Subject: [PATCH 9/9] fix docs for `switch_user_group` --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6f04c84..c138250 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -656,9 +656,9 @@ impl Drop for SwitchUserGuard { /// Safely switch user and group for the current scope. /// Requires root access. /// -/// ```rust +/// ```ignore /// { -/// let guard = switch_user_group(1001, 1001); +/// let _guard = switch_user_group(1001, 1001); /// // current and effective user and group ids are 1001 /// } /// // back to the old values