So this gets rid of unveil's PLEDGE_STAT.

Instead we use UNVEIL_INSPECT which is set by the stat and access opeerations
that are needed for realpath() type traversals that effectively call stat/access
for each component of a pathname before doing a final operation on the end. 

The intended semantic of UNVEIL_INSPECT (which is only used in the kernel) 
is to allow inspection of vnodes that are traversed on the way to an 
unveil'ed component - just like what PLEDGE_STAT did. 

This also removes the use of PLEDGE_STATLIE in unveil - theo and I had
discussed that this was probably fine in lubljana, but I never did it
then. I'll remove STATLIE later if we decide that's the way we
are going. 

Passes regress - realpath still works, etc. etc.

ok?

Index: kern/kern_pledge.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_pledge.c,v
retrieving revision 1.239
diff -u -p -u -p -r1.239 kern_pledge.c
--- kern/kern_pledge.c  2 Aug 2018 15:34:07 -0000       1.239
+++ kern/kern_pledge.c  5 Aug 2018 17:45:52 -0000
@@ -608,14 +608,14 @@ pledge_namei(struct proc *p, struct name
        switch (p->p_pledge_syscall) {
        case SYS_access:
                /* tzset() needs this. */
-               if ((ni->ni_pledge == (PLEDGE_RPATH | PLEDGE_STAT)) &&
+               if (ni->ni_pledge == PLEDGE_RPATH &&
                    strcmp(path, "/etc/localtime") == 0) {
                        ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                        return (0);
                }
 
                /* when avoiding YP mode, getpw* functions touch this */
-               if (ni->ni_pledge == (PLEDGE_RPATH | PLEDGE_STAT) &&
+               if (ni->ni_pledge == PLEDGE_RPATH &&
                    strcmp(path, "/var/run/ypbind.lock") == 0) {
                        if (p->p_p->ps_pledge & PLEDGE_GETPW) {
                                ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
@@ -713,7 +713,7 @@ pledge_namei(struct proc *p, struct name
                break;
        case SYS_readlink:
                /* Allow /etc/malloc.conf for malloc(3). */
-               if ((ni->ni_pledge == (PLEDGE_RPATH | PLEDGE_STAT)) &&
+               if ((ni->ni_pledge == PLEDGE_RPATH) &&
                    strcmp(path, "/etc/malloc.conf") == 0) {
                        ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                        return (0);
@@ -721,7 +721,7 @@ pledge_namei(struct proc *p, struct name
                break;
        case SYS_stat:
                /* DNS needs /etc/resolv.conf. */
-               if ((ni->ni_pledge == (PLEDGE_RPATH | PLEDGE_STAT)) &&
+               if ((ni->ni_pledge == PLEDGE_RPATH) &&
                    (p->p_p->ps_pledge & PLEDGE_DNS) &&
                    strcmp(path, "/etc/resolv.conf") == 0) {
                        ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
@@ -732,9 +732,9 @@ pledge_namei(struct proc *p, struct name
 
        /*
         * Ensure each flag of ni_pledge has counterpart allowing it in
-        * ps_pledge. discard PLEDGE_STAT as it is unveil(2) stuff.
+        * ps_pledge.
         */
-       if ((ni->ni_pledge & ~PLEDGE_STAT) & ~p->p_p->ps_pledge)
+       if (ni->ni_pledge & ~p->p_p->ps_pledge)
                return (pledge_fail(p, EPERM, (ni->ni_pledge & 
~p->p_p->ps_pledge)));
 
        /* continue, and check unveil if present */
Index: kern/kern_unveil.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_unveil.c,v
retrieving revision 1.11
diff -u -p -u -p -r1.11 kern_unveil.c
--- kern/kern_unveil.c  5 Aug 2018 14:23:57 -0000       1.11
+++ kern/kern_unveil.c  5 Aug 2018 17:45:52 -0000
@@ -379,13 +379,20 @@ unveil_add_vnode(struct process *pr, str
        return (uv);
 }
 
-void
+int
 unveil_add_traversed_vnodes(struct proc *p, struct nameidata *ndp)
 {
        /*
-        * add the traversed vnodes with 0 flags if they
-        * are not already present.
+        * Add the traversed vnodes with the UNVEIL_INSPECT flag
+        * if they are not already present to allow traversal
+        * operations such as access and stat. This lets
+        * TOCTOU fans that call access on all components of
+        * an unveil'ed path before the final operation
+        * work.
         */
+       int ret = 0;
+       struct unveil *uv;
+
        if (ndp->ni_tvpsize) {
                size_t i;
 
@@ -394,10 +401,15 @@ unveil_add_traversed_vnodes(struct proc 
                        if (unveil_lookup(vp, p) == NULL) {
                                vref(vp);
                                vp->v_uvcount++;
-                               unveil_add_vnode(p->p_p, vp);
+                               uv = unveil_add_vnode(p->p_p, vp);
+                               if (uv != NULL)
+                                       uv->uv_flags = UNVEIL_INSPECT;
+                               else
+                                       ret = E2BIG;
                        }
                }
        }
+       return ret;
 }
 
 int
@@ -524,7 +536,7 @@ unveil_add(struct proc *p, struct nameid
 
  done:
        if (ret == 0)
-               unveil_add_traversed_vnodes(p, ndp);
+               ret = unveil_add_traversed_vnodes(p, ndp);
        unveil_free_traversed_vnodes(ndp);
        pool_put(&namei_pool, ndp->ni_cnd.cn_pnbuf);
        return ret;
@@ -538,26 +550,11 @@ int
 unveil_flagmatch(struct nameidata *ni, u_char flags)
 {
        if (flags == 0) {
-               /* XXX Fix this, you can do it better */
-               if (ni->ni_pledge & PLEDGE_STAT) {
-#ifdef DEBUG_UNVEIL
-                       printf("allowing stat/accesss for 0 flags");
-#endif
-                       SET(ni->ni_pledge, PLEDGE_STATLIE);
-                       return 1;
-               }
 #ifdef DEBUG_UNVEIL
                printf("All operations forbidden for 0 flags\n");
 #endif
                return 0;
        }
-       if (ni->ni_pledge & PLEDGE_STAT) {
-#ifdef DEBUG_UNVEIL
-               printf("Allowing stat for nonzero flags\n");
-#endif
-               CLR(ni->ni_pledge, PLEDGE_STATLIE);
-               return 1;
-       }
        if (ni->ni_unveil & UNVEIL_READ) {
                if ((flags & UNVEIL_READ) == 0) {
 #ifdef DEBUG_UNVEIL
@@ -590,6 +587,11 @@ unveil_flagmatch(struct nameidata *ni, u
                        return 0;
                }
        }
+       if (ni->ni_unveil & UNVEIL_INSPECT) {
+#ifdef DEBUG_UNVEIL
+               printf("any unveil allows UNVEIL_INSPECT\n");
+#endif
+       }
        return 1;
 }
 
@@ -607,7 +609,7 @@ unveil_check_component(struct proc *p, s
                    (uv = unveil_lookup(dp, p)) != NULL) {
                        /* if directory flags match, it's a match */
                        if (unveil_flagmatch(ni, uv->uv_flags)) {
-                               if (uv->uv_flags) {
+                               if (uv->uv_flags & UNVEIL_RWXC) {
                                        ni->ni_unveil_match = uv;
 #ifdef DEBUG_UNVEIL
                                        printf("unveil: %s(%d): component 
directory match"
Index: kern/vfs_syscalls.c
===================================================================
RCS file: /cvs/src/sys/kern/vfs_syscalls.c,v
retrieving revision 1.301
diff -u -p -u -p -r1.301 vfs_syscalls.c
--- kern/vfs_syscalls.c 5 Aug 2018 14:23:57 -0000       1.301
+++ kern/vfs_syscalls.c 5 Aug 2018 17:45:52 -0000
@@ -1809,8 +1809,8 @@ dofaccessat(struct proc *p, int fd, cons
        }
 
        NDINITAT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, fd, path, p);
-       nd.ni_pledge = PLEDGE_RPATH | PLEDGE_STAT;
-       nd.ni_unveil = 0; /* XXX fix this when we fix PLEDGE_STAT */
+       nd.ni_pledge = PLEDGE_RPATH;
+       nd.ni_unveil = UNVEIL_INSPECT;
        if ((error = namei(&nd)) != 0)
                goto out;
        vp = nd.ni_vp;
@@ -1880,8 +1880,8 @@ dofstatat(struct proc *p, int fd, const 
 
        follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW;
        NDINITAT(&nd, LOOKUP, follow | LOCKLEAF, UIO_USERSPACE, fd, path, p);
-       nd.ni_pledge = PLEDGE_RPATH | PLEDGE_STAT;
-       nd.ni_unveil = 0;
+       nd.ni_pledge = PLEDGE_RPATH;
+       nd.ni_unveil = UNVEIL_INSPECT;
        if ((error = namei(&nd)) != 0)
                return (error);
        error = vn_stat(nd.ni_vp, &sb, p);
@@ -1989,8 +1989,8 @@ doreadlinkat(struct proc *p, int fd, con
        struct nameidata nd;
 
        NDINITAT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, fd, path, p);
-       nd.ni_pledge = PLEDGE_RPATH | PLEDGE_STAT;
-       nd.ni_unveil = 0;
+       nd.ni_pledge = PLEDGE_RPATH;
+       nd.ni_unveil = UNVEIL_INSPECT;
        if ((error = namei(&nd)) != 0)
                return (error);
        vp = nd.ni_vp;
Index: sys/namei.h
===================================================================
RCS file: /cvs/src/sys/sys/namei.h,v
retrieving revision 1.36
diff -u -p -u -p -r1.36 namei.h
--- sys/namei.h 5 Aug 2018 14:23:57 -0000       1.36
+++ sys/namei.h 5 Aug 2018 17:45:52 -0000
@@ -257,5 +257,7 @@ struct      nchstats {
 #define        UNVEIL_WRITE    0x02
 #define        UNVEIL_CREATE   0x04
 #define        UNVEIL_EXEC     0x08
+#define        UNVEIL_RWXC     0x0F
+#define        UNVEIL_INSPECT  0x80
 
 #endif /* !_SYS_NAMEI_H_ */
Index: sys/pledge.h
===================================================================
RCS file: /cvs/src/sys/sys/pledge.h,v
retrieving revision 1.37
diff -u -p -u -p -r1.37 pledge.h
--- sys/pledge.h        13 Jul 2018 09:25:23 -0000      1.37
+++ sys/pledge.h        5 Aug 2018 17:45:52 -0000
@@ -68,9 +68,8 @@
  * to track program behaviours which have been observed.
  */
 #define PLEDGE_USERSET 0x0fffffffffffffffULL
-#define PLEDGE_STAT    0x2000000000000000ULL   /* XXX this is a stat */
-#define PLEDGE_STATLIE 0x4000000000000000ULL
-#define PLEDGE_YPACTIVE        0x8000000000000000ULL   /* YP use detected and 
allowed */
+#define PLEDGE_STATLIE 0x2000000000000000ULL
+#define PLEDGE_YPACTIVE        0x4000000000000000ULL   /* YP use detected and 
allowed */
 
 #ifdef PLEDGENAMES
 static struct {




Reply via email to