Hi,

Sharing my patch in case someone finds it useful. Didn't have much to offer 
besides donations and CD purchases all these years. 

Tested on amd64 6.5-stable (Changes are not arch-dependent) : base + Xorg + vmd 
+ ntpd + sshd + chromium : work fine
Patch is against current src tree as on 2019-06-15.

The current thoroughly done pledge(2) mechanism goes deep in denying dangerous 
functionality to pledged processes. Depth is apparent in sysctl(2), ioctl(2) 
and namei(9) scrutiny. 

The only issue is that it is voluntary :
1. Attacker's code will not make a pledge.
2. Unpledged software is missing out on this extra security.
3. The model is that the program does all of it's prep-work early and then 
makes a pledge. But during the prep-work no restrictions are imposed. This is 
needed for processes which start as root and daemons, but for the rest of 
unprivileged processes, leaves a window of unrestricted behaviour.


This patch is a kernel change which enforces a default set of promises and a 
syscall whitelist in the form of promises on all unprivileged processes.


This is how it works :
+ The root processes are left untouched. This is to provide a way via doas(1) 
to gain restricted functionality. Also ensures that boot and init scripts are 
not interfered with. Of course, privsep'ed daemons will cotinue to work.

+ We have a default promises list which includes all the functionality needed 
for Xorg (_x11), chromium, vmd, ntpd and all /bin /sbin tools. This list looks 
big but reflects reality. Still, better to have additional checks and 
hard-coded enforcements such as ACCESSPERMS, than not.

+ All subsequent pledge(2) calls are enforced to be subsets of the default 
promises. This is enforced silently to let over-pledged programs go past 
pledge(2) calls into program flows where they may not exercise any restricted 
functionality, which is caught anyway. In short, allow more programs to work 
for more command line options or control flows without modification. pledge(2) 
itself is not the end, syscall is, hence.

+ execve(2), set.*uid(2) syscall handlers set the default promises when uid 
changes to or is non-zero.

+ A syscall whitelist (same as default promises) is enforced early in the chain 
for unprivileged porcesses. Independant of pledge(2) mechanism, this 
effectively ensures all unprivileged software is prevented from dangerous 
operations early. This was optional, but I wanted it this way.

+ Default promises are immutable right now since they are already very 
comprehensive. For more secure server/firmware requirements, sys admins may 
want to further tighten it. If folks are interested, this can be exposed as a 
sysctl setting which can be set early on boot before securelevel is raised to 1 
or 2.

+ Removed PLEDGE_ERROR or "error" promise to ensure malicious/compromised 
process doesn't get to live. Detailed error message gives enough information to 
know why it aborted. This whole patch is about enforcement rather than 
voluntary restrictions.


Advantages of using this patch :
+ Everything runs pledged whether explicitly or otherwise (root can be 
discounted since trying to contain root is futile). Hence, more security than 
the current default, especially for ports and software from non-OpenBSD sources.

+ Default promises set provides a single knob to control what you want to 
permit on your machine based on the intended functionality.

+ Slightly more informative pledge violation error messages


Disadvantages of using this patch :
+ Available syscalls list has shrunk a little and they have become more 
restrictive. The kernel contract with software changed since this is now 
default for all unprivileged processes.

+ Current assumptions of programmers are not exactly correct anymore. Those who 
use pledge(2) understand what they are getting into, but default promises 
changes that for those who didn't pledge(2) or don't care or can't pledge(2).

+ May induce people to open up their doas.conf too much or turn on setuid bit 
more.

+ Of course, compiling your own kernel may reduce chances of getting help on 
mailing lists.


Do let me know if modifications are needed or there are any questions.
Also have the same patch ready against 6.5-stable if needed.

Srikant T.
--


--- ./sys/arch/amd64/amd64/vmm.c.orig   Sat Jun 15 13:49:35 2019
+++ ./sys/arch/amd64/amd64/vmm.c        Sat Jun 15 15:26:42 2019
@@ -763,7 +763,7 @@ vm_find(uint32_t id, struct vm **res)
                        if (((p->p_p->ps_pledge &
                            (PLEDGE_VMM|PLEDGE_PROC)) == PLEDGE_VMM) &&
                            (vm->vm_creator_pid != p->p_p->ps_pid))
-                               return (pledge_fail(p, EPERM, PLEDGE_VMM));
+                               return (pledge_fail(p, EPERM, PLEDGE_VMM, 
"vm_find"));
                        *res = vm;
                        return (0);
                }
