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