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> 65*4a144410SRobert Watson #include <sys/capsicum.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> 730dac22d8SPawel Jakub Dawidek #include <sys/syscallsubr.h> 7496fcc75fSRobert Watson #include <sys/sysproto.h> 7596fcc75fSRobert Watson #include <sys/sysctl.h> 7696fcc75fSRobert Watson #include <sys/systm.h> 7796fcc75fSRobert Watson #include <sys/ucred.h> 78c601ad8eSDag-Erling Smørgrav #include <sys/uio.h> 79c601ad8eSDag-Erling Smørgrav #include <sys/ktrace.h> 8096fcc75fSRobert Watson 8196fcc75fSRobert Watson #include <security/audit/audit.h> 8296fcc75fSRobert Watson 8396fcc75fSRobert Watson #include <vm/uma.h> 8496fcc75fSRobert Watson #include <vm/vm.h> 8596fcc75fSRobert Watson 8624c1c3bfSJonathan Anderson #ifdef CAPABILITY_MODE 8796fcc75fSRobert Watson 88854d7b9fSRobert Watson FEATURE(security_capability_mode, "Capsicum Capability Mode"); 89d783bbd2SAlexander Leidinger 9096fcc75fSRobert Watson /* 9196fcc75fSRobert Watson * System call to enter capability mode for the process. 9296fcc75fSRobert Watson */ 9396fcc75fSRobert Watson int 948451d0ddSKip Macy sys_cap_enter(struct thread *td, struct cap_enter_args *uap) 9596fcc75fSRobert Watson { 9696fcc75fSRobert Watson struct ucred *newcred, *oldcred; 9796fcc75fSRobert Watson struct proc *p; 9896fcc75fSRobert Watson 9996fcc75fSRobert Watson if (IN_CAPABILITY_MODE(td)) 10096fcc75fSRobert Watson return (0); 10196fcc75fSRobert Watson 10296fcc75fSRobert Watson newcred = crget(); 10396fcc75fSRobert Watson p = td->td_proc; 10496fcc75fSRobert Watson PROC_LOCK(p); 10596fcc75fSRobert Watson oldcred = p->p_ucred; 10696fcc75fSRobert Watson crcopy(newcred, oldcred); 10796fcc75fSRobert Watson newcred->cr_flags |= CRED_FLAG_CAPMODE; 10896fcc75fSRobert Watson p->p_ucred = newcred; 10996fcc75fSRobert Watson PROC_UNLOCK(p); 11096fcc75fSRobert Watson crfree(oldcred); 11196fcc75fSRobert Watson return (0); 11296fcc75fSRobert Watson } 11396fcc75fSRobert Watson 11496fcc75fSRobert Watson /* 11596fcc75fSRobert Watson * System call to query whether the process is in capability mode. 11696fcc75fSRobert Watson */ 11796fcc75fSRobert Watson int 1188451d0ddSKip Macy sys_cap_getmode(struct thread *td, struct cap_getmode_args *uap) 11996fcc75fSRobert Watson { 12096fcc75fSRobert Watson u_int i; 12196fcc75fSRobert Watson 12211b0cfe3SPawel Jakub Dawidek i = IN_CAPABILITY_MODE(td) ? 1 : 0; 12396fcc75fSRobert Watson return (copyout(&i, uap->modep, sizeof(i))); 12496fcc75fSRobert Watson } 12596fcc75fSRobert Watson 12624c1c3bfSJonathan Anderson #else /* !CAPABILITY_MODE */ 12796fcc75fSRobert Watson 12896fcc75fSRobert Watson int 1298451d0ddSKip Macy sys_cap_enter(struct thread *td, struct cap_enter_args *uap) 13096fcc75fSRobert Watson { 13196fcc75fSRobert Watson 13296fcc75fSRobert Watson return (ENOSYS); 13396fcc75fSRobert Watson } 13496fcc75fSRobert Watson 13596fcc75fSRobert Watson int 1368451d0ddSKip Macy sys_cap_getmode(struct thread *td, struct cap_getmode_args *uap) 13796fcc75fSRobert Watson { 13896fcc75fSRobert Watson 13996fcc75fSRobert Watson return (ENOSYS); 14096fcc75fSRobert Watson } 14196fcc75fSRobert Watson 14224c1c3bfSJonathan Anderson #endif /* CAPABILITY_MODE */ 143af098ed8SJonathan Anderson 144af098ed8SJonathan Anderson #ifdef CAPABILITIES 145af098ed8SJonathan Anderson 146854d7b9fSRobert Watson FEATURE(security_capabilities, "Capsicum Capabilities"); 147854d7b9fSRobert Watson 14892981fdfSPawel Jakub Dawidek MALLOC_DECLARE(M_FILECAPS); 14992981fdfSPawel Jakub Dawidek 1502609222aSPawel Jakub Dawidek static inline int 1517008be5bSPawel Jakub Dawidek _cap_check(const cap_rights_t *havep, const cap_rights_t *needp, 1527008be5bSPawel Jakub Dawidek enum ktr_cap_fail_type type) 153745bae37SJonathan Anderson { 1547008be5bSPawel Jakub Dawidek int i; 155745bae37SJonathan Anderson 1567008be5bSPawel Jakub Dawidek for (i = 0; i < nitems(havep->cr_rights); i++) { 1577008be5bSPawel Jakub Dawidek if (!cap_rights_contains(havep, needp)) { 158c601ad8eSDag-Erling Smørgrav #ifdef KTRACE 159c601ad8eSDag-Erling Smørgrav if (KTRPOINT(curthread, KTR_CAPFAIL)) 1607008be5bSPawel Jakub Dawidek ktrcapfail(type, needp, havep); 161c601ad8eSDag-Erling Smørgrav #endif 162af098ed8SJonathan Anderson return (ENOTCAPABLE); 163c601ad8eSDag-Erling Smørgrav } 1647008be5bSPawel Jakub Dawidek } 165af098ed8SJonathan Anderson return (0); 166af098ed8SJonathan Anderson } 167af098ed8SJonathan Anderson 168af098ed8SJonathan Anderson /* 1692609222aSPawel Jakub Dawidek * Test whether a capability grants the requested rights. 1702609222aSPawel Jakub Dawidek */ 1712609222aSPawel Jakub Dawidek int 1727008be5bSPawel Jakub Dawidek cap_check(const cap_rights_t *havep, const cap_rights_t *needp) 1732609222aSPawel Jakub Dawidek { 1742609222aSPawel Jakub Dawidek 1757008be5bSPawel Jakub Dawidek return (_cap_check(havep, needp, CAPFAIL_NOTCAPABLE)); 1762609222aSPawel Jakub Dawidek } 1772609222aSPawel Jakub Dawidek 1782609222aSPawel Jakub Dawidek /* 1792609222aSPawel Jakub Dawidek * Convert capability rights into VM access flags. 1802609222aSPawel Jakub Dawidek */ 1812609222aSPawel Jakub Dawidek u_char 1827008be5bSPawel Jakub Dawidek cap_rights_to_vmprot(cap_rights_t *havep) 1832609222aSPawel Jakub Dawidek { 1842609222aSPawel Jakub Dawidek u_char maxprot; 1852609222aSPawel Jakub Dawidek 1862609222aSPawel Jakub Dawidek maxprot = VM_PROT_NONE; 1877008be5bSPawel Jakub Dawidek if (cap_rights_is_set(havep, CAP_MMAP_R)) 1882609222aSPawel Jakub Dawidek maxprot |= VM_PROT_READ; 1897008be5bSPawel Jakub Dawidek if (cap_rights_is_set(havep, CAP_MMAP_W)) 1902609222aSPawel Jakub Dawidek maxprot |= VM_PROT_WRITE; 1917008be5bSPawel Jakub Dawidek if (cap_rights_is_set(havep, CAP_MMAP_X)) 1922609222aSPawel Jakub Dawidek maxprot |= VM_PROT_EXECUTE; 1932609222aSPawel Jakub Dawidek 1942609222aSPawel Jakub Dawidek return (maxprot); 1952609222aSPawel Jakub Dawidek } 1962609222aSPawel Jakub Dawidek 1972609222aSPawel Jakub Dawidek /* 198745bae37SJonathan Anderson * Extract rights from a capability for monitoring purposes -- not for use in 199745bae37SJonathan Anderson * any other way, as we want to keep all capability permission evaluation in 200745bae37SJonathan Anderson * this one file. 201745bae37SJonathan Anderson */ 2027008be5bSPawel Jakub Dawidek cap_rights_t * 2032609222aSPawel Jakub Dawidek cap_rights(struct filedesc *fdp, int fd) 204745bae37SJonathan Anderson { 205745bae37SJonathan Anderson 2067008be5bSPawel Jakub Dawidek return (&fdp->fd_ofiles[fd].fde_rights); 207745bae37SJonathan Anderson } 208745bae37SJonathan Anderson 209745bae37SJonathan Anderson /* 2102609222aSPawel Jakub Dawidek * System call to limit rights of the given capability. 211cfb9df55SJonathan Anderson */ 212cfb9df55SJonathan Anderson int 2132609222aSPawel Jakub Dawidek sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap) 214cfb9df55SJonathan Anderson { 2152609222aSPawel Jakub Dawidek struct filedesc *fdp; 2162609222aSPawel Jakub Dawidek cap_rights_t rights; 2177008be5bSPawel Jakub Dawidek int error, fd, version; 2187008be5bSPawel Jakub Dawidek 2197008be5bSPawel Jakub Dawidek cap_rights_init(&rights); 2207008be5bSPawel Jakub Dawidek 2217008be5bSPawel Jakub Dawidek error = copyin(uap->rightsp, &rights, sizeof(rights.cr_rights[0])); 2227008be5bSPawel Jakub Dawidek if (error != 0) 2237008be5bSPawel Jakub Dawidek return (error); 2247008be5bSPawel Jakub Dawidek version = CAPVER(&rights); 2257008be5bSPawel Jakub Dawidek if (version != CAP_RIGHTS_VERSION_00) 2267008be5bSPawel Jakub Dawidek return (EINVAL); 2277008be5bSPawel Jakub Dawidek 2287008be5bSPawel Jakub Dawidek error = copyin(uap->rightsp, &rights, 2297008be5bSPawel Jakub Dawidek sizeof(rights.cr_rights[0]) * CAPARSIZE(&rights)); 2307008be5bSPawel Jakub Dawidek if (error != 0) 2317008be5bSPawel Jakub Dawidek return (error); 2327008be5bSPawel Jakub Dawidek /* Check for race. */ 2337008be5bSPawel Jakub Dawidek if (CAPVER(&rights) != version) 2347008be5bSPawel Jakub Dawidek return (EINVAL); 2357008be5bSPawel Jakub Dawidek 2367008be5bSPawel Jakub Dawidek if (!cap_rights_is_valid(&rights)) 2377008be5bSPawel Jakub Dawidek return (EINVAL); 2387008be5bSPawel Jakub Dawidek 2397008be5bSPawel Jakub Dawidek if (version != CAP_RIGHTS_VERSION) { 2407008be5bSPawel Jakub Dawidek rights.cr_rights[0] &= ~(0x3ULL << 62); 2417008be5bSPawel Jakub Dawidek rights.cr_rights[0] |= ((uint64_t)CAP_RIGHTS_VERSION << 62); 2427008be5bSPawel Jakub Dawidek } 2437008be5bSPawel Jakub Dawidek #ifdef KTRACE 2447008be5bSPawel Jakub Dawidek if (KTRPOINT(td, KTR_STRUCT)) 2457008be5bSPawel Jakub Dawidek ktrcaprights(&rights); 2467008be5bSPawel Jakub Dawidek #endif 2472609222aSPawel Jakub Dawidek 2482609222aSPawel Jakub Dawidek fd = uap->fd; 249cfb9df55SJonathan Anderson 250cfb9df55SJonathan Anderson AUDIT_ARG_FD(fd); 2517008be5bSPawel Jakub Dawidek AUDIT_ARG_RIGHTS(&rights); 2522609222aSPawel Jakub Dawidek 2532609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 2542609222aSPawel Jakub Dawidek FILEDESC_XLOCK(fdp); 2552609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 2562609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 2572609222aSPawel Jakub Dawidek return (EBADF); 2582609222aSPawel Jakub Dawidek } 2597008be5bSPawel Jakub Dawidek error = _cap_check(cap_rights(fdp, fd), &rights, CAPFAIL_INCREASE); 2602609222aSPawel Jakub Dawidek if (error == 0) { 2612609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_rights = rights; 2627008be5bSPawel Jakub Dawidek if (!cap_rights_is_set(&rights, CAP_IOCTL)) { 26392981fdfSPawel Jakub Dawidek free(fdp->fd_ofiles[fd].fde_ioctls, M_FILECAPS); 2642609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_ioctls = NULL; 2652609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_nioctls = 0; 2662609222aSPawel Jakub Dawidek } 2677008be5bSPawel Jakub Dawidek if (!cap_rights_is_set(&rights, CAP_FCNTL)) 2682609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_fcntls = 0; 2692609222aSPawel Jakub Dawidek } 2702609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 27162a9fc76SPeter Holm return (error); 272cfb9df55SJonathan Anderson } 273cfb9df55SJonathan Anderson 274cfb9df55SJonathan Anderson /* 275cfb9df55SJonathan Anderson * System call to query the rights mask associated with a capability. 276cfb9df55SJonathan Anderson */ 277cfb9df55SJonathan Anderson int 2787008be5bSPawel Jakub Dawidek sys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap) 279cfb9df55SJonathan Anderson { 2802609222aSPawel Jakub Dawidek struct filedesc *fdp; 2812609222aSPawel Jakub Dawidek cap_rights_t rights; 2827008be5bSPawel Jakub Dawidek int error, fd, i, n; 2837008be5bSPawel Jakub Dawidek 2847008be5bSPawel Jakub Dawidek if (uap->version != CAP_RIGHTS_VERSION_00) 2857008be5bSPawel Jakub Dawidek return (EINVAL); 286cfb9df55SJonathan Anderson 2872609222aSPawel Jakub Dawidek fd = uap->fd; 2882609222aSPawel Jakub Dawidek 2892609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 2902609222aSPawel Jakub Dawidek 2912609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 2922609222aSPawel Jakub Dawidek FILEDESC_SLOCK(fdp); 2932609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 2942609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 2952609222aSPawel Jakub Dawidek return (EBADF); 2962609222aSPawel Jakub Dawidek } 2977008be5bSPawel Jakub Dawidek rights = *cap_rights(fdp, fd); 2982609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 2997008be5bSPawel Jakub Dawidek n = uap->version + 2; 3007008be5bSPawel Jakub Dawidek if (uap->version != CAPVER(&rights)) { 3017008be5bSPawel Jakub Dawidek /* 3027008be5bSPawel Jakub Dawidek * For older versions we need to check if the descriptor 3037008be5bSPawel Jakub Dawidek * doesn't contain rights not understood by the caller. 3047008be5bSPawel Jakub Dawidek * If it does, we have to return an error. 3057008be5bSPawel Jakub Dawidek */ 3067008be5bSPawel Jakub Dawidek for (i = n; i < CAPARSIZE(&rights); i++) { 3077008be5bSPawel Jakub Dawidek if ((rights.cr_rights[i] & ~(0x7FULL << 57)) != 0) 3087008be5bSPawel Jakub Dawidek return (EINVAL); 3097008be5bSPawel Jakub Dawidek } 3107008be5bSPawel Jakub Dawidek } 3117008be5bSPawel Jakub Dawidek error = copyout(&rights, uap->rightsp, sizeof(rights.cr_rights[0]) * n); 3127008be5bSPawel Jakub Dawidek #ifdef KTRACE 3137008be5bSPawel Jakub Dawidek if (error == 0 && KTRPOINT(td, KTR_STRUCT)) 3147008be5bSPawel Jakub Dawidek ktrcaprights(&rights); 3157008be5bSPawel Jakub Dawidek #endif 3167008be5bSPawel Jakub Dawidek return (error); 317cfb9df55SJonathan Anderson } 318cfb9df55SJonathan Anderson 319cfb9df55SJonathan Anderson /* 3202609222aSPawel Jakub Dawidek * Test whether a capability grants the given ioctl command. 3212609222aSPawel Jakub Dawidek * If descriptor doesn't have CAP_IOCTL, then ioctls list is empty and 3222609222aSPawel Jakub Dawidek * ENOTCAPABLE will be returned. 323745bae37SJonathan Anderson */ 324745bae37SJonathan Anderson int 3252609222aSPawel Jakub Dawidek cap_ioctl_check(struct filedesc *fdp, int fd, u_long cmd) 326745bae37SJonathan Anderson { 3272609222aSPawel Jakub Dawidek u_long *cmds; 3282609222aSPawel Jakub Dawidek ssize_t ncmds; 3292609222aSPawel Jakub Dawidek long i; 330745bae37SJonathan Anderson 3312609222aSPawel Jakub Dawidek FILEDESC_LOCK_ASSERT(fdp); 3322609222aSPawel Jakub Dawidek KASSERT(fd >= 0 && fd < fdp->fd_nfiles, 3332609222aSPawel Jakub Dawidek ("%s: invalid fd=%d", __func__, fd)); 334745bae37SJonathan Anderson 3352609222aSPawel Jakub Dawidek ncmds = fdp->fd_ofiles[fd].fde_nioctls; 3362609222aSPawel Jakub Dawidek if (ncmds == -1) 3372609222aSPawel Jakub Dawidek return (0); 3382609222aSPawel Jakub Dawidek 3392609222aSPawel Jakub Dawidek cmds = fdp->fd_ofiles[fd].fde_ioctls; 3402609222aSPawel Jakub Dawidek for (i = 0; i < ncmds; i++) { 3412609222aSPawel Jakub Dawidek if (cmds[i] == cmd) 3422609222aSPawel Jakub Dawidek return (0); 3432609222aSPawel Jakub Dawidek } 3442609222aSPawel Jakub Dawidek 345745bae37SJonathan Anderson return (ENOTCAPABLE); 346745bae37SJonathan Anderson } 3472609222aSPawel Jakub Dawidek 3482609222aSPawel Jakub Dawidek /* 3492609222aSPawel Jakub Dawidek * Check if the current ioctls list can be replaced by the new one. 3502609222aSPawel Jakub Dawidek */ 3512609222aSPawel Jakub Dawidek static int 3522609222aSPawel Jakub Dawidek cap_ioctl_limit_check(struct filedesc *fdp, int fd, const u_long *cmds, 3532609222aSPawel Jakub Dawidek size_t ncmds) 3542609222aSPawel Jakub Dawidek { 3552609222aSPawel Jakub Dawidek u_long *ocmds; 3562609222aSPawel Jakub Dawidek ssize_t oncmds; 3572609222aSPawel Jakub Dawidek u_long i; 3582609222aSPawel Jakub Dawidek long j; 3592609222aSPawel Jakub Dawidek 3602609222aSPawel Jakub Dawidek oncmds = fdp->fd_ofiles[fd].fde_nioctls; 3612609222aSPawel Jakub Dawidek if (oncmds == -1) 3622609222aSPawel Jakub Dawidek return (0); 3632609222aSPawel Jakub Dawidek if (oncmds < (ssize_t)ncmds) 3642609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 3652609222aSPawel Jakub Dawidek 3662609222aSPawel Jakub Dawidek ocmds = fdp->fd_ofiles[fd].fde_ioctls; 3672609222aSPawel Jakub Dawidek for (i = 0; i < ncmds; i++) { 3682609222aSPawel Jakub Dawidek for (j = 0; j < oncmds; j++) { 3692609222aSPawel Jakub Dawidek if (cmds[i] == ocmds[j]) 3702609222aSPawel Jakub Dawidek break; 3712609222aSPawel Jakub Dawidek } 3722609222aSPawel Jakub Dawidek if (j == oncmds) 3732609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 3742609222aSPawel Jakub Dawidek } 3752609222aSPawel Jakub Dawidek 3762609222aSPawel Jakub Dawidek return (0); 3772609222aSPawel Jakub Dawidek } 3782609222aSPawel Jakub Dawidek 3792609222aSPawel Jakub Dawidek int 3800dac22d8SPawel Jakub Dawidek kern_cap_ioctls_limit(struct thread *td, int fd, u_long *cmds, size_t ncmds) 3812609222aSPawel Jakub Dawidek { 3822609222aSPawel Jakub Dawidek struct filedesc *fdp; 3830dac22d8SPawel Jakub Dawidek u_long *ocmds; 3840dac22d8SPawel Jakub Dawidek int error; 3852609222aSPawel Jakub Dawidek 3862609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 3872609222aSPawel Jakub Dawidek 3882609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 3892609222aSPawel Jakub Dawidek FILEDESC_XLOCK(fdp); 3902609222aSPawel Jakub Dawidek 3912609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 3922609222aSPawel Jakub Dawidek error = EBADF; 3932609222aSPawel Jakub Dawidek goto out; 3942609222aSPawel Jakub Dawidek } 3952609222aSPawel Jakub Dawidek 3962609222aSPawel Jakub Dawidek error = cap_ioctl_limit_check(fdp, fd, cmds, ncmds); 3972609222aSPawel Jakub Dawidek if (error != 0) 3982609222aSPawel Jakub Dawidek goto out; 3992609222aSPawel Jakub Dawidek 4002609222aSPawel Jakub Dawidek ocmds = fdp->fd_ofiles[fd].fde_ioctls; 4012609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_ioctls = cmds; 4022609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_nioctls = ncmds; 4032609222aSPawel Jakub Dawidek 4042609222aSPawel Jakub Dawidek cmds = ocmds; 4052609222aSPawel Jakub Dawidek error = 0; 4062609222aSPawel Jakub Dawidek out: 4072609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 40892981fdfSPawel Jakub Dawidek free(cmds, M_FILECAPS); 4092609222aSPawel Jakub Dawidek return (error); 4102609222aSPawel Jakub Dawidek } 4112609222aSPawel Jakub Dawidek 4122609222aSPawel Jakub Dawidek int 4130dac22d8SPawel Jakub Dawidek sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap) 4140dac22d8SPawel Jakub Dawidek { 4150dac22d8SPawel Jakub Dawidek u_long *cmds; 4160dac22d8SPawel Jakub Dawidek size_t ncmds; 4170dac22d8SPawel Jakub Dawidek int error; 4180dac22d8SPawel Jakub Dawidek 4190dac22d8SPawel Jakub Dawidek ncmds = uap->ncmds; 4200dac22d8SPawel Jakub Dawidek 4210dac22d8SPawel Jakub Dawidek if (ncmds > 256) /* XXX: Is 256 sane? */ 4220dac22d8SPawel Jakub Dawidek return (EINVAL); 4230dac22d8SPawel Jakub Dawidek 4240dac22d8SPawel Jakub Dawidek if (ncmds == 0) { 4250dac22d8SPawel Jakub Dawidek cmds = NULL; 4260dac22d8SPawel Jakub Dawidek } else { 4270dac22d8SPawel Jakub Dawidek cmds = malloc(sizeof(cmds[0]) * ncmds, M_FILECAPS, M_WAITOK); 4280dac22d8SPawel Jakub Dawidek error = copyin(uap->cmds, cmds, sizeof(cmds[0]) * ncmds); 4290dac22d8SPawel Jakub Dawidek if (error != 0) { 4300dac22d8SPawel Jakub Dawidek free(cmds, M_FILECAPS); 4310dac22d8SPawel Jakub Dawidek return (error); 4320dac22d8SPawel Jakub Dawidek } 4330dac22d8SPawel Jakub Dawidek } 4340dac22d8SPawel Jakub Dawidek 4350dac22d8SPawel Jakub Dawidek return (kern_cap_ioctls_limit(td, uap->fd, cmds, ncmds)); 4360dac22d8SPawel Jakub Dawidek } 4370dac22d8SPawel Jakub Dawidek 4380dac22d8SPawel Jakub Dawidek int 4392609222aSPawel Jakub Dawidek sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap) 4402609222aSPawel Jakub Dawidek { 4412609222aSPawel Jakub Dawidek struct filedesc *fdp; 4422609222aSPawel Jakub Dawidek struct filedescent *fdep; 4432609222aSPawel Jakub Dawidek u_long *cmds; 4442609222aSPawel Jakub Dawidek size_t maxcmds; 4452609222aSPawel Jakub Dawidek int error, fd; 4462609222aSPawel Jakub Dawidek 4472609222aSPawel Jakub Dawidek fd = uap->fd; 4482609222aSPawel Jakub Dawidek cmds = uap->cmds; 4492609222aSPawel Jakub Dawidek maxcmds = uap->maxcmds; 4502609222aSPawel Jakub Dawidek 4512609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 4522609222aSPawel Jakub Dawidek 4532609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 4542609222aSPawel Jakub Dawidek FILEDESC_SLOCK(fdp); 4552609222aSPawel Jakub Dawidek 4562609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 4572609222aSPawel Jakub Dawidek error = EBADF; 4582609222aSPawel Jakub Dawidek goto out; 459e141be6fSDag-Erling Smørgrav } 460745bae37SJonathan Anderson 461745bae37SJonathan Anderson /* 4622609222aSPawel Jakub Dawidek * If all ioctls are allowed (fde_nioctls == -1 && fde_ioctls == NULL) 4632609222aSPawel Jakub Dawidek * the only sane thing we can do is to not populate the given array and 4642609222aSPawel Jakub Dawidek * return CAP_IOCTLS_ALL. 465745bae37SJonathan Anderson */ 466745bae37SJonathan Anderson 4672609222aSPawel Jakub Dawidek fdep = &fdp->fd_ofiles[fd]; 4682609222aSPawel Jakub Dawidek if (cmds != NULL && fdep->fde_ioctls != NULL) { 4692609222aSPawel Jakub Dawidek error = copyout(fdep->fde_ioctls, cmds, 4702609222aSPawel Jakub Dawidek sizeof(cmds[0]) * MIN(fdep->fde_nioctls, maxcmds)); 4712609222aSPawel Jakub Dawidek if (error != 0) 4722609222aSPawel Jakub Dawidek goto out; 4732609222aSPawel Jakub Dawidek } 4742609222aSPawel Jakub Dawidek if (fdep->fde_nioctls == -1) 4752609222aSPawel Jakub Dawidek td->td_retval[0] = CAP_IOCTLS_ALL; 476745bae37SJonathan Anderson else 4772609222aSPawel Jakub Dawidek td->td_retval[0] = fdep->fde_nioctls; 478745bae37SJonathan Anderson 4792609222aSPawel Jakub Dawidek error = 0; 4802609222aSPawel Jakub Dawidek out: 4812609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 482af098ed8SJonathan Anderson return (error); 483af098ed8SJonathan Anderson } 484af098ed8SJonathan Anderson 485af098ed8SJonathan Anderson /* 4862609222aSPawel Jakub Dawidek * Test whether a capability grants the given fcntl command. 487af098ed8SJonathan Anderson */ 488af098ed8SJonathan Anderson int 4892609222aSPawel Jakub Dawidek cap_fcntl_check(struct filedesc *fdp, int fd, int cmd) 490af098ed8SJonathan Anderson { 4912609222aSPawel Jakub Dawidek uint32_t fcntlcap; 492af098ed8SJonathan Anderson 4932609222aSPawel Jakub Dawidek KASSERT(fd >= 0 && fd < fdp->fd_nfiles, 4942609222aSPawel Jakub Dawidek ("%s: invalid fd=%d", __func__, fd)); 4952609222aSPawel Jakub Dawidek 4962609222aSPawel Jakub Dawidek fcntlcap = (1 << cmd); 4972609222aSPawel Jakub Dawidek KASSERT((CAP_FCNTL_ALL & fcntlcap) != 0, 4982609222aSPawel Jakub Dawidek ("Unsupported fcntl=%d.", cmd)); 4992609222aSPawel Jakub Dawidek 5002609222aSPawel Jakub Dawidek if ((fdp->fd_ofiles[fd].fde_fcntls & fcntlcap) != 0) 5012609222aSPawel Jakub Dawidek return (0); 5022609222aSPawel Jakub Dawidek 5032609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 5042609222aSPawel Jakub Dawidek } 5052609222aSPawel Jakub Dawidek 5062609222aSPawel Jakub Dawidek int 5072609222aSPawel Jakub Dawidek sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap) 5082609222aSPawel Jakub Dawidek { 5092609222aSPawel Jakub Dawidek struct filedesc *fdp; 5102609222aSPawel Jakub Dawidek uint32_t fcntlrights; 5112609222aSPawel Jakub Dawidek int fd; 5122609222aSPawel Jakub Dawidek 5132609222aSPawel Jakub Dawidek fd = uap->fd; 5142609222aSPawel Jakub Dawidek fcntlrights = uap->fcntlrights; 5152609222aSPawel Jakub Dawidek 5162609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 5172609222aSPawel Jakub Dawidek AUDIT_ARG_FCNTL_RIGHTS(fcntlrights); 5182609222aSPawel Jakub Dawidek 5192609222aSPawel Jakub Dawidek if ((fcntlrights & ~CAP_FCNTL_ALL) != 0) 5202609222aSPawel Jakub Dawidek return (EINVAL); 5212609222aSPawel Jakub Dawidek 5222609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 5232609222aSPawel Jakub Dawidek FILEDESC_XLOCK(fdp); 5242609222aSPawel Jakub Dawidek 5252609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 5262609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 5272609222aSPawel Jakub Dawidek return (EBADF); 5282609222aSPawel Jakub Dawidek } 5292609222aSPawel Jakub Dawidek 5302609222aSPawel Jakub Dawidek if ((fcntlrights & ~fdp->fd_ofiles[fd].fde_fcntls) != 0) { 5312609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 5322609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 5332609222aSPawel Jakub Dawidek } 5342609222aSPawel Jakub Dawidek 5352609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_fcntls = fcntlrights; 5362609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 5372609222aSPawel Jakub Dawidek 538af098ed8SJonathan Anderson return (0); 539af098ed8SJonathan Anderson } 5402609222aSPawel Jakub Dawidek 5412609222aSPawel Jakub Dawidek int 5422609222aSPawel Jakub Dawidek sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap) 5432609222aSPawel Jakub Dawidek { 5442609222aSPawel Jakub Dawidek struct filedesc *fdp; 5452609222aSPawel Jakub Dawidek uint32_t rights; 5462609222aSPawel Jakub Dawidek int fd; 5472609222aSPawel Jakub Dawidek 5482609222aSPawel Jakub Dawidek fd = uap->fd; 5492609222aSPawel Jakub Dawidek 5502609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 5512609222aSPawel Jakub Dawidek 5522609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 5532609222aSPawel Jakub Dawidek FILEDESC_SLOCK(fdp); 5542609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 5552609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 5562609222aSPawel Jakub Dawidek return (EBADF); 5572609222aSPawel Jakub Dawidek } 5582609222aSPawel Jakub Dawidek rights = fdp->fd_ofiles[fd].fde_fcntls; 5592609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 5602609222aSPawel Jakub Dawidek 5612609222aSPawel Jakub Dawidek return (copyout(&rights, uap->fcntlrightsp, sizeof(rights))); 5622609222aSPawel Jakub Dawidek } 5632609222aSPawel Jakub Dawidek 564af098ed8SJonathan Anderson #else /* !CAPABILITIES */ 565af098ed8SJonathan Anderson 566af098ed8SJonathan Anderson /* 567af098ed8SJonathan Anderson * Stub Capability functions for when options CAPABILITIES isn't compiled 568af098ed8SJonathan Anderson * into the kernel. 569af098ed8SJonathan Anderson */ 5702609222aSPawel Jakub Dawidek 5712609222aSPawel Jakub Dawidek int 5722609222aSPawel Jakub Dawidek sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap) 5732609222aSPawel Jakub Dawidek { 5742609222aSPawel Jakub Dawidek 5752609222aSPawel Jakub Dawidek return (ENOSYS); 5762609222aSPawel Jakub Dawidek } 5772609222aSPawel Jakub Dawidek 5782609222aSPawel Jakub Dawidek int 57988eb5488SSean Bruno sys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap) 5802609222aSPawel Jakub Dawidek { 5812609222aSPawel Jakub Dawidek 5822609222aSPawel Jakub Dawidek return (ENOSYS); 5832609222aSPawel Jakub Dawidek } 5842609222aSPawel Jakub Dawidek 5852609222aSPawel Jakub Dawidek int 5862609222aSPawel Jakub Dawidek sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap) 5872609222aSPawel Jakub Dawidek { 5882609222aSPawel Jakub Dawidek 5892609222aSPawel Jakub Dawidek return (ENOSYS); 5902609222aSPawel Jakub Dawidek } 5912609222aSPawel Jakub Dawidek 5922609222aSPawel Jakub Dawidek int 5932609222aSPawel Jakub Dawidek sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap) 5942609222aSPawel Jakub Dawidek { 5952609222aSPawel Jakub Dawidek 5962609222aSPawel Jakub Dawidek return (ENOSYS); 5972609222aSPawel Jakub Dawidek } 5982609222aSPawel Jakub Dawidek 5992609222aSPawel Jakub Dawidek int 6002609222aSPawel Jakub Dawidek sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap) 6012609222aSPawel Jakub Dawidek { 6022609222aSPawel Jakub Dawidek 6032609222aSPawel Jakub Dawidek return (ENOSYS); 6042609222aSPawel Jakub Dawidek } 6052609222aSPawel Jakub Dawidek 6062609222aSPawel Jakub Dawidek int 6072609222aSPawel Jakub Dawidek sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap) 6082609222aSPawel Jakub Dawidek { 6092609222aSPawel Jakub Dawidek 6102609222aSPawel Jakub Dawidek return (ENOSYS); 6112609222aSPawel Jakub Dawidek } 6122609222aSPawel Jakub Dawidek 613af098ed8SJonathan Anderson #endif /* CAPABILITIES */ 614