diff options
-rw-r--r-- | include/unistd.h | 3 | ||||
-rw-r--r-- | lib/libc/Symbols.list | 2 | ||||
-rw-r--r-- | lib/libc/hidden/unistd.h | 3 | ||||
-rw-r--r-- | lib/libc/sys/Makefile.inc | 6 | ||||
-rw-r--r-- | lib/libc/sys/pledge.2 | 8 | ||||
-rw-r--r-- | lib/libc/sys/unveil.2 | 158 | ||||
-rw-r--r-- | regress/sys/kern/Makefile | 3 | ||||
-rw-r--r-- | sys/conf/files | 3 | ||||
-rw-r--r-- | sys/kern/init_sysent.c | 6 | ||||
-rw-r--r-- | sys/kern/kern_exec.c | 10 | ||||
-rw-r--r-- | sys/kern/kern_exit.c | 5 | ||||
-rw-r--r-- | sys/kern/kern_fork.c | 16 | ||||
-rw-r--r-- | sys/kern/kern_pledge.c | 85 | ||||
-rw-r--r-- | sys/kern/kern_unveil.c | 727 | ||||
-rw-r--r-- | sys/kern/syscalls.c | 4 | ||||
-rw-r--r-- | sys/kern/syscalls.master | 5 | ||||
-rw-r--r-- | sys/kern/vfs_lookup.c | 55 | ||||
-rw-r--r-- | sys/kern/vfs_subr.c | 5 | ||||
-rw-r--r-- | sys/kern/vfs_syscalls.c | 108 | ||||
-rw-r--r-- | sys/sys/namei.h | 9 | ||||
-rw-r--r-- | sys/sys/pledge.h | 6 | ||||
-rw-r--r-- | sys/sys/proc.h | 30 | ||||
-rw-r--r-- | sys/sys/syscall.h | 6 | ||||
-rw-r--r-- | sys/sys/syscallargs.h | 8 | ||||
-rw-r--r-- | sys/sys/vnode.h | 3 | ||||
-rw-r--r-- | usr.bin/kdump/ktrstruct.c | 6 |
26 files changed, 1224 insertions, 56 deletions
diff --git a/include/unistd.h b/include/unistd.h index dab92899383..c9432eadb23 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -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 */ diff --git a/lib/libc/Symbols.list b/lib/libc/Symbols.list index 63420faebdd..55500aea43d 100644 --- a/lib/libc/Symbols.list +++ b/lib/libc/Symbols.list @@ -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 diff --git a/lib/libc/hidden/unistd.h b/lib/libc/hidden/unistd.h index bfa424fe795..692951637c8 100644 --- a/lib/libc/hidden/unistd.h +++ b/lib/libc/hidden/unistd.h @@ -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); diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index 6823d7472a8..e7792063ae9 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -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 diff --git a/lib/libc/sys/pledge.2 b/lib/libc/sys/pledge.2 index ab606be7960..3c32586c478 100644 --- a/lib/libc/sys/pledge.2 +++ b/lib/libc/sys/pledge.2 @@ -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 index 00000000000..d98515dd6a2 --- /dev/null +++ b/lib/libc/sys/unveil.2 @@ -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 . diff --git a/regress/sys/kern/Makefile b/regress/sys/kern/Makefile index 569733580b9..18780fadf96 100644 --- a/regress/sys/kern/Makefile +++ b/regress/sys/kern/Makefile @@ -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. diff --git a/sys/conf/files b/sys/conf/files index b0bdd63777a..e0b49cc8445 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -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 diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c index f96a42ae89b..dcb26836061 100644 --- a/sys/kern/init_sysent.c +++ b/sys/kern/init_sysent.c @@ -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, diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 7ccbab6a374..98a30c4aee0 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -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; } /* diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index dfd4e38078f..6bbf5fb2258 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -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 */ diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 291be50d21c..2e2349ea54c 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -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); diff --git a/sys/kern/kern_pledge.c b/sys/kern/kern_pledge.c index c5f223e8b1b..a057f5f1348 100644 --- a/sys/kern/kern_pledge.c +++ b/sys/kern/kern_pledge.c @@ -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 index 00000000000..9a1f293b6a6 --- /dev/null +++ b/sys/kern/kern_unveil.c @@ -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); +} diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c index 8c2004ccbbd..174cfbd20a8 100644 --- a/sys/kern/syscalls.c +++ b/sys/kern/syscalls.c @@ -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 */ diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 0dedfc15685..2f4396a2171 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -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 @@ -239,7 +239,8 @@ 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 diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index 93de7775c5a..484889c7cd2 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -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; /* diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index 353d94e3c76..fa9e541abec 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -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. diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index da74978c8da..4a66952294f 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -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; diff --git a/sys/sys/namei.h b/sys/sys/namei.h index 4db0a86c734..a7f8accdc3e 100644 --- a/sys/sys/namei.h +++ b/sys/sys/namei.h @@ -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. diff --git a/sys/sys/pledge.h b/sys/sys/pledge.h index 28ca79901f1..910cc351ea3 100644 --- a/sys/sys/pledge.h +++ b/sys/sys/pledge.h @@ -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> @@ -61,12 +61,14 @@ #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 */ diff --git a/sys/sys/proc.h b/sys/sys/proc.h index f05d70ee7d5..a797838c3f3 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -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; diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h index 9e80b413f26..b6e0feff8cf 100644 --- a/sys/sys/syscall.h +++ b/sys/sys/syscall.h @@ -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. @@ -342,7 +342,9 @@ /* 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 */ diff --git a/sys/sys/syscallargs.h b/sys/sys/syscallargs.h index ee697973432..ca0d5f4d311 100644 --- a/sys/sys/syscallargs.h +++ b/sys/sys/syscallargs.h @@ -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 *); diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index 6c2bac007ba..404a50b05e2 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -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 */ diff --git a/usr.bin/kdump/ktrstruct.c b/usr.bin/kdump/ktrstruct.c index 0b3f06ae8eb..ce27c475439 100644 --- a/usr.bin/kdump/ktrstruct.c +++ b/usr.bin/kdump/ktrstruct.c @@ -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); } |