diff options
56 files changed, 564 insertions, 238 deletions
diff --git a/sys/amd64/linux32/linux32_machdep.c b/sys/amd64/linux32/linux32_machdep.c index 26041a3..a8ebe7e 100644 --- a/sys/amd64/linux32/linux32_machdep.c +++ b/sys/amd64/linux32/linux32_machdep.c @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/file.h> #include <sys/fcntl.h> #include <sys/clock.h> @@ -566,7 +567,7 @@ linux_mmap_common(struct thread *td, l_uintptr_t addr, l_size_t len, l_int prot, * protection options specified. */ - if ((error = fget(td, bsd_args.fd, &fp)) != 0) + if ((error = fget(td, bsd_args.fd, CAP_MMAP, &fp)) != 0) return (error); if (fp->f_type != DTYPE_VNODE) { fdrop(fp, td); diff --git a/sys/cddl/compat/opensolaris/sys/file.h b/sys/cddl/compat/opensolaris/sys/file.h index 811b78c..7a3df36 100644 --- a/sys/cddl/compat/opensolaris/sys/file.h +++ b/sys/cddl/compat/opensolaris/sys/file.h @@ -36,12 +36,18 @@ #ifdef _KERNEL typedef struct file file_t; +#include <sys/capability.h> + static __inline file_t * getf(int fd) { struct file *fp; - if (fget(curthread, fd, &fp) == 0) + /* + * We wouldn't need all of these rights on every invocation + * if we had more information about intent. + */ + if (fget(curthread, fd, CAP_READ | CAP_WRITE | CAP_SEEK, &fp) == 0) return (fp); return (NULL); } @@ -51,7 +57,8 @@ releasef(int fd) { struct file *fp; - if (fget(curthread, fd, &fp) == 0) { + /* No CAP_ rights required, as we're only releasing. */ + if (fget(curthread, fd, 0, &fp) == 0) { fdrop(fp, curthread); fdrop(fp, curthread); } diff --git a/sys/compat/freebsd32/freebsd32_ioctl.c b/sys/compat/freebsd32/freebsd32_ioctl.c index e662a5d..1d773ca 100644 --- a/sys/compat/freebsd32/freebsd32_ioctl.c +++ b/sys/compat/freebsd32/freebsd32_ioctl.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include <sys/param.h> +#include <sys/capability.h> #include <sys/cdio.h> #include <sys/fcntl.h> #include <sys/filio.h> @@ -354,7 +355,7 @@ freebsd32_ioctl(struct thread *td, struct freebsd32_ioctl_args *uap) struct file *fp; int error; - if ((error = fget(td, uap->fd, &fp)) != 0) + if ((error = fget(td, uap->fd, CAP_IOCTL, &fp)) != 0) return (error); if ((fp->f_flag & (FREAD | FWRITE)) == 0) { fdrop(fp, td); diff --git a/sys/compat/linux/linux_file.c b/sys/compat/linux/linux_file.c index 44ad193..e923032 100644 --- a/sys/compat/linux/linux_file.c +++ b/sys/compat/linux/linux_file.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/conf.h> #include <sys/dirent.h> #include <sys/fcntl.h> @@ -141,7 +142,7 @@ linux_common_open(struct thread *td, int dirfd, char *path, int l_flags, int mod * having the same filedesc could use that fd without * checking below. */ - error = fget(td, fd, &fp); + error = fget(td, fd, CAP_IOCTL, &fp); if (!error) { sx_slock(&proctree_lock); PROC_LOCK(p); @@ -345,7 +346,7 @@ getdents_common(struct thread *td, struct linux_getdents64_args *args, } else justone = 0; - if ((error = getvnode(td->td_proc->p_fd, args->fd, &fp)) != 0) + if ((error = getvnode(td->td_proc->p_fd, args->fd, CAP_READ, &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) { @@ -1041,7 +1042,7 @@ linux_pread(td, uap) if (error == 0) { /* This seems to violate POSIX but linux does it */ - if ((error = fgetvp(td, uap->fd, &vp)) != 0) + if ((error = fgetvp(td, uap->fd, CAP_READ, &vp)) != 0) return (error); if (vp->v_type == VDIR) { vrele(vp); @@ -1390,7 +1391,7 @@ fcntl_common(struct thread *td, struct linux_fcntl64_args *args) * significant effect for pipes (SIGIO is not delivered for * pipes under Linux-2.2.35 at least). */ - error = fget(td, args->fd, &fp); + error = fget(td, args->fd, CAP_FCNTL, &fp); if (error) return (error); if (fp->f_type == DTYPE_PIPE) { diff --git a/sys/compat/linux/linux_ioctl.c b/sys/compat/linux/linux_ioctl.c index 5532c93..d021fba 100644 --- a/sys/compat/linux/linux_ioctl.c +++ b/sys/compat/linux/linux_ioctl.c @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> #include <sys/sysproto.h> +#include <sys/capability.h> #include <sys/cdio.h> #include <sys/dvdio.h> #include <sys/conf.h> @@ -193,7 +194,7 @@ linux_ioctl_hdio(struct thread *td, struct linux_ioctl_args *args) u_int sectorsize, fwcylinders, fwheads, fwsectors; off_t mediasize, bytespercyl; - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); switch (args->cmd & 0xffff) { case LINUX_HDIO_GET_GEO: @@ -274,7 +275,7 @@ linux_ioctl_disk(struct thread *td, struct linux_ioctl_args *args) u_int sectorsize; off_t mediasize; - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); switch (args->cmd & 0xffff) { case LINUX_BLKGETSIZE: @@ -700,7 +701,7 @@ linux_ioctl_termio(struct thread *td, struct linux_ioctl_args *args) struct file *fp; int error; - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); switch (args->cmd & 0xffff) { @@ -1440,7 +1441,7 @@ linux_ioctl_cdrom(struct thread *td, struct linux_ioctl_args *args) struct file *fp; int error; - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); switch (args->cmd & 0xffff) { @@ -1965,7 +1966,7 @@ linux_ioctl_console(struct thread *td, struct linux_ioctl_args *args) struct file *fp; int error; - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); switch (args->cmd & 0xffff) { @@ -2356,7 +2357,7 @@ linux_ioctl_socket(struct thread *td, struct linux_ioctl_args *args) ifp = NULL; error = 0; - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); type = fp->f_type; fdrop(fp, td); @@ -2582,7 +2583,7 @@ linux_ioctl_private(struct thread *td, struct linux_ioctl_args *args) struct file *fp; int error, type; - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); type = fp->f_type; fdrop(fp, td); @@ -2608,7 +2609,7 @@ linux_ioctl_sg(struct thread *td, struct linux_ioctl_args *args) u_long cmd; int error; - if ((error = fget(td, args->fd, &fp)) != 0) { + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) { printf("sg_linux_ioctl: fget returned %d\n", error); return (error); } @@ -2843,7 +2844,7 @@ linux_ioctl_v4l(struct thread *td, struct linux_ioctl_args *args) case LINUX_VIDIOCSCHAN: args->cmd = VIDIOCSCHAN; break; case LINUX_VIDIOCGTUNER: - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); error = copyin((void *) args->arg, &l_vtun, sizeof(l_vtun)); if (error) { @@ -2861,7 +2862,7 @@ linux_ioctl_v4l(struct thread *td, struct linux_ioctl_args *args) return (error); case LINUX_VIDIOCSTUNER: - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); error = copyin((void *) args->arg, &l_vtun, sizeof(l_vtun)); if (error) { @@ -2878,7 +2879,7 @@ linux_ioctl_v4l(struct thread *td, struct linux_ioctl_args *args) case LINUX_VIDIOCCAPTURE: args->cmd = VIDIOCCAPTURE; break; case LINUX_VIDIOCGWIN: - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); error = fo_ioctl(fp, VIDIOCGWIN, &vwin, td->td_ucred, td); if (!error) { @@ -2890,7 +2891,7 @@ linux_ioctl_v4l(struct thread *td, struct linux_ioctl_args *args) return (error); case LINUX_VIDIOCSWIN: - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); error = copyin((void *) args->arg, &l_vwin, sizeof(l_vwin)); if (error) { @@ -2913,7 +2914,7 @@ linux_ioctl_v4l(struct thread *td, struct linux_ioctl_args *args) return (error); case LINUX_VIDIOCGFBUF: - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); error = fo_ioctl(fp, VIDIOCGFBUF, &vbuf, td->td_ucred, td); if (!error) { @@ -2925,7 +2926,7 @@ linux_ioctl_v4l(struct thread *td, struct linux_ioctl_args *args) return (error); case LINUX_VIDIOCSFBUF: - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); error = copyin((void *) args->arg, &l_vbuf, sizeof(l_vbuf)); if (error) { @@ -2953,7 +2954,7 @@ linux_ioctl_v4l(struct thread *td, struct linux_ioctl_args *args) case LINUX_VIDIOCGPLAYINFO: args->cmd = VIDIOCGPLAYINFO; break; case LINUX_VIDIOCSMICROCODE: - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); error = copyin((void *) args->arg, &l_vcode, sizeof(l_vcode)); if (error) { @@ -3197,7 +3198,7 @@ linux_ioctl_v4l2(struct thread *td, struct linux_ioctl_args *args) error = copyin((void *)args->arg, &l_vformat, sizeof(l_vformat)); if (error) return (error); - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); if (linux_to_bsd_v4l2_format(&l_vformat, &vformat) != 0) error = EINVAL; @@ -3220,7 +3221,7 @@ linux_ioctl_v4l2(struct thread *td, struct linux_ioctl_args *args) if (error) return (error); linux_to_bsd_v4l2_standard(&l_vstd, &vstd); - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); error = fo_ioctl(fp, VIDIOC_ENUMSTD, (caddr_t)&vstd, td->td_ucred, td); @@ -3242,7 +3243,7 @@ linux_ioctl_v4l2(struct thread *td, struct linux_ioctl_args *args) sizeof(struct l_v4l2_input)); if (error != 0) return (error); - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); error = fo_ioctl(fp, VIDIOC_ENUMINPUT, (caddr_t)&vinp, td->td_ucred, td); @@ -3261,7 +3262,7 @@ linux_ioctl_v4l2(struct thread *td, struct linux_ioctl_args *args) error = copyin((void *)args->arg, &l_vbuf, sizeof(l_vbuf)); if (error) return (error); - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); linux_to_bsd_v4l2_buffer(&l_vbuf, &vbuf); if ((args->cmd & 0xffff) == LINUX_VIDIOC_QUERYBUF) @@ -3431,7 +3432,7 @@ linux_ioctl(struct thread *td, struct linux_ioctl_args *args) (unsigned long)args->cmd); #endif - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); if ((fp->f_flag & (FREAD|FWRITE)) == 0) { fdrop(fp, td); diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c index 6940e45..08728a1 100644 --- a/sys/compat/linux/linux_socket.c +++ b/sys/compat/linux/linux_socket.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include <sys/proc.h> #include <sys/systm.h> #include <sys/sysproto.h> +#include <sys/capability.h> #include <sys/fcntl.h> #include <sys/file.h> #include <sys/limits.h> @@ -743,7 +744,7 @@ linux_connect(struct thread *td, struct linux_connect_args *args) * socket and use the file descriptor reference instead of * creating a new one. */ - error = fgetsock(td, args->s, &so, &fflag); + error = fgetsock(td, args->s, CAP_CONNECT, &so, &fflag); if (error == 0) { error = EISCONN; if (fflag & FNONBLOCK) { diff --git a/sys/compat/linux/linux_stats.c b/sys/compat/linux/linux_stats.c index 8fa08b6..90f860d 100644 --- a/sys/compat/linux/linux_stats.c +++ b/sys/compat/linux/linux_stats.c @@ -141,8 +141,11 @@ translate_fd_major_minor(struct thread *td, int fd, struct stat *buf) struct vnode *vp; int major, minor; + /* + * No capability rights required here. + */ if ((!S_ISCHR(buf->st_mode) && !S_ISBLK(buf->st_mode)) || - fget(td, fd, &fp) != 0) + fget(td, fd, 0, &fp) != 0) return; vp = fp->f_vnode; if (vp != NULL && vp->v_rdev != NULL && diff --git a/sys/compat/svr4/svr4_fcntl.c b/sys/compat/svr4/svr4_fcntl.c index 88e4fc2..ce1452a 100644 --- a/sys/compat/svr4/svr4_fcntl.c +++ b/sys/compat/svr4/svr4_fcntl.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> +#include <sys/capability.h> #include <sys/systm.h> #include <sys/file.h> #include <sys/filedesc.h> @@ -261,7 +262,17 @@ fd_revoke(td, fd) int error, *retval; retval = td->td_retval; - if ((error = fgetvp(td, fd, &vp)) != 0) + /* + * If we ever want to support Capsicum on SVR4 processes (unlikely) + * or FreeBSD grows a native frevoke() (more likely), we will need a + * CAP_REVOKE here. + * + * In the meantime, use CAP_MASK_VALID: if a SVR4 process wants to + * do an frevoke(), it needs to do it on either a regular file + * descriptor or a fully-privileged capability (which is effectively + * the same as a non-capability-restricted file descriptor). + */ + if ((error = fgetvp(td, fd, CAP_MASK_VALID, &vp)) != 0) return (error); if (vp->v_type != VCHR && vp->v_type != VBLK) { @@ -313,7 +324,7 @@ fd_truncate(td, fd, flp) /* * We only support truncating the file. */ - if ((error = fget(td, fd, &fp)) != 0) + if ((error = fget(td, fd, CAP_FTRUNCATE, &fp)) != 0) return (error); vp = fp->f_vnode; @@ -392,7 +403,7 @@ svr4_sys_open(td, uap) #if defined(NOTYET) struct file *fp; - error = fget(td, retval, &fp); + error = fget(td, retval, CAP_IOCTL, &fp); PROC_UNLOCK(p); /* * we may have lost a race the above open() and diff --git a/sys/compat/svr4/svr4_filio.c b/sys/compat/svr4/svr4_filio.c index ca85653..cb7cada 100644 --- a/sys/compat/svr4/svr4_filio.c +++ b/sys/compat/svr4/svr4_filio.c @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/proc.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/file.h> #include <sys/filio.h> #include <sys/lock.h> @@ -113,7 +114,7 @@ svr4_sys_read(td, uap) ra.buf = uap->buf; ra.nbyte = uap->nbyte; - if (fget(td, uap->fd, &fp) != 0) { + if (fget(td, uap->fd, CAP_READ, &fp) != 0) { DPRINTF(("Something fishy with the user-supplied file descriptor...\n")); return EBADF; } diff --git a/sys/compat/svr4/svr4_ioctl.c b/sys/compat/svr4/svr4_ioctl.c index 1cea41a..36b0580 100644 --- a/sys/compat/svr4/svr4_ioctl.c +++ b/sys/compat/svr4/svr4_ioctl.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/proc.h> +#include <sys/capability.h> #include <sys/file.h> #include <sys/filedesc.h> #include <sys/fcntl.h> @@ -102,7 +103,7 @@ svr4_sys_ioctl(td, uap) retval = td->td_retval; cmd = uap->com; - if ((error = fget(td, uap->fd, &fp)) != 0) + if ((error = fget(td, uap->fd, CAP_IOCTL, &fp)) != 0) return (error); if ((fp->f_flag & (FREAD | FWRITE)) == 0) { diff --git a/sys/compat/svr4/svr4_misc.c b/sys/compat/svr4/svr4_misc.c index 6f80fe6..c0a7478 100644 --- a/sys/compat/svr4/svr4_misc.c +++ b/sys/compat/svr4/svr4_misc.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/dirent.h> #include <sys/fcntl.h> #include <sys/filedesc.h> @@ -246,7 +247,8 @@ svr4_sys_getdents64(td, uap) DPRINTF(("svr4_sys_getdents64(%d, *, %d)\n", uap->fd, uap->nbytes)); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) { + if ((error = getvnode(td->td_proc->p_fd, uap->fd, + CAP_READ | CAP_SEEK, &fp)) != 0) { return (error); } @@ -427,7 +429,8 @@ svr4_sys_getdents(td, uap) if (uap->nbytes < 0) return (EINVAL); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) + if ((error = getvnode(td->td_proc->p_fd, uap->fd, + CAP_READ | CAP_SEEK, &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) { @@ -615,7 +618,8 @@ svr4_sys_fchroot(td, uap) if ((error = priv_check(td, PRIV_VFS_FCHROOT)) != 0) return error; - if ((error = getvnode(fdp, uap->fd, &fp)) != 0) + /* XXX: we have the chroot priv... what cap might we need? all? */ + if ((error = getvnode(fdp, uap->fd, 0, &fp)) != 0) return error; vp = fp->f_vnode; VREF(vp); diff --git a/sys/compat/svr4/svr4_stream.c b/sys/compat/svr4/svr4_stream.c index ec95eec..a1a42c0 100644 --- a/sys/compat/svr4/svr4_stream.c +++ b/sys/compat/svr4/svr4_stream.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/fcntl.h> #include <sys/filedesc.h> #include <sys/filio.h> @@ -1448,7 +1449,7 @@ svr4_sys_putmsg(td, uap) struct file *fp; int error; - if ((error = fget(td, uap->fd, &fp)) != 0) { + if ((error = fget(td, uap->fd, CAP_WRITE, &fp)) != 0) { #ifdef DEBUG_SVR4 uprintf("putmsg: bad fp\n"); #endif @@ -1620,7 +1621,7 @@ svr4_sys_getmsg(td, uap) struct file *fp; int error; - if ((error = fget(td, uap->fd, &fp)) != 0) { + if ((error = fget(td, uap->fd, CAP_READ, &fp)) != 0) { #ifdef DEBUG_SVR4 uprintf("getmsg: bad fp\n"); #endif diff --git a/sys/dev/aac/aac_linux.c b/sys/dev/aac/aac_linux.c index f885235..049e2be 100644 --- a/sys/dev/aac/aac_linux.c +++ b/sys/dev/aac/aac_linux.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/conf.h> #include <sys/kernel.h> #include <sys/module.h> @@ -78,7 +79,7 @@ aac_linux_ioctl(struct thread *td, struct linux_ioctl_args *args) u_long cmd; int error; - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); cmd = args->cmd; diff --git a/sys/dev/amr/amr_linux.c b/sys/dev/amr/amr_linux.c index cb8c457..44e858b 100644 --- a/sys/dev/amr/amr_linux.c +++ b/sys/dev/amr/amr_linux.c @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/conf.h> #include <sys/kernel.h> #include <sys/module.h> @@ -74,7 +75,7 @@ amr_linux_ioctl(struct thread *p, struct linux_ioctl_args *args) struct file *fp; int error; - if ((error = fget(p, args->fd, &fp)) != 0) + if ((error = fget(p, args->fd, CAP_IOCTL, &fp)) != 0) return (error); error = fo_ioctl(fp, args->cmd, (caddr_t)args->arg, p->td_ucred, p); fdrop(fp, p); diff --git a/sys/dev/hwpmc/hwpmc_logging.c b/sys/dev/hwpmc/hwpmc_logging.c index 633c6f9..b85572a 100644 --- a/sys/dev/hwpmc/hwpmc_logging.c +++ b/sys/dev/hwpmc/hwpmc_logging.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> +#include <sys/capability.h> #include <sys/file.h> #include <sys/kernel.h> #include <sys/kthread.h> @@ -589,7 +590,7 @@ pmclog_configure_log(struct pmc_mdep *md, struct pmc_owner *po, int logfd) po->po_file)); /* get a reference to the file state */ - error = fget_write(curthread, logfd, &po->po_file); + error = fget_write(curthread, logfd, CAP_WRITE, &po->po_file); if (error) goto error; diff --git a/sys/dev/ipmi/ipmi_linux.c b/sys/dev/ipmi/ipmi_linux.c index fcf2bd5..430bd08 100644 --- a/sys/dev/ipmi/ipmi_linux.c +++ b/sys/dev/ipmi/ipmi_linux.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/conf.h> #include <sys/kernel.h> #include <sys/module.h> @@ -92,7 +93,7 @@ ipmi_linux_ioctl(struct thread *td, struct linux_ioctl_args *args) u_long cmd; int error; - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); cmd = args->cmd; diff --git a/sys/dev/iscsi/initiator/iscsi.c b/sys/dev/iscsi/initiator/iscsi.c index d35f631..292ce8f 100644 --- a/sys/dev/iscsi/initiator/iscsi.c +++ b/sys/dev/iscsi/initiator/iscsi.c @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$"); #include "opt_iscsi_initiator.h" #include <sys/param.h> +#include <sys/capability.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/conf.h> @@ -387,11 +388,11 @@ i_setsoc(isc_session_t *sp, int fd, struct thread *td) if(sp->soc != NULL) isc_stop_receiver(sp); - error = fget(td, fd, &sp->fp); + error = fget(td, fd, CAP_SOCK_ALL, &sp->fp); if(error) return error; - if((error = fgetsock(td, fd, &sp->soc, 0)) == 0) { + if((error = fgetsock(td, fd, CAP_SOCK_ALL, &sp->soc, 0)) == 0) { sp->td = td; isc_start_receiver(sp); } diff --git a/sys/dev/mfi/mfi_linux.c b/sys/dev/mfi/mfi_linux.c index 44edf49..12135ff 100644 --- a/sys/dev/mfi/mfi_linux.c +++ b/sys/dev/mfi/mfi_linux.c @@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/conf.h> #include <sys/kernel.h> #include <sys/module.h> @@ -95,7 +96,7 @@ mfi_linux_ioctl(struct thread *p, struct linux_ioctl_args *args) break; } - if ((error = fget(p, args->fd, &fp)) != 0) + if ((error = fget(p, args->fd, CAP_IOCTL, &fp)) != 0) return (error); error = fo_ioctl(fp, cmd, (caddr_t)args->arg, p->td_ucred, p); fdrop(fp, p); diff --git a/sys/dev/snp/snp.c b/sys/dev/snp/snp.c index b05ad2a..1c02660 100644 --- a/sys/dev/snp/snp.c +++ b/sys/dev/snp/snp.c @@ -252,6 +252,9 @@ snp_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, SNP_UNLOCK(); return (EBUSY); } + /* + * XXXRW / XXXJA: no capability check here. + */ error = ttyhook_register(&ss->snp_tty, td->td_proc, *(int *)data, &snp_hook, ss); SNP_UNLOCK(); diff --git a/sys/dev/tdfx/tdfx_linux.c b/sys/dev/tdfx/tdfx_linux.c index 2e877f6..0b769f0 100644 --- a/sys/dev/tdfx/tdfx_linux.c +++ b/sys/dev/tdfx/tdfx_linux.c @@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> +#include <sys/capability.h> #include <sys/file.h> #include <sys/kernel.h> #include <sys/module.h> @@ -53,7 +54,7 @@ linux_ioctl_tdfx(struct thread *td, struct linux_ioctl_args* args) struct file *fp; - if ((error = fget(td, args->fd, &fp)) != 0) + if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) return (error); /* We simply copy the data and send it right to ioctl */ copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio)); diff --git a/sys/fs/coda/coda_psdev.c b/sys/fs/coda/coda_psdev.c index 494f30b..e982a2c 100644 --- a/sys/fs/coda/coda_psdev.c +++ b/sys/fs/coda/coda_psdev.c @@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/conf.h> #include <sys/ioccom.h> #include <sys/kernel.h> @@ -371,7 +372,7 @@ vc_write(struct cdev *dev, struct uio *uiop, int flag) struct vnode *vp = NULL; if (tmp->oh.result == 0) { - error = getvnode(uiop->uio_td->td_proc->p_fd, + error = getvnode(uiop->uio_td->td_proc->p_fd, CAP_WRITE, tmp->fd, &fp); if (!error) { /* diff --git a/sys/fs/fdescfs/fdesc_vnops.c b/sys/fs/fdescfs/fdesc_vnops.c index e11b59c..3c4f44d 100644 --- a/sys/fs/fdescfs/fdesc_vnops.c +++ b/sys/fs/fdescfs/fdesc_vnops.c @@ -40,6 +40,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/conf.h> #include <sys/dirent.h> #include <sys/filedesc.h> @@ -305,7 +306,10 @@ fdesc_lookup(ap) fd = fd1; } - if ((error = fget(td, fd, &fp)) != 0) + /* + * No rights to check since 'fp' isn't actually used. + */ + if ((error = fget(td, fd, 0, &fp)) != 0) goto bad; /* Check if we're looking up ourselves. */ @@ -455,7 +459,7 @@ fdesc_setattr(ap) /* * Allow setattr where there is an underlying vnode. */ - error = getvnode(td->td_proc->p_fd, fd, &fp); + error = getvnode(td->td_proc->p_fd, fd, CAP_EXTATTR_SET, &fp); if (error) { /* * getvnode() returns EINVAL if the file descriptor is not diff --git a/sys/fs/nfsclient/nfs_clport.c b/sys/fs/nfsclient/nfs_clport.c index 44d3c74..acbfa6c 100644 --- a/sys/fs/nfsclient/nfs_clport.c +++ b/sys/fs/nfsclient/nfs_clport.c @@ -36,6 +36,8 @@ __FBSDID("$FreeBSD$"); #include "opt_kdtrace.h" +#include <sys/capability.h> + /* * generally, I don't like #includes inside .h files, but it seems to * be the easiest way to handle the port. @@ -1231,7 +1233,13 @@ nfssvc_nfscl(struct thread *td, struct nfssvc_args *uap) error = copyin(uap->argp, (caddr_t)&nfscbdarg, sizeof(nfscbdarg)); if (error) return (error); - if ((error = fget(td, nfscbdarg.sock, &fp)) != 0) { + /* + * Since we don't know what rights might be required, + * pretend that we need them all. It is better to be too + * careful than too reckless. + */ + if ((error = fget(td, nfscbdarg.sock, CAP_SOCK_ALL, &fp)) + != 0) { return (error); } if (fp->f_type != DTYPE_SOCKET) { diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c index 56c563a..da32b0e 100644 --- a/sys/fs/nfsserver/nfs_nfsdport.c +++ b/sys/fs/nfsserver/nfs_nfsdport.c @@ -34,6 +34,8 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include <sys/capability.h> + /* * Functions that perform the vfs operations required by the routines in * nfsd_serv.c. It is hoped that this change will make the server more @@ -3027,8 +3029,14 @@ nfssvc_nfsd(struct thread *td, struct nfssvc_args *uap) error = copyin(uap->argp, (caddr_t)&sockarg, sizeof (sockarg)); if (error) goto out; - if ((error = fget(td, sockarg.sock, &fp)) != 0) + /* + * Since we don't know what rights might be required, + * pretend that we need them all. It is better to be too + * careful than too reckless. + */ + if ((error = fget(td, sockarg.sock, CAP_SOCK_ALL, &fp)) != 0) goto out; + return (error); if (fp->f_type != DTYPE_SOCKET) { fdrop(fp, td); error = EPERM; diff --git a/sys/fs/portalfs/portal_vfsops.c b/sys/fs/portalfs/portal_vfsops.c index a8c16c4..04e7a3d 100644 --- a/sys/fs/portalfs/portal_vfsops.c +++ b/sys/fs/portalfs/portal_vfsops.c @@ -40,6 +40,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/domain.h> #include <sys/filedesc.h> #include <sys/kernel.h> @@ -112,7 +113,12 @@ portal_mount(struct mount *mp) if (error) return (error); - if ((error = fget(td, v, &fp)) != 0) + /* + * Capsicum is not incompatible with portalfs, but we don't really + * know what rights are required. In the spirit of "better safe than + * sorry", pretend that all rights are required for now. + */ + if ((error = fget(td, v, CAP_MASK_VALID, &fp)) != 0) return (error); if (fp->f_type != DTYPE_SOCKET) { fdrop(fp, td); diff --git a/sys/fs/portalfs/portal_vnops.c b/sys/fs/portalfs/portal_vnops.c index 6fcc1ce..7cd5267 100644 --- a/sys/fs/portalfs/portal_vnops.c +++ b/sys/fs/portalfs/portal_vnops.c @@ -38,7 +38,10 @@ * Portal Filesystem */ +#include "opt_capsicum.h" + #include <sys/param.h> +#include <sys/capability.h> #include <sys/fcntl.h> #include <sys/file.h> #include <sys/kernel.h> @@ -232,6 +235,15 @@ portal_open(ap) struct file *fp; struct portal_cred pcred; +#ifdef CAPABILITY_MODE + /* + * This may require access to a global namespace (e.g. an IP address); + * disallow it entirely, as we do open(2). + */ + if (IN_CAPABILITY_MODE(td)) + return (ECAPMODE); +#endif + /* * Nothing to do when opening the root node. */ @@ -414,7 +426,7 @@ portal_open(ap) * Check that the mode the file is being opened for is a subset * of the mode of the existing descriptor. */ - if ((error = fget(td, fd, &fp)) != 0) + if ((error = fget(td, fd, 0, &fp)) != 0) goto bad; if (((ap->a_mode & (FREAD|FWRITE)) | fp->f_flag) != fp->f_flag) { fdrop(fp, td); diff --git a/sys/gnu/fs/xfs/xfs_dfrag.c b/sys/gnu/fs/xfs/xfs_dfrag.c index 77a71df..7b6b04e 100644 --- a/sys/gnu/fs/xfs/xfs_dfrag.c +++ b/sys/gnu/fs/xfs/xfs_dfrag.c @@ -46,6 +46,7 @@ #include "xfs_mac.h" #include "xfs_rw.h" +#include <sys/capability.h> #include <sys/file.h> /* @@ -79,7 +80,8 @@ xfs_swapext( } /* Pull information for the target fd */ - if (fgetvp(td, (int)sxp->sx_fdtarget, &bvp) != 0) { + if (fgetvp(td, (int)sxp->sx_fdtarget, CAP_READ | CAP_WRITE, &bvp) + != 0) { error = XFS_ERROR(EINVAL); goto error0; } @@ -91,7 +93,7 @@ xfs_swapext( goto error0; } - if (fgetvp(td, (int)sxp->sx_fdtmp, &btvp) != 0) { + if (fgetvp(td, (int)sxp->sx_fdtmp, CAP_READ | CAP_WRITE, &btvp) != 0) { error = XFS_ERROR(EINVAL); goto error0; } diff --git a/sys/i386/ibcs2/ibcs2_fcntl.c b/sys/i386/ibcs2/ibcs2_fcntl.c index 6875aef..fddfcb5 100644 --- a/sys/i386/ibcs2/ibcs2_fcntl.c +++ b/sys/i386/ibcs2/ibcs2_fcntl.c @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/fcntl.h> #include <sys/file.h> #include <sys/filedesc.h> @@ -203,7 +204,7 @@ ibcs2_open(td, uap) struct file *fp; int error; - error = fget(td, td->td_retval[0], &fp); + error = fget(td, td->td_retval[0], CAP_IOCTL, &fp); PROC_UNLOCK(p); if (error) return (EBADF); diff --git a/sys/i386/ibcs2/ibcs2_ioctl.c b/sys/i386/ibcs2/ibcs2_ioctl.c index 90184e3..3a582ce 100644 --- a/sys/i386/ibcs2/ibcs2_ioctl.c +++ b/sys/i386/ibcs2/ibcs2_ioctl.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/consio.h> #include <sys/fcntl.h> #include <sys/file.h> @@ -333,7 +334,7 @@ ibcs2_ioctl(td, uap) struct file *fp; int error; - if ((error = fget(td, uap->fd, &fp)) != 0) { + if ((error = fget(td, uap->fd, CAP_IOCTL, &fp)) != 0) { DPRINTF(("ibcs2_ioctl(%d): bad fd %d ", p->p_pid, uap->fd)); return EBADF; diff --git a/sys/i386/ibcs2/ibcs2_misc.c b/sys/i386/ibcs2/ibcs2_misc.c index c537100..a08fdf3 100644 --- a/sys/i386/ibcs2/ibcs2_misc.c +++ b/sys/i386/ibcs2/ibcs2_misc.c @@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$"); */ #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/dirent.h> #include <sys/fcntl.h> #include <sys/filedesc.h> @@ -336,7 +337,8 @@ ibcs2_getdents(td, uap) #define BSD_DIRENT(cp) ((struct dirent *)(cp)) #define IBCS2_RECLEN(reclen) (reclen + sizeof(u_short)) - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) + if ((error = getvnode(td->td_proc->p_fd, uap->fd, + CAP_READ | CAP_SEEK, &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) { fdrop(fp, td); @@ -492,7 +494,8 @@ ibcs2_read(td, uap) u_long *cookies = NULL, *cookiep; int ncookies; - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) { + if ((error = getvnode(td->td_proc->p_fd, uap->fd, + CAP_READ | CAP_SEEK, &fp)) != 0) { if (error == EINVAL) return read(td, (struct read_args *)uap); else diff --git a/sys/i386/linux/linux_machdep.c b/sys/i386/linux/linux_machdep.c index d1f6ab9..57756c4 100644 --- a/sys/i386/linux/linux_machdep.c +++ b/sys/i386/linux/linux_machdep.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/file.h> #include <sys/fcntl.h> #include <sys/imgact.h> @@ -467,9 +468,12 @@ linux_mmap_common(struct thread *td, l_uintptr_t addr, l_size_t len, l_int prot, * The file descriptor fildes is opened with * read permission, regardless of the * protection options specified. + * + * Checking just CAP_MMAP is fine here, since the real work + * is done in the FreeBSD mmap(). */ - if ((error = fget(td, bsd_args.fd, &fp)) != 0) + if ((error = fget(td, bsd_args.fd, CAP_MMAP, &fp)) != 0) return (error); if (fp->f_type != DTYPE_VNODE) { fdrop(fp, td); diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index f3f9cbc..3082aea 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -431,6 +431,26 @@ fdtofp(int fd, struct filedesc *fdp) return (fp); } +static inline int +fdunwrap(int fd, cap_rights_t rights, struct filedesc *fdp, struct file **fpp) +{ + + *fpp = fdtofp(fd, fdp); + if (*fpp == NULL) + return (EBADF); + +#ifdef CAPABILITIES + if ((*fpp)->f_type == DTYPE_CAPABILITY) { + int err = cap_funwrap(*fpp, rights, fpp); + if (err != 0) { + *fpp = NULL; + return (err); + } + } +#endif /* CAPABILITIES */ + return (0); +} + int kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) { @@ -489,9 +509,9 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) case F_GETFL: FILEDESC_SLOCK(fdp); - if ((fp = fdtofp(fd, fdp)) == NULL) { + error = fdunwrap(fd, CAP_FCNTL, fdp, &fp); + if (error != 0) { FILEDESC_SUNLOCK(fdp); - error = EBADF; break; } td->td_retval[0] = OFLAGS(fp->f_flag); @@ -500,9 +520,9 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) case F_SETFL: FILEDESC_SLOCK(fdp); - if ((fp = fdtofp(fd, fdp)) == NULL) { + error = fdunwrap(fd, CAP_FCNTL, fdp, &fp); + if (error != 0) { FILEDESC_SUNLOCK(fdp); - error = EBADF; break; } fhold(fp); @@ -532,9 +552,9 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) case F_GETOWN: FILEDESC_SLOCK(fdp); - if ((fp = fdtofp(fd, fdp)) == NULL) { + error = fdunwrap(fd, CAP_FCNTL, fdp, &fp); + if (error != 0) { FILEDESC_SUNLOCK(fdp); - error = EBADF; break; } fhold(fp); @@ -547,9 +567,9 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) case F_SETOWN: FILEDESC_SLOCK(fdp); - if ((fp = fdtofp(fd, fdp)) == NULL) { + error = fdunwrap(fd, CAP_FCNTL, fdp, &fp); + if (error != 0) { FILEDESC_SUNLOCK(fdp); - error = EBADF; break; } fhold(fp); @@ -573,9 +593,9 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) case F_SETLK: do_setlk: FILEDESC_SLOCK(fdp); - if ((fp = fdtofp(fd, fdp)) == NULL) { + error = fdunwrap(fd, CAP_FLOCK, fdp, &fp); + if (error != 0) { FILEDESC_SUNLOCK(fdp); - error = EBADF; break; } if (fp->f_type != DTYPE_VNODE) { @@ -668,9 +688,9 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) case F_GETLK: FILEDESC_SLOCK(fdp); - if ((fp = fdtofp(fd, fdp)) == NULL) { + error = fdunwrap(fd, CAP_FLOCK, fdp, &fp); + if (error != 0) { FILEDESC_SUNLOCK(fdp); - error = EBADF; break; } if (fp->f_type != DTYPE_VNODE) { @@ -1312,7 +1332,7 @@ kern_fstat(struct thread *td, int fd, struct stat *sbp) AUDIT_ARG_FD(fd); - if ((error = fget(td, fd, &fp)) != 0) + if ((error = fget(td, fd, CAP_FSTAT, &fp)) != 0) return (error); AUDIT_ARG_FILE(td->td_proc, fp); @@ -1368,7 +1388,7 @@ fpathconf(struct thread *td, struct fpathconf_args *uap) struct vnode *vp; int error; - if ((error = fget(td, uap->fd, &fp)) != 0) + if ((error = fget(td, uap->fd, CAP_FPATHCONF, &fp)) != 0) return (error); /* If asynchronous I/O is available, it works for all descriptors. */ @@ -2294,7 +2314,7 @@ fget_unlocked(struct filedesc *fdp, int fd) #define FGET_GETCAP 0x00000001 static __inline int _fget(struct thread *td, int fd, struct file **fpp, int flags, - cap_rights_t needrights, cap_rights_t *haverights, u_char *maxprotp, + cap_rights_t needrights, cap_rights_t *haverightsp, u_char *maxprotp, int fget_flags) { struct filedesc *fdp; @@ -2369,28 +2389,36 @@ _fget(struct thread *td, int fd, struct file **fpp, int flags, } int -fget(struct thread *td, int fd, struct file **fpp) +fget(struct thread *td, int fd, cap_rights_t rights, struct file **fpp) +{ + + return(_fget(td, fd, fpp, 0, rights, NULL, NULL, 0)); +} + +int +fget_mmap(struct thread *td, int fd, cap_rights_t rights, u_char *maxprotp, + struct file **fpp) { - return(_fget(td, fd, fpp, 0, 0, NULL, NULL, 0)); + return (_fget(td, fd, fpp, 0, rights, NULL, maxprotp, 0)); } int -fget_read(struct thread *td, int fd, struct file **fpp) +fget_read(struct thread *td, int fd, cap_rights_t rights, struct file **fpp) { - return(_fget(td, fd, fpp, FREAD, 0, NULL, NULL, 0)); + return(_fget(td, fd, fpp, FREAD, rights, NULL, NULL, 0)); } int -fget_write(struct thread *td, int fd, struct file **fpp) +fget_write(struct thread *td, int fd, cap_rights_t rights, struct file **fpp) { - return(_fget(td, fd, fpp, FWRITE, 0, NULL, NULL, 0)); + return (_fget(td, fd, fpp, FWRITE, rights, NULL, NULL, 0)); } /* - * Unlike the other fget() calls, which will accept and check capability rights + * Unlike the other fget() calls, which accept and check capability rights * but never return capabilities, fgetcap() returns the capability but doesn't * check capability rights. */ @@ -2410,13 +2438,15 @@ fgetcap(struct thread *td, int fd, struct file **fpp) * XXX: what about the unused flags ? */ static __inline int -_fgetvp(struct thread *td, int fd, struct vnode **vpp, int flags) +_fgetvp(struct thread *td, int fd, int flags, cap_rights_t needrights, + cap_rights_t *haverightsp, struct vnode **vpp) { struct file *fp; int error; *vpp = NULL; - if ((error = _fget(td, fd, &fp, flags, 0, NULL, NULL, 0)) != 0) + if ((error = _fget(td, fd, &fp, flags, needrights, haverightsp, + NULL, 0)) != 0) return (error); if (fp->f_vnode == NULL) { error = EINVAL; @@ -2430,25 +2460,33 @@ _fgetvp(struct thread *td, int fd, struct vnode **vpp, int flags) } int -fgetvp(struct thread *td, int fd, struct vnode **vpp) +fgetvp(struct thread *td, int fd, cap_rights_t rights, struct vnode **vpp) { - return (_fgetvp(td, fd, vpp, 0)); + return (_fgetvp(td, fd, 0, rights, NULL, vpp)); +} + +int +fgetvp_rights(struct thread *td, int fd, cap_rights_t need, cap_rights_t *have, + struct vnode **vpp) +{ + return (_fgetvp(td, fd, 0, need, have, vpp)); } int -fgetvp_read(struct thread *td, int fd, struct vnode **vpp) +fgetvp_read(struct thread *td, int fd, cap_rights_t rights, struct vnode **vpp) { - return (_fgetvp(td, fd, vpp, FREAD)); + return (_fgetvp(td, fd, FREAD, rights, NULL, vpp)); } #ifdef notyet int -fgetvp_write(struct thread *td, int fd, struct vnode **vpp) +fgetvp_write(struct thread *td, int fd, cap_rights_t rights, + struct vnode **vpp) { - return (_fgetvp(td, fd, vpp, FWRITE)); + return (_fgetvp(td, fd, FWRITE, rights, NULL, vpp)); } #endif @@ -2464,7 +2502,8 @@ fgetvp_write(struct thread *td, int fd, struct vnode **vpp) * during use. */ int -fgetsock(struct thread *td, int fd, struct socket **spp, u_int *fflagp) +fgetsock(struct thread *td, int fd, cap_rights_t rights, struct socket **spp, + u_int *fflagp) { struct file *fp; int error; @@ -2472,7 +2511,7 @@ fgetsock(struct thread *td, int fd, struct socket **spp, u_int *fflagp) *spp = NULL; if (fflagp != NULL) *fflagp = 0; - if ((error = _fget(td, fd, &fp, 0, 0, NULL, NULL, 0)) != 0) + if ((error = _fget(td, fd, &fp, 0, rights, NULL, NULL, 0)) != 0) return (error); if (fp->f_type != DTYPE_SOCKET) { error = ENOTSOCK; @@ -2557,7 +2596,7 @@ flock(struct thread *td, struct flock_args *uap) int vfslocked; int error; - if ((error = fget(td, uap->fd, &fp)) != 0) + if ((error = fget(td, uap->fd, CAP_FLOCK, &fp)) != 0) return (error); if (fp->f_type != DTYPE_VNODE) { fdrop(fp, td); diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c index e14ae02..6ec8503 100644 --- a/sys/kern/kern_event.c +++ b/sys/kern/kern_event.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/kernel.h> #include <sys/lock.h> #include <sys/mutex.h> @@ -816,7 +817,7 @@ kern_kevent(struct thread *td, int fd, int nchanges, int nevents, struct file *fp; int i, n, nerrors, error; - if ((error = fget(td, fd, &fp)) != 0) + if ((error = fget(td, fd, CAP_POST_KEVENT, &fp)) != 0) return (error); if ((error = kqueue_acquire(fp, &kq)) != 0) goto done_norel; @@ -972,7 +973,7 @@ kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *td, int wa findkn: if (fops->f_isfd) { KASSERT(td != NULL, ("td is NULL")); - error = fget(td, kev->ident, &fp); + error = fget(td, kev->ident, CAP_POLL_KEVENT, &fp); if (error) goto done; @@ -2181,7 +2182,7 @@ kqfd_register(int fd, struct kevent *kev, struct thread *td, int waitok) struct file *fp; int error; - if ((error = fget(td, fd, &fp)) != 0) + if ((error = fget(td, fd, CAP_POST_KEVENT, &fp)) != 0) return (error); if ((error = kqueue_acquire(fp, &kq)) != 0) goto noacquire; diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index f7f80af..1c42420 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -439,7 +439,11 @@ interpret: imgp->vp = binvp; } else { AUDIT_ARG_FD(args->fd); - error = fgetvp(td, args->fd, &binvp); + /* + * Some might argue that CAP_READ and/or CAP_MMAP should also + * be required here; such arguments will be entertained. + */ + error = fgetvp_read(td, args->fd, CAP_FEXECVE, &binvp); if (error) goto exec_fail; vfslocked = VFS_LOCK_GIANT(binvp->v_mount); diff --git a/sys/kern/sys_capability.c b/sys/kern/sys_capability.c index 6ca9602..b20fa62 100644 --- a/sys/kern/sys_capability.c +++ b/sys/kern/sys_capability.c @@ -225,7 +225,7 @@ cap_new(struct thread *td, struct cap_new_args *uap) AUDIT_ARG_FD(fd); AUDIT_ARG_RIGHTS(rights); - error = fget(td, fd, &fp); + error = fget(td, fd, rights, &fp); if (error) return (error); AUDIT_ARG_FILE(td->td_proc, fp); diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c index 1a2685c..f94be5a 100644 --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -37,12 +37,14 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_capsicum.h" #include "opt_compat.h" #include "opt_ktrace.h" #include <sys/param.h> #include <sys/systm.h> #include <sys/sysproto.h> +#include <sys/capability.h> #include <sys/filedesc.h> #include <sys/filio.h> #include <sys/fcntl.h> @@ -232,7 +234,7 @@ kern_readv(struct thread *td, int fd, struct uio *auio) struct file *fp; int error; - error = fget_read(td, fd, &fp); + error = fget_read(td, fd, CAP_READ | CAP_SEEK, &fp); if (error) return (error); error = dofileread(td, fd, fp, auio, (off_t)-1, 0); @@ -275,7 +277,7 @@ kern_preadv(td, fd, auio, offset) struct file *fp; int error; - error = fget_read(td, fd, &fp); + error = fget_read(td, fd, CAP_READ, &fp); if (error) return (error); if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE)) @@ -441,7 +443,7 @@ kern_writev(struct thread *td, int fd, struct uio *auio) struct file *fp; int error; - error = fget_write(td, fd, &fp); + error = fget_write(td, fd, CAP_WRITE | CAP_SEEK, &fp); if (error) return (error); error = dofilewrite(td, fd, fp, auio, (off_t)-1, 0); @@ -484,7 +486,7 @@ kern_pwritev(td, fd, auio, offset) struct file *fp; int error; - error = fget_write(td, fd, &fp); + error = fget_write(td, fd, CAP_WRITE, &fp); if (error) return (error); if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE)) @@ -566,7 +568,7 @@ kern_ftruncate(td, fd, length) AUDIT_ARG_FD(fd); if (length < 0) return (EINVAL); - error = fget(td, fd, &fp); + error = fget(td, fd, CAP_FTRUNCATE, &fp); if (error) return (error); AUDIT_ARG_FILE(td->td_proc, fp); @@ -696,7 +698,7 @@ kern_ioctl(struct thread *td, int fd, u_long com, caddr_t data) AUDIT_ARG_FD(fd); AUDIT_ARG_CMD(com); - if ((error = fget(td, fd, &fp)) != 0) + if ((error = fget(td, fd, CAP_IOCTL, &fp)) != 0) return (error); if ((fp->f_flag & (FREAD | FWRITE)) == 0) { fdrop(fp, td); @@ -1054,6 +1056,37 @@ selsetbits(fd_mask **ibits, fd_mask **obits, int idx, fd_mask bit, int events) return (n); } +static __inline int +getselfd_cap(struct filedesc *fdp, int fd, struct file **fpp) +{ + struct file *fp; +#ifdef CAPABILITIES + struct file *fp_fromcap; + int error; +#endif + + if ((fp = fget_unlocked(fdp, fd)) == NULL) + return (EBADF); +#ifdef CAPABILITIES + /* + * If the file descriptor is for a capability, test rights and use + * the file descriptor references by the capability. + */ + error = cap_funwrap(fp, CAP_POLL_KEVENT, &fp_fromcap); + if (error) { + fdrop(fp, curthread); + return (error); + } + if (fp != fp_fromcap) { + fhold(fp_fromcap); + fdrop(fp, curthread); + fp = fp_fromcap; + } +#endif /* CAPABILITIES */ + *fpp = fp; + return (0); +} + /* * Traverse the list of fds attached to this thread's seltd and check for * completion. @@ -1069,6 +1102,7 @@ selrescan(struct thread *td, fd_mask **ibits, fd_mask **obits) struct file *fp; fd_mask bit; int fd, ev, n, idx; + int error; fdp = td->td_proc->p_fd; stp = td->td_sel; @@ -1080,8 +1114,9 @@ selrescan(struct thread *td, fd_mask **ibits, fd_mask **obits) /* If the selinfo wasn't cleared the event didn't fire. */ if (si != NULL) continue; - if ((fp = fget_unlocked(fdp, fd)) == NULL) - return (EBADF); + error = getselfd_cap(fdp, fd, &fp); + if (error) + return (error); idx = fd / NFDBITS; bit = (fd_mask)1 << (fd % NFDBITS); ev = fo_poll(fp, selflags(ibits, idx, bit), td->td_ucred, td); @@ -1109,6 +1144,7 @@ selscan(td, ibits, obits, nfd) fd_mask bit; int ev, flags, end, fd; int n, idx; + int error; fdp = td->td_proc->p_fd; n = 0; @@ -1119,8 +1155,9 @@ selscan(td, ibits, obits, nfd) flags = selflags(ibits, idx, bit); if (flags == 0) continue; - if ((fp = fget_unlocked(fdp, fd)) == NULL) - return (EBADF); + error = getselfd_cap(fdp, fd, &fp); + if (error) + return (error); selfdalloc(td, (void *)(uintptr_t)fd); ev = fo_poll(fp, flags, td->td_ucred, td); fdrop(fp, td); diff --git a/sys/kern/tty.c b/sys/kern/tty.c index 187e635..77c02dd 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -30,9 +30,11 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_capsicum.h" #include "opt_compat.h" #include <sys/param.h> +#include <sys/capability.h> #include <sys/conf.h> #include <sys/cons.h> #include <sys/fcntl.h> @@ -1810,6 +1812,9 @@ ttyhook_register(struct tty **rtp, struct proc *p, int fd, { struct tty *tp; struct file *fp; +#ifdef CAPABILITIES + struct file *fp_cap; +#endif struct cdev *dev; struct cdevsw *cdp; struct filedesc *fdp; @@ -1827,6 +1832,13 @@ ttyhook_register(struct tty **rtp, struct proc *p, int fd, goto done1; } +#ifdef CAPABILITIES + fp_cap = fp; + error = cap_funwrap(fp_cap, CAP_TTYHOOK, &fp); + if (error) + return (error); +#endif + /* * Make sure the vnode is bound to a character device. * Unlocked check for the vnode type is ok there, because we diff --git a/sys/kern/uipc_mqueue.c b/sys/kern/uipc_mqueue.c index 9b334ac..e13912c 100644 --- a/sys/kern/uipc_mqueue.c +++ b/sys/kern/uipc_mqueue.c @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/limits.h> #include <sys/buf.h> +#include <sys/capability.h> #include <sys/dirent.h> #include <sys/event.h> #include <sys/eventhandler.h> @@ -2087,19 +2088,19 @@ kmq_unlink(struct thread *td, struct kmq_unlink_args *uap) return (error); } -typedef int (*_fgetf)(struct thread *, int, struct file **); +typedef int (*_fgetf)(struct thread *, int, cap_rights_t, struct file **); /* * Get message queue by giving file slot */ static int -_getmq(struct thread *td, int fd, _fgetf func, +_getmq(struct thread *td, int fd, cap_rights_t rights, _fgetf func, struct file **fpp, struct mqfs_node **ppn, struct mqueue **pmq) { struct mqfs_node *pn; int error; - error = func(td, fd, fpp); + error = func(td, fd, rights, fpp); if (error) return (error); if (&mqueueops != (*fpp)->f_ops) { @@ -2118,21 +2119,21 @@ static __inline int getmq(struct thread *td, int fd, struct file **fpp, struct mqfs_node **ppn, struct mqueue **pmq) { - return _getmq(td, fd, fget, fpp, ppn, pmq); + return _getmq(td, fd, CAP_POLL_KEVENT, fget, fpp, ppn, pmq); } static __inline int getmq_read(struct thread *td, int fd, struct file **fpp, struct mqfs_node **ppn, struct mqueue **pmq) { - return _getmq(td, fd, fget_read, fpp, ppn, pmq); + return _getmq(td, fd, CAP_READ, fget_read, fpp, ppn, pmq); } static __inline int getmq_write(struct thread *td, int fd, struct file **fpp, struct mqfs_node **ppn, struct mqueue **pmq) { - return _getmq(td, fd, fget_write, fpp, ppn, pmq); + return _getmq(td, fd, CAP_WRITE, fget_write, fpp, ppn, pmq); } static int @@ -2243,7 +2244,7 @@ kmq_notify(struct thread *td, struct kmq_notify_args *uap) struct filedesc *fdp; struct proc *p; struct mqueue *mq; - struct file *fp; + struct file *fp, *fp2; struct mqueue_notifier *nt, *newnt = NULL; int error; @@ -2267,7 +2268,18 @@ kmq_notify(struct thread *td, struct kmq_notify_args *uap) return (error); again: FILEDESC_SLOCK(fdp); - if (fget_locked(fdp, uap->mqd) != fp) { + fp2 = fget_locked(fdp, uap->mqd); + if (fp2 == NULL) { + FILEDESC_SUNLOCK(fdp); + error = EBADF; + goto out; + } + error = cap_funwrap(fp2, CAP_POLL_KEVENT, &fp2); + if (error) { + FILEDESC_SUNLOCK(fdp); + goto out; + } + if (fp2 != fp) { FILEDESC_SUNLOCK(fdp); error = EBADF; goto out; diff --git a/sys/kern/uipc_sem.c b/sys/kern/uipc_sem.c index 917c343..82a4622 100644 --- a/sys/kern/uipc_sem.c +++ b/sys/kern/uipc_sem.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include "opt_posix.h" #include <sys/param.h> +#include <sys/capability.h> #include <sys/condvar.h> #include <sys/fcntl.h> #include <sys/file.h> @@ -116,7 +117,8 @@ static int ksem_create(struct thread *td, const char *path, semid_t *semidp, mode_t mode, unsigned int value, int flags, int compat32); static void ksem_drop(struct ksem *ks); -static int ksem_get(struct thread *td, semid_t id, struct file **fpp); +static int ksem_get(struct thread *td, semid_t id, cap_rights_t rights, + struct file **fpp); static struct ksem *ksem_hold(struct ksem *ks); static void ksem_insert(char *path, Fnv32_t fnv, struct ksem *ks); static struct ksem *ksem_lookup(char *path, Fnv32_t fnv); @@ -525,13 +527,13 @@ ksem_create(struct thread *td, const char *name, semid_t *semidp, mode_t mode, } static int -ksem_get(struct thread *td, semid_t id, struct file **fpp) +ksem_get(struct thread *td, semid_t id, cap_rights_t rights, struct file **fpp) { struct ksem *ks; struct file *fp; int error; - error = fget(td, id, &fp); + error = fget(td, id, rights, &fp); if (error) return (EINVAL); if (fp->f_type != DTYPE_SEM) { @@ -623,7 +625,8 @@ ksem_close(struct thread *td, struct ksem_close_args *uap) struct file *fp; int error; - error = ksem_get(td, uap->id, &fp); + /* No capability rights required to close a semaphore. */ + error = ksem_get(td, uap->id, 0, &fp); if (error) return (error); ks = fp->f_data; @@ -648,7 +651,7 @@ ksem_post(struct thread *td, struct ksem_post_args *uap) struct ksem *ks; int error; - error = ksem_get(td, uap->id, &fp); + error = ksem_get(td, uap->id, CAP_SEM_POST, &fp); if (error) return (error); ks = fp->f_data; @@ -738,7 +741,7 @@ kern_sem_wait(struct thread *td, semid_t id, int tryflag, int error; DP((">>> kern_sem_wait entered! pid=%d\n", (int)td->td_proc->p_pid)); - error = ksem_get(td, id, &fp); + error = ksem_get(td, id, CAP_SEM_WAIT, &fp); if (error) return (error); ks = fp->f_data; @@ -804,7 +807,7 @@ ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap) struct ksem *ks; int error, val; - error = ksem_get(td, uap->id, &fp); + error = ksem_get(td, uap->id, CAP_SEM_GETVALUE, &fp); if (error) return (error); ks = fp->f_data; @@ -838,7 +841,8 @@ ksem_destroy(struct thread *td, struct ksem_destroy_args *uap) struct ksem *ks; int error; - error = ksem_get(td, uap->id, &fp); + /* No capability rights required to close a semaphore. */ + error = ksem_get(td, uap->id, 0, &fp); if (error) return (error); ks = fp->f_data; diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c index c434973..0e5efe6 100644 --- a/sys/kern/uipc_syscalls.c +++ b/sys/kern/uipc_syscalls.c @@ -120,33 +120,47 @@ SYSCTL_INT(_kern_ipc, OID_AUTO, nsfbufsused, CTLFLAG_RD, &nsfbufsused, 0, "Number of sendfile(2) sf_bufs in use"); /* - * Convert a user file descriptor to a kernel file entry. A reference on the - * file entry is held upon returning. This is lighter weight than - * fgetsock(), which bumps the socket reference drops the file reference - * count instead, as this approach avoids several additional mutex operations - * associated with the additional reference count. If requested, return the - * open file flags. + * Convert a user file descriptor to a kernel file entry and check that, if + * it is a capability, the right rights are present. A reference on the file + * entry is held upon returning. */ static int -getsock(struct filedesc *fdp, int fd, struct file **fpp, u_int *fflagp) +getsock_cap(struct filedesc *fdp, int fd, cap_rights_t rights, + struct file **fpp, u_int *fflagp) { struct file *fp; +#ifdef CAPABILITIES + struct file *fp_fromcap; int error; +#endif fp = NULL; - if (fdp == NULL || (fp = fget_unlocked(fdp, fd)) == NULL) { - error = EBADF; - } else if (fp->f_type != DTYPE_SOCKET) { + if ((fdp == NULL) || ((fp = fget_unlocked(fdp, fd)) == NULL)) + return (EBADF); +#ifdef CAPABILITIES + /* + * If the file descriptor is for a capability, test rights and use + * the file descriptor referenced by the capability. + */ + error = cap_funwrap(fp, rights, &fp_fromcap); + if (error) { fdrop(fp, curthread); - fp = NULL; - error = ENOTSOCK; - } else { - if (fflagp != NULL) - *fflagp = fp->f_flag; - error = 0; + return (error); } + if (fp != fp_fromcap) { + fhold(fp_fromcap); + fdrop(fp, curthread); + fp = fp_fromcap; + } +#endif /* CAPABILITIES */ + if (fp->f_type != DTYPE_SOCKET) { + fdrop(fp, curthread); + return (ENOTSOCK); + } + if (fflagp != NULL) + *fflagp = fp->f_flag; *fpp = fp; - return (error); + return (0); } /* @@ -226,7 +240,7 @@ kern_bind(td, fd, sa) int error; AUDIT_ARG_FD(fd); - error = getsock(td->td_proc->p_fd, fd, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, fd, CAP_BIND, &fp, NULL); if (error) return (error); so = fp->f_data; @@ -257,7 +271,7 @@ listen(td, uap) int error; AUDIT_ARG_FD(uap->s); - error = getsock(td->td_proc->p_fd, uap->s, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, uap->s, CAP_LISTEN, &fp, NULL); if (error == 0) { so = fp->f_data; #ifdef MAC @@ -347,7 +361,7 @@ kern_accept(struct thread *td, int s, struct sockaddr **name, AUDIT_ARG_FD(s); fdp = td->td_proc->p_fd; - error = getsock(fdp, s, &headfp, &fflag); + error = getsock_cap(fdp, s, CAP_ACCEPT, &headfp, &fflag); if (error) return (error); head = headfp->f_data; @@ -535,7 +549,7 @@ kern_connect(td, fd, sa) int interrupted = 0; AUDIT_ARG_FD(fd); - error = getsock(td->td_proc->p_fd, fd, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, fd, CAP_CONNECT, &fp, NULL); if (error) return (error); so = fp->f_data; @@ -744,12 +758,16 @@ kern_sendit(td, s, mp, flags, control, segflg) struct socket *so; int i; int len, error; + cap_rights_t rights; #ifdef KTRACE struct uio *ktruio = NULL; #endif AUDIT_ARG_FD(s); - error = getsock(td->td_proc->p_fd, s, &fp, NULL); + rights = CAP_WRITE; + if (mp->msg_name != NULL) + rights |= CAP_CONNECT; + error = getsock_cap(td->td_proc->p_fd, s, rights, &fp, NULL); if (error) return (error); so = (struct socket *)fp->f_data; @@ -953,7 +971,7 @@ kern_recvit(td, s, mp, fromseg, controlp) *controlp = NULL; AUDIT_ARG_FD(s); - error = getsock(td->td_proc->p_fd, s, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, s, CAP_READ, &fp, NULL); if (error) return (error); so = fp->f_data; @@ -1267,7 +1285,8 @@ shutdown(td, uap) int error; AUDIT_ARG_FD(uap->s); - error = getsock(td->td_proc->p_fd, uap->s, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, uap->s, CAP_SHUTDOWN, &fp, + NULL); if (error == 0) { so = fp->f_data; error = soshutdown(so, uap->how); @@ -1330,7 +1349,7 @@ kern_setsockopt(td, s, level, name, val, valseg, valsize) } AUDIT_ARG_FD(s); - error = getsock(td->td_proc->p_fd, s, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, s, CAP_SETSOCKOPT, &fp, NULL); if (error == 0) { so = fp->f_data; error = sosetopt(so, &sopt); @@ -1409,7 +1428,7 @@ kern_getsockopt(td, s, level, name, val, valseg, valsize) } AUDIT_ARG_FD(s); - error = getsock(td->td_proc->p_fd, s, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, s, CAP_GETSOCKOPT, &fp, NULL); if (error == 0) { so = fp->f_data; error = sogetopt(so, &sopt); @@ -1471,7 +1490,7 @@ kern_getsockname(struct thread *td, int fd, struct sockaddr **sa, return (EINVAL); AUDIT_ARG_FD(fd); - error = getsock(td->td_proc->p_fd, fd, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, fd, CAP_GETSOCKNAME, &fp, NULL); if (error) return (error); so = fp->f_data; @@ -1571,7 +1590,7 @@ kern_getpeername(struct thread *td, int fd, struct sockaddr **sa, return (EINVAL); AUDIT_ARG_FD(fd); - error = getsock(td->td_proc->p_fd, fd, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, fd, CAP_GETPEERNAME, &fp, NULL); if (error) return (error); so = fp->f_data; @@ -1827,7 +1846,7 @@ kern_sendfile(struct thread *td, struct sendfile_args *uap, * we send only the header/trailer and no payload data. */ AUDIT_ARG_FD(uap->fd); - if ((error = fgetvp_read(td, uap->fd, &vp)) != 0) + if ((error = fgetvp_read(td, uap->fd, CAP_READ, &vp)) != 0) goto out; vfslocked = VFS_LOCK_GIANT(vp->v_mount); vn_lock(vp, LK_SHARED | LK_RETRY); @@ -1865,8 +1884,8 @@ kern_sendfile(struct thread *td, struct sendfile_args *uap, * The socket must be a stream socket and connected. * Remember if it a blocking or non-blocking socket. */ - if ((error = getsock(td->td_proc->p_fd, uap->s, &sock_fp, - NULL)) != 0) + if ((error = getsock_cap(td->td_proc->p_fd, uap->s, CAP_WRITE, + &sock_fp, NULL)) != 0) goto out; so = sock_fp->f_data; if (so->so_type != SOCK_STREAM) { @@ -2298,7 +2317,7 @@ sctp_peeloff(td, uap) fdp = td->td_proc->p_fd; AUDIT_ARG_FD(uap->sd); - error = fgetsock(td, uap->sd, &head, &fflag); + error = fgetsock(td, uap->sd, CAP_PEELOFF, &head, &fflag); if (error) goto done2; error = sctp_can_peel_off(head, (sctp_assoc_t)uap->name); @@ -2391,6 +2410,7 @@ sctp_generic_sendmsg (td, uap) #endif struct uio auio; struct iovec iov[1]; + cap_rights_t rights; if (uap->sinfo) { error = copyin(uap->sinfo, &sinfo, sizeof (sinfo)); @@ -2398,16 +2418,19 @@ sctp_generic_sendmsg (td, uap) return (error); u_sinfo = &sinfo; } + + rights = CAP_WRITE; if (uap->tolen) { error = getsockaddr(&to, uap->to, uap->tolen); if (error) { to = NULL; goto sctp_bad2; } + rights |= CAP_CONNECT; } AUDIT_ARG_FD(uap->sd); - error = getsock(td->td_proc->p_fd, uap->sd, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, uap->sd, rights, &fp, NULL); if (error) goto sctp_bad; #ifdef KTRACE @@ -2494,6 +2517,7 @@ sctp_generic_sendmsg_iov(td, uap) #endif struct uio auio; struct iovec *iov, *tiov; + cap_rights_t rights; if (uap->sinfo) { error = copyin(uap->sinfo, &sinfo, sizeof (sinfo)); @@ -2501,16 +2525,18 @@ sctp_generic_sendmsg_iov(td, uap) return (error); u_sinfo = &sinfo; } + rights = CAP_WRITE; if (uap->tolen) { error = getsockaddr(&to, uap->to, uap->tolen); if (error) { to = NULL; goto sctp_bad2; } + rights |= CAP_CONNECT; } AUDIT_ARG_FD(uap->sd); - error = getsock(td->td_proc->p_fd, uap->sd, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, uap->sd, rights, &fp, NULL); if (error) goto sctp_bad1; @@ -2618,7 +2644,7 @@ sctp_generic_recvmsg(td, uap) #endif AUDIT_ARG_FD(uap->sd); - error = getsock(td->td_proc->p_fd, uap->sd, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, uap->sd, CAP_READ, &fp, NULL); if (error) { return (error); } diff --git a/sys/kern/vfs_acl.c b/sys/kern/vfs_acl.c index b1cda38..9010a50 100644 --- a/sys/kern/vfs_acl.c +++ b/sys/kern/vfs_acl.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> #include <sys/sysproto.h> +#include <sys/capability.h> #include <sys/fcntl.h> #include <sys/kernel.h> #include <sys/malloc.h> @@ -408,7 +409,7 @@ __acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap) struct file *fp; int vfslocked, error; - error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); + error = getvnode(td->td_proc->p_fd, uap->filedes, CAP_ACL_GET, &fp); if (error == 0) { vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp); @@ -427,7 +428,7 @@ __acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap) struct file *fp; int vfslocked, error; - error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); + error = getvnode(td->td_proc->p_fd, uap->filedes, CAP_ACL_SET, &fp); if (error == 0) { vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp); @@ -486,7 +487,8 @@ __acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap) struct file *fp; int vfslocked, error; - error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); + error = getvnode(td->td_proc->p_fd, uap->filedes, CAP_ACL_DELETE, + &fp); if (error == 0) { vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); error = vacl_delete(td, fp->f_vnode, uap->type); @@ -545,7 +547,8 @@ __acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) struct file *fp; int vfslocked, error; - error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); + error = getvnode(td->td_proc->p_fd, uap->filedes, CAP_ACL_CHECK, + &fp); if (error == 0) { vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp); diff --git a/sys/kern/vfs_aio.c b/sys/kern/vfs_aio.c index 69b4e0a..aedbdd0 100644 --- a/sys/kern/vfs_aio.c +++ b/sys/kern/vfs_aio.c @@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$"); #include <sys/malloc.h> #include <sys/bio.h> #include <sys/buf.h> +#include <sys/capability.h> #include <sys/eventhandler.h> #include <sys/sysproto.h> #include <sys/filedesc.h> @@ -1577,17 +1578,30 @@ aio_aqueue(struct thread *td, struct aiocb *job, struct aioliojob *lj, aiocbe->uaiocb.aio_lio_opcode = type; opcode = aiocbe->uaiocb.aio_lio_opcode; - /* Fetch the file object for the specified file descriptor. */ + /* + * Validate the opcode and fetch the file object for the specified + * file descriptor. + * + * XXXRW: Moved the opcode validation up here so that we don't + * retrieve a file descriptor without knowing what the capabiltity + * should be. + */ fd = aiocbe->uaiocb.aio_fildes; switch (opcode) { case LIO_WRITE: - error = fget_write(td, fd, &fp); + error = fget_write(td, fd, CAP_WRITE | CAP_SEEK, &fp); break; case LIO_READ: - error = fget_read(td, fd, &fp); + error = fget_read(td, fd, CAP_READ | CAP_SEEK, &fp); + break; + case LIO_SYNC: + error = fget(td, fd, CAP_FSYNC, &fp); + break; + case LIO_NOP: + error = fget(td, fd, 0, &fp); break; default: - error = fget(td, fd, &fp); + error = EINVAL; } if (error) { uma_zfree(aiocb_zone, aiocbe); @@ -1623,11 +1637,6 @@ aio_aqueue(struct thread *td, struct aiocb *job, struct aioliojob *lj, uma_zfree(aiocb_zone, aiocbe); return (0); } - if ((opcode != LIO_READ) && (opcode != LIO_WRITE) && - (opcode != LIO_SYNC)) { - error = EINVAL; - goto aqueue_fail; - } if (aiocbe->uaiocb.aio_sigevent.sigev_notify != SIGEV_KEVENT) goto no_kqueue; @@ -1971,7 +1980,7 @@ aio_cancel(struct thread *td, struct aio_cancel_args *uap) struct vnode *vp; /* Lookup file object. */ - error = fget(td, uap->fd, &fp); + error = fget(td, uap->fd, 0, &fp); if (error) return (error); diff --git a/sys/kern/vfs_extattr.c b/sys/kern/vfs_extattr.c index e7bf2d1..b8b9cdf 100644 --- a/sys/kern/vfs_extattr.c +++ b/sys/kern/vfs_extattr.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> #include <sys/lock.h> #include <sys/mount.h> #include <sys/mutex.h> @@ -230,7 +231,7 @@ extattr_set_fd(td, uap) return (error); AUDIT_ARG_TEXT(attrname); - error = getvnode(td->td_proc->p_fd, uap->fd, &fp); + error = getvnode(td->td_proc->p_fd, uap->fd, CAP_EXTATTR_SET, &fp); if (error) return (error); @@ -410,7 +411,7 @@ extattr_get_fd(td, uap) return (error); AUDIT_ARG_TEXT(attrname); - error = getvnode(td->td_proc->p_fd, uap->fd, &fp); + error = getvnode(td->td_proc->p_fd, uap->fd, CAP_EXTATTR_GET, &fp); if (error) return (error); @@ -560,7 +561,8 @@ extattr_delete_fd(td, uap) return (error); AUDIT_ARG_TEXT(attrname); - error = getvnode(td->td_proc->p_fd, uap->fd, &fp); + error = getvnode(td->td_proc->p_fd, uap->fd, CAP_EXTATTR_DELETE, + &fp); if (error) return (error); @@ -719,7 +721,7 @@ extattr_list_fd(td, uap) AUDIT_ARG_FD(uap->fd); AUDIT_ARG_VALUE(uap->attrnamespace); - error = getvnode(td->td_proc->p_fd, uap->fd, &fp); + error = getvnode(td->td_proc->p_fd, uap->fd, CAP_EXTATTR_LIST, &fp); if (error) return (error); diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index 50a2570..ae8982f 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -37,12 +37,14 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_capsicum.h" #include "opt_kdtrace.h" #include "opt_ktrace.h" #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> +#include <sys/capability.h> #include <sys/fcntl.h> #include <sys/jail.h> #include <sys/lock.h> @@ -212,7 +214,12 @@ namei(struct nameidata *ndp) AUDIT_ARG_ATFD1(ndp->ni_dirfd); if (cnp->cn_flags & AUDITVNODE2) AUDIT_ARG_ATFD2(ndp->ni_dirfd); - error = fgetvp(td, ndp->ni_dirfd, &dp); +#ifdef CAPABILITY_MODE + KASSERT(!IN_CAPABILITY_MODE(td), + ("%s: reached %s:%d in capability mode", + __func__, __FILE__, __LINE__)); +#endif + error = fgetvp(td, ndp->ni_dirfd, 0, &dp); } if (error != 0 || dp != NULL) { FILEDESC_SUNLOCK(fdp); diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index b48c6e7..3a7ca06 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -37,6 +37,7 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_capsicum.h" #include "opt_compat.h" #include "opt_kdtrace.h" #include "opt_ktrace.h" @@ -45,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/bio.h> #include <sys/buf.h> +#include <sys/capability.h> #include <sys/disk.h> #include <sys/sysent.h> #include <sys/malloc.h> @@ -373,7 +375,7 @@ kern_fstatfs(struct thread *td, int fd, struct statfs *buf) int error; AUDIT_ARG_FD(fd); - error = getvnode(td->td_proc->p_fd, fd, &fp); + error = getvnode(td->td_proc->p_fd, fd, CAP_FSTATFS, &fp); if (error) return (error); vp = fp->f_vnode; @@ -746,7 +748,7 @@ fchdir(td, uap) int error; AUDIT_ARG_FD(uap->fd); - if ((error = getvnode(fdp, uap->fd, &fp)) != 0) + if ((error = getvnode(fdp, uap->fd, CAP_FCHDIR, &fp)) != 0) return (error); vp = fp->f_vnode; VREF(vp); @@ -1049,7 +1051,7 @@ kern_openat(struct thread *td, int fd, char *path, enum uio_seg pathseg, struct vnode *vp; int cmode; struct file *nfp; - int type, indx, error; + int type, indx = -1, error; struct flock lf; struct nameidata nd; int vfslocked; @@ -1069,10 +1071,13 @@ kern_openat(struct thread *td, int fd, char *path, enum uio_seg pathseg, else flags = FFLAGS(flags); - error = falloc(td, &nfp, &indx, flags); + /* + * allocate the file descriptor, but don't install a descriptor yet + */ + error = falloc_noinstall(td, &nfp); if (error) return (error); - /* An extra reference on `nfp' has been held for us by falloc(). */ + /* An extra reference on `nfp' has been held for us by falloc_noinstall(). */ fp = nfp; /* Set the flags early so the finit in devfs can pick them up. */ fp->f_flag = flags & FMASK; @@ -1099,12 +1104,13 @@ kern_openat(struct thread *td, int fd, char *path, enum uio_seg pathseg, * if it succeeds. */ if ((error == ENODEV || error == ENXIO) && - td->td_dupfd >= 0 && /* XXX from fdopen */ - (error = - dupfdopen(td, fdp, indx, td->td_dupfd, flags, error)) == 0) { - td->td_retval[0] = indx; - fdrop(fp, td); - return (0); + (td->td_dupfd >= 0)) { + /* XXX from fdopen */ + if ((error = finstall(td, fp, &indx, flags)) != 0) + goto bad_unlocked; + if ((error = dupfdopen(td, fdp, indx, td->td_dupfd, + flags, error)) == 0) + goto success; } /* * Clean up the descriptor, but only if another thread hadn't @@ -1161,6 +1167,14 @@ kern_openat(struct thread *td, int fd, char *path, enum uio_seg pathseg, goto bad; } VFS_UNLOCK_GIANT(vfslocked); +success: + /* + * If we haven't already installed the FD (for dupfdopen), do so now. + */ + if (indx == -1) + if ((error = finstall(td, fp, &indx, flags)) != 0) + goto bad_unlocked; + /* * Release our private reference, leaving the one associated with * the descriptor table intact. @@ -1170,8 +1184,10 @@ kern_openat(struct thread *td, int fd, char *path, enum uio_seg pathseg, return (0); bad: VFS_UNLOCK_GIANT(vfslocked); +bad_unlocked: fdclose(fdp, fp, indx, td); fdrop(fp, td); + td->td_retval[0] = -1; return (error); } @@ -1918,7 +1934,7 @@ lseek(td, uap) int vfslocked; AUDIT_ARG_FD(uap->fd); - if ((error = fget(td, uap->fd, &fp)) != 0) + if ((error = fget(td, uap->fd, CAP_SEEK, &fp)) != 0) return (error); if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE)) { fdrop(fp, td); @@ -2775,7 +2791,8 @@ fchflags(td, uap) AUDIT_ARG_FD(uap->fd); AUDIT_ARG_FFLAGS(uap->flags); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) + if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_FCHFLAGS, + &fp)) != 0) return (error); vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); #ifdef AUDIT @@ -2936,7 +2953,8 @@ fchmod(td, uap) AUDIT_ARG_FD(uap->fd); AUDIT_ARG_MODE(uap->mode); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) + if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_FCHMOD, + &fp)) != 0) return (error); vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); #ifdef AUDIT @@ -3113,7 +3131,8 @@ fchown(td, uap) AUDIT_ARG_FD(uap->fd); AUDIT_ARG_OWNER(uap->uid, uap->gid); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) + if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_FCHOWN, &fp)) + != 0) return (error); vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); #ifdef AUDIT @@ -3348,7 +3367,8 @@ kern_futimes(struct thread *td, int fd, struct timeval *tptr, AUDIT_ARG_FD(fd); if ((error = getutimes(tptr, tptrseg, ts)) != 0) return (error); - if ((error = getvnode(td->td_proc->p_fd, fd, &fp)) != 0) + if ((error = getvnode(td->td_proc->p_fd, fd, CAP_FUTIMES, &fp)) + != 0) return (error); vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); #ifdef AUDIT @@ -3500,7 +3520,8 @@ fsync(td, uap) int error, lock_flags; AUDIT_ARG_FD(uap->fd); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) + if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_FSYNC, + &fp)) != 0) return (error); vp = fp->f_vnode; vfslocked = VFS_LOCK_GIANT(vp->v_mount); @@ -3925,7 +3946,8 @@ kern_ogetdirentries(struct thread *td, struct ogetdirentries_args *uap, /* XXX arbitrary sanity limit on `count'. */ if (uap->count > 64 * 1024) return (EINVAL); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) + if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_READ, + &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) { fdrop(fp, td); @@ -4085,7 +4107,8 @@ kern_getdirentries(struct thread *td, int fd, char *buf, u_int count, AUDIT_ARG_FD(fd); if (count > INT_MAX) return (EINVAL); - if ((error = getvnode(td->td_proc->p_fd, fd, &fp)) != 0) + if ((error = getvnode(td->td_proc->p_fd, fd, CAP_READ | CAP_SEEK, + &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) { fdrop(fp, td); @@ -4248,30 +4271,49 @@ out: } /* - * Convert a user file descriptor to a kernel file entry. - * A reference on the file entry is held upon returning. + * Convert a user file descriptor to a kernel file entry and check that, if it + * is a capability, the correct rights are present. A reference on the file + * entry is held upon returning. */ int -getvnode(fdp, fd, fpp) - struct filedesc *fdp; - int fd; - struct file **fpp; +getvnode(struct filedesc *fdp, int fd, cap_rights_t rights, + struct file **fpp) { - int error; struct file *fp; +#ifdef CAPABILITIES + struct file *fp_fromcap; +#endif + int error; error = 0; fp = NULL; - if (fdp == NULL || (fp = fget_unlocked(fdp, fd)) == NULL) - error = EBADF; - else if (fp->f_vnode == NULL) { - error = EINVAL; + if ((fdp == NULL) || (fp = fget_unlocked(fdp, fd)) == NULL) + return (EBADF); +#ifdef CAPABILITIES + /* + * If the file descriptor is for a capability, test rights and use the + * file descriptor referenced by the capability. + */ + error = cap_funwrap(fp, rights, &fp_fromcap); + if (error) { + fdrop(fp, curthread); + return (error); + } + if (fp != fp_fromcap) { + fhold(fp_fromcap); + fdrop(fp, curthread); + fp = fp_fromcap; + } +#endif /* CAPABILITIES */ + if (fp->f_vnode == NULL) { fdrop(fp, curthread); + return (EINVAL); } *fpp = fp; - return (error); + return (0); } + /* * Get an (NFS) file handle. */ @@ -4683,7 +4725,7 @@ kern_posix_fallocate(struct thread *td, int fd, off_t offset, off_t len) fp = NULL; vfslocked = 0; - error = fget(td, fd, &fp); + error = fget(td, fd, CAP_WRITE, &fp); if (error != 0) goto out; diff --git a/sys/netgraph/ng_socket.c b/sys/netgraph/ng_socket.c index f72f822..8c819c0 100644 --- a/sys/netgraph/ng_socket.c +++ b/sys/netgraph/ng_socket.c @@ -694,7 +694,7 @@ ng_internalize(struct mbuf *control, struct thread *td) /* Check that the FD given is legit. and change it to a pointer to a * struct file. */ fd = CMSG_DATA(cm); - if ((error = fget(td, fd, &fp)) != 0) + if ((error = fget(td, fd, 0, &fp)) != 0) return (error); /* Depending on what kind of resource it is, act differently. For diff --git a/sys/nfsserver/nfs_srvkrpc.c b/sys/nfsserver/nfs_srvkrpc.c index 3c60825..2581092 100644 --- a/sys/nfsserver/nfs_srvkrpc.c +++ b/sys/nfsserver/nfs_srvkrpc.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include "opt_kgssapi.h" #include <sys/param.h> +#include <sys/capability.h> #include <sys/systm.h> #include <sys/sysproto.h> #include <sys/kernel.h> @@ -173,7 +174,7 @@ nfssvc_nfsserver(struct thread *td, struct nfssvc_args *uap) sizeof(addsockarg)); if (error) return (error); - if ((error = fget(td, addsockarg.sock, &fp)) != 0) + if ((error = fget(td, addsockarg.sock, CAP_SOCK_ALL, &fp)) != 0) return (error); if (fp->f_type != DTYPE_SOCKET) { fdrop(fp, td); diff --git a/sys/security/audit/audit_arg.c b/sys/security/audit/audit_arg.c index 4e155da..e4409f2 100644 --- a/sys/security/audit/audit_arg.c +++ b/sys/security/audit/audit_arg.c @@ -899,7 +899,7 @@ audit_sysclose(struct thread *td, int fd) audit_arg_fd(fd); - if (getvnode(td->td_proc->p_fd, fd, &fp) != 0) + if (getvnode(td->td_proc->p_fd, fd, 0, &fp) != 0) return; vp = fp->f_vnode; diff --git a/sys/security/mac/mac_syscalls.c b/sys/security/mac/mac_syscalls.c index aea0885..dc27547 100644 --- a/sys/security/mac/mac_syscalls.c +++ b/sys/security/mac/mac_syscalls.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include "opt_mac.h" #include <sys/param.h> +#include <sys/capability.h> #include <sys/fcntl.h> #include <sys/kernel.h> #include <sys/lock.h> @@ -247,7 +248,7 @@ __mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) } buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); - error = fget(td, uap->fd, &fp); + error = fget(td, uap->fd, CAP_MAC_GET, &fp); if (error) goto out; @@ -442,7 +443,7 @@ __mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) return (error); } - error = fget(td, uap->fd, &fp); + error = fget(td, uap->fd, CAP_MAC_SET, &fp); if (error) goto out; diff --git a/sys/sys/capability.h b/sys/sys/capability.h index b6561c7..bd1d060 100644 --- a/sys/sys/capability.h +++ b/sys/sys/capability.h @@ -115,9 +115,9 @@ #define CAP_SEM_POST 0x0000010000000000ULL #define CAP_SEM_WAIT 0x0000020000000000ULL -/* Events - maybe we need a post/get distinction? */ -#define CAP_EVENT 0x0000040000000000ULL -#define CAP_KEVENT 0x0000080000000000ULL +/* kqueue events. */ +#define CAP_POLL_KEVENT 0x0000040000000000ULL +#define CAP_POST_KEVENT 0x0000080000000000ULL /* Strange and powerful rights that should not be given lightly. */ #define CAP_IOCTL 0x0000100000000000ULL diff --git a/sys/sys/file.h b/sys/sys/file.h index eea2c00..2c64bcf 100644 --- a/sys/sys/file.h +++ b/sys/sys/file.h @@ -176,9 +176,13 @@ extern int maxfiles; /* kernel limit on number of open files */ extern int maxfilesperproc; /* per process limit on number of open files */ extern volatile int openfiles; /* actual number of open files */ -int fget(struct thread *td, int fd, struct file **fpp); -int fget_read(struct thread *td, int fd, struct file **fpp); -int fget_write(struct thread *td, int fd, struct file **fpp); +int fget(struct thread *td, int fd, cap_rights_t rights, struct file **fpp); +int fget_mmap(struct thread *td, int fd, cap_rights_t rights, + u_char *maxprotp, struct file **fpp); +int fget_read(struct thread *td, int fd, cap_rights_t rights, + struct file **fpp); +int fget_write(struct thread *td, int fd, cap_rights_t rights, + struct file **fpp); int fgetcap(struct thread *td, int fd, struct file **fpp); int _fdrop(struct file *fp, struct thread *td); @@ -197,11 +201,16 @@ fo_stat_t soo_stat; fo_close_t soo_close; void finit(struct file *, u_int, short, void *, struct fileops *); -int fgetvp(struct thread *td, int fd, struct vnode **vpp); -int fgetvp_read(struct thread *td, int fd, struct vnode **vpp); -int fgetvp_write(struct thread *td, int fd, struct vnode **vpp); - -int fgetsock(struct thread *td, int fd, struct socket **spp, u_int *fflagp); +int fgetvp(struct thread *td, int fd, cap_rights_t rights, struct vnode **vpp); +int fgetvp_rights(struct thread *td, int fd, cap_rights_t need, + cap_rights_t *have, struct vnode **vpp); +int fgetvp_read(struct thread *td, int fd, cap_rights_t rights, + struct vnode **vpp); +int fgetvp_write(struct thread *td, int fd, cap_rights_t rights, + struct vnode **vpp); + +int fgetsock(struct thread *td, int fd, cap_rights_t rights, + struct socket **spp, u_int *fflagp); void fputsock(struct socket *sp); static __inline int diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h index 2dab741..1b87bab 100644 --- a/sys/sys/filedesc.h +++ b/sys/sys/filedesc.h @@ -128,7 +128,8 @@ struct filedesc *fdshare(struct filedesc *fdp); struct filedesc_to_leader * filedesc_to_leader_alloc(struct filedesc_to_leader *old, struct filedesc *fdp, struct proc *leader); -int getvnode(struct filedesc *fdp, int fd, struct file **fpp); +int getvnode(struct filedesc *fdp, int fd, cap_rights_t rights, + struct file **fpp); void mountcheckdirs(struct vnode *olddp, struct vnode *newdp); void setugidsafety(struct thread *td); diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index f6d0366..1df96a1 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -62,9 +62,11 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_capsicum.h" #include "opt_quota.h" #include <sys/param.h> +#include <sys/capability.h> #include <sys/systm.h> #include <sys/bio.h> #include <sys/buf.h> @@ -2477,7 +2479,8 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS) return (error); if (cmd.version != FFS_CMD_VERSION) return (ERPCMISMATCH); - if ((error = getvnode(td->td_proc->p_fd, cmd.handle, &fp)) != 0) + if ((error = getvnode(td->td_proc->p_fd, cmd.handle, CAP_FSCK, + &fp)) != 0) return (error); vp = fp->f_data; if (vp->v_type != VREG && vp->v_type != VDIR) { @@ -2798,7 +2801,8 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS) (intmax_t)cmd.value); } #endif /* DEBUG */ - if ((error = getvnode(td->td_proc->p_fd, cmd.value, &vfp)) != 0) + if ((error = getvnode(td->td_proc->p_fd, cmd.value, + CAP_FSCK, &vfp)) != 0) break; if (vfp->f_vnode->v_type != VCHR) { fdrop(vfp, td); diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c index c78571d..a46d6b5 100644 --- a/sys/vm/vm_mmap.c +++ b/sys/vm/vm_mmap.c @@ -48,6 +48,8 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/capability.h> +#include <sys/kernel.h> #include <sys/lock.h> #include <sys/mutex.h> #include <sys/sysproto.h> @@ -189,12 +191,13 @@ mmap(td, uap) struct vnode *vp; vm_offset_t addr; vm_size_t size, pageoff; - vm_prot_t prot, maxprot; + vm_prot_t cap_maxprot, prot, maxprot; void *handle; objtype_t handle_type; int flags, error; off_t pos; struct vmspace *vms = td->td_proc->p_vmspace; + cap_rights_t rights; addr = (vm_offset_t) uap->addr; size = uap->len; @@ -274,12 +277,25 @@ mmap(td, uap) handle = NULL; handle_type = OBJT_DEFAULT; maxprot = VM_PROT_ALL; + cap_maxprot = VM_PROT_ALL; } else { /* - * Mapping file, get fp for validation and - * don't let the descriptor disappear on us if we block. + * Mapping file, get fp for validation and don't let the + * descriptor disappear on us if we block. Check capability + * rights, but also return the maximum rights to be combined + * with maxprot later. */ - if ((error = fget(td, uap->fd, &fp)) != 0) + rights = CAP_MMAP; + if (prot & PROT_READ) + rights |= CAP_READ; + if ((flags & MAP_SHARED) != 0) { + if (prot & PROT_WRITE) + rights |= CAP_WRITE; + } + if (prot & PROT_EXEC) + rights |= CAP_MAPEXEC; + if ((error = fget_mmap(td, uap->fd, rights, &cap_maxprot, + &fp)) != 0) goto done; if (fp->f_type == DTYPE_SHM) { handle = fp->f_data; @@ -346,12 +362,14 @@ mmap(td, uap) } } else if (vp->v_type != VCHR || (fp->f_flag & FWRITE) != 0) { maxprot |= VM_PROT_WRITE; + cap_maxprot |= VM_PROT_WRITE; } handle = (void *)vp; handle_type = OBJT_VNODE; } map: td->td_fpop = fp; + maxprot &= cap_maxprot; error = vm_mmap(&vms->vm_map, &addr, size, prot, maxprot, flags, handle_type, handle, pos); td->td_fpop = NULL; diff --git a/tools/regression/security/cap_test/cap_test.c b/tools/regression/security/cap_test/cap_test.c index fb34406..89ba9b7 100644 --- a/tools/regression/security/cap_test/cap_test.c +++ b/tools/regression/security/cap_test/cap_test.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include <sys/wait.h> #include <err.h> +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -60,7 +61,7 @@ main(int argc, char *argv[]) * If no tests have been specified at the command line, run them all. */ if (argc == 1) { - printf("1..%ld\n", test_count); + printf("1..%ju\n", (uintmax_t)test_count); for (size_t i = 0; i < test_count; i++) execute(i + 1, all_tests + i); diff --git a/tools/regression/security/cap_test/cap_test_capabilities.c b/tools/regression/security/cap_test/cap_test_capabilities.c index 818698d..47c0142 100644 --- a/tools/regression/security/cap_test/cap_test_capabilities.c +++ b/tools/regression/security/cap_test/cap_test_capabilities.c @@ -237,8 +237,8 @@ test_capabilities(void) TRY(fd, CAP_MMAP | CAP_MAPEXEC | CAP_WRITE); TRY(fd, CAP_MMAP | CAP_READ | CAP_WRITE | CAP_MAPEXEC); TRY(fd, CAP_FCNTL); - TRY(fd, CAP_EVENT); - TRY(fd, CAP_KEVENT); + TRY(fd, CAP_POST_KEVENT); + TRY(fd, CAP_POLL_KEVENT); TRY(fd, CAP_FSYNC); TRY(fd, CAP_FCHOWN); TRY(fd, CAP_FCHMOD); |