xref: /dragonfly/contrib/dhcpcd/src/privsep-inet.c (revision 469fd606)
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