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