196fcc75fSRobert Watson /*- 296fcc75fSRobert Watson * Copyright (c) 2008-2011 Robert N. M. Watson 396fcc75fSRobert Watson * Copyright (c) 2010-2011 Jonathan Anderson 42609222aSPawel Jakub Dawidek * Copyright (c) 2012 FreeBSD Foundation 596fcc75fSRobert Watson * All rights reserved. 696fcc75fSRobert Watson * 796fcc75fSRobert Watson * This software was developed at the University of Cambridge Computer 896fcc75fSRobert Watson * Laboratory with support from a grant from Google, Inc. 996fcc75fSRobert Watson * 102609222aSPawel Jakub Dawidek * Portions of this software were developed by Pawel Jakub Dawidek under 112609222aSPawel Jakub Dawidek * sponsorship from the FreeBSD Foundation. 122609222aSPawel Jakub Dawidek * 1396fcc75fSRobert Watson * Redistribution and use in source and binary forms, with or without 1496fcc75fSRobert Watson * modification, are permitted provided that the following conditions 1596fcc75fSRobert Watson * are met: 1696fcc75fSRobert Watson * 1. Redistributions of source code must retain the above copyright 1796fcc75fSRobert Watson * notice, this list of conditions and the following disclaimer. 1896fcc75fSRobert Watson * 2. Redistributions in binary form must reproduce the above copyright 1996fcc75fSRobert Watson * notice, this list of conditions and the following disclaimer in the 2096fcc75fSRobert Watson * documentation and/or other materials provided with the distribution. 2196fcc75fSRobert Watson * 2296fcc75fSRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2396fcc75fSRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2496fcc75fSRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2596fcc75fSRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2696fcc75fSRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2796fcc75fSRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2896fcc75fSRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2996fcc75fSRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3096fcc75fSRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3196fcc75fSRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3296fcc75fSRobert Watson * SUCH DAMAGE. 3396fcc75fSRobert Watson */ 3496fcc75fSRobert Watson 3596fcc75fSRobert Watson /* 3696fcc75fSRobert Watson * FreeBSD kernel capability facility. 3796fcc75fSRobert Watson * 3873516dbdSRobert Watson * Two kernel features are implemented here: capability mode, a sandboxed mode 3973516dbdSRobert Watson * of execution for processes, and capabilities, a refinement on file 4073516dbdSRobert Watson * descriptors that allows fine-grained control over operations on the file 4173516dbdSRobert Watson * descriptor. Collectively, these allow processes to run in the style of a 4273516dbdSRobert Watson * historic "capability system" in which they can use only resources 4373516dbdSRobert Watson * explicitly delegated to them. This model is enforced by restricting access 4473516dbdSRobert Watson * to global namespaces in capability mode. 4596fcc75fSRobert Watson * 4673516dbdSRobert Watson * Capabilities wrap other file descriptor types, binding them to a constant 4773516dbdSRobert Watson * rights mask set when the capability is created. New capabilities may be 4873516dbdSRobert Watson * derived from existing capabilities, but only if they have the same or a 4973516dbdSRobert Watson * strict subset of the rights on the original capability. 5073516dbdSRobert Watson * 5173516dbdSRobert Watson * System calls permitted in capability mode are defined in capabilities.conf; 5273516dbdSRobert Watson * calls must be carefully audited for safety to ensure that they don't allow 5373516dbdSRobert Watson * escape from a sandbox. Some calls permit only a subset of operations in 5473516dbdSRobert Watson * capability mode -- for example, shm_open(2) is limited to creating 5573516dbdSRobert Watson * anonymous, rather than named, POSIX shared memory objects. 5696fcc75fSRobert Watson */ 5796fcc75fSRobert Watson 5896fcc75fSRobert Watson #include <sys/cdefs.h> 5996fcc75fSRobert Watson __FBSDID("$FreeBSD$"); 6096fcc75fSRobert Watson 61297f1103SPawel Jakub Dawidek #include "opt_capsicum.h" 62297f1103SPawel Jakub Dawidek #include "opt_ktrace.h" 63297f1103SPawel Jakub Dawidek 6496fcc75fSRobert Watson #include <sys/param.h> 6596fcc75fSRobert Watson #include <sys/capability.h> 6696fcc75fSRobert Watson #include <sys/file.h> 6796fcc75fSRobert Watson #include <sys/filedesc.h> 6896fcc75fSRobert Watson #include <sys/kernel.h> 692609222aSPawel Jakub Dawidek #include <sys/limits.h> 7096fcc75fSRobert Watson #include <sys/lock.h> 7196fcc75fSRobert Watson #include <sys/mutex.h> 7296fcc75fSRobert Watson #include <sys/proc.h> 7396fcc75fSRobert Watson #include <sys/sysproto.h> 7496fcc75fSRobert Watson #include <sys/sysctl.h> 7596fcc75fSRobert Watson #include <sys/systm.h> 7696fcc75fSRobert Watson #include <sys/ucred.h> 77c601ad8eSDag-Erling Smørgrav #include <sys/uio.h> 78c601ad8eSDag-Erling Smørgrav #include <sys/ktrace.h> 7996fcc75fSRobert Watson 8096fcc75fSRobert Watson #include <security/audit/audit.h> 8196fcc75fSRobert Watson 8296fcc75fSRobert Watson #include <vm/uma.h> 8396fcc75fSRobert Watson #include <vm/vm.h> 8496fcc75fSRobert Watson 8524c1c3bfSJonathan Anderson #ifdef CAPABILITY_MODE 8696fcc75fSRobert Watson 87854d7b9fSRobert Watson FEATURE(security_capability_mode, "Capsicum Capability Mode"); 88d783bbd2SAlexander Leidinger 8996fcc75fSRobert Watson /* 9096fcc75fSRobert Watson * System call to enter capability mode for the process. 9196fcc75fSRobert Watson */ 9296fcc75fSRobert Watson int 938451d0ddSKip Macy sys_cap_enter(struct thread *td, struct cap_enter_args *uap) 9496fcc75fSRobert Watson { 9596fcc75fSRobert Watson struct ucred *newcred, *oldcred; 9696fcc75fSRobert Watson struct proc *p; 9796fcc75fSRobert Watson 9896fcc75fSRobert Watson if (IN_CAPABILITY_MODE(td)) 9996fcc75fSRobert Watson return (0); 10096fcc75fSRobert Watson 10196fcc75fSRobert Watson newcred = crget(); 10296fcc75fSRobert Watson p = td->td_proc; 10396fcc75fSRobert Watson PROC_LOCK(p); 10496fcc75fSRobert Watson oldcred = p->p_ucred; 10596fcc75fSRobert Watson crcopy(newcred, oldcred); 10696fcc75fSRobert Watson newcred->cr_flags |= CRED_FLAG_CAPMODE; 10796fcc75fSRobert Watson p->p_ucred = newcred; 10896fcc75fSRobert Watson PROC_UNLOCK(p); 10996fcc75fSRobert Watson crfree(oldcred); 11096fcc75fSRobert Watson return (0); 11196fcc75fSRobert Watson } 11296fcc75fSRobert Watson 11396fcc75fSRobert Watson /* 11496fcc75fSRobert Watson * System call to query whether the process is in capability mode. 11596fcc75fSRobert Watson */ 11696fcc75fSRobert Watson int 1178451d0ddSKip Macy sys_cap_getmode(struct thread *td, struct cap_getmode_args *uap) 11896fcc75fSRobert Watson { 11996fcc75fSRobert Watson u_int i; 12096fcc75fSRobert Watson 12111b0cfe3SPawel Jakub Dawidek i = IN_CAPABILITY_MODE(td) ? 1 : 0; 12296fcc75fSRobert Watson return (copyout(&i, uap->modep, sizeof(i))); 12396fcc75fSRobert Watson } 12496fcc75fSRobert Watson 12524c1c3bfSJonathan Anderson #else /* !CAPABILITY_MODE */ 12696fcc75fSRobert Watson 12796fcc75fSRobert Watson int 1288451d0ddSKip Macy sys_cap_enter(struct thread *td, struct cap_enter_args *uap) 12996fcc75fSRobert Watson { 13096fcc75fSRobert Watson 13196fcc75fSRobert Watson return (ENOSYS); 13296fcc75fSRobert Watson } 13396fcc75fSRobert Watson 13496fcc75fSRobert Watson int 1358451d0ddSKip Macy sys_cap_getmode(struct thread *td, struct cap_getmode_args *uap) 13696fcc75fSRobert Watson { 13796fcc75fSRobert Watson 13896fcc75fSRobert Watson return (ENOSYS); 13996fcc75fSRobert Watson } 14096fcc75fSRobert Watson 14124c1c3bfSJonathan Anderson #endif /* CAPABILITY_MODE */ 142af098ed8SJonathan Anderson 143af098ed8SJonathan Anderson #ifdef CAPABILITIES 144af098ed8SJonathan Anderson 145854d7b9fSRobert Watson FEATURE(security_capabilities, "Capsicum Capabilities"); 146854d7b9fSRobert Watson 147*92981fdfSPawel Jakub Dawidek MALLOC_DECLARE(M_FILECAPS); 148*92981fdfSPawel Jakub Dawidek 1492609222aSPawel Jakub Dawidek static inline int 1502609222aSPawel Jakub Dawidek _cap_check(cap_rights_t have, cap_rights_t need, enum ktr_cap_fail_type type) 151745bae37SJonathan Anderson { 152745bae37SJonathan Anderson 153745bae37SJonathan Anderson 1542609222aSPawel Jakub Dawidek if ((need & ~have) != 0) { 155c601ad8eSDag-Erling Smørgrav #ifdef KTRACE 156c601ad8eSDag-Erling Smørgrav if (KTRPOINT(curthread, KTR_CAPFAIL)) 1572609222aSPawel Jakub Dawidek ktrcapfail(type, need, have); 158c601ad8eSDag-Erling Smørgrav #endif 159af098ed8SJonathan Anderson return (ENOTCAPABLE); 160c601ad8eSDag-Erling Smørgrav } 161af098ed8SJonathan Anderson return (0); 162af098ed8SJonathan Anderson } 163af098ed8SJonathan Anderson 164af098ed8SJonathan Anderson /* 1652609222aSPawel Jakub Dawidek * Test whether a capability grants the requested rights. 1662609222aSPawel Jakub Dawidek */ 1672609222aSPawel Jakub Dawidek int 1682609222aSPawel Jakub Dawidek cap_check(cap_rights_t have, cap_rights_t need) 1692609222aSPawel Jakub Dawidek { 1702609222aSPawel Jakub Dawidek 1712609222aSPawel Jakub Dawidek return (_cap_check(have, need, CAPFAIL_NOTCAPABLE)); 1722609222aSPawel Jakub Dawidek } 1732609222aSPawel Jakub Dawidek 1742609222aSPawel Jakub Dawidek /* 1752609222aSPawel Jakub Dawidek * Convert capability rights into VM access flags. 1762609222aSPawel Jakub Dawidek */ 1772609222aSPawel Jakub Dawidek u_char 1782609222aSPawel Jakub Dawidek cap_rights_to_vmprot(cap_rights_t have) 1792609222aSPawel Jakub Dawidek { 1802609222aSPawel Jakub Dawidek u_char maxprot; 1812609222aSPawel Jakub Dawidek 1822609222aSPawel Jakub Dawidek maxprot = VM_PROT_NONE; 1832609222aSPawel Jakub Dawidek if (have & CAP_MMAP_R) 1842609222aSPawel Jakub Dawidek maxprot |= VM_PROT_READ; 1852609222aSPawel Jakub Dawidek if (have & CAP_MMAP_W) 1862609222aSPawel Jakub Dawidek maxprot |= VM_PROT_WRITE; 1872609222aSPawel Jakub Dawidek if (have & CAP_MMAP_X) 1882609222aSPawel Jakub Dawidek maxprot |= VM_PROT_EXECUTE; 1892609222aSPawel Jakub Dawidek 1902609222aSPawel Jakub Dawidek return (maxprot); 1912609222aSPawel Jakub Dawidek } 1922609222aSPawel Jakub Dawidek 1932609222aSPawel Jakub Dawidek /* 194745bae37SJonathan Anderson * Extract rights from a capability for monitoring purposes -- not for use in 195745bae37SJonathan Anderson * any other way, as we want to keep all capability permission evaluation in 196745bae37SJonathan Anderson * this one file. 197745bae37SJonathan Anderson */ 198745bae37SJonathan Anderson cap_rights_t 1992609222aSPawel Jakub Dawidek cap_rights(struct filedesc *fdp, int fd) 200745bae37SJonathan Anderson { 201745bae37SJonathan Anderson 2022609222aSPawel Jakub Dawidek return (fdp->fd_ofiles[fd].fde_rights); 203745bae37SJonathan Anderson } 204745bae37SJonathan Anderson 205745bae37SJonathan Anderson /* 2062609222aSPawel Jakub Dawidek * System call to limit rights of the given capability. 207cfb9df55SJonathan Anderson */ 208cfb9df55SJonathan Anderson int 2092609222aSPawel Jakub Dawidek sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap) 210cfb9df55SJonathan Anderson { 2112609222aSPawel Jakub Dawidek struct filedesc *fdp; 2122609222aSPawel Jakub Dawidek cap_rights_t rights; 2132609222aSPawel Jakub Dawidek int error, fd; 2142609222aSPawel Jakub Dawidek 2152609222aSPawel Jakub Dawidek fd = uap->fd; 2162609222aSPawel Jakub Dawidek rights = uap->rights; 217cfb9df55SJonathan Anderson 218cfb9df55SJonathan Anderson AUDIT_ARG_FD(fd); 219cfb9df55SJonathan Anderson AUDIT_ARG_RIGHTS(rights); 2202609222aSPawel Jakub Dawidek 2212609222aSPawel Jakub Dawidek if ((rights & ~CAP_ALL) != 0) 2222609222aSPawel Jakub Dawidek return (EINVAL); 2232609222aSPawel Jakub Dawidek 2242609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 2252609222aSPawel Jakub Dawidek FILEDESC_XLOCK(fdp); 2262609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 2272609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 2282609222aSPawel Jakub Dawidek return (EBADF); 2292609222aSPawel Jakub Dawidek } 2302609222aSPawel Jakub Dawidek error = _cap_check(cap_rights(fdp, fd), rights, CAPFAIL_INCREASE); 2312609222aSPawel Jakub Dawidek if (error == 0) { 2322609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_rights = rights; 2332609222aSPawel Jakub Dawidek if ((rights & CAP_IOCTL) == 0) { 234*92981fdfSPawel Jakub Dawidek free(fdp->fd_ofiles[fd].fde_ioctls, M_FILECAPS); 2352609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_ioctls = NULL; 2362609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_nioctls = 0; 2372609222aSPawel Jakub Dawidek } 2382609222aSPawel Jakub Dawidek if ((rights & CAP_FCNTL) == 0) 2392609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_fcntls = 0; 2402609222aSPawel Jakub Dawidek } 2412609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 24262a9fc76SPeter Holm return (error); 243cfb9df55SJonathan Anderson } 244cfb9df55SJonathan Anderson 245cfb9df55SJonathan Anderson /* 246cfb9df55SJonathan Anderson * System call to query the rights mask associated with a capability. 247cfb9df55SJonathan Anderson */ 248cfb9df55SJonathan Anderson int 2492609222aSPawel Jakub Dawidek sys_cap_rights_get(struct thread *td, struct cap_rights_get_args *uap) 250cfb9df55SJonathan Anderson { 2512609222aSPawel Jakub Dawidek struct filedesc *fdp; 2522609222aSPawel Jakub Dawidek cap_rights_t rights; 2532609222aSPawel Jakub Dawidek int fd; 254cfb9df55SJonathan Anderson 2552609222aSPawel Jakub Dawidek fd = uap->fd; 2562609222aSPawel Jakub Dawidek 2572609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 2582609222aSPawel Jakub Dawidek 2592609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 2602609222aSPawel Jakub Dawidek FILEDESC_SLOCK(fdp); 2612609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 2622609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 2632609222aSPawel Jakub Dawidek return (EBADF); 2642609222aSPawel Jakub Dawidek } 2652609222aSPawel Jakub Dawidek rights = cap_rights(fdp, fd); 2662609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 2672609222aSPawel Jakub Dawidek return (copyout(&rights, uap->rightsp, sizeof(*uap->rightsp))); 268cfb9df55SJonathan Anderson } 269cfb9df55SJonathan Anderson 270cfb9df55SJonathan Anderson /* 2712609222aSPawel Jakub Dawidek * Test whether a capability grants the given ioctl command. 2722609222aSPawel Jakub Dawidek * If descriptor doesn't have CAP_IOCTL, then ioctls list is empty and 2732609222aSPawel Jakub Dawidek * ENOTCAPABLE will be returned. 274745bae37SJonathan Anderson */ 275745bae37SJonathan Anderson int 2762609222aSPawel Jakub Dawidek cap_ioctl_check(struct filedesc *fdp, int fd, u_long cmd) 277745bae37SJonathan Anderson { 2782609222aSPawel Jakub Dawidek u_long *cmds; 2792609222aSPawel Jakub Dawidek ssize_t ncmds; 2802609222aSPawel Jakub Dawidek long i; 281745bae37SJonathan Anderson 2822609222aSPawel Jakub Dawidek FILEDESC_LOCK_ASSERT(fdp); 2832609222aSPawel Jakub Dawidek KASSERT(fd >= 0 && fd < fdp->fd_nfiles, 2842609222aSPawel Jakub Dawidek ("%s: invalid fd=%d", __func__, fd)); 285745bae37SJonathan Anderson 2862609222aSPawel Jakub Dawidek ncmds = fdp->fd_ofiles[fd].fde_nioctls; 2872609222aSPawel Jakub Dawidek if (ncmds == -1) 2882609222aSPawel Jakub Dawidek return (0); 2892609222aSPawel Jakub Dawidek 2902609222aSPawel Jakub Dawidek cmds = fdp->fd_ofiles[fd].fde_ioctls; 2912609222aSPawel Jakub Dawidek for (i = 0; i < ncmds; i++) { 2922609222aSPawel Jakub Dawidek if (cmds[i] == cmd) 2932609222aSPawel Jakub Dawidek return (0); 2942609222aSPawel Jakub Dawidek } 2952609222aSPawel Jakub Dawidek 296745bae37SJonathan Anderson return (ENOTCAPABLE); 297745bae37SJonathan Anderson } 2982609222aSPawel Jakub Dawidek 2992609222aSPawel Jakub Dawidek /* 3002609222aSPawel Jakub Dawidek * Check if the current ioctls list can be replaced by the new one. 3012609222aSPawel Jakub Dawidek */ 3022609222aSPawel Jakub Dawidek static int 3032609222aSPawel Jakub Dawidek cap_ioctl_limit_check(struct filedesc *fdp, int fd, const u_long *cmds, 3042609222aSPawel Jakub Dawidek size_t ncmds) 3052609222aSPawel Jakub Dawidek { 3062609222aSPawel Jakub Dawidek u_long *ocmds; 3072609222aSPawel Jakub Dawidek ssize_t oncmds; 3082609222aSPawel Jakub Dawidek u_long i; 3092609222aSPawel Jakub Dawidek long j; 3102609222aSPawel Jakub Dawidek 3112609222aSPawel Jakub Dawidek oncmds = fdp->fd_ofiles[fd].fde_nioctls; 3122609222aSPawel Jakub Dawidek if (oncmds == -1) 3132609222aSPawel Jakub Dawidek return (0); 3142609222aSPawel Jakub Dawidek if (oncmds < (ssize_t)ncmds) 3152609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 3162609222aSPawel Jakub Dawidek 3172609222aSPawel Jakub Dawidek ocmds = fdp->fd_ofiles[fd].fde_ioctls; 3182609222aSPawel Jakub Dawidek for (i = 0; i < ncmds; i++) { 3192609222aSPawel Jakub Dawidek for (j = 0; j < oncmds; j++) { 3202609222aSPawel Jakub Dawidek if (cmds[i] == ocmds[j]) 3212609222aSPawel Jakub Dawidek break; 3222609222aSPawel Jakub Dawidek } 3232609222aSPawel Jakub Dawidek if (j == oncmds) 3242609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 3252609222aSPawel Jakub Dawidek } 3262609222aSPawel Jakub Dawidek 3272609222aSPawel Jakub Dawidek return (0); 3282609222aSPawel Jakub Dawidek } 3292609222aSPawel Jakub Dawidek 3302609222aSPawel Jakub Dawidek int 3312609222aSPawel Jakub Dawidek sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap) 3322609222aSPawel Jakub Dawidek { 3332609222aSPawel Jakub Dawidek struct filedesc *fdp; 3342609222aSPawel Jakub Dawidek u_long *cmds, *ocmds; 3352609222aSPawel Jakub Dawidek size_t ncmds; 3362609222aSPawel Jakub Dawidek int error, fd; 3372609222aSPawel Jakub Dawidek 3382609222aSPawel Jakub Dawidek fd = uap->fd; 3392609222aSPawel Jakub Dawidek ncmds = uap->ncmds; 3402609222aSPawel Jakub Dawidek 3412609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 3422609222aSPawel Jakub Dawidek 3432609222aSPawel Jakub Dawidek if (ncmds > 256) /* XXX: Is 256 sane? */ 3442609222aSPawel Jakub Dawidek return (EINVAL); 3452609222aSPawel Jakub Dawidek 3462609222aSPawel Jakub Dawidek if (ncmds == 0) { 3472609222aSPawel Jakub Dawidek cmds = NULL; 3482609222aSPawel Jakub Dawidek } else { 349*92981fdfSPawel Jakub Dawidek cmds = malloc(sizeof(cmds[0]) * ncmds, M_FILECAPS, M_WAITOK); 3502609222aSPawel Jakub Dawidek error = copyin(uap->cmds, cmds, sizeof(cmds[0]) * ncmds); 3512609222aSPawel Jakub Dawidek if (error != 0) { 352*92981fdfSPawel Jakub Dawidek free(cmds, M_FILECAPS); 3532609222aSPawel Jakub Dawidek return (error); 3542609222aSPawel Jakub Dawidek } 3552609222aSPawel Jakub Dawidek } 3562609222aSPawel Jakub Dawidek 3572609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 3582609222aSPawel Jakub Dawidek FILEDESC_XLOCK(fdp); 3592609222aSPawel Jakub Dawidek 3602609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 3612609222aSPawel Jakub Dawidek error = EBADF; 3622609222aSPawel Jakub Dawidek goto out; 3632609222aSPawel Jakub Dawidek } 3642609222aSPawel Jakub Dawidek 3652609222aSPawel Jakub Dawidek error = cap_ioctl_limit_check(fdp, fd, cmds, ncmds); 3662609222aSPawel Jakub Dawidek if (error != 0) 3672609222aSPawel Jakub Dawidek goto out; 3682609222aSPawel Jakub Dawidek 3692609222aSPawel Jakub Dawidek ocmds = fdp->fd_ofiles[fd].fde_ioctls; 3702609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_ioctls = cmds; 3712609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_nioctls = ncmds; 3722609222aSPawel Jakub Dawidek 3732609222aSPawel Jakub Dawidek cmds = ocmds; 3742609222aSPawel Jakub Dawidek error = 0; 3752609222aSPawel Jakub Dawidek out: 3762609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 377*92981fdfSPawel Jakub Dawidek free(cmds, M_FILECAPS); 3782609222aSPawel Jakub Dawidek return (error); 3792609222aSPawel Jakub Dawidek } 3802609222aSPawel Jakub Dawidek 3812609222aSPawel Jakub Dawidek int 3822609222aSPawel Jakub Dawidek sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap) 3832609222aSPawel Jakub Dawidek { 3842609222aSPawel Jakub Dawidek struct filedesc *fdp; 3852609222aSPawel Jakub Dawidek struct filedescent *fdep; 3862609222aSPawel Jakub Dawidek u_long *cmds; 3872609222aSPawel Jakub Dawidek size_t maxcmds; 3882609222aSPawel Jakub Dawidek int error, fd; 3892609222aSPawel Jakub Dawidek 3902609222aSPawel Jakub Dawidek fd = uap->fd; 3912609222aSPawel Jakub Dawidek cmds = uap->cmds; 3922609222aSPawel Jakub Dawidek maxcmds = uap->maxcmds; 3932609222aSPawel Jakub Dawidek 3942609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 3952609222aSPawel Jakub Dawidek 3962609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 3972609222aSPawel Jakub Dawidek FILEDESC_SLOCK(fdp); 3982609222aSPawel Jakub Dawidek 3992609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 4002609222aSPawel Jakub Dawidek error = EBADF; 4012609222aSPawel Jakub Dawidek goto out; 402e141be6fSDag-Erling Smørgrav } 403745bae37SJonathan Anderson 404745bae37SJonathan Anderson /* 4052609222aSPawel Jakub Dawidek * If all ioctls are allowed (fde_nioctls == -1 && fde_ioctls == NULL) 4062609222aSPawel Jakub Dawidek * the only sane thing we can do is to not populate the given array and 4072609222aSPawel Jakub Dawidek * return CAP_IOCTLS_ALL. 408745bae37SJonathan Anderson */ 409745bae37SJonathan Anderson 4102609222aSPawel Jakub Dawidek fdep = &fdp->fd_ofiles[fd]; 4112609222aSPawel Jakub Dawidek if (cmds != NULL && fdep->fde_ioctls != NULL) { 4122609222aSPawel Jakub Dawidek error = copyout(fdep->fde_ioctls, cmds, 4132609222aSPawel Jakub Dawidek sizeof(cmds[0]) * MIN(fdep->fde_nioctls, maxcmds)); 4142609222aSPawel Jakub Dawidek if (error != 0) 4152609222aSPawel Jakub Dawidek goto out; 4162609222aSPawel Jakub Dawidek } 4172609222aSPawel Jakub Dawidek if (fdep->fde_nioctls == -1) 4182609222aSPawel Jakub Dawidek td->td_retval[0] = CAP_IOCTLS_ALL; 419745bae37SJonathan Anderson else 4202609222aSPawel Jakub Dawidek td->td_retval[0] = fdep->fde_nioctls; 421745bae37SJonathan Anderson 4222609222aSPawel Jakub Dawidek error = 0; 4232609222aSPawel Jakub Dawidek out: 4242609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 425af098ed8SJonathan Anderson return (error); 426af098ed8SJonathan Anderson } 427af098ed8SJonathan Anderson 428af098ed8SJonathan Anderson /* 4292609222aSPawel Jakub Dawidek * Test whether a capability grants the given fcntl command. 430af098ed8SJonathan Anderson */ 431af098ed8SJonathan Anderson int 4322609222aSPawel Jakub Dawidek cap_fcntl_check(struct filedesc *fdp, int fd, int cmd) 433af098ed8SJonathan Anderson { 4342609222aSPawel Jakub Dawidek uint32_t fcntlcap; 435af098ed8SJonathan Anderson 4362609222aSPawel Jakub Dawidek KASSERT(fd >= 0 && fd < fdp->fd_nfiles, 4372609222aSPawel Jakub Dawidek ("%s: invalid fd=%d", __func__, fd)); 4382609222aSPawel Jakub Dawidek 4392609222aSPawel Jakub Dawidek fcntlcap = (1 << cmd); 4402609222aSPawel Jakub Dawidek KASSERT((CAP_FCNTL_ALL & fcntlcap) != 0, 4412609222aSPawel Jakub Dawidek ("Unsupported fcntl=%d.", cmd)); 4422609222aSPawel Jakub Dawidek 4432609222aSPawel Jakub Dawidek if ((fdp->fd_ofiles[fd].fde_fcntls & fcntlcap) != 0) 4442609222aSPawel Jakub Dawidek return (0); 4452609222aSPawel Jakub Dawidek 4462609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 4472609222aSPawel Jakub Dawidek } 4482609222aSPawel Jakub Dawidek 4492609222aSPawel Jakub Dawidek int 4502609222aSPawel Jakub Dawidek sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap) 4512609222aSPawel Jakub Dawidek { 4522609222aSPawel Jakub Dawidek struct filedesc *fdp; 4532609222aSPawel Jakub Dawidek uint32_t fcntlrights; 4542609222aSPawel Jakub Dawidek int fd; 4552609222aSPawel Jakub Dawidek 4562609222aSPawel Jakub Dawidek fd = uap->fd; 4572609222aSPawel Jakub Dawidek fcntlrights = uap->fcntlrights; 4582609222aSPawel Jakub Dawidek 4592609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 4602609222aSPawel Jakub Dawidek AUDIT_ARG_FCNTL_RIGHTS(fcntlrights); 4612609222aSPawel Jakub Dawidek 4622609222aSPawel Jakub Dawidek if ((fcntlrights & ~CAP_FCNTL_ALL) != 0) 4632609222aSPawel Jakub Dawidek return (EINVAL); 4642609222aSPawel Jakub Dawidek 4652609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 4662609222aSPawel Jakub Dawidek FILEDESC_XLOCK(fdp); 4672609222aSPawel Jakub Dawidek 4682609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 4692609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 4702609222aSPawel Jakub Dawidek return (EBADF); 4712609222aSPawel Jakub Dawidek } 4722609222aSPawel Jakub Dawidek 4732609222aSPawel Jakub Dawidek if ((fcntlrights & ~fdp->fd_ofiles[fd].fde_fcntls) != 0) { 4742609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 4752609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 4762609222aSPawel Jakub Dawidek } 4772609222aSPawel Jakub Dawidek 4782609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_fcntls = fcntlrights; 4792609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 4802609222aSPawel Jakub Dawidek 481af098ed8SJonathan Anderson return (0); 482af098ed8SJonathan Anderson } 4832609222aSPawel Jakub Dawidek 4842609222aSPawel Jakub Dawidek int 4852609222aSPawel Jakub Dawidek sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap) 4862609222aSPawel Jakub Dawidek { 4872609222aSPawel Jakub Dawidek struct filedesc *fdp; 4882609222aSPawel Jakub Dawidek uint32_t rights; 4892609222aSPawel Jakub Dawidek int fd; 4902609222aSPawel Jakub Dawidek 4912609222aSPawel Jakub Dawidek fd = uap->fd; 4922609222aSPawel Jakub Dawidek 4932609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 4942609222aSPawel Jakub Dawidek 4952609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 4962609222aSPawel Jakub Dawidek FILEDESC_SLOCK(fdp); 4972609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 4982609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 4992609222aSPawel Jakub Dawidek return (EBADF); 5002609222aSPawel Jakub Dawidek } 5012609222aSPawel Jakub Dawidek rights = fdp->fd_ofiles[fd].fde_fcntls; 5022609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 5032609222aSPawel Jakub Dawidek 5042609222aSPawel Jakub Dawidek return (copyout(&rights, uap->fcntlrightsp, sizeof(rights))); 5052609222aSPawel Jakub Dawidek } 5062609222aSPawel Jakub Dawidek 5072609222aSPawel Jakub Dawidek /* 5082609222aSPawel Jakub Dawidek * For backward compatibility. 5092609222aSPawel Jakub Dawidek */ 5102609222aSPawel Jakub Dawidek int 5112609222aSPawel Jakub Dawidek sys_cap_new(struct thread *td, struct cap_new_args *uap) 5122609222aSPawel Jakub Dawidek { 5132609222aSPawel Jakub Dawidek struct filedesc *fdp; 5142609222aSPawel Jakub Dawidek cap_rights_t rights; 5152609222aSPawel Jakub Dawidek register_t newfd; 5162609222aSPawel Jakub Dawidek int error, fd; 5172609222aSPawel Jakub Dawidek 5182609222aSPawel Jakub Dawidek fd = uap->fd; 5192609222aSPawel Jakub Dawidek rights = uap->rights; 5202609222aSPawel Jakub Dawidek 5212609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 5222609222aSPawel Jakub Dawidek AUDIT_ARG_RIGHTS(rights); 5232609222aSPawel Jakub Dawidek 5242609222aSPawel Jakub Dawidek if ((rights & ~CAP_ALL) != 0) 5252609222aSPawel Jakub Dawidek return (EINVAL); 5262609222aSPawel Jakub Dawidek 5272609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 5282609222aSPawel Jakub Dawidek FILEDESC_SLOCK(fdp); 5292609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 5302609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 5312609222aSPawel Jakub Dawidek return (EBADF); 5322609222aSPawel Jakub Dawidek } 5332609222aSPawel Jakub Dawidek error = _cap_check(cap_rights(fdp, fd), rights, CAPFAIL_INCREASE); 5342609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 5352609222aSPawel Jakub Dawidek if (error != 0) 536af098ed8SJonathan Anderson return (error); 5372609222aSPawel Jakub Dawidek 5382609222aSPawel Jakub Dawidek error = do_dup(td, 0, fd, 0, &newfd); 5392609222aSPawel Jakub Dawidek if (error != 0) 5402609222aSPawel Jakub Dawidek return (error); 5412609222aSPawel Jakub Dawidek 5422609222aSPawel Jakub Dawidek FILEDESC_XLOCK(fdp); 5432609222aSPawel Jakub Dawidek /* 5442609222aSPawel Jakub Dawidek * We don't really care about the race between checking capability 5452609222aSPawel Jakub Dawidek * rights for the source descriptor and now. If capability rights 5462609222aSPawel Jakub Dawidek * were ok at that earlier point, the process had this descriptor 5472609222aSPawel Jakub Dawidek * with those rights, so we don't increase them in security sense, 5482609222aSPawel Jakub Dawidek * the process might have done the cap_new(2) a bit earlier to get 5492609222aSPawel Jakub Dawidek * the same effect. 5502609222aSPawel Jakub Dawidek */ 5512609222aSPawel Jakub Dawidek fdp->fd_ofiles[newfd].fde_rights = rights; 5522609222aSPawel Jakub Dawidek if ((rights & CAP_IOCTL) == 0) { 553*92981fdfSPawel Jakub Dawidek free(fdp->fd_ofiles[newfd].fde_ioctls, M_FILECAPS); 5542609222aSPawel Jakub Dawidek fdp->fd_ofiles[newfd].fde_ioctls = NULL; 5552609222aSPawel Jakub Dawidek fdp->fd_ofiles[newfd].fde_nioctls = 0; 5562609222aSPawel Jakub Dawidek } 5572609222aSPawel Jakub Dawidek if ((rights & CAP_FCNTL) == 0) 5582609222aSPawel Jakub Dawidek fdp->fd_ofiles[newfd].fde_fcntls = 0; 5592609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 5602609222aSPawel Jakub Dawidek 5612609222aSPawel Jakub Dawidek td->td_retval[0] = newfd; 5622609222aSPawel Jakub Dawidek 563af098ed8SJonathan Anderson return (0); 564af098ed8SJonathan Anderson } 565af098ed8SJonathan Anderson 566af098ed8SJonathan Anderson #else /* !CAPABILITIES */ 567af098ed8SJonathan Anderson 568af098ed8SJonathan Anderson /* 569af098ed8SJonathan Anderson * Stub Capability functions for when options CAPABILITIES isn't compiled 570af098ed8SJonathan Anderson * into the kernel. 571af098ed8SJonathan Anderson */ 5722609222aSPawel Jakub Dawidek 5732609222aSPawel Jakub Dawidek int 5742609222aSPawel Jakub Dawidek sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap) 5752609222aSPawel Jakub Dawidek { 5762609222aSPawel Jakub Dawidek 5772609222aSPawel Jakub Dawidek return (ENOSYS); 5782609222aSPawel Jakub Dawidek } 5792609222aSPawel Jakub Dawidek 5802609222aSPawel Jakub Dawidek int 5812609222aSPawel Jakub Dawidek sys_cap_rights_get(struct thread *td, struct cap_rights_get_args *uap) 5822609222aSPawel Jakub Dawidek { 5832609222aSPawel Jakub Dawidek 5842609222aSPawel Jakub Dawidek return (ENOSYS); 5852609222aSPawel Jakub Dawidek } 5862609222aSPawel Jakub Dawidek 5872609222aSPawel Jakub Dawidek int 5882609222aSPawel Jakub Dawidek sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap) 5892609222aSPawel Jakub Dawidek { 5902609222aSPawel Jakub Dawidek 5912609222aSPawel Jakub Dawidek return (ENOSYS); 5922609222aSPawel Jakub Dawidek } 5932609222aSPawel Jakub Dawidek 5942609222aSPawel Jakub Dawidek int 5952609222aSPawel Jakub Dawidek sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap) 5962609222aSPawel Jakub Dawidek { 5972609222aSPawel Jakub Dawidek 5982609222aSPawel Jakub Dawidek return (ENOSYS); 5992609222aSPawel Jakub Dawidek } 6002609222aSPawel Jakub Dawidek 6012609222aSPawel Jakub Dawidek int 6022609222aSPawel Jakub Dawidek sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap) 6032609222aSPawel Jakub Dawidek { 6042609222aSPawel Jakub Dawidek 6052609222aSPawel Jakub Dawidek return (ENOSYS); 6062609222aSPawel Jakub Dawidek } 6072609222aSPawel Jakub Dawidek 6082609222aSPawel Jakub Dawidek int 6092609222aSPawel Jakub Dawidek sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap) 6102609222aSPawel Jakub Dawidek { 6112609222aSPawel Jakub Dawidek 6122609222aSPawel Jakub Dawidek return (ENOSYS); 6132609222aSPawel Jakub Dawidek } 6142609222aSPawel Jakub Dawidek 615af098ed8SJonathan Anderson int 6168451d0ddSKip Macy sys_cap_new(struct thread *td, struct cap_new_args *uap) 617cfb9df55SJonathan Anderson { 618cfb9df55SJonathan Anderson 619cfb9df55SJonathan Anderson return (ENOSYS); 620cfb9df55SJonathan Anderson } 621cfb9df55SJonathan Anderson 622af098ed8SJonathan Anderson #endif /* CAPABILITIES */ 623