1 /*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 2009-2020 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
privs_log_error(const char * msg,int xerrno)67 static void privs_log_error(const char *msg, int xerrno) {
68 switch (xerrno) {
69 case EPERM:
70 pr_log_debug(DEBUG2, "%s: %s", msg, strerror(xerrno));
71 break;
72
73 default:
74 pr_log_pri(PR_LOG_ERR, "%s: %s", msg, strerror(xerrno));
75 }
76 }
77
pr_privs_setup(uid_t uid,gid_t gid,const char * file,int lineno)78 int pr_privs_setup(uid_t uid, gid_t gid, const char *file, int lineno) {
79 if (nonroot_daemon == TRUE) {
80 session.ouid = session.uid = getuid();
81 session.gid = getgid();
82
83 pr_trace_msg(trace_channel, 9,
84 "PRIVS_SETUP called at %s:%d for nonroot daemon, ignoring", file, lineno);
85 return 0;
86 }
87
88 pr_log_debug(DEBUG9, "SETUP PRIVS at %s:%d", file, lineno);
89
90 /* Reset the user/root privs counters. */
91 root_privs = user_privs = 0;
92 pr_trace_msg(trace_channel, 9, "PRIVS_SETUP called, "
93 "resetting user/root privs count");
94
95 pr_signals_block();
96
97 if (getuid() != PR_ROOT_UID) {
98 session.ouid = session.uid = getuid();
99 session.gid = getgid();
100
101 if (setgid(session.gid) < 0) {
102 privs_log_error("SETUP PRIVS: unable to setgid()", errno);
103 }
104
105 #if defined(HAVE_SETEUID)
106 if (setuid(session.uid) < 0) {
107 privs_log_error("SETUP PRIVS: unable to setuid()", errno);
108 }
109
110 if (seteuid(session.uid) < 0) {
111 privs_log_error("SETUP PRIVS: unable to seteuid()", errno);
112 }
113 #else
114 if (setreuid(session.uid, session.uid) < 0) {
115 privs_log_error("SETUP PRIVS: unable to setreuid()", errno);
116 }
117 #endif /* !HAVE_SETEUID */
118
119 } else {
120 session.ouid = getuid();
121 session.uid = uid;
122 session.gid = gid;
123
124 #if defined(HAVE_SETEUID)
125 if (setuid(PR_ROOT_UID) < 0) {
126 privs_log_error("SETUP PRIVS: unable to setuid()", errno);
127 }
128
129 if (setgid(gid) < 0) {
130 privs_log_error("SETUP PRIVS: unable to setgid()", errno);
131 }
132
133 if (seteuid(uid) < 0) {
134 privs_log_error("SETUP PRIVS: unable to seteuid()", errno);
135 }
136 #else
137 if (setgid(session.gid) < 0) {
138 privs_log_error("SETUP PRIVS: unable to setgid()", errno);
139 }
140
141 if (setreuid(PR_ROOT_UID, session.uid) < 0) {
142 privs_log_error("SETUP PRIVS: unable to setreuid()", errno);
143 }
144 #endif /* !HAVE_SETEUID */
145 }
146
147 pr_signals_unblock();
148 return 0;
149 }
150
pr_privs_root(const char * file,int lineno)151 int pr_privs_root(const char *file, int lineno) {
152 if (nonroot_daemon == TRUE) {
153 pr_trace_msg(trace_channel, 9,
154 "PRIVS_ROOT called at %s:%d for nonroot daemon, ignoring", file, lineno);
155 return 0;
156 }
157
158 pr_log_debug(DEBUG9, "ROOT PRIVS at %s:%d", file, lineno);
159
160 if (root_privs > 0) {
161 pr_trace_msg(trace_channel, 9, "root privs count = %u, ignoring PRIVS_ROOT",
162 root_privs);
163 return 0;
164 }
165
166 pr_trace_msg(trace_channel, 9, "root privs count = %u, honoring PRIVS_ROOT",
167 root_privs);
168 root_privs++;
169
170 pr_signals_block();
171
172 if (!session.disable_id_switching) {
173
174 #if defined(HAVE_SETEUID)
175 if (seteuid(PR_ROOT_UID) < 0) {
176 privs_log_error("ROOT PRIVS: unable to seteuid()", errno);
177 }
178
179 if (setegid(PR_ROOT_GID) < 0) {
180 privs_log_error("ROOT PRIVS: unable to setegid()", errno);
181 }
182 #else
183 if (setreuid(session.uid, PR_ROOT_UID) < 0) {
184 privs_log_error("ROOT PRIVS: unable to setreuid()", errno);
185 }
186
187 if (setregid(session.gid, PR_ROOT_GID)) {
188 privs_log_error("ROOT PRIVS: unable to setregid()", errno);
189 }
190 #endif /* !HAVE_SETEUID */
191
192 } else {
193 pr_log_debug(DEBUG9, "ROOT PRIVS: ID switching disabled");
194 }
195
196 pr_signals_unblock();
197 return 0;
198 }
199
pr_privs_user(const char * file,int lineno)200 int pr_privs_user(const char *file, int lineno) {
201 if (nonroot_daemon == TRUE) {
202 pr_trace_msg(trace_channel, 9,
203 "PRIVS_USER called at %s:%d for nonroot daemon, ignoring", file, lineno);
204 return 0;
205 }
206
207 pr_log_debug(DEBUG9, "USER PRIVS %s at %s:%d",
208 pr_uid2str(NULL, session.login_uid), file, lineno);
209
210 if (user_privs > 0) {
211 pr_trace_msg(trace_channel, 9, "user privs count = %u, ignoring PRIVS_USER",
212 user_privs);
213 return 0;
214 }
215
216 pr_trace_msg(trace_channel, 9, "user privs count = %u, honoring PRIVS_USER",
217 user_privs);
218 user_privs++;
219
220 pr_signals_block();
221
222 if (!session.disable_id_switching) {
223 #if defined(HAVE_SETEUID)
224 if (seteuid(PR_ROOT_UID) < 0) {
225 privs_log_error("USER PRIVS: unable to seteuid(PR_ROOT_UID)", errno);
226 }
227
228 if (setegid(session.login_gid) < 0) {
229 privs_log_error("USER PRIVS: unable to setegid(session.login_gid)",
230 errno);
231 }
232
233 if (seteuid(session.login_uid) < 0) {
234 privs_log_error("USER PRIVS: unable to seteuid(session.login_uid)",
235 errno);
236 }
237 #else
238 if (setreuid(session.uid, PR_ROOT_UID) < 0) {
239 privs_log_error(
240 "USER PRIVS: unable to setreuid(session.uid, PR_ROOT_UID)", errno);
241 }
242
243 if (setregid(session.gid, session.login_gid) < 0) {
244 privs_log_error(
245 "USER PRIVS: unable to setregid(session.gid, session.login_gid)",
246 errno);
247 }
248
249 if (setreuid(session.uid, session.login_uid) < 0) {
250 privs_log_error(
251 "USER PRIVS: unable to setreuid(session.uid, session.login_uid)",
252 errno);
253 }
254 #endif /* !HAVE_SETEUID */
255
256 } else {
257 pr_log_debug(DEBUG9, "USER PRIVS: ID switching disabled");
258 }
259
260 pr_signals_unblock();
261 return 0;
262 }
263
pr_privs_relinquish(const char * file,int lineno)264 int pr_privs_relinquish(const char *file, int lineno) {
265 if (nonroot_daemon == TRUE) {
266 pr_trace_msg(trace_channel, 9,
267 "PRIVS_RELINQUISH called at %s:%d for nonroot daemon, ignoring", file,
268 lineno);
269 return 0;
270 }
271
272 pr_log_debug(DEBUG9, "RELINQUISH PRIVS at %s:%d", file, lineno);
273
274 if (root_privs == 0 &&
275 user_privs == 0) {
276 /* No privs to relinquish here. */
277 pr_trace_msg(trace_channel, 9,
278 "user/root privs count = 0, ignoring PRIVS_RELINQUISH");
279 return 0;
280 }
281
282 /* We only want to actually relinquish the privs (user or root) when
283 * the nesting count reaches 1.
284 */
285 if (root_privs + user_privs > 1) {
286 pr_trace_msg(trace_channel, 9,
287 "root privs count = %u, user privs count = %u, ignoring PRIVS_RELINQUISH",
288 root_privs, user_privs);
289 return 0;
290
291 } else {
292 pr_trace_msg(trace_channel, 9, "root privs count = %u, user privs "
293 "count = %u, honoring PRIVS_RELINQUISH", root_privs, user_privs);
294 }
295
296 pr_signals_block();
297
298 if (!session.disable_id_switching) {
299 #if defined(HAVE_SETEUID)
300 if (geteuid() != PR_ROOT_UID) {
301 if (seteuid(PR_ROOT_UID) < 0) {
302 privs_log_error(
303 "RELINQUISH PRIVS: unable to seteuid(PR_ROOT_UID)", errno);
304 }
305
306 if (user_privs > 0) {
307 user_privs--;
308 }
309
310 } else {
311 if (root_privs > 0) {
312 root_privs--;
313 }
314 }
315
316 if (setegid(session.gid) < 0) {
317 privs_log_error(
318 "RELINQUISH PRIVS: unable to setegid(session.gid)", errno);
319 }
320
321 if (seteuid(session.uid) < 0) {
322 privs_log_error(
323 "RELINQUISH PRIVS: unable to seteuid(session.uid)", errno);
324 }
325 #else
326 if (geteuid() != PR_ROOT_UID) {
327 if (setreuid(session.uid, PR_ROOT_UID) < 0) {
328 privs_log_error(
329 "RELINQUISH PRIVS: unable to setreuid(session.uid, PR_ROOT_UID)",
330 errno);
331 }
332
333 if (user_privs > 0) {
334 user_privs--;
335 }
336
337 } else {
338 if (root_privs > 0) {
339 root_privs--;
340 }
341 }
342
343 if (getegid() != PR_ROOT_GID) {
344 if (setregid(session.gid, PR_ROOT_GID) < 0) {
345 privs_log_error(
346 "RELINQUISH PRIVS: unable to setregid(session.gid, PR_ROOT_GID)",
347 errno);
348 }
349 }
350
351 if (setregid(session.gid, session.gid)) {
352 privs_log_error(
353 "RELINQUISH PRIVS: unable to setregid(session.gid, session.gid)",
354 errno);
355 }
356
357 if (setreuid(session.uid, session.uid)) {
358 privs_log_error(
359 "RELINQUISH PRIVS: unable to setreuid(session.uid, session.uid)",
360 errno);
361 }
362
363 #endif /* !HAVE_SETEUID */
364 } else {
365 pr_log_debug(DEBUG9, "RELINQUISH PRIVS: ID switching disabled");
366 }
367
368 pr_signals_unblock();
369 return 0;
370 }
371
pr_privs_revoke(const char * file,int lineno)372 int pr_privs_revoke(const char *file, int lineno) {
373 if (nonroot_daemon == TRUE) {
374 pr_trace_msg(trace_channel, 9,
375 "PRIVS_REVOKE called at %s:%d for nonroot daemon, ignoring", file,
376 lineno);
377 return 0;
378 }
379
380 pr_log_debug(DEBUG9, "REVOKE PRIVS at %s:%d", file, lineno);
381
382 root_privs = user_privs = 0;
383 pr_trace_msg(trace_channel, 9, "PRIVS_REVOKE called, "
384 "clearing user/root privs count");
385
386 pr_signals_block();
387
388 #if defined(HAVE_SETEUID)
389 if (seteuid(PR_ROOT_UID) < 0) {
390 privs_log_error("REVOKE PRIVS: unable to seteuid()", errno);
391 }
392
393 if (setgid(session.gid) < 0) {
394 privs_log_error("REVOKE PRIVS: unable to setgid()", errno);
395 }
396
397 if (setuid(session.uid) < 0) {
398 privs_log_error("REVOKE PRIVS: unable to setuid()", errno);
399 }
400 #else
401 if (setreuid(PR_ROOT_UID, PR_ROOT_UID) < 0) {
402 privs_log_error(
403 "REVOKE PRIVS: unable to setreuid(PR_ROOT_UID, PR_ROOT_UID)", errno);
404 }
405
406 if (setgid(session.gid) < 0) {
407 privs_log_error("REVOKE PRIVS: unable to setgid()", errno);
408 }
409
410 if (setuid(session.uid) < 0) {
411 privs_log_error("REVOKE PRIVS: unable to setuid()", errno);
412 }
413 #endif /* !HAVE_SETEUID */
414
415 pr_signals_unblock();
416 return 0;
417 }
418
419 /* Returns the previous value, or -1 on error. */
set_nonroot_daemon(int nonroot)420 int set_nonroot_daemon(int nonroot) {
421 int was_nonroot;
422
423 if (nonroot != TRUE &&
424 nonroot != FALSE) {
425 errno = EINVAL;
426 return -1;
427 }
428
429 was_nonroot = nonroot_daemon;
430 nonroot_daemon = nonroot;
431
432 return was_nonroot;
433 }
434
init_privs(void)435 int init_privs(void) {
436 /* Check to see if we have real root privs. */
437 if (getuid() != PR_ROOT_UID) {
438 set_nonroot_daemon(TRUE);
439 }
440
441 return 0;
442 }
443