-
Notifications
You must be signed in to change notification settings - Fork 37
/
switch.rs
143 lines (127 loc) · 4.64 KB
/
switch.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! Functions for switching the running process’s user or group.
use std::io::{Error as IOError, Result as IOResult};
use libc::{uid_t, gid_t, c_int, setuid, seteuid, setgid};
use base::{get_effective_uid, get_effective_gid};
// NOTE: for whatever reason, it seems these are not available in libc on BSD platforms, so they
// need to be included manually
extern {
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 the **current user** for the running process to the one with the
/// given user ID. Uses `setuid` internally.
///
/// Typically, trying to switch to anyone other than the user already running
/// the process requires root privileges.
pub fn set_current_uid(uid: uid_t) -> IOResult<()> {
match unsafe { setuid(uid) } {
0 => Ok(()),
-1 => Err(IOError::last_os_error()),
n => unreachable!("setuid returned {}", n)
}
}
/// Sets the **current group** for the running process to the one with the
/// given group ID. Uses `setgid` internally.
///
/// Typically, trying to switch to any group other than the group already
/// running the process requires root privileges.
pub fn set_current_gid(gid: gid_t) -> IOResult<()> {
match unsafe { setgid(gid) } {
0 => Ok(()),
-1 => Err(IOError::last_os_error()),
n => unreachable!("setgid returned {}", n)
}
}
/// Sets the **effective user** for the running process to the one with the
/// given user ID. Uses `seteuid` internally.
///
/// Typically, trying to switch to anyone other than the user already running
/// the process requires root privileges.
pub fn set_effective_uid(uid: uid_t) -> IOResult<()> {
match unsafe { seteuid(uid) } {
0 => Ok(()),
-1 => Err(IOError::last_os_error()),
n => unreachable!("seteuid returned {}", n)
}
}
/// Sets the **effective group** for the running process to the one with the
/// given group ID. Uses `setegid` internally.
///
/// Typically, trying to switch to any group other than the group already
/// running the process requires root privileges.
pub fn set_effective_gid(gid: gid_t) -> IOResult<()> {
match unsafe { setegid(gid) } {
0 => Ok(()),
-1 => Err(IOError::last_os_error()),
n => unreachable!("setegid returned {}", n)
}
}
/// Sets both the **current user** and the **effective user** for the running
/// process to the ones with the given user IDs. Uses `setreuid` internally.
///
/// Typically, trying to switch to anyone other than the user already running
/// the process requires root privileges.
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()),
n => unreachable!("setreuid returned {}", n)
}
}
/// Sets both the **current group** and the **effective group** for the
/// running process to the ones with the given group IDs. Uses `setregid`
/// internally.
///
/// Typically, trying to switch to any group other than the group already
/// running the process requires root privileges.
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()),
n => unreachable!("setregid returned {}", n)
}
}
/// Guard returned from a `switch_user_group` call.
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();
}
}
/// Sets the **effective user** and the **effective group** for the current
/// scope.
///
/// Typically, trying to switch to any user or group other than the ones already
/// running the process requires root privileges.
///
/// **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!
///
/// ### Examples
///
/// ```no_run
/// use users::switch::switch_user_group;
///
/// {
/// let _guard = switch_user_group(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: gid_t) -> IOResult<SwitchUserGuard> {
let current_state = SwitchUserGuard {
uid: get_effective_uid(),
gid: get_effective_gid(),
};
try!(set_effective_gid(gid));
try!(set_effective_uid(uid));
Ok(current_state)
}