-/* $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 $ */
/*-
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 */
_thread_sys_unlink
_thread_sys_unlinkat
_thread_sys_unmount
+_thread_sys_unveil
_thread_sys_utimensat
_thread_sys_utimes
_thread_sys_utrace
unlink
unlinkat
unmount
+unveil
utimensat
utimes
utrace
-/* $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>
*
PROTO_DEPRECATED(ualarm);
PROTO_NORMAL(unlink);
PROTO_NORMAL(unlinkat);
+PROTO_NORMAL(unveil);
PROTO_DEPRECATED(usleep);
PROTO_WRAP(vfork);
PROTO_CANCEL(write);
-# $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
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}}
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
-.\" $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>
.\"
.\" 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
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 .
--- /dev/null
+.\" $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 .
-# $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
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.
-# $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
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
-/* $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.
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,
-/* $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 $ */
/*-
#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
} 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;
}
/*
-/* $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 $ */
/*
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 --
*/
(void)chgproccnt(pr->ps_ucred->cr_ruid, -1);
+ unveil_destroy(pr);
+
/*
* Release reference to text vnode
*/
-/* $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 $ */
/*
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);
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);
-/* $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>
#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>
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
[SYS_setgroups] = PLEDGE_ID,
[SYS_setlogin] = PLEDGE_ID,
+ [SYS_unveil] = PLEDGE_UNVEIL,
+
[SYS_execve] = PLEDGE_EXEC,
[SYS_chdir] = PLEDGE_RPATH,
{ "tmppath", PLEDGE_TMPPATH },
{ "tty", PLEDGE_TTY },
{ "unix", PLEDGE_UNIX },
+ { "unveil", PLEDGE_UNVEIL },
{ "vminfo", PLEDGE_VMINFO },
{ "vmm", PLEDGE_VMM },
{ "wpath", PLEDGE_WPATH },
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;
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))
(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);
}
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);
}
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);
}
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);
}
(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) &&
* 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
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);
}
if (vp->v_type != VDIR)
return (0);
- break;
}
return pledge_fail(p, EINVAL, PLEDGE_RECVFD);
}
--- /dev/null
+/* $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);
+}
-/* $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.
"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 */
-; $OpenBSD: syscalls.master,v 1.185 2018/07/05 15:31:04 mpi Exp $
+; $OpenBSD: syscalls.master,v 1.186 2018/07/13 09:25:23 beck Exp $
; $NetBSD: syscalls.master,v 1.32 1996/04/23 10:24:21 mycroft Exp $
; @(#)syscalls.master 8.2 (Berkeley) 1/13/94
112 STD { int sys_sendsyslog(const char *buf, size_t nbyte, \
int flags); }
113 UNIMPL fktrace
-114 OBSOL osendmsg
+114 STD { int sys_unveil(const char *path, \
+ const char *flags); }
115 OBSOL vtrace
116 OBSOL t32_gettimeofday
117 OBSOL t32_getrusage
-/* $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 $ */
/*
#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)
* 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) {
return (ENOTDIR);
}
vref(dp);
+ unveil_check_component(p, ndp, dp);
FRELE(fp, p);
}
for (;;) {
* 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
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);
* 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
*/
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 ||
dp = dp->v_mount->mnt_vnodecovered;
vput(tdp);
vref(dp);
+ unveil_check_component(curproc, ndp, dp);
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
}
}
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
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;
/*
-/* $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 $ */
/*
}
#endif
vp->v_usecount--;
+ KASSERT(vp->v_usecount > 0 || vp->v_uvcount == 0);
if (vp->v_usecount > 0) {
VOP_UNLOCK(vp);
return;
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.
-/* $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 $ */
/*
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
vref(newdp);
fdp->fd_rdir = newdp;
}
+ pr->ps_uvpcwd = NULL;
+ /* XXX */
+ pr->ps_uvpcwdgone = 1;
}
if (rootvnode == olddp) {
free_count++;
dounmount_leaf(struct mount *mp, int flags, struct proc *p)
{
struct vnode *coveredvp;
+ struct vnode *vp, *nvp;
int error;
int hadsyncer = 0;
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))
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;
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);
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.
}
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;
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);
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);
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;
-/* $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 $ */
/*
*/
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
#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.
-/* $OpenBSD: pledge.h,v 1.36 2018/06/16 15:37:00 florian Exp $ */
+/* $OpenBSD: pledge.h,v 1.37 2018/07/13 09:25:23 beck Exp $ */
/*
* Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org>
#define PLEDGE_BPF 0x0000000200000000ULL /* bpf ioctl */
#define PLEDGE_ERROR 0x0000000400000000ULL /* ENOSYS instead of kill */
#define PLEDGE_WROUTE 0x0000000800000000ULL /* interface address ioctls */
+#define PLEDGE_UNVEIL 0x0000001000000000ULL /* allow unveil() */
/*
* Bits outside PLEDGE_USERSET are used by the kernel itself
* to track program behaviours which have been observed.
*/
#define PLEDGE_USERSET 0x0fffffffffffffffULL
+#define PLEDGE_STAT 0x2000000000000000ULL /* XXX this is a stat */
#define PLEDGE_STATLIE 0x4000000000000000ULL
#define PLEDGE_YPACTIVE 0x8000000000000000ULL /* YP use detected and allowed */
{ PLEDGE_BPF, "bpf" },
{ PLEDGE_ERROR, "error" },
{ PLEDGE_WROUTE, "wroute" },
+ { PLEDGE_UNVEIL, "unveil" },
{ 0, NULL },
};
#endif
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 */
-/* $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 $ */
/*-
#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>
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.
*
* run-time information needed by threads.
*/
#ifdef __need_process
+struct unveil;
struct process {
/*
* ps_mainproc is the original thread in the 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
#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;
-/* $OpenBSD: syscall.h,v 1.195 2018/07/05 15:31:51 mpi Exp $ */
+/* $OpenBSD: syscall.h,v 1.196 2018/07/13 09:25:23 beck Exp $ */
/*
* System call numbers.
/* syscall: "sendsyslog" ret: "int" args: "const char *" "size_t" "int" */
#define SYS_sendsyslog 112
- /* 114 is obsolete osendmsg */
+/* syscall: "unveil" ret: "int" args: "const char *" "const char *" */
+#define SYS_unveil 114
+
/* 115 is obsolete vtrace */
/* 116 is obsolete t32_gettimeofday */
/* 117 is obsolete t32_getrusage */
-/* $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.
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;
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 *);
-/* $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 $ */
/*
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 */
-/* $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
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);
}