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
ps_root_doioctldom(struct dhcpcd_ctx * ctx,int domain,unsigned long req,void * data,size_t len)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
ps_root_doroute(struct dhcpcd_ctx * ctx,void * data,size_t len)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
ps_root_doindirectioctl(struct dhcpcd_ctx * ctx,unsigned long req,void * data,size_t len)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
ps_root_doifignoregroup(struct dhcpcd_ctx * ctx,void * data,size_t len)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
ps_root_dosysctl(unsigned long flags,void * data,size_t len,void ** rdata,size_t * rlen)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
ps_root_os(struct dhcpcd_ctx * ctx,struct ps_msghdr * psm,struct msghdr * msg,void ** rdata,size_t * rlen,bool * free_rdata)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
ps_root_ioctldom(struct dhcpcd_ctx * ctx,uint16_t domain,unsigned long request,void * data,size_t len)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
ps_root_ioctllink(struct dhcpcd_ctx * ctx,unsigned long request,void * data,size_t len)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
ps_root_ioctl6(struct dhcpcd_ctx * ctx,unsigned long request,void * data,size_t len)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
ps_root_route(struct dhcpcd_ctx * ctx,void * data,size_t len)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
ps_root_indirectioctl(struct dhcpcd_ctx * ctx,unsigned long request,const char * ifname,void * data,size_t len)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
ps_root_ifignoregroup(struct dhcpcd_ctx * ctx,const char * ifname)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
ps_root_sysctl(struct dhcpcd_ctx * ctx,const int * name,unsigned int namelen,void * oldp,size_t * oldlenp,const void * newp,size_t newlen)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