1 /* SPDX-License-Identifier: BSD-2-Clause */ 2 /* 3 * Privilege Separation for dhcpcd, BSD driver 4 * Copyright (c) 2006-2023 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 <sys/ioctl.h> 30 #include <sys/types.h> 31 #include <sys/sysctl.h> 32 33 /* Need these for filtering the ioctls */ 34 #include <arpa/inet.h> 35 #include <net/if.h> 36 #include <netinet/if_ether.h> 37 #include <netinet/in.h> 38 #include <netinet6/in6_var.h> 39 #include <netinet6/nd6.h> 40 #ifdef __NetBSD__ 41 #include <netinet/if_ether.h> 42 #include <net/if_vlanvar.h> /* Needs netinet/if_ether.h */ 43 #elif defined(__DragonFly__) 44 #include <net/vlan/if_vlan_var.h> 45 #else 46 #include <net/if_vlan_var.h> 47 #endif 48 #ifdef __DragonFly__ 49 # include <netproto/802_11/ieee80211_ioctl.h> 50 #else 51 # include <net80211/ieee80211.h> 52 # include <net80211/ieee80211_ioctl.h> 53 #endif 54 55 #include <errno.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <unistd.h> 59 60 #include "dhcpcd.h" 61 #include "if.h" 62 #include "logerr.h" 63 #include "privsep.h" 64 65 static ssize_t 66 ps_root_doioctldom(struct dhcpcd_ctx *ctx, int domain, unsigned long req, void *data, size_t len) 67 { 68 #if defined(INET6) || (defined(SIOCALIFADDR) && defined(IFLR_ACTIVE)) 69 struct priv *priv = (struct priv *)ctx->priv; 70 #endif 71 int s; 72 73 switch(domain) { 74 #ifdef INET 75 case PF_INET: 76 s = ctx->pf_inet_fd; 77 break; 78 #endif 79 #ifdef INET6 80 case PF_INET6: 81 s = priv->pf_inet6_fd; 82 break; 83 #endif 84 #if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */ 85 case PF_LINK: 86 s = priv->pf_link_fd; 87 break; 88 #endif 89 default: 90 errno = EPFNOSUPPORT; 91 return -1; 92 } 93 94 /* Only allow these ioctls */ 95 switch(req) { 96 #ifdef SIOCGIFDATA 97 case SIOCGIFDATA: /* FALLTHROUGH */ 98 #endif 99 #ifdef SIOCG80211NWID 100 case SIOCG80211NWID: /* FALLTHROUGH */ 101 #endif 102 #ifdef SIOCGETVLAN 103 case SIOCGETVLAN: /* FALLTHROUGH */ 104 #endif 105 #ifdef SIOCIFAFATTACH 106 case SIOCIFAFATTACH: /* FALLTHROUGH */ 107 #endif 108 #ifdef SIOCSIFXFLAGS 109 case SIOCSIFXFLAGS: /* FALLTHROUGH */ 110 #endif 111 #ifdef SIOCSIFINFO_FLAGS 112 case SIOCSIFINFO_FLAGS: /* FALLTHROUGH */ 113 #endif 114 #ifdef SIOCSRTRFLUSH_IN6 115 case SIOCSRTRFLUSH_IN6: /* FALLTHROUGH */ 116 case SIOCSPFXFLUSH_IN6: /* FALLTHROUGH */ 117 #endif 118 #if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) 119 case SIOCALIFADDR: /* FALLTHROUGH */ 120 case SIOCDLIFADDR: /* FALLTHROUGH */ 121 #else 122 case SIOCSIFLLADDR: /* FALLTHROUGH */ 123 #endif 124 #ifdef SIOCSIFINFO_IN6 125 case SIOCSIFINFO_IN6: /* FALLTHROUGH */ 126 #endif 127 case SIOCAIFADDR_IN6: /* FALLTHROUGH */ 128 case SIOCDIFADDR_IN6: 129 break; 130 default: 131 errno = EPERM; 132 return -1; 133 } 134 135 return ioctl(s, req, data, len); 136 } 137 138 static ssize_t 139 ps_root_doroute(struct dhcpcd_ctx *ctx, void *data, size_t len) 140 { 141 142 return write(ctx->link_fd, data, len); 143 } 144 145 #if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE) 146 static ssize_t 147 ps_root_doindirectioctl(struct dhcpcd_ctx *ctx, 148 unsigned long req, void *data, size_t len) 149 { 150 char *p = data; 151 struct ifreq ifr = { .ifr_flags = 0 }; 152 153 /* ioctl filtering is done in ps_root_doioctldom */ 154 155 if (len < IFNAMSIZ + 1) { 156 errno = EINVAL; 157 return -1; 158 } 159 160 strlcpy(ifr.ifr_name, p, IFNAMSIZ); 161 len -= IFNAMSIZ; 162 memmove(data, p + IFNAMSIZ, len); 163 ifr.ifr_data = data; 164 165 return ps_root_doioctldom(ctx, PF_INET, req, &ifr, sizeof(ifr)); 166 } 167 #endif 168 169 #ifdef HAVE_PLEDGE 170 static ssize_t 171 ps_root_doifignoregroup(struct dhcpcd_ctx *ctx, void *data, size_t len) 172 { 173 174 if (len == 0 || ((const char *)data)[len - 1] != '\0') { 175 errno = EINVAL; 176 return -1; 177 } 178 179 return if_ignoregroup(ctx->pf_inet_fd, data); 180 } 181 #endif 182 183 #ifdef HAVE_CAPSICUM 184 static ssize_t 185 ps_root_dosysctl(unsigned long flags, 186 void *data, size_t len, void **rdata, size_t *rlen) 187 { 188 char *p = data, *e = p + len; 189 int name[10]; 190 unsigned int namelen; 191 void *oldp; 192 size_t *oldlenp, oldlen, nlen; 193 void *newp; 194 size_t newlen; 195 int err; 196 197 if (sizeof(namelen) >= len) { 198 errno = EINVAL; 199 return -1; 200 } 201 memcpy(&namelen, p, sizeof(namelen)); 202 p += sizeof(namelen); 203 nlen = sizeof(*name) * namelen; 204 if (namelen > __arraycount(name)) { 205 errno = ENOBUFS; 206 return -1; 207 } 208 if (p + nlen > e) { 209 errno = EINVAL; 210 return -1; 211 } 212 memcpy(name, p, nlen); 213 p += nlen; 214 if (p + sizeof(oldlen) > e) { 215 errno = EINVAL; 216 return -1; 217 } 218 memcpy(&oldlen, p, sizeof(oldlen)); 219 p += sizeof(oldlen); 220 if (p + sizeof(newlen) > e) { 221 errno = EINVAL; 222 return -1; 223 } 224 memcpy(&newlen, p, sizeof(newlen)); 225 p += sizeof(newlen); 226 if (p + newlen > e) { 227 errno = EINVAL; 228 return -1; 229 } 230 newp = newlen ? p : NULL; 231 232 if (flags & PS_SYSCTL_OLEN) { 233 *rlen = sizeof(oldlen) + oldlen; 234 *rdata = malloc(*rlen); 235 if (*rdata == NULL) 236 return -1; 237 oldlenp = (size_t *)*rdata; 238 *oldlenp = oldlen; 239 if (flags & PS_SYSCTL_ODATA) 240 oldp = (char *)*rdata + sizeof(oldlen); 241 else 242 oldp = NULL; 243 } else { 244 oldlenp = NULL; 245 oldp = NULL; 246 } 247 248 err = sysctl(name, namelen, oldp, oldlenp, newp, newlen); 249 return err; 250 } 251 #endif 252 253 ssize_t 254 ps_root_os(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg, 255 void **rdata, size_t *rlen, bool *free_rdata) 256 { 257 struct iovec *iov = msg->msg_iov; 258 void *data = iov->iov_base; 259 size_t len = iov->iov_len; 260 ssize_t err; 261 262 switch (psm->ps_cmd) { 263 case PS_IOCTLLINK: 264 err = ps_root_doioctldom(ctx, PF_LINK, psm->ps_flags, data, len); 265 break; 266 case PS_IOCTL6: 267 err = ps_root_doioctldom(ctx, PF_INET6, psm->ps_flags, data, len); 268 break; 269 case PS_ROUTE: 270 return ps_root_doroute(ctx, data, len); 271 #if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE) 272 case PS_IOCTLINDIRECT: 273 err = ps_root_doindirectioctl(ctx, psm->ps_flags, data, len); 274 break; 275 #endif 276 #ifdef HAVE_PLEDGE 277 case PS_IFIGNOREGRP: 278 return ps_root_doifignoregroup(ctx, data, len); 279 #endif 280 #ifdef HAVE_CAPSICUM 281 case PS_SYSCTL: 282 *free_rdata = true; 283 return ps_root_dosysctl(psm->ps_flags, data, len, rdata, rlen); 284 #else 285 UNUSED(free_rdata); 286 #endif 287 default: 288 errno = ENOTSUP; 289 return -1; 290 } 291 292 if (err != -1) { 293 *rdata = data; 294 *rlen = len; 295 } 296 return err; 297 } 298 299 static ssize_t 300 ps_root_ioctldom(struct dhcpcd_ctx *ctx, uint16_t domain, unsigned long request, 301 void *data, size_t len) 302 { 303 304 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), domain, 305 request, data, len) == -1) 306 return -1; 307 return ps_root_readerror(ctx, data, len); 308 } 309 310 ssize_t 311 ps_root_ioctllink(struct dhcpcd_ctx *ctx, unsigned long request, 312 void *data, size_t len) 313 { 314 315 return ps_root_ioctldom(ctx, PS_IOCTLLINK, request, data, len); 316 } 317 318 ssize_t 319 ps_root_ioctl6(struct dhcpcd_ctx *ctx, unsigned long request, 320 void *data, size_t len) 321 { 322 323 return ps_root_ioctldom(ctx, PS_IOCTL6, request, data, len); 324 } 325 326 ssize_t 327 ps_root_route(struct dhcpcd_ctx *ctx, void *data, size_t len) 328 { 329 330 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_ROUTE, 0, data, len) == -1) 331 return -1; 332 return ps_root_readerror(ctx, data, len); 333 } 334 335 #if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE) 336 ssize_t 337 ps_root_indirectioctl(struct dhcpcd_ctx *ctx, unsigned long request, 338 const char *ifname, void *data, size_t len) 339 { 340 char buf[PS_BUFLEN]; 341 342 if (IFNAMSIZ + len > sizeof(buf)) { 343 errno = ENOBUFS; 344 return -1; 345 } 346 347 strlcpy(buf, ifname, IFNAMSIZ); 348 memcpy(buf + IFNAMSIZ, data, len); 349 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_IOCTLINDIRECT, 350 request, buf, IFNAMSIZ + len) == -1) 351 return -1; 352 return ps_root_readerror(ctx, data, len); 353 } 354 #endif 355 356 #ifdef HAVE_PLEDGE 357 ssize_t 358 ps_root_ifignoregroup(struct dhcpcd_ctx *ctx, const char *ifname) 359 { 360 361 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_IFIGNOREGRP, 0, 362 ifname, strlen(ifname) + 1) == -1) 363 return -1; 364 return ps_root_readerror(ctx, NULL, 0); 365 } 366 #endif 367 368 #ifdef HAVE_CAPSICUM 369 ssize_t 370 ps_root_sysctl(struct dhcpcd_ctx *ctx, 371 const int *name, unsigned int namelen, 372 void *oldp, size_t *oldlenp, const void *newp, size_t newlen) 373 { 374 char buf[PS_BUFLEN], *p = buf; 375 unsigned long flags = 0; 376 size_t olen = (oldp && oldlenp) ? *oldlenp : 0, nolen; 377 378 if (sizeof(namelen) + (sizeof(*name) * namelen) + 379 sizeof(oldlenp) + 380 sizeof(newlen) + newlen > sizeof(buf)) 381 { 382 errno = ENOBUFS; 383 return -1; 384 } 385 386 if (oldlenp) 387 flags |= PS_SYSCTL_OLEN; 388 if (oldp) 389 flags |= PS_SYSCTL_ODATA; 390 memcpy(p, &namelen, sizeof(namelen)); 391 p += sizeof(namelen); 392 memcpy(p, name, sizeof(*name) * namelen); 393 p += sizeof(*name) * namelen; 394 memcpy(p, &olen, sizeof(olen)); 395 p += sizeof(olen); 396 memcpy(p, &newlen, sizeof(newlen)); 397 p += sizeof(newlen); 398 if (newlen) { 399 memcpy(p, newp, newlen); 400 p += newlen; 401 } 402 403 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_SYSCTL, 404 flags, buf, (size_t)(p - buf)) == -1) 405 return -1; 406 407 if (ps_root_readerror(ctx, buf, sizeof(buf)) == -1) 408 return -1; 409 410 p = buf; 411 memcpy(&nolen, p, sizeof(nolen)); 412 p += sizeof(nolen); 413 if (oldlenp) { 414 *oldlenp = nolen; 415 if (oldp && nolen <= olen) 416 memcpy(oldp, p, nolen); 417 } 418 419 return 0; 420 } 421 #endif 422