Unveiling unveil(2).
authorbeck <beck@openbsd.org>
Fri, 13 Jul 2018 09:25:22 +0000 (09:25 +0000)
committerbeck <beck@openbsd.org>
Fri, 13 Jul 2018 09:25:22 +0000 (09:25 +0000)
This brings unveil into the tree, disabled by default - Currently
this will return EPERM on all attempts to use it until we are
fully certain it is ready for people to start using, but this
now allows for others to do more tweaking and experimentation.

Still needs to send the unveil's across forks and execs before
fully enabling.

Many thanks to robert@ and deraadt@ for extensive testing.
ok deraadt@

26 files changed:
include/unistd.h
lib/libc/Symbols.list
lib/libc/hidden/unistd.h
lib/libc/sys/Makefile.inc
lib/libc/sys/pledge.2
lib/libc/sys/unveil.2 [new file with mode: 0644]
regress/sys/kern/Makefile
sys/conf/files
sys/kern/init_sysent.c
sys/kern/kern_exec.c
sys/kern/kern_exit.c
sys/kern/kern_fork.c
sys/kern/kern_pledge.c
sys/kern/kern_unveil.c [new file with mode: 0644]
sys/kern/syscalls.c
sys/kern/syscalls.master
sys/kern/vfs_lookup.c
sys/kern/vfs_subr.c
sys/kern/vfs_syscalls.c
sys/sys/namei.h
sys/sys/pledge.h
sys/sys/proc.h
sys/sys/syscall.h
sys/sys/syscallargs.h
sys/sys/vnode.h
usr.bin/kdump/ktrstruct.c

index dab9289..c9432ea 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: unistd.h,v 1.105 2017/12/12 01:12:34 deraadt Exp $ */
+/*     $OpenBSD: unistd.h,v 1.106 2018/07/13 09:25:22 beck Exp $ */
 /*     $NetBSD: unistd.h,v 1.26.4.1 1996/05/28 02:31:51 mrg Exp $      */
 
 /*-
@@ -523,6 +523,7 @@ int  swapctl(int cmd, const void *arg, int misc);
 int     syscall(int, ...);
 int     getentropy(void *, size_t);
 int     pledge(const char *, const char *);
+int     unveil(const char *, const char *);
 pid_t   __tfork_thread(const struct __tfork *, size_t, void (*)(void *),
            void *);
 #endif /* __BSD_VISIBLE */
index 63420fa..55500ae 100644 (file)
@@ -231,6 +231,7 @@ _thread_sys_umask
 _thread_sys_unlink
 _thread_sys_unlinkat
 _thread_sys_unmount
+_thread_sys_unveil
 _thread_sys_utimensat
 _thread_sys_utimes
 _thread_sys_utrace
@@ -434,6 +435,7 @@ umask
 unlink
 unlinkat
 unmount
+unveil
 utimensat
 utimes
 utrace
index bfa424f..6929516 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: unistd.h,v 1.10 2016/09/12 19:36:26 guenther Exp $    */
+/*     $OpenBSD: unistd.h,v 1.11 2018/07/13 09:25:22 beck Exp $        */
 /*
  * Copyright (c) 2015 Philip Guenther <guenther@openbsd.org>
  *
@@ -161,6 +161,7 @@ PROTO_NORMAL(ttyname_r);
 PROTO_DEPRECATED(ualarm);
 PROTO_NORMAL(unlink);
 PROTO_NORMAL(unlinkat);
+PROTO_NORMAL(unveil);
 PROTO_DEPRECATED(usleep);
 PROTO_WRAP(vfork);
 PROTO_CANCEL(write);
index 6823d74..e779206 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile.inc,v 1.154 2018/01/12 04:36:12 deraadt Exp $
+#      $OpenBSD: Makefile.inc,v 1.155 2018/07/13 09:25:22 beck Exp $
 #      $NetBSD: Makefile.inc,v 1.35 1995/10/16 23:49:07 jtc Exp $
 #      @(#)Makefile.inc        8.1 (Berkeley) 6/17/93
 
@@ -73,7 +73,7 @@ ASM=  __semctl.o __syscall.o __thrsigdivert.o \
        shmget.o shutdown.o sigaltstack.o socket.o \
        socketpair.o stat.o statfs.o swapctl.o symlink.o symlinkat.o \
        sysarch.o sysctl.o thrkill.o unlink.o unlinkat.o \
-       unmount.o utimensat.o utimes.o utrace.o
+       unmount.o unveil.o utimensat.o utimes.o utrace.o
 
 SRCS+= ${SRCS_${MACHINE_CPU}}
 .for i in ${SRCS_${MACHINE_CPU}}
@@ -195,4 +195,4 @@ MAN+=       __get_tcb.2 __thrsigdivert.2 __thrsleep.2 _exit.2 accept.2 \
        sigprocmask.2 sigreturn.2 sigsuspend.2 socket.2 \
        socketpair.2 stat.2 statfs.2 swapctl.2 symlink.2 \
        sync.2 sysarch.2 syscall.2 sysctl.2 thrkill.2 truncate.2 \
-       umask.2 unlink.2 utimes.2 utrace.2 vfork.2 wait.2 write.2
+       umask.2 unlink.2 unveil.2 utimes.2 utrace.2 vfork.2 wait.2 write.2
index ab606be..3c32586 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: pledge.2,v 1.52 2018/03/16 07:11:03 jmc Exp $
+.\" $OpenBSD: pledge.2,v 1.53 2018/07/13 09:25:22 beck Exp $
 .\"
 .\" Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: March 16 2018 $
+.Dd $Mdocdate: July 13 2018 $
 .Dt PLEDGE 2
 .Os
 .Sh NAME
@@ -553,6 +553,10 @@ Allow
 operation for statistics collection from a
 .Xr bpf 4
 device.
+.It Va unveil
+Allow
+.Xr unveil 2
+to be called.
 .It Va error
 Rather than killing the process upon violation, indicate error with
 .Er ENOSYS .
diff --git a/lib/libc/sys/unveil.2 b/lib/libc/sys/unveil.2
new file mode 100644 (file)
index 0000000..d98515d
--- /dev/null
@@ -0,0 +1,158 @@
+.\" $OpenBSD: unveil.2,v 1.1 2018/07/13 09:25:22 beck Exp $
+.\"
+.\" Copyright (c) 2018 Bob Beck <beck@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: July 13 2018 $
+.Dt UNVEIL 2
+.Os
+.Sh NAME
+.Nm unveil
+.Nd unveil parts of a restricted filesystem view
+.Sh SYNOPSIS
+.In unistd.h
+.Ft int
+.Fn unveil "const char *path" "const char *flags"
+.Sh DESCRIPTION
+The first call to
+.Nm
+removes visibility of the envire filesystem from all other
+filesystem-related system calls (
+.Xr open 2 ,
+.Xr chmod 2 ,
+.Xe rename 2,
+etc).
+except for the specified
+.Ar path.
+Subequent calls to
+.Nm
+expose additional views of the filesystem.
+.Pp
+The
+.Nm
+call is treated specially and can continue to see the filesystem for
+subsequent calls.
+.Nm
+can be locked,
+preventing further filesytem exposure by calling
+.Nm
+with two
+.Ar NULL
+arguments.
+.Xr pledge 2
+may alternatively be used
+to remove the "unveil" permission.
+.Pp
+The
+.Fa flags
+argument points to a string consisting of the following characters. 
+.Pp
+.Bl -tag -width c -offset indent -compact
+.It Dv r
+.Ar path
+hould be made  be available for read operations corresponding to
+.Xr pledge 2
+promise
+.Ar rpath .
+.It Dv w
+.Ar path
+should be available for write operations corresponding to
+.Xr pledge 2
+promise
+.Ar wpath .
+.It Dv x
+.Ar path
+should be available for execute operations corresponding to 
+.Xr pledge 2
+promise
+.Ar wpath .
+.It Dv c
+.Ar path
+should be allowed to be created and removed, corresponding to 
+.Xr pledge 2
+promise
+.Ar cpath .
+.El
+.Pp
+A
+.Ar path
+that is a directory will enable all filesystem access underneath
+.Ar path
+using
+.Ar flags
+if and only if no more specific matching
+.Fn unveil
+exists at a lower level.
+.Pp
+Attempts to access paths not allowed by
+.Nm
+will result in an error of
+.Ar EACCES
+when the
+.Ar flags
+argument does not match the attempted operation.
+.Ar ENOENT
+is returned for paths for which no
+.Nm
+flags are present.
+.Pp
+As with
+.Xr pledge 2 ,
+the use of
+.Fn unveil
+in an application will require lots of study and understanding
+of the interfaces called.
+In most cases it is best practice to unveil the directories
+in which an application makes use of files.
+It is important to consider that directory results are remembered at
+the time of a call to
+.Fn unveil .
+This means that a directory that is removed and recreated after a call to
+.Fn unveil
+will appear to not exist.
+Non directories are remembered by name within their containing directory,
+and so may be created, removed, or re-created after a call to
+.Fn unveil
+and still appear to exist.
+.Sh RETURN VALUES
+.Fn unveil
+returns 0 on success or -1 on failure.
+.Sh ERRORS
+.Bl -tag -width Er
+.It E2BIG
+The addition of
+.Ar path
+would exceed the per-process limit for pledged paths.
+.It ENOENT
+A directory in
+.Ar path
+did not exist.
+.It EINVAL
+An invalid value of
+.Ar flags
+was used.
+.It EPERM
+An attempt to add permission to
+.Ar flags
+was made, or
+.Ar path
+was not accessible, or
+.Nm
+was called after it was locked
+.El
+.Sh HISTORY
+The
+.Fn unveil
+system call first appeared in
+.Ox 6.4 .
index 5697335..18780fa 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile,v 1.72 2018/07/08 02:16:48 anton Exp $
+#      $OpenBSD: Makefile,v 1.73 2018/07/13 09:25:22 beck Exp $
 
 SUBDIR+= __syscall access accept dup2 dup2_accept dup2_self
 SUBDIR+= exec_self execve exit extent
@@ -20,6 +20,7 @@ SUBDIR+= sosplice
 SUBDIR+= syscall sysvmsg sysvsem
 SUBDIR+= sysvshm unalign unfdpass wait
 SUBDIR+= sig-stop
+SUBDIR+= unveil
 SUBDIR+= unixsock bind
 
 # The setuid subtest creates set user/group id binaries in the obj directory.
index b0bdd63..e0b49cc 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: files,v 1.663 2018/07/10 20:30:31 claudio Exp $
+#      $OpenBSD: files,v 1.664 2018/07/13 09:25:22 beck Exp $
 #      $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
 
 #      @(#)files.newconf       7.5 (Berkeley) 5/10/93
@@ -683,6 +683,7 @@ file kern/kern_proc.c
 file kern/kern_prot.c
 file kern/kern_resource.c
 file kern/kern_pledge.c
+file kern/kern_unveil.c
 file kern/kern_sched.c
 file kern/kern_sensors.c
 file kern/kern_sig.c
index f96a42a..dcb2683 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: init_sysent.c,v 1.196 2018/07/05 15:31:51 mpi Exp $   */
+/*     $OpenBSD: init_sysent.c,v 1.197 2018/07/13 09:25:23 beck Exp $  */
 
 /*
  * System call switch table.
@@ -260,8 +260,8 @@ struct sysent sysent[] = {
            sys_sendsyslog },                   /* 112 = sendsyslog */
        { 0, 0, 0,
            sys_nosys },                        /* 113 = unimplemented fktrace */
