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