1 /* SPDX-License-Identifier: BSD-2-Clause */
3 * Privilege Separation for dhcpcd, network proxy
4 * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
29 #include <sys/socket.h>
30 #include <sys/types.h>
32 #include <netinet/in.h>
33 #include <netinet/icmp6.h>
52 ps_inet_recvbootp(void *arg
, unsigned short events
)
54 struct dhcpcd_ctx
*ctx
= arg
;
56 if (ps_recvmsg(ctx
, ctx
->udp_rfd
, events
,
57 PS_BOOTP
, ctx
->ps_inet
->psp_fd
) == -1)
64 ps_inet_recvra(void *arg
, unsigned short events
)
67 struct interface
*ifp
= arg
;
68 struct rs_state
*state
= RS_STATE(ifp
);
69 struct dhcpcd_ctx
*ctx
= ifp
->ctx
;
71 if (ps_recvmsg(ctx
, state
->nd_fd
, events
,
72 PS_ND
, ctx
->ps_inet
->psp_fd
) == -1)
75 struct dhcpcd_ctx
*ctx
= arg
;
77 if (ps_recvmsg(ctx
, ctx
->nd_fd
, events
,
78 PS_ND
, ctx
->ps_inet
->psp_fd
) == -1)
86 ps_inet_recvdhcp6(void *arg
, unsigned short events
)
88 struct dhcpcd_ctx
*ctx
= arg
;
90 if (ps_recvmsg(ctx
, ctx
->dhcp6_rfd
, events
,
91 PS_DHCP6
, ctx
->ps_inet
->psp_fd
) == -1)
97 ps_inet_canstart(const struct dhcpcd_ctx
*ctx
)
101 if ((ctx
->options
& (DHCPCD_IPV4
| DHCPCD_MANAGER
)) ==
102 (DHCPCD_IPV4
| DHCPCD_MANAGER
))
105 #if defined(INET6) && !defined(__sun)
106 if (ctx
->options
& DHCPCD_IPV6
)
110 if ((ctx
->options
& (DHCPCD_IPV6
| DHCPCD_MANAGER
)) ==
111 (DHCPCD_IPV6
| DHCPCD_MANAGER
))
119 ps_inet_startcb(struct ps_process
*psp
)
121 struct dhcpcd_ctx
*ctx
= psp
->psp_ctx
;
124 if (ctx
->options
& DHCPCD_MANAGER
)
125 setproctitle("[network proxy]");
127 setproctitle("[network proxy] %s%s%s",
128 ctx
->ifc
!= 0 ? ctx
->ifv
[0] : "",
129 ctx
->options
& DHCPCD_IPV4
? " [ip4]" : "",
130 ctx
->options
& DHCPCD_IPV6
? " [ip6]" : "");
132 /* This end is the main engine, so it's useless for us. */
133 close(ctx
->ps_data_fd
);
134 ctx
->ps_data_fd
= -1;
139 if ((ctx
->options
& (DHCPCD_IPV4
| DHCPCD_MANAGER
)) ==
140 (DHCPCD_IPV4
| DHCPCD_MANAGER
))
142 ctx
->udp_rfd
= dhcp_openudp(NULL
);
143 if (ctx
->udp_rfd
== -1)
144 logerr("%s: dhcp_open", __func__
);
145 #ifdef PRIVSEP_RIGHTS
146 else if (ps_rights_limit_fd_rdonly(ctx
->udp_rfd
) == -1) {
147 logerr("%s: ps_rights_limit_fd_rdonly", __func__
);
152 else if (eloop_event_add(ctx
->eloop
, ctx
->udp_rfd
, ELE_READ
,
153 ps_inet_recvbootp
, ctx
) == -1)
155 logerr("%s: eloop_event_add DHCP", __func__
);
162 #if defined(INET6) && !defined(__sun)
163 if (ctx
->options
& DHCPCD_IPV6
) {
164 ctx
->nd_fd
= ipv6nd_open(true);
165 if (ctx
->nd_fd
== -1)
166 logerr("%s: ipv6nd_open", __func__
);
167 #ifdef PRIVSEP_RIGHTS
168 else if (ps_rights_limit_fd_rdonly(ctx
->nd_fd
) == -1) {
169 logerr("%s: ps_rights_limit_fd_rdonly", __func__
);
174 else if (eloop_event_add(ctx
->eloop
, ctx
->nd_fd
, ELE_READ
,
175 ps_inet_recvra
, ctx
) == -1)
177 logerr("%s: eloop_event_add RA", __func__
);
185 if ((ctx
->options
& (DHCPCD_IPV6
| DHCPCD_MANAGER
)) ==
186 (DHCPCD_IPV6
| DHCPCD_MANAGER
))
188 ctx
->dhcp6_rfd
= dhcp6_openudp(0, NULL
);
189 if (ctx
->dhcp6_rfd
== -1)
190 logerr("%s: dhcp6_open", __func__
);
191 #ifdef PRIVSEP_RIGHTS
192 else if (ps_rights_limit_fd_rdonly(ctx
->dhcp6_rfd
) == -1) {
193 logerr("%s: ps_rights_limit_fd_rdonly", __func__
);
194 close(ctx
->dhcp6_rfd
);
198 else if (eloop_event_add(ctx
->eloop
, ctx
->dhcp6_rfd
, ELE_READ
,
199 ps_inet_recvdhcp6
, ctx
) == -1)
201 logerr("%s: eloop_event_add DHCP6", __func__
);
202 close(ctx
->dhcp6_rfd
);
209 if (ret
== 0 && errno
== 0) {
217 ps_inet_validudp(struct msghdr
*msg
, uint16_t sport
, uint16_t dport
)
220 struct iovec
*iov
= msg
->msg_iov
;
222 if (msg
->msg_iovlen
== 0 || iov
->iov_len
< sizeof(udp
)) {
227 memcpy(&udp
, iov
->iov_base
, sizeof(udp
));
228 if (udp
.uh_sport
!= htons(sport
) || udp
.uh_dport
!= htons(dport
)) {
237 ps_inet_validnd(struct msghdr
*msg
)
239 struct icmp6_hdr icmp6
;
240 struct iovec
*iov
= msg
->msg_iov
;
242 if (msg
->msg_iovlen
== 0 || iov
->iov_len
< sizeof(icmp6
)) {
247 memcpy(&icmp6
, iov
->iov_base
, sizeof(icmp6
));
248 switch(icmp6
.icmp6_type
) {
249 case ND_ROUTER_SOLICIT
:
250 case ND_NEIGHBOR_ADVERT
:
262 ps_inet_sendmsg(struct dhcpcd_ctx
*ctx
,
263 struct ps_msghdr
*psm
, struct msghdr
*msg
)
265 struct ps_process
*psp
;
268 psp
= ps_findprocess(ctx
, &psm
->ps_id
);
270 s
= psp
->psp_work_fd
;
274 switch (psm
->ps_cmd
) {
277 if (!ps_inet_validudp(msg
, BOOTPC
, BOOTPS
))
282 #if defined(INET6) && !defined(__sun)
284 if (!ps_inet_validnd(msg
))
291 if (!ps_inet_validudp(msg
, DHCP6_CLIENT_PORT
,DHCP6_SERVER_PORT
))
302 return sendmsg(s
, msg
, 0);
306 ps_inet_recvmsg(void *arg
, unsigned short events
)
308 struct ps_process
*psp
= arg
;
310 /* Receive shutdown */
311 if (ps_recvpsmsg(psp
->psp_ctx
, psp
->psp_fd
, events
, NULL
, NULL
) == -1)
316 ps_inet_dispatch(void *arg
, struct ps_msghdr
*psm
, struct msghdr
*msg
)
318 struct dhcpcd_ctx
*ctx
= arg
;
320 switch (psm
->ps_cmd
) {
323 dhcp_recvmsg(ctx
, msg
);
328 ipv6nd_recvmsg(ctx
, msg
);
333 dhcp6_recvmsg(ctx
, msg
, NULL
);
344 ps_inet_dodispatch(void *arg
, unsigned short events
)
346 struct ps_process
*psp
= arg
;
348 if (ps_recvpsmsg(psp
->psp_ctx
, psp
->psp_fd
, events
,
349 ps_inet_dispatch
, psp
->psp_ctx
) == -1)
354 ps_inet_start(struct dhcpcd_ctx
*ctx
)
360 struct ps_process
*psp
;
363 psp
= ctx
->ps_inet
= ps_newprocess(ctx
, &id
);
367 strlcpy(psp
->psp_name
, "network proxy", sizeof(psp
->psp_name
));
368 pid
= ps_startprocess(psp
, ps_inet_recvmsg
, ps_inet_dodispatch
,
369 ps_inet_startcb
, NULL
, PSF_DROPPRIVS
);
372 ps_entersandbox("stdio", NULL
);
378 ps_inet_stop(struct dhcpcd_ctx
*ctx
)
381 return ps_stopprocess(ctx
->ps_inet
);
386 ps_inet_recvinbootp(void *arg
, unsigned short events
)
388 struct ps_process
*psp
= arg
;
390 if (ps_recvmsg(psp
->psp_ctx
, psp
->psp_work_fd
, events
,
391 PS_BOOTP
, psp
->psp_ctx
->ps_data_fd
) == -1)
396 ps_inet_listenin(struct ps_process
*psp
)
398 struct in_addr
*ia
= &psp
->psp_id
.psi_addr
.psa_in_addr
;
399 char buf
[INET_ADDRSTRLEN
];
401 inet_ntop(AF_INET
, ia
, buf
, sizeof(buf
));
402 setproctitle("[%s proxy] %s", psp
->psp_protostr
, buf
);
404 psp
->psp_work_fd
= dhcp_openudp(ia
);
405 if (psp
->psp_work_fd
== -1) {
410 #ifdef PRIVSEP_RIGHTS
411 if (ps_rights_limit_fd_rdonly(psp
->psp_work_fd
) == -1) {
412 logerr("%s: ps_rights_limit_fd_rdonly", __func__
);
417 if (eloop_event_add(psp
->psp_ctx
->eloop
, psp
->psp_work_fd
, ELE_READ
,
418 ps_inet_recvinbootp
, psp
) == -1)
420 logerr("%s: eloop_event_add DHCP", __func__
);
427 #if defined(INET6) && defined(__sun)
429 ps_inet_recvin6nd(void *arg
)
431 struct ps_process
*psp
= arg
;
433 if (ps_recvmsg(psp
->psp_ctx
, psp
->psp_work_fd
,
434 PS_ND
, psp
->psp_ctx
->ps_data_fd
) == -1)
439 ps_inet_listennd(struct ps_process
*psp
)
442 setproctitle("[ND network proxy]");
444 psp
->psp_work_fd
= ipv6nd_open(&psp
->psp_ifp
);
445 if (psp
->psp_work_fd
== -1) {
450 #ifdef PRIVSEP_RIGHTS
451 if (ps_rights_limit_fd_rdonly(psp
->psp_work_fd
) == -1) {
452 logerr("%s: ps_rights_limit_fd_rdonly", __func__
);
457 if (eloop_event_add(psp
->psp_ctx
->eloop
, psp
->psp_work_fd
,
458 ps_inet_recvin6nd
, psp
) == -1)
469 ps_inet_recvin6dhcp6(void *arg
, unsigned short events
)
471 struct ps_process
*psp
= arg
;
473 if (ps_recvmsg(psp
->psp_ctx
, psp
->psp_work_fd
, events
,
474 PS_DHCP6
, psp
->psp_ctx
->ps_data_fd
) == -1)
479 ps_inet_listenin6(struct ps_process
*psp
)
481 struct in6_addr
*ia
= &psp
->psp_id
.psi_addr
.psa_in6_addr
;
482 char buf
[INET6_ADDRSTRLEN
];
484 inet_ntop(AF_INET6
, ia
, buf
, sizeof(buf
));
485 setproctitle("[%s proxy] %s", psp
->psp_protostr
, buf
);
487 psp
->psp_work_fd
= dhcp6_openudp(psp
->psp_id
.psi_ifindex
, ia
);
488 if (psp
->psp_work_fd
== -1) {
493 #ifdef PRIVSEP_RIGHTS
494 if (ps_rights_limit_fd_rdonly(psp
->psp_work_fd
) == -1) {
495 logerr("%s: ps_rights_limit_fd_rdonly", __func__
);
500 if (eloop_event_add(psp
->psp_ctx
->eloop
, psp
->psp_work_fd
, ELE_READ
,
501 ps_inet_recvin6dhcp6
, psp
) == -1)
503 logerr("%s: eloop_event_add DHCP", __func__
);
511 ps_inet_recvmsgpsp(void *arg
, unsigned short events
)
513 struct ps_process
*psp
= arg
;
515 /* Receive shutdown. */
516 if (ps_recvpsmsg(psp
->psp_ctx
, psp
->psp_fd
, events
, NULL
, NULL
) == -1)
521 ps_inet_cmd(struct dhcpcd_ctx
*ctx
, struct ps_msghdr
*psm
, struct msghdr
*msg
)
524 struct ps_process
*psp
;
525 int (*start_func
)(struct ps_process
*);
527 struct ps_addr
*psa
= &psm
->ps_id
.psi_addr
;
529 char buf
[INET_MAX_ADDRSTRLEN
];
531 cmd
= (uint16_t)(psm
->ps_cmd
& ~(PS_START
| PS_STOP
));
532 if (cmd
== psm
->ps_cmd
)
533 return ps_inet_sendmsg(ctx
, psm
, msg
);
535 psp
= ps_findprocess(ctx
, &psm
->ps_id
);
538 logerrx("%s: IN cmd %x, psp %p", __func__
, psm
->ps_cmd
, psp
);
541 if (psm
->ps_cmd
& PS_STOP
) {
546 if (!(psm
->ps_cmd
& PS_START
)) {
554 psp
= ps_newprocess(ctx
, &psm
->ps_id
);
562 start_func
= ps_inet_listenin
;
563 psp
->psp_protostr
= "BOOTP";
564 ia
= &psa
->psa_in_addr
;
570 start_func
= ps_inet_listennd
;
571 psp
->psp_protostr
= "ND";
572 ia
= &psa
->psa_in6_addr
;
577 start_func
= ps_inet_listenin6
;
578 psp
->psp_protostr
= "DHCP6";
579 ia
= &psa
->psa_in6_addr
;
584 logerrx("%s: unknown command %x", __func__
, psm
->ps_cmd
);
589 snprintf(psp
->psp_name
, sizeof(psp
->psp_name
),
590 "%s proxy %s", psp
->psp_protostr
,
591 inet_ntop(psa
->psa_family
, ia
, buf
, sizeof(buf
)));
592 start
= ps_startprocess(psp
, ps_inet_recvmsgpsp
, NULL
,
593 start_func
, NULL
, PSF_DROPPRIVS
);
599 ps_entersandbox("stdio", NULL
);
602 logdebugx("%s: spawned %s on PID %d",
603 psp
->psp_ifname
, psp
->psp_name
, psp
->psp_pid
);
611 ps_inet_in_docmd(struct ipv4_addr
*ia
, uint16_t cmd
, const struct msghdr
*msg
)
614 struct dhcpcd_ctx
*ctx
= ia
->iface
->ctx
;
615 struct ps_msghdr psm
= {
618 .psi_cmd
= (uint8_t)(cmd
& ~(PS_START
| PS_STOP
)),
619 .psi_ifindex
= ia
->iface
->index
,
620 .psi_addr
.psa_family
= AF_INET
,
621 .psi_addr
.psa_in_addr
= ia
->addr
,
625 return ps_sendpsmmsg(ctx
, ctx
->ps_root
->psp_fd
, &psm
, msg
);
629 ps_inet_openbootp(struct ipv4_addr
*ia
)
632 return ps_inet_in_docmd(ia
, PS_START
| PS_BOOTP
, NULL
);
636 ps_inet_closebootp(struct ipv4_addr
*ia
)
639 return ps_inet_in_docmd(ia
, PS_STOP
| PS_BOOTP
, NULL
);
643 ps_inet_sendbootp(struct interface
*ifp
, const struct msghdr
*msg
)
645 struct dhcpcd_ctx
*ctx
= ifp
->ctx
;
647 return ps_sendmsg(ctx
, ctx
->ps_root
->psp_fd
, PS_BOOTP
, 0, msg
);
654 ps_inet_ifp_docmd(struct interface
*ifp
, uint16_t cmd
, const struct msghdr
*msg
)
656 struct dhcpcd_ctx
*ctx
= ifp
->ctx
;
657 struct ps_msghdr psm
= {
660 .psi_cmd
= (uint8_t)(cmd
& ~(PS_START
| PS_STOP
)),
661 .psi_ifindex
= ifp
->index
,
662 .psi_addr
.psa_family
= AF_INET6
,
666 return ps_sendpsmmsg(ctx
, ctx
->ps_root
->psp_fd
, &psm
, msg
);
670 ps_inet_opennd(struct interface
*ifp
)
673 return ps_inet_ifp_docmd(ifp
, PS_ND
| PS_START
, NULL
);
677 ps_inet_closend(struct interface
*ifp
)
680 return ps_inet_ifp_docmd(ifp
, PS_ND
| PS_STOP
, NULL
);
684 ps_inet_sendnd(struct interface
*ifp
, const struct msghdr
*msg
)
687 return ps_inet_ifp_docmd(ifp
, PS_ND
, msg
);
691 ps_inet_sendnd(struct interface
*ifp
, const struct msghdr
*msg
)
693 struct dhcpcd_ctx
*ctx
= ifp
->ctx
;
695 return ps_sendmsg(ctx
, ctx
->ps_root
->psp_fd
, PS_ND
, 0, msg
);
701 ps_inet_in6_docmd(struct ipv6_addr
*ia
, uint16_t cmd
, const struct msghdr
*msg
)
703 struct dhcpcd_ctx
*ctx
= ia
->iface
->ctx
;
704 struct ps_msghdr psm
= {
707 .psi_cmd
= (uint8_t)(cmd
& ~(PS_START
| PS_STOP
)),
708 .psi_ifindex
= ia
->iface
->index
,
709 .psi_addr
.psa_family
= AF_INET6
,
710 .psi_addr
.psa_in6_addr
= ia
->addr
,
714 return ps_sendpsmmsg(ctx
, ctx
->ps_root
->psp_fd
, &psm
, msg
);
718 ps_inet_opendhcp6(struct ipv6_addr
*ia
)
721 return ps_inet_in6_docmd(ia
, PS_DHCP6
| PS_START
, NULL
);
725 ps_inet_closedhcp6(struct ipv6_addr
*ia
)
728 return ps_inet_in6_docmd(ia
, PS_DHCP6
| PS_STOP
, NULL
);
732 ps_inet_senddhcp6(struct interface
*ifp
, const struct msghdr
*msg
)
734 struct dhcpcd_ctx
*ctx
= ifp
->ctx
;
736 return ps_sendmsg(ctx
, ctx
->ps_root
->psp_fd
, PS_DHCP6
, 0, msg
);