Annotation of src/external/bsd/dhcpcd/dist/src/privsep-root.c, Revision 1.1.1.1.2.2
1.1.1.1.2.2! martin 1: /* SPDX-License-Identifier: BSD-2-Clause */
! 2: /*
! 3: * Privilege Separation for dhcpcd, privileged actioneer
! 4: * Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
! 5: * All rights reserved
! 6:
! 7: * Redistribution and use in source and binary forms, with or without
! 8: * modification, are permitted provided that the following conditions
! 9: * are met:
! 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
! 12: * 2. Redistributions in binary form must reproduce the above copyright
! 13: * notice, this list of conditions and the following disclaimer in the
! 14: * documentation and/or other materials provided with the distribution.
! 15: *
! 16: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
! 17: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 18: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 19: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
! 20: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 21: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 22: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 23: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 24: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 25: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 26: * SUCH DAMAGE.
! 27: */
! 28:
! 29: #include <sys/ioctl.h>
! 30: #include <sys/socket.h>
! 31: #include <sys/stat.h>
! 32: #include <sys/time.h>
! 33: #include <sys/types.h>
! 34: #include <sys/wait.h>
! 35:
! 36: #include <assert.h>
! 37: #include <errno.h>
! 38: #include <fcntl.h>
! 39: #include <pwd.h>
! 40: #include <signal.h>
! 41: #include <stdlib.h>
! 42: #include <string.h>
! 43: #include <unistd.h>
! 44:
! 45: #include "common.h"
! 46: #include "dhcpcd.h"
! 47: #include "eloop.h"
! 48: #include "if.h"
! 49: #include "logerr.h"
! 50: #include "privsep.h"
! 51: #include "script.h"
! 52:
! 53: __CTASSERT(sizeof(ioctl_request_t) <= sizeof(unsigned long));
! 54:
! 55: struct psr_error
! 56: {
! 57: ssize_t psr_result;
! 58: int psr_errno;
! 59: char psr_pad[sizeof(ssize_t) - sizeof(int)];
! 60: };
! 61:
! 62: struct psr_ctx {
! 63: struct dhcpcd_ctx *psr_ctx;
! 64: struct psr_error psr_error;
! 65: };
! 66:
! 67: static void
! 68: ps_root_readerrorsig(__unused int sig, void *arg)
! 69: {
! 70: struct dhcpcd_ctx *ctx = arg;
! 71:
! 72: eloop_exit(ctx->ps_eloop, EXIT_FAILURE);
! 73: }
! 74:
! 75: static void
! 76: ps_root_readerrorcb(void *arg)
! 77: {
! 78: struct psr_ctx *psr_ctx = arg;
! 79: struct dhcpcd_ctx *ctx = psr_ctx->psr_ctx;
! 80: struct psr_error *psr_error = &psr_ctx->psr_error;
! 81: ssize_t len;
! 82: int exit_code = EXIT_FAILURE;
! 83:
! 84: len = read(ctx->ps_root_fd, psr_error, sizeof(*psr_error));
! 85: if (len == 0 || len == -1) {
! 86: logerr(__func__);
! 87: psr_error->psr_result = -1;
! 88: psr_error->psr_errno = errno;
! 89: } else if ((size_t)len < sizeof(*psr_error)) {
! 90: logerrx("%s: psr_error truncated", __func__);
! 91: psr_error->psr_result = -1;
! 92: psr_error->psr_errno = EINVAL;
! 93: } else
! 94: exit_code = EXIT_SUCCESS;
! 95:
! 96: eloop_exit(ctx->ps_eloop, exit_code);
! 97: }
! 98:
! 99: ssize_t
! 100: ps_root_readerror(struct dhcpcd_ctx *ctx)
! 101: {
! 102: struct psr_ctx psr_ctx = { .psr_ctx = ctx };
! 103:
! 104: if (eloop_event_add(ctx->ps_eloop, ctx->ps_root_fd,
! 105: ps_root_readerrorcb, &psr_ctx) == -1)
! 106: {
! 107: logerr(__func__);
! 108: return -1;
! 109: }
! 110:
! 111: eloop_start(ctx->ps_eloop, &ctx->sigset);
! 112:
! 113: errno = psr_ctx.psr_error.psr_errno;
! 114: return psr_ctx.psr_error.psr_result;
! 115: }
! 116:
! 117: static ssize_t
! 118: ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result)
! 119: {
! 120: struct psr_error psr = {
! 121: .psr_result = result,
! 122: .psr_errno = errno,
! 123: };
! 124:
! 125: #ifdef PRIVSEP_DEBUG
! 126: logdebugx("%s: result %zd errno %d", __func__, result, errno);
! 127: #endif
! 128:
! 129: return write(ctx->ps_root_fd, &psr, sizeof(psr));
! 130: }
! 131:
! 132: static ssize_t
! 133: ps_root_doioctl(unsigned long req, void *data, size_t len)
! 134: {
! 135: int s, err;
! 136:
! 137: s = socket(PF_INET, SOCK_DGRAM, 0);
! 138: if (s != -1)
! 139: #ifdef IOCTL_REQUEST_TYPE
! 140: {
! 141: ioctl_request_t reqt;
! 142:
! 143: memcpy(&reqt, &req, sizeof(reqt));
! 144: err = ioctl(s, reqt, data, len);
! 145: }
! 146: #else
! 147: err = ioctl(s, req, data, len);
! 148: #endif
! 149: else
! 150: err = -1;
! 151: if (s != -1)
! 152: close(s);
! 153: return err;
! 154: }
! 155:
! 156: static ssize_t
! 157: ps_root_run_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
! 158: {
! 159: const char *envbuf = data;
! 160: char * const argv[] = { UNCONST(data), NULL };
! 161: pid_t pid;
! 162: int status;
! 163:
! 164: #ifdef PRIVSEP_DEBUG
! 165: logdebugx("%s: IN %zu", __func__, len);
! 166: #endif
! 167:
! 168: if (len == 0)
! 169: return 0;
! 170:
! 171: /* Script is the first one, find the environment buffer. */
! 172: while (*envbuf != '\0') {
! 173: if (len == 0)
! 174: return EINVAL;
! 175: envbuf++;
! 176: len--;
! 177: }
! 178:
! 179: if (len != 0) {
! 180: envbuf++;
! 181: len--;
! 182: }
! 183:
! 184: #ifdef PRIVSEP_DEBUG
! 185: logdebugx("%s: run script: %s", __func__, argv[0]);
! 186: #endif
! 187:
! 188: if (script_buftoenv(ctx, UNCONST(envbuf), len) == NULL)
! 189: return -1;
! 190:
! 191: pid = script_exec(ctx, argv, ctx->script_env);
! 192: if (pid == -1)
! 193: return -1;
! 194: /* Wait for the script to finish */
! 195: while (waitpid(pid, &status, 0) == -1) {
! 196: if (errno != EINTR) {
! 197: logerr(__func__);
! 198: status = 0;
! 199: break;
! 200: }
! 201: }
! 202: return status;
! 203: }
! 204:
! 205: #if defined(__linux__) && !defined(st_mtimespec)
! 206: #define st_atimespec st_atim
! 207: #define st_mtimespec st_mtim
! 208: #endif
! 209: ssize_t
! 210: ps_root_docopychroot(struct dhcpcd_ctx *ctx, const char *file)
! 211: {
! 212:
! 213: char path[PATH_MAX], buf[BUFSIZ], *slash;
! 214: struct stat from_sb, to_sb;
! 215: int from_fd, to_fd;
! 216: ssize_t rcount, wcount, total;
! 217: #if defined(BSD) || defined(__linux__)
! 218: struct timespec ts[2];
! 219: #else
! 220: struct timeval tv[2];
! 221: #endif
! 222:
! 223: if (snprintf(path, sizeof(path), "%s/%s",
! 224: ctx->ps_user->pw_dir, file) == -1)
! 225: return -1;
! 226: if (stat(file, &from_sb) == -1)
! 227: return -1;
! 228: if (stat(path, &to_sb) == 0) {
! 229: #if defined(BSD) || defined(__linux__)
! 230: if (from_sb.st_mtimespec.tv_sec == to_sb.st_mtimespec.tv_sec &&
! 231: from_sb.st_mtimespec.tv_nsec == to_sb.st_mtimespec.tv_nsec)
! 232: return 0;
! 233: #else
! 234: if (from_sb.st_mtime == to_sb.st_mtime)
! 235: return 0;
! 236: #endif
! 237: } else {
! 238: /* Ensure directory exists */
! 239: slash = strrchr(path, '/');
! 240: if (slash != NULL) {
! 241: *slash = '\0';
! 242: ps_mkdir(path);
! 243: *slash = '/';
! 244: }
! 245: }
! 246:
! 247: if (unlink(path) == -1 && errno != ENOENT)
! 248: return -1;
! 249: if ((from_fd = open(file, O_RDONLY, 0)) == -1)
! 250: return -1;
! 251: if ((to_fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0555)) == -1)
! 252: return -1;
! 253:
! 254: total = 0;
! 255: while ((rcount = read(from_fd, buf, sizeof(buf))) > 0) {
! 256: wcount = write(to_fd, buf, (size_t)rcount);
! 257: if (wcount != rcount) {
! 258: total = -1;
! 259: break;
! 260: }
! 261: total += wcount;
! 262: }
! 263:
! 264: #if defined(BSD) || defined(__linux__)
! 265: ts[0] = from_sb.st_atimespec;
! 266: ts[1] = from_sb.st_mtimespec;
! 267: if (futimens(to_fd, ts) == -1)
! 268: total = -1;
! 269: #else
! 270: tv[0].tv_sec = from_sb.st_atime;
! 271: tv[0].tv_usec = 0;
! 272: tv[1].tv_sec = from_sb.st_mtime;
! 273: tv[1].tv_usec = 0;
! 274: if (futimes(to_fd, tv) == -1)
! 275: total = -1;
! 276: #endif
! 277: close(from_fd);
! 278: close(to_fd);
! 279:
! 280: return total;
! 281: }
! 282:
! 283: static ssize_t
! 284: ps_root_dofileop(struct dhcpcd_ctx *ctx, void *data, size_t len, uint8_t op)
! 285: {
! 286: char *path = data;
! 287: size_t plen;
! 288:
! 289: if (len < sizeof(plen)) {
! 290: errno = EINVAL;
! 291: return -1;
! 292: }
! 293:
! 294: memcpy(&plen, path, sizeof(plen));
! 295: path += sizeof(plen);
! 296: if (sizeof(plen) + plen > len) {
! 297: errno = EINVAL;
! 298: return -1;
! 299: }
! 300:
! 301: switch(op) {
! 302: case PS_COPY:
! 303: return ps_root_docopychroot(ctx, path);
! 304: case PS_UNLINK:
! 305: return (ssize_t)unlink(path);
! 306: default:
! 307: errno = ENOTSUP;
! 308: return -1;
! 309: }
! 310: }
! 311:
! 312: static ssize_t
! 313: ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
! 314: {
! 315: struct dhcpcd_ctx *ctx = arg;
! 316: uint8_t cmd;
! 317: struct ps_process *psp;
! 318: struct iovec *iov = msg->msg_iov;
! 319: void *data = iov->iov_base;
! 320: size_t len = iov->iov_len;
! 321: ssize_t err;
! 322:
! 323: cmd = (uint8_t)(psm->ps_cmd & ~(PS_START | PS_STOP | PS_DELETE));
! 324: psp = ps_findprocess(ctx, &psm->ps_id);
! 325:
! 326: #ifdef PRIVSEP_DEBUG
! 327: logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
! 328: #endif
! 329:
! 330: if ((!(psm->ps_cmd & PS_START) || cmd == PS_BPF_ARP_ADDR) &&
! 331: psp != NULL)
! 332: {
! 333: if (psm->ps_cmd & PS_STOP) {
! 334: int ret = ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd);
! 335:
! 336: ps_freeprocess(psp);
! 337: return ret;
! 338: }
! 339: return ps_sendpsmmsg(ctx, psp->psp_fd, psm, msg);
! 340: }
! 341:
! 342: if (psm->ps_cmd & (PS_STOP | PS_DELETE) && psp == NULL)
! 343: return 0;
! 344:
! 345: /* All these should just be PS_START */
! 346: switch (cmd) {
! 347: #ifdef INET
! 348: #ifdef ARP
! 349: case PS_BPF_ARP: /* FALLTHROUGH */
! 350: #endif
! 351: case PS_BPF_BOOTP:
! 352: return ps_bpf_cmd(ctx, psm, msg);
! 353: #endif
! 354: #ifdef INET
! 355: case PS_BOOTP:
! 356: return ps_inet_cmd(ctx, psm, msg);
! 357: #endif
! 358: #ifdef INET6
! 359: #ifdef DHCP6
! 360: case PS_DHCP6: /* FALLTHROUGH */
! 361: #endif
! 362: case PS_ND:
! 363: return ps_inet_cmd(ctx, psm, msg);
! 364: #endif
! 365: default:
! 366: break;
! 367: }
! 368:
! 369: assert(msg->msg_iovlen == 1);
! 370:
! 371: /* Reset errno */
! 372: errno = 0;
! 373:
! 374: switch (psm->ps_cmd) {
! 375: case PS_IOCTL:
! 376: err = ps_root_doioctl(psm->ps_flags, data, len);
! 377: break;
! 378: case PS_SCRIPT:
! 379: err = ps_root_run_script(ctx, data, len);
! 380: break;
! 381: case PS_COPY: /* FALLTHROUGH */
! 382: case PS_UNLINK:
! 383: err = ps_root_dofileop(ctx, data, len, psm->ps_cmd);
! 384: break;
! 385: default:
! 386: err = ps_root_os(psm, msg);
! 387: break;
! 388: }
! 389:
! 390: return ps_root_writeerror(ctx, err);
! 391: }
! 392:
! 393: /* Receive from state engine, do an action. */
! 394: static void
! 395: ps_root_recvmsg(void *arg)
! 396: {
! 397: struct dhcpcd_ctx *ctx = arg;
! 398:
! 399: if (ps_recvpsmsg(ctx, ctx->ps_root_fd, ps_root_recvmsgcb, ctx) == -1 &&
! 400: errno != ECONNRESET)
! 401: logerr(__func__);
! 402: }
! 403:
! 404: static int
! 405: ps_root_startcb(void *arg)
! 406: {
! 407: struct dhcpcd_ctx *ctx = arg;
! 408:
! 409: setproctitle("[privileged actioneer]");
! 410: ctx->ps_root_pid = getpid();
! 411: return 0;
! 412: }
! 413:
! 414: static void
! 415: ps_root_signalcb(int sig, void *arg)
! 416: {
! 417: struct dhcpcd_ctx *ctx = arg;
! 418:
! 419: /* Ignore SIGINT, respect PS_STOP command or SIGTERM. */
! 420: if (sig == SIGINT)
! 421: return;
! 422:
! 423: logerrx("process %d unexpectedly terminating on signal %d",
! 424: getpid(), sig);
! 425: if (ctx->ps_root_pid == getpid()) {
! 426: shutdown(ctx->ps_root_fd, SHUT_RDWR);
! 427: shutdown(ctx->ps_data_fd, SHUT_RDWR);
! 428: }
! 429: eloop_exit(ctx->eloop, sig == SIGTERM ? EXIT_SUCCESS : EXIT_FAILURE);
! 430: }
! 431:
! 432: static ssize_t
! 433: ps_root_dispatchcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
! 434: {
! 435: struct dhcpcd_ctx *ctx = arg;
! 436: ssize_t err;
! 437:
! 438: err = ps_bpf_dispatch(ctx, psm, msg);
! 439: if (err == -1 && errno == ENOTSUP)
! 440: err = ps_inet_dispatch(ctx, psm, msg);
! 441: return err;
! 442: }
! 443:
! 444: static void
! 445: ps_root_dispatch(void *arg)
! 446: {
! 447: struct dhcpcd_ctx *ctx = arg;
! 448:
! 449: if (ps_recvpsmsg(ctx, ctx->ps_data_fd, ps_root_dispatchcb, ctx) == -1)
! 450: logerr(__func__);
! 451: }
! 452:
! 453: pid_t
! 454: ps_root_start(struct dhcpcd_ctx *ctx)
! 455: {
! 456: int fd[2];
! 457: pid_t pid;
! 458:
! 459: #define SOCK_CXNB SOCK_CLOEXEC | SOCK_NONBLOCK
! 460: if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1)
! 461: return -1;
! 462:
! 463: pid = ps_dostart(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd,
! 464: ps_root_recvmsg, NULL, ctx,
! 465: ps_root_startcb, ps_root_signalcb, 0);
! 466:
! 467: if (pid == 0) {
! 468: ctx->ps_data_fd = fd[1];
! 469: close(fd[0]);
! 470: return 0;
! 471: } else if (pid == -1)
! 472: return -1;
! 473:
! 474: ctx->ps_data_fd = fd[0];
! 475: close(fd[1]);
! 476: if (eloop_event_add(ctx->eloop, ctx->ps_data_fd,
! 477: ps_root_dispatch, ctx) == -1)
! 478: logerr(__func__);
! 479:
! 480: if ((ctx->ps_eloop = eloop_new()) == NULL) {
! 481: logerr(__func__);
! 482: return -1;
! 483: }
! 484:
! 485: if (eloop_signal_set_cb(ctx->ps_eloop,
! 486: dhcpcd_signals, dhcpcd_signals_len,
! 487: ps_root_readerrorsig, ctx) == -1)
! 488: {
! 489: logerr(__func__);
! 490: return -1;
! 491: }
! 492: return pid;
! 493: }
! 494:
! 495: int
! 496: ps_root_stop(struct dhcpcd_ctx *ctx)
! 497: {
! 498:
! 499: return ps_dostop(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd);
! 500: }
! 501:
! 502: ssize_t
! 503: ps_root_script(const struct interface *ifp, const void *data, size_t len)
! 504: {
! 505: char buf[PS_BUFLEN], *p = buf;
! 506: size_t blen = PS_BUFLEN, slen = strlen(ifp->options->script) + 1;
! 507:
! 508: #ifdef PRIVSEP_DEBUG
! 509: logdebugx("%s: sending script: %zu %s len %zu",
! 510: __func__, slen, ifp->options->script, len);
! 511: #endif
! 512:
! 513: if (slen > blen) {
! 514: errno = ENOBUFS;
! 515: return -1;
! 516: }
! 517: memcpy(p, ifp->options->script, slen);
! 518: p += slen;
! 519: blen -= slen;
! 520:
! 521: if (len > blen) {
! 522: errno = ENOBUFS;
! 523: return -1;
! 524: }
! 525: memcpy(p, data, len);
! 526:
! 527: #ifdef PRIVSEP_DEBUG
! 528: logdebugx("%s: sending script data: %zu", __func__, slen + len);
! 529: #endif
! 530:
! 531: if (ps_sendcmd(ifp->ctx, ifp->ctx->ps_root_fd, PS_SCRIPT, 0,
! 532: buf, slen + len) == -1)
! 533: return -1;
! 534:
! 535: return ps_root_readerror(ifp->ctx);
! 536: }
! 537:
! 538: ssize_t
! 539: ps_root_ioctl(struct dhcpcd_ctx *ctx, ioctl_request_t req, void *data,
! 540: size_t len)
! 541: {
! 542: #ifdef IOCTL_REQUEST_TYPE
! 543: unsigned long ulreq = 0;
! 544:
! 545: memcpy(&ulreq, &req, sizeof(req));
! 546: if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL, ulreq, data, len) == -1)
! 547: return -1;
! 548: #else
! 549: if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL, req, data, len) == -1)
! 550: return -1;
! 551: #endif
! 552: return ps_root_readerror(ctx);
! 553: }
! 554:
! 555: static ssize_t
! 556: ps_root_fileop(struct dhcpcd_ctx *ctx, const char *path, uint8_t op)
! 557: {
! 558: char buf[PATH_MAX], *p = buf;
! 559: size_t plen = strlen(path) + 1;
! 560: size_t len = sizeof(plen) + plen;
! 561:
! 562: if (len > sizeof(buf)) {
! 563: errno = ENOBUFS;
! 564: return -1;
! 565: }
! 566:
! 567: memcpy(p, &plen, sizeof(plen));
! 568: p += sizeof(plen);
! 569: memcpy(p, path, plen);
! 570:
! 571: if (ps_sendcmd(ctx, ctx->ps_root_fd, op, 0, buf, len) == -1)
! 572: return -1;
! 573: return ps_root_readerror(ctx);
! 574: }
! 575:
! 576:
! 577: ssize_t
! 578: ps_root_copychroot(struct dhcpcd_ctx *ctx, const char *path)
! 579: {
! 580:
! 581: return ps_root_fileop(ctx, path, PS_COPY);
! 582: }
! 583:
! 584: ssize_t
! 585: ps_root_unlink(struct dhcpcd_ctx *ctx, const char *path)
! 586: {
! 587:
! 588: return ps_root_fileop(ctx, path, PS_UNLINK);
! 589: }
CVSweb <webmaster@jp.NetBSD.org>