Coverage Report

Created: 2024-02-28 06:06

/src/proftpd/src/privs.c
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
}