From 8b23add8c74b86d0da67de43302cf21b97b028be Mon Sep 17 00:00:00 2001 From: beck Date: Fri, 13 Jul 2018 09:25:22 +0000 Subject: [PATCH] Unveiling unveil(2). 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@ --- include/unistd.h | 3 +- lib/libc/Symbols.list | 2 + lib/libc/hidden/unistd.h | 3 +- lib/libc/sys/Makefile.inc | 6 +- lib/libc/sys/pledge.2 | 8 +- lib/libc/sys/unveil.2 | 158 +++++++++ regress/sys/kern/Makefile | 3 +- sys/conf/files | 3 +- sys/kern/init_sysent.c | 6 +- sys/kern/kern_exec.c | 10 +- sys/kern/kern_exit.c | 5 +- sys/kern/kern_fork.c | 16 +- sys/kern/kern_pledge.c | 85 ++++- sys/kern/kern_unveil.c | 727 ++++++++++++++++++++++++++++++++++++++ sys/kern/syscalls.c | 4 +- sys/kern/syscalls.master | 5 +- sys/kern/vfs_lookup.c | 55 ++- sys/kern/vfs_subr.c | 5 +- sys/kern/vfs_syscalls.c | 108 +++++- sys/sys/namei.h | 9 +- sys/sys/pledge.h | 6 +- sys/sys/proc.h | 30 +- sys/sys/syscall.h | 6 +- sys/sys/syscallargs.h | 8 +- sys/sys/vnode.h | 3 +- usr.bin/kdump/ktrstruct.c | 6 +- 26 files changed, 1224 insertions(+), 56 deletions(-) create mode 100644 lib/libc/sys/unveil.2 create mode 100644 sys/kern/kern_unveil.c 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 * @@ -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 .\" @@ -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 +.\" +.\" 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 #include +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 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -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 + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define PLEDGENAMES +#include + +/* #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 #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 @@ -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 /* For struct klist */ #include /* For struct mutex */ #include /* For struct rusage */ +#include /* For struct rwlock */ +#include #ifdef _KERNEL #include @@ -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); } -- 2.20.1