GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/syslogd/privsep.c Lines: 202 447 45.2 %
Date: 2017-11-13 Branches: 103 277 37.2 %

Line Branch Exec Source
1
/*	$OpenBSD: privsep.c,v 1.67 2017/04/05 11:31:45 bluhm 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.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
 */
55
enum 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
62
enum 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
74
static int priv_fd = -1;
75
static volatile pid_t child_pid = -1;
76
static volatile sig_atomic_t cur_state = STATE_INIT;
77
78
/* Queue for the allowed logfiles */
79
struct logname {
80
	char path[PATH_MAX];
81
	TAILQ_ENTRY(logname) next;
82
};
83
static TAILQ_HEAD(, logname) lognames;
84
85
static void check_log_name(char *, size_t);
86
static int open_file(char *);
87
static int open_pipe(char *);
88
static void check_tty_name(char *, size_t);
89
static void increase_state(int);
90
static void sig_pass_to_chld(int);
91
static void sig_got_chld(int);
92
static void must_read(int, void *, size_t);
93
static void must_write(int, void *, size_t);
94
static int  may_read(int, void *, size_t);
95
96
void
97
priv_init(int lockfd, int nullfd, int argc, char *argv[])
98
{
99
	int i, socks[2];
100
	struct passwd *pw;
101
	char *execpath, childnum[11], **privargv;
102
103
	/* Create sockets */
104
	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
105
		err(1, "socketpair() failed");
106
107
	pw = getpwnam("_syslogd");
108
	if (pw == NULL)
109
		errx(1, "unknown user _syslogd");
110
111
	child_pid = fork();
112
	if (child_pid < 0)
113
		err(1, "fork() failed");
114
115
	if (!child_pid) {
116
		/* Child - drop privileges and return */
117
		if (chroot(pw->pw_dir) != 0)
118
			err(1, "chroot %s", pw->pw_dir);
119
		if (chdir("/") != 0)
120
			err(1, "chdir %s", pw->pw_dir);
121
122
		if (setgroups(1, &pw->pw_gid) == -1)
123
			err(1, "setgroups() failed");
124
		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
125
			err(1, "setresgid() failed");
126
		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
127
			err(1, "setresuid() failed");
128
		close(socks[0]);
129
		priv_fd = socks[1];
130
		return;
131
	}
132
	close(socks[1]);
133
134
	if (strchr(argv[0], '/') == NULL)
135
		execpath = argv[0];
136
	else if ((execpath = realpath(argv[0], NULL)) == NULL)
137
		err(1, "realpath %s", argv[0]);
138
	if (chdir("/") != 0)
139
		err(1, "chdir /");
140
141
	if (!Debug) {
142
		close(lockfd);
143
		dup2(nullfd, STDIN_FILENO);
144
		dup2(nullfd, STDOUT_FILENO);
145
		dup2(nullfd, STDERR_FILENO);
146
	}
147
	if (nullfd > 2)
148
		close(nullfd);
149
150
	if (dup3(socks[0], 3, 0) == -1)
151
		err(1, "dup3 priv sock failed");
152
	if (closefrom(4) == -1)
153
		err(1, "closefrom 4 failed");
154
155
	snprintf(childnum, sizeof(childnum), "%d", child_pid);
156
	if ((privargv = reallocarray(NULL, argc + 3, sizeof(char *))) == NULL)
157
		err(1, "alloc priv argv failed");
158
	privargv[0] = execpath;
159
	for (i = 1; i < argc; i++)
160
		privargv[i] = argv[i];
161
	privargv[i++] = "-P";
162
	privargv[i++] = childnum;
163
	privargv[i++] = NULL;
164
	execvp(privargv[0], privargv);
165
	err(1, "exec priv '%s' failed", privargv[0]);
166
}
167
168
__dead void
169
priv_exec(char *conf, int numeric, int child, int argc, char *argv[])
170
{
171
1430
	int i, fd, sock, cmd, addr_len, result, restart;
172
715
	size_t path_len, protoname_len, hostname_len, servname_len;
173
715
	char path[PATH_MAX], protoname[5];
174
715
	char hostname[NI_MAXHOST], servname[NI_MAXSERV];
175
715
	struct sockaddr_storage addr;
176
715
	struct stat cf_info, cf_stat;
177
715
	struct addrinfo hints, *res0;
178
715
	struct sigaction sa;
179
715
	sigset_t sigmask;
180
181
1430
	if (pledge("stdio rpath wpath cpath dns getpw sendfd id proc exec flock rpath cpath wpath",
182
715
	    NULL) == -1)
183
		err(1, "pledge priv");
184
185

1430
	if (argc <= 2 || strcmp("-P", argv[argc - 2]) != 0)
186
		errx(1, "exec without priv");
187
715
	argv[argc -= 2] = NULL;
188
189
	sock = 3;
190
715
	closefrom(4);
191
192
715
	child_pid = child;
193
194
715
	memset(&sa, 0, sizeof(sa));
195
715
	sigemptyset(&sa.sa_mask);
196
715
	sa.sa_flags = SA_RESTART;
197
715
	sa.sa_handler = SIG_DFL;
198
47190
	for (i = 1; i < _NSIG; i++)
199
22880
		sigaction(i, &sa, NULL);
200
201
	/* Pass TERM/HUP/INT/QUIT through to child, and accept CHLD */
202
715
	sa.sa_handler = sig_pass_to_chld;
203
715
	sigaction(SIGTERM, &sa, NULL);
204
715
	sigaction(SIGHUP, &sa, NULL);
205
715
	sigaction(SIGINT, &sa, NULL);
206
715
	sigaction(SIGQUIT, &sa, NULL);
207
715
	sa.sa_handler = sig_got_chld;
208
715
	sa.sa_flags |= SA_NOCLDSTOP;
209
715
	sigaction(SIGCHLD, &sa, NULL);
210
211
715
	setproctitle("[priv]");
212
715
	log_debug("[priv]: fork+exec done");
213
214
715
	sigemptyset(&sigmask);
215
715
	if (sigprocmask(SIG_SETMASK, &sigmask, NULL) == -1)
216
		err(1, "sigprocmask priv");
217
218
715
	if (stat(conf, &cf_info) < 0)
219
		err(1, "stat config file failed");
220
221
715
	TAILQ_INIT(&lognames);
222
715
	increase_state(STATE_CONFIG);
223
	restart = 0;
224
225
81846
	while (cur_state < STATE_QUIT) {
226
40910
		if (may_read(sock, &cmd, sizeof(int)))
227
			break;
228


40208
		switch (cmd) {
229
		case PRIV_OPEN_TTY:
230
17130
			log_debug("[priv]: msg PRIV_OPEN_TTY received");
231
			/* Expecting: length, path */
232
17130
			must_read(sock, &path_len, sizeof(size_t));
233
17130
			if (path_len == 0 || path_len > sizeof(path))
234
				_exit(1);
235
17130
			must_read(sock, &path, path_len);
236
17130
			path[path_len - 1] = '\0';
237
17130
			check_tty_name(path, sizeof(path));
238
17130
			fd = open(path, O_WRONLY|O_NONBLOCK, 0);
239
17130
			send_fd(sock, fd);
240
17130
			if (fd < 0)
241
				warnx("priv_open_tty failed");
242
			else
243
17130
				close(fd);
244
			break;
245
246
		case PRIV_OPEN_LOG:
247
		case PRIV_OPEN_PIPE:
248
2168
			log_debug("[priv]: msg PRIV_OPEN_%s received",
249
2168
			    cmd == PRIV_OPEN_PIPE ? "PIPE" : "LOG");
250
			/* Expecting: length, path */
251
2168
			must_read(sock, &path_len, sizeof(size_t));
252
2168
			if (path_len == 0 || path_len > sizeof(path))
253
				_exit(1);
254
2168
			must_read(sock, &path, path_len);
255
2168
			path[path_len - 1] = '\0';
256
2168
			check_log_name(path, sizeof(path));
257
258
2168
			if (cmd == PRIV_OPEN_LOG)
259
1436
				fd = open_file(path);
260
732
			else if (cmd == PRIV_OPEN_PIPE)
261
732
				fd = open_pipe(path);
262
			else
263
				errx(1, "invalid cmd");
264
265
2168
			send_fd(sock, fd);
266
2168
			if (fd < 0)
267
16
				warnx("priv_open_log failed");
268
			else
269
2152
				close(fd);
270
			break;
271
272
		case PRIV_OPEN_UTMP:
273
16385
			log_debug("[priv]: msg PRIV_OPEN_UTMP received");
274
16385
			fd = open(_PATH_UTMP, O_RDONLY|O_NONBLOCK, 0);
275
16385
			send_fd(sock, fd);
276
16385
			if (fd < 0)
277
				warnx("priv_open_utmp failed");
278
			else
279
16385
				close(fd);
280
			break;
281
282
		case PRIV_OPEN_CONFIG:
283
755
			log_debug("[priv]: msg PRIV_OPEN_CONFIG received");
284
755
			stat(conf, &cf_info);
285
755
			fd = open(conf, O_RDONLY|O_NONBLOCK, 0);
286
755
			send_fd(sock, fd);
287
755
			if (fd < 0)
288
				warnx("priv_open_config failed");
289
			else
290
755
				close(fd);
291
			break;
292
293
		case PRIV_CONFIG_MODIFIED:
294
755
			log_debug("[priv]: msg PRIV_CONFIG_MODIFIED received");
295

1510
			if (stat(conf, &cf_stat) < 0 ||
296
1510
			    timespeccmp(&cf_info.st_mtimespec,
297

755
			    &cf_stat.st_mtimespec, <) ||
298
755
			    cf_info.st_size != cf_stat.st_size) {
299
				log_debug("config file modified: restarting");
300
				restart = result = 1;
301
				must_write(sock, &result, sizeof(int));
302
			} else {
303
755
				result = 0;
304
755
				must_write(sock, &result, sizeof(int));
305
			}
306
			break;
307
308
		case PRIV_DONE_CONFIG_PARSE:
309
715
			log_debug("[priv]: msg PRIV_DONE_CONFIG_PARSE "
310
			    "received");
311
715
			increase_state(STATE_RUNNING);
312
715
			break;
313
314
		case PRIV_GETADDRINFO:
315
736
			log_debug("[priv]: msg PRIV_GETADDRINFO received");
316
			/* Expecting: len, proto, len, host, len, serv */
317
736
			must_read(sock, &protoname_len, sizeof(size_t));
318
1472
			if (protoname_len == 0 ||
319
736
			    protoname_len > sizeof(protoname))
320
				_exit(1);
321
736
			must_read(sock, &protoname, protoname_len);
322
736
			protoname[protoname_len - 1] = '\0';
323
324
736
			must_read(sock, &hostname_len, sizeof(size_t));
325
1472
			if (hostname_len == 0 ||
326
736
			    hostname_len > sizeof(hostname))
327
				_exit(1);
328
736
			must_read(sock, &hostname, hostname_len);
329
736
			hostname[hostname_len - 1] = '\0';
330
331
736
			must_read(sock, &servname_len, sizeof(size_t));
332
1472
			if (servname_len == 0 ||
333
736
			    servname_len > sizeof(servname))
334
				_exit(1);
335
736
			must_read(sock, &servname, servname_len);
336
736
			servname[servname_len - 1] = '\0';
337
338
736
			memset(&hints, 0, sizeof(hints));
339
736
			switch (strlen(protoname)) {
340
			case 3:
341
				hints.ai_family = AF_UNSPEC;
342
				break;
343
			case 4:
344
124
				switch (protoname[3]) {
345
				case '4':
346
					hints.ai_family = AF_INET;
347
48
					break;
348
				case '6':
349
					hints.ai_family = AF_INET6;
350
					break;
351
				default:
352
					errx(1, "bad ip version %s", protoname);
353
				}
354
				break;
355
			default:
356
				errx(1, "bad protocol length %s", protoname);
357
			}
358
736
			if (strncmp(protoname, "udp", 3) == 0) {
359
				hints.ai_socktype = SOCK_DGRAM;
360
				hints.ai_protocol = IPPROTO_UDP;
361
200
			} else if (strncmp(protoname, "tcp", 3) == 0) {
362
				hints.ai_socktype = SOCK_STREAM;
363
				hints.ai_protocol = IPPROTO_TCP;
364
			} else {
365
				errx(1, "unknown protocol %s", protoname);
366
			}
367
736
			i = getaddrinfo(hostname, servname, &hints, &res0);
368
736
			if (i != 0 || res0 == NULL) {
369
8
				addr_len = 0;
370
8
				must_write(sock, &addr_len, sizeof(int));
371
8
			} else {
372
				/* Just send the first address */
373
728
				i = res0->ai_addrlen;
374
728
				must_write(sock, &i, sizeof(int));
375
728
				must_write(sock, res0->ai_addr, i);
376
728
				freeaddrinfo(res0);
377
			}
378
			break;
379
380
		case PRIV_GETNAMEINFO:
381
1564
			log_debug("[priv]: msg PRIV_GETNAMEINFO received");
382
1564
			if (numeric)
383
				errx(1, "rejected attempt to getnameinfo");
384
			/* Expecting: length, sockaddr */
385
1564
			must_read(sock, &addr_len, sizeof(int));
386

3128
			if (addr_len <= 0 || (size_t)addr_len > sizeof(addr))
387
				_exit(1);
388
1564
			must_read(sock, &addr, addr_len);
389
4692
			if (getnameinfo((struct sockaddr *)&addr, addr_len,
390
1564
			    hostname, sizeof(hostname), NULL, 0,
391
1564
			    NI_NOFQDN|NI_NAMEREQD|NI_DGRAM) != 0) {
392
				addr_len = 0;
393
				must_write(sock, &addr_len, sizeof(int));
394
			} else {
395
1564
				addr_len = strlen(hostname) + 1;
396
1564
				must_write(sock, &addr_len, sizeof(int));
397
1564
				must_write(sock, hostname, addr_len);
398
			}
399
			break;
400
		default:
401
			errx(1, "unknown command %d", cmd);
402
			break;
403
		}
404
	}
405
406
715
	close(sock);
407
408
	/* Unlink any domain sockets that have been opened */
409
3028
	for (i = 0; i < nunix; i++)
410
799
		(void)unlink(path_unix[i]);
411
715
	if (path_ctlsock != NULL)
412
40
		(void)unlink(path_ctlsock);
413
414
715
	if (restart) {
415
		int status;
416
417
		waitpid(child_pid, &status, 0);
418
		sigemptyset(&sigmask);
419
		sigaddset(&sigmask, SIGHUP);
420
		if (sigprocmask(SIG_SETMASK, &sigmask, NULL) == -1)
421
			err(1, "sigprocmask exec");
422
		execvp(argv[0], argv);
423
		err(1, "exec restart '%s' failed", argv[0]);
424
	}
425
	unlink(_PATH_LOGPID);
426
	exit(0);
427
}
428
429
static int
430
open_file(char *path)
431
{
432
	/* must not start with | */
433
2872
	if (path[0] == '|')
434
		return (-1);
435
436
1436
	return (open(path, O_WRONLY|O_APPEND|O_NONBLOCK, 0));
437
1436
}
438
439
static int
440
open_pipe(char *cmd)
441
{
442
1464
	char *argp[] = {"sh", "-c", NULL, NULL};
443
	struct passwd *pw;
444
732
	int fd[2];
445
732
	int bsize, flags;
446
	pid_t pid;
447
448
	/* skip over leading | and whitespace */
449
732
	if (cmd[0] != '|')
450
		return (-1);
451

2196
	for (cmd++; *cmd && *cmd == ' '; cmd++)
452
		; /* nothing */
453
732
	if (!*cmd)
454
		return (-1);
455
456
732
	argp[2] = cmd;
457
458
732
	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd) == -1) {
459
		warnx("open_pipe");
460
		return (-1);
461
	}
