1 /* SPDX-License-Identifier: BSD-2-Clause */
3 * Privilege Separation for dhcpcd, network proxy
4 * Copyright (c) 2006-2020 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>
51 #include <sys/capsicum.h>
56 ps_inet_recvbootp(void *arg
)
58 struct dhcpcd_ctx
*ctx
= arg
;
60 if (ps_recvmsg(ctx
, ctx
->udp_rfd
, PS_BOOTP
, ctx
->ps_inet_fd
) == -1)
67 ps_inet_recvra(void *arg
)
70 struct interface
*ifp
= arg
;
71 struct rs_state
*state
= RS_STATE(ifp
);
72 struct dhcpcd_ctx
*ctx
= ifp
->ctx
;
74 if (ps_recvmsg(ctx
, state
->nd_fd
, PS_ND
, ctx
->ps_inet_fd
) == -1)
77 struct dhcpcd_ctx
*ctx
= arg
;
79 if (ps_recvmsg(ctx
, ctx
->nd_fd
, PS_ND
, ctx
->ps_inet_fd
) == -1)
87 ps_inet_recvdhcp6(void *arg
)
89 struct dhcpcd_ctx
*ctx
= arg
;
91 if (ps_recvmsg(ctx
, ctx
->dhcp6_rfd
, PS_DHCP6
, ctx
->ps_inet_fd
) == -1)
97 ps_inet_startcb(void *arg
)
99 struct dhcpcd_ctx
*ctx
= arg
;
102 if (ctx
->options
& DHCPCD_MASTER
)
103 setproctitle("[network proxy]");
105 setproctitle("[network proxy] %s%s%s",
107 ctx
->options
& DHCPCD_IPV4
? " [ip4]" : "",
108 ctx
->options
& DHCPCD_IPV6
? " [ip6]" : "");
110 /* This end is the main engine, so it's useless for us. */
111 close(ctx
->ps_data_fd
);
112 ctx
->ps_data_fd
= -1;
117 if ((ctx
->options
& (DHCPCD_IPV4
| DHCPCD_MASTER
)) ==
118 (DHCPCD_IPV4
| DHCPCD_MASTER
))
120 ctx
->udp_rfd
= dhcp_openudp(NULL
);
121 if (ctx
->udp_rfd
== -1)
122 logerr("%s: dhcp_open", __func__
);
123 #ifdef PRIVSEP_RIGHTS
124 else if (ps_rights_limit_fd_rdonly(ctx
->udp_rfd
) == -1) {
125 logerr("%s: ps_rights_limit_fd_rdonly", __func__
);
130 else if (eloop_event_add(ctx
->eloop
, ctx
->udp_rfd
,
131 ps_inet_recvbootp
, ctx
) == -1)
133 logerr("%s: eloop_event_add DHCP", __func__
);
140 #if defined(INET6) && !defined(__sun)
141 if (ctx
->options
& DHCPCD_IPV6
) {
142 ctx
->nd_fd
= ipv6nd_open(true);
143 if (ctx
->nd_fd
== -1)
144 logerr("%s: ipv6nd_open", __func__
);
145 #ifdef PRIVSEP_RIGHTS
146 else if (ps_rights_limit_fd_rdonly(ctx
->nd_fd
) == -1) {
147 logerr("%s: ps_rights_limit_fd_rdonly", __func__
);
152 else if (eloop_event_add(ctx
->eloop
, ctx
->nd_fd
,
153 ps_inet_recvra
, ctx
) == -1)
155 logerr("%s: eloop_event_add RA", __func__
);
163 if ((ctx
->options
& (DHCPCD_IPV6
| DHCPCD_MASTER
)) ==
164 (DHCPCD_IPV6
| DHCPCD_MASTER
))
166 ctx
->dhcp6_rfd
= dhcp6_openudp(0, NULL
);
167 if (ctx
->dhcp6_rfd
== -1)
168 logerr("%s: dhcp6_open", __func__
);
169 #ifdef PRIVSEP_RIGHTS
170 else if (ps_rights_limit_fd_rdonly(ctx
->dhcp6_rfd
) == -1) {
171 logerr("%s: ps_rights_limit_fd_rdonly", __func__
);
172 close(ctx
->dhcp6_rfd
);
176 else if (eloop_event_add(ctx
->eloop
, ctx
->dhcp6_rfd
,
177 ps_inet_recvdhcp6
, ctx
) == -1)
179 logerr("%s: eloop_event_add DHCP6", __func__
);
180 close(ctx
->dhcp6_rfd
);
187 if (ret
== 0 && errno
== 0) {
195 ps_inet_validudp(struct msghdr
*msg
, uint16_t sport
, uint16_t dport
)
198 struct iovec
*iov
= msg
->msg_iov
;
200 if (msg
->msg_iovlen
== 0 || iov
->iov_len
< sizeof(udp
)) {
205 memcpy(&udp
, iov
->iov_base
, sizeof(udp
));
206 if (udp
.uh_sport
!= htons(sport
) || udp
.uh_dport
!= htons(dport
)) {
215 ps_inet_validnd(struct msghdr
*msg
)
217 struct icmp6_hdr icmp6
;
218 struct iovec
*iov
= msg
->msg_iov
;
220 if (msg
->msg_iovlen
== 0 || iov
->iov_len
< sizeof(icmp6
)) {
225 memcpy(&icmp6
, iov
->iov_base
, sizeof(icmp6
));
226 switch(icmp6
.icmp6_type
) {
227 case ND_ROUTER_SOLICIT
:
228 case ND_NEIGHBOR_ADVERT
:
240 ps_inet_sendmsg(struct dhcpcd_ctx
*ctx
,
241 struct ps_msghdr
*psm
, struct msghdr
*msg
)
243 struct ps_process
*psp
;
246 psp
= ps_findprocess(ctx
, &psm
->ps_id
);
248 s
= psp
->psp_work_fd
;
252 switch (psm
->ps_cmd
) {
255 if (!ps_inet_validudp(msg
, BOOTPC
, BOOTPS
))
260 #if defined(INET6) && !defined(__sun)
262 if (!ps_inet_validnd(msg
))
269 if (!ps_inet_validudp(msg
, DHCP6_CLIENT_PORT
,DHCP6_SERVER_PORT
))
280 return sendmsg(s
, msg
, 0);
284 ps_inet_recvmsg(void *arg
)
286 struct dhcpcd_ctx
*ctx
= arg
;
288 /* Receive shutdown */
289 if (ps_recvpsmsg(ctx
, ctx
->ps_inet_fd
, NULL
, NULL
) == -1)
294 ps_inet_dispatch(void *arg
, struct ps_msghdr
*psm
, struct msghdr
*msg
)
296 struct dhcpcd_ctx
*ctx
= arg
;
298 switch (psm
->ps_cmd
) {
301 dhcp_recvmsg(ctx
, msg
);
306 ipv6nd_recvmsg(ctx
, msg
);
311 dhcp6_recvmsg(ctx
, msg
, NULL
);
322 ps_inet_dodispatch(void *arg
)
324 struct dhcpcd_ctx
*ctx
= arg
;
326 if (ps_recvpsmsg(ctx
, ctx
->ps_inet_fd
, ps_inet_dispatch
, ctx
) == -1)
331 ps_inet_start(struct dhcpcd_ctx
*ctx
)
335 pid
= ps_dostart(ctx
, &ctx
->ps_inet_pid
, &ctx
->ps_inet_fd
,
336 ps_inet_recvmsg
, ps_inet_dodispatch
, ctx
,
337 ps_inet_startcb
, NULL
,
341 if (pid
== 0 && cap_enter() == -1 && errno
!= ENOSYS
)
342 logerr("%s: cap_enter", __func__
);
345 if (pid
== 0 && pledge("stdio", NULL
) == -1)
346 logerr("%s: pledge", __func__
);
353 ps_inet_stop(struct dhcpcd_ctx
*ctx
)
356 return ps_dostop(ctx
, &ctx
->ps_inet_pid
, &ctx
->ps_inet_fd
);
361 ps_inet_recvinbootp(void *arg
)
363 struct ps_process
*psp
= arg
;
365 if (ps_recvmsg(psp
->psp_ctx
, psp
->psp_work_fd
,
366 PS_BOOTP
, psp
->psp_ctx
->ps_data_fd
) == -1)
371 ps_inet_listenin(void *arg
)
373 struct ps_process
*psp
= arg
;
374 struct in_addr
*ia
= &psp
->psp_id
.psi_addr
.psa_in_addr
;
375 char buf
[INET_ADDRSTRLEN
];
377 inet_ntop(AF_INET
, ia
, buf
, sizeof(buf
));
378 setproctitle("[network proxy] %s", buf
);
380 psp
->psp_work_fd
= dhcp_openudp(ia
);
381 if (psp
->psp_work_fd
== -1) {
386 #ifdef PRIVSEP_RIGHTS
387 if (ps_rights_limit_fd_rdonly(psp
->psp_work_fd
) == -1) {
388 logerr("%s: ps_rights_limit_fd_rdonly", __func__
);
393 if (eloop_event_add(psp
->psp_ctx
->eloop
, psp
->psp_work_fd
,
394 ps_inet_recvinbootp
, psp
) == -1)
396 logerr("%s: eloop_event_add DHCP", __func__
);
400 logdebugx("spawned listener %s on PID %d", buf
, getpid());
405 #if defined(INET6) && defined(__sun)
407 ps_inet_recvin6nd(void *arg
)
409 struct ps_process
*psp
= arg
;
411 if (ps_recvmsg(psp
->psp_ctx
, psp
->psp_work_fd
,
412 PS_ND
, psp
->psp_ctx
->ps_data_fd
) == -1)
417 ps_inet_listennd(void *arg
)
419 struct ps_process
*psp
= arg
;
421 setproctitle("[ND network proxy]");
423 psp
->psp_work_fd
= ipv6nd_open(&psp
->psp_ifp
);
424 if (psp
->psp_work_fd
== -1) {
429 #ifdef PRIVSEP_RIGHTS
430 if (ps_rights_limit_fd_rdonly(psp
->psp_work_fd
) == -1) {
431 logerr("%s: ps_rights_limit_fd_rdonly", __func__
);
436 if (eloop_event_add(psp
->psp_ctx
->eloop
, psp
->psp_work_fd
,
437 ps_inet_recvin6nd
, psp
) == -1)
443 logdebugx("spawned ND listener on PID %d", getpid());
450 ps_inet_recvin6dhcp6(void *arg
)
452 struct ps_process
*psp
= arg
;
454 if (ps_recvmsg(psp
->psp_ctx
, psp
->psp_work_fd
,
455 PS_DHCP6
, psp
->psp_ctx
->ps_data_fd
) == -1)
460 ps_inet_listenin6(void *arg
)
462 struct ps_process
*psp
= arg
;
463 struct in6_addr
*ia
= &psp
->psp_id
.psi_addr
.psa_in6_addr
;
464 char buf
[INET6_ADDRSTRLEN
];
466 inet_ntop(AF_INET6
, ia
, buf
, sizeof(buf
));
467 setproctitle("[network proxy] %s", buf
);
469 psp
->psp_work_fd
= dhcp6_openudp(psp
->psp_id
.psi_ifindex
, ia
);
470 if (psp
->psp_work_fd
== -1) {
475 #ifdef PRIVSEP_RIGHTS
476 if (ps_rights_limit_fd_rdonly(psp
->psp_work_fd
) == -1) {
477 logerr("%s: ps_rights_limit_fd_rdonly", __func__
);
482 if (eloop_event_add(psp
->psp_ctx
->eloop
, psp
->psp_work_fd
,
483 ps_inet_recvin6dhcp6
, psp
) == -1)
485 logerr("%s: eloop_event_add DHCP", __func__
);
489 logdebugx("spawned listener %s on PID %d", buf
, getpid());
495 ps_inet_recvmsgpsp(void *arg
)
497 struct ps_process
*psp
= arg
;
499 /* Receive shutdown. */
500 if (ps_recvpsmsg(psp
->psp_ctx
, psp
->psp_fd
, NULL
, NULL
) == -1)
505 ps_inet_cmd(struct dhcpcd_ctx
*ctx
, struct ps_msghdr
*psm
, struct msghdr
*msg
)
508 struct ps_process
*psp
;
509 int (*start_func
)(void *);
512 cmd
= (uint16_t)(psm
->ps_cmd
& ~(PS_START
| PS_STOP
));
513 if (cmd
== psm
->ps_cmd
)
514 return ps_inet_sendmsg(ctx
, psm
, msg
);
516 psp
= ps_findprocess(ctx
, &psm
->ps_id
);
519 logerrx("%s: IN cmd %x, psp %p", __func__
, psm
->ps_cmd
, psp
);
522 if (psm
->ps_cmd
& PS_STOP
) {
530 start_func
= ps_inet_listenin
;
536 start_func
= ps_inet_listennd
;
541 start_func
= ps_inet_listenin6
;
546 logerrx("%s: unknown command %x", __func__
, psm
->ps_cmd
);
551 if (!(psm
->ps_cmd
& PS_START
)) {
559 psp
= ps_newprocess(ctx
, &psm
->ps_id
);
563 start
= ps_dostart(ctx
,
564 &psp
->psp_pid
, &psp
->psp_fd
,
565 ps_inet_recvmsgpsp
, NULL
, psp
,
574 if (cap_enter() == -1 && errno
!= ENOSYS
)
575 logerr("%s: cap_enter", __func__
);
578 if (pledge("stdio", NULL
) == -1)
579 logerr("%s: pledge", __func__
);
590 ps_inet_in_docmd(struct ipv4_addr
*ia
, uint16_t cmd
, const struct msghdr
*msg
)
593 struct dhcpcd_ctx
*ctx
= ia
->iface
->ctx
;
594 struct ps_msghdr psm
= {
597 .psi_cmd
= (uint8_t)(cmd
& ~(PS_START
| PS_STOP
)),
598 .psi_ifindex
= ia
->iface
->index
,
599 .psi_addr
.psa_in_addr
= ia
->addr
,
603 return ps_sendpsmmsg(ctx
, ctx
->ps_root_fd
, &psm
, msg
);
607 ps_inet_openbootp(struct ipv4_addr
*ia
)
610 return ps_inet_in_docmd(ia
, PS_START
| PS_BOOTP
, NULL
);
614 ps_inet_closebootp(struct ipv4_addr
*ia
)
617 return ps_inet_in_docmd(ia
, PS_STOP
| PS_BOOTP
, NULL
);
621 ps_inet_sendbootp(struct interface
*ifp
, const struct msghdr
*msg
)
624 return ps_sendmsg(ifp
->ctx
, ifp
->ctx
->ps_root_fd
, PS_BOOTP
, 0, msg
);
631 ps_inet_ifp_docmd(struct interface
*ifp
, uint16_t cmd
, const struct msghdr
*msg
)
633 struct dhcpcd_ctx
*ctx
= ifp
->ctx
;
634 struct ps_msghdr psm
= {
637 .psi_cmd
= (uint8_t)(cmd
& ~(PS_START
| PS_STOP
)),
638 .psi_ifindex
= ifp
->index
,
642 return ps_sendpsmmsg(ctx
, ctx
->ps_root_fd
, &psm
, msg
);
646 ps_inet_opennd(struct interface
*ifp
)
649 return ps_inet_ifp_docmd(ifp
, PS_ND
| PS_START
, NULL
);
653 ps_inet_closend(struct interface
*ifp
)
656 return ps_inet_ifp_docmd(ifp
, PS_ND
| PS_STOP
, NULL
);
660 ps_inet_sendnd(struct interface
*ifp
, const struct msghdr
*msg
)
663 return ps_inet_ifp_docmd(ifp
, PS_ND
, msg
);
667 ps_inet_sendnd(struct interface
*ifp
, const struct msghdr
*msg
)
670 return ps_sendmsg(ifp
->ctx
, ifp
->ctx
->ps_root_fd
, PS_ND
, 0, msg
);
676 ps_inet_in6_docmd(struct ipv6_addr
*ia
, uint16_t cmd
, const struct msghdr
*msg
)
678 struct dhcpcd_ctx
*ctx
= ia
->iface
->ctx
;
679 struct ps_msghdr psm
= {
682 .psi_cmd
= (uint8_t)(cmd
& ~(PS_START
| PS_STOP
)),
683 .psi_ifindex
= ia
->iface
->index
,
684 .psi_addr
.psa_in6_addr
= ia
->addr
,
688 return ps_sendpsmmsg(ctx
, ctx
->ps_root_fd
, &psm
, msg
);
692 ps_inet_opendhcp6(struct ipv6_addr
*ia
)
695 return ps_inet_in6_docmd(ia
, PS_DHCP6
| PS_START
, NULL
);
699 ps_inet_closedhcp6(struct ipv6_addr
*ia
)
702 return ps_inet_in6_docmd(ia
, PS_DHCP6
| PS_STOP
, NULL
);
706 ps_inet_senddhcp6(struct interface
*ifp
, const struct msghdr
*msg
)
709 return ps_sendmsg(ifp
->ctx
, ifp
->ctx
->ps_root_fd
, PS_DHCP6
, 0, msg
);