1 /* SPDX-License-Identifier: BSD-2-Clause */ 2 /* 3 * Privilege Separation for dhcpcd, network proxy 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/socket.h> 30 #include <sys/types.h> 31 32 #include <assert.h> 33 #include <errno.h> 34 #include <signal.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include "arp.h" 40 #include "bpf.h" 41 #include "dhcp.h" 42 #include "dhcp6.h" 43 #include "eloop.h" 44 #include "ipv6nd.h" 45 #include "logerr.h" 46 #include "privsep.h" 47 48 #ifdef INET 49 static void 50 ps_inet_recvbootp(void *arg) 51 { 52 struct dhcpcd_ctx *ctx = arg; 53 54 if (ps_recvmsg(ctx, ctx->udp_fd, PS_BOOTP, ctx->ps_inet_fd) == -1) 55 logerr(__func__); 56 } 57 #endif 58 59 #ifdef INET6 60 static void 61 ps_inet_recvra(void *arg) 62 { 63 #ifdef __sun 64 struct interface *ifp = arg; 65 struct rs_state *state = RS_STATE(ifp); 66 struct dhcpcd_ctx *ctx = ifp->ctx; 67 68 if (ps_recvmsg(ctx, state->nd_fd, PS_ND, ctx->ps_inet_fd) == -1) 69 logerr(__func__); 70 #else 71 struct dhcpcd_ctx *ctx = arg; 72 73 if (ps_recvmsg(ctx, ctx->nd_fd, PS_ND, ctx->ps_inet_fd) == -1) 74 logerr(__func__); 75 #endif 76 } 77 #endif 78 79 #ifdef DHCP6 80 static void 81 ps_inet_recvdhcp6(void *arg) 82 { 83 struct dhcpcd_ctx *ctx = arg; 84 85 if (ps_recvmsg(ctx, ctx->dhcp6_fd, PS_DHCP6, ctx->ps_inet_fd) == -1) 86 logerr(__func__); 87 } 88 #endif 89 90 static int 91 ps_inet_startcb(void *arg) 92 { 93 struct dhcpcd_ctx *ctx = arg; 94 int ret = 0; 95 96 setproctitle("[network proxy]"); 97 98 /* This end is the main engine, so it's useless for us. */ 99 close(ctx->ps_data_fd); 100 ctx->ps_data_fd = -1; 101 102 errno = 0; 103 104 #ifdef INET 105 if ((ctx->options & (DHCPCD_IPV4 | DHCPCD_MASTER)) == 106 (DHCPCD_IPV4 | DHCPCD_MASTER)) 107 { 108 ctx->udp_fd = dhcp_openudp(NULL); 109 if (ctx->udp_fd == -1) 110 logerr("%s: dhcp_open", __func__); 111 else if (eloop_event_add(ctx->eloop, ctx->udp_fd, 112 ps_inet_recvbootp, ctx) == -1) 113 { 114 logerr("%s: eloop_event_add DHCP", __func__); 115 close(ctx->udp_fd); 116 ctx->udp_fd = -1; 117 } else 118 ret++; 119 } 120 #endif 121 #if defined(INET6) && !defined(__sun) 122 if (ctx->options & DHCPCD_IPV6) { 123 if (ipv6nd_open(ctx) == -1) 124 logerr("%s: ipv6nd_open", __func__); 125 else if (eloop_event_add(ctx->eloop, ctx->nd_fd, 126 ps_inet_recvra, ctx) == -1) 127 { 128 logerr("%s: eloop_event_add RA", __func__); 129 close(ctx->nd_fd); 130 ctx->nd_fd = -1; 131 } else 132 ret++; 133 } 134 #endif 135 #ifdef DHCP6 136 if ((ctx->options & (DHCPCD_DHCP6 | DHCPCD_MASTER)) == 137 (DHCPCD_DHCP6 | DHCPCD_MASTER)) 138 { 139 ctx->dhcp6_fd = dhcp6_openudp(0, NULL); 140 if (ctx->dhcp6_fd == -1) 141 logerr("%s: dhcp6_open", __func__); 142 else if (eloop_event_add(ctx->eloop, ctx->dhcp6_fd, 143 ps_inet_recvdhcp6, ctx) == -1) 144 { 145 logerr("%s: eloop_event_add DHCP6", __func__); 146 close(ctx->dhcp6_fd); 147 ctx->dhcp6_fd = -1; 148 } else 149 ret++; 150 } 151 #endif 152 153 if (ret == 0 && errno == 0) { 154 errno = ENXIO; 155 return -1; 156 } 157 return ret; 158 } 159 160 static ssize_t 161 ps_inet_recvmsg_cb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) 162 { 163 struct dhcpcd_ctx *ctx = arg; 164 struct ps_process *psp; 165 int s; 166 167 psp = ps_findprocess(ctx, &psm->ps_id); 168 if (psp != NULL) { 169 s = psp->psp_work_fd; 170 logerrx("psp found fd %d", s); 171 goto dosend; 172 } 173 174 switch (psm->ps_cmd) { 175 #ifdef INET 176 case PS_BOOTP: 177 s = ctx->udp_fd; 178 break; 179 #endif 180 #if defined(INET6) && !defined(__sun) 181 case PS_ND: 182 s = ctx->nd_fd; 183 break; 184 #endif 185 #ifdef DHCP6 186 case PS_DHCP6: 187 s = ctx->dhcp6_fd; 188 break; 189 #endif 190 default: 191 errno = EINVAL; 192 return -1; 193 } 194 195 dosend: 196 197 return sendmsg(s, msg, 0); 198 } 199 200 /* Receive from state engine, send message on wire. */ 201 static void 202 ps_inet_recvmsg(void *arg) 203 { 204 struct dhcpcd_ctx *ctx = arg; 205 206 if (ps_recvpsmsg(ctx, ctx->ps_inet_fd, ps_inet_recvmsg_cb, ctx) == -1) 207 logerr(__func__); 208 } 209 210 static void 211 ps_inet_signalcb(int sig, void *arg) 212 { 213 struct dhcpcd_ctx *ctx = arg; 214 215 /* Ignore SIGINT, respect PS_STOP command or SIGTERM. */ 216 if (sig == SIGINT) 217 return; 218 219 shutdown(ctx->ps_inet_fd, SHUT_RDWR); 220 eloop_exit(ctx->eloop, sig == SIGTERM ? EXIT_SUCCESS : EXIT_FAILURE); 221 } 222 223 ssize_t 224 ps_inet_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg) 225 { 226 struct dhcpcd_ctx *ctx = arg; 227 228 switch (psm->ps_cmd) { 229 #ifdef INET 230 case PS_BOOTP: 231 dhcp_recvmsg(ctx, msg); 232 break; 233 #endif 234 #ifdef INET6 235 case PS_ND: 236 ipv6nd_recvmsg(ctx, msg); 237 break; 238 #endif 239 #ifdef DHCP6 240 case PS_DHCP6: 241 dhcp6_recvmsg(ctx, msg, NULL); 242 break; 243 #endif 244 default: 245 errno = ENOTSUP; 246 return -1; 247 } 248 return 1; 249 } 250 251 static void 252 ps_inet_dodispatch(void *arg) 253 { 254 struct dhcpcd_ctx *ctx = arg; 255 256 if (ps_recvpsmsg(ctx, ctx->ps_inet_fd, ps_inet_dispatch, ctx) == -1) 257 logerr(__func__); 258 } 259 260 pid_t 261 ps_inet_start(struct dhcpcd_ctx *ctx) 262 { 263 264 return ps_dostart(ctx, &ctx->ps_inet_pid, &ctx->ps_inet_fd, 265 ps_inet_recvmsg, ps_inet_dodispatch, ctx, 266 ps_inet_startcb, ps_inet_signalcb, PSF_DROPPRIVS); 267 } 268 269 int 270 ps_inet_stop(struct dhcpcd_ctx *ctx) 271 { 272 273 return ps_dostop(ctx, &ctx->ps_inet_pid, &ctx->ps_inet_fd); 274 } 275 276 ssize_t 277 ps_inet_sendmsg(struct dhcpcd_ctx *ctx, uint8_t cmd, const struct msghdr *msg) 278 { 279 280 return ps_sendmsg(ctx, ctx->ps_inet_fd, cmd, 0, msg); 281 } 282 283 #ifdef INET 284 static void 285 ps_inet_recvinbootp(void *arg) 286 { 287 struct ps_process *psp = arg; 288 289 if (ps_recvmsg(psp->psp_ctx, psp->psp_work_fd, 290 PS_BOOTP, psp->psp_ctx->ps_data_fd) == -1) 291 logerr(__func__); 292 } 293 294 static int 295 ps_inet_listenin(void *arg) 296 { 297 struct ps_process *psp = arg; 298 struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr; 299 char buf[INET_ADDRSTRLEN]; 300 301 inet_ntop(AF_INET, ia, buf, sizeof(buf)); 302 setproctitle("[network proxy] %s", buf); 303 304 psp->psp_work_fd = dhcp_openudp(ia); 305 if (psp->psp_work_fd == -1) { 306 logerr(__func__); 307 return -1; 308 } 309 310 if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, 311 ps_inet_recvinbootp, psp) == -1) 312 { 313 logerr("%s: eloop_event_add DHCP", __func__); 314 return -1; 315 } 316 317 #ifdef PRIVSEP_DEBUG 318 logdebugx("spawned listener %s on PID %d", buf, getpid()); 319 #endif 320 return 0; 321 } 322 #endif 323 324 #if defined(INET6) && defined(__sun) 325 static void 326 ps_inet_recvin6nd(void *arg) 327 { 328 struct ps_process *psp = arg; 329 330 if (ps_recvmsg(psp->psp_ctx, psp->psp_work_fd, 331 PS_ND, psp->psp_ctx->ps_data_fd) == -1) 332 logerr(__func__); 333 } 334 335 static int 336 ps_inet_listennd(void *arg) 337 { 338 struct ps_process *psp = arg; 339 340 setproctitle("[ND network proxy]"); 341 342 psp->psp_work_fd = ipv6nd_open(&psp->psp_ifp); 343 if (psp->psp_work_fd == -1) { 344 logerr(__func__); 345 return -1; 346 } 347 348 if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, 349 ps_inet_recvin6nd, psp) == -1) 350 { 351 logerr(__func__); 352 return -1; 353 } 354 355 logdebugx("spawned ND listener on PID %d", getpid()); 356 return 0; 357 } 358 #endif 359 360 #ifdef DHCP6 361 static void 362 ps_inet_recvin6dhcp6(void *arg) 363 { 364 struct ps_process *psp = arg; 365 366 if (ps_recvmsg(psp->psp_ctx, psp->psp_work_fd, 367 PS_DHCP6, psp->psp_ctx->ps_data_fd) == -1) 368 logerr(__func__); 369 } 370 371 static int 372 ps_inet_listenin6(void *arg) 373 { 374 struct ps_process *psp = arg; 375 struct in6_addr *ia = &psp->psp_id.psi_addr.psa_in6_addr; 376 char buf[INET6_ADDRSTRLEN]; 377 378 inet_ntop(AF_INET6, ia, buf, sizeof(buf)); 379 setproctitle("[network proxy] %s", buf); 380 381 psp->psp_work_fd = dhcp6_openudp(psp->psp_id.psi_ifindex, ia); 382 if (psp->psp_work_fd == -1) { 383 logerr(__func__); 384 return -1; 385 } 386 387 if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, 388 ps_inet_recvin6dhcp6, psp) == -1) 389 { 390 logerr("%s: eloop_event_add DHCP", __func__); 391 return -1; 392 } 393 394 logdebugx("spawned listener %s on PID %d", buf, getpid()); 395 return 0; 396 } 397 #endif 398 399 static ssize_t 400 ps_inet_recvmsgpsp_cb(void *arg, __unused struct ps_msghdr *psm, 401 struct msghdr *msg) 402 { 403 struct ps_process *psp = arg; 404 405 return sendmsg(psp->psp_work_fd, msg, 0); 406 } 407 408 static void 409 ps_inet_recvmsgpsp(void *arg) 410 { 411 struct ps_process *psp = arg; 412 413 if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, 414 ps_inet_recvmsgpsp_cb, psp) == -1) 415 logerr(__func__); 416 } 417 418 ssize_t 419 ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, 420 __unused struct msghdr *msg) 421 { 422 uint8_t cmd; 423 struct ps_process *psp; 424 int (*start_func)(void *); 425 pid_t start; 426 427 cmd = (uint8_t)(psm->ps_cmd & ~(PS_START | PS_STOP)); 428 psp = ps_findprocess(ctx, &psm->ps_id); 429 430 #ifdef PRIVSEP_DEBUG 431 logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp); 432 #endif 433 434 if (psm->ps_cmd & PS_STOP) { 435 assert(psp == NULL); 436 return 0; 437 } 438 439 switch (cmd) { 440 #ifdef INET 441 case PS_BOOTP: 442 start_func = ps_inet_listenin; 443 break; 444 #endif 445 #ifdef INET6 446 #ifdef __sun 447 case PS_ND: 448 start_func = ps_inet_listennd; 449 break; 450 #endif 451 #ifdef DHCP6 452 case PS_DHCP6: 453 start_func = ps_inet_listenin6; 454 break; 455 #endif 456 #endif 457 default: 458 logerrx("%s: unknown command %x", __func__, psm->ps_cmd); 459 errno = ENOTSUP; 460 return -1; 461 } 462 463 if (!(psm->ps_cmd & PS_START)) { 464 errno = EINVAL; 465 return -1; 466 } 467 468 if (psp != NULL) 469 return 1; 470 471 psp = ps_newprocess(ctx, &psm->ps_id); 472 if (psp == NULL) 473 return -1; 474 475 start = ps_dostart(ctx, 476 &psp->psp_pid, &psp->psp_fd, 477 ps_inet_recvmsgpsp, NULL, psp, 478 start_func, ps_inet_signalcb, PSF_DROPPRIVS); 479 switch (start) { 480 case -1: 481 ps_freeprocess(psp); 482 return -1; 483 case 0: 484 break; 485 default: 486 break; 487 } 488 return start; 489 } 490 491 #ifdef INET 492 static ssize_t 493 ps_inet_in_docmd(struct ipv4_addr *ia, uint8_t cmd, const struct msghdr *msg) 494 { 495 assert(ia != NULL); 496 struct dhcpcd_ctx *ctx = ia->iface->ctx; 497 struct ps_msghdr psm = { 498 .ps_cmd = cmd, 499 .ps_id = { 500 .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)), 501 .psi_ifindex = ia->iface->index, 502 .psi_addr.psa_in_addr = ia->addr, 503 }, 504 }; 505 506 return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg); 507 } 508 509 ssize_t 510 ps_inet_openbootp(struct ipv4_addr *ia) 511 { 512 513 return ps_inet_in_docmd(ia, PS_START | PS_BOOTP, NULL); 514 } 515 516 ssize_t 517 ps_inet_closebootp(struct ipv4_addr *ia) 518 { 519 520 return ps_inet_in_docmd(ia, PS_STOP | PS_BOOTP, NULL); 521 } 522 523 ssize_t 524 ps_inet_sendbootp(struct ipv4_addr *ia, const struct msghdr *msg) 525 { 526 struct dhcpcd_ctx *ctx = ia->iface->ctx; 527 528 if (ctx->options & DHCPCD_MASTER) 529 return ps_sendmsg(ctx, ctx->ps_inet_fd, PS_BOOTP, 0, msg); 530 return ps_inet_in_docmd(ia, PS_BOOTP, msg); 531 } 532 #endif /* INET */ 533 534 #ifdef INET6 535 #ifdef __sun 536 static ssize_t 537 ps_inet_ifp_docmd(struct interface *ifp, uint8_t cmd, const struct msghdr *msg) 538 { 539 struct dhcpcd_ctx *ctx = ifp->ctx; 540 struct ps_msghdr psm = { 541 .ps_cmd = cmd, 542 .ps_id = { 543 .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)), 544 .psi_ifindex = ifp->index, 545 }, 546 }; 547 548 return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg); 549 } 550 551 ssize_t 552 ps_inet_opennd(struct interface *ifp) 553 { 554 555 return ps_inet_ifp_docmd(ifp, PS_ND | PS_START, NULL); 556 } 557 558 ssize_t 559 ps_inet_closend(struct interface *ifp) 560 { 561 562 return ps_inet_ifp_docmd(ifp, PS_ND | PS_STOP, NULL); 563 } 564 565 ssize_t 566 ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg) 567 { 568 569 return ps_inet_ifp_docmd(ifp, PS_ND, msg); 570 } 571 #else 572 ssize_t 573 ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg) 574 { 575 576 return ps_inet_sendmsg(ifp->ctx, PS_ND, msg); 577 } 578 #endif 579 580 #ifdef DHCP6 581 static ssize_t 582 ps_inet_in6_docmd(struct ipv6_addr *ia, uint8_t cmd, const struct msghdr *msg) 583 { 584 struct dhcpcd_ctx *ctx = ia->iface->ctx; 585 struct ps_msghdr psm = { 586 .ps_cmd = cmd, 587 .ps_id = { 588 .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)), 589 .psi_ifindex = ia->iface->index, 590 .psi_addr.psa_in6_addr = ia->addr, 591 }, 592 }; 593 594 return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg); 595 } 596 597 ssize_t 598 ps_inet_opendhcp6(struct ipv6_addr *ia) 599 { 600 601 return ps_inet_in6_docmd(ia, PS_DHCP6 | PS_START, NULL); 602 } 603 604 ssize_t 605 ps_inet_closedhcp6(struct ipv6_addr *ia) 606 { 607 608 return ps_inet_in6_docmd(ia, PS_DHCP6 | PS_STOP, NULL); 609 } 610 611 ssize_t 612 ps_inet_senddhcp6(struct ipv6_addr *ia, const struct msghdr *msg) 613 { 614 struct dhcpcd_ctx *ctx = ia->iface->ctx; 615 616 if (ctx->options & DHCPCD_MASTER) 617 return ps_sendmsg(ctx, ctx->ps_inet_fd, PS_DHCP6, 0, msg); 618 return ps_inet_in6_docmd(ia, PS_DHCP6, msg); 619 } 620 #endif /* DHCP6 */ 621 #endif /* INET6 */ 622