462
463
	/* make the fd on syslogd's side nonblocking */
464
732
	if ((flags = fcntl(fd[1], F_GETFL)) == -1) {
465
		warnx("fcntl");
466
		return (-1);
467
	}
468
732
	flags |= O_NONBLOCK;
469
732
	if ((flags = fcntl(fd[1], F_SETFL, flags)) == -1) {
470
		warnx("fcntl");
471
		return (-1);
472
	}
473
474
732
	switch (pid = fork()) {
475
	case -1:
476
		warnx("fork error");
477
		return (-1);
478
	case 0:
479
		break;
480
	default:
481
732
		close(fd[0]);
482
732
		return (fd[1]);
483
	}
484
485
	close(fd[1]);
486
487
	/* grow receive buffer */
488
	bsize = 65535;
489
	while (bsize > 0 && setsockopt(fd[0], SOL_SOCKET, SO_RCVBUF,
490
	    &bsize, sizeof(bsize)) == -1)
491
		bsize /= 2;
492
493
	if ((pw = getpwnam("_syslogd")) == NULL)
494
		errx(1, "unknown user _syslogd");
495
	if (setgroups(1, &pw->pw_gid) == -1 ||
496
	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
497
	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
498
		err(1, "failure dropping privs");
499
	endpwent();
500
501
	if (dup2(fd[0], STDIN_FILENO) == -1)
502
		err(1, "dup2 failed");
503
	if (execv("/bin/sh", argp) == -1)
504
		err(1, "execv %s", cmd);
505
	/* NOTREACHED */
506
	return (-1);
507
732
}
508
509
/* Check that the terminal device is ok, and if not, rewrite to /dev/null.
510
 * Either /dev/console or /dev/tty* are allowed.
511
 */