-       { 0, 0, 0,
-           sys_nosys },                        /* 114 = obsolete osendmsg */
+       { 2, s(struct sys_unveil_args), 0,
+           sys_unveil },                       /* 114 = unveil */
        { 0, 0, 0,
            sys_nosys },                        /* 115 = obsolete vtrace */
        { 0, 0, 0,
index 7ccbab6..98a30c4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_exec.c,v 1.198 2018/06/18 09:15:05 mpi Exp $     */
+/*     $OpenBSD: kern_exec.c,v 1.199 2018/07/13 09:25:23 beck Exp $    */
 /*     $NetBSD: kern_exec.c,v 1.75 1996/02/09 18:59:28 christos Exp $  */
 
 /*-
@@ -64,6 +64,8 @@
 #include <uvm/uvm_extern.h>
 #include <machine/tcb.h>
 
+void   unveil_destroy(struct process *ps);
+
 const struct kmem_va_mode kv_exec = {
        .kv_wait = 1,
        .kv_map = &exec_map
@@ -532,6 +534,12 @@ sys_execve(struct proc *p, void *v, register_t *retval)
        } else {
                atomic_clearbits_int(&pr->ps_flags, PS_PLEDGE);
                pr->ps_pledge = 0;
+               /* XXX XXX XXX XXX */
+               /* Clear our unveil paths out so the child
+                * starts afresh
+                */
+               unveil_destroy(pr);
+               pr->ps_uvdone = 0;
        }
 
        /*
index dfd4e38..6bbf5fb 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_exit.c,v 1.164 2018/02/10 10:32:51 mpi Exp $     */
+/*     $OpenBSD: kern_exit.c,v 1.165 2018/07/13 09:25:23 beck Exp $    */
 /*     $NetBSD: kern_exit.c,v 1.39 1996/04/22 01:38:25 christos Exp $  */
 
 /*
@@ -73,6 +73,7 @@
 void   proc_finish_wait(struct proc *, struct proc *);
 void   process_zap(struct process *);
 void   proc_free(struct proc *);
+void   unveil_destroy(struct process *ps);
 
 /*
  * exit --
@@ -606,6 +607,8 @@ process_zap(struct process *pr)
         */
        (void)chgproccnt(pr->ps_ucred->cr_ruid, -1);
 
+       unveil_destroy(pr);
+
        /*
         * Release reference to text vnode
         */
index 291be50..2e2349e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_fork.c,v 1.203 2018/06/17 08:22:02 anton Exp $   */
+/*     $OpenBSD: kern_fork.c,v 1.204 2018/07/13 09:25:23 beck Exp $    */
 /*     $NetBSD: kern_fork.c,v 1.29 1996/02/09 18:59:34 christos Exp $  */
 
 /*
@@ -75,6 +75,8 @@ pid_t alloctid(void);
 pid_t allocpid(void);
 int ispidtaken(pid_t);
 
+struct unveil *unveil_copy(struct process *s, size_t *count);
+
 struct proc *thread_new(struct proc *_parent, vaddr_t _uaddr);
 struct process *process_new(struct proc *, struct process *, int);
 int fork_check_maxthread(uid_t _uid);
@@ -235,6 +237,18 @@ process_new(struct proc *p, struct process *parent, int flags)
        pr->ps_textvp = parent->ps_textvp;
        if (pr->ps_textvp)
                vref(pr->ps_textvp);
+#if 0  /* XXX Fix this */
+       /* copy unveil if unveil is active */
+       if (parent->ps_uvvcount) {
+               pr->ps_uvpaths = unveil_copy(parent, &pr->ps_uvncount);
+               if (parent->ps_uvpcwd)
+                       pr->ps_uvpcwd = pr->ps_uvpaths +
+                           (parent->ps_uvpcwd - parent->ps_uvpaths);
+               pr->ps_uvpcwdgone = parent->ps_uvpcwdgone;
+               pr->ps_uvdone = parent->ps_uvdone;
+               pr->ps_uvshrink = 1;
+       }
+#endif
 
        pr->ps_flags = parent->ps_flags &
            (PS_SUGID | PS_SUGIDEXEC | PS_PLEDGE | PS_EXECPLEDGE | PS_WXNEEDED);
index c5f223e..a057f5f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_pledge.c,v 1.235 2018/07/12 01:23:38 cheloha Exp $       */
+/*     $OpenBSD: kern_pledge.c,v 1.236 2018/07/13 09:25:23 beck Exp $  */
 
 /*
  * Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org>
@@ -25,6 +25,7 @@
 #include <sys/file.h>
 #include <sys/filedesc.h>
 #include <sys/namei.h>
+#include <sys/pool.h>
 #include <sys/socketvar.h>
 #include <sys/vnode.h>
 #include <sys/mbuf.h>
@@ -87,6 +88,7 @@ uint64_t pledgereq_flags(const char *req);
 int     parsepledges(struct proc *p, const char *kname,
            const char *promises, u_int64_t *fp);
 int     canonpath(const char *input, char *buf, size_t bufsize);
+void    unveil_destroy(struct process *ps);
 
 /* #define DEBUG_PLEDGE */
 #ifdef DEBUG_PLEDGE
