Hi,
two notes about vmd with this diff:
1. "vmm" pledge can be !fdpledged as well as it already pre-opens the
/dev/vmm fd for ioctls. I added the following chunk on top of your
diff and it works as expected:
---snip---
if ((p->p_p->ps_pledge & PLEDGE_VMM)) {
#if NVMM > 0
- if ((fp->f_type == DTYPE_VNODE) &&
+ if (!fdpledged &&
+ (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
+#endif /* NVMM > 0 */
}
---snap---
2. vmd calls openpty() in the pledged parent whenever a new VM is
started - effectively doing ioctls on post-pledge fds. I will
probably solve this by opening the pty in the non-pledged "priv"
process, and do some additional passing, but then I'll also have to
give up its chroot to access /dev/.
vmd: ioctl 40287401 post-pledge fd 12
vmd(51681): syscall 54 "tty"
Reyk
On Tue, Jan 24, 2017 at 07:36:47PM -0700, Theo de Raadt wrote:
> Here is the proposed ioctl lock-down policy for file descriptors
> allocated in a process before pledge(2).
>
> The manual page diff is first, that explains the direction this is
> going.
>
> The other supporting code has been commited already, so feel free
> to take this for a ride and let's see what programs need further
> modification to cope with it.
>
> Index: lib/libc/sys/pledge.2
> ===================================================================
> RCS file: /cvs/src/lib/libc/sys/pledge.2,v
> retrieving revision 1.39
> diff -u -p -u -r1.39 pledge.2
> --- lib/libc/sys/pledge.2 23 Jan 2017 07:19:39 -0000 1.39
> +++ lib/libc/sys/pledge.2 24 Jan 2017 10:04:31 -0000
> @@ -43,6 +43,16 @@ Subsequent calls to
> .Fn pledge
> can reduce the abilities further, but abilities can never be regained.
> .Pp
> +New file descriptors created by a pledged process are annotated internally
> +to permit a greater set of
> +.Xr ioctl 2
> +operations.
> +File descriptors obtained via
> +.Xr dup 2
> +or
> +.Xr recvmsg 2
> +retain the annotation from the process who originally opened them.
> +.Pp
> A process which attempts a restricted operation is killed with an uncatchable
> .Dv SIGABRT ,
> delivering a core file if possible.
> 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/kern/kern_pledge.c
> ===================================================================
> RCS file: /cvs/src/sys/kern/kern_pledge.c,v
> retrieving revision 1.192
> diff -u -p -u -r1.192 kern_pledge.c
> --- sys/kern/kern_pledge.c 23 Jan 2017 05:49:24 -0000 1.192
> +++ sys/kern/kern_pledge.c 24 Jan 2017 08:19:03 -0000
> @@ -66,6 +66,7 @@
>
> #include "audio.h"
> #include "pf.h"
> +#include "bpfilter.h"
> #include "pty.h"
>
> #if defined(__amd64__) || defined(__i386__)
> @@ -538,6 +539,22 @@ sys_pledge(struct proc *p, void *v, regi
> }
>
> if (SCARG(uap, request)) {
> +#ifdef DIAGNOSTIC
> + if (!ISSET(pr->ps_flags, PS_PLEDGE)) {
> + struct filedesc *fdp = p->p_fd;
> + int fd;
> +
> + fdplock(fdp);
> + for (fd = 0; fd <= fdp->fd_lastfile; fd++) {
> + if (fdp->fd_ofiles[fd] &&
> + (fdp->fd_ofileflags[fd] & UF_PLEDGED))
> + printf("%s: fd %d was set!\n",
> + pr->ps_comm, fd);
> + fdp->fd_ofileflags[fd] &= ~UF_PLEDGED;
> + }
> + fdpunlock(fdp);
> + }
> +#endif
> pr->ps_pledge = flags;
> pr->ps_flags |= PS_PLEDGE;
> }
> @@ -1102,7 +1119,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
> fdpledged)
> {
> struct vnode *vp = NULL;
> int error = EPERM;
> @@ -1137,30 +1154,32 @@ pledge_ioctl(struct proc *p, long com, s
> }
> }
>
> +#if NBPFILTER > 0
> if ((p->p_p->ps_pledge & PLEDGE_BPF)) {
> switch (com) {
> - case BIOCGSTATS: /* bpf: tcpdump privsep on ^C */
> - if (fp->f_type == DTYPE_VNODE &&
> - fp->f_ops->fo_ioctl == vn_ioctl)
> + case BIOCGSTATS: /* tcpdump, pflogd */
> + if (!fdpledged &&
> + fp->f_type == DTYPE_VNODE &&
> + vp->v_type == VCHR &&
> + cdevsw[major(vp->v_rdev)].d_open == bpfopen)
> return (0);
> break;
> }
> }
> +#endif /* NBPFILTER > 0 */
>
> 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);
> - break;
> }
> }
>
> - if ((p->p_p->ps_pledge & PLEDGE_DRM)) {
> #if NDRM > 0
> + if ((p->p_p->ps_pledge & PLEDGE_DRM)) {
> if ((fp->f_type == DTYPE_VNODE) &&
> (vp->v_type == VCHR) &&
> (cdevsw[major(vp->v_rdev)].d_open == drmopen)) {
> @@ -1168,11 +1187,11 @@ pledge_ioctl(struct proc *p, long com, s
> if (error == 0)
> return 0;
> }
> -#endif /* NDRM > 0 */
> }
> +#endif /* NDRM > 0 */
>
> - if ((p->p_p->ps_pledge & PLEDGE_AUDIO)) {
> #if NAUDIO > 0
> + if ((p->p_p->ps_pledge & PLEDGE_AUDIO)) {
> switch (com) {
> case AUDIO_GETPOS:
> case AUDIO_GETPAR:
> @@ -1184,19 +1203,22 @@ pledge_ioctl(struct proc *p, long com, s
> cdevsw[major(vp->v_rdev)].d_open == audioopen)
> return (0);
> }
> -#endif /* NAUDIO > 0 */
> }
> +#endif /* NAUDIO > 0 */
>
> 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 (fdpledged)
> + 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) ||
> @@ -1213,10 +1235,10 @@ pledge_ioctl(struct proc *p, long com, s
> }
> }
>
> - if ((p->p_p->ps_pledge & PLEDGE_PF)) {
> #if NPF > 0
> + if ((p->p_p->ps_pledge & PLEDGE_PF)) {
> switch (com) {
> - case DIOCADDRULE:
> + case DIOCADDRULE: /* relayd */
> case DIOCGETSTATUS:
> case DIOCNATLOOK:
> case DIOCRADDTABLES:
> @@ -1225,41 +1247,46 @@ 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 (!fdpledged &&
> + (fp->f_type == DTYPE_VNODE) &&
> (vp->v_type == VCHR) &&
> (cdevsw[major(vp->v_rdev)].d_open == pfopen))
> return (0);
> break;
> }
> -#endif
> }
> +#endif /* NPF > 0 */
>
> 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 (!fdpledged &&
> + (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 (!fdpledged &&
> + (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 !fdpledged 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:
> @@ -1267,7 +1294,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 */
> @@ -1275,7 +1302,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))
> @@ -1296,7 +1323,7 @@ pledge_ioctl(struct proc *p, long com, s
> case SIOCGNBRINFO_IN6:
> case SIOCGIFINFO_IN6:
> case SIOCGIFMEDIA:
> - if (fp->f_type == DTYPE_SOCKET)
> + if (!fdpledged && fp->f_type == DTYPE_SOCKET)
> return (0);
> break;
> }
> @@ -1311,9 +1338,11 @@ pledge_ioctl(struct proc *p, long com, s
> if (error == 0)
> return 0;
> }
> -#endif
> +#endif /* NVMM > 0 */
> }
>
> + if (fdpledged)
> + 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.114
> diff -u -p -u -r1.114 sys_generic.c
> --- sys/kern/sys_generic.c 24 Jan 2017 00:58:55 -0000 1.114
> +++ sys/kern/sys_generic.c 24 Jan 2017 11:23:11 -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_PLEDGED;
> + 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);
>
>
--