512
static void
513
check_tty_name(char *tty, size_t ttysize)
514
{
515
	const char ttypre[] = "/dev/tty";
516
	char *p;
517
518
	/* Any path containing '..' is invalid.  */
519

586788
	for (p = tty; p + 1 < tty + ttysize && *p; p++)
520

172756
		if (*p == '.' && *(p + 1) == '.')
521
			goto bad_path;
522
523

33532
	if (strcmp(_PATH_CONSOLE, tty) && strncmp(tty, ttypre, strlen(ttypre)))
524
		goto bad_path;
525
17130
	return;
526
527
bad_path:
528
	warnx ("%s: invalid attempt to open %s: rewriting to /dev/null",
529
	    "check_tty_name", tty);
530
	strlcpy(tty, "/dev/null", ttysize);
531
17130
}
532
533
/* If we are in the initial configuration state, accept a logname and add
534
 * it to the list of acceptable logfiles.  Otherwise, check against this list
535
 * and rewrite to /dev/null if it's a bad path.
536
 */
537
static void
538
check_log_name(char *lognam, size_t logsize)
539
{
540
	struct logname *lg;
541
	char *p;
542
543
	/* Any path containing '..' is invalid.  */
544

294305
	for (p = lognam; p + 1 < lognam + logsize && *p; p++)
545

98735
		if (*p == '.' && *(p + 1) == '.')
546
			goto bad_path;
547
548
2168
	switch (cur_state) {
549
	case STATE_CONFIG:
550
2004
		lg = malloc(sizeof(struct logname));
551
2004
		if (!lg)
552
			err(1, "check_log_name() malloc");
553
2004
		strlcpy(lg->path, lognam, PATH_MAX);
554
2004
		TAILQ_INSERT_TAIL(&lognames, lg, next);
555
		break;
556
	case STATE_RUNNING:
557
2264
		TAILQ_FOREACH(lg, &lognames, next)
558
1132
			if (!strcmp(lg->path, lognam))
559
164
				return;
560
		goto bad_path;
561
		break;
562
	default:
563
		/* Any other state should just refuse the request */
564
		goto bad_path;
565
		break;
566
	}
567
2004
	return;
568
569
bad_path:
570
	warnx("%s: invalid attempt to open %s: rewriting to /dev/null",
571
	    "check_log_name", lognam);
572
	strlcpy(lognam, "/dev/null", logsize);
573
2168
}
574
575
/* Crank our state into less permissive modes */
576
static void
577
increase_state(int state)
578
{
579
2860
	if (state <= cur_state)
580
		errx(1, "attempt to decrease or match current state");
581
1430
	if (state < STATE_INIT || state > STATE_QUIT)
582
		errx(1, "attempt to switch to invalid state");
583
1430
	cur_state = state;
584
1430
}
585
586
/* Open console or a terminal device for writing */
587
int
588
priv_open_tty(const char *tty)
589
{
590
	char path[PATH_MAX];
591
	int cmd, fd;
592
	size_t path_len;
593
594
	if (priv_fd < 0)
595
		errx(1, "%s: called from privileged portion", __func__);
596
597
	if (strlcpy(path, tty, sizeof path) >= sizeof(path))
598
		return -1;
599
	path_len = strlen(path) + 1;
600
601
	cmd = PRIV_OPEN_TTY;
602
	must_write(priv_fd, &cmd, sizeof(int));
603
	must_write(priv_fd, &path_len, sizeof(size_t));
604
	must_write(priv_fd, path, path_len);
605
	fd = receive_fd(priv_fd);
606
	return fd;
607
}
608
609
/* Open log-file */
610
int
611
priv_open_log(const char *lognam)
612
{
613
	char path[PATH_MAX];
614
	int cmd, fd;
615
	size_t path_len;
616
617
	if (priv_fd < 0)
618
		errx(1, "%s: called from privileged child", __func__);
619
620
	if (strlcpy(path, lognam, sizeof path) >= sizeof(path))
621
		return -1;
622
	path_len = strlen(path) + 1;
623
624
	if (lognam[0] == '|')
625
		cmd = PRIV_OPEN_PIPE;
626
	else
627
		cmd = PRIV_OPEN_LOG;
628
	must_write(priv_fd, &cmd, sizeof(int));
629
	must_write(priv_fd, &path_len, sizeof(size_t));
630
	must_write(priv_fd, path, path_len);
631
	fd = receive_fd(priv_fd);
632
	return fd;
633
}
634
635
/* Open utmp for reading */
636
FILE *
637
priv_open_utmp(void)
638
{
639
	int cmd, fd;
640
	FILE *fp;
641
642
	if (priv_fd < 0)
643
		errx(1, "%s: called from privileged portion", __func__);
644
645
	cmd = PRIV_OPEN_UTMP;
646
	must_write(priv_fd, &cmd, sizeof(int));
647
	fd = receive_fd(priv_fd);
648
	if (fd < 0)
649
		return NULL;
650
651
	fp = fdopen(fd, "r");
652
	if (!fp) {
653
		warn("priv_open_utmp: fdopen() failed");
654
		close(fd);
655
		return NULL;
656
	}
657
658
	return fp;
659
}
660
661
/* Open syslog config file for reading */
662
FILE *
663
priv_open_config(void)
664
{
665
	int cmd, fd;
666
	FILE *fp;
667
668
	if (priv_fd < 0)
669
		errx(1, "%s: called from privileged portion", __func__);
670
671
	cmd = PRIV_OPEN_CONFIG;
672
	must_write(priv_fd, &cmd, sizeof(int));
673
	fd = receive_fd(priv_fd);
674
	if (fd < 0)
675
		return NULL;
676
677
	fp = fdopen(fd, "r");
678
	if (!fp) {
679
		warn("priv_open_config: fdopen() failed");
680
		close(fd);
681
		return NULL;
682
	}
683
684
	return fp;
685
}
686
687
/* Ask if config file has been modified since last attempt to read it */
688
int
689
priv_config_modified(void)
690
{
691
	int cmd, res;
692
693
	if (priv_fd < 0)
694
		errx(1, "%s: called from privileged portion", __func__);
695
696
	cmd = PRIV_CONFIG_MODIFIED;
697
	must_write(priv_fd, &cmd, sizeof(int));
698
699
	/* Expect back integer signalling 1 for modification */
700
	must_read(priv_fd, &res, sizeof(int));
701
	return res;
702
}
703
704
/* Child can signal that its initial parsing is done, so that parent
705
 * can revoke further logfile permissions.  This call only works once. */
