summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/amd64/linux32/linux32_machdep.c3
-rw-r--r--sys/cddl/compat/opensolaris/sys/file.h11
-rw-r--r--sys/compat/freebsd32/freebsd32_ioctl.c3
-rw-r--r--sys/compat/linux/linux_file.c9
-rw-r--r--sys/compat/linux/linux_ioctl.c41
-rw-r--r--sys/compat/linux/linux_socket.c3
-rw-r--r--sys/compat/linux/linux_stats.c5
-rw-r--r--sys/compat/svr4/svr4_fcntl.c17
-rw-r--r--sys/compat/svr4/svr4_filio.c3
-rw-r--r--sys/compat/svr4/svr4_ioctl.c3
-rw-r--r--sys/compat/svr4/svr4_misc.c10
-rw-r--r--sys/compat/svr4/svr4_stream.c5
-rw-r--r--sys/dev/aac/aac_linux.c3
-rw-r--r--sys/dev/amr/amr_linux.c3
-rw-r--r--sys/dev/hwpmc/hwpmc_logging.c3
-rw-r--r--sys/dev/ipmi/ipmi_linux.c3
-rw-r--r--sys/dev/iscsi/initiator/iscsi.c5
-rw-r--r--sys/dev/mfi/mfi_linux.c3
-rw-r--r--sys/dev/snp/snp.c3
-rw-r--r--sys/dev/tdfx/tdfx_linux.c3
-rw-r--r--sys/fs/coda/coda_psdev.c3
-rw-r--r--sys/fs/fdescfs/fdesc_vnops.c8
-rw-r--r--sys/fs/nfsclient/nfs_clport.c10
-rw-r--r--sys/fs/nfsserver/nfs_nfsdport.c10
-rw-r--r--sys/fs/portalfs/portal_vfsops.c8
-rw-r--r--sys/fs/portalfs/portal_vnops.c14
-rw-r--r--sys/gnu/fs/xfs/xfs_dfrag.c6
-rw-r--r--sys/i386/ibcs2/ibcs2_fcntl.c3
-rw-r--r--sys/i386/ibcs2/ibcs2_ioctl.c3
-rw-r--r--sys/i386/ibcs2/ibcs2_misc.c7
-rw-r--r--sys/i386/linux/linux_machdep.c6
-rw-r--r--sys/kern/kern_descrip.c105
-rw-r--r--sys/kern/kern_event.c7
-rw-r--r--sys/kern/kern_exec.c6
-rw-r--r--sys/kern/sys_capability.c2
-rw-r--r--sys/kern/sys_generic.c57
-rw-r--r--sys/kern/tty.c12
-rw-r--r--sys/kern/uipc_mqueue.c28
-rw-r--r--sys/kern/uipc_sem.c20
-rw-r--r--sys/kern/uipc_syscalls.c96
-rw-r--r--sys/kern/vfs_acl.c11
-rw-r--r--sys/kern/vfs_aio.c29
-rw-r--r--sys/kern/vfs_extattr.c10
-rw-r--r--sys/kern/vfs_lookup.c9
-rw-r--r--sys/kern/vfs_syscalls.c106
-rw-r--r--sys/netgraph/ng_socket.c2
-rw-r--r--sys/nfsserver/nfs_srvkrpc.c3
-rw-r--r--sys/security/audit/audit_arg.c2
-rw-r--r--sys/security/mac/mac_syscalls.c5
-rw-r--r--sys/sys/capability.h6
-rw-r--r--sys/sys/file.h25
-rw-r--r--sys/sys/filedesc.h3
-rw-r--r--sys/ufs/ffs/ffs_alloc.c8
-rw-r--r--sys/vm/vm_mmap.c26
-rw-r--r--tools/regression/security/cap_test/cap_test.c3
-rw-r--r--tools/regression/security/cap_test/cap_test_capabilities.c4
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);
OpenPOWER on IntegriCloud