summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2011-08-11 12:30:23 +0000
committerrwatson <rwatson@FreeBSD.org>2011-08-11 12:30:23 +0000
commit4af919b491560ff051b65cdf1ec730bdeb820b2e (patch)
tree4b691c0e209134040c3cf5ce75660b61282933d0
parentb3f993efadd59e4731fbd8ece5b71425df684b2d (diff)
downloadFreeBSD-src-4af919b491560ff051b65cdf1ec730bdeb820b2e.zip
FreeBSD-src-4af919b491560ff051b65cdf1ec730bdeb820b2e.tar.gz
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
-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