706
void
707
priv_config_parse_done(void)
708
{
709
	int cmd;
710
711
	if (priv_fd < 0)
712
		errx(1, "%s: called from privileged portion", __func__);
713
714
	cmd = PRIV_DONE_CONFIG_PARSE;
715
	must_write(priv_fd, &cmd, sizeof(int));
716
}
717
718
/* Name/service to address translation.  Response is placed into addr.
719
 * Return 0 for success or < 0 for error like getaddrinfo(3) */
720
int
721
priv_getaddrinfo(char *proto, char *host, char *serv, struct sockaddr *addr,
722
    size_t addr_len)
723
{
724
	char protocpy[5], hostcpy[NI_MAXHOST], servcpy[NI_MAXSERV];
725
	int cmd, ret_len;
726
	size_t protoname_len, hostname_len, servname_len;
727
728
	if (priv_fd < 0)
729
		errx(1, "%s: called from privileged portion", __func__);
730
731
	if (strlcpy(protocpy, proto, sizeof(protocpy)) >= sizeof(protocpy))
732
		errx(1, "%s: overflow attempt in protoname", __func__);
733
	protoname_len = strlen(protocpy) + 1;
734
	if (strlcpy(hostcpy, host, sizeof(hostcpy)) >= sizeof(hostcpy))
735
		errx(1, "%s: overflow attempt in hostname", __func__);
736
	hostname_len = strlen(hostcpy) + 1;
737
	if (strlcpy(servcpy, serv, sizeof(servcpy)) >= sizeof(servcpy))
738
		errx(1, "%s: overflow attempt in servname", __func__);
739
	servname_len = strlen(servcpy) + 1;
740
741
	cmd = PRIV_GETADDRINFO;
742
	must_write(priv_fd, &cmd, sizeof(int));
743
	must_write(priv_fd, &protoname_len, sizeof(size_t));
744
	must_write(priv_fd, protocpy, protoname_len);
745
	must_write(priv_fd, &hostname_len, sizeof(size_t));
746
	must_write(priv_fd, hostcpy, hostname_len);
747
	must_write(priv_fd, &servname_len, sizeof(size_t));
748
	must_write(priv_fd, servcpy, servname_len);
749
750
	/* Expect back an integer size, and then a string of that length */
751
	must_read(priv_fd, &ret_len, sizeof(int));
752
753
	/* Check there was no error (indicated by a return of 0) */
754
	if (!ret_len)
755
		return (-1);
756
757
	/* Make sure we aren't overflowing the passed in buffer */
758
	if (ret_len < 0 || (size_t)ret_len > addr_len)
759
		errx(1, "%s: overflow attempt in return", __func__);
760
761
	/* Read the resolved address and make sure we got all of it */
762
	memset(addr, '\0', addr_len);
763
	must_read(priv_fd, addr, ret_len);
764
765
	return (0);
766
}
767
768
/* Reverse address resolution; response is placed into host.
769
 * Return 0 for success or < 0 for error like getnameinfo(3) */
