#include <sys/param.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/mutex.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/namei.h>
#include <sys/socketvar.h>
#include <sys/vnode.h>
#include <sys/mman.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/ktrace.h>
#include <sys/acct.h>
#include <sys/swap.h>
#include <sys/ioctl.h>
#include <sys/termios.h>
#include <sys/tty.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/dkio.h>
#include <sys/mtio.h>
#include <sys/audioio.h>
#include <sys/videoio.h>
#include <net/bpf.h>
#include <net/route.h>
#include <net/if.h>
#include <net/if_var.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <netinet6/nd6.h>
#include <netinet/tcp.h>
#include <net/pfvar.h>
#include <sys/conf.h>
#include <sys/specdev.h>
#include <sys/signal.h>
#include <sys/signalvar.h>
#include <sys/syscall.h>
#include <sys/syscallargs.h>
#include <sys/systm.h>
#include <dev/biovar.h>
#define PLEDGENAMES
#include <sys/pledge.h>
#include "audio.h"
#include "bpfilter.h"
#include "pf.h"
#include "video.h"
#include "pty.h"
#if defined(__amd64__)
#include "vmm.h"
#include "psp.h"
#include <machine/conf.h>
#endif
#include "drm.h"
uint64_t pledgereq_flags(const char *req);
int parsepledges(struct proc *p, const char *kname,
const char *promises, u_int64_t *fp);
int checkpledgepaths(const char *path);
void unveil_destroy(struct process *ps);
const uint64_t pledge_syscalls[SYS_MAXSYSCALL] = {
[SYS_exit] = PLEDGE_ALWAYS,
[SYS_kbind] = PLEDGE_ALWAYS,
[SYS___get_tcb] = PLEDGE_ALWAYS,
[SYS___set_tcb] = PLEDGE_ALWAYS,
[SYS_pledge] = PLEDGE_ALWAYS,
[SYS_sendsyslog] = PLEDGE_ALWAYS,
[SYS_thrkill] = PLEDGE_ALWAYS,
[SYS_utrace] = PLEDGE_ALWAYS,
[SYS_pinsyscalls] = PLEDGE_ALWAYS,
[SYS_getuid] = PLEDGE_STDIO,
[SYS_geteuid] = PLEDGE_STDIO,
[SYS_getresuid] = PLEDGE_STDIO,
[SYS_getgid] = PLEDGE_STDIO,
[SYS_getegid] = PLEDGE_STDIO,
[SYS_getresgid] = PLEDGE_STDIO,
[SYS_getgroups] = PLEDGE_STDIO,
[SYS_getlogin_r] = PLEDGE_STDIO,
[SYS_getpgrp] = PLEDGE_STDIO,
[SYS_getpgid] = PLEDGE_STDIO,
[SYS_getppid] = PLEDGE_STDIO,
[SYS_getsid] = PLEDGE_STDIO,
[SYS_getthrid] = PLEDGE_STDIO,
[SYS_getrlimit] = PLEDGE_STDIO,
[SYS_getrtable] = PLEDGE_STDIO,
[SYS_gettimeofday] = PLEDGE_STDIO,
[SYS_getdtablecount] = PLEDGE_STDIO,
[SYS_getrusage] = PLEDGE_STDIO,
[SYS_issetugid] = PLEDGE_STDIO,
[SYS_clock_getres] = PLEDGE_STDIO,
[SYS_clock_gettime] = PLEDGE_STDIO,
[SYS_getpid] = PLEDGE_STDIO,
[SYS_sysctl] = PLEDGE_STDIO,
[SYS_profil] = PLEDGE_STDIO,
[SYS_getentropy] = PLEDGE_STDIO,
[SYS_madvise] = PLEDGE_STDIO,
[SYS_minherit] = PLEDGE_STDIO,
[SYS_mmap] = PLEDGE_STDIO,
[SYS_mprotect] = PLEDGE_STDIO,
[SYS_mimmutable] = PLEDGE_STDIO,
[SYS_mquery] = PLEDGE_STDIO,
[SYS_munmap] = PLEDGE_STDIO,
[SYS_msync] = PLEDGE_STDIO,
[SYS_break] = PLEDGE_STDIO,
[SYS_umask] = PLEDGE_STDIO,
[SYS_read] = PLEDGE_STDIO,
[SYS_readv] = PLEDGE_STDIO,
[SYS_pread] = PLEDGE_STDIO,
[SYS_preadv] = PLEDGE_STDIO,
[SYS_write] = PLEDGE_STDIO,
[SYS_writev] = PLEDGE_STDIO,
[SYS_pwrite] = PLEDGE_STDIO,
[SYS_pwritev] = PLEDGE_STDIO,
[SYS_recvmsg] = PLEDGE_STDIO,
[SYS_recvmmsg] = PLEDGE_STDIO,
[SYS_recvfrom] = PLEDGE_STDIO,
[SYS_ftruncate] = PLEDGE_STDIO,
[SYS_lseek] = PLEDGE_STDIO,
[SYS_fpathconf] = PLEDGE_STDIO,
[SYS_sendto] = PLEDGE_STDIO,
[SYS_sendmsg] = PLEDGE_STDIO,
[SYS_sendmmsg] = PLEDGE_STDIO,
[SYS_nanosleep] = PLEDGE_STDIO,
[SYS_sigaltstack] = PLEDGE_STDIO,
[SYS_sigprocmask] = PLEDGE_STDIO,
[SYS_sigsuspend] = PLEDGE_STDIO,
[SYS_sigaction] = PLEDGE_STDIO,
[SYS_sigreturn] = PLEDGE_STDIO,
[SYS_sigpending] = PLEDGE_STDIO,
[SYS_getitimer] = PLEDGE_STDIO,
[SYS_setitimer] = PLEDGE_STDIO,
[SYS_poll] = PLEDGE_STDIO,
[SYS_ppoll] = PLEDGE_STDIO,
[SYS_kevent] = PLEDGE_STDIO,
[SYS_kqueue] = PLEDGE_STDIO,
[SYS_kqueue1] = PLEDGE_STDIO,
[SYS_select] = PLEDGE_STDIO,
[SYS_pselect] = PLEDGE_STDIO,
[SYS_fstat] = PLEDGE_STDIO,
[SYS_fsync] = PLEDGE_STDIO,
[SYS_setsockopt] = PLEDGE_STDIO,
[SYS_getsockopt] = PLEDGE_STDIO,
[SYS_fcntl] = PLEDGE_STDIO,
[SYS_close] = PLEDGE_STDIO,
[SYS_dup] = PLEDGE_STDIO,
[SYS_dup2] = PLEDGE_STDIO,
[SYS_dup3] = PLEDGE_STDIO,
[SYS_closefrom] = PLEDGE_STDIO,
[SYS_shutdown] = PLEDGE_STDIO,
[SYS_fchdir] = PLEDGE_STDIO,
[SYS_pipe] = PLEDGE_STDIO,
[SYS_pipe2] = PLEDGE_STDIO,
[SYS_socketpair] = PLEDGE_STDIO,
[SYS_wait4] = PLEDGE_STDIO,
[SYS_waitid] = PLEDGE_STDIO,
[SYS_kill] = PLEDGE_STDIO,
[SYS_ioctl] = PLEDGE_STDIO,
[SYS_open] = PLEDGE_RPATH | PLEDGE_WPATH,
[SYS___pledge_open] = PLEDGE_STDIO,
[SYS_stat] = PLEDGE_RPATH,
[SYS_access] = PLEDGE_RPATH,
[SYS_readlink] = PLEDGE_RPATH,
[SYS___realpath] = PLEDGE_RPATH,
[SYS_adjtime] = PLEDGE_STDIO,
[SYS_adjfreq] = PLEDGE_SETTIME,
[SYS_settimeofday] = PLEDGE_SETTIME,
[SYS___tfork] = PLEDGE_STDIO,
[SYS_sched_yield] = PLEDGE_STDIO,
[SYS_futex] = PLEDGE_STDIO,
[SYS___thrsleep] = PLEDGE_STDIO,
[SYS___thrwakeup] = PLEDGE_STDIO,
[SYS___threxit] = PLEDGE_STDIO,
[SYS___thrsigdivert] = PLEDGE_STDIO,
[SYS_getthrname] = PLEDGE_STDIO,
[SYS_setthrname] = PLEDGE_STDIO,
[SYS_fork] = PLEDGE_PROC,
[SYS_vfork] = PLEDGE_PROC,
[SYS_setpgid] = PLEDGE_PROC,
[SYS_setsid] = PLEDGE_PROC,
[SYS_setrlimit] = PLEDGE_PROC | PLEDGE_ID,
[SYS_getpriority] = PLEDGE_PROC | PLEDGE_ID,
[SYS_setpriority] = PLEDGE_PROC | PLEDGE_ID,
[SYS_setuid] = PLEDGE_ID,
[SYS_seteuid] = PLEDGE_ID,
[SYS_setreuid] = PLEDGE_ID,
[SYS_setresuid] = PLEDGE_ID,
[SYS_setgid] = PLEDGE_ID,
[SYS_setegid] = PLEDGE_ID,
[SYS_setregid] = PLEDGE_ID,
[SYS_setresgid] = PLEDGE_ID,
[SYS_setgroups] = PLEDGE_ID,
[SYS_setlogin] = PLEDGE_ID,
[SYS_setrtable] = PLEDGE_ID,
[SYS_unveil] = PLEDGE_UNVEIL,
[SYS_execve] = PLEDGE_EXEC,
[SYS_chdir] = PLEDGE_RPATH,
[SYS_openat] = PLEDGE_RPATH | PLEDGE_WPATH,
[SYS_fstatat] = PLEDGE_RPATH,
[SYS_faccessat] = PLEDGE_RPATH,
[SYS_readlinkat] = PLEDGE_RPATH,
[SYS_lstat] = PLEDGE_RPATH,
[SYS_truncate] = PLEDGE_WPATH,
[SYS_rename] = PLEDGE_RPATH | PLEDGE_CPATH,
[SYS_rmdir] = PLEDGE_CPATH,
[SYS_renameat] = PLEDGE_CPATH,
[SYS_link] = PLEDGE_CPATH,
[SYS_linkat] = PLEDGE_CPATH,
[SYS_symlink] = PLEDGE_CPATH,
[SYS_symlinkat] = PLEDGE_CPATH,
[SYS_unlink] = PLEDGE_CPATH,
[SYS_unlinkat] = PLEDGE_CPATH,
[SYS_mkdir] = PLEDGE_CPATH,
[SYS_mkdirat] = PLEDGE_CPATH,
[SYS_mkfifo] = PLEDGE_DPATH,
[SYS_mkfifoat] = PLEDGE_DPATH,
[SYS_mknod] = PLEDGE_DPATH,
[SYS_mknodat] = PLEDGE_DPATH,
[SYS_revoke] = PLEDGE_TTY,
[SYS___getcwd] = PLEDGE_RPATH,
[SYS_getdents] = PLEDGE_RPATH,
[SYS_getfsstat] = PLEDGE_RPATH,
[SYS_statfs] = PLEDGE_RPATH,
[SYS_fstatfs] = PLEDGE_RPATH,
[SYS_pathconf] = PLEDGE_RPATH,
[SYS_pathconfat] = 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_fchflags] = PLEDGE_FATTR,
[SYS_chown] = PLEDGE_CHOWN,
[SYS_fchownat] = PLEDGE_CHOWN,
[SYS_lchown] = PLEDGE_CHOWN,
[SYS_fchown] = PLEDGE_CHOWN,
[SYS_socket] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS,
[SYS_connect] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS,
[SYS_bind] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS,
[SYS_getsockname] = PLEDGE_STDIO,
[SYS_listen] = PLEDGE_INET | PLEDGE_UNIX,
[SYS_accept4] = PLEDGE_INET | PLEDGE_UNIX,
[SYS_accept] = PLEDGE_INET | PLEDGE_UNIX,
[SYS_getpeername] = PLEDGE_STDIO,
[SYS_flock] = PLEDGE_FLOCK,
[SYS_ypconnect] = PLEDGE_GETPW,
[SYS_swapctl] = PLEDGE_VMINFO,
[SYS_sysarch] = PLEDGE_PROTEXEC,
};
static const struct {
char *name;
uint64_t flags;
} pledgereq[] = {
{ "audio", PLEDGE_AUDIO },
{ "bpf", PLEDGE_BPF },
{ "chown", PLEDGE_CHOWN | PLEDGE_CHOWNUID },
{ "cpath", PLEDGE_CPATH },
{ "disklabel", PLEDGE_DISKLABEL },
{ "dns", PLEDGE_DNS },
{ "dpath", PLEDGE_DPATH },
{ "drm", PLEDGE_DRM },
{ "error", PLEDGE_ERROR },
{ "exec", PLEDGE_EXEC },
{ "fattr", PLEDGE_FATTR | PLEDGE_CHOWN },
{ "flock", PLEDGE_FLOCK },
{ "getpw", PLEDGE_GETPW },
{ "id", PLEDGE_ID },
{ "inet", PLEDGE_INET },
{ "mcast", PLEDGE_MCAST },
{ "pf", PLEDGE_PF },
{ "proc", PLEDGE_PROC },
{ "prot_exec", PLEDGE_PROTEXEC },
{ "ps", PLEDGE_PS },
{ "recvfd", PLEDGE_RECVFD },
{ "route", PLEDGE_ROUTE },
{ "rpath", PLEDGE_RPATH },
{ "sendfd", PLEDGE_SENDFD },
{ "settime", PLEDGE_SETTIME },
{ "stdio", PLEDGE_STDIO },
{ "tape", PLEDGE_TAPE },
{ "tty", PLEDGE_TTY },
{ "unix", PLEDGE_UNIX },
{ "unveil", PLEDGE_UNVEIL },
{ "video", PLEDGE_VIDEO },
{ "vminfo", PLEDGE_VMINFO },
{ "vmm", PLEDGE_VMM },
{ "wpath", PLEDGE_WPATH },
{ "wroute", PLEDGE_WROUTE },
};
int
parsepledges(struct proc *p, const char *kname, const char *promises, u_int64_t *fp)
{
size_t rbuflen;
char *rbuf, *rp, *pn;
u_int64_t flags = 0, f;
int error;
rbuf = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
error = copyinstr(promises, rbuf, MAXPATHLEN, &rbuflen);
if (error) {
free(rbuf, M_TEMP, MAXPATHLEN);
return (error);
}
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrstruct(p, kname, rbuf, rbuflen-1);
#endif
for (rp = rbuf; rp && *rp; rp = pn) {
pn = strchr(rp, ' ');
if (pn) {
while (*pn == ' ')
*pn++ = '\0';
}
if ((f = pledgereq_flags(rp)) == 0) {
free(rbuf, M_TEMP, MAXPATHLEN);
return (EINVAL);
}
flags |= f;
}
free(rbuf, M_TEMP, MAXPATHLEN);
*fp = flags;
return 0;
}
int
sys_pledge(struct proc *p, void *v, register_t *retval)
{
struct sys_pledge_args
*uap = v;
struct process *pr = p->p_p;
uint64_t promises, execpromises;
int error = 0;
int unveil_cleanup = 0;
if (SCARG(uap, promises)) {
error = parsepledges(p, "pledgereq",
SCARG(uap, promises), &promises);
if (error)
return (error);
}
if (SCARG(uap, execpromises)) {
error = parsepledges(p, "pledgeexecreq",
SCARG(uap, execpromises), &execpromises);
if (error)
return (error);
}
mtx_enter(&pr->ps_mtx);
if (SCARG(uap, promises)) {
if (ISSET(pr->ps_flags, PS_PLEDGE) &&
(pr->ps_pledge & PLEDGE_ERROR))
promises &= (pr->ps_pledge & PLEDGE_USERSET);
if (ISSET(pr->ps_flags, PS_PLEDGE) &&
(((promises | pr->ps_pledge) != pr->ps_pledge))) {
error = EPERM;
goto fail;
}
}
if (SCARG(uap, execpromises)) {
if (ISSET(pr->ps_flags, PS_EXECPLEDGE) &&
(((execpromises | pr->ps_execpledge) != pr->ps_execpledge))) {
error = EPERM;
goto fail;
}
}
if (SCARG(uap, promises)) {
pr->ps_pledge = promises;
atomic_setbits_int(&pr->ps_flags, PS_PLEDGE);
if ((pr->ps_pledge & (PLEDGE_RPATH | PLEDGE_WPATH |
PLEDGE_CPATH | PLEDGE_DPATH | PLEDGE_EXEC |
PLEDGE_UNIX | PLEDGE_UNVEIL)) == 0)
unveil_cleanup = 1;
}
if (SCARG(uap, execpromises)) {
pr->ps_execpledge = execpromises;
atomic_setbits_int(&pr->ps_flags, PS_EXECPLEDGE);
}
fail:
mtx_leave(&pr->ps_mtx);
if (unveil_cleanup) {
single_thread_set(p, SINGLE_UNWIND);
KERNEL_LOCK();
unveil_destroy(pr);
KERNEL_UNLOCK();
single_thread_clear(p);
}
return (error);
}
int
pledge_syscall(struct proc *p, int code, uint64_t *tval)
{
p->p_pledge_syscall = code;
*tval = 0;
if (code < 0 || code > SYS_MAXSYSCALL - 1)
return (EINVAL);
if (pledge_syscalls[code] == PLEDGE_ALWAYS)
return (0);
p->p_pledge = READ_ONCE(p->p_p->ps_pledge);
if (p->p_pledge & pledge_syscalls[code])
return (0);
*tval = pledge_syscalls[code];
return (EPERM);
}
int
pledge_fail(struct proc *p, int error, uint64_t code)
{
const char *codes = "";
int i;
for (i = 0; code && pledgenames[i].bits != 0; i++)
if (pledgenames[i].bits & code) {
codes = pledgenames[i].name;
break;
}
#ifdef KTRACE
if (KTRPOINT(p, KTR_PLEDGE))
ktrpledge(p, error, code, p->p_pledge_syscall);
#endif
if (p->p_pledge & PLEDGE_ERROR)
return (ENOSYS);
KERNEL_LOCK();
uprintf("%s[%d]: pledge \"%s\", syscall %d\n",
p->p_p->ps_comm, p->p_p->ps_pid, codes, p->p_pledge_syscall);
p->p_p->ps_acflag |= APLEDGE;
if (P_HASSIBLING(p))
single_thread_set(p, SINGLE_UNWIND | SINGLE_DEEP);
sigabort(p);
p->p_p->ps_pledge = 0;
KERNEL_UNLOCK();
return (error);
}
#ifndef SMALL_KERNEL
#define PLEDGEPATH_NULL 1
#define PLEDGEPATH_TTY 2
#define PLEDGEPATH_SPWD 3
#define PLEDGEPATH_PWD 4
#define PLEDGEPATH_GROUP 5
#define PLEDGEPATH_NETID 6
#define PLEDGEPATH_RESOLVCONF 7
#define PLEDGEPATH_HOSTS 8
#define PLEDGEPATH_SERVICES 9
#define PLEDGEPATH_PROTOCOLS 10
#define PLEDGEPATH_LOCALTIME 11
#define PLEDGEPATH_ZONEINFO 12
static const struct {
char *name;
int item;
} pledgepaths[] = {
{ "/dev/null", PLEDGEPATH_NULL },
{ "/dev/tty", PLEDGEPATH_TTY },
{ "/etc/group", PLEDGEPATH_GROUP },
{ "/etc/hosts", PLEDGEPATH_HOSTS },
{ "/etc/localtime", PLEDGEPATH_LOCALTIME },
{ "/etc/netid", PLEDGEPATH_NETID },
{ "/etc/protocols", PLEDGEPATH_PROTOCOLS },
{ "/etc/pwd.db", PLEDGEPATH_PWD },
{ "/etc/resolv.conf", PLEDGEPATH_RESOLVCONF },
{ "/etc/services", PLEDGEPATH_SERVICES },
{ "/etc/spwd.db", PLEDGEPATH_SPWD },
};
int
checkpledgepaths(const char *path)
{
int base = 0, cmp, i, lim;
for (lim = nitems(pledgepaths); lim != 0; lim >>= 1) {
i = base + (lim >> 1);
cmp = strcmp(path, pledgepaths[i].name);
if (cmp == 0)
return (pledgepaths[i].item);
if (cmp > 0) {
base = i + 1;
lim--;
}
}
return (0);
}
#endif
int
checkzoneinfopath(const char *path)
{
const char *cp;
if (strncmp(path, "/usr/share/zoneinfo/",
sizeof("/usr/share/zoneinfo/") - 1) != 0)
return -1;
for (cp = path + sizeof("/usr/share/zoneinfo/") - 2; *cp; cp++) {
if (cp[0] == '/' &&
cp[1] == '.' && cp[2] == '.' &&
(cp[3] == '/' || cp[3] == '\0'))
return -1;
}
return 0;
}
int
pledge_namei(struct proc *p, struct nameidata *ni, char *path)
{
uint64_t ple, nip;
if ((p->p_p->ps_flags & PS_PLEDGE) == 0 ||
(p->p_p->ps_flags & PS_COREDUMP))
return (0);
ple = p->p_pledge;
nip = ni->ni_pledge;
if (nip == 0)
panic("pledge_namei: ni_pledge");
if ((nip & PLEDGE_EXEC) && (ple & PLEDGE_EXEC))
return (0);
if (ni->ni_unveil & UNVEIL_PLEDGEOPEN) {
#ifdef SMALL_KERNEL
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
#else
int item;
item = checkpledgepaths(path);
if (item == 0 && checkzoneinfopath(path) == 0)
item = PLEDGEPATH_ZONEINFO;
switch (item) {
case 0:
return (pledge_fail(p, EACCES, (nip & ~ple)));
case PLEDGEPATH_NULL:
if ((nip & ~(PLEDGE_RPATH | PLEDGE_WPATH)) == 0)
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
break;
case PLEDGEPATH_TTY:
if ((ple & PLEDGE_TTY) &&
(nip & ~(PLEDGE_RPATH | PLEDGE_WPATH)) == 0)
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
break;
case PLEDGEPATH_SPWD:
if ((ple & PLEDGE_GETPW) && (nip == PLEDGE_RPATH))
return (EPERM);
break;
case PLEDGEPATH_PWD:
case PLEDGEPATH_GROUP:
case PLEDGEPATH_NETID:
if ((ple & PLEDGE_GETPW) && (nip == PLEDGE_RPATH))
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
break;
case PLEDGEPATH_RESOLVCONF:
case PLEDGEPATH_HOSTS:
case PLEDGEPATH_SERVICES:
case PLEDGEPATH_PROTOCOLS:
if ((ple & PLEDGE_DNS) && (nip == PLEDGE_RPATH))
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
break;
case PLEDGEPATH_LOCALTIME:
ni->ni_cnd.cn_flags |= BPU_LOCALTIME;
if (nip == PLEDGE_RPATH)
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
break;
case PLEDGEPATH_ZONEINFO:
ni->ni_cnd.cn_flags |= BPU_ZONEINFO;
if (nip == PLEDGE_RPATH)
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
break;
default:
panic("pledgepaths table is broken");
}
#endif
}
if (ni->ni_cnd.cn_flags & BYPASSUNVEIL)
return (0);
if (nip & ~ple)
return (pledge_fail(p, EPERM, (nip & ~ple)));
return (0);
}
int
pledge_recvfd(struct proc *p, struct file *fp)
{
struct vnode *vp;
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if ((p->p_pledge & PLEDGE_RECVFD) == 0)
return pledge_fail(p, EPERM, PLEDGE_RECVFD);
switch (fp->f_type) {
case DTYPE_SOCKET:
case DTYPE_PIPE:
case DTYPE_DMABUF:
case DTYPE_SYNC:
return (0);
case DTYPE_VNODE:
vp = fp->f_data;
if (vp->v_type != VDIR)
return (0);
}
return (EPERM);
}
int
pledge_sendfd(struct proc *p, struct file *fp)
{
struct vnode *vp;
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if ((p->p_pledge & PLEDGE_SENDFD) == 0)
return pledge_fail(p, EPERM, PLEDGE_SENDFD);
switch (fp->f_type) {
case DTYPE_SOCKET:
case DTYPE_PIPE:
case DTYPE_DMABUF:
case DTYPE_SYNC:
return (0);
case DTYPE_VNODE:
vp = fp->f_data;
if (vp->v_type != VDIR)
return (0);
break;
}
return pledge_fail(p, EINVAL, PLEDGE_SENDFD);
}
#ifndef SMALL_KERNEL
int
pledge_sysctl(struct proc *p, int miblen, int *mib, void *new)
{
uint64_t pledge;
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
pledge = p->p_pledge;
if (new)
return pledge_fail(p, EFAULT, 0);
if ((pledge & PLEDGE_ROUTE)) {
if ((miblen == 6 || miblen == 7) &&
mib[0] == CTL_NET && mib[1] == PF_ROUTE &&
mib[2] == 0 &&
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 || mib[4] == NET_RT_SOURCE))
return (0);
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_FLAGS && mib[5] == RTF_LLINFO)
return (0);
}
if (pledge & (PLEDGE_PS | PLEDGE_VMINFO)) {
if (miblen == 2 &&
mib[0] == CTL_KERN && mib[1] == KERN_FSCALE)
return (0);
if (miblen == 2 &&
mib[0] == CTL_KERN && mib[1] == KERN_BOOTTIME)
return (0);
if (miblen == 2 &&
mib[0] == CTL_KERN && mib[1] == KERN_CONSDEV)
return (0);
if (miblen == 2 &&
mib[0] == CTL_KERN && mib[1] == KERN_CPTIME)
return (0);
if (miblen == 3 &&
mib[0] == CTL_KERN && mib[1] == KERN_CPTIME2)
return (0);
if (miblen == 3 &&
mib[0] == CTL_KERN && mib[1] == KERN_CPUSTATS)
return (0);
}
if ((pledge & PLEDGE_PS)) {
if (miblen == 4 &&
mib[0] == CTL_KERN && mib[1] == KERN_PROC_ARGS &&
(mib[3] == KERN_PROC_ARGV || mib[3] == KERN_PROC_ENV))
return (0);
if (miblen == 6 &&
mib[0] == CTL_KERN && mib[1] == KERN_PROC)
return (0);
if (miblen == 3 &&
mib[0] == CTL_KERN && mib[1] == KERN_PROC_CWD)
return (0);
if (miblen == 2 &&
mib[0] == CTL_KERN && mib[1] == KERN_CCPU)
return (0);
if (miblen == 2 &&
mib[0] == CTL_VM && mib[1] == VM_MAXSLP)
return (0);
}
if ((pledge & PLEDGE_VMINFO)) {
if (miblen == 2 &&
mib[0] == CTL_VM && mib[1] == VM_UVMEXP)
return (0);
if (miblen == 3 &&
mib[0] == CTL_VFS && mib[1] == VFS_GENERIC &&
mib[2] == VFS_BCACHESTAT)
return (0);
if (miblen == 3 &&
mib[0] == CTL_NET && mib[1] == PF_INET6)
return (0);
}
if ((pledge & (PLEDGE_INET | PLEDGE_UNIX))) {
if (miblen == 2 &&
mib[0] == CTL_KERN && mib[1] == KERN_SOMAXCONN)
return (0);
}
if ((pledge & (PLEDGE_ROUTE | PLEDGE_INET | PLEDGE_DNS))) {
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_IFLIST)
return (0);
}
if ((pledge & PLEDGE_DISKLABEL)) {
if (miblen == 2 &&
mib[0] == CTL_KERN &&
mib[1] == KERN_RAWPARTITION)
return (0);
if (miblen == 2 &&
mib[0] == CTL_KERN &&
mib[1] == KERN_MAXPARTITIONS)
return (0);
#ifdef CPU_CHR2BLK
if (miblen == 3 &&
mib[0] == CTL_MACHDEP &&
mib[1] == CPU_CHR2BLK)
return (0);
#endif
}
if (miblen >= 3 &&
mib[0] == CTL_HW && mib[1] == HW_SENSORS)
return (0);
if (miblen == 6 &&
mib[0] == CTL_NET && mib[1] == PF_ROUTE &&
mib[2] == 0 && mib[3] == 0 && mib[4] == NET_RT_IFNAMES)
return (0);
if (miblen == 2) {
switch (mib[0]) {
case CTL_KERN:
switch (mib[1]) {
case KERN_DOMAINNAME:
case KERN_HOSTNAME:
case KERN_OSTYPE:
case KERN_OSRELEASE:
case KERN_OSVERSION:
case KERN_VERSION:
case KERN_CLOCKRATE:
case KERN_ARGMAX:
case KERN_NGROUPS:
case KERN_SYSVSHM:
case KERN_POSIX1:
case KERN_AUTOCONF_SERIAL:
return (0);
}
break;
case CTL_HW:
switch (mib[1]) {
case HW_MACHINE:
case HW_PAGESIZE:
case HW_PHYSMEM64:
case HW_NCPU:
case HW_NCPUONLINE:
case HW_USERMEM64:
return (0);
}
break;
case CTL_VM:
switch (mib[1]) {
case VM_PSSTRINGS:
case VM_LOADAVG:
case VM_MALLOC_CONF:
return (0);
}
break;
default:
break;
}
}
#ifdef CPU_SSE
if (miblen == 2 &&
mib[0] == CTL_MACHDEP && mib[1] == CPU_SSE)
return (0);
#endif
#ifdef CPU_ID_AA64ISAR0
if (miblen == 2 &&
mib[0] == CTL_MACHDEP && mib[1] == CPU_ID_AA64ISAR0)
return (0);
#endif
#ifdef CPU_ID_AA64ISAR1
if (miblen == 2 &&
mib[0] == CTL_MACHDEP && mib[1] == CPU_ID_AA64ISAR1)
return (0);
#endif
return pledge_fail(p, EINVAL, 0);
}
#endif
int
pledge_chown(struct proc *p, uid_t uid, gid_t gid)
{
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if (p->p_pledge & PLEDGE_CHOWNUID)
return (0);
if (uid != -1 && uid != p->p_ucred->cr_uid)
return (EPERM);
if (gid != -1 && !groupmember(gid, p->p_ucred))
return (EPERM);
return (0);
}
int
pledge_adjtime(struct proc *p, const void *v)
{
const struct timeval *delta = v;
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if ((p->p_pledge & PLEDGE_SETTIME))
return (0);
if (delta)
return (EPERM);
return (0);
}
int
pledge_sendit(struct proc *p, const void *to)
{
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if ((p->p_pledge & (PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS)))
return (0);
if (to == NULL)
return (0);
return pledge_fail(p, EPERM, PLEDGE_INET);
}
int
pledge_ioctl(struct proc *p, long com, struct file *fp)
{
struct vnode *vp = NULL;
int error = EPERM;
uint64_t pledge;
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
pledge = p->p_pledge;
switch (com) {
case FIONREAD:
case FIONBIO:
case FIOCLEX:
case FIONCLEX:
return (0);
}
if (fp->f_type == DTYPE_VNODE) {
vp = fp->f_data;
if (vp->v_type == VBAD)
return (ENOTTY);
}
if ((pledge & PLEDGE_INET)) {
switch (com) {
case SIOCATMARK:
case SIOCGIFGROUP:
if (fp->f_type == DTYPE_SOCKET)
return (0);
break;
}
}
#if NBPFILTER > 0
if ((pledge & PLEDGE_BPF)) {
switch (com) {
case BIOCGSTATS:
if (fp->f_type == DTYPE_VNODE &&
fp->f_ops->fo_ioctl == vn_ioctl &&
vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_open == bpfopen)
return (0);
break;
}
}
#endif
if ((pledge & PLEDGE_TAPE)) {
switch (com) {
case MTIOCGET:
case MTIOCTOP:
if (fp->f_type == DTYPE_VNODE &&
vp->v_type == VCHR) {
if (vp->v_flag & VISTTY)
return (ENOTTY);
else
return (0);
}
break;
}
}
#if NDRM > 0
if ((pledge & PLEDGE_DRM)) {
if ((fp->f_type == DTYPE_VNODE) &&
(vp->v_type == VCHR) &&
(cdevsw[major(vp->v_rdev)].d_open == drmopen)) {
error = pledge_ioctl_drm(p, com, vp->v_rdev);
if (error == 0)
return 0;
}
}
#endif
#if NAUDIO > 0
if ((pledge & PLEDGE_AUDIO)) {
switch (com) {
case AUDIO_GETDEV:
case AUDIO_GETPOS:
case AUDIO_GETPAR:
case AUDIO_SETPAR:
case AUDIO_START:
case AUDIO_STOP:
case AUDIO_MIXER_DEVINFO:
case AUDIO_MIXER_READ:
case AUDIO_MIXER_WRITE:
if (fp->f_type == DTYPE_VNODE &&
vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_open == audioopen)
return (0);
}
}
#endif
if ((pledge & PLEDGE_DISKLABEL)) {
switch (com) {
case DIOCGDINFO:
case DIOCGPDINFO:
case DIOCRLDINFO:
case DIOCWDINFO:
case BIOCDISK:
case BIOCINQ:
case BIOCINSTALLBOOT:
case BIOCVOL:
if (fp->f_type == DTYPE_VNODE &&
((vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_type == D_DISK) ||
(vp->v_type == VBLK &&
bdevsw[major(vp->v_rdev)].d_type == D_DISK)))
return (0);
break;
case DIOCMAP:
if (fp->f_type == DTYPE_VNODE &&
vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_ioctl == diskmapioctl)
return (0);
break;
}
}
#if NVIDEO > 0
if ((pledge & PLEDGE_VIDEO)) {
switch (com) {
case VIDIOC_QUERYCAP:
case VIDIOC_TRY_FMT:
case VIDIOC_ENUM_FMT:
case VIDIOC_S_FMT:
case VIDIOC_QUERYCTRL:
case VIDIOC_G_CTRL:
case VIDIOC_S_CTRL:
case VIDIOC_G_PARM:
case VIDIOC_S_PARM:
case VIDIOC_REQBUFS:
case VIDIOC_QBUF:
case VIDIOC_DQBUF:
case VIDIOC_QUERYBUF:
case VIDIOC_STREAMON:
case VIDIOC_STREAMOFF:
case VIDIOC_ENUM_FRAMESIZES:
case VIDIOC_ENUM_FRAMEINTERVALS:
case VIDIOC_DQEVENT:
case VIDIOC_ENCODER_CMD:
case VIDIOC_EXPBUF:
case VIDIOC_G_CROP:
case VIDIOC_G_EXT_CTRLS:
case VIDIOC_G_FMT:
case VIDIOC_G_SELECTION:
case VIDIOC_QUERYMENU:
case VIDIOC_SUBSCRIBE_EVENT:
case VIDIOC_S_EXT_CTRLS:
case VIDIOC_S_SELECTION:
case VIDIOC_TRY_DECODER_CMD:
case VIDIOC_TRY_ENCODER_CMD:
if (fp->f_type == DTYPE_VNODE &&
vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_open == videoopen)
return (0);
break;
}
}
#endif
#if NPF > 0
if ((pledge & PLEDGE_PF)) {
switch (com) {
case DIOCADDRULE:
case DIOCGETSTATUS:
case DIOCNATLOOK:
case DIOCRADDTABLES:
case DIOCRCLRADDRS:
case DIOCRCLRTABLES:
case DIOCRCLRTSTATS:
case DIOCRGETTSTATS:
case DIOCRSETADDRS:
case DIOCXBEGIN:
case DIOCXCOMMIT:
case DIOCKILLSRCNODES:
if (fp->f_type == DTYPE_VNODE &&
vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_open == pfopen)
return (0);
break;
}
}
#endif
if ((pledge & PLEDGE_TTY)) {
switch (com) {
#if NPTY > 0
case PTMGET:
if ((pledge & PLEDGE_RPATH) == 0)
break;
if ((pledge & PLEDGE_WPATH) == 0)
break;
if (fp->f_type == DTYPE_VNODE &&
vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_open == ptmopen)
return (0);
break;
case TIOCUCNTL:
if ((pledge & PLEDGE_RPATH) == 0)
break;
if ((pledge & PLEDGE_WPATH) == 0)
break;
if (fp->f_type == DTYPE_VNODE &&
vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_open == ptcopen)
return (0);
break;
#endif
case TIOCSPGRP:
if ((pledge & PLEDGE_PROC) == 0)
break;
case TIOCFLUSH:
case TIOCSTART:
case TIOCGPGRP:
case TIOCGETA:
case TIOCGWINSZ:
case TIOCSTAT:
if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY))
return (0);
return (ENOTTY);
case TIOCSWINSZ:
case TIOCEXT:
case TIOCCBRK:
case TIOCSBRK:
case TIOCCDTR:
case TIOCSDTR:
case TIOCEXCL:
case TIOCSETA:
case TIOCSETAW:
case TIOCSETAF:
case TIOCSCTTY:
if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY))
return (0);
break;
}
}
if ((pledge & PLEDGE_ROUTE)) {
switch (com) {
case SIOCGIFADDR:
case SIOCGIFAFLAG_IN6:
case SIOCGIFALIFETIME_IN6:
case SIOCGIFDESCR:
case SIOCGIFFLAGS:
case SIOCGIFMETRIC:
case SIOCGIFGMEMB:
case SIOCGIFRDOMAIN:
case SIOCGIFDSTADDR_IN6:
case SIOCGIFNETMASK_IN6:
case SIOCGIFXFLAGS:
case SIOCGNBRINFO_IN6:
case SIOCGIFINFO_IN6:
case SIOCGIFMEDIA:
if (fp->f_type == DTYPE_SOCKET)
return (0);
break;
}
}
if ((pledge & PLEDGE_WROUTE)) {
switch (com) {
case SIOCAIFADDR:
case SIOCDIFADDR:
case SIOCAIFADDR_IN6:
case SIOCDIFADDR_IN6:
if (fp->f_type == DTYPE_SOCKET)
return (0);
break;
case SIOCSIFMTU:
if (fp->f_type == DTYPE_SOCKET)
return (0);
break;
}
}
#if NVMM > 0
if ((pledge & PLEDGE_VMM)) {
if (fp->f_type == DTYPE_VNODE &&
vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_open == vmmopen) {
error = pledge_ioctl_vmm(p, com);
if (error == 0)
return 0;
}
}
#endif
#if NPSP > 0
if ((pledge & PLEDGE_VMM)) {
if (fp->f_type == DTYPE_VNODE &&
vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_open == pspopen) {
error = pledge_ioctl_psp(p, com);
if (error == 0)
return (0);
}
}
#endif
return pledge_fail(p, error, PLEDGE_TTY);
}
int
pledge_sockopt(struct proc *p, int set, int level, int optname)
{
uint64_t pledge;
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
pledge = p->p_pledge;
switch (level) {
case SOL_SOCKET:
switch (optname) {
case SO_RCVBUF:
case SO_ERROR:
return (0);
}
break;
case IPPROTO_TCP:
switch (optname) {
case TCP_NODELAY:
return (0);
}
break;
case IPPROTO_IP:
switch (optname) {
case IP_TOS:
return (0);
}
break;
case IPPROTO_IPV6:
switch (optname) {
case IPV6_TCLASS:
return (0);
}
break;
}
if ((pledge & PLEDGE_WROUTE)) {
switch (level) {
case SOL_SOCKET:
switch (optname) {
case SO_RTABLE:
return (0);
}
}
}
if ((pledge & (PLEDGE_INET|PLEDGE_UNIX|PLEDGE_DNS)) == 0)
return pledge_fail(p, EPERM, PLEDGE_INET);
switch (level) {
case SOL_SOCKET:
switch (optname) {
case SO_TIMESTAMP:
return (0);
}
break;
}
if ((pledge & PLEDGE_DNS)) {
switch (level) {
case IPPROTO_IPV6:
switch (optname) {
case IPV6_RECVPKTINFO:
case IPV6_USE_MIN_MTU:
return (0);
}
}
}
if ((pledge & (PLEDGE_INET|PLEDGE_UNIX)) == 0)
return pledge_fail(p, EPERM, PLEDGE_INET);
switch (level) {
case SOL_SOCKET:
switch (optname) {
case SO_RTABLE:
return pledge_fail(p, EINVAL, PLEDGE_WROUTE);
}
return (0);
}
if ((pledge & PLEDGE_INET) == 0)
return pledge_fail(p, EPERM, PLEDGE_INET);
switch (level) {
case IPPROTO_TCP:
switch (optname) {
case TCP_MD5SIG:
case TCP_SACK_ENABLE:
case TCP_MAXSEG:
case TCP_NOPUSH:
case TCP_INFO:
return (0);
}
break;
case IPPROTO_IP:
switch (optname) {
case IP_OPTIONS:
if (!set)
return (0);
break;
case IP_TTL:
case IP_MINTTL:
case IP_IPDEFTTL:
case IP_PORTRANGE:
case IP_RECVDSTADDR:
case IP_RECVDSTPORT:
return (0);
case IP_MULTICAST_IF:
case IP_MULTICAST_TTL:
case IP_MULTICAST_LOOP:
case IP_ADD_MEMBERSHIP:
case IP_DROP_MEMBERSHIP:
if (pledge & PLEDGE_MCAST)
return (0);
break;
}
break;
case IPPROTO_ICMP:
break;
case IPPROTO_IPV6:
switch (optname) {
case IPV6_DONTFRAG:
case IPV6_UNICAST_HOPS:
case IPV6_MINHOPCOUNT:
case IPV6_RECVHOPLIMIT:
case IPV6_PORTRANGE:
case IPV6_RECVPKTINFO:
case IPV6_RECVDSTPORT:
case IPV6_RECVTCLASS:
case IPV6_V6ONLY:
return (0);
case IPV6_MULTICAST_IF:
case IPV6_MULTICAST_HOPS:
case IPV6_MULTICAST_LOOP:
case IPV6_JOIN_GROUP:
case IPV6_LEAVE_GROUP:
if (pledge & PLEDGE_MCAST)
return (0);
break;
}
break;
case IPPROTO_ICMPV6:
break;
}
return pledge_fail(p, EPERM, PLEDGE_INET);
}
int
pledge_socket(struct proc *p, int domain, unsigned int state)
{
uint64_t pledge;
if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
return 0;
pledge = p->p_pledge;
if (ISSET(state, SS_DNS)) {
if (ISSET(pledge, PLEDGE_DNS))
return 0;
return pledge_fail(p, EPERM, PLEDGE_DNS);
}
switch (domain) {
case -1:
return (0);
case AF_INET:
case AF_INET6:
if (ISSET(pledge, PLEDGE_INET))
return 0;
return pledge_fail(p, EPERM, PLEDGE_INET);
case AF_UNIX:
if (ISSET(pledge, PLEDGE_UNIX))
return 0;
return pledge_fail(p, EPERM, PLEDGE_UNIX);
}
return pledge_fail(p, EINVAL, PLEDGE_INET);
}
int
pledge_flock(struct proc *p)
{
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if ((p->p_pledge & PLEDGE_FLOCK))
return (0);
return (pledge_fail(p, EPERM, PLEDGE_FLOCK));
}
int
pledge_swapctl(struct proc *p, int cmd)
{
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if (p->p_pledge & PLEDGE_VMINFO) {
switch (cmd) {
case SWAP_NSWAP:
case SWAP_STATS:
return (0);
}
}
return pledge_fail(p, EPERM, PLEDGE_VMINFO);
}
uint64_t
pledgereq_flags(const char *req_name)
{
int base = 0, cmp, i, lim;
for (lim = nitems(pledgereq); lim != 0; lim >>= 1) {
i = base + (lim >> 1);
cmp = strcmp(req_name, pledgereq[i].name);
if (cmp == 0)
return (pledgereq[i].flags);
if (cmp > 0) {
base = i + 1;
lim--;
}
}
return (0);
}
int
pledge_fcntl(struct proc *p, int cmd)
{
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if ((p->p_pledge & PLEDGE_PROC) == 0 && cmd == F_SETOWN)
return pledge_fail(p, EPERM, PLEDGE_PROC);
return (0);
}
int
pledge_kill(struct proc *p, pid_t pid)
{
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return 0;
if (p->p_pledge & PLEDGE_PROC)
return 0;
if (pid == 0 || pid == p->p_p->ps_pid)
return 0;
return pledge_fail(p, EPERM, PLEDGE_PROC);
}
int
pledge_protexec(struct proc *p, int prot)
{
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return 0;
if (p->p_p->ps_kbind_addr == 0 && p->p_p->ps_kbind_cookie == 0)
return 0;
if (!(p->p_pledge & PLEDGE_PROTEXEC) && (prot & PROT_EXEC))
return pledge_fail(p, EPERM, PLEDGE_PROTEXEC);
return 0;
}