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