Bug Summary

File:src/usr.sbin/syslogd/privsep.c
Warning:line 213, column 7
Although the value stored to 'argc' is used in the enclosing expression, the value is never actually read from 'argc'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name privsep.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/syslogd/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/syslogd/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.sbin/syslogd/privsep.c
1/* $OpenBSD: privsep.c,v 1.74 2021/10/24 21:24:19 deraadt Exp $ */
2
3/*
4 * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
5 * Copyright (c) 2016 Alexander Bluhm <bluhm@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/queue.h>
21#include <sys/stat.h>
22#include <sys/wait.h>
23
24#include <err.h>
25#include <errno(*__errno()).h>
26#include <fcntl.h>
27#include <limits.h>
28#include <netdb.h>
29#include <paths.h>
30#include <pwd.h>
31#include <signal.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <unistd.h>
36#include <utmp.h>
37
38#include "log.h"
39#include "syslogd.h"
40
41/*
42 * syslogd can only go forward in these states; each state should represent
43 * less privilege. After STATE_INIT, the child is allowed to parse its
44 * config file once, and communicate the information regarding what logfiles
45 * it needs access to back to the parent. When that is done, it sends a
46 * message to the priv parent revoking this access, moving to STATE_RUNNING.
47 * In this state, any log-files not in the access list are rejected.
48 *
49 * This allows a HUP signal to the child to reopen its log files, and
50 * the config file to be parsed if it hasn't been changed (this is still
51 * useful to force resolution of remote syslog servers again).
52 * If the config file has been modified, then the child dies, and
53 * the priv parent restarts itself.
54 */
55enum priv_state {
56 STATE_INIT, /* just started up */
57 STATE_CONFIG, /* parsing config file for first time */
58 STATE_RUNNING, /* running and accepting network traffic */
59 STATE_QUIT /* shutting down */
60};
61
62enum cmd_types {
63 PRIV_OPEN_TTY, /* open terminal or console device */
64 PRIV_OPEN_LOG, /* open logfile for appending */
65 PRIV_OPEN_PIPE, /* fork & exec child that gets logs on stdin */
66 PRIV_OPEN_UTMP, /* open utmp for reading only */
67 PRIV_OPEN_CONFIG, /* open config file for reading only */
68 PRIV_CONFIG_MODIFIED, /* check if config file has been modified */
69 PRIV_GETADDRINFO, /* resolve host/service names */
70 PRIV_GETNAMEINFO, /* resolve numeric address into hostname */
71 PRIV_DONE_CONFIG_PARSE /* signal that initial config parse is done */
72};
73
74static int priv_fd = -1;
75static volatile pid_t child_pid = -1;
76static volatile sig_atomic_t cur_state = STATE_INIT;
77
78/* Queue for the allowed logfiles */
79struct logname {
80 char path[PATH_MAX1024];
81 TAILQ_ENTRY(logname)struct { struct logname *tqe_next; struct logname **tqe_prev;
}
next;
82};
83static TAILQ_HEAD(, logname)struct { struct logname *tqh_first; struct logname **tqh_last
; }
lognames;
84
85static void check_log_name(char *, size_t);
86static int open_file(char *);
87static int open_pipe(char *);
88static void check_tty_name(char *, size_t);
89static void increase_state(int);
90static void sig_pass_to_chld(int);
91static void sig_got_chld(int);
92static void must_read(int, void *, size_t);
93static void must_write(int, void *, size_t);
94static int may_read(int, void *, size_t);
95
96static struct passwd *pw;
97
98void
99priv_init(int lockfd, int nullfd, int argc, char *argv[])
100{
101 int i, socks[2];
102 char *execpath, childnum[11], **privargv;
103
104 /* Create sockets */
105 if (socketpair(AF_LOCAL1, SOCK_STREAM1, PF_UNSPEC0, socks) == -1)
106 err(1, "socketpair() failed");
107
108 pw = getpwnam("_syslogd");
109 if (pw == NULL((void *)0))
110 errx(1, "unknown user _syslogd");
111
112 child_pid = fork();
113 if (child_pid == -1)
114 err(1, "fork() failed");
115
116 if (!child_pid) {
117 /* Child - drop privileges and return */
118 if (chroot(pw->pw_dir) != 0)
119 err(1, "chroot %s", pw->pw_dir);
120 if (chdir("/") != 0)
121 err(1, "chdir %s", pw->pw_dir);
122
123 if (setgroups(1, &pw->pw_gid) == -1)
124 err(1, "setgroups() failed");
125 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
126 err(1, "setresgid() failed");
127 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
128 err(1, "setresuid() failed");
129 close(socks[0]);
130 priv_fd = socks[1];
131 return;
132 }
133 close(socks[1]);
134
135 if (strchr(argv[0], '/') == NULL((void *)0))
136 execpath = argv[0];
137 else if ((execpath = realpath(argv[0], NULL((void *)0))) == NULL((void *)0))
138 err(1, "realpath %s", argv[0]);
139 if (chdir("/") != 0)
140 err(1, "chdir /");
141
142 if (!Debug) {
143 close(lockfd);
144 dup2(nullfd, STDIN_FILENO0);
145 dup2(nullfd, STDOUT_FILENO1);
146 dup2(nullfd, STDERR_FILENO2);
147 }
148 if (nullfd > 2)
149 close(nullfd);
150
151 if (dup3(socks[0], 3, 0) == -1)
152 err(1, "dup3 priv sock failed");
153 if (closefrom(4) == -1)
154 err(1, "closefrom 4 failed");
155
156 snprintf(childnum, sizeof(childnum), "%d", child_pid);
157 if ((privargv = reallocarray(NULL((void *)0), argc + 3, sizeof(char *))) == NULL((void *)0))
158 err(1, "alloc priv argv failed");
159 privargv[0] = execpath;
160 for (i = 1; i < argc; i++)
161 privargv[i] = argv[i];
162 privargv[i++] = "-P";
163 privargv[i++] = childnum;
164 privargv[i++] = NULL((void *)0);
165 execvp(privargv[0], privargv);
166 err(1, "exec priv '%s' failed", privargv[0]);
167}
168
169__dead__attribute__((__noreturn__)) void
170priv_exec(char *conf, int numeric, int child, int argc, char *argv[])
171{
172 int i, fd, sock, cmd, addr_len, result, restart;
173 size_t path_len, protoname_len, hostname_len, servname_len;
174 char path[PATH_MAX1024], protoname[5];
175 char hostname[NI_MAXHOST256], servname[NI_MAXSERV32];
176 struct sockaddr_storage addr;
177 struct stat cf_info, cf_stat;
178 struct addrinfo hints, *res0;
179 struct sigaction sa;
180 sigset_t sigmask;
181
182 /* Redo the password lookup after re-exec of the privsep parent. */
183 pw = getpwnam("_syslogd");
184 if (pw == NULL((void *)0))
185 errx(1, "unknown user _syslogd");
186
187 if (unveil(conf, "r") == -1)
188 err(1, "unveil %s", conf);
189 if (unveil(_PATH_UTMP"/var/run/utmp", "r") == -1)
190 err(1, "unveil %s", _PATH_UTMP"/var/run/utmp");
191 if (unveil(_PATH_DEV"/dev/", "rw") == -1)
192 err(1, "unveil %s", _PATH_DEV"/dev/");
193 if (unveil(_PATH_LOGPID"/var/run/syslog.pid", "c") == -1)
194 err(1, "unveil %s", _PATH_LOGPID"/var/run/syslog.pid");
195
196 /* for pipes */
197 if (unveil(_PATH_BSHELL"/bin/sh", "x") == -1)
198 err(1, "unveil %s", _PATH_BSHELL"/bin/sh");
199
200 /* For HUP / re-exec */
201 if (unveil("/usr/sbin/syslogd", "x") == -1)
202 err(1, "unveil /usr/sbin/syslogd");
203 if (argv[0][0] == '/')
204 if (unveil(argv[0], "x") == -1)
205 err(1, "unveil %s", argv[0]);
206
207 if (pledge("stdio unveil rpath wpath cpath dns sendfd id proc exec",
208 NULL((void *)0)) == -1)
209 err(1, "pledge priv");
210
211 if (argc <= 2 || strcmp("-P", argv[argc - 2]) != 0)
212 errx(1, "exec without priv");
213 argv[argc -= 2] = NULL((void *)0);
Although the value stored to 'argc' is used in the enclosing expression, the value is never actually read from 'argc'
214
215 sock = 3;
216 closefrom(4);
217
218 child_pid = child;
219
220 memset(&sa, 0, sizeof(sa));
221 sigemptyset(&sa.sa_mask);
222 sa.sa_flags = SA_RESTART0x0002;
223 sa.sa_handler__sigaction_u.__sa_handler = SIG_DFL(void (*)(int))0;
224 for (i = 1; i < _NSIG33; i++)
225 sigaction(i, &sa, NULL((void *)0));
226
227 /* Pass TERM/HUP/INT/QUIT through to child, and accept CHLD */
228 sa.sa_handler__sigaction_u.__sa_handler = sig_pass_to_chld;
229 sigaction(SIGTERM15, &sa, NULL((void *)0));
230 sigaction(SIGHUP1, &sa, NULL((void *)0));
231 sigaction(SIGINT2, &sa, NULL((void *)0));
232 sigaction(SIGQUIT3, &sa, NULL((void *)0));
233 sa.sa_handler__sigaction_u.__sa_handler = sig_got_chld;
234 sa.sa_flags |= SA_NOCLDSTOP0x0008;
235 sigaction(SIGCHLD20, &sa, NULL((void *)0));
236
237 setproctitle("[priv]");
238 log_debug("[priv]: fork+exec done");
239
240 sigemptyset(&sigmask);
241 if (sigprocmask(SIG_SETMASK3, &sigmask, NULL((void *)0)) == -1)
242 err(1, "sigprocmask priv");
243
244 if (stat(conf, &cf_info) == -1)
245 err(1, "stat config file failed");
246
247 TAILQ_INIT(&lognames)do { (&lognames)->tqh_first = ((void *)0); (&lognames
)->tqh_last = &(&lognames)->tqh_first; } while (
0)
;
248 increase_state(STATE_CONFIG);
249 restart = 0;
250
251 while (cur_state < STATE_QUIT) {
252 if (may_read(sock, &cmd, sizeof(int)))
253 break;
254 switch (cmd) {
255 case PRIV_OPEN_TTY:
256 log_debug("[priv]: msg PRIV_OPEN_TTY received");
257 /* Expecting: length, path */
258 must_read(sock, &path_len, sizeof(size_t));
259 if (path_len == 0 || path_len > sizeof(path))
260 _exit(1);
261 must_read(sock, &path, path_len);
262 path[path_len - 1] = '\0';
263 check_tty_name(path, sizeof(path));
264 fd = open(path, O_WRONLY0x0001|O_NONBLOCK0x0004);
265 send_fd(sock, fd);
266 if (fd == -1)
267 warnx("priv_open_tty failed");
268 else
269 close(fd);
270 break;
271
272 case PRIV_OPEN_LOG:
273 case PRIV_OPEN_PIPE:
274 log_debug("[priv]: msg PRIV_OPEN_%s received",
275 cmd == PRIV_OPEN_PIPE ? "PIPE" : "LOG");
276 /* Expecting: length, path */
277 must_read(sock, &path_len, sizeof(size_t));
278 if (path_len == 0 || path_len > sizeof(path))
279 _exit(1);
280 must_read(sock, &path, path_len);
281 path[path_len - 1] = '\0';
282 check_log_name(path, sizeof(path));
283
284 if (cmd == PRIV_OPEN_LOG)
285 fd = open_file(path);
286 else if (cmd == PRIV_OPEN_PIPE)
287 fd = open_pipe(path);
288 else
289 errx(1, "invalid cmd");
290
291 send_fd(sock, fd);
292 if (fd == -1)
293 warnx("priv_open_log failed");
294 else
295 close(fd);
296 break;
297
298 case PRIV_OPEN_UTMP:
299 log_debug("[priv]: msg PRIV_OPEN_UTMP received");
300 fd = open(_PATH_UTMP"/var/run/utmp", O_RDONLY0x0000|O_NONBLOCK0x0004);
301 send_fd(sock, fd);
302 if (fd == -1)
303 warnx("priv_open_utmp failed");
304 else
305 close(fd);
306 break;
307
308 case PRIV_OPEN_CONFIG:
309 log_debug("[priv]: msg PRIV_OPEN_CONFIG received");
310 stat(conf, &cf_info);
311 fd = open(conf, O_RDONLY0x0000|O_NONBLOCK0x0004);
312 send_fd(sock, fd);
313 if (fd == -1)
314 warnx("priv_open_config failed");
315 else
316 close(fd);
317 break;
318
319 case PRIV_CONFIG_MODIFIED:
320 log_debug("[priv]: msg PRIV_CONFIG_MODIFIED received");
321 if (stat(conf, &cf_stat) == -1 ||
322 timespeccmp(&cf_info.st_mtimespec,(((&cf_info.st_mtim)->tv_sec == (&cf_stat.st_mtim)
->tv_sec) ? ((&cf_info.st_mtim)->tv_nsec < (&
cf_stat.st_mtim)->tv_nsec) : ((&cf_info.st_mtim)->tv_sec
< (&cf_stat.st_mtim)->tv_sec))
323 &cf_stat.st_mtimespec, <)(((&cf_info.st_mtim)->tv_sec == (&cf_stat.st_mtim)
->tv_sec) ? ((&cf_info.st_mtim)->tv_nsec < (&
cf_stat.st_mtim)->tv_nsec) : ((&cf_info.st_mtim)->tv_sec
< (&cf_stat.st_mtim)->tv_sec))
||
324 cf_info.st_size != cf_stat.st_size) {
325 log_debug("config file modified: restarting");
326 restart = result = 1;
327 must_write(sock, &result, sizeof(int));
328 } else {
329 result = 0;
330 must_write(sock, &result, sizeof(int));
331 }
332 break;
333
334 case PRIV_DONE_CONFIG_PARSE:
335 if (pledge("stdio rpath wpath cpath dns sendfd id proc exec",
336 NULL((void *)0)) == -1)
337 err(1, "pledge done config");
338 log_debug("[priv]: msg PRIV_DONE_CONFIG_PARSE "
339 "received");
340 increase_state(STATE_RUNNING);
341 break;
342
343 case PRIV_GETADDRINFO:
344 log_debug("[priv]: msg PRIV_GETADDRINFO received");
345 /* Expecting: len, proto, len, host, len, serv */
346 must_read(sock, &protoname_len, sizeof(size_t));
347 if (protoname_len == 0 ||
348 protoname_len > sizeof(protoname))
349 _exit(1);
350 must_read(sock, &protoname, protoname_len);
351 protoname[protoname_len - 1] = '\0';
352
353 must_read(sock, &hostname_len, sizeof(size_t));
354 if (hostname_len == 0 ||
355 hostname_len > sizeof(hostname))
356 _exit(1);
357 must_read(sock, &hostname, hostname_len);
358 hostname[hostname_len - 1] = '\0';
359
360 must_read(sock, &servname_len, sizeof(size_t));
361 if (servname_len == 0 ||
362 servname_len > sizeof(servname))
363 _exit(1);
364 must_read(sock, &servname, servname_len);
365 servname[servname_len - 1] = '\0';
366
367 memset(&hints, 0, sizeof(hints));
368 switch (strlen(protoname)) {
369 case 3:
370 hints.ai_family = AF_UNSPEC0;
371 break;
372 case 4:
373 switch (protoname[3]) {
374 case '4':
375 hints.ai_family = AF_INET2;
376 break;
377 case '6':
378 hints.ai_family = AF_INET624;
379 break;
380 default:
381 errx(1, "bad ip version %s", protoname);
382 }
383 break;
384 default:
385 errx(1, "bad protocol length %s", protoname);
386 }
387 if (strncmp(protoname, "udp", 3) == 0) {
388 hints.ai_socktype = SOCK_DGRAM2;
389 hints.ai_protocol = IPPROTO_UDP17;
390 } else if (strncmp(protoname, "tcp", 3) == 0) {
391 hints.ai_socktype = SOCK_STREAM1;
392 hints.ai_protocol = IPPROTO_TCP6;
393 } else {
394 errx(1, "unknown protocol %s", protoname);
395 }
396 i = getaddrinfo(hostname, servname, &hints, &res0);
397 if (i != 0 || res0 == NULL((void *)0)) {
398 addr_len = 0;
399 must_write(sock, &addr_len, sizeof(int));
400 } else {
401 /* Just send the first address */
402 i = res0->ai_addrlen;
403 must_write(sock, &i, sizeof(int));
404 must_write(sock, res0->ai_addr, i);
405 freeaddrinfo(res0);
406 }
407 break;
408
409 case PRIV_GETNAMEINFO:
410 log_debug("[priv]: msg PRIV_GETNAMEINFO received");
411 if (numeric)
412 errx(1, "rejected attempt to getnameinfo");
413 /* Expecting: length, sockaddr */
414 must_read(sock, &addr_len, sizeof(int));
415 if (addr_len <= 0 || (size_t)addr_len > sizeof(addr))
416 _exit(1);
417 must_read(sock, &addr, addr_len);
418 if (getnameinfo((struct sockaddr *)&addr, addr_len,
419 hostname, sizeof(hostname), NULL((void *)0), 0,
420 NI_NOFQDN4|NI_NAMEREQD8|NI_DGRAM16) != 0) {
421 addr_len = 0;
422 must_write(sock, &addr_len, sizeof(int));
423 } else {
424 addr_len = strlen(hostname) + 1;
425 must_write(sock, &addr_len, sizeof(int));
426 must_write(sock, hostname, addr_len);
427 }
428 break;
429 default:
430 errx(1, "unknown command %d", cmd);
431 break;
432 }
433 }
434
435 close(sock);
436
437 if (restart) {
438 int status;
439
440 waitpid(child_pid, &status, 0);
441 sigemptyset(&sigmask);
442 sigaddset(&sigmask, SIGHUP1);
443 if (sigprocmask(SIG_SETMASK3, &sigmask, NULL((void *)0)) == -1)
444 err(1, "sigprocmask exec");
445 execvp(argv[0], argv);
446 err(1, "exec restart '%s' failed", argv[0]);
447 }
448 unlink(_PATH_LOGPID"/var/run/syslog.pid");
449 exit(0);
450}
451
452static int
453open_file(char *path)
454{
455 /* must not start with | */
456 if (path[0] == '|')
457 return (-1);
458
459 return (open(path, O_WRONLY0x0001|O_APPEND0x0008|O_NONBLOCK0x0004));
460}
461
462static int
463open_pipe(char *cmd)
464{
465 char *argp[] = {"sh", "-c", NULL((void *)0), NULL((void *)0)};
466 int fd[2];
467 int bsize, flags;
468 pid_t pid;
469
470 /* skip over leading | and whitespace */
471 if (cmd[0] != '|')
472 return (-1);
473 for (cmd++; *cmd && *cmd == ' '; cmd++)
474 ; /* nothing */
475 if (!*cmd)
476 return (-1);
477
478 argp[2] = cmd;
479
480 if (socketpair(AF_UNIX1, SOCK_STREAM1, PF_UNSPEC0, fd) == -1) {
481 warnx("open_pipe");
482 return (-1);
483 }
484
485 /* make the fd on syslogd's side nonblocking */
486 if ((flags = fcntl(fd[1], F_GETFL3)) == -1) {
487 warnx("fcntl");
488 return (-1);
489 }
490 flags |= O_NONBLOCK0x0004;
491 if ((flags = fcntl(fd[1], F_SETFL4, flags)) == -1) {
492 warnx("fcntl");
493 return (-1);
494 }
495
496 switch (pid = fork()) {
497 case -1:
498 warnx("fork error");
499 return (-1);
500 case 0:
501 break;
502 default:
503 close(fd[0]);
504 return (fd[1]);
505 }
506
507 close(fd[1]);
508
509 /* grow receive buffer */
510 bsize = 65535;
511 while (bsize > 0 && setsockopt(fd[0], SOL_SOCKET0xffff, SO_RCVBUF0x1002,
512 &bsize, sizeof(bsize)) == -1)
513 bsize /= 2;
514
515 if (setgroups(1, &pw->pw_gid) == -1 ||
516 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
517 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
518 err(1, "failure dropping privs");
519
520 if (dup2(fd[0], STDIN_FILENO0) == -1)
521 err(1, "dup2 failed");
522 closefrom(STDERR_FILENO2 + 1);
523 if (execv("/bin/sh", argp) == -1)
524 err(1, "execv %s", cmd);
525 /* NOTREACHED */
526 return (-1);
527}
528
529/* Check that the terminal device is ok, and if not, rewrite to /dev/null.
530 * Either /dev/console or /dev/tty* are allowed.
531 */
532static void
533check_tty_name(char *tty, size_t ttysize)
534{
535 const char ttypre[] = "/dev/tty";
536 char *p;
537
538 /* Any path containing '..' is invalid. */
539 for (p = tty; p + 1 < tty + ttysize && *p; p++)
540 if (*p == '.' && *(p + 1) == '.')
541 goto bad_path;
542
543 if (strcmp(_PATH_CONSOLE"/dev/console", tty) && strncmp(tty, ttypre, strlen(ttypre)))
544 goto bad_path;
545 return;
546
547bad_path:
548 warnx ("%s: invalid attempt to open %s: rewriting to /dev/null",
549 "check_tty_name", tty);
550 strlcpy(tty, "/dev/null", ttysize);
551}
552
553/* If we are in the initial configuration state, accept a logname and add
554 * it to the list of acceptable logfiles. Otherwise, check against this list
555 * and rewrite to /dev/null if it's a bad path.
556 */
557static void
558check_log_name(char *lognam, size_t logsize)
559{
560 struct logname *lg;
561 char *p;
562
563 /* Any path containing '..' is invalid. */
564 for (p = lognam; p + 1 < lognam + logsize && *p; p++)
565 if (*p == '.' && *(p + 1) == '.')
566 goto bad_path;
567
568 switch (cur_state) {
569 case STATE_CONFIG:
570 lg = malloc(sizeof(struct logname));
571 if (!lg)
572 err(1, "check_log_name() malloc");
573 strlcpy(lg->path, lognam, PATH_MAX1024);
574 TAILQ_INSERT_TAIL(&lognames, lg, next)do { (lg)->next.tqe_next = ((void *)0); (lg)->next.tqe_prev
= (&lognames)->tqh_last; *(&lognames)->tqh_last
= (lg); (&lognames)->tqh_last = &(lg)->next.tqe_next
; } while (0)
;
575 if (lognam[0] != '|') {
576 if (unveil(lognam, "w") == -1)
577 goto bad_path;
578 }
579 break;
580 case STATE_RUNNING:
581 TAILQ_FOREACH(lg, &lognames, next)for((lg) = ((&lognames)->tqh_first); (lg) != ((void *)
0); (lg) = ((lg)->next.tqe_next))
582 if (!strcmp(lg->path, lognam))
583 return;
584 goto bad_path;
585 break;
586 default:
587 /* Any other state should just refuse the request */
588 goto bad_path;
589 break;
590 }
591 return;
592
593bad_path:
594 warnx("%s: invalid attempt to open %s: rewriting to /dev/null",
595 "check_log_name", lognam);
596 strlcpy(lognam, "/dev/null", logsize);
597}
598
599/* Crank our state into less permissive modes */
600static void
601increase_state(int state)
602{
603 if (state <= cur_state)
604 errx(1, "attempt to decrease or match current state");
605 if (state < STATE_INIT || state > STATE_QUIT)
606 errx(1, "attempt to switch to invalid state");
607 cur_state = state;
608}
609
610/* Open console or a terminal device for writing */
611int
612priv_open_tty(const char *tty)
613{
614 char path[PATH_MAX1024];
615 int cmd, fd;
616 size_t path_len;
617
618 if (priv_fd < 0)
619 errx(1, "%s: called from privileged portion", __func__);
620
621 if (strlcpy(path, tty, sizeof path) >= sizeof(path))
622 return -1;
623 path_len = strlen(path) + 1;
624
625 cmd = PRIV_OPEN_TTY;
626 must_write(priv_fd, &cmd, sizeof(int));
627 must_write(priv_fd, &path_len, sizeof(size_t));
628 must_write(priv_fd, path, path_len);
629 fd = receive_fd(priv_fd);
630 return fd;
631}
632
633/* Open log-file */
634int
635priv_open_log(const char *lognam)
636{
637 char path[PATH_MAX1024];
638 int cmd, fd;
639 size_t path_len;
640
641 if (priv_fd < 0)
642 errx(1, "%s: called from privileged child", __func__);
643
644 if (strlcpy(path, lognam, sizeof path) >= sizeof(path))
645 return -1;
646 path_len = strlen(path) + 1;
647
648 if (lognam[0] == '|')
649 cmd = PRIV_OPEN_PIPE;
650 else
651 cmd = PRIV_OPEN_LOG;
652 must_write(priv_fd, &cmd, sizeof(int));
653 must_write(priv_fd, &path_len, sizeof(size_t));
654 must_write(priv_fd, path, path_len);
655 fd = receive_fd(priv_fd);
656 return fd;
657}
658
659/* Open utmp for reading */
660FILE *
661priv_open_utmp(void)
662{
663 int cmd, fd;
664 FILE *fp;
665
666 if (priv_fd < 0)
667 errx(1, "%s: called from privileged portion", __func__);
668
669 cmd = PRIV_OPEN_UTMP;
670 must_write(priv_fd, &cmd, sizeof(int));
671 fd = receive_fd(priv_fd);
672 if (fd < 0)
673 return NULL((void *)0);
674
675 fp = fdopen(fd, "r");
676 if (!fp) {
677 warn("priv_open_utmp: fdopen() failed");
678 close(fd);
679 return NULL((void *)0);
680 }
681
682 return fp;
683}
684
685/* Open syslog config file for reading */
686FILE *
687priv_open_config(void)
688{
689 int cmd, fd;
690 FILE *fp;
691
692 if (priv_fd < 0)
693 errx(1, "%s: called from privileged portion", __func__);
694
695 cmd = PRIV_OPEN_CONFIG;
696 must_write(priv_fd, &cmd, sizeof(int));
697 fd = receive_fd(priv_fd);
698 if (fd < 0)
699 return NULL((void *)0);
700
701 fp = fdopen(fd, "r");
702 if (!fp) {
703 warn("priv_open_config: fdopen() failed");
704 close(fd);
705 return NULL((void *)0);
706 }
707
708 return fp;
709}
710
711/* Ask if config file has been modified since last attempt to read it */
712int
713priv_config_modified(void)
714{
715 int cmd, res;
716
717 if (priv_fd < 0)
718 errx(1, "%s: called from privileged portion", __func__);
719
720 cmd = PRIV_CONFIG_MODIFIED;
721 must_write(priv_fd, &cmd, sizeof(int));
722
723 /* Expect back integer signalling 1 for modification */
724 must_read(priv_fd, &res, sizeof(int));
725 return res;
726}
727
728/* Child can signal that its initial parsing is done, so that parent
729 * can revoke further logfile permissions. This call only works once. */
730void
731priv_config_parse_done(void)
732{
733 int cmd;
734
735 if (priv_fd < 0)
736 errx(1, "%s: called from privileged portion", __func__);
737
738 cmd = PRIV_DONE_CONFIG_PARSE;
739 must_write(priv_fd, &cmd, sizeof(int));
740}
741
742/* Name/service to address translation. Response is placed into addr.
743 * Return 0 for success or < 0 for error like getaddrinfo(3) */
744int
745priv_getaddrinfo(char *proto, char *host, char *serv, struct sockaddr *addr,
746 size_t addr_len)
747{
748 char protocpy[5], hostcpy[NI_MAXHOST256], servcpy[NI_MAXSERV32];
749 int cmd, ret_len;
750 size_t protoname_len, hostname_len, servname_len;
751
752 if (priv_fd < 0)
753 errx(1, "%s: called from privileged portion", __func__);
754
755 if (strlcpy(protocpy, proto, sizeof(protocpy)) >= sizeof(protocpy))
756 errx(1, "%s: overflow attempt in protoname", __func__);
757 protoname_len = strlen(protocpy) + 1;
758 if (strlcpy(hostcpy, host, sizeof(hostcpy)) >= sizeof(hostcpy))
759 errx(1, "%s: overflow attempt in hostname", __func__);
760 hostname_len = strlen(hostcpy) + 1;
761 if (strlcpy(servcpy, serv, sizeof(servcpy)) >= sizeof(servcpy))
762 errx(1, "%s: overflow attempt in servname", __func__);
763 servname_len = strlen(servcpy) + 1;
764
765 cmd = PRIV_GETADDRINFO;
766 must_write(priv_fd, &cmd, sizeof(int));
767 must_write(priv_fd, &protoname_len, sizeof(size_t));
768 must_write(priv_fd, protocpy, protoname_len);
769 must_write(priv_fd, &hostname_len, sizeof(size_t));
770 must_write(priv_fd, hostcpy, hostname_len);
771 must_write(priv_fd, &servname_len, sizeof(size_t));
772 must_write(priv_fd, servcpy, servname_len);
773
774 /* Expect back an integer size, and then a string of that length */
775 must_read(priv_fd, &ret_len, sizeof(int));
776
777 /* Check there was no error (indicated by a return of 0) */
778 if (!ret_len)
779 return (-1);
780
781 /* Make sure we aren't overflowing the passed in buffer */
782 if (ret_len < 0 || (size_t)ret_len > addr_len)
783 errx(1, "%s: overflow attempt in return", __func__);
784
785 /* Read the resolved address and make sure we got all of it */
786 memset(addr, '\0', addr_len);
787 must_read(priv_fd, addr, ret_len);
788
789 return (0);
790}
791
792/* Reverse address resolution; response is placed into host.
793 * Return 0 for success or < 0 for error like getnameinfo(3) */
794int
795priv_getnameinfo(struct sockaddr *sa, socklen_t salen, char *host,
796 size_t hostlen)
797{
798 int cmd, ret_len;
799
800 if (priv_fd < 0)
801 errx(1, "%s called from privileged portion", __func__);
802
803 cmd = PRIV_GETNAMEINFO;
804 must_write(priv_fd, &cmd, sizeof(int));
805 must_write(priv_fd, &salen, sizeof(int));
806 must_write(priv_fd, sa, salen);
807
808 /* Expect back an integer size, and then a string of that length */
809 must_read(priv_fd, &ret_len, sizeof(int));
810
811 /* Check there was no error (indicated by a return of 0) */
812 if (!ret_len)
813 return (-1);
814
815 /* Check we don't overflow the passed in buffer */
816 if (ret_len < 0 || (size_t)ret_len > hostlen)
817 errx(1, "%s: overflow attempt in return", __func__);
818
819 /* Read the resolved hostname */
820 must_read(priv_fd, host, ret_len);
821 return (0);
822}
823
824/* Pass the signal through to child */
825static void
826sig_pass_to_chld(int sig)
827{
828 int save_errno = errno(*__errno());
829
830 if (child_pid != -1)
831 kill(child_pid, sig);
832 errno(*__errno()) = save_errno;
833}
834
835/* When child dies, move into the shutdown state */
836/* ARGSUSED */
837static void
838sig_got_chld(int sig)
839{
840 int save_errno = errno(*__errno());
841 pid_t pid;
842
843 do {
844 pid = waitpid(WAIT_ANY(-1), NULL((void *)0), WNOHANG1);
845 if (pid == child_pid && cur_state < STATE_QUIT)
846 cur_state = STATE_QUIT;
847 } while (pid > 0 || (pid == -1 && errno(*__errno()) == EINTR4));
848 errno(*__errno()) = save_errno;
849}
850
851/* Read all data or return 1 for error. */
852static int
853may_read(int fd, void *buf, size_t n)
854{
855 char *s = buf;
856 ssize_t res;
857 size_t pos = 0;
858
859 while (n > pos) {
860 res = read(fd, s + pos, n - pos);
861 switch (res) {
862 case -1:
863 if (errno(*__errno()) == EINTR4 || errno(*__errno()) == EAGAIN35)
864 continue;
865 case 0:
866 return (1);
867 default:
868 pos += res;
869 }
870 }
871 return (0);
872}
873
874/* Read data with the assertion that it all must come through, or
875 * else abort the process. Based on atomicio() from openssh. */
876static void
877must_read(int fd, void *buf, size_t n)
878{
879 char *s = buf;
880 ssize_t res;
881 size_t pos = 0;
882
883 while (n > pos) {
884 res = read(fd, s + pos, n - pos);
885 switch (res) {
886 case -1:
887 if (errno(*__errno()) == EINTR4 || errno(*__errno()) == EAGAIN35)
888 continue;
889 case 0:
890 _exit(1);
891 default:
892 pos += res;
893 }
894 }
895}
896
897/* Write data with the assertion that it all has to be written, or
898 * else abort the process. Based on atomicio() from openssh. */
899static void
900must_write(int fd, void *buf, size_t n)
901{
902 char *s = buf;
903 ssize_t res;
904 size_t pos = 0;
905
906 while (n > pos) {
907 res = write(fd, s + pos, n - pos);
908 switch (res) {
909 case -1:
910 if (errno(*__errno()) == EINTR4 || errno(*__errno()) == EAGAIN35)
911 continue;
912 case 0:
913 _exit(1);
914 default:
915 pos += res;
916 }
917 }
918}