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> 654a144410SRobert 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); 105bbd685e3SMark Johnston oldcred = crcopysafe(p, newcred); 10696fcc75fSRobert Watson newcred->cr_flags |= CRED_FLAG_CAPMODE; 107daf63fd2SMateusz Guzik proc_set_cred(p, 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 14792981fdfSPawel Jakub Dawidek MALLOC_DECLARE(M_FILECAPS); 14892981fdfSPawel Jakub Dawidek 1492609222aSPawel Jakub Dawidek static inline int 1507008be5bSPawel Jakub Dawidek _cap_check(const cap_rights_t *havep, const cap_rights_t *needp, 1517008be5bSPawel Jakub Dawidek enum ktr_cap_fail_type type) 152745bae37SJonathan Anderson { 1537008be5bSPawel Jakub Dawidek int i; 154745bae37SJonathan Anderson 1557008be5bSPawel Jakub Dawidek for (i = 0; i < nitems(havep->cr_rights); i++) { 1567008be5bSPawel Jakub Dawidek if (!cap_rights_contains(havep, needp)) { 157c601ad8eSDag-Erling Smørgrav #ifdef KTRACE 158c601ad8eSDag-Erling Smørgrav if (KTRPOINT(curthread, KTR_CAPFAIL)) 1597008be5bSPawel Jakub Dawidek ktrcapfail(type, needp, havep); 160c601ad8eSDag-Erling Smørgrav #endif 161af098ed8SJonathan Anderson return (ENOTCAPABLE); 162c601ad8eSDag-Erling Smørgrav } 1637008be5bSPawel Jakub Dawidek } 164af098ed8SJonathan Anderson return (0); 165af098ed8SJonathan Anderson } 166af098ed8SJonathan Anderson 167af098ed8SJonathan Anderson /* 1682609222aSPawel Jakub Dawidek * Test whether a capability grants the requested rights. 1692609222aSPawel Jakub Dawidek */ 1702609222aSPawel Jakub Dawidek int 1717008be5bSPawel Jakub Dawidek cap_check(const cap_rights_t *havep, const cap_rights_t *needp) 1722609222aSPawel Jakub Dawidek { 1732609222aSPawel Jakub Dawidek 1747008be5bSPawel Jakub Dawidek return (_cap_check(havep, needp, CAPFAIL_NOTCAPABLE)); 1752609222aSPawel Jakub Dawidek } 1762609222aSPawel Jakub Dawidek 1772609222aSPawel Jakub Dawidek /* 1782609222aSPawel Jakub Dawidek * Convert capability rights into VM access flags. 1792609222aSPawel Jakub Dawidek */ 1802609222aSPawel Jakub Dawidek u_char 1817008be5bSPawel Jakub Dawidek cap_rights_to_vmprot(cap_rights_t *havep) 1822609222aSPawel Jakub Dawidek { 1832609222aSPawel Jakub Dawidek u_char maxprot; 1842609222aSPawel Jakub Dawidek 1852609222aSPawel Jakub Dawidek maxprot = VM_PROT_NONE; 1867008be5bSPawel Jakub Dawidek if (cap_rights_is_set(havep, CAP_MMAP_R)) 1872609222aSPawel Jakub Dawidek maxprot |= VM_PROT_READ; 1887008be5bSPawel Jakub Dawidek if (cap_rights_is_set(havep, CAP_MMAP_W)) 1892609222aSPawel Jakub Dawidek maxprot |= VM_PROT_WRITE; 1907008be5bSPawel Jakub Dawidek if (cap_rights_is_set(havep, CAP_MMAP_X)) 1912609222aSPawel Jakub Dawidek maxprot |= VM_PROT_EXECUTE; 1922609222aSPawel Jakub Dawidek 1932609222aSPawel Jakub Dawidek return (maxprot); 1942609222aSPawel Jakub Dawidek } 1952609222aSPawel Jakub Dawidek 1962609222aSPawel Jakub Dawidek /* 197745bae37SJonathan Anderson * Extract rights from a capability for monitoring purposes -- not for use in 198745bae37SJonathan Anderson * any other way, as we want to keep all capability permission evaluation in 199745bae37SJonathan Anderson * this one file. 200745bae37SJonathan Anderson */ 201a1bf8115SMateusz Guzik 202a1bf8115SMateusz Guzik cap_rights_t * 203a1bf8115SMateusz Guzik cap_rights_fde(struct filedescent *fde) 204a1bf8115SMateusz Guzik { 205a1bf8115SMateusz Guzik 206a1bf8115SMateusz Guzik return (&fde->fde_rights); 207a1bf8115SMateusz Guzik } 208a1bf8115SMateusz Guzik 2097008be5bSPawel Jakub Dawidek cap_rights_t * 2102609222aSPawel Jakub Dawidek cap_rights(struct filedesc *fdp, int fd) 211745bae37SJonathan Anderson { 212745bae37SJonathan Anderson 213a1bf8115SMateusz Guzik return (cap_rights_fde(&fdp->fd_ofiles[fd])); 214745bae37SJonathan Anderson } 215745bae37SJonathan Anderson 216*aa04a06dSEd Schouten int 217*aa04a06dSEd Schouten kern_cap_rights_limit(struct thread *td, int fd, cap_rights_t *rights) 218*aa04a06dSEd Schouten { 219*aa04a06dSEd Schouten struct filedesc *fdp; 220*aa04a06dSEd Schouten int error; 221*aa04a06dSEd Schouten 222*aa04a06dSEd Schouten fdp = td->td_proc->p_fd; 223*aa04a06dSEd Schouten FILEDESC_XLOCK(fdp); 224*aa04a06dSEd Schouten if (fget_locked(fdp, fd) == NULL) { 225*aa04a06dSEd Schouten FILEDESC_XUNLOCK(fdp); 226*aa04a06dSEd Schouten return (EBADF); 227*aa04a06dSEd Schouten } 228*aa04a06dSEd Schouten error = _cap_check(cap_rights(fdp, fd), rights, CAPFAIL_INCREASE); 229*aa04a06dSEd Schouten if (error == 0) { 230*aa04a06dSEd Schouten fdp->fd_ofiles[fd].fde_rights = *rights; 231*aa04a06dSEd Schouten if (!cap_rights_is_set(rights, CAP_IOCTL)) { 232*aa04a06dSEd Schouten free(fdp->fd_ofiles[fd].fde_ioctls, M_FILECAPS); 233*aa04a06dSEd Schouten fdp->fd_ofiles[fd].fde_ioctls = NULL; 234*aa04a06dSEd Schouten fdp->fd_ofiles[fd].fde_nioctls = 0; 235*aa04a06dSEd Schouten } 236*aa04a06dSEd Schouten if (!cap_rights_is_set(rights, CAP_FCNTL)) 237*aa04a06dSEd Schouten fdp->fd_ofiles[fd].fde_fcntls = 0; 238*aa04a06dSEd Schouten } 239*aa04a06dSEd Schouten FILEDESC_XUNLOCK(fdp); 240*aa04a06dSEd Schouten return (error); 241*aa04a06dSEd Schouten } 242*aa04a06dSEd Schouten 243745bae37SJonathan Anderson /* 2442609222aSPawel Jakub Dawidek * System call to limit rights of the given capability. 245cfb9df55SJonathan Anderson */ 246cfb9df55SJonathan Anderson int 2472609222aSPawel Jakub Dawidek sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap) 248cfb9df55SJonathan Anderson { 2492609222aSPawel Jakub Dawidek cap_rights_t rights; 250*aa04a06dSEd Schouten int error, version; 2517008be5bSPawel Jakub Dawidek 2527008be5bSPawel Jakub Dawidek cap_rights_init(&rights); 2537008be5bSPawel Jakub Dawidek 2547008be5bSPawel Jakub Dawidek error = copyin(uap->rightsp, &rights, sizeof(rights.cr_rights[0])); 2557008be5bSPawel Jakub Dawidek if (error != 0) 2567008be5bSPawel Jakub Dawidek return (error); 2577008be5bSPawel Jakub Dawidek version = CAPVER(&rights); 2587008be5bSPawel Jakub Dawidek if (version != CAP_RIGHTS_VERSION_00) 2597008be5bSPawel Jakub Dawidek return (EINVAL); 2607008be5bSPawel Jakub Dawidek 2617008be5bSPawel Jakub Dawidek error = copyin(uap->rightsp, &rights, 2627008be5bSPawel Jakub Dawidek sizeof(rights.cr_rights[0]) * CAPARSIZE(&rights)); 2637008be5bSPawel Jakub Dawidek if (error != 0) 2647008be5bSPawel Jakub Dawidek return (error); 2657008be5bSPawel Jakub Dawidek /* Check for race. */ 2667008be5bSPawel Jakub Dawidek if (CAPVER(&rights) != version) 2677008be5bSPawel Jakub Dawidek return (EINVAL); 2687008be5bSPawel Jakub Dawidek 2697008be5bSPawel Jakub Dawidek if (!cap_rights_is_valid(&rights)) 2707008be5bSPawel Jakub Dawidek return (EINVAL); 2717008be5bSPawel Jakub Dawidek 2727008be5bSPawel Jakub Dawidek if (version != CAP_RIGHTS_VERSION) { 2737008be5bSPawel Jakub Dawidek rights.cr_rights[0] &= ~(0x3ULL << 62); 2747008be5bSPawel Jakub Dawidek rights.cr_rights[0] |= ((uint64_t)CAP_RIGHTS_VERSION << 62); 2757008be5bSPawel Jakub Dawidek } 2767008be5bSPawel Jakub Dawidek #ifdef KTRACE 2777008be5bSPawel Jakub Dawidek if (KTRPOINT(td, KTR_STRUCT)) 2787008be5bSPawel Jakub Dawidek ktrcaprights(&rights); 2797008be5bSPawel Jakub Dawidek #endif 2802609222aSPawel Jakub Dawidek 281*aa04a06dSEd Schouten AUDIT_ARG_FD(uap->fd); 2827008be5bSPawel Jakub Dawidek AUDIT_ARG_RIGHTS(&rights); 283*aa04a06dSEd Schouten return (kern_cap_rights_limit(td, uap->fd, &rights)); 284cfb9df55SJonathan Anderson } 285cfb9df55SJonathan Anderson 286cfb9df55SJonathan Anderson /* 287cfb9df55SJonathan Anderson * System call to query the rights mask associated with a capability. 288cfb9df55SJonathan Anderson */ 289cfb9df55SJonathan Anderson int 2907008be5bSPawel Jakub Dawidek sys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap) 291cfb9df55SJonathan Anderson { 2922609222aSPawel Jakub Dawidek struct filedesc *fdp; 2932609222aSPawel Jakub Dawidek cap_rights_t rights; 2947008be5bSPawel Jakub Dawidek int error, fd, i, n; 2957008be5bSPawel Jakub Dawidek 2967008be5bSPawel Jakub Dawidek if (uap->version != CAP_RIGHTS_VERSION_00) 2977008be5bSPawel Jakub Dawidek return (EINVAL); 298cfb9df55SJonathan Anderson 2992609222aSPawel Jakub Dawidek fd = uap->fd; 3002609222aSPawel Jakub Dawidek 3012609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 3022609222aSPawel Jakub Dawidek 3032609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 3042609222aSPawel Jakub Dawidek FILEDESC_SLOCK(fdp); 3052609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 3062609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 3072609222aSPawel Jakub Dawidek return (EBADF); 3082609222aSPawel Jakub Dawidek } 3097008be5bSPawel Jakub Dawidek rights = *cap_rights(fdp, fd); 3102609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 3117008be5bSPawel Jakub Dawidek n = uap->version + 2; 3127008be5bSPawel Jakub Dawidek if (uap->version != CAPVER(&rights)) { 3137008be5bSPawel Jakub Dawidek /* 3147008be5bSPawel Jakub Dawidek * For older versions we need to check if the descriptor 3157008be5bSPawel Jakub Dawidek * doesn't contain rights not understood by the caller. 3167008be5bSPawel Jakub Dawidek * If it does, we have to return an error. 3177008be5bSPawel Jakub Dawidek */ 3187008be5bSPawel Jakub Dawidek for (i = n; i < CAPARSIZE(&rights); i++) { 3197008be5bSPawel Jakub Dawidek if ((rights.cr_rights[i] & ~(0x7FULL << 57)) != 0) 3207008be5bSPawel Jakub Dawidek return (EINVAL); 3217008be5bSPawel Jakub Dawidek } 3227008be5bSPawel Jakub Dawidek } 3237008be5bSPawel Jakub Dawidek error = copyout(&rights, uap->rightsp, sizeof(rights.cr_rights[0]) * n); 3247008be5bSPawel Jakub Dawidek #ifdef KTRACE 3257008be5bSPawel Jakub Dawidek if (error == 0 && KTRPOINT(td, KTR_STRUCT)) 3267008be5bSPawel Jakub Dawidek ktrcaprights(&rights); 3277008be5bSPawel Jakub Dawidek #endif 3287008be5bSPawel Jakub Dawidek return (error); 329cfb9df55SJonathan Anderson } 330cfb9df55SJonathan Anderson 331cfb9df55SJonathan Anderson /* 3322609222aSPawel Jakub Dawidek * Test whether a capability grants the given ioctl command. 3332609222aSPawel Jakub Dawidek * If descriptor doesn't have CAP_IOCTL, then ioctls list is empty and 3342609222aSPawel Jakub Dawidek * ENOTCAPABLE will be returned. 335745bae37SJonathan Anderson */ 336745bae37SJonathan Anderson int 3372609222aSPawel Jakub Dawidek cap_ioctl_check(struct filedesc *fdp, int fd, u_long cmd) 338745bae37SJonathan Anderson { 3392609222aSPawel Jakub Dawidek u_long *cmds; 3402609222aSPawel Jakub Dawidek ssize_t ncmds; 3412609222aSPawel Jakub Dawidek long i; 342745bae37SJonathan Anderson 3432609222aSPawel Jakub Dawidek FILEDESC_LOCK_ASSERT(fdp); 3442609222aSPawel Jakub Dawidek KASSERT(fd >= 0 && fd < fdp->fd_nfiles, 3452609222aSPawel Jakub Dawidek ("%s: invalid fd=%d", __func__, fd)); 346745bae37SJonathan Anderson 3472609222aSPawel Jakub Dawidek ncmds = fdp->fd_ofiles[fd].fde_nioctls; 3482609222aSPawel Jakub Dawidek if (ncmds == -1) 3492609222aSPawel Jakub Dawidek return (0); 3502609222aSPawel Jakub Dawidek 3512609222aSPawel Jakub Dawidek cmds = fdp->fd_ofiles[fd].fde_ioctls; 3522609222aSPawel Jakub Dawidek for (i = 0; i < ncmds; i++) { 3532609222aSPawel Jakub Dawidek if (cmds[i] == cmd) 3542609222aSPawel Jakub Dawidek return (0); 3552609222aSPawel Jakub Dawidek } 3562609222aSPawel Jakub Dawidek 357745bae37SJonathan Anderson return (ENOTCAPABLE); 358745bae37SJonathan Anderson } 3592609222aSPawel Jakub Dawidek 3602609222aSPawel Jakub Dawidek /* 3612609222aSPawel Jakub Dawidek * Check if the current ioctls list can be replaced by the new one. 3622609222aSPawel Jakub Dawidek */ 3632609222aSPawel Jakub Dawidek static int 3642609222aSPawel Jakub Dawidek cap_ioctl_limit_check(struct filedesc *fdp, int fd, const u_long *cmds, 3652609222aSPawel Jakub Dawidek size_t ncmds) 3662609222aSPawel Jakub Dawidek { 3672609222aSPawel Jakub Dawidek u_long *ocmds; 3682609222aSPawel Jakub Dawidek ssize_t oncmds; 3692609222aSPawel Jakub Dawidek u_long i; 3702609222aSPawel Jakub Dawidek long j; 3712609222aSPawel Jakub Dawidek 3722609222aSPawel Jakub Dawidek oncmds = fdp->fd_ofiles[fd].fde_nioctls; 3732609222aSPawel Jakub Dawidek if (oncmds == -1) 3742609222aSPawel Jakub Dawidek return (0); 3752609222aSPawel Jakub Dawidek if (oncmds < (ssize_t)ncmds) 3762609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 3772609222aSPawel Jakub Dawidek 3782609222aSPawel Jakub Dawidek ocmds = fdp->fd_ofiles[fd].fde_ioctls; 3792609222aSPawel Jakub Dawidek for (i = 0; i < ncmds; i++) { 3802609222aSPawel Jakub Dawidek for (j = 0; j < oncmds; j++) { 3812609222aSPawel Jakub Dawidek if (cmds[i] == ocmds[j]) 3822609222aSPawel Jakub Dawidek break; 3832609222aSPawel Jakub Dawidek } 3842609222aSPawel Jakub Dawidek if (j == oncmds) 3852609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 3862609222aSPawel Jakub Dawidek } 3872609222aSPawel Jakub Dawidek 3882609222aSPawel Jakub Dawidek return (0); 3892609222aSPawel Jakub Dawidek } 3902609222aSPawel Jakub Dawidek 3912609222aSPawel Jakub Dawidek int 3920dac22d8SPawel Jakub Dawidek kern_cap_ioctls_limit(struct thread *td, int fd, u_long *cmds, size_t ncmds) 3932609222aSPawel Jakub Dawidek { 3942609222aSPawel Jakub Dawidek struct filedesc *fdp; 3950dac22d8SPawel Jakub Dawidek u_long *ocmds; 3960dac22d8SPawel Jakub Dawidek int error; 3972609222aSPawel Jakub Dawidek 3982609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 3992609222aSPawel Jakub Dawidek 4002609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 4012609222aSPawel Jakub Dawidek FILEDESC_XLOCK(fdp); 4022609222aSPawel Jakub Dawidek 4032609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 4042609222aSPawel Jakub Dawidek error = EBADF; 4052609222aSPawel Jakub Dawidek goto out; 4062609222aSPawel Jakub Dawidek } 4072609222aSPawel Jakub Dawidek 4082609222aSPawel Jakub Dawidek error = cap_ioctl_limit_check(fdp, fd, cmds, ncmds); 4092609222aSPawel Jakub Dawidek if (error != 0) 4102609222aSPawel Jakub Dawidek goto out; 4112609222aSPawel Jakub Dawidek 4122609222aSPawel Jakub Dawidek ocmds = fdp->fd_ofiles[fd].fde_ioctls; 4132609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_ioctls = cmds; 4142609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_nioctls = ncmds; 4152609222aSPawel Jakub Dawidek 4162609222aSPawel Jakub Dawidek cmds = ocmds; 4172609222aSPawel Jakub Dawidek error = 0; 4182609222aSPawel Jakub Dawidek out: 4192609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 42092981fdfSPawel Jakub Dawidek free(cmds, M_FILECAPS); 4212609222aSPawel Jakub Dawidek return (error); 4222609222aSPawel Jakub Dawidek } 4232609222aSPawel Jakub Dawidek 4242609222aSPawel Jakub Dawidek int 4250dac22d8SPawel Jakub Dawidek sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap) 4260dac22d8SPawel Jakub Dawidek { 4270dac22d8SPawel Jakub Dawidek u_long *cmds; 4280dac22d8SPawel Jakub Dawidek size_t ncmds; 4290dac22d8SPawel Jakub Dawidek int error; 4300dac22d8SPawel Jakub Dawidek 4310dac22d8SPawel Jakub Dawidek ncmds = uap->ncmds; 4320dac22d8SPawel Jakub Dawidek 4330dac22d8SPawel Jakub Dawidek if (ncmds > 256) /* XXX: Is 256 sane? */ 4340dac22d8SPawel Jakub Dawidek return (EINVAL); 4350dac22d8SPawel Jakub Dawidek 4360dac22d8SPawel Jakub Dawidek if (ncmds == 0) { 4370dac22d8SPawel Jakub Dawidek cmds = NULL; 4380dac22d8SPawel Jakub Dawidek } else { 4390dac22d8SPawel Jakub Dawidek cmds = malloc(sizeof(cmds[0]) * ncmds, M_FILECAPS, M_WAITOK); 4400dac22d8SPawel Jakub Dawidek error = copyin(uap->cmds, cmds, sizeof(cmds[0]) * ncmds); 4410dac22d8SPawel Jakub Dawidek if (error != 0) { 4420dac22d8SPawel Jakub Dawidek free(cmds, M_FILECAPS); 4430dac22d8SPawel Jakub Dawidek return (error); 4440dac22d8SPawel Jakub Dawidek } 4450dac22d8SPawel Jakub Dawidek } 4460dac22d8SPawel Jakub Dawidek 4470dac22d8SPawel Jakub Dawidek return (kern_cap_ioctls_limit(td, uap->fd, cmds, ncmds)); 4480dac22d8SPawel Jakub Dawidek } 4490dac22d8SPawel Jakub Dawidek 4500dac22d8SPawel Jakub Dawidek int 4512609222aSPawel Jakub Dawidek sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap) 4522609222aSPawel Jakub Dawidek { 4532609222aSPawel Jakub Dawidek struct filedesc *fdp; 4542609222aSPawel Jakub Dawidek struct filedescent *fdep; 4552609222aSPawel Jakub Dawidek u_long *cmds; 4562609222aSPawel Jakub Dawidek size_t maxcmds; 4572609222aSPawel Jakub Dawidek int error, fd; 4582609222aSPawel Jakub Dawidek 4592609222aSPawel Jakub Dawidek fd = uap->fd; 4602609222aSPawel Jakub Dawidek cmds = uap->cmds; 4612609222aSPawel Jakub Dawidek maxcmds = uap->maxcmds; 4622609222aSPawel Jakub Dawidek 4632609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 4642609222aSPawel Jakub Dawidek 4652609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 4662609222aSPawel Jakub Dawidek FILEDESC_SLOCK(fdp); 4672609222aSPawel Jakub Dawidek 4682609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 4692609222aSPawel Jakub Dawidek error = EBADF; 4702609222aSPawel Jakub Dawidek goto out; 471e141be6fSDag-Erling Smørgrav } 472745bae37SJonathan Anderson 473745bae37SJonathan Anderson /* 4742609222aSPawel Jakub Dawidek * If all ioctls are allowed (fde_nioctls == -1 && fde_ioctls == NULL) 4752609222aSPawel Jakub Dawidek * the only sane thing we can do is to not populate the given array and 4762609222aSPawel Jakub Dawidek * return CAP_IOCTLS_ALL. 477745bae37SJonathan Anderson */ 478745bae37SJonathan Anderson 4792609222aSPawel Jakub Dawidek fdep = &fdp->fd_ofiles[fd]; 4802609222aSPawel Jakub Dawidek if (cmds != NULL && fdep->fde_ioctls != NULL) { 4812609222aSPawel Jakub Dawidek error = copyout(fdep->fde_ioctls, cmds, 4822609222aSPawel Jakub Dawidek sizeof(cmds[0]) * MIN(fdep->fde_nioctls, maxcmds)); 4832609222aSPawel Jakub Dawidek if (error != 0) 4842609222aSPawel Jakub Dawidek goto out; 4852609222aSPawel Jakub Dawidek } 4862609222aSPawel Jakub Dawidek if (fdep->fde_nioctls == -1) 4872609222aSPawel Jakub Dawidek td->td_retval[0] = CAP_IOCTLS_ALL; 488745bae37SJonathan Anderson else 4892609222aSPawel Jakub Dawidek td->td_retval[0] = fdep->fde_nioctls; 490745bae37SJonathan Anderson 4912609222aSPawel Jakub Dawidek error = 0; 4922609222aSPawel Jakub Dawidek out: 4932609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 494af098ed8SJonathan Anderson return (error); 495af098ed8SJonathan Anderson } 496af098ed8SJonathan Anderson 497af098ed8SJonathan Anderson /* 4982609222aSPawel Jakub Dawidek * Test whether a capability grants the given fcntl command. 499af098ed8SJonathan Anderson */ 500af098ed8SJonathan Anderson int 501a1bf8115SMateusz Guzik cap_fcntl_check_fde(struct filedescent *fde, int cmd) 502af098ed8SJonathan Anderson { 5032609222aSPawel Jakub Dawidek uint32_t fcntlcap; 504af098ed8SJonathan Anderson 5052609222aSPawel Jakub Dawidek fcntlcap = (1 << cmd); 5062609222aSPawel Jakub Dawidek KASSERT((CAP_FCNTL_ALL & fcntlcap) != 0, 5072609222aSPawel Jakub Dawidek ("Unsupported fcntl=%d.", cmd)); 5082609222aSPawel Jakub Dawidek 509a1bf8115SMateusz Guzik if ((fde->fde_fcntls & fcntlcap) != 0) 5102609222aSPawel Jakub Dawidek return (0); 5112609222aSPawel Jakub Dawidek 5122609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 5132609222aSPawel Jakub Dawidek } 5142609222aSPawel Jakub Dawidek 5152609222aSPawel Jakub Dawidek int 516a1bf8115SMateusz Guzik cap_fcntl_check(struct filedesc *fdp, int fd, int cmd) 517a1bf8115SMateusz Guzik { 518a1bf8115SMateusz Guzik 519a1bf8115SMateusz Guzik KASSERT(fd >= 0 && fd < fdp->fd_nfiles, 520a1bf8115SMateusz Guzik ("%s: invalid fd=%d", __func__, fd)); 521a1bf8115SMateusz Guzik 522a1bf8115SMateusz Guzik return (cap_fcntl_check_fde(&fdp->fd_ofiles[fd], cmd)); 523a1bf8115SMateusz Guzik } 524a1bf8115SMateusz Guzik 525a1bf8115SMateusz Guzik int 5262609222aSPawel Jakub Dawidek sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap) 5272609222aSPawel Jakub Dawidek { 5282609222aSPawel Jakub Dawidek struct filedesc *fdp; 5292609222aSPawel Jakub Dawidek uint32_t fcntlrights; 5302609222aSPawel Jakub Dawidek int fd; 5312609222aSPawel Jakub Dawidek 5322609222aSPawel Jakub Dawidek fd = uap->fd; 5332609222aSPawel Jakub Dawidek fcntlrights = uap->fcntlrights; 5342609222aSPawel Jakub Dawidek 5352609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 5362609222aSPawel Jakub Dawidek AUDIT_ARG_FCNTL_RIGHTS(fcntlrights); 5372609222aSPawel Jakub Dawidek 5382609222aSPawel Jakub Dawidek if ((fcntlrights & ~CAP_FCNTL_ALL) != 0) 5392609222aSPawel Jakub Dawidek return (EINVAL); 5402609222aSPawel Jakub Dawidek 5412609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 5422609222aSPawel Jakub Dawidek FILEDESC_XLOCK(fdp); 5432609222aSPawel Jakub Dawidek 5442609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 5452609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 5462609222aSPawel Jakub Dawidek return (EBADF); 5472609222aSPawel Jakub Dawidek } 5482609222aSPawel Jakub Dawidek 5492609222aSPawel Jakub Dawidek if ((fcntlrights & ~fdp->fd_ofiles[fd].fde_fcntls) != 0) { 5502609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 5512609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 5522609222aSPawel Jakub Dawidek } 5532609222aSPawel Jakub Dawidek 5542609222aSPawel Jakub Dawidek fdp->fd_ofiles[fd].fde_fcntls = fcntlrights; 5552609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 5562609222aSPawel Jakub Dawidek 557af098ed8SJonathan Anderson return (0); 558af098ed8SJonathan Anderson } 5592609222aSPawel Jakub Dawidek 5602609222aSPawel Jakub Dawidek int 5612609222aSPawel Jakub Dawidek sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap) 5622609222aSPawel Jakub Dawidek { 5632609222aSPawel Jakub Dawidek struct filedesc *fdp; 5642609222aSPawel Jakub Dawidek uint32_t rights; 5652609222aSPawel Jakub Dawidek int fd; 5662609222aSPawel Jakub Dawidek 5672609222aSPawel Jakub Dawidek fd = uap->fd; 5682609222aSPawel Jakub Dawidek 5692609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 5702609222aSPawel Jakub Dawidek 5712609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 5722609222aSPawel Jakub Dawidek FILEDESC_SLOCK(fdp); 5732609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 5742609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 5752609222aSPawel Jakub Dawidek return (EBADF); 5762609222aSPawel Jakub Dawidek } 5772609222aSPawel Jakub Dawidek rights = fdp->fd_ofiles[fd].fde_fcntls; 5782609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 5792609222aSPawel Jakub Dawidek 5802609222aSPawel Jakub Dawidek return (copyout(&rights, uap->fcntlrightsp, sizeof(rights))); 5812609222aSPawel Jakub Dawidek } 5822609222aSPawel Jakub Dawidek 583af098ed8SJonathan Anderson #else /* !CAPABILITIES */ 584af098ed8SJonathan Anderson 585af098ed8SJonathan Anderson /* 586af098ed8SJonathan Anderson * Stub Capability functions for when options CAPABILITIES isn't compiled 587af098ed8SJonathan Anderson * into the kernel. 588af098ed8SJonathan Anderson */ 5892609222aSPawel Jakub Dawidek 5902609222aSPawel Jakub Dawidek int 5912609222aSPawel Jakub Dawidek sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap) 5922609222aSPawel Jakub Dawidek { 5932609222aSPawel Jakub Dawidek 5942609222aSPawel Jakub Dawidek return (ENOSYS); 5952609222aSPawel Jakub Dawidek } 5962609222aSPawel Jakub Dawidek 5972609222aSPawel Jakub Dawidek int 59888eb5488SSean Bruno sys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap) 5992609222aSPawel Jakub Dawidek { 6002609222aSPawel Jakub Dawidek 6012609222aSPawel Jakub Dawidek return (ENOSYS); 6022609222aSPawel Jakub Dawidek } 6032609222aSPawel Jakub Dawidek 6042609222aSPawel Jakub Dawidek int 6052609222aSPawel Jakub Dawidek sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap) 6062609222aSPawel Jakub Dawidek { 6072609222aSPawel Jakub Dawidek 6082609222aSPawel Jakub Dawidek return (ENOSYS); 6092609222aSPawel Jakub Dawidek } 6102609222aSPawel Jakub Dawidek 6112609222aSPawel Jakub Dawidek int 6122609222aSPawel Jakub Dawidek sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap) 6132609222aSPawel Jakub Dawidek { 6142609222aSPawel Jakub Dawidek 6152609222aSPawel Jakub Dawidek return (ENOSYS); 6162609222aSPawel Jakub Dawidek } 6172609222aSPawel Jakub Dawidek 6182609222aSPawel Jakub Dawidek int 6192609222aSPawel Jakub Dawidek sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap) 6202609222aSPawel Jakub Dawidek { 6212609222aSPawel Jakub Dawidek 6222609222aSPawel Jakub Dawidek return (ENOSYS); 6232609222aSPawel Jakub Dawidek } 6242609222aSPawel Jakub Dawidek 6252609222aSPawel Jakub Dawidek int 6262609222aSPawel Jakub Dawidek sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap) 6272609222aSPawel Jakub Dawidek { 6282609222aSPawel Jakub Dawidek 6292609222aSPawel Jakub Dawidek return (ENOSYS); 6302609222aSPawel Jakub Dawidek } 6312609222aSPawel Jakub Dawidek 632af098ed8SJonathan Anderson #endif /* CAPABILITIES */ 633