@@ -287,6 +289,8 @@ const uint64_t pledge_syscalls[SYS_MAXSYSCALL] = {
        [SYS_setgroups] = PLEDGE_ID,
        [SYS_setlogin] = PLEDGE_ID,
 
+       [SYS_unveil] = PLEDGE_UNVEIL,
+
        [SYS_execve] = PLEDGE_EXEC,
 
        [SYS_chdir] = PLEDGE_RPATH,
@@ -391,6 +395,7 @@ static const struct {
        { "tmppath",            PLEDGE_TMPPATH },
        { "tty",                PLEDGE_TTY },
        { "unix",               PLEDGE_UNIX },
+       { "unveil",             PLEDGE_UNVEIL },
        { "vminfo",             PLEDGE_VMINFO },
        { "vmm",                PLEDGE_VMM },
        { "wpath",              PLEDGE_WPATH },
@@ -477,6 +482,14 @@ sys_pledge(struct proc *p, void *v, register_t *retval)
        if (SCARG(uap, promises)) {
                pr->ps_pledge = promises;
                pr->ps_flags |= PS_PLEDGE;
+               /*
+                * Kill off unveil and drop unveil vnode refs if we no
+                * longer are holding any path-accessing pledge
+                */
+               if ((pr->ps_pledge & (PLEDGE_RPATH | PLEDGE_WPATH |
+                   PLEDGE_CPATH | PLEDGE_DPATH | PLEDGE_TMPPATH | PLEDGE_EXEC |
+                   PLEDGE_UNIX | PLEDGE_UNVEIL)) == 0)
+                       unveil_destroy(pr);
        }
        if (SCARG(uap, execpromises)) {
                pr->ps_execpledge = execpromises;
@@ -558,6 +571,11 @@ pledge_namei(struct proc *p, struct nameidata *ni, char *origpath)
        if (!ni || (ni->ni_pledge == 0))
                panic("ni_pledge");
 
+       /*
+        * We set the BYPASSUNVEIL flag to skip unveil checks
+        * as necessary
+        */
+
        /* Doing a permitted execve() */
        if ((ni->ni_pledge & PLEDGE_EXEC) &&
            (p->p_p->ps_pledge & PLEDGE_EXEC))
@@ -572,6 +590,7 @@ pledge_namei(struct proc *p, struct nameidata *ni, char *origpath)
            (p->p_pledge_syscall == SYS_open) &&
            (ni->ni_pledge & PLEDGE_CPATH) &&
            strncmp(path, "/tmp/", sizeof("/tmp/") - 1) == 0) {
+               ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                return (0);
        }
 
@@ -581,6 +600,7 @@ pledge_namei(struct proc *p, struct nameidata *ni, char *origpath)
        if ((p->p_p->ps_pledge & PLEDGE_TMPPATH) &&
            (p->p_pledge_syscall == SYS_unlink) &&
            strncmp(path, "/tmp/", sizeof("/tmp/") - 1) == 0) {
+               ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                return (0);
        }
 
@@ -589,22 +609,25 @@ pledge_namei(struct proc *p, struct nameidata *ni, char *origpath)
        case SYS_access:
                /* tzset() needs this. */
                if ((ni->ni_pledge == PLEDGE_RPATH) &&
-                   strcmp(path, "/etc/localtime") == 0)
+                   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 &&
                    strcmp(path, "/var/run/ypbind.lock") == 0) {
-                       if (p->p_p->ps_pledge & PLEDGE_GETPW)
+                       if (p->p_p->ps_pledge & PLEDGE_GETPW) {
+                               ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                                return (0);
-                       else
+                       else
                                return (pledge_fail(p, error, PLEDGE_GETPW));
                }
-               break;
        case SYS_open:
                /* daemon(3) or other such functions */
                if ((ni->ni_pledge & ~(PLEDGE_RPATH | PLEDGE_WPATH)) == 0 &&
                    strcmp(path, "/dev/null") == 0) {
+                       ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                        return (0);
                }
 
@@ -612,6 +635,7 @@ pledge_namei(struct proc *p, struct nameidata *ni, char *origpath)
                if ((p->p_p->ps_pledge & PLEDGE_TTY) &&
                    (ni->ni_pledge & ~(PLEDGE_RPATH | PLEDGE_WPATH)) == 0 &&
                    strcmp(path, "/dev/tty") == 0) {
+                       ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                        return (0);
                }
 
@@ -620,23 +644,35 @@ pledge_namei(struct proc *p, struct nameidata *ni, char *origpath)
                    (p->p_p->ps_pledge & PLEDGE_GETPW)) {
                        if (strcmp(path, "/etc/spwd.db") == 0)
                                return (EPERM); /* don't call pledge_fail */
-                       if (strcmp(path, "/etc/pwd.db") == 0)
+                       if (strcmp(path, "/etc/pwd.db") == 0) {
+                               ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                                return (0);
-                       if (strcmp(path, "/etc/group") == 0)
+                       }
+                       if (strcmp(path, "/etc/group") == 0) {
+                               ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                                return (0);
-                       if (strcmp(path, "/etc/netid") == 0)
+                       }
+                       if (strcmp(path, "/etc/netid") == 0) {
+                               ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                                return (0);
+                       }
                }
 
                /* DNS needs /etc/{resolv.conf,hosts,services}. */
                if ((ni->ni_pledge == PLEDGE_RPATH) &&
                    (p->p_p->ps_pledge & PLEDGE_DNS)) {
-                       if (strcmp(path, "/etc/resolv.conf") == 0)
+                       if (strcmp(path, "/etc/resolv.conf") == 0) {
+                               ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                                return (0);
-                       if (strcmp(path, "/etc/hosts") == 0)
+                       }
+                       if (strcmp(path, "/etc/hosts") == 0) {
+                               ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                                return (0);
-                       if (strcmp(path, "/etc/services") == 0)
+                       }
+                       if (strcmp(path, "/etc/services") == 0) {
+                               ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                                return (0);
+                       }
                }
 
                if ((ni->ni_pledge == PLEDGE_RPATH) &&
@@ -651,38 +687,53 @@ pledge_namei(struct proc *p, struct nameidata *ni, char *origpath)
                                 * progress, needing a clever design.
                                 */
                                p->p_p->ps_pledge |= PLEDGE_YPACTIVE;
+                               ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                                return (0);
                        }
                        if (strncmp(path, "/var/yp/binding/",
-                           sizeof("/var/yp/binding/") - 1) == 0)
+                           sizeof("/var/yp/binding/") - 1) == 0) {
+                               ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                                return (0);
+                       }
                }
 
                /* tzset() needs these. */
                if ((ni->ni_pledge == PLEDGE_RPATH) &&
                    strncmp(path, "/usr/share/zoneinfo/",
-                   sizeof("/usr/share/zoneinfo/") - 1) == 0)
+                   sizeof("/usr/share/zoneinfo/") - 1) == 0)  {
+                       ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                        return (0);
+               }
                if ((ni->ni_pledge == PLEDGE_RPATH) &&
-                   strcmp(path, "/etc/localtime") == 0)
+                   strcmp(path, "/etc/localtime") == 0) {
+                       ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                        return (0);
+               }
 
                break;
        case SYS_readlink:
                /* Allow /etc/malloc.conf for malloc(3). */
                if ((ni->ni_pledge == PLEDGE_RPATH) &&
-                   strcmp(path, "/etc/malloc.conf") == 0)
+                   strcmp(path, "/etc/malloc.conf") == 0) {
+                       ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                        return (0);
+               }
                break;
        case SYS_stat:
                /* DNS needs /etc/resolv.conf. */
                if ((ni->ni_pledge == PLEDGE_RPATH) &&
                    (p->p_p->ps_pledge & PLEDGE_DNS) &&
-                   strcmp(path, "/etc/resolv.conf") == 0)
+                   strcmp(path, "/etc/resolv.conf") == 0) {
+                       ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                        return (0);
+               }
                break;
        }
 
+       /* Doing a stat */
+       if (ni->ni_pledge & PLEDGE_STAT)
+               return(0);
+
        /*
         * Ensure each flag of p_pledgenote has counterpart allowing it in
         * ps_pledge
@@ -690,6 +741,7 @@ pledge_namei(struct proc *p, struct nameidata *ni, char *origpath)
        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 */
        return (0);
 }
 
@@ -716,7 +768,6 @@ pledge_recvfd(struct proc *p, struct file *fp)
 
                if (vp->v_type != VDIR)
                        return (0);
-               break;
        }
        return pledge_fail(p, EINVAL, PLEDGE_RECVFD);
 }
