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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
//! Functions for switching the running process’s user or group.

use std::io;
use libc::{uid_t, gid_t, c_int};

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 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.
///
/// Typically, trying to switch to anyone other than the user already running
/// the process requires root privileges.
///
/// # libc functions used
///
/// - [`setuid`](https://docs.rs/libc/*/libc/fn.setuid.html)
///
/// # Errors
///
/// This function will return `Err` when an I/O error occurs during the
/// `setuid` call.
///
/// # Examples
///
/// ```no_run
/// use users::switch::set_current_uid;
///
/// set_current_uid(1001);
/// // current user ID is 1001
/// ```
pub fn set_current_uid(uid: uid_t) -> io::Result<()> {
    match unsafe { libc::setuid(uid) } {
         0 => Ok(()),
        -1 => Err(io::Error::last_os_error()),
         n => unreachable!("setuid returned {}", n)
    }
}

/// Sets the **current group** for the running process to the one with the
/// given group ID.
///
/// Typically, trying to switch to any group other than the group already
/// running the process requires root privileges.
///
/// # libc functions used
///
/// - [`setgid`](https://docs.rs/libc/*/libc/fn.setgid.html)
///
/// # Errors
///
/// This function will return `Err` when an I/O error occurs during the
/// `setgid` call.
///
/// # Examples
///
/// ```no_run
/// use users::switch::set_current_gid;
///
/// set_current_gid(1001);
/// // current group ID is 1001
/// ```
pub fn set_current_gid(gid: gid_t) -> io::Result<()> {
    match unsafe { libc::setgid(gid) } {
         0 => Ok(()),
        -1 => Err(io::Error::last_os_error()),
         n => unreachable!("setgid returned {}", n)
    }
}

/// Sets the **effective user** for the running process to the one with the
/// given user ID.
///
/// Typically, trying to switch to anyone other than the user already running
/// the process requires root privileges.
///
/// # libc functions used
///
/// - [`seteuid`](https://docs.rs/libc/*/libc/fn.seteuid.html)
///
/// # Errors
///
/// This function will return `Err` when an I/O error occurs during the
/// `seteuid` call.
///
/// # Examples
///
/// ```no_run
/// use users::switch::set_effective_uid;
///
/// set_effective_uid(1001);
/// // current effective user ID is 1001
/// ```
pub fn set_effective_uid(uid: uid_t) -> io::Result<()> {
    match unsafe { libc::seteuid(uid) } {
         0 => Ok(()),
        -1 => Err(io::Error::last_os_error()),
         n => unreachable!("seteuid returned {}", n)
    }
}

/// Sets the **effective group** for the running process to the one with the
/// given group ID.
///
/// Typically, trying to switch to any group other than the group already
/// running the process requires root privileges.
///
/// # libc functions used
///
/// - [`setegid`](https://docs.rs/libc/*/libc/fn.setegid.html)
///
/// # Errors
///
/// This function will return `Err` when an I/O error occurs during the
/// `setegid` call.
///
/// # Examples
///
/// ```no_run
/// use users::switch::set_effective_gid;
///
/// set_effective_gid(1001);
/// // current effective group ID is 1001
/// ```
pub fn set_effective_gid(gid: gid_t) -> io::Result<()> {
    match unsafe { libc::setegid(gid) } {
         0 => Ok(()),
        -1 => Err(io::Error::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.
///
/// Typically, trying to switch to anyone other than the user already running
/// the process requires root privileges.
///
/// # libc functions used
///
/// - [`setreuid`](https://docs.rs/libc/*/libc/fn.setreuid.html)
///
/// # Errors
///
/// This function will return `Err` when an I/O error occurs during the
/// `setreuid` call.
///
/// # Examples
///
/// ```no_run
/// use users::switch::set_both_uid;
///
/// set_both_uid(1001, 1001);
/// // current user ID and effective user ID are 1001
/// ```
pub fn set_both_uid(ruid: uid_t, euid: uid_t) -> io::Result<()> {
    match unsafe { setreuid(ruid, euid) } {
         0 => Ok(()),
        -1 => Err(io::Error::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.
///
/// Typically, trying to switch to any group other than the group already
/// running the process requires root privileges.
///
/// # libc functions used
///
/// - [`setregid`](https://docs.rs/libc/*/libc/fn.setregid.html)
///
/// # Errors
///
/// This function will return `Err` when an I/O error occurs during the
/// `setregid` call.
///
/// # Examples
///
/// ```no_run
/// use users::switch::set_both_gid;
///
/// set_both_gid(1001, 1001);
/// // current user ID and effective group ID are 1001
/// ```
pub fn set_both_gid(rgid: gid_t, egid: gid_t) -> io::Result<()> {
    match unsafe { setregid(rgid, egid) } {
         0 => Ok(()),
        -1 => Err(io::Error::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) {
        set_effective_gid(self.gid).expect("Failed to set effective gid");
        set_effective_uid(self.uid).expect("Failed to set effective uid");
    }
}

/// 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.
///
/// # Security considerations
///
/// - Because Rust does not guarantee running the destructor, it’s a good idea
///   to call [`std::mem::drop`](https://doc.rust-lang.org/std/mem/fn.drop.html)
///   on the guard manually in security-sensitive situations.
/// - This function switches the group before the user to prevent the user’s
///   privileges being dropped before trying to change the group (look up
///   `POS36-C`).
/// - This function will panic upon failing to set either walue, so the
///   program does not continue executing with too many privileges.
///
/// # libc functions used
///
/// - [`seteuid`](https://docs.rs/libc/*/libc/fn.seteuid.html)
/// - [`setegid`](https://docs.rs/libc/*/libc/fn.setegid.html)
///
/// # Errors
///
/// This function will return `Err` when an I/O error occurs during either
/// the `seteuid` or `setegid` calls.
///
/// # Examples
///
/// ```no_run
/// use users::switch::switch_user_group;
/// use std::mem::drop;
///
/// {
///     let guard = switch_user_group(1001, 1001);
///     // current and effective user and group IDs are 1001
///     drop(guard);
/// }
/// // back to the old values
/// ```
pub fn switch_user_group(uid: uid_t, gid: gid_t) -> io::Result<SwitchUserGuard> {
    let current_state = SwitchUserGuard {
        gid: get_effective_gid(),
        uid: get_effective_uid(),
    };

    set_effective_gid(gid)?;
    set_effective_uid(uid)?;
    Ok(current_state)
}