1 //! Functions for switching the running process’s user or group.
2
3 use std::io;
4 use libc::{uid_t, gid_t, c_int};
5
6 use base::{get_effective_uid, get_effective_gid};
7
8
9 // NOTE: for whatever reason, it seems these are not available in libc on BSD platforms, so they
10 // need to be included manually
11 extern {
setreuid(ruid: uid_t, euid: uid_t) -> c_int12 fn setreuid(ruid: uid_t, euid: uid_t) -> c_int;
setregid(rgid: gid_t, egid: gid_t) -> c_int13 fn setregid(rgid: gid_t, egid: gid_t) -> c_int;
14 }
15
16
17 /// Sets the **current user** for the running process to the one with the
18 /// given user ID.
19 ///
20 /// Typically, trying to switch to anyone other than the user already running
21 /// the process requires root privileges.
22 ///
23 /// # libc functions used
24 ///
25 /// - [`setuid`](https://docs.rs/libc/*/libc/fn.setuid.html)
26 ///
27 /// # Errors
28 ///
29 /// This function will return `Err` when an I/O error occurs during the
30 /// `setuid` call.
31 ///
32 /// # Examples
33 ///
34 /// ```no_run
35 /// use users::switch::set_current_uid;
36 ///
37 /// set_current_uid(1001);
38 /// // current user ID is 1001
39 /// ```
set_current_uid(uid: uid_t) -> io::Result<()>40 pub fn set_current_uid(uid: uid_t) -> io::Result<()> {
41 match unsafe { libc::setuid(uid) } {
42 0 => Ok(()),
43 -1 => Err(io::Error::last_os_error()),
44 n => unreachable!("setuid returned {}", n)
45 }
46 }
47
48 /// Sets the **current group** for the running process to the one with the
49 /// given group ID.
50 ///
51 /// Typically, trying to switch to any group other than the group already
52 /// running the process requires root privileges.
53 ///
54 /// # libc functions used
55 ///
56 /// - [`setgid`](https://docs.rs/libc/*/libc/fn.setgid.html)
57 ///
58 /// # Errors
59 ///
60 /// This function will return `Err` when an I/O error occurs during the
61 /// `setgid` call.
62 ///
63 /// # Examples
64 ///
65 /// ```no_run
66 /// use users::switch::set_current_gid;
67 ///
68 /// set_current_gid(1001);
69 /// // current group ID is 1001
70 /// ```
set_current_gid(gid: gid_t) -> io::Result<()>71 pub fn set_current_gid(gid: gid_t) -> io::Result<()> {
72 match unsafe { libc::setgid(gid) } {
73 0 => Ok(()),
74 -1 => Err(io::Error::last_os_error()),
75 n => unreachable!("setgid returned {}", n)
76 }
77 }
78
79 /// Sets the **effective user** for the running process to the one with the
80 /// given user ID.
81 ///
82 /// Typically, trying to switch to anyone other than the user already running
83 /// the process requires root privileges.
84 ///
85 /// # libc functions used
86 ///
87 /// - [`seteuid`](https://docs.rs/libc/*/libc/fn.seteuid.html)
88 ///
89 /// # Errors
90 ///
91 /// This function will return `Err` when an I/O error occurs during the
92 /// `seteuid` call.
93 ///
94 /// # Examples
95 ///
96 /// ```no_run
97 /// use users::switch::set_effective_uid;
98 ///
99 /// set_effective_uid(1001);
100 /// // current effective user ID is 1001
101 /// ```
set_effective_uid(uid: uid_t) -> io::Result<()>102 pub fn set_effective_uid(uid: uid_t) -> io::Result<()> {
103 match unsafe { libc::seteuid(uid) } {
104 0 => Ok(()),
105 -1 => Err(io::Error::last_os_error()),
106 n => unreachable!("seteuid returned {}", n)
107 }
108 }
109
110 /// Sets the **effective group** for the running process to the one with the
111 /// given group ID.
112 ///
113 /// Typically, trying to switch to any group other than the group already
114 /// running the process requires root privileges.
115 ///
116 /// # libc functions used
117 ///
118 /// - [`setegid`](https://docs.rs/libc/*/libc/fn.setegid.html)
119 ///
120 /// # Errors
121 ///
122 /// This function will return `Err` when an I/O error occurs during the
123 /// `setegid` call.
124 ///
125 /// # Examples
126 ///
127 /// ```no_run
128 /// use users::switch::set_effective_gid;
129 ///
130 /// set_effective_gid(1001);
131 /// // current effective group ID is 1001
132 /// ```
set_effective_gid(gid: gid_t) -> io::Result<()>133 pub fn set_effective_gid(gid: gid_t) -> io::Result<()> {
134 match unsafe { libc::setegid(gid) } {
135 0 => Ok(()),
136 -1 => Err(io::Error::last_os_error()),
137 n => unreachable!("setegid returned {}", n)
138 }
139 }
140
141 /// Sets both the **current user** and the **effective user** for the running
142 /// process to the ones with the given user IDs.
143 ///
144 /// Typically, trying to switch to anyone other than the user already running
145 /// the process requires root privileges.
146 ///
147 /// # libc functions used
148 ///
149 /// - [`setreuid`](https://docs.rs/libc/*/libc/fn.setreuid.html)
150 ///
151 /// # Errors
152 ///
153 /// This function will return `Err` when an I/O error occurs during the
154 /// `setreuid` call.
155 ///
156 /// # Examples
157 ///
158 /// ```no_run
159 /// use users::switch::set_both_uid;
160 ///
161 /// set_both_uid(1001, 1001);
162 /// // current user ID and effective user ID are 1001
163 /// ```
set_both_uid(ruid: uid_t, euid: uid_t) -> io::Result<()>164 pub fn set_both_uid(ruid: uid_t, euid: uid_t) -> io::Result<()> {
165 match unsafe { setreuid(ruid, euid) } {
166 0 => Ok(()),
167 -1 => Err(io::Error::last_os_error()),
168 n => unreachable!("setreuid returned {}", n)
169 }
170 }
171
172 /// Sets both the **current group** and the **effective group** for the
173 /// running process to the ones with the given group IDs.
174 ///
175 /// Typically, trying to switch to any group other than the group already
176 /// running the process requires root privileges.
177 ///
178 /// # libc functions used
179 ///
180 /// - [`setregid`](https://docs.rs/libc/*/libc/fn.setregid.html)
181 ///
182 /// # Errors
183 ///
184 /// This function will return `Err` when an I/O error occurs during the
185 /// `setregid` call.
186 ///
187 /// # Examples
188 ///
189 /// ```no_run
190 /// use users::switch::set_both_gid;
191 ///
192 /// set_both_gid(1001, 1001);
193 /// // current user ID and effective group ID are 1001
194 /// ```
set_both_gid(rgid: gid_t, egid: gid_t) -> io::Result<()>195 pub fn set_both_gid(rgid: gid_t, egid: gid_t) -> io::Result<()> {
196 match unsafe { setregid(rgid, egid) } {
197 0 => Ok(()),
198 -1 => Err(io::Error::last_os_error()),
199 n => unreachable!("setregid returned {}", n)
200 }
201 }
202
203 /// Guard returned from a `switch_user_group` call.
204 pub struct SwitchUserGuard {
205 uid: uid_t,
206 gid: gid_t,
207 }
208
209 impl Drop for SwitchUserGuard {
drop(&mut self)210 fn drop(&mut self) {
211 set_effective_gid(self.gid).expect("Failed to set effective gid");
212 set_effective_uid(self.uid).expect("Failed to set effective uid");
213 }
214 }
215
216 /// Sets the **effective user** and the **effective group** for the current
217 /// scope.
218 ///
219 /// Typically, trying to switch to any user or group other than the ones already
220 /// running the process requires root privileges.
221 ///
222 /// # Security considerations
223 ///
224 /// - Because Rust does not guarantee running the destructor, it’s a good idea
225 /// to call [`std::mem::drop`](https://doc.rust-lang.org/std/mem/fn.drop.html)
226 /// on the guard manually in security-sensitive situations.
227 /// - This function switches the group before the user to prevent the user’s
228 /// privileges being dropped before trying to change the group (look up
229 /// `POS36-C`).
230 /// - This function will panic upon failing to set either walue, so the
231 /// program does not continue executing with too many privileges.
232 ///
233 /// # libc functions used
234 ///
235 /// - [`seteuid`](https://docs.rs/libc/*/libc/fn.seteuid.html)
236 /// - [`setegid`](https://docs.rs/libc/*/libc/fn.setegid.html)
237 ///
238 /// # Errors
239 ///
240 /// This function will return `Err` when an I/O error occurs during either
241 /// the `seteuid` or `setegid` calls.
242 ///
243 /// # Examples
244 ///
245 /// ```no_run
246 /// use users::switch::switch_user_group;
247 /// use std::mem::drop;
248 ///
249 /// {
250 /// let guard = switch_user_group(1001, 1001);
251 /// // current and effective user and group IDs are 1001
252 /// drop(guard);
253 /// }
254 /// // back to the old values
255 /// ```
switch_user_group(uid: uid_t, gid: gid_t) -> io::Result<SwitchUserGuard>256 pub fn switch_user_group(uid: uid_t, gid: gid_t) -> io::Result<SwitchUserGuard> {
257 let current_state = SwitchUserGuard {
258 gid: get_effective_gid(),
259 uid: get_effective_uid(),
260 };
261
262 set_effective_gid(gid)?;
263 set_effective_uid(uid)?;
264 Ok(current_state)
265 }
266