diff --git a/sys/kern/kern_unveil.c b/sys/kern/kern_unveil.c
new file mode 100644 (file)
index 0000000..9a1f293
--- /dev/null
@@ -0,0 +1,727 @@
+/*     $OpenBSD: kern_unveil.c,v 1.1 2018/07/13 09:25:23 beck Exp $    */
+
+/*
+ * Copyright (c) 2017-2018 Bob Beck <beck@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/namei.h>
+#include <sys/pool.h>
+#include <sys/vnode.h>
+#include <sys/ktrace.h>
+#include <sys/types.h>
+#include <sys/malloc.h>
+#include <sys/tree.h>
+
+#include <sys/conf.h>
+#include <sys/syscall.h>
+#include <sys/syscallargs.h>
+#include <sys/systm.h>
+
+#define PLEDGENAMES
+#include <sys/pledge.h>
+
+/* #define DEBUG_UNVEIL */
+
+#define UNVEIL_MAX_VNODES      128
+#define UNVEIL_MAX_NAMES       128
+
+static inline int
+unvname_compare(const struct unvname *n1, const struct unvname *n2)
+{
+       if (n1->un_namesize == n2->un_namesize)
+               return (memcmp(n1->un_name, n2->un_name, n1->un_namesize));
+       else
+               return (n1->un_namesize - n2->un_namesize);
+}
+
+struct unvname *
+unvname_new(const char *name, size_t size, int flags)
+{
+       struct unvname *ret = malloc(sizeof(struct unvname), M_PROC, M_WAITOK);
+       ret->un_name = malloc(size, M_PROC, M_WAITOK);
+       memcpy(ret->un_name, name, size);
+       ret->un_namesize = size;
+       ret->un_flags = flags;
+       return ret;
+}
+
+void
+unveil_free_traversed_vnodes(struct nameidata *ndp) {
+       if (ndp->ni_tvpsize) {
+               size_t i;
+               for (i = 0; i < ndp->ni_tvpend; i++)
+                       vrele(ndp->ni_tvp[i]); /* ref for being in list */
+               free(ndp->ni_tvp, M_PROC, ndp->ni_tvpsize * sizeof(struct vnode *));
+               ndp->ni_tvpsize = 0;
+               ndp->ni_tvpend = 0;
+       }
+}
+
+void
+unveil_save_traversed_vnode(struct nameidata *ndp, struct vnode *vp) {
+       if (ndp->ni_tvpsize == 0) {
+               ndp->ni_tvp = mallocarray(MAXPATHLEN, sizeof(struct vnode *),
+                   M_PROC, M_WAITOK);
+               ndp->ni_tvpsize = MAXPATHLEN;
+       }
+       /* This should be limited by MAXPATHLEN on a single lookup */
+       KASSERT(ndp->ni_tvpsize > ndp->ni_tvpend);
+       vref(vp); /* ref for being in the list */
+       ndp->ni_tvp[ndp->ni_tvpend++] = vp;
+}
+
+void
+unvname_delete(struct unvname *name)
+{
+       free(name->un_name, M_PROC, name->un_namesize);;
+       free(name, M_PROC, sizeof(struct unvname));
+}
+
+RBT_PROTOTYPE(unvname_rbt, unvname, un_rbt, unvname_compare);
+RBT_GENERATE(unvname_rbt, unvname, un_rbt, unvname_compare);
+
+int
+unveil_delete_names(struct unveil *uv)
+{
+       struct unvname *unvn, *next;
+       int ret = 0;
+
+       rw_enter_write(&uv->uv_lock);
+       RBT_FOREACH_SAFE(unvn, unvname_rbt, &uv->uv_names, next) {
+               RBT_REMOVE(unvname_rbt, &uv->uv_names, unvn);
+               unvname_delete(unvn);
+               ret++;
+       }
+       rw_exit_write(&uv->uv_lock);
+       return ret;
+}
+
+void
+unveil_add_name(struct unveil *uv, char *name, uint64_t flags)
+{
+       struct unvname *unvn;
+
+       rw_enter_write(&uv->uv_lock);
+       unvn = unvname_new(name, strlen(name) + 1, flags);
+       RBT_INSERT(unvname_rbt, &uv->uv_names, unvn);
+       rw_exit_write(&uv->uv_lock);
+#ifdef DEBUG_UNVEIL
+       printf("added name %s\n", name);
+#endif
+}
+
+struct unvname *
+unveil_namelookup(struct unveil *uv, char *name)
+{
+       struct unvname n, *ret = NULL;
+
+       rw_enter_read(&uv->uv_lock);
+
+#ifdef DEBUG_UNVEIL
+       printf("unveil_namelookup: looking up name %s (%p) in vnode %p\n",
+           name, name, uv->uv_vp);
+#endif
+
+       KASSERT(uv->uv_vp != NULL);
+
+       n.un_name = name;
+       n.un_namesize = strlen(name) + 1;
+
+       ret = RBT_FIND(unvname_rbt, &uv->uv_names, &n);
+
+       rw_exit_read(&uv->uv_lock);
+
+#ifdef DEBUG_UNVEIL
+       if (ret == NULL)
+               printf("unveil_namelookup: no match for name %s in vnode %p\n",
+                   name, uv->uv_vp);
+       else
+               printf("unveil_namelookup: matched name %s in vnode %p\n",
+                   name, uv->uv_vp);
+#endif
+       return ret;
+}
+
+void
+unveil_destroy(struct process *ps)
+{
+       size_t i;
+
+       for (i = 0; ps->ps_uvpaths != NULL && i < ps->ps_uvvcount; i++) {
+               struct unveil *uv = ps->ps_uvpaths + i;
+
+               struct vnode *vp = uv->uv_vp;
+               /* skip any vnodes zapped by unveil_removevnode */
+               if (vp != NULL) {
+                       vp->v_uvcount--;
+#ifdef DEBUG_UNVEIL
+                       printf("unveil: %s(%d): removing vnode %p uvcount %d "
+                           "in position %ld\n",
+                           ps->ps_comm, ps->ps_pid, vp, vp->v_uvcount, i);
+#endif
+                       vrele(vp);
+               }
+               ps->ps_uvncount -= unveil_delete_names(uv);
+               uv->uv_vp = NULL;
+               uv->uv_flags = 0;
+       }
+
+       KASSERT(ps->ps_uvncount == 0);
+       free(ps->ps_uvpaths, M_PROC, UNVEIL_MAX_VNODES *
+           sizeof(struct unveil));
+       ps->ps_uvvcount = 0;
+       ps->ps_uvpaths = NULL;
+}
+
+struct unveil *
+unveil_copy(struct process *ps, size_t *count)
+{
+       struct unveil *ret;
+       size_t i;
+
+        ret = mallocarray(UNVEIL_MAX_VNODES, sizeof(struct unveil),
+           M_PROC, M_WAITOK|M_ZERO);
+
+       *count = 0;
+       for (i = 0; ps->ps_uvpaths != NULL && i < ps->ps_uvvcount; i++) {
+               struct unveil *uv = ps->ps_uvpaths + i;
+               struct unvname *unvn, *next;
+
+               ret[i].uv_vp = uv->uv_vp;
+               if (ret[i].uv_vp != NULL) {
+                       vref(ret[i].uv_vp);
+                       ret[i].uv_vp->v_uvcount++;
+               }
+               rw_init(&ret[i].uv_lock, "unveil");
+               RBT_INIT(unvname_rbt, &ret[i].uv_names);
+               rw_enter_read(&uv->uv_lock);
+               RBT_FOREACH_SAFE(unvn, unvname_rbt, &uv->uv_names, next) {
+                       unveil_add_name(&ret[i], unvn->un_name, unvn->un_flags);
+                       (*count)++;
+               }
+               printf("count now %ld\n", *count);
+               rw_exit_read(&uv->uv_lock);
+               ret[i].uv_flags = uv->uv_flags;
+       }
+       return(ret);
+}
+
+
+struct unveil *
+unveil_lookup(struct vnode *vp, struct proc *p)
+{
+       struct process *pr = p->p_p;
+       struct unveil *uv = pr->ps_uvpaths;
+       ssize_t l, r;
+
+       if (vp->v_uvcount == 0)
+               return NULL;
+
+       /*
+        * shrink if told to do so to remove dead vnodes.
+        */
+       if (pr->ps_uvshrink) {
+               size_t i = 0, j;
+               while (i < pr->ps_uvvcount) {
+                       if (uv[i].uv_vp == NULL)  {
+                               pr->ps_uvncount -= unveil_delete_names(&uv[i]);
+                               for (j = i + 1; j < pr->ps_uvvcount; j++)
+                                       uv[j - 1] = uv[j];
+                               pr->ps_uvvcount--;
+                       }
+                       i++;
+               }
+               pr->ps_uvshrink = 0;
+       }
+
+       if (pr->ps_uvvcount == 0)
+               return NULL;
+
+       /* clear the cwd unveil when we .. past it */
+       if (pr->ps_uvpcwd && (vp == pr->ps_uvpcwd->uv_vp)) {
+#ifdef DEBUG_UNVEIL
+               printf("unveil: %s(%d): nuking cwd traversing vnode %p\n",
+                   p->p_p->ps_comm, p->p_p->ps_pid, vp);
+#endif
+               p->p_p->ps_uvpcwd = NULL;
+               p->p_p->ps_uvpcwdgone = 0;
+       }
+#ifdef DEBUG_UNVEIL
+       else {
+               if (pr->ps_uvpcwd) {
+                       printf("unveil: %s(%d): did not nuke cwd because %p != %p\n",
+                           p->p_p->ps_comm, p->p_p->ps_pid, vp, pr->ps_uvpcwd->uv_vp);
+               }
+               else
+                       printf("unveil: %s(%d): cwd is null\n",
+                           p->p_p->ps_comm, p->p_p->ps_pid);
+
+       }
+#endif
+
+       l = 0;
+       r = pr->ps_uvvcount - 1;
+       while (l <= r) {
+               size_t m = l + (r - l)/2;
+#ifdef DEBUG_UNVEIL
+               printf("unveil: checking vnode %p vs. unveil vnode %p\n",
+                  vp, uv[m].uv_vp);
+#endif
+               if (vp == uv[m].uv_vp) {
+                       KASSERT(uv[m].uv_vp->v_uvcount > 0);
+                       KASSERT(uv[m].uv_vp->v_usecount > 0);
+                       return &uv[m];
+               }
+               if (vp > uv[m].uv_vp)
+                       l = m + 1;
+               else
+                       r = m - 1;
+       }
+       return NULL;
+}
+
+int
+unveil_parseflags(const char *cflags, uint64_t *flags)
+{
+       size_t i = 0;
+       char c;
+
+       *flags = 0;
+       while ((c = cflags[i++]) != '\0') {
+               switch (c) {
+               case 'r':
+                       *flags |= PLEDGE_RPATH;
+                       break;
+               case 'w':
+                       *flags |= PLEDGE_WPATH;
+                       break;
+               case 'x':
+                       *flags |= PLEDGE_EXEC;
+                       break;
+               case 'c':
+                       *flags |= PLEDGE_CPATH;
+                       break;
+               default:
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+int
+unveil_setflags(uint64_t *flags, uint64_t nflags)
+{
+#if 0
+       if (((~(*flags)) & nflags) != 0) {
+#ifdef DEBUG_UNVEIL
+               printf("Flags escalation %llX -> %llX\n", *flags, nflags);
+#endif
+               return 1;
+       }
+#endif
+       *flags = nflags;
+       return 1;
+}
+
+struct unveil *
+unveil_add_vnode(struct process *pr, struct vnode *vp)
+{
+       struct unveil *uv = NULL;
+       ssize_t i;
+       for (i = pr->ps_uvvcount;
+            i > 0 && pr->ps_uvpaths[i - 1].uv_vp > vp;
+            i--)
+               pr->ps_uvpaths[i] = pr->ps_uvpaths[i - 1];
+
+       uv = &pr->ps_uvpaths[i];
+       rw_init(&uv->uv_lock, "unveil");
+       RBT_INIT(unvname_rbt, &uv->uv_names);
+       uv->uv_vp = vp;
+       uv->uv_flags = 0;
+       pr->ps_uvvcount++;
+       return (uv);
+}
+
+void
+unveil_add_traversed_vnodes(struct proc *p, struct nameidata *ndp)
+{
+       /*
+        * add the traversed vnodes with 0 flags if they
+        * are not already present.
+        */
+       if (ndp->ni_tvpsize) {
+               size_t i;
+               for (i = 0; i < ndp->ni_tvpend; i++) {
+                       struct vnode *vp = ndp->ni_tvp[i];
+                       if (unveil_lookup(vp, p) == NULL) {
+                               vref(vp);
+                               vp->v_uvcount++;
+                               unveil_add_vnode(p->p_p, vp);
+                       }
+               }
+       }
+}
+
+int
+unveil_add(struct proc *p, struct nameidata *ndp, const char *cflags)
+{
+       struct process *pr = p->p_p;
+       struct vnode *vp;
+       struct unveil *uv;
+       int directory_add;
+       int ret = EINVAL;
+       u_int64_t flags;
+
+       KASSERT(ISSET(ndp->ni_cnd.cn_flags, HASBUF)); /* must have SAVENAME */
+
+       if (unveil_parseflags(cflags, &flags) == -1)
+               goto done;
+
+       if (pr->ps_uvpaths == NULL) {
+               pr->ps_uvpaths = mallocarray(UNVEIL_MAX_VNODES,
+                   sizeof(struct unveil), M_PROC, M_WAITOK|M_ZERO);
+       }
+
+       if (pr->ps_uvvcount >= UNVEIL_MAX_VNODES ||
+           pr->ps_uvncount >= UNVEIL_MAX_NAMES) {
+               ret = E2BIG;
+               goto done;
+       }
+
+       /* Are we a directory? or something else */
+       directory_add = ndp->ni_vp != NULL && ndp->ni_vp->v_type == VDIR;
+
+       if (directory_add)
+               vp=ndp->ni_vp;
+       else
+               vp=ndp->ni_dvp;
+
+       KASSERT(vp->v_type == VDIR);
+       vref(vp);
+       vp->v_uvcount++;
+       if ((uv = unveil_lookup(vp, p)) != NULL) {
+               /*
+                * We already have unveiled this directory
+                * vnode
+                */
+               vp->v_uvcount--;
+               vrele(vp);
+
+               /*
+                * If we are adding a directory which was already
+                * unveiled containing only specific terminals,
+                * unrestrict it.
+                */
+               if (directory_add) {
+#ifdef DEBUG_UNVEIL
+                       printf("unveil: %s(%d): updating directory vnode %p"
+                           " to unrestricted uvcount %d\n",
+                           pr->ps_comm, pr->ps_pid, vp, vp->v_uvcount);
+#endif
+                       if (!unveil_setflags(&uv->uv_flags, flags))
+                               ret = EPERM;
+                       else
+                               ret = 0;
+                       goto done;
+               }
+
+               /*
+                * If we are adding a terminal that is already unveiled, just
+                * replace the flags and we are done
+                */
+               if (!directory_add) {
+                       struct unvname *tname;
+                       if ((tname = unveil_namelookup(uv,
+                           ndp->ni_cnd.cn_nameptr)) != NULL) {
+#ifdef DEBUG_UNVEIL
+                               printf("unveil: %s(%d): changing flags for %s"
+                                   "in vnode %p, uvcount %d\n",
+                                   pr->ps_comm, pr->ps_pid, tname->un_name, vp,
+                                   vp->v_uvcount);
+#endif
+                               if (!unveil_setflags(&tname->un_flags, flags))
+                                       ret = EPERM;
+                               else
+                                       ret = 0;
+                               goto done;
+                       }
+               }
+
+       } else  {
+               /*
+                * New unveil involving this directory vnode.
+                */
+               uv = unveil_add_vnode(pr, vp);
+       }
+
+       /*
+        * At this stage with have a unveil in uv with a vnode for a
+        * directory. If the component we are adding is a directory,
+        * we are done. Otherwise, we add the component name the name
+        * list in uv.
+        */
+
+       if (directory_add) {
+               uv->uv_flags = flags;
+               ret = 0;
+#ifdef DEBUG_UNVEIL
+               printf("unveil: %s(%d): added unrestricted directory vnode %p"
+                   ", uvcount %d\n",
+                   pr->ps_comm, pr->ps_pid, vp, vp->v_uvcount);
+#endif
+               goto done;
+       }
+
+       unveil_add_name(uv, ndp->ni_cnd.cn_nameptr, flags);
+       pr->ps_uvncount++;
+       ret = 0;
+
+#ifdef DEBUG_UNVEIL
+       printf("unveil: %s(%d): added name %s beneath %s vnode %p,"
+           " uvcount %d\n",
+           pr->ps_comm, pr->ps_pid, ndp->ni_cnd.cn_nameptr,
+           uv->uv_flags ? "unrestricted" : "restricted",
+           vp, vp->v_uvcount);
+#endif
+
+ done:
+       if (ret == 0)
+               unveil_add_traversed_vnodes(p, ndp);
+       unveil_free_traversed_vnodes(ndp);
+       pool_put(&namei_pool, ndp->ni_cnd.cn_pnbuf);
+       return ret;
+}
+
+/*
+ * XXX this will probably change.
+ * XXX collapse down later once debug surely unneded
+ */
+int
+unveil_flagmatch(struct nameidata *ni, uint64_t flags)
+{
+       if (flags == 0) {
+               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_pledge & PLEDGE_RPATH) {
+               if ((flags & PLEDGE_RPATH) == 0) {
+#ifdef DEBUG_UNVEIL
+                       printf("Pledge wants read but disallowed\n");
+#endif
+                       return 0;
+               }
+       }
+       if (ni->ni_pledge & PLEDGE_WPATH) {
+               if ((flags & PLEDGE_WPATH) == 0) {
+#ifdef DEBUG_UNVEIL
+                       printf("Pledge wants write but disallowed\n");
+#endif
+                       return 0;
+               }
+       }
+       if (ni->ni_pledge & PLEDGE_EXEC) {
+               if ((flags & PLEDGE_EXEC) == 0) {
+#ifdef DEBUG_UNVEIL
+                       printf("Pledge wants exec but disallowed\n");
+#endif
+                       return 0;
+               }
+       }
+       if (ni->ni_pledge & PLEDGE_CPATH) {
+               if ((flags & PLEDGE_CPATH) == 0) {
+#ifdef DEBUG_UNVEIL
+                       printf("Pledge wants cpath but disallowed\n");
+#endif
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+/*
+ * unveil checking - for component directories in a namei lookup.
+ */
+void
+unveil_check_component(struct proc *p, struct nameidata *ni, struct vnode *dp )
+{
+       struct unveil *uv = NULL;
+
+       if (ni->ni_pledge != PLEDGE_UNVEIL) {
+               if ((ni->ni_cnd.cn_flags & BYPASSUNVEIL) == 0 &&
+                   (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) {
+                                       ni->ni_unveil_match = uv;
+#ifdef DEBUG_UNVEIL
+                                       printf("unveil: %s(%d): component directory match"
+                                           " for vnode %p\n",
+                                           p->p_p->ps_comm, p->p_p->ps_pid, dp);
+
+#endif
+                               }
+                       }
+               }
+       }
+       else
+               unveil_save_traversed_vnode(ni, dp);
+}
+
+/*
+ * unveil checking - only done after namei lookup has succeeded on
+ * the last compoent of a namei lookup.
+ */
+int
+unveil_check_final(struct proc *p, struct nameidata *ni)
+{
+       struct unveil *uv;
+       struct unvname *tname = NULL;
+
+       if (ni->ni_pledge == PLEDGE_UNVEIL ||
+           p->p_p->ps_uvpaths == NULL)
+               return (0);
+
+       if (ni->ni_cnd.cn_flags & BYPASSUNVEIL) {
+#ifdef DEBUG_UNVEIL
+               printf("unveil: %s(%d): BYPASSUNVEIL.\n",
+                   p->p_p->ps_comm, p->p_p->ps_pid);
+#endif
+               CLR(ni->ni_pledge, PLEDGE_STATLIE);
+               return (0);
+       }
+       if (ni->ni_vp != NULL && ni->ni_vp->v_type == VDIR) {
+               uv = unveil_lookup(ni->ni_vp, p);
+               if (uv == NULL) {
+#ifdef DEBUG_UNVEIL
+                       printf("unveil: %s(%d) no match for vnode %p\n",
+                           p->p_p->ps_comm, p->p_p->ps_pid, ni->ni_vp);
+#endif
+                       goto done;
+               }
+               if (!unveil_flagmatch(ni, uv->uv_flags)) {
+#ifdef DEBUG_UNVEIL
+                       printf("unveil: %s(%d) flag mismatch for directory"
+                           " vnode %p\n",
+                           p->p_p->ps_comm, p->p_p->ps_pid, ni->ni_vp);
+#endif
+                       return EACCES;
+               }
+       } else {
+               uv = unveil_lookup(ni->ni_dvp, p);
+               if (uv == NULL) {
+#ifdef DEBUG_UNVEIL
+                       printf("unveil: %s(%d) no match for directory"
+                           " vnode %p\n",
+                           p->p_p->ps_comm, p->p_p->ps_pid, ni->ni_dvp);
+#endif
+                       goto done;
+               }
+               if ((tname = unveil_namelookup(uv, ni->ni_cnd.cn_nameptr))
+                   == NULL) {
+#ifdef DEBUG_UNVEIL
+                       printf("unveil: %s(%d) no match for terminal '%s' in "
+                           "directory vnode %p\n",
+                           p->p_p->ps_comm, p->p_p->ps_pid,
+                           ni->ni_cnd.cn_nameptr, ni->ni_dvp);
+#endif
+                       uv = NULL;
+                       goto done;
+               }
+               if (!unveil_flagmatch(ni, tname->un_flags)) {
+#ifdef DEBUG_UNVEIL
+                       printf("unveil: %s(%d) flag mismatch for terminal '%s'\n",
+                           p->p_p->ps_comm, p->p_p->ps_pid, tname->un_name);
+#endif
+                       return EACCES;
+               }
+       }
+       ni->ni_unveil_match = uv;
+done:
+       if (ni->ni_unveil_match) {
+#ifdef DEBUG_UNVEIL
+               printf("unveil: %s(%d): matched \"%s\" underneath/at vnode %p\n",
+                   p->p_p->ps_comm, p->p_p->ps_pid, ni->ni_cnd.cn_nameptr,
+                   ni->ni_unveil_match->uv_vp);
+#endif
+               return (0);
+       }
+       else if (p->p_p->ps_uvpcwd) {
+               ni->ni_unveil_match = p->p_p->ps_uvpcwd;
+#ifdef DEBUG_UNVEIL
+               printf("unveil: %s(%d): used cwd unveil vnode from vnode %p\n",
+                   p->p_p->ps_comm, p->p_p->ps_pid, ni->ni_unveil_match->uv_vp);
+#endif
+               return (0);
+       } else if (p->p_p->ps_uvpcwdgone) {
+               printf("Corner cases make Bob cry in a corner\n");
+       }
+       return ENOENT;
+}
+
+/*
+ * Scan all active processes to see if any of them have a unveil
+ * to this vnode. If so, NULL the vnode in their unveil list,
+ * vrele, drop the reference, and mark their unveil list
+ * as needing to have the hole shrunk the next time the process
+ * uses it for lookup.
+ */
+void
+unveil_removevnode(struct vnode *vp)
+{
+       struct process *pr;
+       int count = 0;
+
+       if (vp->v_uvcount == 0)
+               return;
+#ifdef DEBUG_UNVEIL
+       printf("unveil_removevnode found vnode %p with count %d", vp, vp->v_uvcount);
+#endif
+       LIST_FOREACH(pr, &allprocess, ps_list) {
+               struct unveil * uv;
+               if ((uv = unveil_lookup(vp, pr->ps_mainproc)) != NULL) {
+                       uv->uv_vp = NULL;
+                       uv->uv_flags = 0;
+#ifdef DEBUG_UNVEIL
+       printf("unveil_removevnode vnode %p now count %d", vp, vp->v_uvcount);
+#endif
+                       pr->ps_uvshrink = 1;
+                       count++;
+               }
+       }
+       KASSERT(vp->v_uvcount == count);
+
+       while (vp->v_uvcount--)
+               vrele(vp);
+}
index 8c2004c..174cfbd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: syscalls.c,v 1.195 2018/07/05 15:31:51 mpi Exp $      */
+/*     $OpenBSD: syscalls.c,v 1.196 2018/07/13 09:25:23 beck Exp $     */
 
 /*
  * System call names.
@@ -134,7 +134,7 @@ char *syscallnames[] = {
        "sigsuspend",                   /* 111 = sigsuspend */
        "sendsyslog",                   /* 112 = sendsyslog */
        "#113 (unimplemented fktrace)",         /* 113 = unimplemented fktrace */
-       "#114 (obsolete osendmsg)",             /* 114 = obsolete osendmsg */
+       "unveil",                       /* 114 = unveil */
        "#115 (obsolete vtrace)",               /* 115 = obsolete vtrace */
        "#116 (obsolete t32_gettimeofday)",             /* 116 = obsolete t32_gettimeofday */
        "#117 (obsolete t32_getrusage)",                /* 117 = obsolete t32_getrusage */
index 0dedfc1..2f4396a 100644 (file)
@@ -1,4 +1,4 @@
-;      $OpenBSD: syscalls.master,v 1.185 2018/07/05 15:31:04 mpi Exp $
+;      $OpenBSD: syscalls.master,v 1.186 2018/07/13 09:25:23 beck Exp $
 ;      $NetBSD: syscalls.master,v 1.32 1996/04/23 10:24:21 mycroft Exp $
 
 ;      @(#)syscalls.master     8.2 (Berkeley) 1/13/94
 112    STD             { int sys_sendsyslog(const char *buf, size_t nbyte, \
                            int flags); }
 113    UNIMPL          fktrace
-114    OBSOL           osendmsg
+114    STD             { int sys_unveil(const char *path, \
+                           const char *flags); }
 115    OBSOL           vtrace
 116    OBSOL           t32_gettimeofday
 117    OBSOL           t32_getrusage
index 93de777..484889c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vfs_lookup.c,v 1.70 2018/07/05 00:00:18 bluhm Exp $   */
+/*     $OpenBSD: vfs_lookup.c,v 1.71 2018/07/13 09:25:23 beck Exp $    */
 /*     $NetBSD: vfs_lookup.c,v 1.17 1996/02/09 19:00:59 christos Exp $ */
 
 /*
@@ -57,6 +57,9 @@
 #include <sys/ktrace.h>
 #endif
 
+void unveil_check_component(struct proc *p, struct nameidata *ni, struct vnode *dp );
+int unveil_check_final(struct proc *p, struct nameidata *ni);
+
 void
 ndinitat(struct nameidata *ndp, u_long op, u_long flags,
     enum uio_seg segflg, int dirfd, const char *namep, struct proc *p)
@@ -179,11 +182,14 @@ fail:
         * Check if starting from root directory or current directory.
         */
        if (cnp->cn_pnbuf[0] == '/') {
+               curproc->p_p->ps_uvpcwd = NULL;
+               curproc->p_p->ps_uvpcwdgone = 0;
                dp = ndp->ni_rootdir;
                vref(dp);
        } else if (ndp->ni_dirfd == AT_FDCWD) {
                dp = fdp->fd_cdir;
                vref(dp);
+               unveil_check_component(p, ndp, dp);
        } else {
                struct file *fp = fd_getfile(fdp, ndp->ni_dirfd);
                if (fp == NULL) {
@@ -197,6 +203,7 @@ fail:
                        return (ENOTDIR);
                }
                vref(dp);
+               unveil_check_component(p, ndp, dp);
                FRELE(fp, p);
        }
        for (;;) {
@@ -217,6 +224,20 @@ fail:
                 * If not a symbolic link, return search result.
                 */
                if ((cnp->cn_flags & ISSYMLINK) == 0) {
+                       if ((error = unveil_check_final(p, ndp))) {
+                               pool_put(&namei_pool, cnp->cn_pnbuf);
+                               if ((cnp->cn_flags & LOCKPARENT) &&
+                                   (cnp->cn_flags & ISLASTCN))
+                                       VOP_UNLOCK(ndp->ni_dvp);
+                               if (ndp->ni_vp) {
+                                       if ((cnp->cn_flags & LOCKLEAF))
+                                               vput(ndp->ni_vp);
+                                       else
+                                               vrele(ndp->ni_vp);
+                               }
+                               ndp->ni_vp = NULL;
+                               return (error);
+                       }
                        if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0)
                                pool_put(&namei_pool, cnp->cn_pnbuf);
                        else
@@ -274,7 +295,17 @@ badlink:
                        vrele(dp);
                        dp = ndp->ni_rootdir;
                        vref(dp);
+                       ndp->ni_unveil_match = NULL;
+                       curproc->p_p->ps_uvpcwd = NULL;
+                       unveil_check_component(p, ndp, dp);
+               } else {
+                       /*
+                        * this is a relative link, so remember our
+                        * unveil match from this point
+                        */
+                       curproc->p_p->ps_uvpcwd = ndp->ni_unveil_match;
                }
+
        }
        pool_put(&namei_pool, cnp->cn_pnbuf);
        vrele(ndp->ni_dvp);
@@ -455,6 +486,7 @@ dirloop:
         * Handle "..": two special cases.
         * 1. If at root directory (e.g. after chroot)
         *    or at absolute root directory
+        *    or we are under unveil restrictions
         *    then ignore it so can't get out.
         * 2. If this vnode is the root of a mounted
         *    filesystem, then replace it with the
@@ -463,10 +495,20 @@ dirloop:
         */
        if (cnp->cn_flags & ISDOTDOT) {
                for (;;) {
+                       if (curproc->p_p->ps_uvvcount > 0) {
+#if 0
+                               error = ENOENT;
+                               goto bad;
+#else
+                               ndp->ni_unveil_match = NULL;
+#endif
+                       }
                        if (dp == ndp->ni_rootdir || dp == rootvnode) {
                                ndp->ni_dvp = dp;
                                ndp->ni_vp = dp;
                                vref(dp);
+                               curproc->p_p->ps_uvpcwd = NULL;
+                               curproc->p_p->ps_uvpcwdgone = 0;
                                goto nextname;
                        }
                        if ((dp->v_flag & VROOT) == 0 ||
@@ -476,6 +518,7 @@ dirloop:
                        dp = dp->v_mount->mnt_vnodecovered;
                        vput(tdp);
                        vref(dp);
+                       unveil_check_component(curproc, ndp, dp);
                        vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
                }
        }
@@ -486,6 +529,7 @@ dirloop:
        ndp->ni_dvp = dp;
        ndp->ni_vp = NULL;
        cnp->cn_flags &= ~PDIRUNLOCK;
+       unveil_check_component(curproc, ndp, dp);
 
        if ((error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) != 0) {
 #ifdef DIAGNOSTIC
@@ -493,8 +537,15 @@ dirloop:
                        panic("leaf should be empty");
 #endif
 #ifdef NAMEI_DIAGNOSTIC
-                       printf("not found\n");
+               printf("not found\n");
 #endif
+               /*
+                * Allow for unveiling of a file in a directory
+                * where we don't have access to create it ourselves
+                */
+               if (ndp->ni_pledge == PLEDGE_UNVEIL && error == EACCES)
+                       error = EJUSTRETURN;
+
                if (error != EJUSTRETURN)
                        goto bad;
                /*
index 353d94e..fa9e541 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vfs_subr.c,v 1.276 2018/07/02 20:56:22 bluhm Exp $    */
+/*     $OpenBSD: vfs_subr.c,v 1.277 2018/07/13 09:25:23 beck Exp $     */
 /*     $NetBSD: vfs_subr.c,v 1.53 1996/04/22 01:39:13 christos Exp $   */
 
 /*
@@ -722,6 +722,7 @@ vput(struct vnode *vp)
        }
 #endif
        vp->v_usecount--;
+       KASSERT(vp->v_usecount > 0 || vp->v_uvcount == 0);
        if (vp->v_usecount > 0) {
                VOP_UNLOCK(vp);
                return;
@@ -1067,6 +1068,8 @@ vgonel(struct vnode *vp, struct proc *p)
        struct vnode *vq;
        struct vnode *vx;
 
+       KASSERT(vp->v_uvcount == 0);
+
        /*
         * If a vgone (or vclean) is already in progress,
         * wait until it is done and return.
index da74978..4a66952 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vfs_syscalls.c,v 1.292 2018/07/03 20:40:25 kettenis Exp $     */
+/*     $OpenBSD: vfs_syscalls.c,v 1.293 2018/07/13 09:25:23 beck Exp $ */
 /*     $NetBSD: vfs_syscalls.c,v 1.71 1996/04/23 10:29:02 mycroft Exp $        */
 
 /*
@@ -90,6 +90,8 @@ int doutimensat(struct proc *, int, const char *, struct timespec [2], int);
 int dovutimens(struct proc *, struct vnode *, struct timespec [2]);
 int dofutimens(struct proc *, int, struct timespec [2]);
 int dounmount_leaf(struct mount *, int, struct proc *);
+int unveil_add(struct proc *, struct nameidata *, const char *);
+void unveil_removevnode(struct vnode *vp);
 
 /*
  * Virtual File System System Calls
@@ -350,6 +352,9 @@ checkdirs(struct vnode *olddp)
                        vref(newdp);
                        fdp->fd_rdir = newdp;
                }
+               pr->ps_uvpcwd = NULL;
+               /* XXX */
+               pr->ps_uvpcwdgone = 1;
        }
        if (rootvnode == olddp) {
                free_count++;
@@ -483,6 +488,7 @@ int
 dounmount_leaf(struct mount *mp, int flags, struct proc *p)
 {
        struct vnode *coveredvp;
+       struct vnode *vp, *nvp;
        int error;
        int hadsyncer = 0;
 
@@ -493,6 +499,15 @@ dounmount_leaf(struct mount *mp, int flags, struct proc *p)
                vgone(mp->mnt_syncer);
                mp->mnt_syncer = NULL;
        }
+
+       /*
+        * Before calling file system unmount, make sure
+        * all unveils to vnodes in here are dropped.
+        */
+       LIST_FOREACH_SAFE(vp , &mp->mnt_vnodelist, v_mntvnodes, nvp) {
+               unveil_removevnode(vp);
+       }
+
        if (((mp->mnt_flag & MNT_RDONLY) ||
            (error = VFS_SYNC(mp, MNT_WAIT, 0, p->p_ucred, p)) == 0) ||
            (flags & MNT_FORCE))
@@ -623,6 +638,7 @@ sys_statfs(struct proc *p, void *v, register_t *retval)
 
        NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p);
        nd.ni_pledge = PLEDGE_RPATH;
+       nd.ni_cnd.cn_flags |= BYPASSUNVEIL;
        if ((error = namei(&nd)) != 0)
                return (error);
        mp = nd.ni_vp->v_mount;
@@ -795,6 +811,8 @@ sys_chdir(struct proc *p, void *v, register_t *retval)
        nd.ni_pledge = PLEDGE_RPATH;
        if ((error = change_dir(&nd, p)) != 0)
                return (error);
+       p->p_p->ps_uvpcwd = nd.ni_unveil_match;
+       p->p_p->ps_uvpcwdgone = 0;
        old_cdir = fdp->fd_cdir;
        fdp->fd_cdir = nd.ni_vp;
        vrele(old_cdir);
@@ -860,6 +878,78 @@ change_dir(struct nameidata *ndp, struct proc *p)
        return (error);
 }
 
+int
+sys_unveil(struct proc *p, void *v, register_t *retval)
+{
+       struct sys_unveil_args /* {
+               syscallarg(const char *) path;
+               syscallarg(const char *) flags;
+       } */ *uap = v;
+       char pathname[MAXPATHLEN];
+       struct nameidata nd;
+       size_t pathlen;
+       char cflags[5];
+       int error;
+
+       if (SCARG(uap, path) == NULL && SCARG(uap, flags) == NULL) {
+               p->p_p->ps_uvdone = 1;
+               return (0);
+       }
+
+       if (p->p_p->ps_uvdone != 0)
+               return EINVAL;
+
+       error = copyinstr(SCARG(uap, flags), cflags, sizeof(cflags), NULL);
+       if (error)
+               return(error);
+       error = copyinstr(SCARG(uap, path), pathname, sizeof(pathname), &pathlen);
+       if (error)
+               return(error);
+
+#ifdef KTRACE
+       if (KTRPOINT(p, KTR_STRUCT))
+               ktrstruct(p, "unveil", cflags, strlen(cflags));
+#endif
+       if (pathlen < 2)
+               return EINVAL;
+
+       /* XXX unveil is disabled for now */
+       return EPERM;
+
+       if (pathlen == 2 && pathname[0] == '/')
+               NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | SAVENAME,
+                   UIO_SYSSPACE, pathname, p);
+       else
+               NDINIT(&nd, CREATE, FOLLOW | LOCKLEAF | LOCKPARENT | SAVENAME,
+                   UIO_SYSSPACE, pathname, p);
+
+       nd.ni_pledge = PLEDGE_UNVEIL;
+       if ((error = namei(&nd)) != 0)
+               return (error);
+
+       /*
+        * XXX Any access to the file or directory will allow us to
+        * pledge path it
+        */
+       if ((nd.ni_vp &&
+           (VOP_ACCESS(nd.ni_vp, VREAD, p->p_ucred, p) == 0 ||
+           VOP_ACCESS(nd.ni_vp, VWRITE, p->p_ucred, p) == 0 ||
+           VOP_ACCESS(nd.ni_vp, VEXEC, p->p_ucred, p) == 0)) ||
+           VOP_ACCESS(nd.ni_dvp, VREAD, p->p_ucred, p) == 0 ||
+           VOP_ACCESS(nd.ni_dvp, VWRITE, p->p_ucred, p) == 0 ||
+           VOP_ACCESS(nd.ni_dvp, VEXEC, p->p_ucred, p) == 0)
+               error = unveil_add(p, &nd, cflags);
+       else
+               error = EPERM;
+
+       /* release vref and lock from namei, but not vref from ppath_add */
+       if (nd.ni_vp)
+               vput(nd.ni_vp);
+       if (nd.ni_dvp)
+               vput(nd.ni_dvp);
+       return (error);
+}
+
 /*
  * Check permissions, allocate an open file structure,
  * and call the device open routine if any.
@@ -1706,7 +1796,7 @@ dofaccessat(struct proc *p, int fd, const char *path, int amode, int flag)
        }
 
        NDINITAT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, fd, path, p);
-       nd.ni_pledge = PLEDGE_RPATH;
+       nd.ni_pledge = PLEDGE_RPATH | PLEDGE_STAT;
        if ((error = namei(&nd)) != 0)
                goto out;
        vp = nd.ni_vp;
@@ -1776,7 +1866,7 @@ dofstatat(struct proc *p, int fd, const char *path, struct stat *buf, int flag)
 
        follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW;
        NDINITAT(&nd, LOOKUP, follow | LOCKLEAF, UIO_USERSPACE, fd, path, p);
-       nd.ni_pledge = PLEDGE_RPATH;
+       nd.ni_pledge = PLEDGE_RPATH | PLEDGE_STAT;
        if ((error = namei(&nd)) != 0)
                return (error);
        error = vn_stat(nd.ni_vp, &sb, p);
@@ -1784,11 +1874,11 @@ dofstatat(struct proc *p, int fd, const char *path, struct stat *buf, int flag)
        if (error)
                return (error);
        if (nd.ni_pledge & PLEDGE_STATLIE) {
-               if (S_ISDIR(sb.st_mode)) {
-                       sb.st_mode &= ~ALLPERMS;
-                       sb.st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
-                       sb.st_uid = 0;
-                       sb.st_gid = 0;
+               if (S_ISDIR(sb.st_mode) || S_ISLNK(sb.st_mode)) {
+                       if (sb.st_uid >= 1000) {
+                               sb.st_uid = p->p_ucred->cr_uid;
+                               sb.st_gid = p->p_ucred->cr_gid;;
+                       }
                        sb.st_gen = 0;
                } else
                        return (ENOENT);
@@ -1883,7 +1973,7 @@ doreadlinkat(struct proc *p, int fd, const char *path, char *buf,
        struct nameidata nd;
 
        NDINITAT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, fd, path, p);
-       nd.ni_pledge = PLEDGE_RPATH;
+       nd.ni_pledge = PLEDGE_RPATH | PLEDGE_STAT;
        if ((error = namei(&nd)) != 0)
                return (error);
        vp = nd.ni_vp;
index 4db0a86..a7f8acc 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: namei.h,v 1.34 2017/08/29 02:51:27 deraadt Exp $      */
+/*     $OpenBSD: namei.h,v 1.35 2018/07/13 09:25:23 beck Exp $ */
 /*     $NetBSD: namei.h,v 1.11 1996/02/09 18:25:20 christos Exp $      */
 
 /*
@@ -64,12 +64,18 @@ struct nameidata {
         */
        struct  vnode *ni_vp;           /* vnode of result */
        struct  vnode *ni_dvp;          /* vnode of intermediate directory */
+
        /*
         * Shared between namei and lookup/commit routines.
         */
        size_t  ni_pathlen;             /* remaining chars in path */
        char    *ni_next;               /* next location in pathname */
        u_long  ni_loopcnt;             /* count of symlinks encountered */
+       struct unveil *ni_unveil_match; /* last matching unveil component */
+       struct vnode **ni_tvp;          /* traversed vnodes */
+       size_t ni_tvpend;               /* end of traversed vnode list */
+       size_t ni_tvpsize;              /* size of traversed vnode list */
+
        /*
         * Lookup parameters: this structure describes the subset of
         * information from the nameidata structure that is passed
@@ -138,6 +144,7 @@ struct nameidata {
 #define        REQUIREDIR      0x080000      /* must be a directory */
 #define STRIPSLASHES    0x100000      /* strip trailing slashes */
 #define PDIRUNLOCK     0x200000      /* vfs_lookup() unlocked parent dir */
+#define BYPASSUNVEIL   0x400000      /* bypass pledgepath check */
 
 /*
  * Initialization of an nameidata structure.
index 28ca799..910cc35 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pledge.h,v 1.36 2018/06/16 15:37:00 florian Exp $     */
+/*     $OpenBSD: pledge.h,v 1.37 2018/07/13 09:25:23 beck Exp $        */
 
 /*
  * Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org>
 #define PLEDGE_BPF     0x0000000200000000ULL   /* bpf ioctl */
 #define PLEDGE_ERROR   0x0000000400000000ULL   /* ENOSYS instead of kill */
 #define PLEDGE_WROUTE  0x0000000800000000ULL   /* interface address ioctls */
+#define PLEDGE_UNVEIL  0x0000001000000000ULL   /* allow unveil() */
 
 /*
  * Bits outside PLEDGE_USERSET are used by the kernel itself
  * 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 */
 
@@ -109,6 +111,7 @@ static struct {
        { PLEDGE_BPF,           "bpf" },
        { PLEDGE_ERROR,         "error" },
        { PLEDGE_WROUTE,        "wroute" },
+       { PLEDGE_UNVEIL,        "unveil" },
        { 0, NULL },
 };
 #endif
@@ -137,6 +140,7 @@ int pledge_fcntl(struct proc *p, int cmd);
 int    pledge_swapctl(struct proc *p);
 int    pledge_kill(struct proc *p, pid_t pid);
 int    pledge_protexec(struct proc *p, int prot);
+void   ppath_destroy(struct process *ps);
 
 #endif /* _KERNEL */
 
index f05d70e..a797838 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: proc.h,v 1.251 2018/07/12 01:23:38 cheloha Exp $      */
+/*     $OpenBSD: proc.h,v 1.252 2018/07/13 09:25:23 beck Exp $ */
 /*     $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $       */
 
 /*-
@@ -48,6 +48,8 @@
 #include <sys/event.h>                 /* For struct klist */
 #include <sys/mutex.h>                 /* For struct mutex */
 #include <sys/resource.h>              /* For struct rusage */
+#include <sys/rwlock.h>                        /* For struct rwlock */
+#include <sys/tree.h>
 
 #ifdef _KERNEL
 #include <sys/atomic.h>
@@ -129,6 +131,15 @@ struct tusage {
        uint64_t        tu_iticks;      /* Statclock hits processing intr. */
 };
 
+struct unvname {
+       char                    *un_name;
+       size_t                  un_namesize;
+       uint64_t                un_flags;
+       RBT_ENTRY(unvnmae)      un_rbt;
+};
+
+RBT_HEAD(unvname_rbt, unvname);
+
 /*
  * Description of a process.
  *
@@ -142,6 +153,7 @@ struct tusage {
  * run-time information needed by threads.
  */
 #ifdef __need_process
+struct unveil;
 struct process {
        /*
         * ps_mainproc is the original thread in the process.
@@ -191,6 +203,15 @@ struct process {
 
        u_int64_t ps_wxcounter;
 
+       struct unveil *ps_uvpaths;      /* unveil vnodes and names */
+       struct unveil *ps_uvpcwd;       /* pointer to unveil of cwd, NULL if none */
+       size_t ps_uvvcount;             /* count of unveil vnodes held */
+       size_t ps_uvncount;             /* count of unveil names allocated */
+       int ps_uvshrink;                /* do we need to shrink vnode list */
+       int ps_uvactive;                /* is unveil active */
+       int ps_uvdone;                  /* no more unveil is permitted */
+       int ps_uvpcwdgone;              /* need to reevaluate cwd unveil */
+
 /* End area that is zeroed on creation. */
 #define        ps_endzero      ps_startcopy
 
@@ -404,6 +425,13 @@ struct proc {
 
 #ifdef _KERNEL
 
+struct unveil {
+       struct vnode            *uv_vp;
+       struct unvname_rbt      uv_names;
+       struct rwlock           uv_lock;
+       u_int64_t               uv_flags;
+};
+
 struct uidinfo {
        LIST_ENTRY(uidinfo) ui_hash;
        uid_t   ui_uid;
index 9e80b41..b6e0fef 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: syscall.h,v 1.195 2018/07/05 15:31:51 mpi Exp $       */
+/*     $OpenBSD: syscall.h,v 1.196 2018/07/13 09:25:23 beck Exp $      */
 
 /*
  * System call numbers.
 /* syscall: "sendsyslog" ret: "int" args: "const char *" "size_t" "int" */
 #define        SYS_sendsyslog  112
 
-                               /* 114 is obsolete osendmsg */
+/* syscall: "unveil" ret: "int" args: "const char *" "const char *" */
+#define        SYS_unveil      114
+
                                /* 115 is obsolete vtrace */
                                /* 116 is obsolete t32_gettimeofday */
                                /* 117 is obsolete t32_getrusage */
index ee69797..ca0d5f4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: syscallargs.h,v 1.198 2018/07/05 15:31:51 mpi Exp $   */
+/*     $OpenBSD: syscallargs.h,v 1.199 2018/07/13 09:25:23 beck Exp $  */
 
 /*
  * System call argument lists.
@@ -568,6 +568,11 @@ struct sys_sendsyslog_args {
        syscallarg(int) flags;
 };
 
+struct sys_unveil_args {
+       syscallarg(const char *) path;
+       syscallarg(const char *) flags;
+};
+
 struct sys_getsockopt_args {
        syscallarg(int) s;
        syscallarg(int) level;
@@ -1219,6 +1224,7 @@ int       sys_ppoll(struct proc *, void *, register_t *);
 int    sys_pselect(struct proc *, void *, register_t *);
 int    sys_sigsuspend(struct proc *, void *, register_t *);
 int    sys_sendsyslog(struct proc *, void *, register_t *);
+int    sys_unveil(struct proc *, void *, register_t *);
 int    sys_getsockopt(struct proc *, void *, register_t *);
 int    sys_thrkill(struct proc *, void *, register_t *);
 int    sys_readv(struct proc *, void *, register_t *);
index 6c2bac0..404a50b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vnode.h,v 1.146 2018/05/27 06:02:15 visa Exp $        */
+/*     $OpenBSD: vnode.h,v 1.147 2018/07/13 09:25:23 beck Exp $        */
 /*     $NetBSD: vnode.h,v 1.38 1996/02/29 20:59:05 cgd Exp $   */
 
 /*
@@ -93,6 +93,7 @@ struct vnode {
        enum    vtagtype v_tag;                 /* type of underlying data */
        u_int   v_flag;                         /* vnode flags (see below) */
        u_int   v_usecount;                     /* reference count of users */
+       u_int   v_uvcount;                      /* unveil references */
        /* reference count of writers */
        u_int   v_writecount;
        /* Flags that can be read/written in interrupts */
index 0b3f06a..ce27c47 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ktrstruct.c,v 1.24 2017/12/12 01:12:34 deraadt Exp $  */
+/*     $OpenBSD: ktrstruct.c,v 1.25 2018/07/13 09:25:23 beck Exp $     */
 
 /*-
  * Copyright (c) 1988, 1993
@@ -654,6 +654,10 @@ ktrstruct(char *buf, size_t buflen)
                printf("execpromise=");
                showbufc(basecol + sizeof("execpromise=") - 1,
                    (unsigned char *)data, datalen, VIS_DQ | VIS_TAB | VIS_NL);
+       } else if (strcmp(name, "unveil") == 0) {
+               printf("flags=");
+               showbufc(basecol + sizeof("flags=") - 1,
+                   (unsigned char *)data, datalen, VIS_DQ | VIS_TAB | VIS_NL);
        } else {
                printf("unknown structure %s\n", name);
        }