Bug Summary

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

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);
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()) {
Although the value stored to 'pid' is used in the enclosing expression, the value is never actually read from 'pid'
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}