summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_tame.c
diff options
context:
space:
mode:
authorderaadt <deraadt@openbsd.org>2015-10-09 01:17:18 +0000
committerderaadt <deraadt@openbsd.org>2015-10-09 01:17:18 +0000
commit350b464c5787ce73b4bf8e99960537423713261c (patch)
treeb10e2a726174c7b2a35325727d88a16d43b470d4 /sys/kern/kern_tame.c
parentsync (diff)
downloadwireguard-openbsd-350b464c5787ce73b4bf8e99960537423713261c.tar.xz
wireguard-openbsd-350b464c5787ce73b4bf8e99960537423713261c.zip
Rename tame() to pledge(). This fairly interface has evolved to be more
strict than anticipated. It allows a programmer to pledge/promise/covenant that their program will operate within an easily defined subset of the Unix environment, or it pays the price.
Diffstat (limited to 'sys/kern/kern_tame.c')
-rw-r--r--sys/kern/kern_tame.c1241
1 files changed, 0 insertions, 1241 deletions
diff --git a/sys/kern/kern_tame.c b/sys/kern/kern_tame.c
deleted file mode 100644
index 3ccb09989f8..00000000000
--- a/sys/kern/kern_tame.c
+++ /dev/null
@@ -1,1241 +0,0 @@
-/* $OpenBSD: kern_tame.c,v 1.71 2015/10/09 01:10:27 deraadt Exp $ */
-
-/*
- * Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org>
- * Copyright (c) 2015 Theo de Raadt <deraadt@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/types.h>
-
-#include <sys/mount.h>
-#include <sys/proc.h>
-#include <sys/fcntl.h>
-#include <sys/file.h>
-#include <sys/filedesc.h>
-#include <sys/vnode.h>
-#include <sys/mbuf.h>
-#include <sys/sysctl.h>
-#include <sys/ktrace.h>
-
-#include <sys/ioctl.h>
-#include <sys/termios.h>
-#include <sys/mtio.h>
-#include <net/bpf.h>
-#include <net/route.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-
-#include <sys/signal.h>
-#include <sys/signalvar.h>
-#include <sys/syscall.h>
-#include <sys/syscallargs.h>
-#include <sys/systm.h>
-#include <sys/pledge.h>
-
-int canonpath(const char *input, char *buf, size_t bufsize);
-
-const u_int pledge_syscalls[SYS_MAXSYSCALL] = {
- [SYS_exit] = 0xffffffff,
- [SYS_kbind] = 0xffffffff,
-
- [SYS_getuid] = PLEDGE_SELF,
- [SYS_geteuid] = PLEDGE_SELF,
- [SYS_getresuid] = PLEDGE_SELF,
- [SYS_getgid] = PLEDGE_SELF,
- [SYS_getegid] = PLEDGE_SELF,
- [SYS_getresgid] = PLEDGE_SELF,
- [SYS_getgroups] = PLEDGE_SELF,
- [SYS_getlogin] = PLEDGE_SELF,
- [SYS_getpgrp] = PLEDGE_SELF,
- [SYS_getpgid] = PLEDGE_SELF,
- [SYS_getppid] = PLEDGE_SELF,
- [SYS_getsid] = PLEDGE_SELF,
- [SYS_getthrid] = PLEDGE_SELF,
- [SYS_getrlimit] = PLEDGE_SELF,
- [SYS_gettimeofday] = PLEDGE_SELF,
- [SYS_getdtablecount] = PLEDGE_SELF,
- [SYS_getrusage] = PLEDGE_SELF,
- [SYS_issetugid] = PLEDGE_SELF,
- [SYS_clock_getres] = PLEDGE_SELF,
- [SYS_clock_gettime] = PLEDGE_SELF,
- [SYS_getpid] = PLEDGE_SELF,
- [SYS_umask] = PLEDGE_SELF,
- [SYS_sysctl] = PLEDGE_SELF, /* read-only; narrow subset */
- [SYS_adjtime] = PLEDGE_SELF, /* read-only */
-
- [SYS_fchdir] = PLEDGE_SELF, /* careful of directory fd inside jails */
-
- /* needed by threaded programs */
- [SYS_sched_yield] = PLEDGE_SELF,
- [SYS___thrsleep] = PLEDGE_SELF,
- [SYS___thrwakeup] = PLEDGE_SELF,
- [SYS___threxit] = PLEDGE_SELF,
- [SYS___thrsigdivert] = PLEDGE_SELF,
-
- [SYS_sendsyslog] = PLEDGE_SELF,
- [SYS_nanosleep] = PLEDGE_SELF,
- [SYS_sigprocmask] = PLEDGE_SELF,
- [SYS_sigaction] = PLEDGE_SELF,
- [SYS_sigreturn] = PLEDGE_SELF,
- [SYS_sigpending] = PLEDGE_SELF,
- [SYS_getitimer] = PLEDGE_SELF,
- [SYS_setitimer] = PLEDGE_SELF,
-
- [SYS_pledge] = PLEDGE_SELF,
-
- [SYS_wait4] = PLEDGE_SELF,
-
- [SYS_poll] = PLEDGE_RW,
- [SYS_kevent] = PLEDGE_RW,
- [SYS_kqueue] = PLEDGE_RW,
- [SYS_select] = PLEDGE_RW,
-
- [SYS_close] = PLEDGE_RW,
- [SYS_dup] = PLEDGE_RW,
- [SYS_dup2] = PLEDGE_RW,
- [SYS_dup3] = PLEDGE_RW,
- [SYS_closefrom] = PLEDGE_RW,
- [SYS_shutdown] = PLEDGE_RW,
- [SYS_read] = PLEDGE_RW,
- [SYS_readv] = PLEDGE_RW,
- [SYS_pread] = PLEDGE_RW,
- [SYS_preadv] = PLEDGE_RW,
- [SYS_write] = PLEDGE_RW,
- [SYS_writev] = PLEDGE_RW,
- [SYS_pwrite] = PLEDGE_RW,
- [SYS_pwritev] = PLEDGE_RW,
- [SYS_ftruncate] = PLEDGE_RW,
- [SYS_lseek] = PLEDGE_RW,
- [SYS_fstat] = PLEDGE_RW,
-
- [SYS_fcntl] = PLEDGE_RW,
- [SYS_fsync] = PLEDGE_RW,
- [SYS_pipe] = PLEDGE_RW,
- [SYS_pipe2] = PLEDGE_RW,
- [SYS_socketpair] = PLEDGE_RW,
- [SYS_getdents] = PLEDGE_RW,
-
- [SYS_sendto] = PLEDGE_RW | PLEDGE_DNS_ACTIVE | PLEDGE_YP_ACTIVE,
- [SYS_sendmsg] = PLEDGE_RW,
- [SYS_recvmsg] = PLEDGE_RW,
- [SYS_recvfrom] = PLEDGE_RW | PLEDGE_DNS_ACTIVE | PLEDGE_YP_ACTIVE,
-
- [SYS_fork] = PLEDGE_PROC,
- [SYS_vfork] = PLEDGE_PROC,
- [SYS_kill] = PLEDGE_PROC,
- [SYS_setpgid] = PLEDGE_PROC,
- [SYS_sigsuspend] = PLEDGE_PROC,
- [SYS_setrlimit] = PLEDGE_PROC,
-
- [SYS_execve] = PLEDGE_EXEC,
-
- [SYS_setgroups] = PLEDGE_PROC,
- [SYS_setresgid] = PLEDGE_PROC,
- [SYS_setresuid] = PLEDGE_PROC,
-
- /* FIONREAD/FIONBIO, plus further checks in pledge_ioctl_check() */
- [SYS_ioctl] = PLEDGE_RW | PLEDGE_IOCTL | PLEDGE_TTY,
-
- [SYS_getentropy] = PLEDGE_MALLOC,
- [SYS_madvise] = PLEDGE_MALLOC,
- [SYS_minherit] = PLEDGE_MALLOC,
- [SYS_mmap] = PLEDGE_MALLOC,
- [SYS_mprotect] = PLEDGE_MALLOC,
- [SYS_mquery] = PLEDGE_MALLOC,
- [SYS_munmap] = PLEDGE_MALLOC,
-
- [SYS_open] = PLEDGE_SELF, /* further checks in namei */
- [SYS_stat] = PLEDGE_SELF, /* further checks in namei */
- [SYS_access] = PLEDGE_SELF, /* further checks in namei */
- [SYS_readlink] = PLEDGE_SELF, /* further checks in namei */
-
- [SYS_chdir] = PLEDGE_RPATH,
- [SYS_openat] = PLEDGE_RPATH | PLEDGE_WPATH,
- [SYS_fstatat] = PLEDGE_RPATH | PLEDGE_WPATH,
- [SYS_faccessat] = PLEDGE_RPATH | PLEDGE_WPATH,
- [SYS_readlinkat] = PLEDGE_RPATH | PLEDGE_WPATH,
- [SYS_lstat] = PLEDGE_RPATH | PLEDGE_WPATH | PLEDGE_TMPPATH,
- [SYS_rename] = PLEDGE_CPATH,
- [SYS_rmdir] = PLEDGE_CPATH,
- [SYS_renameat] = PLEDGE_CPATH,
- [SYS_link] = PLEDGE_CPATH,
- [SYS_linkat] = PLEDGE_CPATH,
- [SYS_symlink] = PLEDGE_CPATH,
- [SYS_unlink] = PLEDGE_CPATH | PLEDGE_TMPPATH,
- [SYS_unlinkat] = PLEDGE_CPATH,
- [SYS_mkdir] = PLEDGE_CPATH,
- [SYS_mkdirat] = PLEDGE_CPATH,
-
- /*
- * Classify as RPATH|WPATH, because of path information leakage.
- * WPATH due to unknown use of mk*temp(3) on non-/tmp paths..
- */
- [SYS___getcwd] = PLEDGE_RPATH | PLEDGE_WPATH,
-
- /* Classify as RPATH, because these leak path information */
- [SYS_getfsstat] = PLEDGE_RPATH,
- [SYS_statfs] = PLEDGE_RPATH,
- [SYS_fstatfs] = PLEDGE_RPATH,
-
- [SYS_utimes] = PLEDGE_FATTR,
- [SYS_futimes] = PLEDGE_FATTR,
- [SYS_utimensat] = PLEDGE_FATTR,
- [SYS_futimens] = PLEDGE_FATTR,
- [SYS_chmod] = PLEDGE_FATTR,
- [SYS_fchmod] = PLEDGE_FATTR,
- [SYS_fchmodat] = PLEDGE_FATTR,
- [SYS_chflags] = PLEDGE_FATTR,
- [SYS_chflagsat] = PLEDGE_FATTR,
- [SYS_chown] = PLEDGE_FATTR,
- [SYS_fchownat] = PLEDGE_FATTR,
- [SYS_lchown] = PLEDGE_FATTR,
- [SYS_fchown] = PLEDGE_FATTR,
-
- [SYS_socket] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS_ACTIVE | PLEDGE_YP_ACTIVE,
- [SYS_connect] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS_ACTIVE | PLEDGE_YP_ACTIVE,
-
- [SYS_listen] = PLEDGE_INET | PLEDGE_UNIX,
- [SYS_bind] = PLEDGE_INET | PLEDGE_UNIX,
- [SYS_accept4] = PLEDGE_INET | PLEDGE_UNIX,
- [SYS_accept] = PLEDGE_INET | PLEDGE_UNIX,
- [SYS_getpeername] = PLEDGE_INET | PLEDGE_UNIX,
- [SYS_getsockname] = PLEDGE_INET | PLEDGE_UNIX,
- [SYS_setsockopt] = PLEDGE_INET | PLEDGE_UNIX,
- [SYS_getsockopt] = PLEDGE_INET | PLEDGE_UNIX,
-
- [SYS_flock] = PLEDGE_GETPW,
-};
-
-static const struct {
- char *name;
- int flags;
-} pledgereq[] = {
- { "malloc", PLEDGE_SELF | PLEDGE_MALLOC },
- { "rw", PLEDGE_SELF | PLEDGE_RW },
- { "stdio", PLEDGE_SELF | PLEDGE_MALLOC | PLEDGE_RW },
- { "rpath", PLEDGE_SELF | PLEDGE_RW | PLEDGE_RPATH },
- { "wpath", PLEDGE_SELF | PLEDGE_RW | PLEDGE_WPATH },
- { "tmppath", PLEDGE_SELF | PLEDGE_RW | PLEDGE_TMPPATH },
- { "inet", PLEDGE_SELF | PLEDGE_RW | PLEDGE_INET },
- { "unix", PLEDGE_SELF | PLEDGE_RW | PLEDGE_UNIX },
- { "dns", PLEDGE_SELF | PLEDGE_MALLOC | PLEDGE_DNSPATH },
- { "getpw", PLEDGE_SELF | PLEDGE_MALLOC | PLEDGE_RW | PLEDGE_GETPW },
-/*X*/ { "cmsg", PLEDGE_UNIX | PLEDGE_INET | PLEDGE_SENDFD | PLEDGE_RECVFD },
- { "sendfd", PLEDGE_RW | PLEDGE_SENDFD },
- { "recvfd", PLEDGE_RW | PLEDGE_RECVFD },
- { "ioctl", PLEDGE_IOCTL },
- { "route", PLEDGE_ROUTE },
- { "mcast", PLEDGE_MCAST },
- { "tty", PLEDGE_TTY },
- { "proc", PLEDGE_PROC },
- { "exec", PLEDGE_EXEC },
- { "cpath", PLEDGE_CPATH },
- { "abort", PLEDGE_ABORT },
- { "fattr", PLEDGE_FATTR },
- { "prot_exec", PLEDGE_PROTEXEC },
-};
-
-int
-sys_pledge(struct proc *p, void *v, register_t *retval)
-{
- struct sys_pledge_args /* {
- syscallarg(const char *)request;
- syscallarg(const char **)paths;
- } */ *uap = v;
- int flags = 0;
- int error;
-
- if (SCARG(uap, request)) {
- size_t rbuflen;
- char *rbuf, *rp, *pn;
- int f, i;
-
- rbuf = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
- error = copyinstr(SCARG(uap, request), rbuf, MAXPATHLEN,
- &rbuflen);
- if (error) {
- free(rbuf, M_TEMP, MAXPATHLEN);
- return (error);
- }
-#ifdef KTRACE
- if (KTRPOINT(p, KTR_STRUCT))
- ktrstruct(p, "pledgereq", rbuf, rbuflen-1);
-#endif
-
- for (rp = rbuf; rp && *rp && error == 0; rp = pn) {
- pn = strchr(rp, ' '); /* find terminator */
- if (pn) {
- while (*pn == ' ')
- *pn++ = '\0';
- }
-
- for (f = i = 0; i < nitems(pledgereq); i++) {
- if (strcmp(rp, pledgereq[i].name) == 0) {
- f = pledgereq[i].flags;
- break;
- }
- }
- if (f == 0) {
- free(rbuf, M_TEMP, MAXPATHLEN);
- return (EINVAL);
- }
- flags |= f;
- }
- free(rbuf, M_TEMP, MAXPATHLEN);
- }
-
- if (flags & ~PLEDGE_USERSET)
- return (EINVAL);
-
- if ((p->p_p->ps_flags & PS_PLEDGE)) {
- /* Already pledged, only allow reductions */
- if (((flags | p->p_p->ps_pledge) & PLEDGE_USERSET) !=
- (p->p_p->ps_pledge & PLEDGE_USERSET)) {
- return (EPERM);
- }
-
- flags &= p->p_p->ps_pledge;
- flags &= PLEDGE_USERSET; /* Relearn _ACTIVE */
- }
-
- if (SCARG(uap, paths)) {
- const char **u = SCARG(uap, paths), *sp;
- struct whitepaths *wl;
- char *cwdpath = NULL, *path;
- size_t cwdpathlen = MAXPATHLEN * 4, cwdlen, len, maxargs = 0;
- int i, error;
-
- if (p->p_p->ps_pledgepaths)
- return (EPERM);
-
- /* Count paths */
- for (i = 0; i < PLEDGE_MAXPATHS; i++) {
- if ((error = copyin(u + i, &sp, sizeof(sp))) != 0)
- return (error);
- if (sp == NULL)
- break;
- }
- if (i == PLEDGE_MAXPATHS)
- return (E2BIG);
-
- wl = malloc(sizeof *wl + sizeof(struct whitepath) * (i+1),
- M_TEMP, M_WAITOK | M_ZERO);
- wl->wl_size = sizeof *wl + sizeof(struct whitepath) * (i+1);
- wl->wl_count = i;
- wl->wl_ref = 1;
-
- path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
-
- /* Copy in */
- for (i = 0; i < wl->wl_count; i++) {
- char *fullpath = NULL, *builtpath = NULL, *canopath = NULL, *cwd;
- size_t builtlen = 0;
-
- if ((error = copyin(u + i, &sp, sizeof(sp))) != 0)
- break;
- if (sp == NULL)
- break;
- if ((error = copyinstr(sp, path, MAXPATHLEN, &len)) != 0)
- break;
-#ifdef KTRACE
- if (KTRPOINT(p, KTR_STRUCT))
- ktrstruct(p, "pledgepath", path, len-1);
-#endif
-
- /* If path is relative, prepend cwd */
- if (path[0] != '/') {
- if (cwdpath == NULL) {
- char *bp, *bpend;
-
- cwdpath = malloc(cwdpathlen, M_TEMP, M_WAITOK);
- bp = &cwdpath[cwdpathlen];
- bpend = bp;
- *(--bp) = '\0';
-
- error = vfs_getcwd_common(p->p_fd->fd_cdir,
- NULL, &bp, cwdpath, cwdpathlen/2,
- GETCWD_CHECK_ACCESS, p);
- if (error)
- break;
- cwd = bp;
- cwdlen = (bpend - bp);
- }
-
- /* NUL included in cwd component */
- builtlen = cwdlen + 1 + strlen(path);
- if (builtlen > PATH_MAX) {
- error = ENAMETOOLONG;
- break;
- }
- builtpath = malloc(builtlen, M_TEMP, M_WAITOK);
- snprintf(builtpath, builtlen, "%s/%s", cwd, path);
- // printf("pledge: builtpath = %s\n", builtpath);
- fullpath = builtpath;
- } else
- fullpath = path;
-
- canopath = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
- error = canonpath(fullpath, canopath, MAXPATHLEN);
-
- free(builtpath, M_TEMP, builtlen);
- if (error != 0) {
- free(canopath, M_TEMP, MAXPATHLEN);
- break;
- }
-
- len = strlen(canopath) + 1;
-
- //printf("pledge: canopath = %s %lld strlen %lld\n", canopath,
- // (long long)len, (long long)strlen(canopath));
-
- if (maxargs += len > ARG_MAX) {
- error = E2BIG;
- break;
- }
- wl->wl_paths[i].name = malloc(len, M_TEMP, M_WAITOK);
- memcpy(wl->wl_paths[i].name, canopath, len);
- wl->wl_paths[i].len = len;
- free(canopath, M_TEMP, MAXPATHLEN);
- }
- free(path, M_TEMP, MAXPATHLEN);
- free(cwdpath, M_TEMP, cwdpathlen);
-
- if (error) {
- for (i = 0; i < wl->wl_count; i++)
- free(wl->wl_paths[i].name,
- M_TEMP, wl->wl_paths[i].len);
- free(wl, M_TEMP, wl->wl_size);
- return (error);
- }
- p->p_p->ps_pledgepaths = wl;
-#if 0
- printf("pledge: %s(%d): paths loaded:\n", p->p_comm, p->p_pid);
- for (i = 0; i < wl->wl_count; i++)
- if (wl->wl_paths[i].name)
- printf("pledge: %d=%s %lld\n", i, wl->wl_paths[i].name,
- (long long)wl->wl_paths[i].len);
-#endif
- }
-
- p->p_p->ps_pledge = flags;
- p->p_p->ps_flags |= PS_PLEDGE;
- return (0);
-}
-
-int
-pledge_check(struct proc *p, int code)
-{
- p->p_pledgenote = p->p_pledgeafter = 0; /* XX optimise? */
- p->p_pledge_syscall = code;
-
- if (code < 0 || code > SYS_MAXSYSCALL - 1)
- return (0);
-
- if (p->p_p->ps_pledge == 0)
- return (code == SYS_exit || code == SYS_kbind);
- return (p->p_p->ps_pledge & pledge_syscalls[code]);
-}
-
-int
-pledge_fail(struct proc *p, int error, int code)
-{
- printf("%s(%d): syscall %d\n", p->p_comm, p->p_pid, p->p_pledge_syscall);
- if (p->p_p->ps_pledge & PLEDGE_ABORT) { /* Core dump requested */
- struct sigaction sa;
-
- memset(&sa, 0, sizeof sa);
- sa.sa_handler = SIG_DFL;
- setsigvec(p, SIGABRT, &sa);
- psignal(p, SIGABRT);
- } else
- psignal(p, SIGKILL);
-
- p->p_p->ps_pledge = 0; /* Disable all PLEDGE_ flags */
- return (error);
-}
-
-/*
- * Need to make it more obvious that one cannot get through here
- * without the right flags set
- */
-int
-pledge_namei(struct proc *p, char *origpath)
-{
- char path[PATH_MAX];
-
- if (p->p_pledgenote == TMN_COREDUMP)
- return (0); /* Allow a coredump */
-
- if (canonpath(origpath, path, sizeof(path)) != 0)
- return (pledge_fail(p, EPERM, PLEDGE_RPATH));
-
- if ((p->p_pledgenote & TMN_FATTR) &&
- (p->p_p->ps_pledge & PLEDGE_FATTR) == 0) {
- printf("%s(%d): inode syscall%d, not allowed\n",
- p->p_comm, p->p_pid, p->p_pledge_syscall);
- return (pledge_fail(p, EPERM, PLEDGE_FATTR));
- }
-
- /* Detect what looks like a mkstemp(3) family operation */
- if ((p->p_p->ps_pledge & PLEDGE_TMPPATH) &&
- (p->p_pledge_syscall == SYS_open) &&
- (p->p_pledgenote & TMN_CPATH) &&
- strncmp(path, "/tmp/", sizeof("/tmp/") - 1) == 0) {
- return (0);
- }
-
- /* Allow unlinking of a mkstemp(3) file...
- * Good opportunity for strict checks here.
- */
- if ((p->p_p->ps_pledge & PLEDGE_TMPPATH) &&
- (p->p_pledge_syscall == SYS_unlink) &&
- strncmp(path, "/tmp/", sizeof("/tmp/") - 1) == 0) {
- return (0);
- }
-
- /* open, mkdir, or other path creation operation */
- if ((p->p_pledgenote & TMN_CPATH) &&
- ((p->p_p->ps_pledge & PLEDGE_CPATH) == 0))
- return (pledge_fail(p, EPERM, PLEDGE_CPATH));
-
- if ((p->p_pledgenote & TMN_WPATH) &&
- (p->p_p->ps_pledge & PLEDGE_WPATH) == 0)
- return (pledge_fail(p, EPERM, PLEDGE_WPATH));
-
- /* Read-only paths used occasionally by libc */
- switch (p->p_pledge_syscall) {
- case SYS_access:
- /* tzset() needs this. */
- if ((p->p_pledgenote == TMN_RPATH) &&
- strcmp(path, "/etc/localtime") == 0)
- return (0);
- break;
- case SYS_open:
- /* getpw* and friends need a few files */
- if ((p->p_pledgenote == TMN_RPATH) &&
- (p->p_p->ps_pledge & PLEDGE_GETPW)) {
- if (strcmp(path, "/etc/spwd.db") == 0)
- return (EPERM);
- if (strcmp(path, "/etc/pwd.db") == 0)
- return (0);
- if (strcmp(path, "/etc/group") == 0)
- return (0);
- }
-
- /* DNS needs /etc/{resolv.conf,hosts,services}. */
- if ((p->p_pledgenote == TMN_RPATH) &&
- (p->p_p->ps_pledge & PLEDGE_DNSPATH)) {
- if (strcmp(path, "/etc/resolv.conf") == 0) {
- p->p_pledgeafter |= TMA_DNSRESOLV;
- return (0);
- }
- if (strcmp(path, "/etc/hosts") == 0)
- return (0);
- if (strcmp(path, "/etc/services") == 0)
- return (0);
- }
- if ((p->p_pledgenote == TMN_RPATH) &&
- (p->p_p->ps_pledge & PLEDGE_GETPW)) {
- if (strcmp(path, "/var/run/ypbind.lock") == 0) {
- p->p_pledgeafter |= TMA_YPLOCK;
- return (0);
- }
- if (strncmp(path, "/var/yp/binding/",
- sizeof("/var/yp/binding/") - 1) == 0)
- return (0);
- }
- /* tzset() needs these. */
- if ((p->p_pledgenote == TMN_RPATH) &&
- strncmp(path, "/usr/share/zoneinfo/",
- sizeof("/usr/share/zoneinfo/") - 1) == 0)
- return (0);
- if ((p->p_pledgenote == TMN_RPATH) &&
- strcmp(path, "/etc/localtime") == 0)
- return (0);
-
- /* /usr/share/nls/../libc.cat has to succeed for strerror(3). */
- if ((p->p_pledgenote == TMN_RPATH) &&
- strncmp(path, "/usr/share/nls/",
- sizeof("/usr/share/nls/") - 1) == 0 &&
- strcmp(path + strlen(path) - 9, "/libc.cat") == 0)
- return (0);
- break;
- case SYS_readlink:
- /* Allow /etc/malloc.conf for malloc(3). */
- if ((p->p_pledgenote == TMN_RPATH) &&
- strcmp(path, "/etc/malloc.conf") == 0)
- return (0);
- break;
- case SYS_stat:
- /* DNS needs /etc/resolv.conf. */
- if ((p->p_pledgenote == TMN_RPATH) &&
- (p->p_p->ps_pledge & PLEDGE_DNSPATH)) {
- if (strcmp(path, "/etc/resolv.conf") == 0) {
- p->p_pledgeafter |= TMA_DNSRESOLV;
- return (0);
- }
- }
- break;
- }
-
- /*
- * If a whitelist is set, compare canonical paths. Anything
- * not on the whitelist gets ENOENT.
- */
- if (p->p_p->ps_pledgepaths) {
- struct whitepaths *wl = p->p_p->ps_pledgepaths;
- char *fullpath, *builtpath = NULL, *canopath = NULL;
- size_t builtlen = 0;
- int i, error;
-
- if (origpath[0] != '/') {
- char *cwdpath, *cwd, *bp, *bpend;
- size_t cwdpathlen = MAXPATHLEN * 4, cwdlen;
-
- cwdpath = malloc(cwdpathlen, M_TEMP, M_WAITOK);
- bp = &cwdpath[cwdpathlen];
- bpend = bp;
- *(--bp) = '\0';
-
- error = vfs_getcwd_common(p->p_fd->fd_cdir,
- NULL, &bp, cwdpath, cwdpathlen/2,
- GETCWD_CHECK_ACCESS, p);
- if (error) {
- free(cwdpath, M_TEMP, cwdpathlen);
- return (error);
- }
- cwd = bp;
- cwdlen = (bpend - bp);
-
- /* NUL included in cwd component */
- builtlen = cwdlen + 1 + strlen(origpath);
- builtpath = malloc(builtlen, M_TEMP, M_WAITOK);
- snprintf(builtpath, builtlen, "%s/%s", cwd, origpath);
- fullpath = builtpath;
- free(cwdpath, M_TEMP, cwdpathlen);
-
- //printf("namei: builtpath = %s %lld strlen %lld\n", builtpath,
- // (long long)builtlen, (long long)strlen(builtpath));
- } else
- fullpath = path;
-
- canopath = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
- error = canonpath(fullpath, canopath, MAXPATHLEN);
-
- free(builtpath, M_TEMP, builtlen);
- if (error != 0) {
- free(canopath, M_TEMP, MAXPATHLEN);
- return (pledge_fail(p, EPERM, PLEDGE_RPATH));
- }
-
- //printf("namei: canopath = %s strlen %lld\n", canopath,
- // (long long)strlen(canopath));
-
- error = ENOENT;
- for (i = 0; i < wl->wl_count && wl->wl_paths[i].name && error; i++) {
- if (strncmp(canopath, wl->wl_paths[i].name,
- wl->wl_paths[i].len - 1) == 0) {
- u_char term = canopath[wl->wl_paths[i].len - 1];
-
- if (term == '\0' || term == '/' ||
- wl->wl_paths[i].name[1] == '\0')
- error = 0;
- }
- }
- free(canopath, M_TEMP, MAXPATHLEN);
- return (error); /* Don't hint why it failed */
- }
-
- if (p->p_p->ps_pledge & PLEDGE_RPATH)
- return (0);
- if (p->p_p->ps_pledge & PLEDGE_WPATH)
- return (0);
- if (p->p_p->ps_pledge & PLEDGE_CPATH)
- return (0);
-
- return (pledge_fail(p, EPERM, PLEDGE_RPATH));
-}
-
-void
-pledge_aftersyscall(struct proc *p, int code, int error)
-{
- if ((p->p_pledgeafter & TMA_YPLOCK) && error == 0)
- atomic_setbits_int(&p->p_p->ps_pledge, PLEDGE_YP_ACTIVE | PLEDGE_INET);
- if ((p->p_pledgeafter & TMA_DNSRESOLV) && error == 0)
- atomic_setbits_int(&p->p_p->ps_pledge, PLEDGE_DNS_ACTIVE);
-}
-
-/*
- * By default, only the advisory cmsg's can be received from the kernel,
- * such as TIMESTAMP ntpd.
- *
- * If PLEDGE_RECVFD is set SCM_RIGHTS is also allowed in for a carefully
- * selected set of descriptors (specifically to exclude directories).
- *
- * This results in a kill upon recv, if some other process on the system
- * send a SCM_RIGHTS to an open socket of some sort. That will discourage
- * leaving such sockets lying around...
- */
-int
-pledge_cmsg_recv(struct proc *p, struct mbuf *control)
-{
- struct msghdr tmp;
- struct cmsghdr *cmsg;
- int *fdp, fd;
- struct file *fp;
- int nfds, i;
-
- if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
- return (0);
-
- /* Scan the cmsg */
- memset(&tmp, 0, sizeof(tmp));
- tmp.msg_control = mtod(control, struct cmsghdr *);
- tmp.msg_controllen = control->m_len;
- cmsg = CMSG_FIRSTHDR(&tmp);
-
- while (cmsg != NULL) {
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_RIGHTS)
- break;
- cmsg = CMSG_NXTHDR(&tmp, cmsg);
- }
-
- /* No SCM_RIGHTS found -> OK */
- if (cmsg == NULL)
- return (0);
-
- if ((p->p_p->ps_pledge & PLEDGE_RECVFD) == 0)
- return pledge_fail(p, EPERM, PLEDGE_RECVFD);
-
- /* In OpenBSD, a CMSG only contains one SCM_RIGHTS. Check it. */
- fdp = (int *)CMSG_DATA(cmsg);
- nfds = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(*cmsg))) /
- sizeof(struct file *);
- for (i = 0; i < nfds; i++) {
- struct vnode *vp;
-
- fd = *fdp++;
- fp = fd_getfile(p->p_fd, fd);
- if (fp == NULL)
- return pledge_fail(p, EBADF, PLEDGE_RECVFD);
-
- /* Only allow passing of sockets, pipes, and pure files */
- switch (fp->f_type) {
- case DTYPE_SOCKET:
- case DTYPE_PIPE:
- continue;
- case DTYPE_VNODE:
- vp = (struct vnode *)fp->f_data;
- if (vp->v_type == VREG)
- continue;
- break;
- default:
- break;
- }
- return pledge_fail(p, EPERM, PLEDGE_RECVFD);
- }
- return (0);
-}
-
-/*
- * When pledged, default prevents sending of a cmsg.
- *
- * Unlike pledge_cmsg_recv pledge_cmsg_send is called with individual
- * cmsgs one per mbuf. So no need to loop or scan.
- */
-int
-pledge_cmsg_send(struct proc *p, struct mbuf *control)
-{
- struct cmsghdr *cmsg;
- int *fdp, fd;
- struct file *fp;
- int nfds, i;
-
- if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
- return (0);
-
- if ((p->p_p->ps_pledge & PLEDGE_SENDFD) == 0)
- return pledge_fail(p, EPERM, PLEDGE_SENDFD);
-
- /* Scan the cmsg */
- cmsg = mtod(control, struct cmsghdr *);
-
- /* Contains no SCM_RIGHTS, so OK */
- if (!(cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_RIGHTS))
- return (0);
-
- /* In OpenBSD, a CMSG only contains one SCM_RIGHTS. Check it. */
- fdp = (int *)CMSG_DATA(cmsg);
- nfds = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(*cmsg))) /
- sizeof(struct file *);
- for (i = 0; i < nfds; i++) {
- struct vnode *vp;
-
- fd = *fdp++;
- fp = fd_getfile(p->p_fd, fd);
- if (fp == NULL)
- return pledge_fail(p, EBADF, PLEDGE_SENDFD);
-
- /* Only allow passing of sockets, pipes, and pure files */
- switch (fp->f_type) {
- case DTYPE_SOCKET:
- case DTYPE_PIPE:
- continue;
- case DTYPE_VNODE:
- vp = (struct vnode *)fp->f_data;
- if (vp->v_type == VREG)
- continue;
- break;
- default:
- break;
- }
- /* Not allowed to send a bad fd type */
- return pledge_fail(p, EPERM, PLEDGE_SENDFD);
- }
- return (0);
-}
-
-int
-pledge_sysctl_check(struct proc *p, int miblen, int *mib, void *new)
-{
- if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
- return (0);
-
- if (new)
- return (EFAULT);
-
- /* routing table observation */
- if ((p->p_p->ps_pledge & PLEDGE_ROUTE)) {
- if (miblen == 7 &&
- mib[0] == CTL_NET && mib[1] == PF_ROUTE &&
- mib[2] == 0 &&
- (mib[3] == 0 || mib[3] == AF_INET6 || mib[3] == AF_INET) &&
- mib[4] == NET_RT_DUMP)
- return (0);
-
- if (miblen == 6 &&
- mib[0] == CTL_NET && mib[1] == PF_ROUTE &&
- mib[2] == 0 &&
- (mib[3] == 0 || mib[3] == AF_INET6 || mib[3] == AF_INET) &&
- mib[4] == NET_RT_TABLE)
- return (0);
-
- if (miblen == 7 && /* exposes MACs */
- mib[0] == CTL_NET && mib[1] == PF_ROUTE &&
- mib[2] == 0 && mib[3] == AF_INET &&
- mib[4] == NET_RT_FLAGS && mib[5] == RTF_LLINFO)
- return (0);
- }
-
- if ((p->p_p->ps_pledge & (PLEDGE_ROUTE | PLEDGE_INET))) {
- if (miblen == 6 && /* getifaddrs() */
- mib[0] == CTL_NET && mib[1] == PF_ROUTE &&
- mib[2] == 0 &&
- (mib[3] == 0 || mib[3] == AF_INET6 || mib[3] == AF_INET) &&
- mib[4] == NET_RT_IFLIST)
- return (0);
- }
-
- /* used by ntpd(8) to read sensors. */
- if (miblen >= 3 &&
- mib[0] == CTL_HW && mib[1] == HW_SENSORS)
- return (0);
-
- if (miblen == 2 && /* getdomainname() */
- mib[0] == CTL_KERN && mib[1] == KERN_DOMAINNAME)
- return (0);
- if (miblen == 2 && /* gethostname() */
- mib[0] == CTL_KERN && mib[1] == KERN_HOSTNAME)
- return (0);
- if (miblen == 2 && /* uname() */
- mib[0] == CTL_KERN && mib[1] == KERN_OSTYPE)
- return (0);
- if (miblen == 2 && /* uname() */
- mib[0] == CTL_KERN && mib[1] == KERN_OSRELEASE)
- return (0);
- if (miblen == 2 && /* uname() */
- mib[0] == CTL_KERN && mib[1] == KERN_OSVERSION)
- return (0);
- if (miblen == 2 && /* uname() */
- mib[0] == CTL_KERN && mib[1] == KERN_VERSION)
- return (0);
- if (miblen == 2 && /* uname() */
- mib[0] == CTL_HW && mib[1] == HW_MACHINE)
- return (0);
- if (miblen == 2 && /* getpagesize() */
- mib[0] == CTL_HW && mib[1] == HW_PAGESIZE)
- return (0);
- if (miblen == 2 && /* setproctitle() */
- mib[0] == CTL_VM && mib[1] == VM_PSSTRINGS)
- return (0);
-
- printf("%s(%d): sysctl %d: %d %d %d %d %d %d\n",
- p->p_comm, p->p_pid, miblen, mib[0], mib[1],
- mib[2], mib[3], mib[4], mib[5]);
- return (EFAULT);
-}
-
-int
-pledge_adjtime_check(struct proc *p, const void *v)
-{
- const struct timeval *delta = v;
-
- if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
- return (0);
-
- if (delta)
- return (EFAULT);
- return (0);
-}
-
-int
-pledge_connect_check(struct proc *p)
-{
- if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
- return (0);
-
- if ((p->p_p->ps_pledge & PLEDGE_DNS_ACTIVE))
- return (0); /* A port check happens inside sys_connect() */
-
- if ((p->p_p->ps_pledge & (PLEDGE_INET | PLEDGE_UNIX)))
- return (0);
- return (EPERM);
-}
-
-int
-pledge_recvfrom_check(struct proc *p, void *v)
-{
- struct sockaddr *from = v;
-
- if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
- return (0);
-
- if ((p->p_p->ps_pledge & PLEDGE_DNS_ACTIVE) && from == NULL)
- return (0);
- if (p->p_p->ps_pledge & PLEDGE_INET)
- return (0);
- if (p->p_p->ps_pledge & PLEDGE_UNIX)
- return (0);
- if (from == NULL)
- return (0); /* behaves just like write */
- return (EPERM);
-}
-
-int
-pledge_sendto_check(struct proc *p, const void *v)
-{
- const struct sockaddr *to = v;
-
- if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
- return (0);
-
- if ((p->p_p->ps_pledge & PLEDGE_DNS_ACTIVE) && to == NULL)
- return (0);
-
- if ((p->p_p->ps_pledge & PLEDGE_INET))
- return (0);
- if ((p->p_p->ps_pledge & PLEDGE_UNIX))
- return (0);
- if (to == NULL)
- return (0); /* behaves just like write */
- return (EPERM);
-}
-
-int
-pledge_socket_check(struct proc *p, int domain)
-{
- if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
- return (0);
- if ((p->p_p->ps_pledge & (PLEDGE_INET | PLEDGE_UNIX)))
- return (0);
- if ((p->p_p->ps_pledge & PLEDGE_DNS_ACTIVE) &&
- (domain == AF_INET || domain == AF_INET6))
- return (0);
- return (EPERM);
-}
-
-int
-pledge_bind_check(struct proc *p, const void *v)
-{
-
- if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
- return (0);
- if ((p->p_p->ps_pledge & PLEDGE_INET))
- return (0);
- return (EPERM);
-}
-
-int
-pledge_ioctl_check(struct proc *p, long com, void *v)
-{
- struct file *fp = v;
- struct vnode *vp = NULL;
-
- if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
- return (0);
-
- /*
- * The ioctl's which are always allowed.
- */
- switch (com) {
- case FIONREAD:
- case FIONBIO:
- return (0);
- }
-
- if (fp == NULL)
- return (EBADF);
- vp = (struct vnode *)fp->f_data;
-
- /*
- * Further sets of ioctl become available, but are checked a
- * bit more carefully against the vnode.
- */
- if ((p->p_p->ps_pledge & PLEDGE_IOCTL)) {
- switch (com) {
- case FIOCLEX:
- case FIONCLEX:
- case FIOASYNC:
- case FIOSETOWN:
- case FIOGETOWN:
- return (0);
- case TIOCGETA:
- case TIOCGPGRP:
- case TIOCGWINSZ: /* various programs */
- if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY))
- return (0);
- break;
- case BIOCGSTATS: /* bpf: tcpdump privsep on ^C */
- if (fp->f_type == DTYPE_VNODE &&
- fp->f_ops->fo_ioctl == vn_ioctl)
- return (0);
- break;
- case MTIOCGET:
- case MTIOCTOP:
- /* for pax(1) and such, checking tapes... */
- if (fp->f_type == DTYPE_VNODE &&
- (vp->v_type == VCHR || vp->v_type == VBLK))
- return (0);
- break;
- case SIOCGIFGROUP:
- if ((p->p_p->ps_pledge & PLEDGE_INET) &&
- fp->f_type == DTYPE_SOCKET)
- return (0);
- break;
- }
- }
-
- if ((p->p_p->ps_pledge & PLEDGE_ROUTE)) {
- switch (com) {
- case SIOCGIFADDR:
- case SIOCGIFFLAGS:
- case SIOCGIFRDOMAIN:
- if (fp->f_type == DTYPE_SOCKET)
- return (0);
- break;
- }
- }
-
- if ((p->p_p->ps_pledge & PLEDGE_TTY)) {
- switch (com) {
- case TIOCSPGRP:
- if ((p->p_p->ps_pledge & PLEDGE_PROC) == 0)
- break;
- /* FALTHROUGH */
- case TIOCGETA:
- case TIOCGPGRP:
- case TIOCGWINSZ: /* various programs */
-#if notyet
- case TIOCSTI: /* ksh? csh? */
-#endif
- case TIOCSBRK: /* cu */
- case TIOCCDTR: /* cu */
- case TIOCSETA: /* cu, ... */
- case TIOCSETAW: /* cu, ... */
- case TIOCSETAF: /* tcsetattr TCSAFLUSH, script */
- if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY))
- return (0);
- break;
- }
- }
-
- return pledge_fail(p, EPERM, PLEDGE_IOCTL);
-}
-
-int
-pledge_setsockopt_check(struct proc *p, int level, int optname)
-{
- if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
- return (0);
-
- /* common case for PLEDGE_UNIX and PLEDGE_INET */
- switch (level) {
- case SOL_SOCKET:
- switch (optname) {
- case SO_RTABLE:
- return (EPERM);
- }
- return (0);
- }
-
- if ((p->p_p->ps_pledge & PLEDGE_INET) == 0)
- return (EPERM);
-
- switch (level) {
- case IPPROTO_TCP:
- switch (optname) {
- case TCP_NODELAY:
- case TCP_MD5SIG:
- case TCP_SACK_ENABLE:
- case TCP_MAXSEG:
- case TCP_NOPUSH:
- return (0);
- }
- break;
- case IPPROTO_IP:
- switch (optname) {
- case IP_TOS:
- case IP_TTL:
- case IP_MINTTL:
- case IP_PORTRANGE:
- case IP_RECVDSTADDR:
- return (0);
- case IP_MULTICAST_IF:
- case IP_ADD_MEMBERSHIP:
- case IP_DROP_MEMBERSHIP:
- if ((p->p_p->ps_pledge & PLEDGE_MCAST) == 0)
- return (0);
- break;
- }
- break;
- case IPPROTO_ICMP:
- break;
- case IPPROTO_IPV6:
- switch (optname) {
- case IPV6_UNICAST_HOPS:
- case IPV6_RECVHOPLIMIT:
- case IPV6_PORTRANGE:
- case IPV6_RECVPKTINFO:
-#ifdef notyet
- case IPV6_V6ONLY:
-#endif
- return (0);
- case IPV6_MULTICAST_IF:
- case IPV6_JOIN_GROUP:
- case IPV6_LEAVE_GROUP:
- if ((p->p_p->ps_pledge & PLEDGE_MCAST) == 0)
- return (0);
- break;
- }
- break;
- case IPPROTO_ICMPV6:
- break;
- }
- return (EPERM);
-}
-
-int
-pledge_dns_check(struct proc *p, in_port_t port)
-{
- if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
- return (0);
-
- if ((p->p_p->ps_pledge & PLEDGE_INET))
- return (0);
- if ((p->p_p->ps_pledge & PLEDGE_DNS_ACTIVE) && port == htons(53))
- return (0); /* Allow a DNS connect outbound */
- return (EPERM);
-}
-
-void
-pledge_dropwpaths(struct process *pr)
-{
- if (pr->ps_pledgepaths && --pr->ps_pledgepaths->wl_ref == 0) {
- struct whitepaths *wl = pr->ps_pledgepaths;
- int i;
-
- for (i = 0; i < wl->wl_count; i++)
- free(wl->wl_paths[i].name, M_TEMP, wl->wl_paths[i].len);
- free(wl, M_TEMP, wl->wl_size);
- }
- pr->ps_pledgepaths = NULL;
-}
-
-int
-canonpath(const char *input, char *buf, size_t bufsize)
-{
- char *p, *q, *s, *end;
-
- /* can't canon relative paths, don't bother */
- if (input[0] != '/') {
- if (strlcpy(buf, input, bufsize) >= bufsize)
- return (ENAMETOOLONG);
- return (0);
- }
-
- /* easiest to work with strings always ending in '/' */
- if (snprintf(buf, bufsize, "%s/", input) >= bufsize)
- return (ENAMETOOLONG);
-
- /* after this we will only be shortening the string. */
- p = buf;
- q = p;
- while (*p) {
- if (p[0] == '/' && p[1] == '/') {
- p += 1;
- } else if (p[0] == '/' && p[1] == '.' &&
- p[2] == '/') {
- p += 2;
- } else {
- *q++ = *p++;
- }
- }
- *q = 0;
-
- end = buf + strlen(buf);
- s = buf;
- p = s;
- while (1) {
- /* find "/../" (where's strstr when you need it?) */
- while (p < end) {
- if (p[0] == '/' && strncmp(p + 1, "../", 3) == 0)
- break;
- p++;
- }
- if (p == end)
- break;
- if (p == s) {
- memmove(s, p + 3, end - p - 3 + 1);
- end -= 3;
- } else {
- /* s starts with '/', so we know there's one
- * somewhere before p. */
- q = p - 1;
- while (*q != '/')
- q--;
- memmove(q, p + 3, end - p - 3 + 1);
- end -= p + 3 - q;
- p = q;
- }
- }
- if (end > s + 1)
- *(end - 1) = 0; /* remove trailing '/' */
-
- return 0;
-}