--- ./sys/kern/kern_pledge.c.orig       Sat Jun 15 13:50:39 2019
+++ ./sys/kern/kern_pledge.c    Sat Jun 15 15:26:42 2019
@@ -212,6 +212,7 @@ const uint64_t pledge_syscalls[SYS_MAXSYSCALL] = {
 
        [SYS_fstat] = PLEDGE_STDIO,
        [SYS_fsync] = PLEDGE_STDIO,
+       [SYS_sync] = PLEDGE_STDIO,
 
        [SYS_setsockopt] = PLEDGE_STDIO,        /* narrow whitelist */
        [SYS_getsockopt] = PLEDGE_STDIO,        /* narrow whitelist */
@@ -225,6 +226,7 @@ const uint64_t pledge_syscalls[SYS_MAXSYSCALL] = {
        [SYS_dup3] = PLEDGE_STDIO,
        [SYS_closefrom] = PLEDGE_STDIO,
        [SYS_shutdown] = PLEDGE_STDIO,
+       [SYS_reboot] = PLEDGE_STDIO,
        [SYS_fchdir] = PLEDGE_STDIO,    /* XXX consider tightening */
 
        [SYS_pipe] = PLEDGE_STDIO,
@@ -258,6 +260,7 @@ const uint64_t pledge_syscalls[SYS_MAXSYSCALL] = {
        [SYS_adjtime] = PLEDGE_STDIO,   /* setting requires "settime" */
        [SYS_adjfreq] = PLEDGE_SETTIME,
        [SYS_settimeofday] = PLEDGE_SETTIME,
+       [SYS_clock_settime] = PLEDGE_SETTIME,
 
        /*
         * Needed by threaded programs
@@ -291,6 +294,7 @@ const uint64_t pledge_syscalls[SYS_MAXSYSCALL] = {
        [SYS_setresgid] = PLEDGE_ID,
        [SYS_setgroups] = PLEDGE_ID,
        [SYS_setlogin] = PLEDGE_ID,
+       [SYS_chroot] = PLEDGE_ID,
 
        [SYS_unveil] = PLEDGE_UNVEIL,
 
@@ -360,10 +364,16 @@ const uint64_t pledge_syscalls[SYS_MAXSYSCALL] = {
        [SYS_accept4] = PLEDGE_INET | PLEDGE_UNIX,
        [SYS_accept] = PLEDGE_INET | PLEDGE_UNIX,
        [SYS_getpeername] = PLEDGE_INET | PLEDGE_UNIX,
+       
+       [SYS_setrtable] = PLEDGE_WROUTE,
 
        [SYS_flock] = PLEDGE_FLOCK | PLEDGE_YPACTIVE,
 
-       [SYS_swapctl] = PLEDGE_VMINFO,  /* XXX should limit to "get" operations 
*/
+       [SYS_swapctl] = PLEDGE_VMINFO, /* XXX should limit to "get" ops */
+
+       [SYS_mount] = PLEDGE_MOUNT,
+       [SYS_unmount] = PLEDGE_MOUNT,
+
 };
 
 static const struct {
@@ -406,9 +416,41 @@ static const struct {
        { "vmm",                PLEDGE_VMM },
        { "wpath",              PLEDGE_WPATH },
        { "wroute",             PLEDGE_WROUTE },
+       { "mount",              PLEDGE_MOUNT },
 };
 
 int
+is_unpledged_superuser(struct proc *p) {
+       return ((0 == (p->p_p->ps_flags & PS_PLEDGE))
+               && (0 == p->p_ucred->cr_uid));
+}
+
+int
+is_unpledged_regular_user(struct proc *p) {
+       return ((0 == (p->p_p->ps_flags & PS_PLEDGE))
+               && (p->p_ucred->cr_uid != 0));
+}
+
+int
+is_unpledged_xorg(struct proc *p) {
+       return ((0 == (p->p_p->ps_flags & PS_PLEDGE))
+               && (35 == p->p_ucred->cr_uid));
+}
+
+void
+set_default_promises(struct proc *p) {
+       /* XXX cludge to let Xorg function */
+       if (35 == p->p_ucred->cr_uid)
+               return;
+
+       if (p->p_ucred->cr_uid != 0) {
+               p->p_p->ps_pledge = DEFAULT_PROMISES;
+               p->p_p->ps_flags |= PS_PLEDGE;
+       }
+       return;
+}
+
+int
 parsepledges(struct proc *p, const char *kname, const char *promises, 
u_int64_t *fp)
 {
        size_t rbuflen;
@@ -462,16 +504,15 @@ sys_pledge(struct proc *p, void *v, register_t *retval
                if (error)
                        return (error);
 
-               /* In "error" mode, ignore promise increase requests,
-                * but accept promise decrease requests */
-               if (ISSET(pr->ps_flags, PS_PLEDGE) &&
-                   (pr->ps_pledge & PLEDGE_ERROR))
-                       promises &= (pr->ps_pledge & PLEDGE_USERSET);
-
                /* Only permit reductions */
-               if (ISSET(pr->ps_flags, PS_PLEDGE) &&
-                   (((promises | pr->ps_pledge) != pr->ps_pledge)))
-                       return (EPERM);
+/*
+ * Being lenient with over-pledged software. We enforce DEFAULT_PROMISES next
+ * Uncomment this if you remove DEFAULT_PROMISES
+ *
+ *             if (ISSET(pr->ps_flags, PS_PLEDGE) &&
+ *                 (((promises | pr->ps_pledge) != pr->ps_pledge)))
+ *                     return (EPERM);
+ */
        }
        if (SCARG(uap, execpromises)) {
                error = parsepledges(p, "pledgeexecreq",
@@ -480,13 +521,21 @@ sys_pledge(struct proc *p, void *v, register_t *retval
                        return (error);
 
                /* Only permit reductions */
-               if (ISSET(pr->ps_flags, PS_EXECPLEDGE) &&
-                   (((execpromises | pr->ps_execpledge) != pr->ps_execpledge)))
-                       return (EPERM);
+/*
+ * Being lenient with over-pledged software. We enforce DEFAULT_PROMISES next
+ * Uncomment this if you remove DEFAULT_PROMISES
+ *
+ *             if (ISSET(pr->ps_flags, PS_EXECPLEDGE) &&
+ *                 (((execpromises | pr->ps_execpledge) != pr->ps_execpledge)))
+ *                     return (EPERM);
+ */
        }
 
        if (SCARG(uap, promises)) {
                pr->ps_pledge = promises;
+               /* Enforce DEFAULT_PROMISES silently */
+               if (suser(p) != 0)
+                       p->p_p->ps_pledge &= DEFAULT_PROMISES;
                pr->ps_flags |= PS_PLEDGE;
                /*
                 * Kill off unveil and drop unveil vnode refs if we no
@@ -499,6 +548,9 @@ sys_pledge(struct proc *p, void *v, register_t *retval
        }
        if (SCARG(uap, execpromises)) {
                pr->ps_execpledge = execpromises;
+               /* Enforce DEFAULT_PROMISES silently */
+               if (suser(p) != 0)
+                       p->p_p->ps_execpledge &= DEFAULT_PROMISES;
                pr->ps_flags |= PS_EXECPLEDGE;
        }
        return (0);
@@ -516,15 +568,35 @@ pledge_syscall(struct proc *p, int code, uint64_t *tva
        if (pledge_syscalls[code] == PLEDGE_ALWAYS)
                return (0);
 
-       if (p->p_p->ps_pledge & pledge_syscalls[code])
+       /* We don't try to contain root processes */
+       if (is_unpledged_superuser(p))
                return (0);
 
-       *tval = pledge_syscalls[code];
-       return (EPERM);
+       /* XXX cludge to let Xorg function */
+       if (is_unpledged_xorg(p))
+               return (0);
+
+       /*
+        * syscall whitelist in the form of promises
+        * Has nothing to do with the pledge mechanism
+        */
+       if ((suser(p) != 0)
+               && (0 == (pledge_syscalls[code] & DEFAULT_PROMISES))) {
+               *tval = pledge_syscalls[code];
+               return (EPERM);
+       }
+
+       if (ISSET(p->p_p->ps_flags, PS_PLEDGE)
+               && (0 == (p->p_p->ps_pledge & pledge_syscalls[code]))) {
+               *tval = pledge_syscalls[code];
+               return (EPERM);
+       }
+
+       return (0);
 }
 
 int
-pledge_fail(struct proc *p, int error, uint64_t code)
+pledge_fail(struct proc *p, int error, uint64_t code, char *caller)
 {
        char *codes = "";
        int i;
@@ -540,12 +612,17 @@ pledge_fail(struct proc *p, int error, uint64_t code)
        if (KTRPOINT(p, KTR_PLEDGE))
                ktrpledge(p, error, code, p->p_pledge_syscall);
 #endif
-       if (p->p_p->ps_pledge & PLEDGE_ERROR)
-               return (ENOSYS);
 
        KERNEL_LOCK();
-       log(LOG_ERR, "%s[%d]: pledge \"%s\", syscall %d\n",
-           p->p_p->ps_comm, p->p_p->ps_pid, codes, p->p_pledge_syscall);
+       if (ISSET(p->p_p->ps_flags, PS_PLEDGE)) {
+               log(LOG_ERR, "%s[uid:%d][pid:%d][pledged]: \"%s\", syscall %d, 
promises 0x%llx, caller %s\n",
+                       p->p_p->ps_comm, p->p_ucred->cr_uid, p->p_p->ps_pid,
+                       codes, p->p_pledge_syscall, p->p_p->ps_pledge, caller);
+       } else {
+               log(LOG_ERR, "%s[uid:%d][pid:%d][NOT pledged]: \"%s\", syscall 
%d, promises 0x%llx, caller %s\n",
+                       p->p_p->ps_comm, p->p_ucred->cr_uid, p->p_p->ps_pid,
+                       codes, p->p_pledge_syscall, p->p_p->ps_pledge, caller);
+       }
        p->p_p->ps_acflag |= APLEDGE;
 
        /* Send uncatchable SIGABRT for coredump */
@@ -562,7 +639,9 @@ pledge_fail(struct proc *p, int error, uint64_t code)
 
 /*
  * Need to make it more obvious that one cannot get through here
- * without the right flags set
+ * without the right flags set.
+ *
+ * This gets called from namei() to handle pledge related checks
  */
 int
 pledge_namei(struct proc *p, struct nameidata *ni, char *origpath)
@@ -570,12 +649,18 @@ pledge_namei(struct proc *p, struct nameidata *ni, cha
        char path[PATH_MAX];
        int error;
 
-       if ((p->p_p->ps_flags & PS_PLEDGE) == 0 ||
-           (p->p_p->ps_flags & PS_COREDUMP))
+       if (ISSET(p->p_p->ps_flags, PS_COREDUMP)
+               || !ISSET(p->p_p->ps_flags, PS_PLEDGE))
                return (0);
 
+       /*
+        * ni_pledge is not populated for initial procs : swapper and init
+        * So, we ignore pledge checks for these
+        */
+       if (p->p_p->ps_pid <= 1)
+               return (0);
        if (ni->ni_pledge == 0)
-               panic("pledge_namei: ni_pledge");
+               panic("pledge_namei: ni_pledge is not initialized");
 
        /*
         * We set the BYPASSUNVEIL flag to skip unveil checks
@@ -628,7 +713,7 @@ pledge_namei(struct proc *p, struct nameidata *ni, cha
                                ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
                                return (0);
                        } else
-                               return (pledge_fail(p, error, PLEDGE_GETPW));
+                               return (pledge_fail(p, error, PLEDGE_GETPW, 
"pledge_namei:getpw"));
                }
                break;
        case SYS_open:
@@ -743,7 +828,7 @@ pledge_namei(struct proc *p, struct nameidata *ni, cha
         * ps_pledge.
         */
        if (ni->ni_pledge & ~p->p_p->ps_pledge)
-               return (pledge_fail(p, EPERM, (ni->ni_pledge & 
~p->p_p->ps_pledge)));
+               return (pledge_fail(p, EPERM, (ni->ni_pledge & 
~p->p_p->ps_pledge), "pledge_namei:ni_pledge==ps_pledge"));
 
        /* continue, and check unveil if present */
        return (0);
@@ -757,10 +842,11 @@ pledge_recvfd(struct proc *p, struct file *fp)
 {
        struct vnode *vp;
 
-       if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
+       if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
                return (0);
+
        if ((p->p_p->ps_pledge & PLEDGE_RECVFD) == 0)
-               return pledge_fail(p, EPERM, PLEDGE_RECVFD);
+               return pledge_fail(p, EPERM, PLEDGE_RECVFD, 
"pledge_recvfd:pledge_chk");
 
        switch (fp->f_type) {
        case DTYPE_SOCKET:
@@ -773,7 +859,7 @@ pledge_recvfd(struct proc *p, struct file *fp)
                if (vp->v_type != VDIR)
                        return (0);
        }
-       return pledge_fail(p, EINVAL, PLEDGE_RECVFD);
+       return pledge_fail(p, EINVAL, PLEDGE_RECVFD, 
"pledge_recvfd:default_deny");
 }
 
 /*
@@ -784,10 +870,11 @@ pledge_sendfd(struct proc *p, struct file *fp)
 {
        struct vnode *vp;
 
-       if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
+       if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
                return (0);
+
        if ((p->p_p->ps_pledge & PLEDGE_SENDFD) == 0)
-               return pledge_fail(p, EPERM, PLEDGE_SENDFD);
+               return pledge_fail(p, EPERM, PLEDGE_SENDFD, 
"pledge_sendfd:pledge_chk");
 
        switch (fp->f_type) {
        case DTYPE_SOCKET:
@@ -801,7 +888,7 @@ pledge_sendfd(struct proc *p, struct file *fp)
                        return (0);
                break;
        }
-       return pledge_fail(p, EINVAL, PLEDGE_SENDFD);
+       return pledge_fail(p, EINVAL, PLEDGE_SENDFD, 
"pledge_sendfd:default_deny");
 }
 
 int
@@ -810,11 +897,11 @@ pledge_sysctl(struct proc *p, int miblen, int *mib, vo
        char    buf[80];
        int     i;
 
-       if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
+       if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
                return (0);
 
        if (new)
-               return pledge_fail(p, EFAULT, 0);
+               return pledge_fail(p, EFAULT, 0, "pledge_sysctl:new");
 
        /* routing table observation */
        if ((p->p_p->ps_pledge & PLEDGE_ROUTE)) {
@@ -988,13 +1075,13 @@ pledge_sysctl(struct proc *p, int miblen, int *mib, vo
        }
        log(LOG_ERR, "%s\n", buf);
 
-       return pledge_fail(p, EINVAL, 0);
+       return pledge_fail(p, EINVAL, 0, "pledge_sysctl:default_deny");
 }
 
 int
 pledge_chown(struct proc *p, uid_t uid, gid_t gid)
 {
-       if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
+       if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
                return (0);
 
        if (p->p_p->ps_pledge & PLEDGE_CHOWNUID)
@@ -1012,7 +1099,7 @@ pledge_adjtime(struct proc *p, const void *v)
 {
        const struct timeval *delta = v;
 
-       if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
+       if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
                return (0);
 
        if ((p->p_p->ps_pledge & PLEDGE_SETTIME))
@@ -1025,14 +1112,14 @@ pledge_adjtime(struct proc *p, const void *v)
 int
 pledge_sendit(struct proc *p, const void *to)
 {
-       if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
+       if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
                return (0);
 
        if ((p->p_p->ps_pledge & (PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS | 
PLEDGE_YPACTIVE)))
                return (0);             /* may use address */
        if (to == NULL)
                return (0);             /* behaves just like write */
-       return pledge_fail(p, EPERM, PLEDGE_INET);
+       return pledge_fail(p, EPERM, PLEDGE_INET, "pledge_sendit");
 }
 
 int
@@ -1041,7 +1128,7 @@ pledge_ioctl(struct proc *p, long com, struct file *fp
        struct vnode *vp = NULL;
        int error = EPERM;
 
-       if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
+       if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
                return (0);
 
        /*
@@ -1308,13 +1395,13 @@ pledge_ioctl(struct proc *p, long com, struct file *fp
        }
 #endif
 
-       return pledge_fail(p, error, PLEDGE_TTY);
+       return pledge_fail(p, error, PLEDGE_TTY, "pledge_ioctl:default_deny");
 }
 
 int
 pledge_sockopt(struct proc *p, int set, int level, int optname)
 {
-       if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
+       if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
                return (0);
 
        /* Always allow these, which are too common to reject */
@@ -1329,7 +1416,7 @@ pledge_sockopt(struct proc *p, int set, int level, int
        }
 
        if ((p->p_p->ps_pledge & 
(PLEDGE_INET|PLEDGE_UNIX|PLEDGE_DNS|PLEDGE_YPACTIVE)) == 0)
-               return pledge_fail(p, EPERM, PLEDGE_INET);
+               return pledge_fail(p, EPERM, PLEDGE_INET, 
"pledge_sockopt:pledge_chk1");
        /* In use by some service libraries */
        switch (level) {
        case SOL_SOCKET:
@@ -1372,18 +1459,18 @@ pledge_sockopt(struct proc *p, int set, int level, int
        }
 
        if ((p->p_p->ps_pledge & (PLEDGE_INET|PLEDGE_UNIX)) == 0)
-               return pledge_fail(p, EPERM, PLEDGE_INET);
+               return pledge_fail(p, EPERM, PLEDGE_INET, 
"pledge_sockopt:pledge_chk2");
        switch (level) {
        case SOL_SOCKET:
                switch (optname) {
                case SO_RTABLE:
-                       return pledge_fail(p, EINVAL, PLEDGE_INET);
+                       return pledge_fail(p, EINVAL, PLEDGE_INET, 
"pledge_sockopt:SO_RTABLE");
                }
                return (0);
        }
 
        if ((p->p_p->ps_pledge & PLEDGE_INET) == 0)
-               return pledge_fail(p, EPERM, PLEDGE_INET);
+               return pledge_fail(p, EPERM, PLEDGE_INET, 
"pledge_sockopt:pledge_chk3");
        switch (level) {
        case IPPROTO_TCP:
                switch (optname) {
@@ -1445,19 +1532,19 @@ pledge_sockopt(struct proc *p, int set, int level, int
        case IPPROTO_ICMPV6:
                break;
        }
-       return pledge_fail(p, EPERM, PLEDGE_INET);
+       return pledge_fail(p, EPERM, PLEDGE_INET, 
"pledge_sockopt:default_deny");
 }
 
 int
 pledge_socket(struct proc *p, int domain, unsigned int state)
 {
-       if (! ISSET(p->p_p->ps_flags, PS_PLEDGE))
-               return 0;
+       if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
+               return (0);
 
        if (ISSET(state, SS_DNS)) {
                if (ISSET(p->p_p->ps_pledge, PLEDGE_DNS))
                        return 0;
-               return pledge_fail(p, EPERM, PLEDGE_DNS);
+               return pledge_fail(p, EPERM, PLEDGE_DNS, 
"pledge_socket:SS_DNS");
        }
 
        switch (domain) {
@@ -1468,33 +1555,34 @@ pledge_socket(struct proc *p, int domain, unsigned int
                if (ISSET(p->p_p->ps_pledge, PLEDGE_INET) ||
                    ISSET(p->p_p->ps_pledge, PLEDGE_YPACTIVE))
                        return 0;
-               return pledge_fail(p, EPERM, PLEDGE_INET);
+               return pledge_fail(p, EPERM, PLEDGE_INET, 
"pledge_socket:pledge_chk1");
 
        case AF_UNIX:
                if (ISSET(p->p_p->ps_pledge, PLEDGE_UNIX))
                        return 0;
-               return pledge_fail(p, EPERM, PLEDGE_UNIX);
+               return pledge_fail(p, EPERM, PLEDGE_UNIX, 
"pledge_socket:pledge_chk2");
        }
 
-       return pledge_fail(p, EINVAL, PLEDGE_INET);
+       return pledge_fail(p, EINVAL, PLEDGE_INET, 
"pledge_socket:default_deny");
 }
 
 int
 pledge_flock(struct proc *p)
 {
-       if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
+       if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
                return (0);
 
        if ((p->p_p->ps_pledge & PLEDGE_FLOCK))
                return (0);
-       return (pledge_fail(p, EPERM, PLEDGE_FLOCK));
+       return (pledge_fail(p, EPERM, PLEDGE_FLOCK, "pledge_flock"));
 }
 
 int
 pledge_swapctl(struct proc *p)
 {
-       if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
+       if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
                return (0);
+
        return (EPERM);
 }
 
@@ -1520,35 +1608,38 @@ pledgereq_flags(const char *req_name)
 int
 pledge_fcntl(struct proc *p, int cmd)
 {
-       if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
+       if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
                return (0);
+
        if ((p->p_p->ps_pledge & PLEDGE_PROC) == 0 && cmd == F_SETOWN)
-               return pledge_fail(p, EPERM, PLEDGE_PROC);
+               return pledge_fail(p, EPERM, PLEDGE_PROC, "pledge_fcntl");
        return (0);
 }
 
 int
 pledge_kill(struct proc *p, pid_t pid)
 {
-       if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
-               return 0;
+       if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
+               return (0);
+
        if (p->p_p->ps_pledge & PLEDGE_PROC)
                return 0;
        if (pid == 0 || pid == p->p_p->ps_pid)
                return 0;
-       return pledge_fail(p, EPERM, PLEDGE_PROC);
+       return pledge_fail(p, EPERM, PLEDGE_PROC, "pledge_kill");
 }
 
 int
 pledge_protexec(struct proc *p, int prot)
 {
-       if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
-               return 0;
+       if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
+               return (0);
+
        /* Before kbind(2) call, ld.so and crt may create EXEC mappings */
        if (p->p_p->ps_kbind_addr == 0 && p->p_p->ps_kbind_cookie == 0)
                return 0;
        if (!(p->p_p->ps_pledge & PLEDGE_PROTEXEC) && (prot & PROT_EXEC))
-               return pledge_fail(p, EPERM, PLEDGE_PROTEXEC);
+               return pledge_fail(p, EPERM, PLEDGE_PROTEXEC, 
"pledge_protexec");
        return 0;
 }
 
--- ./sys/kern/vfs_syscalls.c.orig      Sat Jun 15 13:50:40 2019
+++ ./sys/kern/vfs_syscalls.c   Sat Jun 15 15:26:42 2019
@@ -136,6 +136,7 @@ sys_mount(struct proc *p, void *v, register_t *retval)
         * Get vnode to be covered
         */
        NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspath, p);
+       nd.ni_pledge = PLEDGE_MOUNT;
        if ((error = namei(&nd)) != 0)
                goto fail;
        vp = nd.ni_vp;
@@ -379,6 +380,7 @@ sys_unmount(struct proc *p, void *v, register_t *retva
 
        NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
            SCARG(uap, path), p);
+       nd.ni_pledge = PLEDGE_MOUNT;
        if ((error = namei(&nd)) != 0)
                return (error);
        vp = nd.ni_vp;
@@ -826,6 +828,7 @@ sys_chroot(struct proc *p, void *v, register_t *retval
                return (error);
        NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
            SCARG(uap, path), p);
+       nd.ni_pledge = PLEDGE_ID;
        if ((error = change_dir(&nd, p)) != 0)
                return (error);
        if (fdp->fd_rdir != NULL) {
@@ -1125,7 +1128,7 @@ doopenat(struct proc *p, int fd, const char *path, int
        }
 
        cmode = ((mode &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT;
-       if ((p->p_p->ps_flags & PS_PLEDGE))
+       if (ISSET(p->p_p->ps_flags, PS_PLEDGE))
                cmode &= ACCESSPERMS;
        NDINITAT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fd, path, p);
        nd.ni_pledge = ni_pledge;
@@ -2250,7 +2253,7 @@ dofchmodat(struct proc *p, int fd, const char *path, m
 
        if (mode & ~(S_IFMT | ALLPERMS))
                return (EINVAL);
-       if ((p->p_p->ps_flags & PS_PLEDGE))
+       if (ISSET(p->p_p->ps_flags, PS_PLEDGE))
                mode &= ACCESSPERMS;
        if (flag & ~AT_SYMLINK_NOFOLLOW)
                return (EINVAL);
@@ -2292,7 +2295,7 @@ sys_fchmod(struct proc *p, void *v, register_t *retval
 
        if (mode & ~(S_IFMT | ALLPERMS))
                return (EINVAL);
-       if ((p->p_p->ps_flags & PS_PLEDGE))
+       if (ISSET(p->p_p->ps_flags, PS_PLEDGE))
                mode &= ACCESSPERMS;
 
        if ((error = getvnode(p, SCARG(uap, fd), &fp)) != 0)
--- ./sys/kern/kern_prot.c.orig Sat Jun 15 13:50:39 2019
+++ ./sys/kern/kern_prot.c      Sat Jun 15 15:26:42 2019
@@ -49,6 +49,7 @@
 #include <sys/proc.h>
 #include <sys/filedesc.h>
 #include <sys/pool.h>
+#include <sys/pledge.h>
 
 #include <sys/mount.h>
 #include <sys/syscallargs.h>
@@ -413,6 +414,8 @@ sys_setresuid(struct proc *p, void *v, register_t *ret
        pr->ps_ucred = newcred;
        atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
 
+       set_default_promises(p);
+
        /* now that we can sleep, transfer proc count to new user */
        if (ruid != (uid_t)-1 && ruid != pruc->cr_ruid) {
                chgproccnt(pruc->cr_ruid, -1);
@@ -680,6 +683,8 @@ sys_setreuid(struct proc *p, void *v, register_t *retv
        pr->ps_ucred = newcred;
        atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
 
+       set_default_promises(p);
+
        /* now that we can sleep, transfer proc count to new user */
        if (ruid != (uid_t)-1 && ruid != pruc->cr_ruid) {
                chgproccnt(pruc->cr_ruid, -1);
@@ -736,6 +741,8 @@ sys_setuid(struct proc *p, void *v, register_t *retval
        pr->ps_ucred = newcred;
        atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
 
+       set_default_promises(p);
+
        /*
         * Transfer proc count to new user.
         */
@@ -778,6 +785,9 @@ sys_seteuid(struct proc *p, void *v, register_t *retva
        newcred->cr_uid = euid;
        pr->ps_ucred = newcred;
        atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
+
+       set_default_promises(p);
+
        crfree(pruc);
        return (0);
 }
--- ./sys/kern/kern_exec.c.orig Sat Jun 15 13:50:39 2019
+++ ./sys/kern/kern_exec.c      Sat Jun 15 15:26:42 2019
@@ -537,6 +537,7 @@ sys_execve(struct proc *p, void *v, register_t *retval
        } else {
                atomic_clearbits_int(&pr->ps_flags, PS_PLEDGE);
                pr->ps_pledge = 0;
+               pr->ps_execpledge = 0;
                /* XXX XXX XXX XXX */
                /* Clear our unveil paths out so the child
                 * starts afresh
@@ -648,6 +649,8 @@ sys_execve(struct proc *p, void *v, register_t *retval
                pr->ps_ucred = cred;
                crfree(ocred);
        }
+
+       set_default_promises(p);
 
        if (pr->ps_flags & PS_SUGIDEXEC) {
                int i, s = splclock();
--- ./sys/kern/kern_event.c.orig        Sat Jun 15 13:50:39 2019
+++ ./sys/kern/kern_event.c     Sat Jun 15 15:26:42 2019
@@ -214,9 +214,9 @@ filt_procattach(struct knote *kn)
 {
        struct process *pr;
 
-       if ((curproc->p_p->ps_flags & PS_PLEDGE) &&
-           (curproc->p_p->ps_pledge & PLEDGE_PROC) == 0)
-               return pledge_fail(curproc, EPERM, PLEDGE_PROC);
+       if (!ISSET(curproc->p_p->ps_pledge, PLEDGE_PROC)
+               && ISSET(curproc->p_p->ps_flags, PS_PLEDGE))
+               return pledge_fail(curproc, EPERM, PLEDGE_PROC, 
"filt_procattach");
 
        if (kn->kn_id > PID_MAX)
                return ESRCH;
--- ./sys/kern/uipc_syscalls.c.orig     Sat Jun 15 13:50:40 2019
+++ ./sys/kern/uipc_syscalls.c  Sat Jun 15 15:26:42 2019
@@ -150,8 +150,8 @@ dns_portcheck(struct proc *p, struct socket *so, void 
                        error = 0;
 #endif
        }
-       if (error && p->p_p->ps_flags & PS_PLEDGE)
-               return (pledge_fail(p, EPERM, PLEDGE_DNS));
+       if (error && ISSET(p->p_p->ps_flags, PS_PLEDGE))
+               return (pledge_fail(p, EPERM, PLEDGE_DNS, "dns_portcheck"));
        return error;
 }
 
--- ./sys/sys/pledge.h.orig     Sat Jun 15 13:50:45 2019
+++ ./sys/sys/pledge.h  Sat Jun 15 15:26:42 2019
@@ -63,6 +63,7 @@
 #define PLEDGE_WROUTE  0x0000000800000000ULL   /* interface address ioctls */
 #define PLEDGE_UNVEIL  0x0000001000000000ULL   /* allow unveil() */
 #define PLEDGE_VIDEO   0x0000002000000000ULL   /* video ioctls */
+#define PLEDGE_MOUNT   0x0000004000000000ULL   /* mount(2) or unmount(2) */
 
 /*
  * Bits outside PLEDGE_USERSET are used by the kernel itself
@@ -113,14 +114,48 @@ static struct {
        { PLEDGE_WROUTE,        "wroute" },
        { PLEDGE_UNVEIL,        "unveil" },
        { PLEDGE_VIDEO,         "video" },
+       { PLEDGE_MOUNT,         "mount" },
        { 0, NULL },
 };
 #endif
 
 #ifdef _KERNEL
 
+#define DEFAULT_PROMISES \
+               ( PLEDGE_STDIO \
+               | PLEDGE_PROC \
+               | PLEDGE_EXEC \
+               | PLEDGE_PROTEXEC \
+               | PLEDGE_UNVEIL \
+               | PLEDGE_TMPPATH \
+               | PLEDGE_TTY \
+               | PLEDGE_RPATH \
+               | PLEDGE_WPATH \
+               | PLEDGE_CPATH \
+               | PLEDGE_FATTR \
+               | PLEDGE_FLOCK \
+               | PLEDGE_ID \
+               | PLEDGE_PS \
+               | PLEDGE_VMINFO \
+               | PLEDGE_CHOWN \
+               | PLEDGE_CHOWNUID \
+               | PLEDGE_INET \
+               | PLEDGE_UNIX \
+               | PLEDGE_DNS \
+               | PLEDGE_MCAST \
+               | PLEDGE_ROUTE \
+               | PLEDGE_RECVFD \
+               | PLEDGE_SENDFD \
+               | PLEDGE_VMM \
+               | PLEDGE_GETPW)
+
+int     is_unpledged_superuser(struct proc *);
+int     is_unpledged_regular_user(struct proc *);
+int    is_unpledged_xorg(struct proc *);
+void    set_default_promises(struct proc *);
+
 int    pledge_syscall(struct proc *, int, uint64_t *);
-int    pledge_fail(struct proc *, int, uint64_t);
+int    pledge_fail(struct proc *, int, uint64_t, char *);
 
 struct mbuf;
 struct nameidata;
--- ./sys/sys/syscall_mi.h.orig Sat Jun 15 13:50:46 2019
+++ ./sys/sys/syscall_mi.h      Sat Jun 15 15:26:42 2019
@@ -81,11 +81,10 @@ mi_syscall(struct proc *p, register_t code, const stru
 
        if (lock)
                KERNEL_LOCK();
-       pledged = (p->p_p->ps_flags & PS_PLEDGE);
-       if (pledged && (error = pledge_syscall(p, code, &tval))) {
+       if ((error = pledge_syscall(p, code, &tval))) {
                if (!lock)
                        KERNEL_LOCK();
-               error = pledge_fail(p, error, tval);
+               error = pledge_fail(p, error, tval, "mi_syscall");
                KERNEL_UNLOCK();
                return (error);
        }
--- ./lib/libc/sys/pledge.2.orig        Sat Jun 15 15:45:40 2019
+++ ./lib/libc/sys/pledge.2     Sat Jun 15 18:00:31 2019
@@ -75,6 +75,71 @@ or
 .Ar execpromises
 specifies to not change the current value.
 .Pp
+When
+.Nm pledge
+is called with higher
+.Ar promises
+or
+.Ar execpromises ,
+those changes will be ignored and return success.
+This is useful when a parent enforces
+.Ar execpromises
+but an execve'd child has a different idea.
+.Pp
+All new unprivileged processes are automatically pledged to a set of
+.Ar default promises
+upon creation via
+.Xr execve 2 .
+When a process changes it's
+.Ar effective uid
+to an unprivileged
+.Ar uid
+via system calls:
+.Xr setuid 2 ,
+.Xr seteuid 2 ,
+.Xr setreuid 2 or
+.Xr setresuid 2 ,
+it too is automatically assigned 
+.Ar default promises .
+Subsequent calls to
+.Nm pledge
+are only allowed to be a subset of
+.Ar default promises .
+.Bl -ohang -offset indent
+.It Xo
+The current set of
+.Ar default promises
+consists of:
+.Xc
+.It Xo
+.Va stdio ,
+.Va proc ,
+.Va exec ,
+.Va prot_exec ,
+.Va unveil ,
+.Va tmppath ,
+.Va tty ,
+.Va rpath ,
+.Va wpath ,
+.Va cpath ,
+.Va fattr ,
+.Va flock ,
+.Va id ,
+.Va ps ,
+.Va vminfo ,
+.Va chown ,
+.Va inet ,
+.Va unix ,
+.Va dns ,
+.Va mcast ,
+.Va route ,
+.Va recvfd ,
+.Va sendfd ,
+.Va vmm and
+.Va getpw .
+.Xc
+.El
+.Pp
 Some system calls, when allowed, have restrictions applied to them:
 .Bl -ohang -offset indent
 .It Xr access 2 :
@@ -579,20 +644,12 @@ device.
 Allow
 .Xr unveil 2
 to be called.
-.It Va error
-Rather than killing the process upon violation, indicate error with
-.Er ENOSYS .
-.Pp
-Also when
-.Nm pledge
-is called with higher
-.Ar promises
-or
-.Ar execpromises ,
-those changes will be ignored and return success.
-This is useful when a parent enforces
-.Ar execpromises
-but an execve'd child has a different idea.
+.It Va mount
+Allow
+.Xr mount 2
+and
+.Xr unmount 2
+to be called.
 .El
 .Sh RETURN VALUES
 .Rv -std


---
END.

Reply via email to