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 /* Ignore SIGINT, respect PS_STOP command or SIGTERM. */ 104 if (sig == SIGINT) 105 return; 106 107 shutdown(ctx->ps_control_fd, SHUT_RDWR); 108 eloop_exit(ctx->eloop, sig == SIGTERM ? EXIT_SUCCESS : EXIT_FAILURE); 109 } 110 111 ssize_t 112 ps_ctl_handleargs(struct fd_list *fd, char *data, size_t len) 113 { 114 115 /* Make any change here in dhcpcd.c as well. */ 116 if (strncmp(data, "--version", 117 MIN(strlen("--version"), len)) == 0) { 118 return control_queue(fd, UNCONST(VERSION), 119 strlen(VERSION) + 1); 120 } else if (strncmp(data, "--getconfigfile", 121 MIN(strlen("--getconfigfile"), len)) == 0) { 122 return control_queue(fd, UNCONST(fd->ctx->cffile), 123 strlen(fd->ctx->cffile) + 1); 124 } else if (strncmp(data, "--listen", 125 MIN(strlen("--listen"), len)) == 0) { 126 fd->flags |= FD_LISTEN; 127 return 0; 128 } 129 130 if (fd->ctx->ps_control_client != NULL && 131 fd->ctx->ps_control_client != fd) 132 { 133 logerrx("%s: cannot handle another client", __func__); 134 return 0; 135 } 136 return 1; 137 } 138 139 static ssize_t 140 ps_ctl_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg) 141 { 142 struct dhcpcd_ctx *ctx = arg; 143 struct iovec *iov = msg->msg_iov; 144 struct fd_list *fd; 145 unsigned int fd_flags = FD_SENDLEN; 146 147 switch (psm->ps_flags) { 148 case PS_CTL_PRIV: 149 break; 150 case PS_CTL_UNPRIV: 151 fd_flags |= FD_UNPRIV; 152 break; 153 } 154 155 switch (psm->ps_cmd) { 156 case PS_CTL: 157 if (msg->msg_iovlen != 1) { 158 errno = EINVAL; 159 return -1; 160 } 161 if (ctx->ps_control_client != NULL) { 162 logerrx("%s: cannot handle another client", __func__); 163 return 0; 164 } 165 fd = control_new(ctx, ctx->ps_control_data_fd, fd_flags); 166 if (fd == NULL) 167 return -1; 168 ctx->ps_control_client = fd; 169 control_recvdata(fd, iov->iov_base, iov->iov_len); 170 break; 171 case PS_CTL_EOF: 172 control_free(ctx->ps_control_client); 173 break; 174 default: 175 errno = ENOTSUP; 176 return -1; 177 } 178 return 0; 179 } 180 181 static void 182 ps_ctl_dodispatch(void *arg) 183 { 184 struct dhcpcd_ctx *ctx = arg; 185 186 if (ps_recvpsmsg(ctx, ctx->ps_control_fd, ps_ctl_dispatch, ctx) == -1) 187 logerr(__func__); 188 } 189 190 static void 191 ps_ctl_recv(void *arg) 192 { 193 struct dhcpcd_ctx *ctx = arg; 194 char buf[BUFSIZ]; 195 ssize_t len; 196 197 errno = 0; 198 len = read(ctx->ps_control_data_fd, buf, sizeof(buf)); 199 if (len == -1 || len == 0) { 200 logerr("%s: read", __func__); 201 eloop_exit(ctx->eloop, EXIT_FAILURE); 202 return; 203 } 204 if (ctx->ps_control_client == NULL) /* client disconnected */ 205 return; 206 errno = 0; 207 if (control_queue(ctx->ps_control_client, buf, (size_t)len) == -1) 208 logerr("%s: control_queue", __func__); 209 } 210 211 static void 212 ps_ctl_listen(void *arg) 213 { 214 struct dhcpcd_ctx *ctx = arg; 215 char buf[BUFSIZ]; 216 ssize_t len; 217 struct fd_list *fd; 218 219 errno = 0; 220 len = read(ctx->ps_control->fd, buf, sizeof(buf)); 221 if (len == -1 || len == 0) { 222 logerr("%s: read", __func__); 223 eloop_exit(ctx->eloop, EXIT_FAILURE); 224 return; 225 } 226 227 /* Send to our listeners */ 228 TAILQ_FOREACH(fd, &ctx->control_fds, next) { 229 if (!(fd->flags & FD_LISTEN)) 230 continue; 231 if (control_queue(fd, buf, (size_t)len)== -1) 232 logerr("%s: control_queue", __func__); 233 } 234 } 235 236 pid_t 237 ps_ctl_start(struct dhcpcd_ctx *ctx) 238 { 239 int data_fd[2], listen_fd[2]; 240 pid_t pid; 241 242 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, data_fd) == -1) 243 return -1; 244 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, listen_fd) == -1) 245 return -1; 246 #ifdef PRIVSEP_RIGHTS 247 if (ps_rights_limit_fdpair(data_fd) == -1) 248 return -1; 249 if (ps_rights_limit_fdpair(listen_fd) == -1) 250 return -1; 251 #endif 252 253 pid = ps_dostart(ctx, &ctx->ps_control_pid, &ctx->ps_control_fd, 254 ps_ctl_recvmsg, ps_ctl_dodispatch, ctx, 255 ps_ctl_startcb, ps_ctl_signalcb, 256 PSF_DROPPRIVS); 257 258 if (pid == -1) 259 return -1; 260 else if (pid != 0) { 261 ctx->ps_control_data_fd = data_fd[1]; 262 close(data_fd[0]); 263 ctx->ps_control = control_new(ctx, 264 listen_fd[1], FD_SENDLEN | FD_LISTEN); 265 if (ctx->ps_control == NULL) 266 return -1; 267 close(listen_fd[0]); 268 return pid; 269 } 270 271 ctx->ps_control_data_fd = data_fd[0]; 272 close(data_fd[1]); 273 if (eloop_event_add(ctx->eloop, ctx->ps_control_data_fd, 274 ps_ctl_recv, ctx) == -1) 275 return -1; 276 277 ctx->ps_control = control_new(ctx, 278 listen_fd[0], 0); 279 close(listen_fd[1]); 280 if (ctx->ps_control == NULL) 281 return -1; 282 if (eloop_event_add(ctx->eloop, ctx->ps_control->fd, 283 ps_ctl_listen, ctx) == -1) 284 return -1; 285 286 #ifdef HAVE_CAPSICUM 287 if (cap_enter() == -1 && errno != ENOSYS) 288 logerr("%s: cap_enter", __func__); 289 #endif 290 #ifdef HAVE_PLEDGE 291 if (pledge("stdio inet", NULL) == -1) 292 logerr("%s: pledge", __func__); 293 #endif 294 return 0; 295 } 296 297 int 298 ps_ctl_stop(struct dhcpcd_ctx *ctx) 299 { 300 301 return ps_dostop(ctx, &ctx->ps_control_pid, &ctx->ps_control_fd); 302 } 303 304 ssize_t 305 ps_ctl_sendargs(struct fd_list *fd, void *data, size_t len) 306 { 307 struct dhcpcd_ctx *ctx = fd->ctx; 308 309 if (ctx->ps_control_client != NULL && ctx->ps_control_client != fd) 310 logerrx("%s: cannot deal with another client", __func__); 311 ctx->ps_control_client = fd; 312 return ps_sendcmd(ctx, ctx->ps_control_fd, PS_CTL, 313 fd->flags & FD_UNPRIV ? PS_CTL_UNPRIV : PS_CTL_PRIV, 314 data, len); 315 } 316 317 ssize_t 318 ps_ctl_sendeof(struct fd_list *fd) 319 { 320 struct dhcpcd_ctx *ctx = fd->ctx; 321 322 return ps_sendcmd(ctx, ctx->ps_control_fd, PS_CTL_EOF, 0, NULL, 0); 323 } 324