From a9d2f8d84f69e98100b5746816b35666bcf992ac Mon Sep 17 00:00:00 2001 From: Robert Watson Date: Thu, 11 Aug 2011 12:30:23 +0000 Subject: [PATCH] Second-to-last commit implementing Capsicum capabilities in the FreeBSD kernel for FreeBSD 9.0: Add a new capability mask argument to fget(9) and friends, allowing system call code to declare what capabilities are required when an integer file descriptor is converted into an in-kernel struct file *. With options CAPABILITIES compiled into the kernel, this enforces capability protection; without, this change is effectively a no-op. Some cases require special handling, such as mmap(2), which must preserve information about the maximum rights at the time of mapping in the memory map so that they can later be enforced in mprotect(2) -- this is done by narrowing the rights in the existing max_protection field used for similar purposes with file permissions. In namei(9), we assert that the code is not reached from within capability mode, as we're not yet ready to enforce namespace capabilities there. This will follow in a later commit. Update two capability names: CAP_EVENT and CAP_KEVENT become CAP_POST_KEVENT and CAP_POLL_KEVENT to more accurately indicate what they represent. Approved by: re (bz) Submitted by: jonathan Sponsored by: Google Inc --- sys/amd64/linux32/linux32_machdep.c | 3 +- sys/cddl/compat/opensolaris/sys/file.h | 11 +- sys/compat/freebsd32/freebsd32_ioctl.c | 3 +- sys/compat/linux/linux_file.c | 9 +- sys/compat/linux/linux_ioctl.c | 41 +++---- sys/compat/linux/linux_socket.c | 3 +- sys/compat/linux/linux_stats.c | 5 +- sys/compat/svr4/svr4_fcntl.c | 17 ++- sys/compat/svr4/svr4_filio.c | 3 +- sys/compat/svr4/svr4_ioctl.c | 3 +- sys/compat/svr4/svr4_misc.c | 10 +- sys/compat/svr4/svr4_stream.c | 5 +- sys/dev/aac/aac_linux.c | 3 +- sys/dev/amr/amr_linux.c | 3 +- sys/dev/hwpmc/hwpmc_logging.c | 3 +- sys/dev/ipmi/ipmi_linux.c | 3 +- sys/dev/iscsi/initiator/iscsi.c | 5 +- sys/dev/mfi/mfi_linux.c | 3 +- sys/dev/snp/snp.c | 3 + sys/dev/tdfx/tdfx_linux.c | 3 +- sys/fs/coda/coda_psdev.c | 3 +- sys/fs/fdescfs/fdesc_vnops.c | 8 +- sys/fs/nfsclient/nfs_clport.c | 10 +- sys/fs/nfsserver/nfs_nfsdport.c | 10 +- sys/fs/portalfs/portal_vfsops.c | 8 +- sys/fs/portalfs/portal_vnops.c | 14 ++- sys/gnu/fs/xfs/xfs_dfrag.c | 6 +- sys/i386/ibcs2/ibcs2_fcntl.c | 3 +- sys/i386/ibcs2/ibcs2_ioctl.c | 3 +- sys/i386/ibcs2/ibcs2_misc.c | 7 +- sys/i386/linux/linux_machdep.c | 6 +- sys/kern/kern_descrip.c | 105 +++++++++++------ sys/kern/kern_event.c | 7 +- sys/kern/kern_exec.c | 6 +- sys/kern/sys_capability.c | 2 +- sys/kern/sys_generic.c | 57 ++++++++-- sys/kern/tty.c | 12 ++ sys/kern/uipc_mqueue.c | 28 +++-- sys/kern/uipc_sem.c | 20 ++-- sys/kern/uipc_syscalls.c | 96 ++++++++++------ sys/kern/vfs_acl.c | 11 +- sys/kern/vfs_aio.c | 29 +++-- sys/kern/vfs_extattr.c | 10 +- sys/kern/vfs_lookup.c | 9 +- sys/kern/vfs_syscalls.c | 106 ++++++++++++------ sys/netgraph/ng_socket.c | 2 +- sys/nfsserver/nfs_srvkrpc.c | 3 +- sys/security/audit/audit_arg.c | 2 +- sys/security/mac/mac_syscalls.c | 5 +- sys/sys/capability.h | 6 +- sys/sys/file.h | 23 ++-- sys/sys/filedesc.h | 3 +- sys/ufs/ffs/ffs_alloc.c | 8 +- sys/vm/vm_mmap.c | 26 ++++- tools/regression/security/cap_test/cap_test.c | 3 +- .../security/cap_test/cap_test_capabilities.c | 4 +- 56 files changed, 563 insertions(+), 237 deletions(-) diff --git a/sys/amd64/linux32/linux32_machdep.c b/sys/amd64/linux32/linux32_machdep.c index 26041a309935..a8ebe7ef1629 100644 --- a/sys/amd64/linux32/linux32_machdep.c +++ b/sys/amd64/linux32/linux32_machdep.c @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -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 811b78cf87a6..7a3df369603a 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 + 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 e662a5d25ed9..1d773caab3bc 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 +#include #include #include #include @@ -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 44ad193c8b70..e92303287803 100644 --- a/sys/compat/linux/linux_file.c +++ b/sys/compat/linux/linux_file.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -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 5532c931fb39..d021fbada650 100644 --- a/sys/compat/linux/linux_ioctl.c +++ b/sys/compat/linux/linux_ioctl.c @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -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 6940e45be0bd..08728a13db19 100644 --- a/sys/compat/linux/linux_socket.c +++ b/sys/compat/linux/linux_socket.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -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 8fa08b690f9c..90f860d44776 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 88e4fc2bc12d..ce1452a3043b 100644 --- a/sys/compat/svr4/svr4_fcntl.c +++ b/sys/compat/svr4/svr4_fcntl.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include #include @@ -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 ca85653823ab..cb7cadae9c4f 100644 --- a/sys/compat/svr4/svr4_filio.c +++ b/sys/compat/svr4/svr4_filio.c @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -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 1cea41af856b..36b05803821f 100644 --- a/sys/compat/svr4/svr4_ioctl.c +++ b/sys/compat/svr4/svr4_ioctl.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -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 6f80fe64e667..c0a74780ee48 100644 --- a/sys/compat/svr4/svr4_misc.c +++ b/sys/compat/svr4/svr4_misc.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -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 ec95eece0e5b..a1a42c030116 100644 --- a/sys/compat/svr4/svr4_stream.c +++ b/sys/compat/svr4/svr4_stream.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -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 f8852358adec..049e2be78e5b 100644 --- a/sys/dev/aac/aac_linux.c +++ b/sys/dev/aac/aac_linux.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -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 cb8c4573ef6b..44e858ba804e 100644 --- a/sys/dev/amr/amr_linux.c +++ b/sys/dev/amr/amr_linux.c @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -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 633c6f953b6e..b85572a3c86f 100644 --- a/sys/dev/hwpmc/hwpmc_logging.c +++ b/sys/dev/hwpmc/hwpmc_logging.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include #include @@ -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 fcf2bd50322d..430bd0858948 100644 --- a/sys/dev/ipmi/ipmi_linux.c +++ b/sys/dev/ipmi/ipmi_linux.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -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 d35f6310747a..292ce8f21fa0 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 +#include #include #include #include @@ -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 44edf49109db..12135ff2470b 100644 --- a/sys/dev/mfi/mfi_linux.c +++ b/sys/dev/mfi/mfi_linux.c @@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -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 b05ad2a81b96..1c02660bec1d 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 2e877f6d0173..0b769f01a241 100644 --- a/sys/dev/tdfx/tdfx_linux.c +++ b/sys/dev/tdfx/tdfx_linux.c @@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include #include @@ -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 494f30bff513..e982a2c467e7 100644 --- a/sys/fs/coda/coda_psdev.c +++ b/sys/fs/coda/coda_psdev.c @@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -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 e11b59ca6716..3c4f44d17c96 100644 --- a/sys/fs/fdescfs/fdesc_vnops.c +++ b/sys/fs/fdescfs/fdesc_vnops.c @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -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 44d3c7421c48..acbfa6ceda8f 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 + /* * 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 56c563ae1f33..da32b0e544c2 100644 --- a/sys/fs/nfsserver/nfs_nfsdport.c +++ b/sys/fs/nfsserver/nfs_nfsdport.c @@ -34,6 +34,8 @@ #include __FBSDID("$FreeBSD$"); +#include + /* * 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 a8c16c4b17fa..04e7a3d22500 100644 --- a/sys/fs/portalfs/portal_vfsops.c +++ b/sys/fs/portalfs/portal_vfsops.c @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -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 6fcc1ce64219..7cd5267698b7 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 +#include #include #include #include @@ -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 77a71dfe143c..7b6b04ebcda7 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 #include /* @@ -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 6875aef71fdc..fddfcb59321b 100644 --- a/sys/i386/ibcs2/ibcs2_fcntl.c +++ b/sys/i386/ibcs2/ibcs2_fcntl.c @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -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 90184e3cb628..3a582ce2f337 100644 --- a/sys/i386/ibcs2/ibcs2_ioctl.c +++ b/sys/i386/ibcs2/ibcs2_ioctl.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -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 c537100ba177..a08fdf3914d1 100644 --- a/sys/i386/ibcs2/ibcs2_misc.c +++ b/sys/i386/ibcs2/ibcs2_misc.c @@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$"); */ #include #include +#include #include #include #include @@ -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 d1f6ab99780c..57756c4d3e83 100644 --- a/sys/i386/linux/linux_machdep.c +++ b/sys/i386/linux/linux_machdep.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -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 f3f9cbcaf469..3082aea810d8 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, 0, NULL, NULL, 0)); + return(_fget(td, fd, fpp, 0, rights, NULL, NULL, 0)); } int -fget_read(struct thread *td, int fd, struct file **fpp) +fget_mmap(struct thread *td, int fd, cap_rights_t rights, u_char *maxprotp, + struct file **fpp) { - return(_fget(td, fd, fpp, FREAD, 0, NULL, NULL, 0)); + return (_fget(td, fd, fpp, 0, rights, NULL, maxprotp, 0)); } int -fget_write(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, FWRITE, 0, NULL, NULL, 0)); + return(_fget(td, fd, fpp, FREAD, rights, NULL, NULL, 0)); +} + +int +fget_write(struct thread *td, int fd, cap_rights_t rights, struct file **fpp) +{ + + 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_read(struct thread *td, int fd, struct vnode **vpp) +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, 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 e14ae0211bc6..6ec8503d5691 100644 --- a/sys/kern/kern_event.c +++ b/sys/kern/kern_event.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -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 f7f80af68c25..1c424204800f 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -439,7 +439,11 @@ do_execve(td, args, mac_p) 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 6ca960269d6c..b20fa62e123a 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 1a2685cc1f01..f94be5a6e868 100644 --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -37,12 +37,14 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_capsicum.h" #include "opt_compat.h" #include "opt_ktrace.h" #include #include #include +#include #include #include #include @@ -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 187e6358f751..77c02dd57dd7 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -30,9 +30,11 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_capsicum.h" #include "opt_compat.h" #include +#include #include #include #include @@ -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 9b334acb3fe3..e13912c4aca8 100644 --- a/sys/kern/uipc_mqueue.c +++ b/sys/kern/uipc_mqueue.c @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -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 917c343ed3a4..82a4622c5dd6 100644 --- a/sys/kern/uipc_sem.c +++ b/sys/kern/uipc_sem.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include "opt_posix.h" #include +#include #include #include #include @@ -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 c434973a1b67..0e5efe6f3a9b 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 b1cda38419e5..9010a5068bfb 100644 --- a/sys/kern/vfs_acl.c +++ b/sys/kern/vfs_acl.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -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 69b4e0a00c70..aedbdd05385a 100644 --- a/sys/kern/vfs_aio.c +++ b/sys/kern/vfs_aio.c @@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -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 e7bf2d19d7d3..b8b9cdf058bd 100644 --- a/sys/kern/vfs_extattr.c +++ b/sys/kern/vfs_extattr.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -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 50a2570254f4..ae8982fee0d6 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -37,12 +37,14 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_capsicum.h" #include "opt_kdtrace.h" #include "opt_ktrace.h" #include #include #include +#include #include #include #include @@ -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 b48c6e796958..3a7ca062faad 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -37,6 +37,7 @@ #include __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 #include #include +#include #include #include #include @@ -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 @@ revoke(td, uap) } /* - * 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 f72f8226c76e..8c819c01429d 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 3c60825f2087..2581092eafe0 100644 --- a/sys/nfsserver/nfs_srvkrpc.c +++ b/sys/nfsserver/nfs_srvkrpc.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include "opt_kgssapi.h" #include +#include #include #include #include @@ -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 4e155da619e0..e4409f28bf67 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 aea088582b31..dc275477be50 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 +#include #include #include #include @@ -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 b6561c7d3d53..bd1d06087576 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 eea2c0091f65..2c64bcf644e0 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 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, struct socket **spp, u_int *fflagp); +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 2dab7416b6e0..1b87bab201d8 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 f6d0366b9ac7..1df96a102af2 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -62,9 +62,11 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_capsicum.h" #include "opt_quota.h" #include +#include #include #include #include @@ -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 c78571d30539..a46d6b5ccf60 100644 --- a/sys/vm/vm_mmap.c +++ b/sys/vm/vm_mmap.c @@ -48,6 +48,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#include #include #include #include @@ -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 fb3440672337..89ba9b71e77a 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 #include +#include #include #include #include @@ -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 818698db2376..47c0142f4dab 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);