1 /* SPDX-License-Identifier: BSD-2-Clause */ 2 /* 3 * Privilege Separation for dhcpcd, BSD driver 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 <sys/ioctl.h> 30 31 /* Need these for filtering the ioctls */ 32 #include <arpa/inet.h> 33 #include <net/if.h> 34 #include <netinet/if_ether.h> 35 #include <netinet/in.h> 36 #include <netinet6/in6_var.h> 37 #include <netinet6/nd6.h> 38 #ifdef __NetBSD__ 39 #include <netinet/if_ether.h> 40 #include <net/if_vlanvar.h> /* Needs netinet/if_ether.h */ 41 #elif defined(__DragonFly__) 42 #include <net/vlan/if_vlan_var.h> 43 #else 44 #include <net/if_vlan_var.h> 45 #endif 46 #ifdef __DragonFly__ 47 # include <netproto/802_11/ieee80211_ioctl.h> 48 #else 49 # include <net80211/ieee80211.h> 50 # include <net80211/ieee80211_ioctl.h> 51 #endif 52 53 #include <errno.h> 54 #include <string.h> 55 #include <unistd.h> 56 57 #include "dhcpcd.h" 58 #include "logerr.h" 59 #include "privsep.h" 60 61 static ssize_t 62 ps_root_doioctldom(int domain, unsigned long req, void *data, size_t len) 63 { 64 int s, err; 65 66 /* Only allow these ioctls */ 67 switch(req) { 68 #ifdef SIOCGIFDATA 69 case SIOCGIFDATA: /* FALLTHROUGH */ 70 #endif 71 #ifdef SIOCG80211NWID 72 case SIOCG80211NWID: /* FALLTHROUGH */ 73 #endif 74 #ifdef SIOCGETVLAN 75 case SIOCGETVLAN: /* FALLTHROUGH */ 76 #endif 77 #ifdef SIOCIFAFATTACH 78 case SIOCIFAFATTACH: /* FALLTHROUGH */ 79 #endif 80 #ifdef SIOCSIFXFLAGS 81 case SIOCSIFXFLAGS: /* FALLTHROUGH */ 82 #endif 83 #ifdef SIOCSIFINFO_FLAGS 84 case SIOCSIFINFO_FLAGS: /* FALLTHROUGH */ 85 #endif 86 #ifdef SIOCSRTRFLUSH_IN6 87 case SIOCSRTRFLUSH_IN6: /* FALLTHROUGH */ 88 case SIOCSPFXFLUSH_IN6: /* FALLTHROUGH */ 89 #endif 90 #if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) 91 case SIOCALIFADDR: /* FALLTHROUGH */ 92 case SIOCDLIFADDR: /* FALLTHROUGH */ 93 #else 94 case SIOCSIFLLADDR: /* FALLTHROUGH */ 95 #endif 96 #ifdef SIOCSIFINFO_IN6 97 case SIOCSIFINFO_IN6: /* FALLTHROUGH */ 98 #endif 99 case SIOCAIFADDR_IN6: /* FALLTHROUGH */ 100 case SIOCDIFADDR_IN6: 101 break; 102 default: 103 errno = EPERM; 104 return -1; 105 } 106 107 s = socket(domain, SOCK_DGRAM, 0); 108 if (s == -1) 109 return -1; 110 err = ioctl(s, req, data, len); 111 close(s); 112 return err; 113 } 114 115 static ssize_t 116 ps_root_doroute(void *data, size_t len) 117 { 118 int s; 119 ssize_t err; 120 121 s = socket(PF_ROUTE, SOCK_RAW, 0); 122 if (s != -1) 123 err = write(s, data, len); 124 else 125 err = -1; 126 if (s != -1) 127 close(s); 128 return err; 129 } 130 131 #if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE) 132 static ssize_t 133 ps_root_doindirectioctl(unsigned long req, void *data, size_t len) 134 { 135 char *p = data; 136 struct ifreq ifr = { .ifr_flags = 0 }; 137 138 /* ioctl filtering is done in ps_root_doioctldom */ 139 140 if (len < IFNAMSIZ + 1) { 141 errno = EINVAL; 142 return -1; 143 } 144 145 strlcpy(ifr.ifr_name, p, IFNAMSIZ); 146 len -= IFNAMSIZ; 147 memmove(data, p + IFNAMSIZ, len); 148 ifr.ifr_data = data; 149 150 return ps_root_doioctldom(PF_INET, req, &ifr, sizeof(ifr)); 151 } 152 #endif 153 154 #ifdef HAVE_PLEDGE 155 static ssize_t 156 ps_root_doifignoregroup(void *data, size_t len) 157 { 158 int s, err; 159 160 if (len == 0 || ((const char *)data)[len - 1] != '\0') { 161 errno = EINVAL; 162 return -1; 163 } 164 165 s = socket(PF_INET, SOCK_DGRAM, 0); 166 if (s == -1) 167 return -1; 168 err = if_ignoregroup(s, data); 169 close(s); 170 return err; 171 } 172 #endif 173 174 ssize_t 175 ps_root_os(struct ps_msghdr *psm, struct msghdr *msg, 176 void **rdata, size_t *rlen) 177 { 178 struct iovec *iov = msg->msg_iov; 179 void *data = iov->iov_base; 180 size_t len = iov->iov_len; 181 ssize_t err; 182 183 switch (psm->ps_cmd) { 184 case PS_IOCTLLINK: 185 err = ps_root_doioctldom(PF_LINK, psm->ps_flags, data, len); 186 break; 187 case PS_IOCTL6: 188 err = ps_root_doioctldom(PF_INET6, psm->ps_flags, data, len); 189 break; 190 case PS_ROUTE: 191 return ps_root_doroute(data, len); 192 #if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE) 193 case PS_IOCTLINDIRECT: 194 err = ps_root_doindirectioctl(psm->ps_flags, data, len); 195 break; 196 #endif 197 #ifdef HAVE_PLEDGE 198 case PS_IFIGNOREGRP: 199 return ps_root_doifignoregroup(data, len); 200 #endif 201 default: 202 errno = ENOTSUP; 203 return -1; 204 } 205 206 if (err != -1) { 207 *rdata = data; 208 *rlen = len; 209 } 210 return err; 211 } 212 213 static ssize_t 214 ps_root_ioctldom(struct dhcpcd_ctx *ctx, uint16_t domain, unsigned long request, 215 void *data, size_t len) 216 { 217 218 if (ps_sendcmd(ctx, ctx->ps_root_fd, domain, 219 request, data, len) == -1) 220 return -1; 221 return ps_root_readerror(ctx, data, len); 222 } 223 224 ssize_t 225 ps_root_ioctllink(struct dhcpcd_ctx *ctx, unsigned long request, 226 void *data, size_t len) 227 { 228 229 return ps_root_ioctldom(ctx, PS_IOCTLLINK, request, data, len); 230 } 231 232 ssize_t 233 ps_root_ioctl6(struct dhcpcd_ctx *ctx, unsigned long request, 234 void *data, size_t len) 235 { 236 237 return ps_root_ioctldom(ctx, PS_IOCTL6, request, data, len); 238 } 239 240 ssize_t 241 ps_root_route(struct dhcpcd_ctx *ctx, void *data, size_t len) 242 { 243 244 if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_ROUTE, 0, data, len) == -1) 245 return -1; 246 return ps_root_readerror(ctx, data, len); 247 } 248 249 #if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE) 250 ssize_t 251 ps_root_indirectioctl(struct dhcpcd_ctx *ctx, unsigned long request, 252 const char *ifname, void *data, size_t len) 253 { 254 char buf[PS_BUFLEN]; 255 256 if (IFNAMSIZ + len > sizeof(buf)) { 257 errno = ENOBUFS; 258 return -1; 259 } 260 261 strlcpy(buf, ifname, IFNAMSIZ); 262 memcpy(buf + IFNAMSIZ, data, len); 263 if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTLINDIRECT, 264 request, buf, IFNAMSIZ + len) == -1) 265 return -1; 266 return ps_root_readerror(ctx, data, len); 267 } 268 269 ssize_t 270 ps_root_ifignoregroup(struct dhcpcd_ctx *ctx, const char *ifname) 271 { 272 273 if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IFIGNOREGRP, 0, 274 ifname, strlen(ifname) + 1) == -1) 275 return -1; 276 return ps_root_readerror(ctx, NULL, 0); 277 } 278 #endif 279