Hello once again,

I have CC'd the original developers of tame/pledge; hope that is
not in violation of etiquette?  This is my last post on this
subject (or to any devs emails) if interest remains cool, but
it deserves a fair shot, and after all, 500 hours is nothing
to sneeze at!

These non-invasive additions apply cleanly to the latest code of the
three affected source files.  I admit I am still running -current
from mid-April, so I can't test it with these latest files, but the
only change since my mid-April versions was to syscalls.master, and
merged easily.

https://fremissant.net/pledge

This doesn't include patches to insert into <unistd.h>, but it's
sufficient for the present, so that pledge(1) can run at full
capabilities for anyone who has the patched kernel.  Also, the
pledge.2 mdoc patch is not included; but is available at that URL.

I don't think the patch is too long to include on tech@?
Copied below.

The future of pledge(1) depends on your decision.

Thanks for your consideration,
Andy.

Index: sys/kern/kern_pledge.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_pledge.c,v
retrieving revision 1.251
diff -u -p -r1.251 kern_pledge.c
--- sys/kern/kern_pledge.c      14 Feb 2019 15:41:47 -0000      1.251
+++ sys/kern/kern_pledge.c      11 May 2019 23:50:24 -0000
@@ -233,6 +233,11 @@ const uint64_t pledge_syscalls[SYS_MAXSY
 
        [SYS_wait4] = PLEDGE_STDIO,
 
+       [SYS_pledgepid] = PLEDGE_ALWAYS,
+#if 0
+       [SYS_pledgepid] = PLEDGE_PROC,    /* if choose this, move it down */
+#endif
+
        /*
         * Can kill self with "stdio".  Killing another pid
         * requires "proc"
@@ -500,6 +512,219 @@ sys_pledge(struct proc *p, void *v, regi
                pr->ps_execpledge = execpromises;
                pr->ps_flags |= PS_EXECPLEDGE;
        }
+       return (0);
+}
+
+/*
+ * To query, PLEDGEPID_QUERY opts bit must be set, and results are
+ * returned through copyout() to the pbits/epbits pointers.
+ * To set/drop [exec]promises, set opts PLEDGEPID_SET/DROP_[E]PROMS;
+ * the promise data are read from [e]pbits.
+ * It is permissible to combine query and set/drop.
+ * Query only affects the components (pbits/epbits) which were
+ * passed non-NULL.  Passing NULL with set/drop causes current
+ * pledge state in that component to remain unchanged.
+ * Caveat: query destructively overwrites userland data pointed to
+ * by pbits/epbits (when not NULL).
+ * -----
+ * [ based on sys_pledge(), sys/kern/kern_sig.c:cansignal(),sys_kill() ]
+ */
+int
+sys_pledgepid(struct proc *caller_p, void *v, register_t *retval)
+{
+       struct sys_pledgepid_args /* {
+               syscallarg(pid_t)pid;
+               syscallarg(uint32_t)opts;
+               syscallarg(uint64_t *)pbits;
+               syscallarg(uint64_t *)epbits;
+               syscallarg(const void *)aux;
+       } */    *uap = v;
+       uint32_t opts = SCARG(uap, opts);
+       uint64_t u64tmp, *pbits = NULL, *epbits = NULL;
+       struct process *caller_pr = caller_p->p_p;
+       struct process *target_pr;  /* will be proc. corresp. to pid arg. */
+       pid_t pid_caller = caller_pr->ps_pid;
+       pid_t pid_target = SCARG(uap, pid);
+       /* see kern_sig.c:cansignal() */
+       struct ucred *caller_p_uc = caller_p->p_ucred;
+                                    /* ...and not caller_pr->ps_ucred... */
+       struct ucred *target_pr_uc;  /* will use (*process)->ps_ucred */
+       int empowered = 0;
+       int error;
+
+       /*
+        * If caller is under pledge, and pid argument is not that of
+        * the caller (or 0), fail immediately unless hold "proc" promise.
+        */
+       if ((error = pledge_kill(caller_p, pid_target)) != 0)
+               return (error);
+
+       if (pid_target < 0)
+               return (EINVAL);
+
+       if (pid_target == 0) {
+               pid_target = pid_caller;
+               target_pr = caller_pr;
+       } else if((target_pr = prfind(pid_target)) == NULL) {
+               if ((target_pr = zombiefind(pid_target)) == NULL)
+                       return (ESRCH);
+               else {
+                       /*
+                        * Let it go ahead and try to act on the zombie; if
+                        * it's possible to pledge it down as requested, so
+                        * much the better, zombie or not. :)
+                        */
+#if 0
+/* (This happens quite often when pledge(1) tries to set post-init promises
+ * on a command which finishes sooner than the default 400 ms delay.)
+ */
+                       zombie = 1;
+                       uprintf("pledgepid: warn: [%d] zombie\n", pid_target);
+#endif
+               }
+       }
+
+       target_pr_uc = target_pr->ps_ucred;
+
+       if (opts & (PLEDGEPID_SET_PROMS | PLEDGEPID_SET_EPROMS |
+           PLEDGEPID_DROP_PROMS | PLEDGEPID_DROP_EPROMS)) {
+               /* Copy them now, or query will stomp. */
+               if (SCARG(uap, pbits) != NULL) {
+                       pbits = malloc(sizeof(*pbits), M_TEMP, M_WAITOK);
+                       if (copyin(SCARG(uap, pbits), pbits, sizeof(*pbits)))
+                               return (EFAULT);
+                       *pbits &= PLEDGE_USERSET;
+               }
+               if (SCARG(uap, epbits) != NULL) {
+                       epbits = malloc(sizeof(*epbits), M_TEMP, M_WAITOK);
+                       if (copyin(SCARG(uap, epbits), epbits, sizeof(*epbits)))
+                               return (EFAULT);
+                       *epbits &= PLEDGE_USERSET;
+               }
+       }
+
+       /*
+        * At present, any process can query any other process that it
+        * can signal.  Another possibility would be to only allow querying
+        * when the caller has uid 0.  The highest nibble is reserved for
+        * kernel use, so it is safe to repurpose a high bit to additionally
+        * convey the pr->flags PS_PLEDGE/PS_EXECPLEDGE status to userland.
+        */
+       if (opts & PLEDGEPID_QUERY) {
+               if (SCARG(uap, pbits) != NULL) {
+                       u64tmp = target_pr->ps_pledge & PLEDGE_USERSET;
+                       if (!ISSET(target_pr->ps_flags, PS_PLEDGE))
+                               u64tmp |= PLEDGEPID_UNPLEDGED;
+                       if (copyout(&u64tmp, SCARG(uap, pbits), 8))
+                               return (EFAULT);
+               }
+               if (SCARG(uap, epbits) != NULL) {
+                       u64tmp = target_pr->ps_execpledge & PLEDGE_USERSET;
+                       if (!ISSET(target_pr->ps_flags, PS_EXECPLEDGE))
+                               u64tmp |= PLEDGEPID_UNPLEDGED;
+                       if (copyout(&u64tmp, SCARG(uap, epbits), 8))
+                               return (EFAULT);
+               }
+       }
+
+       if (!(opts & PLEDGEPID_MODSET))
+               return (0);
+
+       /*
+        * Note that the additional checks that promises are not augmented
+        * are performed below; not even root is allowed to add promises!
+        */
+
+       if (caller_p_uc->cr_uid == 0)
+               empowered = 1;          /* root can always pledge any pid */
+
+       if (caller_pr == target_pr)
+               empowered = 1;          /* process can always pledge itself */
+
+       /* see sys/kern/kern_sig.c:cansignal() */
+       if (!empowered &&
+           !(caller_p_uc->cr_ruid == target_pr_uc->cr_ruid ||
+           caller_p_uc->cr_ruid == target_pr_uc->cr_svuid ||
+           caller_p_uc->cr_uid == target_pr_uc->cr_ruid ||
+           caller_p_uc->cr_uid == target_pr_uc->cr_svuid))
+               return (EPERM);
+
+       if (pbits == NULL)
+               goto pledgepid_skip_1;
+
+       if (opts & PLEDGEPID_DROP_PROMS) {
+               /* Never augments promises */
+               u64tmp = target_pr->ps_pledge;
+               u64tmp &= ~*pbits; /* already masked by PLEDGE_USERSET */
+               target_pr->ps_pledge = u64tmp;
+#if 1
+               target_pr->ps_flags |= PS_PLEDGE;
+#else
+               atomic_setbits_int(&target_pr->ps_flags, PS_PLEDGE);
+#endif
+       } else if (opts & PLEDGEPID_SET_PROMS) {
+               /* In "error" mode, ignore promise increase requests,
+                * but accept promise decrease requests */
+               if (ISSET(target_pr->ps_flags, PS_PLEDGE) &&
+                   (target_pr->ps_pledge & PLEDGE_ERROR))
+                       *pbits &= target_pr->ps_pledge;
+
+               /* Only permit reductions */
+               if (ISSET(target_pr->ps_flags, PS_PLEDGE) &&
+                   (((*pbits | target_pr->ps_pledge) !=
+                       target_pr->ps_pledge)))
+                           return (EPERM);
+
+               target_pr->ps_pledge = *pbits;
+               target_pr->ps_flags |= PS_PLEDGE;
+       }
+
+pledgepid_skip_1:
+
+       if (epbits == NULL)
+               goto pledgepid_skip_2;
+
+       if (opts & PLEDGEPID_DROP_EPROMS) {
+               /* Never augments execpromises */
+               u64tmp = target_pr->ps_execpledge;
+               u64tmp &= ~*epbits; /* already masked by PLEDGE_USERSET */
+               target_pr->ps_execpledge = u64tmp;
+#if 1
+               target_pr->ps_flags |= PS_EXECPLEDGE;
+#else
+               atomic_setbits_int(&target_pr->ps_flags, PS_EXECPLEDGE);
+#endif
+       } else if (opts & PLEDGEPID_SET_EPROMS) {
+               /* XXX should not such a case also be in pledge(2)? */
+               if (ISSET(target_pr->ps_flags, PS_PLEDGE) &&
+                   (target_pr->ps_pledge & PLEDGE_ERROR))
+                       *epbits &= target_pr->ps_execpledge;
+
+               /* Only permit reductions */
+               if (ISSET(target_pr->ps_flags, PS_EXECPLEDGE) &&
+                   (((*epbits | target_pr->ps_execpledge) !=
+                       target_pr->ps_execpledge)))
+                           return (EPERM);
+
+               target_pr->ps_execpledge = *epbits;
+               target_pr->ps_flags |= PS_EXECPLEDGE;
+       }
+
+pledgepid_skip_2:
+       if (pbits != NULL)
+               free(pbits, M_TEMP, sizeof(*pbits));
+       if (epbits != NULL)
+               free(epbits, M_TEMP, sizeof(*epbits));
+
+       /*
+        * Kill off unveil and drop unveil vnode refs if target process
+        * no longer holds any path-accessing pledge.
+        */
+       if ((target_pr->ps_pledge & (PLEDGE_RPATH | PLEDGE_WPATH |
+           PLEDGE_CPATH | PLEDGE_DPATH | PLEDGE_TMPPATH | PLEDGE_EXEC |
+           PLEDGE_UNIX | PLEDGE_UNVEIL)) == 0)
+               unveil_destroy(target_pr);
+
        return (0);
 }
 
Index: sys/kern/syscalls.master
===================================================================
RCS file: /cvs/src/sys/kern/syscalls.master,v
retrieving revision 1.190
diff -u -p -r1.190 syscalls.master
--- sys/kern/syscalls.master    9 May 2019 20:30:22 -0000       1.190
+++ sys/kern/syscalls.master    11 May 2019 23:50:25 -0000
@@ -226,7 +226,7 @@
 106    STD             { int sys_listen(int s, int backlog); }
 107    STD             { int sys_chflagsat(int fd, const char *path, \
                            u_int flags, int atflags); }
-108    STD             { int sys_pledge(const char *promises, \
+108    STD             { int sys_pledge(const char *promises, \
                            const char *execpromises); }
 109    STD             { int sys_ppoll(struct pollfd *fds, \
                            u_int nfds, const struct timespec *ts, \
@@ -238,7 +238,7 @@
 112    STD             { int sys_sendsyslog(const char *buf, size_t nbyte, \
                            int flags); }
 113    UNIMPL          fktrace
-114    STD             { int sys_unveil(const char *path, \
+114    STD             { int sys_unveil(const char *path, \
                            const char *permissions); }
 115    OBSOL           vtrace
 116    OBSOL           t32_gettimeofday
@@ -565,3 +565,6 @@
 328    OBSOL           __tfork51
 329    STD NOLOCK      { void sys___set_tcb(void *tcb); }
 330    STD NOLOCK      { void *sys___get_tcb(void); }
+331    STD             { int sys_pledgepid(pid_t pid, uint32_t opts, \
+                           uint64_t *pbits, uint64_t *epbits, \
+                           const void *aux); }
Index: sys/sys/pledge.h
===================================================================
RCS file: /cvs/src/sys/sys/pledge.h,v
retrieving revision 1.39
diff -u -p -r1.39 pledge.h
--- sys/sys/pledge.h    21 Jan 2019 20:09:37 -0000      1.39
+++ sys/sys/pledge.h    11 May 2019 23:50:25 -0000
@@ -23,6 +23,35 @@
 #include <sys/cdefs.h>
 
 /*
+ * pledgepid(2) opts flags
+ */
+/* Permissions allowing, the promises data is returned through pbits/epbits: */
+#define PLEDGEPID_QUERY                        0x00000001U
+#if 0
+#define PLEDGEPID__unused_1            0x00000002U
+#endif
+/*
+ * pledge(2)-style operation: sets initial promises if unpledged in that
+ * component; otherwise, sets the given promises if they are a subset
+ * (inclusive) of the target's currently held set; otherwise returns EPERM.
+ */
+#define PLEDGEPID_SET_PROMS            0x00000004U
+#define PLEDGEPID_SET_EPROMS           0x00000008U
+/* Subtractive operation: remove specified promises from an existing pledge: */
+#define PLEDGEPID_DROP_PROMS           0x00000010U
+#define PLEDGEPID_DROP_EPROMS          0x00000020U
+/*
+ * The 4 high bits of uint64_t promises/execpromises are available for
+ * dual (independent) use kernel/userland; these are for userland.
+ */
+#if 0
+#define PLEDGEPID__unused_2    0x1000000000000000ULL
+#define PLEDGEPID__unused_3    0x2000000000000000ULL
+#endif
+#define PLEDGEPID_SUBTRACTIVE  0x4000000000000000ULL
+#define PLEDGEPID_UNPLEDGED    0x8000000000000000ULL
+
+/*
  * pledge(2) requests
  */
 #define PLEDGE_ALWAYS  0xffffffffffffffffULL
@@ -50,6 +79,7 @@
 #define PLEDGE_MCAST   0x0000000000200000ULL   /* multicast joins */
 #define PLEDGE_VMINFO  0x0000000000400000ULL   /* vminfo listings */
 #define PLEDGE_PS      0x0000000000800000ULL   /* ps listings */
+#define PLEDGE_seniuk  0x0000000001000000ULL   /* XXX reserved ;) */
 #define PLEDGE_DISKLABEL 0x0000000002000000ULL /* disklabels */
 #define PLEDGE_PF      0x0000000004000000ULL   /* pf ioctls */
 #define PLEDGE_AUDIO   0x0000000008000000ULL   /* audio ioctls */

Reply via email to