770
int
771
priv_getnameinfo(struct sockaddr *sa, socklen_t salen, char *host,
772
    size_t hostlen)
773
{
774
	int cmd, ret_len;
775
776
	if (priv_fd < 0)
777
		errx(1, "%s called from privileged portion", __func__);
778
779
	cmd = PRIV_GETNAMEINFO;
780
	must_write(priv_fd, &cmd, sizeof(int));
781
	must_write(priv_fd, &salen, sizeof(int));
782
	must_write(priv_fd, sa, salen);
783
784
	/* Expect back an integer size, and then a string of that length */
785
	must_read(priv_fd, &ret_len, sizeof(int));
786
787
	/* Check there was no error (indicated by a return of 0) */
788
	if (!ret_len)
789
		return (-1);
790
791
	/* Check we don't overflow the passed in buffer */
792
	if (ret_len < 0 || (size_t)ret_len > hostlen)
793
		errx(1, "%s: overflow attempt in return", __func__);
794
795
	/* Read the resolved hostname */
796
	must_read(priv_fd, host, ret_len);
797
	return (0);
798
}
799
800
/* Pass the signal through to child */
801
static void
802
sig_pass_to_chld(int sig)
803
{
804
1390
	int save_errno = errno;
805
806
695
	if (child_pid != -1)
807
695
		kill(child_pid, sig);
808
695
	errno = save_errno;
809
695
}
810
811
/* When child dies, move into the shutdown state */
812
/* ARGSUSED */
813
static void
814
sig_got_chld(int sig)
815
{
816
1532
	int save_errno = errno;
817
	pid_t	pid;
818
819
766
	do {
820
1544
		pid = waitpid(WAIT_ANY, NULL, WNOHANG);
821

2259
		if (pid == child_pid && cur_state < STATE_QUIT)
822
715
			cur_state = STATE_QUIT;
823

2353
	} while (pid > 0 || (pid == -1 && errno == EINTR));
824
766
	errno = save_errno;
825
766
}
826
827
/* Read all data or return 1 for error.  */
828
static int
829
may_read(int fd, void *buf, size_t n)
830
{
831
	char *s = buf;
832
	ssize_t res;
833
	size_t pos = 0;
834
835
203146
	while (n > pos) {
836
40910
		res = read(fd, s + pos, n - pos);
837
40910
		switch (res) {
838
		case -1:
839
			if (errno == EINTR || errno == EAGAIN)
840
				continue;
841
		case 0:
842
702
			return (1);
843
		default:
844
40208
			pos += res;
845
		}
846
	}
847
40208
	return (0);
848
40910
}
849
850
/* Read data with the assertion that it all must come through, or
851
 * else abort the process.  Based on atomicio() from openssh. */
852
static void
853
must_read(int fd, void *buf, size_t n)
854
{
855
	char *s = buf;
856
	ssize_t res;
857
	size_t pos = 0;
858
859
230700
	while (n > pos) {
860
46140
		res = read(fd, s + pos, n - pos);
861
46140
		switch (res) {
862
		case -1:
863
			if (errno == EINTR || errno == EAGAIN)
864
				continue;
865
		case 0:
866
			_exit(1);
867
		default:
868
46140
			pos += res;
869
		}
870
	}
871
46140
}
872
873
/* Write data with the assertion that it all has to be written, or
874
 * else abort the process.  Based on atomicio() from openssh. */
875
static void
876
must_write(int fd, void *buf, size_t n)
877
{
878
	char *s = buf;
879
	ssize_t res;
880
	size_t pos = 0;
881
882
26735
	while (n > pos) {
883
5347
		res = write(fd, s + pos, n - pos);
884
5347
		switch (res) {
885
		case -1:
886
			if (errno == EINTR || errno == EAGAIN)
887
				continue;
888
		case 0:
889
			_exit(1);
890
		default:
891
5347
			pos += res;
892
		}
893
	}
894
5347
}