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;