Line data Source code
1 : /* $OpenBSD: kern_unveil.c,v 1.14 2018/08/28 02:51:55 beck Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2017-2018 Bob Beck <beck@openbsd.org>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : #include <sys/param.h>
20 :
21 : #include <sys/mount.h>
22 : #include <sys/proc.h>
23 : #include <sys/namei.h>
24 : #include <sys/pool.h>
25 : #include <sys/vnode.h>
26 : #include <sys/ktrace.h>
27 : #include <sys/types.h>
28 : #include <sys/malloc.h>
29 : #include <sys/tree.h>
30 :
31 : #include <sys/conf.h>
32 : #include <sys/syscall.h>
33 : #include <sys/syscallargs.h>
34 : #include <sys/systm.h>
35 :
36 : #include <sys/pledge.h>
37 :
38 : /* #define DEBUG_UNVEIL */
39 :
40 : #define UNVEIL_MAX_VNODES 128
41 : #define UNVEIL_MAX_NAMES 128
42 :
43 : static inline int
44 0 : unvname_compare(const struct unvname *n1, const struct unvname *n2)
45 : {
46 0 : if (n1->un_namesize == n2->un_namesize)
47 0 : return (memcmp(n1->un_name, n2->un_name, n1->un_namesize));
48 : else
49 0 : return (n1->un_namesize - n2->un_namesize);
50 0 : }
51 :
52 : struct unvname *
53 0 : unvname_new(const char *name, size_t size, u_char flags)
54 : {
55 0 : struct unvname *ret = malloc(sizeof(struct unvname), M_PROC, M_WAITOK);
56 0 : ret->un_name = malloc(size, M_PROC, M_WAITOK);
57 0 : memcpy(ret->un_name, name, size);
58 0 : ret->un_namesize = size;
59 0 : ret->un_flags = flags;
60 0 : return ret;
61 : }
62 :
63 : void
64 0 : unveil_free_traversed_vnodes(struct nameidata *ndp)
65 : {
66 0 : if (ndp->ni_tvpsize) {
67 : size_t i;
68 :
69 0 : for (i = 0; i < ndp->ni_tvpend; i++)
70 0 : vrele(ndp->ni_tvp[i]); /* ref for being in list */
71 0 : free(ndp->ni_tvp, M_PROC, ndp->ni_tvpsize * sizeof(struct vnode *));
72 0 : ndp->ni_tvpsize = 0;
73 0 : ndp->ni_tvpend = 0;
74 0 : }
75 0 : }
76 :
77 : void
78 0 : unveil_save_traversed_vnode(struct nameidata *ndp, struct vnode *vp)
79 : {
80 0 : if (ndp->ni_tvpsize == 0) {
81 0 : ndp->ni_tvp = mallocarray(MAXPATHLEN, sizeof(struct vnode *),
82 : M_PROC, M_WAITOK);
83 0 : ndp->ni_tvpsize = MAXPATHLEN;
84 0 : }
85 : /* This should be limited by MAXPATHLEN on a single lookup */
86 0 : KASSERT(ndp->ni_tvpsize > ndp->ni_tvpend);
87 0 : vref(vp); /* ref for being in the list */
88 0 : ndp->ni_tvp[ndp->ni_tvpend++] = vp;
89 0 : }
90 :
91 : void
92 0 : unvname_delete(struct unvname *name)
93 : {
94 0 : free(name->un_name, M_PROC, name->un_namesize);;
95 0 : free(name, M_PROC, sizeof(struct unvname));
96 0 : }
97 :
98 0 : RBT_PROTOTYPE(unvname_rbt, unvname, un_rbt, unvname_compare);
99 0 : RBT_GENERATE(unvname_rbt, unvname, un_rbt, unvname_compare);
100 :
101 : int
102 0 : unveil_delete_names(struct unveil *uv)
103 : {
104 : struct unvname *unvn, *next;
105 : int ret = 0;
106 :
107 0 : rw_enter_write(&uv->uv_lock);
108 0 : RBT_FOREACH_SAFE(unvn, unvname_rbt, &uv->uv_names, next) {
109 0 : RBT_REMOVE(unvname_rbt, &uv->uv_names, unvn);
110 0 : unvname_delete(unvn);
111 0 : ret++;
112 : }
113 0 : rw_exit_write(&uv->uv_lock);
114 : #ifdef DEBUG_UNVEIL
115 : printf("deleted %d names\n", ret);
116 : #endif
117 0 : return ret;
118 : }
119 :
120 : void
121 0 : unveil_add_name(struct unveil *uv, char *name, u_char flags)
122 : {
123 : struct unvname *unvn;
124 :
125 0 : rw_enter_write(&uv->uv_lock);
126 0 : unvn = unvname_new(name, strlen(name) + 1, flags);
127 0 : RBT_INSERT(unvname_rbt, &uv->uv_names, unvn);
128 0 : rw_exit_write(&uv->uv_lock);
129 : #ifdef DEBUG_UNVEIL
130 : printf("added name %s underneath vnode %p\n", name, uv->uv_vp);
131 : #endif
132 0 : }
133 :
134 : struct unvname *
135 0 : unveil_namelookup(struct unveil *uv, char *name)
136 : {
137 0 : struct unvname n, *ret = NULL;
138 :
139 0 : rw_enter_read(&uv->uv_lock);
140 :
141 : #ifdef DEBUG_UNVEIL
142 : printf("unveil_namelookup: looking up name %s (%p) in vnode %p\n",
143 : name, name, uv->uv_vp);
144 : #endif
145 :
146 0 : KASSERT(uv->uv_vp != NULL);
147 :
148 0 : n.un_name = name;
149 0 : n.un_namesize = strlen(name) + 1;
150 :
151 0 : ret = RBT_FIND(unvname_rbt, &uv->uv_names, &n);
152 :
153 0 : rw_exit_read(&uv->uv_lock);
154 :
155 : #ifdef DEBUG_UNVEIL
156 : if (ret == NULL)
157 : printf("unveil_namelookup: no match for name %s in vnode %p\n",
158 : name, uv->uv_vp);
159 : else
160 : printf("unveil_namelookup: matched name %s in vnode %p\n",
161 : name, uv->uv_vp);
162 : #endif
163 0 : return ret;
164 0 : }
165 :
166 : void
167 0 : unveil_destroy(struct process *ps)
168 : {
169 : size_t i;
170 :
171 0 : for (i = 0; ps->ps_uvpaths != NULL && i < ps->ps_uvvcount; i++) {
172 0 : struct unveil *uv = ps->ps_uvpaths + i;
173 :
174 0 : struct vnode *vp = uv->uv_vp;
175 : /* skip any vnodes zapped by unveil_removevnode */
176 0 : if (vp != NULL) {
177 0 : vp->v_uvcount--;
178 : #ifdef DEBUG_UNVEIL
179 : printf("unveil: %s(%d): removing vnode %p uvcount %d "
180 : "in position %ld\n",
181 : ps->ps_comm, ps->ps_pid, vp, vp->v_uvcount, i);
182 : #endif
183 0 : vrele(vp);
184 0 : }
185 0 : ps->ps_uvncount -= unveil_delete_names(uv);
186 0 : uv->uv_vp = NULL;
187 0 : uv->uv_flags = 0;
188 : }
189 :
190 0 : KASSERT(ps->ps_uvncount == 0);
191 0 : free(ps->ps_uvpaths, M_PROC, UNVEIL_MAX_VNODES *
192 : sizeof(struct unveil));
193 0 : ps->ps_uvvcount = 0;
194 0 : ps->ps_uvpaths = NULL;
195 0 : }
196 :
197 : void
198 0 : unveil_copy(struct process *parent, struct process *child)
199 : {
200 : size_t i;
201 :
202 0 : if (parent->ps_uvvcount == 0)
203 0 : return;
204 :
205 0 : child->ps_uvpaths = mallocarray(UNVEIL_MAX_VNODES, sizeof(struct unveil),
206 : M_PROC, M_WAITOK|M_ZERO);
207 :
208 0 : child->ps_uvncount = 0;
209 0 : for (i = 0; parent->ps_uvpaths != NULL && i < parent->ps_uvvcount;
210 0 : i++) {
211 0 : struct unveil *from = parent->ps_uvpaths + i;
212 0 : struct unveil *to = child->ps_uvpaths + i;
213 : struct unvname *unvn, *next;
214 :
215 0 : to->uv_vp = from->uv_vp;
216 0 : if (to->uv_vp != NULL) {
217 0 : vref(to->uv_vp);
218 0 : to->uv_vp->v_uvcount++;
219 0 : }
220 0 : rw_init(&to->uv_lock, "unveil");
221 0 : RBT_INIT(unvname_rbt, &to->uv_names);
222 0 : rw_enter_read(&from->uv_lock);
223 0 : RBT_FOREACH_SAFE(unvn, unvname_rbt, &from->uv_names, next) {
224 0 : unveil_add_name(&child->ps_uvpaths[i], unvn->un_name,
225 0 : unvn->un_flags);
226 0 : child->ps_uvncount++;
227 : }
228 0 : rw_exit_read(&from->uv_lock);
229 0 : to->uv_flags = from->uv_flags;
230 : }
231 0 : child->ps_uvvcount = parent->ps_uvvcount;
232 0 : if (parent->ps_uvpcwd)
233 0 : child->ps_uvpcwd = child->ps_uvpaths +
234 0 : (parent->ps_uvpcwd - parent->ps_uvpaths);
235 0 : child->ps_uvpcwdgone = parent->ps_uvpcwdgone;
236 0 : child->ps_uvdone = parent->ps_uvdone;
237 0 : child->ps_uvshrink = parent->ps_uvshrink;
238 0 : }
239 :
240 : struct unveil *
241 0 : unveil_lookup(struct vnode *vp, struct proc *p)
242 : {
243 0 : struct process *pr = p->p_p;
244 0 : struct unveil *uv = pr->ps_uvpaths;
245 : ssize_t l, r;
246 :
247 0 : if (vp->v_uvcount == 0)
248 0 : return NULL;
249 :
250 : /*
251 : * shrink if told to do so to remove dead vnodes.
252 : */
253 0 : if (pr->ps_uvshrink) {
254 : size_t i = 0, j;
255 :
256 0 : while (i < pr->ps_uvvcount) {
257 0 : if (uv[i].uv_vp == NULL) {
258 0 : pr->ps_uvncount -= unveil_delete_names(&uv[i]);
259 0 : for (j = i + 1; j < pr->ps_uvvcount; j++)
260 0 : uv[j - 1] = uv[j];
261 0 : pr->ps_uvvcount--;
262 0 : }
263 0 : i++;
264 : }
265 0 : pr->ps_uvshrink = 0;
266 0 : }
267 :
268 0 : if (pr->ps_uvvcount == 0)
269 0 : return NULL;
270 :
271 : /* clear the cwd unveil when we .. past it */
272 0 : if (pr->ps_uvpcwd && (vp == pr->ps_uvpcwd->uv_vp)) {
273 : #ifdef DEBUG_UNVEIL
274 : printf("unveil: %s(%d): nuking cwd traversing vnode %p\n",
275 : p->p_p->ps_comm, p->p_p->ps_pid, vp);
276 : #endif
277 0 : p->p_p->ps_uvpcwd = NULL;
278 0 : p->p_p->ps_uvpcwdgone = 0;
279 0 : }
280 : #ifdef DEBUG_UNVEIL
281 : else {
282 : if (pr->ps_uvpcwd) {
283 : printf("unveil: %s(%d): did not nuke cwd because %p != %p\n",
284 : p->p_p->ps_comm, p->p_p->ps_pid, vp, pr->ps_uvpcwd->uv_vp);
285 : } else
286 : printf("unveil: %s(%d): cwd is null\n",
287 : p->p_p->ps_comm, p->p_p->ps_pid);
288 : }
289 : #endif
290 :
291 : l = 0;
292 0 : r = pr->ps_uvvcount - 1;
293 0 : while (l <= r) {
294 0 : size_t m = l + (r - l)/2;
295 : #ifdef DEBUG_UNVEIL
296 : printf("unveil: checking vnode %p vs. unveil vnode %p\n",
297 : vp, uv[m].uv_vp);
298 : #endif
299 0 : if (vp == uv[m].uv_vp) {
300 0 : KASSERT(uv[m].uv_vp->v_uvcount > 0);
301 0 : KASSERT(uv[m].uv_vp->v_usecount > 0);
302 0 : return &uv[m];
303 : }
304 0 : if (vp > uv[m].uv_vp)
305 0 : l = m + 1;
306 : else
307 0 : r = m - 1;
308 0 : }
309 0 : return NULL;
310 0 : }
311 :
312 : int
313 0 : unveil_parsepermissions(const char *permissions, u_char *perms)
314 : {
315 : size_t i = 0;
316 : char c;
317 :
318 0 : *perms = 0;
319 0 : while ((c = permissions[i++]) != '\0') {
320 0 : switch (c) {
321 : case 'r':
322 0 : *perms |= UNVEIL_READ;
323 0 : break;
324 : case 'w':
325 0 : *perms |= UNVEIL_WRITE;
326 0 : break;
327 : case 'x':
328 0 : *perms |= UNVEIL_EXEC;
329 0 : break;
330 : case 'c':
331 0 : *perms |= UNVEIL_CREATE;
332 0 : break;
333 : default:
334 0 : return -1;
335 : }
336 : }
337 0 : return 0;
338 0 : }
339 :
340 : int
341 0 : unveil_setflags(u_char *flags, u_char nflags)
342 : {
343 : #if 0
344 : if (((~(*flags)) & nflags) != 0) {
345 : #ifdef DEBUG_UNVEIL
346 : printf("Flags escalation %llX -> %llX\n", *flags, nflags);
347 : #endif
348 : return 1;
349 : }
350 : #endif
351 0 : *flags = nflags;
352 0 : return 1;
353 : }
354 :
355 : struct unveil *
356 0 : unveil_add_vnode(struct process *pr, struct vnode *vp)
357 : {
358 : struct unveil *uv = NULL;
359 : ssize_t i;
360 :
361 0 : KASSERT(pr->ps_uvvcount < UNVEIL_MAX_VNODES);
362 :
363 0 : for (i = pr->ps_uvvcount;
364 0 : i > 0 && pr->ps_uvpaths[i - 1].uv_vp > vp;
365 0 : i--)
366 0 : pr->ps_uvpaths[i] = pr->ps_uvpaths[i - 1];
367 :
368 : uv = &pr->ps_uvpaths[i];
369 0 : rw_init(&uv->uv_lock, "unveil");
370 0 : RBT_INIT(unvname_rbt, &uv->uv_names);
371 0 : uv->uv_vp = vp;
372 : /*
373 : * Added vnodes are added with the UNVEIL_INSPECT flag
374 : * to allow operations such as access and stat. This lets
375 : * TOCTOU fans that call access on all components of
376 : * an unveil'ed path before the final operations
377 : * work.
378 : */
379 0 : uv->uv_flags = UNVEIL_INSPECT;
380 0 : pr->ps_uvvcount++;
381 0 : return (uv);
382 : }
383 :
384 : void
385 0 : unveil_add_traversed_vnodes(struct proc *p, struct nameidata *ndp)
386 : {
387 : struct unveil *uv;
388 :
389 0 : if (ndp->ni_tvpsize) {
390 : size_t i;
391 :
392 0 : for (i = 0; i < ndp->ni_tvpend; i++) {
393 0 : struct vnode *vp = ndp->ni_tvp[i];
394 0 : if (unveil_lookup(vp, p) == NULL) {
395 0 : vref(vp);
396 0 : vp->v_uvcount++;
397 0 : uv = unveil_add_vnode(p->p_p, vp);
398 0 : }
399 : }
400 0 : }
401 0 : }
402 :
403 : int
404 0 : unveil_add(struct proc *p, struct nameidata *ndp, const char *permissions)
405 : {
406 0 : struct process *pr = p->p_p;
407 : struct vnode *vp;
408 : struct unveil *uv;
409 : int directory_add;
410 : int ret = EINVAL;
411 0 : u_char flags;
412 :
413 0 : KASSERT(ISSET(ndp->ni_cnd.cn_flags, HASBUF)); /* must have SAVENAME */
414 :
415 0 : if (unveil_parsepermissions(permissions, &flags) == -1)
416 : goto done;
417 :
418 0 : if (pr->ps_uvpaths == NULL) {
419 0 : pr->ps_uvpaths = mallocarray(UNVEIL_MAX_VNODES,
420 : sizeof(struct unveil), M_PROC, M_WAITOK|M_ZERO);
421 0 : }
422 :
423 0 : if ((pr->ps_uvvcount + ndp->ni_tvpend) >= UNVEIL_MAX_VNODES ||
424 0 : pr->ps_uvncount >= UNVEIL_MAX_NAMES) {
425 : ret = E2BIG;
426 0 : goto done;
427 : }
428 :
429 : /* Are we a directory? or something else */
430 0 : directory_add = ndp->ni_vp != NULL && ndp->ni_vp->v_type == VDIR;
431 :
432 0 : if (directory_add)
433 0 : vp = ndp->ni_vp;
434 : else
435 0 : vp = ndp->ni_dvp;
436 :
437 0 : KASSERT(vp->v_type == VDIR);
438 0 : vref(vp);
439 0 : vp->v_uvcount++;
440 0 : if ((uv = unveil_lookup(vp, p)) != NULL) {
441 : /*
442 : * We already have unveiled this directory
443 : * vnode
444 : */
445 0 : vp->v_uvcount--;
446 0 : vrele(vp);
447 :
448 : /*
449 : * If we are adding a directory which was already
450 : * unveiled containing only specific terminals,
451 : * unrestrict it.
452 : */
453 0 : if (directory_add) {
454 : #ifdef DEBUG_UNVEIL
455 : printf("unveil: %s(%d): updating directory vnode %p"
456 : " to unrestricted uvcount %d\n",
457 : pr->ps_comm, pr->ps_pid, vp, vp->v_uvcount);
458 : #endif
459 0 : if (!unveil_setflags(&uv->uv_flags, flags))
460 0 : ret = EPERM;
461 : else
462 : ret = 0;
463 : goto done;
464 : }
465 :
466 : /*
467 : * If we are adding a terminal that is already unveiled, just
468 : * replace the flags and we are done
469 : */
470 0 : if (!directory_add) {
471 : struct unvname *tname;
472 0 : if ((tname = unveil_namelookup(uv,
473 0 : ndp->ni_cnd.cn_nameptr)) != NULL) {
474 : #ifdef DEBUG_UNVEIL
475 : printf("unveil: %s(%d): changing flags for %s"
476 : "in vnode %p, uvcount %d\n",
477 : pr->ps_comm, pr->ps_pid, tname->un_name, vp,
478 : vp->v_uvcount);
479 : #endif
480 0 : if (!unveil_setflags(&tname->un_flags, flags))
481 0 : ret = EPERM;
482 : else
483 : ret = 0;
484 0 : goto done;
485 : }
486 0 : }
487 :
488 : } else {
489 : /*
490 : * New unveil involving this directory vnode.
491 : */
492 0 : uv = unveil_add_vnode(pr, vp);
493 : }
494 :
495 : /*
496 : * At this stage with have a unveil in uv with a vnode for a
497 : * directory. If the component we are adding is a directory,
498 : * we are done. Otherwise, we add the component name the name
499 : * list in uv.
500 : */
501 :
502 0 : if (directory_add) {
503 0 : uv->uv_flags = flags;
504 : ret = 0;
505 : #ifdef DEBUG_UNVEIL
506 : printf("unveil: %s(%d): added unrestricted directory vnode %p"
507 : ", uvcount %d\n",
508 : pr->ps_comm, pr->ps_pid, vp, vp->v_uvcount);
509 : #endif
510 0 : goto done;
511 : }
512 :
513 0 : unveil_add_name(uv, ndp->ni_cnd.cn_nameptr, flags);
514 0 : pr->ps_uvncount++;
515 0 : ret = 0;
516 :
517 : #ifdef DEBUG_UNVEIL
518 : printf("unveil: %s(%d): added name %s beneath %s vnode %p,"
519 : " uvcount %d\n",
520 : pr->ps_comm, pr->ps_pid, ndp->ni_cnd.cn_nameptr,
521 : uv->uv_flags ? "unrestricted" : "restricted",
522 : vp, vp->v_uvcount);
523 : #endif
524 :
525 : done:
526 0 : if (ret == 0)
527 0 : unveil_add_traversed_vnodes(p, ndp);
528 0 : unveil_free_traversed_vnodes(ndp);
529 0 : pool_put(&namei_pool, ndp->ni_cnd.cn_pnbuf);
530 0 : return ret;
531 0 : }
532 :
533 : /*
534 : * XXX this will probably change.
535 : * XXX collapse down later once debug surely unneded
536 : */
537 : int
538 0 : unveil_flagmatch(struct nameidata *ni, u_char flags)
539 : {
540 0 : if (flags == 0) {
541 : #ifdef DEBUG_UNVEIL
542 : printf("All operations forbidden for 0 flags\n");
543 : #endif
544 0 : return 0;
545 : }
546 0 : if (ni->ni_unveil & UNVEIL_READ) {
547 0 : if ((flags & UNVEIL_READ) == 0) {
548 : #ifdef DEBUG_UNVEIL
549 : printf("unveil lacks UNVEIL_READ\n");
550 : #endif
551 0 : return 0;
552 : }
553 : }
554 0 : if (ni->ni_unveil & UNVEIL_WRITE) {
555 0 : if ((flags & UNVEIL_WRITE) == 0) {
556 : #ifdef DEBUG_UNVEIL
557 : printf("unveil lacks UNVEIL_WRITE\n");
558 : #endif
559 0 : return 0;
560 : }
561 : }
562 0 : if (ni->ni_unveil & UNVEIL_EXEC) {
563 0 : if ((flags & UNVEIL_EXEC) == 0) {
564 : #ifdef DEBUG_UNVEIL
565 : printf("unveil lacks UNVEIL_EXEC\n");
566 : #endif
567 0 : return 0;
568 : }
569 : }
570 0 : if (ni->ni_unveil & UNVEIL_CREATE) {
571 0 : if ((flags & UNVEIL_CREATE) == 0) {
572 : #ifdef DEBUG_UNVEIL
573 : printf("unveil lacks UNVEIL_CREATE\n");
574 : #endif
575 0 : return 0;
576 : }
577 : }
578 0 : if (ni->ni_unveil & UNVEIL_INSPECT) {
579 : #ifdef DEBUG_UNVEIL
580 : printf("any unveil allows UNVEIL_INSPECT\n");
581 : #endif
582 : }
583 0 : return 1;
584 0 : }
585 :
586 : /*
587 : * unveil checking - for component directories in a namei lookup.
588 : */
589 : void
590 0 : unveil_check_component(struct proc *p, struct nameidata *ni, struct vnode *dp)
591 : {
592 : struct unveil *uv = NULL;
593 :
594 0 : if (ni->ni_pledge != PLEDGE_UNVEIL) {
595 0 : if ((ni->ni_cnd.cn_flags & BYPASSUNVEIL) == 0 &&
596 0 : ! (ni->ni_cnd.cn_flags & ISDOTDOT) &&
597 0 : (uv = unveil_lookup(dp, p)) != NULL) {
598 : /* if directory flags match, it's a match */
599 0 : if (unveil_flagmatch(ni, uv->uv_flags)) {
600 0 : if (uv->uv_flags & UNVEIL_USERSET) {
601 0 : ni->ni_unveil_match = uv;
602 : #ifdef DEBUG_UNVEIL
603 : printf("unveil: %s(%d): component directory match"
604 : " for vnode %p\n",
605 : p->p_p->ps_comm, p->p_p->ps_pid, dp);
606 :
607 : #endif
608 0 : }
609 : }
610 : }
611 : } else
612 0 : unveil_save_traversed_vnode(ni, dp);
613 0 : }
614 :
615 : /*
616 : * unveil checking - only done after namei lookup has succeeded on
617 : * the last compoent of a namei lookup.
618 : */
619 : int
620 0 : unveil_check_final(struct proc *p, struct nameidata *ni)
621 : {
622 : struct unveil *uv;
623 : struct unvname *tname = NULL;
624 :
625 0 : if (ni->ni_pledge == PLEDGE_UNVEIL ||
626 0 : p->p_p->ps_uvpaths == NULL)
627 0 : return (0);
628 :
629 0 : if (ni->ni_cnd.cn_flags & BYPASSUNVEIL) {
630 : #ifdef DEBUG_UNVEIL
631 : printf("unveil: %s(%d): BYPASSUNVEIL.\n",
632 : p->p_p->ps_comm, p->p_p->ps_pid);
633 : #endif
634 0 : CLR(ni->ni_pledge, PLEDGE_STATLIE);
635 0 : return (0);
636 : }
637 0 : if (ni->ni_vp != NULL && ni->ni_vp->v_type == VDIR) {
638 0 : uv = unveil_lookup(ni->ni_vp, p);
639 0 : if (uv == NULL) {
640 : #ifdef DEBUG_UNVEIL
641 : printf("unveil: %s(%d) no match for vnode %p\n",
642 : p->p_p->ps_comm, p->p_p->ps_pid, ni->ni_vp);
643 : #endif
644 : goto done;
645 : }
646 0 : if (!unveil_flagmatch(ni, uv->uv_flags)) {
647 : #ifdef DEBUG_UNVEIL
648 : printf("unveil: %s(%d) flag mismatch for directory"
649 : " vnode %p\n",
650 : p->p_p->ps_comm, p->p_p->ps_pid, ni->ni_vp);
651 : #endif
652 0 : return EACCES;
653 : }
654 : } else {
655 0 : uv = unveil_lookup(ni->ni_dvp, p);
656 0 : if (uv == NULL) {
657 : #ifdef DEBUG_UNVEIL
658 : printf("unveil: %s(%d) no match for directory"
659 : " vnode %p\n",
660 : p->p_p->ps_comm, p->p_p->ps_pid, ni->ni_dvp);
661 : #endif
662 : goto done;
663 : }
664 0 : if ((tname = unveil_namelookup(uv, ni->ni_cnd.cn_nameptr))
665 0 : == NULL) {
666 : #ifdef DEBUG_UNVEIL
667 : printf("unveil: %s(%d) no match for terminal '%s' in "
668 : "directory vnode %p\n",
669 : p->p_p->ps_comm, p->p_p->ps_pid,
670 : ni->ni_cnd.cn_nameptr, ni->ni_dvp);
671 : #endif
672 : uv = NULL;
673 0 : goto done;
674 : }
675 0 : if (!unveil_flagmatch(ni, tname->un_flags)) {
676 : #ifdef DEBUG_UNVEIL
677 : printf("unveil: %s(%d) flag mismatch for terminal '%s'\n",
678 : p->p_p->ps_comm, p->p_p->ps_pid, tname->un_name);
679 : #endif
680 0 : return EACCES;
681 : }
682 : }
683 0 : ni->ni_unveil_match = uv;
684 : done:
685 0 : if (ni->ni_unveil_match) {
686 : #ifdef DEBUG_UNVEIL
687 : printf("unveil: %s(%d): matched \"%s\" underneath/at vnode %p\n",
688 : p->p_p->ps_comm, p->p_p->ps_pid, ni->ni_cnd.cn_nameptr,
689 : ni->ni_unveil_match->uv_vp);
690 : #endif
691 0 : return (0);
692 0 : } else if (p->p_p->ps_uvpcwd) {
693 0 : ni->ni_unveil_match = p->p_p->ps_uvpcwd;
694 : #ifdef DEBUG_UNVEIL
695 : printf("unveil: %s(%d): used cwd unveil vnode from vnode %p\n",
696 : p->p_p->ps_comm, p->p_p->ps_pid, ni->ni_unveil_match->uv_vp);
697 : #endif
698 0 : return (0);
699 0 : } else if (p->p_p->ps_uvpcwdgone) {
700 0 : printf("Corner cases make Bob cry in a corner\n");
701 0 : }
702 0 : return ENOENT;
703 0 : }
704 :
705 : /*
706 : * Scan all active processes to see if any of them have a unveil
707 : * to this vnode. If so, NULL the vnode in their unveil list,
708 : * vrele, drop the reference, and mark their unveil list
709 : * as needing to have the hole shrunk the next time the process
710 : * uses it for lookup.
711 : */
712 : void
713 0 : unveil_removevnode(struct vnode *vp)
714 : {
715 : struct process *pr;
716 :
717 0 : if (vp->v_uvcount == 0)
718 0 : return;
719 :
720 : #ifdef DEBUG_UNVEIL
721 : printf("unveil_removevnode found vnode %p with count %d\n",
722 : vp, vp->v_uvcount);
723 : #endif
724 0 : vref(vp); /* make sure it is held till we are done */
725 :
726 0 : LIST_FOREACH(pr, &allprocess, ps_list) {
727 : struct unveil * uv;
728 :
729 0 : if ((uv = unveil_lookup(vp, pr->ps_mainproc)) != NULL &&
730 0 : uv->uv_vp != NULL) {
731 0 : uv->uv_vp = NULL;
732 0 : uv->uv_flags = 0;
733 : #ifdef DEBUG_UNVEIL
734 : printf("unveil_removevnode vnode %p now count %d\n",
735 : vp, vp->v_uvcount);
736 : #endif
737 0 : pr->ps_uvshrink = 1;
738 0 : if (vp->v_uvcount > 0) {
739 0 : vrele(vp);
740 0 : vp->v_uvcount--;
741 : } else
742 0 : panic("vp %p, v_uvcount of %d should be 0",
743 : vp, vp->v_uvcount);
744 0 : }
745 : }
746 0 : KASSERT(vp->v_uvcount == 0);
747 :
748 0 : vrele(vp); /* release our ref */
749 0 : }
|