There has been developer pressure to permit an increasing number of
ioctl's to pledged programs.  The problem is that providing a specific
ioctl under a promise to one program, means it becomes supplied to all
other programs that make that promise.  There is no discrete method
to differentiate further, until now.

This proposal annotates file descriptors allocated before the first
pledge(2) call in a process, with a marker.  Such file descriptors can
be dup'd; the mark is retained.  They can fdpassed, which also retains
the mark in the receiver (therefore, a non-pledged process can feed a
pledged process).  Execve clears this flag.

That is the proposed semantic.  There are still a few glitches.

Once that mark exists, I feel more comfortable adding additional ioctl's
which demand early-open fd's.  Specifically in privsep daemons.

5 programs have been discovered in the tree which needed small changes
(csh, ksh, less, tmux, dhclient).

less and tmux fixes are not commited, be cautious about tmux.

Index: sys/sys/filedesc.h
===================================================================
RCS file: /cvs/src/sys/sys/filedesc.h,v
retrieving revision 1.30
diff -u -p -u -r1.30 filedesc.h
--- sys/sys/filedesc.h  6 May 2015 08:52:17 -0000       1.30
+++ sys/sys/filedesc.h  21 Jan 2017 17:48:41 -0000
@@ -105,6 +105,7 @@ struct filedesc0 {
  * Per-process open flags.
  */
 #define        UF_EXCLOSE      0x01            /* auto-close on exec */
+#define        UF_PREPLEDGE    0x02            /* opened previous to pledge */
 
 /*
  * Flags on the file descriptor table.
Index: sys/sys/pledge.h
===================================================================
RCS file: /cvs/src/sys/sys/pledge.h,v
retrieving revision 1.30
diff -u -p -u -r1.30 pledge.h
--- sys/sys/pledge.h    23 Jan 2017 04:25:05 -0000      1.30
+++ sys/sys/pledge.h    23 Jan 2017 11:40:58 -0000
@@ -126,7 +126,7 @@ int pledge_adjtime(struct proc *p, const
 int    pledge_sendit(struct proc *p, const void *to);
 int    pledge_sockopt(struct proc *p, int set, int level, int optname);
 int    pledge_socket(struct proc *p, int domain, int state);
-int    pledge_ioctl(struct proc *p, long com, struct file *);
+int    pledge_ioctl(struct proc *p, long com, struct file *, int, int);
 int    pledge_ioctl_drm(struct proc *p, long com, dev_t device);
 int    pledge_ioctl_vmm(struct proc *p, long com);
 int    pledge_flock(struct proc *p);
Index: sys/sys/unpcb.h
===================================================================
RCS file: /cvs/src/sys/sys/unpcb.h,v
retrieving revision 1.12
diff -u -p -u -r1.12 unpcb.h
--- sys/sys/unpcb.h     28 Aug 2015 04:38:47 -0000      1.12
+++ sys/sys/unpcb.h     22 Jan 2017 09:19:54 -0000
@@ -86,17 +86,22 @@ struct      unpcb {
 #define        sotounpcb(so)   ((struct unpcb *)((so)->so_pcb))
 
 #ifdef _KERNEL
+struct fdpass {
+       struct file     *fp;
+       int             flags;
+};
+
 int    unp_attach(struct socket *);
 int    unp_bind(struct unpcb *, struct mbuf *, struct proc *);
 int    unp_connect(struct socket *, struct mbuf *, struct proc *);
 int    unp_connect2(struct socket *, struct socket *);
 void   unp_detach(struct unpcb *);
-void   unp_discard(struct file **, int);
+void   unp_discard(struct fdpass *, int);
 void   unp_disconnect(struct unpcb *);
 void   unp_drop(struct unpcb *, int);
 void   unp_gc(void *);
-void   unp_mark(struct file **, int);
-void   unp_scan(struct mbuf *, void (*)(struct file **, int));
+void   unp_mark(struct fdpass *, int);
+void   unp_scan(struct mbuf *, void (*)(struct fdpass *, int));
 void   unp_shutdown(struct unpcb *);
 int    unp_externalize(struct mbuf *, socklen_t, int);
 int    unp_internalize(struct mbuf *, struct proc *);
Index: sys/kern/kern_descrip.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_descrip.c,v
retrieving revision 1.136
diff -u -p -u -r1.136 kern_descrip.c
--- sys/kern/kern_descrip.c     24 Sep 2016 18:39:17 -0000      1.136
+++ sys/kern/kern_descrip.c     23 Jan 2017 07:03:23 -0000
@@ -1307,9 +1307,11 @@ fdcloseexec(struct proc *p)
        int fd;
 
        fdplock(fdp);
-       for (fd = 0; fd <= fdp->fd_lastfile; fd++)
+       for (fd = 0; fd <= fdp->fd_lastfile; fd++) {
+               fdp->fd_ofileflags[fd] &= ~UF_PREPLEDGE;
                if (fdp->fd_ofileflags[fd] & UF_EXCLOSE)
                        (void) fdrelease(p, fd);
+       }
        fdpunlock(fdp);
 }
 
Index: sys/kern/kern_pledge.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_pledge.c,v
retrieving revision 1.191
diff -u -p -u -r1.191 kern_pledge.c
--- sys/kern/kern_pledge.c      23 Jan 2017 04:25:05 -0000      1.191
+++ sys/kern/kern_pledge.c      23 Jan 2017 11:41:49 -0000
@@ -400,8 +400,9 @@ sys_pledge(struct proc *p, void *v, regi
                syscallarg(const char **)paths;
        } */    *uap = v;
        struct process *pr = p->p_p;
+       struct filedesc *fdp = p->p_fd;
        uint64_t flags = 0;
-       int error;
+       int error, fd;
 
        if (SCARG(uap, request)) {
                size_t rbuflen;
@@ -537,6 +538,13 @@ sys_pledge(struct proc *p, void *v, regi
        }
 
        if (SCARG(uap, request)) {
+               if (!ISSET(pr->ps_flags, PS_PLEDGE)) {
+                       fdplock(fdp);
+                       for (fd = 0; fd <= fdp->fd_lastfile; fd++)
+                               if (fdp->fd_ofiles[fd])
+                                       fdp->fd_ofileflags[fd] |= UF_PREPLEDGE;
+                       fdpunlock(fdp);
+               }
                pr->ps_pledge = flags;
                pr->ps_flags |= PS_PLEDGE;
        }
@@ -1101,7 +1109,7 @@ pledge_sendit(struct proc *p, const void
 }
 
 int
-pledge_ioctl(struct proc *p, long com, struct file *fp)
+pledge_ioctl(struct proc *p, long com, struct file *fp, int fd, int prepledge)
 {
        struct vnode *vp = NULL;
        int error = EPERM;
@@ -1138,8 +1146,9 @@ pledge_ioctl(struct proc *p, long com, s
 
        if ((p->p_p->ps_pledge & PLEDGE_BPF)) {
                switch (com) {
-               case BIOCGSTATS:        /* bpf: tcpdump privsep on ^C */
-                       if (fp->f_type == DTYPE_VNODE &&
+               case BIOCGSTATS:        /* tcpdump, pflogd */
+                       if (prepledge &&
+                           fp->f_type == DTYPE_VNODE &&
                            fp->f_ops->fo_ioctl == vn_ioctl)
                                return (0);
                        break;
@@ -1148,9 +1157,8 @@ pledge_ioctl(struct proc *p, long com, s
 
        if ((p->p_p->ps_pledge & PLEDGE_TAPE)) {
                switch (com) {
-               case MTIOCGET:
+               case MTIOCGET:          /* pax */
                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);
@@ -1160,7 +1168,8 @@ pledge_ioctl(struct proc *p, long com, s
 
        if ((p->p_p->ps_pledge & PLEDGE_DRM)) {
 #if NDRM > 0
-               if ((fp->f_type == DTYPE_VNODE) &&
+               if (prepledge &&
+                   (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);
@@ -1188,14 +1197,17 @@ pledge_ioctl(struct proc *p, long com, s
 
        if ((p->p_p->ps_pledge & PLEDGE_DISKLABEL)) {
                switch (com) {
-               case DIOCGDINFO:
-               case DIOCGPDINFO:
-               case DIOCRLDINFO:
+               case DIOCRLDINFO:       /* change operations, etc. */
                case DIOCWDINFO:
                case BIOCDISK:
                case BIOCINQ:
                case BIOCINSTALLBOOT:
                case BIOCVOL:
+                       if (!prepledge)
+                               break;
+                       /* FALLTHROUGH */
+               case DIOCGDINFO:        /* read operations, etc. */
+               case DIOCGPDINFO:
                        if (fp->f_type == DTYPE_VNODE &&
                            ((vp->v_type == VCHR &&
                            cdevsw[major(vp->v_rdev)].d_type == D_DISK) ||
@@ -1215,7 +1227,7 @@ pledge_ioctl(struct proc *p, long com, s
        if ((p->p_p->ps_pledge & PLEDGE_PF)) {
 #if NPF > 0
                switch (com) {
-               case DIOCADDRULE:
+               case DIOCADDRULE:       /* relayd */
                case DIOCGETSTATUS:
                case DIOCNATLOOK:
                case DIOCRADDTABLES:
@@ -1224,10 +1236,14 @@ pledge_ioctl(struct proc *p, long com, s
                case DIOCRCLRTSTATS:
                case DIOCRGETTSTATS:
                case DIOCRSETADDRS:
-               case DIOCXBEGIN:
+               case DIOCXBEGIN:        /* relayd, proxies */
                case DIOCXCOMMIT:
                case DIOCKILLSRCNODES:
-                       if ((fp->f_type == DTYPE_VNODE) &&
+               case DIOCRGETASTATS:    /* bgpd */
+               case DIOCRDELADDRS:
+               case DIOCRADDADDRS:
+                       if (prepledge &&
+                           (fp->f_type == DTYPE_VNODE) &&
                            (vp->v_type == VCHR) &&
                            (cdevsw[major(vp->v_rdev)].d_open == pfopen))
                                return (0);
@@ -1239,26 +1255,27 @@ pledge_ioctl(struct proc *p, long com, s
        if ((p->p_p->ps_pledge & PLEDGE_TTY)) {
                switch (com) {
 #if NPTY > 0
-               case PTMGET:
-                       if ((p->p_p->ps_pledge & PLEDGE_RPATH) == 0)
-                               break;
-                       if ((p->p_p->ps_pledge & PLEDGE_WPATH) == 0)
-                               break;
-                       if (fp->f_type != DTYPE_VNODE || vp->v_type != VCHR)
-                               break;
-                       if (cdevsw[major(vp->v_rdev)].d_open != ptmopen)
-                               break;
-                       return (0);
+               case PTMGET:            /* tmux */
+                       if (prepledge &&
+                           (p->p_p->ps_pledge & PLEDGE_RPATH) &&
+                           (p->p_p->ps_pledge & PLEDGE_WPATH) &&
+                           fp->f_type == DTYPE_VNODE && vp->v_type == VCHR &&
+                           cdevsw[major(vp->v_rdev)].d_open == ptmopen)
+                               return (0);
+                       break;
 #endif /* NPTY > 0 */
-               case TIOCSTI:           /* ksh? csh? */
-                       if ((p->p_p->ps_pledge & PLEDGE_PROC) &&
+               case TIOCSTI:           /* csh */
+                       if (prepledge &&
+                           (p->p_p->ps_pledge & PLEDGE_PROC) &&
                            fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY))
                                return (0);
                        break;
                case TIOCSPGRP:
-                       if ((p->p_p->ps_pledge & PLEDGE_PROC) == 0)
-                               break;
-                       /* FALLTHROUGH */
+                       /* cannot do prepledge test */
+                       if ((p->p_p->ps_pledge & PLEDGE_PROC) &&
+                           fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY))
+                               return (0);
+                       break;
                case TIOCFLUSH:         /* getty, telnet */
                case TIOCGPGRP:
                case TIOCGETA:
@@ -1266,7 +1283,7 @@ pledge_ioctl(struct proc *p, long com, s
                        if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY))
                                return (0);
                        return (ENOTTY);
-               case TIOCSWINSZ:
+               case TIOCSWINSZ:        /* tmux, script */
                case TIOCEXT:           /* mail, libedit .. */
                case TIOCCBRK:          /* cu */
                case TIOCSBRK:          /* cu */
@@ -1274,7 +1291,7 @@ pledge_ioctl(struct proc *p, long com, s
                case TIOCSDTR:          /* cu */
                case TIOCEXCL:          /* cu */
                case TIOCSETA:          /* cu, ... */
-               case TIOCSETAW:         /* cu, ... */
+               case TIOCSETAW:         /* tmux, cu, ksh ... */
                case TIOCSETAF:         /* tcsetattr TCSAFLUSH, script */
                case TIOCSCTTY:         /* forkpty(3), login_tty(3), ... */
                        if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY))
@@ -1295,7 +1312,7 @@ pledge_ioctl(struct proc *p, long com, s
                case SIOCGNBRINFO_IN6:
                case SIOCGIFINFO_IN6:
                case SIOCGIFMEDIA:
-                       if (fp->f_type == DTYPE_SOCKET)
+                       if (prepledge && fp->f_type == DTYPE_SOCKET)
                                return (0);
                        break;
                }
@@ -1313,6 +1330,8 @@ pledge_ioctl(struct proc *p, long com, s
 #endif
        }
 
+       if (!prepledge)
+               printf("%s: ioctl %08lx post-pledge fd %d\n", p->p_p->ps_comm, 
com, fd);
        return pledge_fail(p, error, PLEDGE_TTY);
 }
 
Index: sys/kern/sys_generic.c
===================================================================
RCS file: /cvs/src/sys/kern/sys_generic.c,v
retrieving revision 1.113
diff -u -p -u -r1.113 sys_generic.c
--- sys/kern/sys_generic.c      7 Nov 2016 00:26:33 -0000       1.113
+++ sys/kern/sys_generic.c      23 Jan 2017 08:43:43 -0000
@@ -390,17 +390,20 @@ sys_ioctl(struct proc *p, void *v, regis
                syscallarg(void *) data;
        } */ *uap = v;
        struct file *fp;
-       struct filedesc *fdp;
+       struct filedesc *fdp = p->p_fd;
        u_long com = SCARG(uap, com);
        int error;
        u_int size;
        caddr_t data, memp;
-       int tmp;
+       int tmp, pl;
 #define STK_PARAMS     128
        long long stkbuf[STK_PARAMS / sizeof(long long)];
 
-       fdp = p->p_fd;
+       fdplock(fdp);
        fp = fd_getfile_mode(fdp, SCARG(uap, fd), FREAD|FWRITE);
+       if (fp)
+               pl = fdp->fd_ofileflags[SCARG(uap, fd)] & UF_PREPLEDGE;
+       fdpunlock(fdp);
 
        if (fp == NULL)
                return (EBADF);
@@ -412,7 +415,7 @@ sys_ioctl(struct proc *p, void *v, regis
                        return (EINVAL);
        }
 
-       error = pledge_ioctl(p, com, fp);
+       error = pledge_ioctl(p, com, fp, SCARG(uap, fd), pl);
        if (error)
                return (error);
 
Index: sys/kern/uipc_usrreq.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_usrreq.c,v
retrieving revision 1.109
diff -u -p -u -r1.109 uipc_usrreq.c
--- sys/kern/uipc_usrreq.c      29 Dec 2016 12:12:43 -0000      1.109
+++ sys/kern/uipc_usrreq.c      22 Jan 2017 09:19:54 -0000
@@ -63,8 +63,8 @@ LIST_HEAD(unp_head, unpcb) unp_head = LI
 struct unp_deferral {
        SLIST_ENTRY(unp_deferral)       ud_link;
        int     ud_n;
-       /* followed by ud_n struct file * pointers */
-       struct file *ud_fp[];
+       /* followed by ud_n struct fdpass */
+       struct fdpass ud_fp[];
 };
 
 /* list of sets of files that were sent over sockets that are now closed */
@@ -664,12 +664,12 @@ unp_externalize(struct mbuf *rights, soc
        struct proc *p = curproc;               /* XXX */
        struct cmsghdr *cm = mtod(rights, struct cmsghdr *);
        int i, *fdp = NULL;
-       struct file **rp;
+       struct fdpass *rp;
        struct file *fp;
        int nfds, error = 0;
 
        nfds = (cm->cmsg_len - CMSG_ALIGN(sizeof(*cm))) /
-           sizeof(struct file *);
+           sizeof(struct fdpass);
        if (controllen < CMSG_ALIGN(sizeof(struct cmsghdr)))
                controllen = 0;
        else
@@ -680,9 +680,10 @@ unp_externalize(struct mbuf *rights, soc
        }
 
        /* Make sure the recipient should be able to see the descriptors.. */
-       rp = (struct file **)CMSG_DATA(cm);
+       rp = (struct fdpass *)CMSG_DATA(cm);
        for (i = 0; i < nfds; i++) {
-               fp = *rp++;
+               fp = rp->fp;
+               rp++;
                error = pledge_recvfd(p, fp);
                if (error)
                        break;
@@ -709,7 +710,7 @@ restart:
        fdplock(p->p_fd);
        if (error != 0) {
                if (nfds > 0) {
-                       rp = ((struct file **)CMSG_DATA(cm));
+                       rp = ((struct fdpass *)CMSG_DATA(cm));
                        unp_discard(rp, nfds);
                }
                goto out;
@@ -719,7 +720,7 @@ restart:
         * First loop -- allocate file descriptor table slots for the
         * new descriptors.
         */
-       rp = ((struct file **)CMSG_DATA(cm));
+       rp = ((struct fdpass *)CMSG_DATA(cm));
        for (i = 0; i < nfds; i++) {
                if ((error = fdalloc(p, 0, &fdp[i])) != 0) {
                        /*
@@ -748,7 +749,9 @@ restart:
                 * fdalloc() works properly.. We finalize it all
                 * in the loop below.
                 */
-               p->p_fd->fd_ofiles[fdp[i]] = *rp++;
+               p->p_fd->fd_ofiles[fdp[i]] = rp->fp;
+               p->p_fd->fd_ofileflags[fdp[i]] |= (rp->flags & UF_PREPLEDGE);
+               rp++;
 
                if (flags & MSG_CMSG_CLOEXEC)
                        p->p_fd->fd_ofileflags[fdp[i]] |= UF_EXCLOSE;
@@ -758,11 +761,12 @@ restart:
         * Now that adding them has succeeded, update all of the
         * descriptor passing state.
         */
-       rp = (struct file **)CMSG_DATA(cm);
+       rp = (struct fdpass *)CMSG_DATA(cm);
        for (i = 0; i < nfds; i++) {
                struct unpcb *unp;
 
-               fp = *rp++;
+               fp = rp->fp;
+               rp++;
                if ((unp = fptounp(fp)) != NULL)
                        unp->unp_msgcount--;
                unp_rights--;
@@ -787,7 +791,8 @@ unp_internalize(struct mbuf *control, st
 {
        struct filedesc *fdp = p->p_fd;
        struct cmsghdr *cm = mtod(control, struct cmsghdr *);
-       struct file **rp, *fp;
+       struct fdpass *rp;
+       struct file *fp;
        struct unpcb *unp;
        int i, error;
        int nfds, *ip, fd, neededspace;
@@ -807,7 +812,7 @@ unp_internalize(struct mbuf *control, st
 
        /* Make sure we have room for the struct file pointers */
 morespace:
-       neededspace = CMSG_SPACE(nfds * sizeof(struct file *)) -
+       neededspace = CMSG_SPACE(nfds * sizeof(struct fdpass)) -
            control->m_len;
        if (neededspace > M_TRAILINGSPACE(control)) {
                char *tmp;
@@ -834,11 +839,11 @@ morespace:
        }
 
        /* adjust message & mbuf to note amount of space actually used. */
-       cm->cmsg_len = CMSG_LEN(nfds * sizeof(struct file *));
-       control->m_len = CMSG_SPACE(nfds * sizeof(struct file *));
+       cm->cmsg_len = CMSG_LEN(nfds * sizeof(struct fdpass));
+       control->m_len = CMSG_SPACE(nfds * sizeof(struct fdpass));
 
        ip = ((int *)CMSG_DATA(cm)) + nfds - 1;
-       rp = ((struct file **)CMSG_DATA(cm)) + nfds - 1;
+       rp = ((struct fdpass *)CMSG_DATA(cm)) + nfds - 1;
        for (i = 0; i < nfds; i++) {
                memcpy(&fd, ip, sizeof fd);
                ip--;
@@ -859,7 +864,8 @@ morespace:
                        error = EINVAL;
                        goto fail;
                }
-               memcpy(rp, &fp, sizeof fp);
+               rp->fp = fp;
+               rp->flags = fdp->fd_ofileflags[fd] & UF_PREPLEDGE;
                rp--;
                fp->f_count++;
                if ((unp = fptounp(fp)) != NULL) {
@@ -873,7 +879,7 @@ fail:
        /* Back out what we just did. */
        for ( ; i > 0; i--) {
                rp++;
-               memcpy(&fp, rp, sizeof(fp));
+               fp = rp->fp;
                fp->f_count--;
                if ((unp = fptounp(fp)) != NULL)
                        unp->unp_msgcount--;
@@ -902,7 +908,7 @@ unp_gc(void *arg __unused)
        while ((defer = SLIST_FIRST(&unp_deferred)) != NULL) {
                SLIST_REMOVE_HEAD(&unp_deferred, ud_link);
                for (i = 0; i < defer->ud_n; i++) {
-                       fp = defer->ud_fp[i];
+                       fp = defer->ud_fp[i].fp;
                        if (fp == NULL)
                                continue;
                        FREF(fp);
@@ -911,7 +917,7 @@ unp_gc(void *arg __unused)
                        unp_rights--;
                        (void) closef(fp, NULL);
                }
-               free(defer, M_TEMP, sizeof(*defer) + sizeof(fp) * defer->ud_n);
+               free(defer, M_TEMP, sizeof(*defer) + sizeof(struct fdpass) * 
defer->ud_n);
        }
 
        unp_defer = 0;
@@ -1005,10 +1011,10 @@ unp_dispose(struct mbuf *m)
 }
 
 void
-unp_scan(struct mbuf *m0, void (*op)(struct file **, int))
+unp_scan(struct mbuf *m0, void (*op)(struct fdpass *, int))
 {
        struct mbuf *m;
-       struct file **rp;
+       struct fdpass *rp;
        struct cmsghdr *cm;
        int qfds;
 
@@ -1021,9 +1027,9 @@ unp_scan(struct mbuf *m0, void (*op)(str
                                    cm->cmsg_type != SCM_RIGHTS)
                                        continue;
                                qfds = (cm->cmsg_len - CMSG_ALIGN(sizeof *cm))
-                                   / sizeof(struct file *);
+                                   / sizeof(struct fdpass);
                                if (qfds > 0) {
-                                       rp = (struct file **)CMSG_DATA(cm);
+                                       rp = (struct fdpass *)CMSG_DATA(cm);
                                        op(rp, qfds);
                                }
                                break;          /* XXX, but saves time */
@@ -1034,16 +1040,16 @@ unp_scan(struct mbuf *m0, void (*op)(str
 }
 
 void
-unp_mark(struct file **rp, int nfds)
+unp_mark(struct fdpass *rp, int nfds)
 {
        struct unpcb *unp;
        int i;
 
        for (i = 0; i < nfds; i++) {
-               if (rp[i] == NULL)
+               if (rp[i].fp == NULL)
                        continue;
 
-               unp = fptounp(rp[i]);
+               unp = fptounp(rp[i].fp);
                if (unp == NULL)
                        continue;
 
@@ -1057,7 +1063,7 @@ unp_mark(struct file **rp, int nfds)
 }
 
 void
-unp_discard(struct file **rp, int nfds)
+unp_discard(struct fdpass *rp, int nfds)
 {
        struct unp_deferral *defer;
 

Reply via email to