1 /* $OpenBSD: privsep.c,v 1.26 2005/06/06 23:20:44 djm Exp $ */
2
3 /*
4 * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #include <sys/ioctl.h>
19 #include <sys/param.h>
20 #include <sys/queue.h>
21 #include <sys/uio.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <sys/stat.h>
25 #include <sys/wait.h>
26 #include <err.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <netdb.h>
30 #include <paths.h>
31 #include <poll.h>
32 #include <pwd.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <util.h>
39 #include <utmp.h>
40 #include "syslogd.h"
41
42 /*
43 * syslogd can only go forward in these states; each state should represent
44 * less privilege. After STATE_INIT, the child is allowed to parse its
45 * config file once, and communicate the information regarding what logfiles
46 * it needs access to back to the parent. When that is done, it sends a
47 * message to the priv parent revoking this access, moving to STATE_RUNNING.
48 * In this state, any log-files not in the access list are rejected.
49 *
50 * This allows a HUP signal to the child to reopen its log files, and
51 * the config file to be parsed if it hasn't been changed (this is still
52 * useful to force resolution of remote syslog servers again).
53 * If the config file has been modified, then the child dies, and
54 * the priv parent restarts itself.
55 */
56 enum priv_state {
57 STATE_INIT, /* just started up */
58 STATE_CONFIG, /* parsing config file for first time */
59 STATE_RUNNING, /* running and accepting network traffic */
60 STATE_QUIT /* shutting down */
61 };
62
63 enum cmd_types {
64 PRIV_OPEN_TTY, /* open terminal or console device */
65 PRIV_OPEN_LOG, /* open logfile for appending */
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_GETHOSTSERV, /* resolve host/service names */
70 PRIV_GETHOSTBYADDR, /* resolve numeric address into hostname */
71 PRIV_DONE_CONFIG_PARSE /* signal that the initial config parse is done */
72 };
73
74 static int priv_fd = -1;
75 static volatile pid_t child_pid = -1;
76 static char config_file[MAXPATHLEN];
77 static struct stat cf_info;
78 static int allow_gethostbyaddr = 0;
79 static volatile sig_atomic_t cur_state = STATE_INIT;
80
81 /* Queue for the allowed logfiles */
82 struct logname {
83 char path[MAXPATHLEN];
84 TAILQ_ENTRY(logname) next;
85 };
86 static TAILQ_HEAD(, logname) lognames;
87
88 static void check_log_name(char *, size_t);
89 static void check_tty_name(char *, size_t);
90 static void increase_state(int);
91 static void sig_pass_to_chld(int);
92 static void sig_got_chld(int);
93 static void must_read(int, void *, size_t);
94 static void must_write(int, void *, size_t);
95 static int may_read(int, void *, size_t);
96
97 int
priv_init(char * conf,int numeric,int lockfd,int nullfd,char * argv[])98 priv_init(char *conf, int numeric, int lockfd, int nullfd, char *argv[])
99 {
100 int i, fd, socks[2], cmd, addr_len, addr_af, result, restart;
101 size_t path_len, hostname_len, servname_len;
102 char path[MAXPATHLEN], hostname[MAXHOSTNAMELEN];
103 char servname[MAXHOSTNAMELEN];
104 struct stat cf_stat;
105 struct hostent *hp;
106 struct passwd *pw;
107 struct addrinfo hints, *res0;
108
109 for (i = 1; i < _NSIG; i++)
110 signal(i, SIG_DFL);
111
112 /* Create sockets */
113 if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
114 err(1, "socketpair() failed");
115
116 pw = getpwnam("_syslogd");
117 if (pw == NULL)
118 errx(1, "unknown user _syslogd");
119
120 child_pid = fork();
121 if (child_pid < 0)
122 err(1, "fork() failed");
123
124 if (!child_pid) {
125 /* Child - drop privileges and return */
126 if (chroot(pw->pw_dir) != 0)
127 err(1, "unable to chroot");
128 if (chdir("/") != 0)
129 err(1, "unable to chdir");
130
131 if (setgroups(1, &pw->pw_gid) == -1)
132 err(1, "setgroups() failed");
133 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
134 err(1, "setresgid() failed");
135 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
136 err(1, "setresuid() failed");
137 close(socks[0]);
138 priv_fd = socks[1];
139 return 0;
140 }
141
142 close(lockfd);
143 if (!Debug) {
144 dup2(nullfd, STDIN_FILENO);
145 dup2(nullfd, STDOUT_FILENO);
146 dup2(nullfd, STDERR_FILENO);
147 }
148
149 if (nullfd > 2)
150 close(nullfd);
151
152 /* Father */
153 /* Pass TERM/HUP/INT/QUIT through to child, and accept CHLD */
154 signal(SIGTERM, sig_pass_to_chld);
155 signal(SIGHUP, sig_pass_to_chld);
156 signal(SIGINT, sig_pass_to_chld);
157 signal(SIGQUIT, sig_pass_to_chld);
158 signal(SIGCHLD, sig_got_chld);
159
160 setproctitle("[priv]");
161 close(socks[1]);
162
163 /* Close descriptors that only the unpriv child needs */
164 for (i = 0; i < nfunix; i++)
165 if (pfd[PFD_UNIX_0 + i].fd != -1)
166 close(pfd[PFD_UNIX_0 + i].fd);
167 if (pfd[PFD_INET].fd != -1)
168 close(pfd[PFD_INET].fd);
169 if (pfd[PFD_CTLSOCK].fd != -1)
170 close(pfd[PFD_CTLSOCK].fd);
171 if (pfd[PFD_CTLCONN].fd != -1)
172 close(pfd[PFD_CTLCONN].fd);
173 if (pfd[PFD_KLOG].fd)
174 close(pfd[PFD_KLOG].fd);
175
176 /* Save the config file specified by the child process */
177 if (strlcpy(config_file, conf, sizeof config_file) >= sizeof(config_file))
178 errx(1, "config_file truncation");
179
180 if (stat(config_file, &cf_info) < 0)
181 err(1, "stat config file failed");
182
183 /* Save whether or not the child can have access to gethostbyaddr(3) */
184 if (numeric > 0)
185 allow_gethostbyaddr = 0;
186 else
187 allow_gethostbyaddr = 1;
188
189 TAILQ_INIT(&lognames);
190 increase_state(STATE_CONFIG);
191 restart = 0;
192
193 while (cur_state < STATE_QUIT) {
194 if (may_read(socks[0], &cmd, sizeof(int)))
195 break;
196 switch (cmd) {
197 case PRIV_OPEN_TTY:
198 dprintf("[priv]: msg PRIV_OPEN_TTY received\n");
199 /* Expecting: length, path */
200 must_read(socks[0], &path_len, sizeof(size_t));
201 if (path_len == 0 || path_len > sizeof(path))
202 _exit(0);
203 must_read(socks[0], &path, path_len);
204 path[path_len - 1] = '\0';
205 check_tty_name(path, path_len);
206 fd = open(path, O_WRONLY|O_NONBLOCK, 0);
207 send_fd(socks[0], fd);
208 if (fd < 0)
209 warnx("priv_open_tty failed");
210 else
211 close(fd);
212 break;
213
214 case PRIV_OPEN_LOG:
215 dprintf("[priv]: msg PRIV_OPEN_LOG received\n");
216 /* Expecting: length, path */
217 must_read(socks[0], &path_len, sizeof(size_t));
218 if (path_len == 0 || path_len > sizeof(path))
219 _exit(0);
220 must_read(socks[0], &path, path_len);
221 path[path_len - 1] = '\0';
222 check_log_name(path, path_len);
223 fd = open(path, O_WRONLY|O_APPEND|O_NONBLOCK, 0);
224 send_fd(socks[0], fd);
225 if (fd < 0)
226 warnx("priv_open_log failed");
227 else
228 close(fd);
229 break;
230
231 case PRIV_OPEN_UTMP:
232 dprintf("[priv]: msg PRIV_OPEN_UTMP received\n");
233 fd = open(_PATH_UTMP, O_RDONLY|O_NONBLOCK, 0);
234 send_fd(socks[0], fd);
235 if (fd < 0)
236 warnx("priv_open_utmp failed");
237 else
238 close(fd);
239 break;
240
241 case PRIV_OPEN_CONFIG:
242 dprintf("[priv]: msg PRIV_OPEN_CONFIG received\n");
243 stat(config_file, &cf_info);
244 fd = open(config_file, O_RDONLY|O_NONBLOCK, 0);
245 send_fd(socks[0], fd);
246 if (fd < 0)
247 warnx("priv_open_config failed");
248 else
249 close(fd);
250 break;
251
252 case PRIV_CONFIG_MODIFIED:
253 dprintf("[priv]: msg PRIV_CONFIG_MODIFIED received\n");
254 if (stat(config_file, &cf_stat) < 0 ||
255 timespeccmp(&cf_info.st_mtimespec,
256 &cf_stat.st_mtimespec, <) ||
257 cf_info.st_size != cf_stat.st_size) {
258 dprintf("config file modified: restarting\n");
259 restart = result = 1;
260 must_write(socks[0], &result, sizeof(int));
261 } else {
262 result = 0;
263 must_write(socks[0], &result, sizeof(int));
264 }
265 break;
266
267 case PRIV_DONE_CONFIG_PARSE:
268 dprintf("[priv]: msg PRIV_DONE_CONFIG_PARSE received\n");
269 increase_state(STATE_RUNNING);
270 break;
271
272 case PRIV_GETHOSTSERV:
273 dprintf("[priv]: msg PRIV_GETHOSTSERV received\n");
274 /* Expecting: len, hostname, len, servname */
275 must_read(socks[0], &hostname_len, sizeof(size_t));
276 if (hostname_len == 0 || hostname_len > sizeof(hostname))
277 _exit(0);
278 must_read(socks[0], &hostname, hostname_len);
279 hostname[hostname_len - 1] = '\0';
280
281 must_read(socks[0], &servname_len, sizeof(size_t));
282 if (servname_len == 0 || servname_len > sizeof(servname))
283 _exit(0);
284 must_read(socks[0], &servname, servname_len);
285 servname[servname_len - 1] = '\0';
286
287 memset(&hints, '\0', sizeof(hints));
288 hints.ai_family = AF_INET;
289 hints.ai_socktype = SOCK_DGRAM;
290 i = getaddrinfo(hostname, servname, &hints, &res0);
291 if (i != 0 || res0 == NULL) {
292 addr_len = 0;
293 must_write(socks[0], &addr_len, sizeof(int));
294 } else {
295 /* Just send the first address */
296 i = res0->ai_addrlen;
297 must_write(socks[0], &i, sizeof(int));
298 must_write(socks[0], res0->ai_addr, i);
299 freeaddrinfo(res0);
300 }
301 break;
302
303 case PRIV_GETHOSTBYADDR:
304 dprintf("[priv]: msg PRIV_GETHOSTBYADDR received\n");
305 if (!allow_gethostbyaddr)
306 errx(1, "rejected attempt to gethostbyaddr");
307 /* Expecting: length, address, address family */
308 must_read(socks[0], &addr_len, sizeof(int));
309 if (addr_len <= 0 || addr_len > sizeof(hostname))
310 _exit(0);
311 must_read(socks[0], hostname, addr_len);
312 must_read(socks[0], &addr_af, sizeof(int));
313 hp = gethostbyaddr(hostname, addr_len, addr_af);
314 if (hp == NULL) {
315 addr_len = 0;
316 must_write(socks[0], &addr_len, sizeof(int));
317 } else {
318 addr_len = strlen(hp->h_name) + 1;
319 must_write(socks[0], &addr_len, sizeof(int));
320 must_write(socks[0], hp->h_name, addr_len);
321 }
322 break;
323 default:
324 errx(1, "unknown command %d", cmd);
325 break;
326 }
327 }
328
329 close(socks[0]);
330
331 /* Unlink any domain sockets that have been opened */
332 for (i = 0; i < nfunix; i++)
333 if (funixn[i] != NULL && pfd[PFD_UNIX_0 + i].fd != -1)
334 (void)unlink(funixn[i]);
335 if (ctlsock_path != NULL && pfd[PFD_CTLSOCK].fd != -1)
336 (void)unlink(ctlsock_path);
337
338 if (restart) {
339 int r;
340
341 wait(&r);
342 execvp(argv[0], argv);
343 }
344 _exit(1);
345 }
346
347 /* Check that the terminal device is ok, and if not, rewrite to /dev/null.
348 * Either /dev/console or /dev/tty* are allowed.
349 */
350 static void
check_tty_name(char * tty,size_t ttylen)351 check_tty_name(char *tty, size_t ttylen)
352 {
353 const char ttypre[] = "/dev/tty";
354 char *p;
355
356 /* Any path containing '..' is invalid. */
357 for (p = tty; *p && (p - tty) < ttylen; p++)
358 if (*p == '.' && *(p + 1) == '.')
359 goto bad_path;
360
361 if (strcmp(_PATH_CONSOLE, tty) && strncmp(tty, ttypre, strlen(ttypre)))
362 goto bad_path;
363 return;
364
365 bad_path:
366 warnx ("%s: invalid attempt to open %s: rewriting to /dev/null",
367 "check_tty_name", tty);
368 strlcpy(tty, "/dev/null", ttylen);
369 }
370
371 /* If we are in the initial configuration state, accept a logname and add
372 * it to the list of acceptable logfiles. Otherwise, check against this list
373 * and rewrite to /dev/null if it's a bad path.
374 */
375 static void
check_log_name(char * lognam,size_t loglen)376 check_log_name(char *lognam, size_t loglen)
377 {
378 struct logname *lg;
379 char *p;
380
381 /* Any path containing '..' is invalid. */
382 for (p = lognam; *p && (p - lognam) < loglen; p++)
383 if (*p == '.' && *(p + 1) == '.')
384 goto bad_path;
385
386 switch (cur_state) {
387 case STATE_CONFIG:
388 lg = malloc(sizeof(struct logname));
389 if (!lg)
390 err(1, "check_log_name() malloc");
391 strlcpy(lg->path, lognam, MAXPATHLEN);
392 TAILQ_INSERT_TAIL(&lognames, lg, next);
393 break;
394 case STATE_RUNNING:
395 TAILQ_FOREACH(lg, &lognames, next)
396 if (!strcmp(lg->path, lognam))
397 return;
398 goto bad_path;
399 break;
400 default:
401 /* Any other state should just refuse the request */
402 goto bad_path;
403 break;
404 }
405 return;
406
407 bad_path:
408 warnx("%s: invalid attempt to open %s: rewriting to /dev/null",
409 "check_log_name", lognam);
410 strlcpy(lognam, "/dev/null", loglen);
411 }
412
413 /* Crank our state into less permissive modes */
414 static void
increase_state(int state)415 increase_state(int state)
416 {
417 if (state <= cur_state)
418 errx(1, "attempt to decrease or match current state");
419 if (state < STATE_INIT || state > STATE_QUIT)
420 errx(1, "attempt to switch to invalid state");
421 cur_state = state;
422 }
423
424 /* Open console or a terminal device for writing */
425 int
priv_open_tty(const char * tty)426 priv_open_tty(const char *tty)
427 {
428 char path[MAXPATHLEN];
429 int cmd, fd;
430 size_t path_len;
431
432 if (priv_fd < 0)
433 errx(1, "%s: called from privileged portion", "priv_open_tty");
434
435 if (strlcpy(path, tty, sizeof path) >= sizeof(path))
436 return -1;
437 path_len = strlen(path) + 1;
438
439 cmd = PRIV_OPEN_TTY;
440 must_write(priv_fd, &cmd, sizeof(int));
441 must_write(priv_fd, &path_len, sizeof(size_t));
442 must_write(priv_fd, path, path_len);
443 fd = receive_fd(priv_fd);
444 return fd;
445 }
446
447 /* Open log-file */
448 int
priv_open_log(const char * lognam)449 priv_open_log(const char *lognam)
450 {
451 char path[MAXPATHLEN];
452 int cmd, fd;
453 size_t path_len;
454
455 if (priv_fd < 0)
456 errx(1, "%s: called from privileged child", "priv_open_log");
457
458 if (strlcpy(path, lognam, sizeof path) >= sizeof(path))
459 return -1;
460 path_len = strlen(path) + 1;
461
462 cmd = PRIV_OPEN_LOG;
463 must_write(priv_fd, &cmd, sizeof(int));
464 must_write(priv_fd, &path_len, sizeof(size_t));
465 must_write(priv_fd, path, path_len);
466 fd = receive_fd(priv_fd);
467 return fd;
468 }
469
470 /* Open utmp for reading */
471 FILE *
priv_open_utmp(void)472 priv_open_utmp(void)
473 {
474 int cmd, fd;
475 FILE *fp;
476
477 if (priv_fd < 0)
478 errx(1, "%s: called from privileged portion", "priv_open_utmp");
479
480 cmd = PRIV_OPEN_UTMP;
481 must_write(priv_fd, &cmd, sizeof(int));
482 fd = receive_fd(priv_fd);
483 if (fd < 0)
484 return NULL;
485
486 fp = fdopen(fd, "r");
487 if (!fp) {
488 warn("priv_open_utmp: fdopen() failed");
489 close(fd);
490 return NULL;
491 }
492
493 return fp;
494 }
495
496 /* Open syslog config file for reading */
497 FILE *
priv_open_config(void)498 priv_open_config(void)
499 {
500 int cmd, fd;
501 FILE *fp;
502
503 if (priv_fd < 0)
504 errx(1, "%s: called from privileged portion", "priv_open_config");
505
506 cmd = PRIV_OPEN_CONFIG;
507 must_write(priv_fd, &cmd, sizeof(int));
508 fd = receive_fd(priv_fd);
509 if (fd < 0)
510 return NULL;
511
512 fp = fdopen(fd, "r");
513 if (!fp) {
514 warn("priv_open_config: fdopen() failed");
515 close(fd);
516 return NULL;
517 }
518
519 return fp;
520 }
521
522 /* Ask if config file has been modified since last attempt to read it */
523 int
priv_config_modified(void)524 priv_config_modified(void)
525 {
526 int cmd, res;
527
528 if (priv_fd < 0)
529 errx(1, "%s: called from privileged portion",
530 "priv_config_modified");
531
532 cmd = PRIV_CONFIG_MODIFIED;
533 must_write(priv_fd, &cmd, sizeof(int));
534
535 /* Expect back integer signalling 1 for modification */
536 must_read(priv_fd, &res, sizeof(int));
537 return res;
538 }
539
540 /* Child can signal that its initial parsing is done, so that parent
541 * can revoke further logfile permissions. This call only works once. */
542 void
priv_config_parse_done(void)543 priv_config_parse_done(void)
544 {
545 int cmd;
546
547 if (priv_fd < 0)
548 errx(1, "%s: called from privileged portion",
549 "priv_config_parse_done");
550
551 cmd = PRIV_DONE_CONFIG_PARSE;
552 must_write(priv_fd, &cmd, sizeof(int));
553 }
554
555 /* Name/service to address translation. Response is placed into addr, and
556 * the length is returned (zero on error) */
557 int
priv_gethostserv(char * host,char * serv,struct sockaddr * addr,size_t addr_len)558 priv_gethostserv(char *host, char *serv, struct sockaddr *addr,
559 size_t addr_len)
560 {
561 char hostcpy[MAXHOSTNAMELEN], servcpy[MAXHOSTNAMELEN];
562 int cmd, ret_len;
563 size_t hostname_len, servname_len;
564
565 if (priv_fd < 0)
566 errx(1, "%s: called from privileged portion", "priv_gethostserv");
567
568 if (strlcpy(hostcpy, host, sizeof hostcpy) >= sizeof(hostcpy))
569 errx(1, "%s: overflow attempt in hostname", "priv_gethostserv");
570 hostname_len = strlen(hostcpy) + 1;
571 if (strlcpy(servcpy, serv, sizeof servcpy) >= sizeof(servcpy))
572 errx(1, "%s: overflow attempt in servname", "priv_gethostserv");
573 servname_len = strlen(servcpy) + 1;
574
575 cmd = PRIV_GETHOSTSERV;
576 must_write(priv_fd, &cmd, sizeof(int));
577 must_write(priv_fd, &hostname_len, sizeof(size_t));
578 must_write(priv_fd, hostcpy, hostname_len);
579 must_write(priv_fd, &servname_len, sizeof(size_t));
580 must_write(priv_fd, servcpy, servname_len);
581
582 /* Expect back an integer size, and then a string of that length */
583 must_read(priv_fd, &ret_len, sizeof(int));
584
585 /* Check there was no error (indicated by a return of 0) */
586 if (!ret_len)
587 return 0;
588
589 /* Make sure we aren't overflowing the passed in buffer */
590 if (addr_len < ret_len)
591 errx(1, "%s: overflow attempt in return", "priv_gethostserv");
592
593 /* Read the resolved address and make sure we got all of it */
594 memset(addr, '\0', addr_len);
595 must_read(priv_fd, addr, ret_len);
596
597 return ret_len;
598 }
599
600 /* Reverse address resolution; response is placed into res, and length of
601 * response is returned (zero on error) */
602 int
priv_gethostbyaddr(char * addr,int addr_len,int af,char * res,size_t res_len)603 priv_gethostbyaddr(char *addr, int addr_len, int af, char *res, size_t res_len)
604 {
605 int cmd, ret_len;
606
607 if (priv_fd < 0)
608 errx(1, "%s called from privileged portion", "priv_gethostbyaddr");
609
610 cmd = PRIV_GETHOSTBYADDR;
611 must_write(priv_fd, &cmd, sizeof(int));
612 must_write(priv_fd, &addr_len, sizeof(int));
613 must_write(priv_fd, addr, addr_len);
614 must_write(priv_fd, &af, sizeof(int));
615
616 /* Expect back an integer size, and then a string of that length */
617 must_read(priv_fd, &ret_len, sizeof(int));
618
619 /* Check there was no error (indicated by a return of 0) */
620 if (!ret_len)
621 return 0;
622
623 /* Check we don't overflow the passed in buffer */
624 if (res_len < ret_len)
625 errx(1, "%s: overflow attempt in return", "priv_gethostbyaddr");
626
627 /* Read the resolved hostname */
628 must_read(priv_fd, res, ret_len);
629 return ret_len;
630 }
631
632 /* Pass the signal through to child */
633 static void
sig_pass_to_chld(int sig)634 sig_pass_to_chld(int sig)
635 {
636 int oerrno = errno;
637
638 if (child_pid != -1)
639 kill(child_pid, sig);
640 errno = oerrno;
641 }
642
643 /* When child dies, move into the shutdown state */
644 /* ARGSUSED */
645 static void
sig_got_chld(int sig)646 sig_got_chld(int sig)
647 {
648 if (cur_state < STATE_QUIT)
649 cur_state = STATE_QUIT;
650 }
651
652 /* Read all data or return 1 for error. */
653 static int
may_read(int fd,void * buf,size_t n)654 may_read(int fd, void *buf, size_t n)
655 {
656 char *s = buf;
657 ssize_t res, pos = 0;
658
659 while (n > pos) {
660 res = read(fd, s + pos, n - pos);
661 switch (res) {
662 case -1:
663 if (errno == EINTR || errno == EAGAIN)
664 continue;
665 case 0:
666 return (1);
667 default:
668 pos += res;
669 }
670 }
671 return (0);
672 }
673
674 /* Read data with the assertion that it all must come through, or
675 * else abort the process. Based on atomicio() from openssh. */
676 static void
must_read(int fd,void * buf,size_t n)677 must_read(int fd, void *buf, size_t n)
678 {
679 char *s = buf;
680 ssize_t res, pos = 0;
681
682 while (n > pos) {
683 res = read(fd, s + pos, n - pos);
684 switch (res) {
685 case -1:
686 if (errno == EINTR || errno == EAGAIN)
687 continue;
688 case 0:
689 _exit(0);
690 default:
691 pos += res;
692 }
693 }
694 }
695
696 /* Write data with the assertion that it all has to be written, or
697 * else abort the process. Based on atomicio() from openssh. */
698 static void
must_write(int fd,void * buf,size_t n)699 must_write(int fd, void *buf, size_t n)
700 {
701 char *s = buf;
702 ssize_t res, pos = 0;
703
704 while (n > pos) {
705 res = write(fd, s + pos, n - pos);
706 switch (res) {
707 case -1:
708 if (errno == EINTR || errno == EAGAIN)
709 continue;
710 case 0:
711 _exit(0);
712 default:
713 pos += res;
714 }
715 }
716 }
717