Update to dhcpcd-9.4.1 with the following changes:
[dragonfly.git] / contrib / dhcpcd / src / privsep-control.c
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * Privilege Separation for dhcpcd, control proxy
4  * Copyright (c) 2006-2021 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 <errno.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "dhcpcd.h"
34 #include "control.h"
35 #include "eloop.h"
36 #include "logerr.h"
37 #include "privsep.h"
38
39 static int
40 ps_ctl_startcb(void *arg)
41 {
42         struct dhcpcd_ctx *ctx = arg;
43         sa_family_t af;
44
45         if (ctx->options & DHCPCD_MANAGER) {
46                 setproctitle("[control proxy]");
47                 af = AF_UNSPEC;
48         } else {
49                 setproctitle("[control proxy] %s%s%s",
50                     ctx->ifv[0],
51                     ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
52                     ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
53                 if ((ctx->options &
54                     (DHCPCD_IPV4 | DHCPCD_IPV6)) == DHCPCD_IPV4)
55                         af = AF_INET;
56                 else if ((ctx->options &
57                     (DHCPCD_IPV4 | DHCPCD_IPV6)) == DHCPCD_IPV6)
58                         af = AF_INET6;
59                 else
60                         af = AF_UNSPEC;
61         }
62
63         ctx->ps_control_pid = getpid();
64
65         return control_start(ctx,
66             ctx->options & DHCPCD_MANAGER ? NULL : *ctx->ifv, af);
67 }
68
69 static ssize_t
70 ps_ctl_recvmsgcb(void *arg, struct ps_msghdr *psm, __unused struct msghdr *msg)
71 {
72         struct dhcpcd_ctx *ctx = arg;
73
74         if (psm->ps_cmd != PS_CTL_EOF) {
75                 errno = ENOTSUP;
76                 return -1;
77         }
78
79         if (ctx->ps_control_client != NULL)
80                 ctx->ps_control_client = NULL;
81         return 0;
82 }
83
84 static void
85 ps_ctl_recvmsg(void *arg)
86 {
87         struct dhcpcd_ctx *ctx = arg;
88
89         if (ps_recvpsmsg(ctx, ctx->ps_control_fd, ps_ctl_recvmsgcb, ctx) == -1)
90                 logerr(__func__);
91 }
92
93 ssize_t
94 ps_ctl_handleargs(struct fd_list *fd, char *data, size_t len)
95 {
96
97         /* Make any change here in dhcpcd.c as well. */
98         if (strncmp(data, "--version",
99             MIN(strlen("--version"), len)) == 0) {
100                 return control_queue(fd, UNCONST(VERSION),
101                     strlen(VERSION) + 1);
102         } else if (strncmp(data, "--getconfigfile",
103             MIN(strlen("--getconfigfile"), len)) == 0) {
104                 return control_queue(fd, UNCONST(fd->ctx->cffile),
105                     strlen(fd->ctx->cffile) + 1);
106         } else if (strncmp(data, "--listen",
107             MIN(strlen("--listen"), len)) == 0) {
108                 fd->flags |= FD_LISTEN;
109                 return 0;
110         }
111
112         if (fd->ctx->ps_control_client != NULL &&
113             fd->ctx->ps_control_client != fd)
114         {
115                 logerrx("%s: cannot handle another client", __func__);
116                 return 0;
117         }
118         return 1;
119 }
120
121 static ssize_t
122 ps_ctl_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
123 {
124         struct dhcpcd_ctx *ctx = arg;
125         struct iovec *iov = msg->msg_iov;
126         struct fd_list *fd;
127         unsigned int fd_flags = FD_SENDLEN;
128
129         switch (psm->ps_flags) {
130         case PS_CTL_PRIV:
131                 break;
132         case PS_CTL_UNPRIV:
133                 fd_flags |= FD_UNPRIV;
134                 break;
135         }
136
137         switch (psm->ps_cmd) {
138         case PS_CTL:
139                 if (msg->msg_iovlen != 1) {
140                         errno = EINVAL;
141                         return -1;
142                 }
143                 if (ctx->ps_control_client != NULL) {
144                         logerrx("%s: cannot handle another client", __func__);
145                         return 0;
146                 }
147                 fd = control_new(ctx, ctx->ps_control_data_fd, fd_flags);
148                 if (fd == NULL)
149                         return -1;
150                 ctx->ps_control_client = fd;
151                 control_recvdata(fd, iov->iov_base, iov->iov_len);
152                 break;
153         case PS_CTL_EOF:
154                 control_free(ctx->ps_control_client);
155                 break;
156         default:
157                 errno = ENOTSUP;
158                 return -1;
159         }
160         return 0;
161 }
162
163 static void
164 ps_ctl_dodispatch(void *arg)
165 {
166         struct dhcpcd_ctx *ctx = arg;
167
168         if (ps_recvpsmsg(ctx, ctx->ps_control_fd, ps_ctl_dispatch, ctx) == -1)
169                 logerr(__func__);
170 }
171
172 static void
173 ps_ctl_recv(void *arg)
174 {
175         struct dhcpcd_ctx *ctx = arg;
176         char buf[BUFSIZ];
177         ssize_t len;
178
179         errno = 0;
180         len = read(ctx->ps_control_data_fd, buf, sizeof(buf));
181         if (len == -1 || len == 0) {
182                 logerr("%s: read", __func__);
183                 eloop_exit(ctx->eloop, EXIT_FAILURE);
184                 return;
185         }
186         if (ctx->ps_control_client == NULL) /* client disconnected */
187                 return;
188         errno = 0;
189         if (control_queue(ctx->ps_control_client, buf, (size_t)len) == -1)
190                 logerr("%s: control_queue", __func__);
191 }
192
193 static void
194 ps_ctl_listen(void *arg)
195 {
196         struct dhcpcd_ctx *ctx = arg;
197         char buf[BUFSIZ];
198         ssize_t len;
199         struct fd_list *fd;
200
201         errno = 0;
202         len = read(ctx->ps_control->fd, buf, sizeof(buf));
203         if (len == -1 || len == 0) {
204                 logerr("%s: read", __func__);
205                 eloop_exit(ctx->eloop, EXIT_FAILURE);
206                 return;
207         }
208
209         /* Send to our listeners */
210         TAILQ_FOREACH(fd, &ctx->control_fds, next) {
211                 if (!(fd->flags & FD_LISTEN))
212                         continue;
213                 if (control_queue(fd, buf, (size_t)len)== -1)
214                         logerr("%s: control_queue", __func__);
215         }
216 }
217
218 pid_t
219 ps_ctl_start(struct dhcpcd_ctx *ctx)
220 {
221         int data_fd[2], listen_fd[2];
222         pid_t pid;
223
224         if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, data_fd) == -1 ||
225             xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, listen_fd) == -1)
226                 return -1;
227 #ifdef PRIVSEP_RIGHTS
228         if (ps_rights_limit_fdpair(data_fd) == -1 ||
229             ps_rights_limit_fdpair(listen_fd) == -1)
230                 return -1;
231 #endif
232
233         pid = ps_dostart(ctx, &ctx->ps_control_pid, &ctx->ps_control_fd,
234             ps_ctl_recvmsg, ps_ctl_dodispatch, ctx,
235             ps_ctl_startcb, NULL,
236             PSF_DROPPRIVS);
237
238         if (pid == -1)
239                 return -1;
240         else if (pid != 0) {
241                 ctx->ps_control_data_fd = data_fd[1];
242                 close(data_fd[0]);
243                 ctx->ps_control = control_new(ctx,
244                     listen_fd[1], FD_SENDLEN | FD_LISTEN);
245                 if (ctx->ps_control == NULL)
246                         return -1;
247                 close(listen_fd[0]);
248                 return pid;
249         }
250
251         ctx->ps_control_data_fd = data_fd[0];
252         close(data_fd[1]);
253         if (eloop_event_add(ctx->eloop, ctx->ps_control_data_fd,
254             ps_ctl_recv, ctx) == -1)
255                 return -1;
256
257         ctx->ps_control = control_new(ctx,
258             listen_fd[0], 0);
259         close(listen_fd[1]);
260         if (ctx->ps_control == NULL)
261                 return -1;
262         if (eloop_event_add(ctx->eloop, ctx->ps_control->fd,
263             ps_ctl_listen, ctx) == -1)
264                 return -1;
265
266         ps_entersandbox("stdio inet", NULL);
267         return 0;
268 }
269
270 int
271 ps_ctl_stop(struct dhcpcd_ctx *ctx)
272 {
273
274         return ps_dostop(ctx, &ctx->ps_control_pid, &ctx->ps_control_fd);
275 }
276
277 ssize_t
278 ps_ctl_sendargs(struct fd_list *fd, void *data, size_t len)
279 {
280         struct dhcpcd_ctx *ctx = fd->ctx;
281
282         if (ctx->ps_control_client != NULL && ctx->ps_control_client != fd)
283                 logerrx("%s: cannot deal with another client", __func__);
284         ctx->ps_control_client = fd;
285         return ps_sendcmd(ctx, ctx->ps_control_fd, PS_CTL,
286             fd->flags & FD_UNPRIV ? PS_CTL_UNPRIV : PS_CTL_PRIV,
287             data, len);
288 }
289
290 ssize_t
291 ps_ctl_sendeof(struct fd_list *fd)
292 {
293         struct dhcpcd_ctx *ctx = fd->ctx;
294
295         return ps_sendcmd(ctx, ctx->ps_control_fd, PS_CTL_EOF, 0, NULL, 0);
296 }