LCOV - code coverage report
Current view: top level - kern - kern_unveil.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 304 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 27 0.0 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.13