Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * ProFTPD - FTP server daemon |
3 | | * Copyright (c) 2009-2022 The ProFTPD Project team |
4 | | * |
5 | | * This program is free software; you can redistribute it and/or modify |
6 | | * it under the terms of the GNU General Public License as published by |
7 | | * the Free Software Foundation; either version 2 of the License, or |
8 | | * (at your option) any later version. |
9 | | * |
10 | | * This program is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | * GNU General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU General Public License |
16 | | * along with this program; if not, write to the Free Software |
17 | | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. |
18 | | * |
19 | | * As a special exemption, The ProFTPD Project and other respective copyright |
20 | | * holders give permission to link this program with OpenSSL, and distribute |
21 | | * the resulting executable, without including the source code for OpenSSL in |
22 | | * the source distribution. |
23 | | */ |
24 | | |
25 | | #include "conf.h" |
26 | | #include "privs.h" |
27 | | |
28 | | /* If proftpd was started up without root privs, then this is set to TRUE. |
29 | | * It is used to prevent spamming the logs with error messages about being |
30 | | * unable to switch privs. |
31 | | */ |
32 | | static int nonroot_daemon = FALSE; |
33 | | |
34 | | /* Functions for manipulating saved, real and effective UID for easy switching |
35 | | * from/to root. |
36 | | * |
37 | | * Note: In version 1.1.5, all of this changed. We USED to play games with |
38 | | * the saved-UID/GID AND setreuid()/setregid(); however this appears to be |
39 | | * slightly non-portable (i.e. w/ BSDs). Since POSIX.1 saved-UIDs are pretty |
40 | | * much useless without setre* (in the case of root), we now use basic UID |
41 | | * swapping if we have seteuid(), and setreuid() swapping if not. |
42 | | * |
43 | | * If seteuid() is present, we set the saved UID/GID using setuid/seteuid(). |
44 | | * setreuid() is no longer used as it is considered obsolete on many systems. |
45 | | * GIDS are also no longer swapped, as they are unnecessary. |
46 | | * |
47 | | * If run as root, proftpd now normally runs as: |
48 | | * real user : root |
49 | | * effective user : <user> |
50 | | * saved user : root |
51 | | * real/eff/saved group : <group> |
52 | | */ |
53 | | |
54 | | /* Porters, please put the most reasonable and secure method of |
55 | | * doing this in here. |
56 | | */ |
57 | | |
58 | | static const char *trace_channel = "privs"; |
59 | | |
60 | | /* We keep a count of the number of times PRIVS_ROOT/PRIVS_RELINQUISH have |
61 | | * been called. This allows for nesting calls to PRIVS_ROOT/PRIVS_RELINQUISH, |
62 | | * so that the last PRIVS_RELINQUISH call actually releases the privs. |
63 | | */ |
64 | | static unsigned int root_privs = 0; |
65 | | static unsigned int user_privs = 0; |
66 | | |
67 | 0 | static void privs_log_error(const char *msg, int xerrno) { |
68 | 0 | if (xerrno == EPERM) { |
69 | 0 | pr_log_debug(DEBUG2, "%s: %s", msg, strerror(xerrno)); |
70 | |
|
71 | 0 | } else { |
72 | 0 | pr_log_pri(PR_LOG_ERR, "%s: %s", msg, strerror(xerrno)); |
73 | 0 | } |
74 | 0 | } |
75 | | |
76 | 0 | int pr_privs_setup(uid_t uid, gid_t gid, const char *file, int lineno) { |
77 | 0 | if (nonroot_daemon == TRUE) { |
78 | 0 | session.ouid = session.uid = getuid(); |
79 | 0 | session.gid = getgid(); |
80 | |
|
81 | 0 | pr_trace_msg(trace_channel, 9, |
82 | 0 | "PRIVS_SETUP called at %s:%d for nonroot daemon, ignoring", file, lineno); |
83 | 0 | return 0; |
84 | 0 | } |
85 | | |
86 | 0 | pr_log_debug(DEBUG9, "SETUP PRIVS at %s:%d", file, lineno); |
87 | | |
88 | | /* Reset the user/root privs counters. */ |
89 | 0 | root_privs = user_privs = 0; |
90 | 0 | pr_trace_msg(trace_channel, 9, "PRIVS_SETUP called, " |
91 | 0 | "resetting user/root privs count"); |
92 | |
|
93 | 0 | pr_signals_block(); |
94 | |
|
95 | 0 | if (getuid() != PR_ROOT_UID) { |
96 | 0 | session.ouid = session.uid = getuid(); |
97 | 0 | session.gid = getgid(); |
98 | |
|
99 | 0 | if (setgid(session.gid) < 0) { |
100 | 0 | privs_log_error("SETUP PRIVS: unable to setgid()", errno); |
101 | 0 | } |
102 | |
|
103 | 0 | #if defined(HAVE_SETEUID) |
104 | 0 | if (setuid(session.uid) < 0) { |
105 | 0 | privs_log_error("SETUP PRIVS: unable to setuid()", errno); |
106 | 0 | } |
107 | |
|
108 | 0 | if (seteuid(session.uid) < 0) { |
109 | 0 | privs_log_error("SETUP PRIVS: unable to seteuid()", errno); |
110 | 0 | } |
111 | | #else |
112 | | if (setreuid(session.uid, session.uid) < 0) { |
113 | | privs_log_error("SETUP PRIVS: unable to setreuid()", errno); |
114 | | } |
115 | | #endif /* !HAVE_SETEUID */ |
116 | |
|
117 | 0 | } else { |
118 | 0 | session.ouid = getuid(); |
119 | 0 | session.uid = uid; |
120 | 0 | session.gid = gid; |
121 | |
|
122 | 0 | #if defined(HAVE_SETEUID) |
123 | 0 | if (setuid(PR_ROOT_UID) < 0) { |
124 | 0 | privs_log_error("SETUP PRIVS: unable to setuid()", errno); |
125 | 0 | } |
126 | |
|
127 | 0 | if (setgid(gid) < 0) { |
128 | 0 | privs_log_error("SETUP PRIVS: unable to setgid()", errno); |
129 | 0 | } |
130 | |
|
131 | 0 | if (seteuid(uid) < 0) { |
132 | 0 | privs_log_error("SETUP PRIVS: unable to seteuid()", errno); |
133 | 0 | } |
134 | | #else |
135 | | if (setgid(session.gid) < 0) { |
136 | | privs_log_error("SETUP PRIVS: unable to setgid()", errno); |
137 | | } |
138 | | |
139 | | if (setreuid(PR_ROOT_UID, session.uid) < 0) { |
140 | | privs_log_error("SETUP PRIVS: unable to setreuid()", errno); |
141 | | } |
142 | | #endif /* !HAVE_SETEUID */ |
143 | 0 | } |
144 | |
|
145 | 0 | pr_signals_unblock(); |
146 | 0 | return 0; |
147 | 0 | } |
148 | | |
149 | 0 | int pr_privs_root(const char *file, int lineno) { |
150 | 0 | if (nonroot_daemon == TRUE) { |
151 | 0 | pr_trace_msg(trace_channel, 9, |
152 | 0 | "PRIVS_ROOT called at %s:%d for nonroot daemon, ignoring", file, lineno); |
153 | 0 | return 0; |
154 | 0 | } |
155 | | |
156 | 0 | pr_log_debug(DEBUG9, "ROOT PRIVS at %s:%d", file, lineno); |
157 | |
|
158 | 0 | if (root_privs > 0) { |
159 | 0 | pr_trace_msg(trace_channel, 9, "root privs count = %u, ignoring PRIVS_ROOT", |
160 | 0 | root_privs); |
161 | 0 | return 0; |
162 | 0 | } |
163 | | |
164 | 0 | pr_trace_msg(trace_channel, 9, "root privs count = %u, honoring PRIVS_ROOT", |
165 | 0 | root_privs); |
166 | 0 | root_privs++; |
167 | |
|
168 | 0 | pr_signals_block(); |
169 | |
|
170 | 0 | if (!session.disable_id_switching) { |
171 | |
|
172 | 0 | #if defined(HAVE_SETEUID) |
173 | 0 | if (seteuid(PR_ROOT_UID) < 0) { |
174 | 0 | privs_log_error("ROOT PRIVS: unable to seteuid()", errno); |
175 | 0 | } |
176 | |
|
177 | 0 | if (setegid(PR_ROOT_GID) < 0) { |
178 | 0 | privs_log_error("ROOT PRIVS: unable to setegid()", errno); |
179 | 0 | } |
180 | | #else |
181 | | if (setreuid(session.uid, PR_ROOT_UID) < 0) { |
182 | | privs_log_error("ROOT PRIVS: unable to setreuid()", errno); |
183 | | } |
184 | | |
185 | | if (setregid(session.gid, PR_ROOT_GID)) { |
186 | | privs_log_error("ROOT PRIVS: unable to setregid()", errno); |
187 | | } |
188 | | #endif /* !HAVE_SETEUID */ |
189 | |
|
190 | 0 | } else { |
191 | 0 | pr_log_debug(DEBUG9, "ROOT PRIVS: ID switching disabled"); |
192 | 0 | } |
193 | |
|
194 | 0 | pr_signals_unblock(); |
195 | 0 | return 0; |
196 | 0 | } |
197 | | |
198 | 0 | int pr_privs_user(const char *file, int lineno) { |
199 | 0 | if (nonroot_daemon == TRUE) { |
200 | 0 | pr_trace_msg(trace_channel, 9, |
201 | 0 | "PRIVS_USER called at %s:%d for nonroot daemon, ignoring", file, lineno); |
202 | 0 | return 0; |
203 | 0 | } |
204 | | |
205 | 0 | pr_log_debug(DEBUG9, "USER PRIVS %s at %s:%d", |
206 | 0 | pr_uid2str(NULL, session.login_uid), file, lineno); |
207 | |
|
208 | 0 | if (user_privs > 0) { |
209 | 0 | pr_trace_msg(trace_channel, 9, "user privs count = %u, ignoring PRIVS_USER", |
210 | 0 | user_privs); |
211 | 0 | return 0; |
212 | 0 | } |
213 | | |
214 | 0 | pr_trace_msg(trace_channel, 9, "user privs count = %u, honoring PRIVS_USER", |
215 | 0 | user_privs); |
216 | 0 | user_privs++; |
217 | |
|
218 | 0 | pr_signals_block(); |
219 | |
|
220 | 0 | if (!session.disable_id_switching) { |
221 | 0 | #if defined(HAVE_SETEUID) |
222 | 0 | if (seteuid(PR_ROOT_UID) < 0) { |
223 | 0 | privs_log_error("USER PRIVS: unable to seteuid(PR_ROOT_UID)", errno); |
224 | 0 | } |
225 | |
|
226 | 0 | if (setegid(session.login_gid) < 0) { |
227 | 0 | privs_log_error("USER PRIVS: unable to setegid(session.login_gid)", |
228 | 0 | errno); |
229 | 0 | } |
230 | |
|
231 | 0 | if (seteuid(session.login_uid) < 0) { |
232 | 0 | privs_log_error("USER PRIVS: unable to seteuid(session.login_uid)", |
233 | 0 | errno); |
234 | 0 | } |
235 | | #else |
236 | | if (setreuid(session.uid, PR_ROOT_UID) < 0) { |
237 | | privs_log_error( |
238 | | "USER PRIVS: unable to setreuid(session.uid, PR_ROOT_UID)", errno); |
239 | | } |
240 | | |
241 | | if (setregid(session.gid, session.login_gid) < 0) { |
242 | | privs_log_error( |
243 | | "USER PRIVS: unable to setregid(session.gid, session.login_gid)", |
244 | | errno); |
245 | | } |
246 | | |
247 | | if (setreuid(session.uid, session.login_uid) < 0) { |
248 | | privs_log_error( |
249 | | "USER PRIVS: unable to setreuid(session.uid, session.login_uid)", |
250 | | errno); |
251 | | } |
252 | | #endif /* !HAVE_SETEUID */ |
253 | |
|
254 | 0 | } else { |
255 | 0 | pr_log_debug(DEBUG9, "USER PRIVS: ID switching disabled"); |
256 | 0 | } |
257 | |
|
258 | 0 | pr_signals_unblock(); |
259 | 0 | return 0; |
260 | 0 | } |
261 | | |
262 | 0 | int pr_privs_relinquish(const char *file, int lineno) { |
263 | 0 | if (nonroot_daemon == TRUE) { |
264 | 0 | pr_trace_msg(trace_channel, 9, |
265 | 0 | "PRIVS_RELINQUISH called at %s:%d for nonroot daemon, ignoring", file, |
266 | 0 | lineno); |
267 | 0 | return 0; |
268 | 0 | } |
269 | | |
270 | 0 | pr_log_debug(DEBUG9, "RELINQUISH PRIVS at %s:%d", file, lineno); |
271 | |
|
272 | 0 | if (root_privs == 0 && |
273 | 0 | user_privs == 0) { |
274 | | /* No privs to relinquish here. */ |
275 | 0 | pr_trace_msg(trace_channel, 9, |
276 | 0 | "user/root privs count = 0, ignoring PRIVS_RELINQUISH"); |
277 | 0 | return 0; |
278 | 0 | } |
279 | | |
280 | | /* We only want to actually relinquish the privs (user or root) when |
281 | | * the nesting count reaches 1. |
282 | | */ |
283 | 0 | if (root_privs + user_privs > 1) { |
284 | 0 | pr_trace_msg(trace_channel, 9, |
285 | 0 | "root privs count = %u, user privs count = %u, ignoring PRIVS_RELINQUISH", |
286 | 0 | root_privs, user_privs); |
287 | 0 | return 0; |
288 | 0 | } |
289 | | |
290 | 0 | pr_trace_msg(trace_channel, 9, "root privs count = %u, user privs " |
291 | 0 | "count = %u, honoring PRIVS_RELINQUISH", root_privs, user_privs); |
292 | |
|
293 | 0 | pr_signals_block(); |
294 | |
|
295 | 0 | if (!session.disable_id_switching) { |
296 | 0 | #if defined(HAVE_SETEUID) |
297 | 0 | if (geteuid() != PR_ROOT_UID) { |
298 | 0 | if (seteuid(PR_ROOT_UID) < 0) { |
299 | 0 | privs_log_error( |
300 | 0 | "RELINQUISH PRIVS: unable to seteuid(PR_ROOT_UID)", errno); |
301 | 0 | } |
302 | |
|
303 | 0 | if (user_privs > 0) { |
304 | 0 | user_privs--; |
305 | 0 | } |
306 | |
|
307 | 0 | } else { |
308 | 0 | if (root_privs > 0) { |
309 | 0 | root_privs--; |
310 | 0 | } |
311 | 0 | } |
312 | |
|
313 | 0 | if (setegid(session.gid) < 0) { |
314 | 0 | privs_log_error( |
315 | 0 | "RELINQUISH PRIVS: unable to setegid(session.gid)", errno); |
316 | 0 | } |
317 | |
|
318 | 0 | if (seteuid(session.uid) < 0) { |
319 | 0 | privs_log_error( |
320 | 0 | "RELINQUISH PRIVS: unable to seteuid(session.uid)", errno); |
321 | 0 | } |
322 | | #else |
323 | | if (geteuid() != PR_ROOT_UID) { |
324 | | if (setreuid(session.uid, PR_ROOT_UID) < 0) { |
325 | | privs_log_error( |
326 | | "RELINQUISH PRIVS: unable to setreuid(session.uid, PR_ROOT_UID)", |
327 | | errno); |
328 | | } |
329 | | |
330 | | if (user_privs > 0) { |
331 | | user_privs--; |
332 | | } |
333 | | |
334 | | } else { |
335 | | if (root_privs > 0) { |
336 | | root_privs--; |
337 | | } |
338 | | } |
339 | | |
340 | | if (getegid() != PR_ROOT_GID) { |
341 | | if (setregid(session.gid, PR_ROOT_GID) < 0) { |
342 | | privs_log_error( |
343 | | "RELINQUISH PRIVS: unable to setregid(session.gid, PR_ROOT_GID)", |
344 | | errno); |
345 | | } |
346 | | } |
347 | | |
348 | | if (setregid(session.gid, session.gid)) { |
349 | | privs_log_error( |
350 | | "RELINQUISH PRIVS: unable to setregid(session.gid, session.gid)", |
351 | | errno); |
352 | | } |
353 | | |
354 | | if (setreuid(session.uid, session.uid)) { |
355 | | privs_log_error( |
356 | | "RELINQUISH PRIVS: unable to setreuid(session.uid, session.uid)", |
357 | | errno); |
358 | | } |
359 | | |
360 | | #endif /* !HAVE_SETEUID */ |
361 | 0 | } else { |
362 | 0 | pr_log_debug(DEBUG9, "RELINQUISH PRIVS: ID switching disabled"); |
363 | 0 | } |
364 | |
|
365 | 0 | pr_signals_unblock(); |
366 | 0 | return 0; |
367 | 0 | } |
368 | | |
369 | 0 | int pr_privs_revoke(const char *file, int lineno) { |
370 | 0 | if (nonroot_daemon == TRUE) { |
371 | 0 | pr_trace_msg(trace_channel, 9, |
372 | 0 | "PRIVS_REVOKE called at %s:%d for nonroot daemon, ignoring", file, |
373 | 0 | lineno); |
374 | 0 | return 0; |
375 | 0 | } |
376 | | |
377 | 0 | pr_log_debug(DEBUG9, "REVOKE PRIVS at %s:%d", file, lineno); |
378 | |
|
379 | 0 | root_privs = user_privs = 0; |
380 | 0 | pr_trace_msg(trace_channel, 9, "PRIVS_REVOKE called, " |
381 | 0 | "clearing user/root privs count"); |
382 | |
|
383 | 0 | pr_signals_block(); |
384 | |
|
385 | 0 | #if defined(HAVE_SETEUID) |
386 | 0 | if (seteuid(PR_ROOT_UID) < 0) { |
387 | 0 | privs_log_error("REVOKE PRIVS: unable to seteuid()", errno); |
388 | 0 | } |
389 | |
|
390 | 0 | if (setgid(session.gid) < 0) { |
391 | 0 | privs_log_error("REVOKE PRIVS: unable to setgid()", errno); |
392 | 0 | } |
393 | |
|
394 | 0 | if (setuid(session.uid) < 0) { |
395 | 0 | privs_log_error("REVOKE PRIVS: unable to setuid()", errno); |
396 | 0 | } |
397 | | #else |
398 | | if (setreuid(PR_ROOT_UID, PR_ROOT_UID) < 0) { |
399 | | privs_log_error( |
400 | | "REVOKE PRIVS: unable to setreuid(PR_ROOT_UID, PR_ROOT_UID)", errno); |
401 | | } |
402 | | |
403 | | if (setgid(session.gid) < 0) { |
404 | | privs_log_error("REVOKE PRIVS: unable to setgid()", errno); |
405 | | } |
406 | | |
407 | | if (setuid(session.uid) < 0) { |
408 | | privs_log_error("REVOKE PRIVS: unable to setuid()", errno); |
409 | | } |
410 | | #endif /* !HAVE_SETEUID */ |
411 | |
|
412 | 0 | pr_signals_unblock(); |
413 | 0 | return 0; |
414 | 0 | } |
415 | | |
416 | | /* Returns the previous value, or -1 on error. */ |
417 | 0 | int set_nonroot_daemon(int nonroot) { |
418 | 0 | int was_nonroot; |
419 | |
|
420 | 0 | if (nonroot != TRUE && |
421 | 0 | nonroot != FALSE) { |
422 | 0 | errno = EINVAL; |
423 | 0 | return -1; |
424 | 0 | } |
425 | | |
426 | 0 | was_nonroot = nonroot_daemon; |
427 | 0 | nonroot_daemon = nonroot; |
428 | |
|
429 | 0 | return was_nonroot; |
430 | 0 | } |
431 | | |
432 | 0 | int init_privs(void) { |
433 | | /* Check to see if we have real root privs. */ |
434 | 0 | if (getuid() != PR_ROOT_UID) { |
435 | 0 | set_nonroot_daemon(TRUE); |
436 | 0 | } |
437 | |
|
438 | 0 | return 0; |
439 | 0 | } |