diff options
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/kern_descrip.c | 105 | ||||
-rw-r--r-- | sys/kern/kern_event.c | 7 | ||||
-rw-r--r-- | sys/kern/kern_exec.c | 6 | ||||
-rw-r--r-- | sys/kern/sys_capability.c | 2 | ||||
-rw-r--r-- | sys/kern/sys_generic.c | 57 | ||||
-rw-r--r-- | sys/kern/tty.c | 12 | ||||
-rw-r--r-- | sys/kern/uipc_mqueue.c | 28 | ||||
-rw-r--r-- | sys/kern/uipc_sem.c | 20 | ||||
-rw-r--r-- | sys/kern/uipc_syscalls.c | 96 | ||||
-rw-r--r-- | sys/kern/vfs_acl.c | 11 | ||||
-rw-r--r-- | sys/kern/vfs_aio.c | 29 | ||||
-rw-r--r-- | sys/kern/vfs_extattr.c | 10 | ||||
-rw-r--r-- | sys/kern/vfs_lookup.c | 9 | ||||
-rw-r--r-- | sys/kern/vfs_syscalls.c | 106 |
14 files changed, 348 insertions, 150 deletions
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; |