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