196fcc75fSRobert Watson /*- 28a36da99SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 38a36da99SPedro F. Giffuni * 496fcc75fSRobert Watson * Copyright (c) 2008-2011 Robert N. M. Watson 596fcc75fSRobert Watson * Copyright (c) 2010-2011 Jonathan Anderson 62609222aSPawel Jakub Dawidek * Copyright (c) 2012 FreeBSD Foundation 796fcc75fSRobert Watson * All rights reserved. 896fcc75fSRobert Watson * 996fcc75fSRobert Watson * This software was developed at the University of Cambridge Computer 1096fcc75fSRobert Watson * Laboratory with support from a grant from Google, Inc. 1196fcc75fSRobert Watson * 122609222aSPawel Jakub Dawidek * Portions of this software were developed by Pawel Jakub Dawidek under 132609222aSPawel Jakub Dawidek * sponsorship from the FreeBSD Foundation. 142609222aSPawel Jakub Dawidek * 1596fcc75fSRobert Watson * Redistribution and use in source and binary forms, with or without 1696fcc75fSRobert Watson * modification, are permitted provided that the following conditions 1796fcc75fSRobert Watson * are met: 1896fcc75fSRobert Watson * 1. Redistributions of source code must retain the above copyright 1996fcc75fSRobert Watson * notice, this list of conditions and the following disclaimer. 2096fcc75fSRobert Watson * 2. Redistributions in binary form must reproduce the above copyright 2196fcc75fSRobert Watson * notice, this list of conditions and the following disclaimer in the 2296fcc75fSRobert Watson * documentation and/or other materials provided with the distribution. 2396fcc75fSRobert Watson * 2496fcc75fSRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2596fcc75fSRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2696fcc75fSRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2796fcc75fSRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2896fcc75fSRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2996fcc75fSRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3096fcc75fSRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3196fcc75fSRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3296fcc75fSRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3396fcc75fSRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3496fcc75fSRobert Watson * SUCH DAMAGE. 3596fcc75fSRobert Watson */ 3696fcc75fSRobert Watson 3796fcc75fSRobert Watson /* 3896fcc75fSRobert Watson * FreeBSD kernel capability facility. 3996fcc75fSRobert Watson * 4073516dbdSRobert Watson * Two kernel features are implemented here: capability mode, a sandboxed mode 4173516dbdSRobert Watson * of execution for processes, and capabilities, a refinement on file 4273516dbdSRobert Watson * descriptors that allows fine-grained control over operations on the file 4373516dbdSRobert Watson * descriptor. Collectively, these allow processes to run in the style of a 4473516dbdSRobert Watson * historic "capability system" in which they can use only resources 4573516dbdSRobert Watson * explicitly delegated to them. This model is enforced by restricting access 4673516dbdSRobert Watson * to global namespaces in capability mode. 4796fcc75fSRobert Watson * 4873516dbdSRobert Watson * Capabilities wrap other file descriptor types, binding them to a constant 4973516dbdSRobert Watson * rights mask set when the capability is created. New capabilities may be 5073516dbdSRobert Watson * derived from existing capabilities, but only if they have the same or a 5173516dbdSRobert Watson * strict subset of the rights on the original capability. 5273516dbdSRobert Watson * 5373516dbdSRobert Watson * System calls permitted in capability mode are defined in capabilities.conf; 5473516dbdSRobert Watson * calls must be carefully audited for safety to ensure that they don't allow 5573516dbdSRobert Watson * escape from a sandbox. Some calls permit only a subset of operations in 5673516dbdSRobert Watson * capability mode -- for example, shm_open(2) is limited to creating 5773516dbdSRobert Watson * anonymous, rather than named, POSIX shared memory objects. 5896fcc75fSRobert Watson */ 5996fcc75fSRobert Watson 6096fcc75fSRobert Watson #include <sys/cdefs.h> 6196fcc75fSRobert Watson __FBSDID("$FreeBSD$"); 6296fcc75fSRobert Watson 63297f1103SPawel Jakub Dawidek #include "opt_capsicum.h" 64297f1103SPawel Jakub Dawidek #include "opt_ktrace.h" 65297f1103SPawel Jakub Dawidek 6696fcc75fSRobert Watson #include <sys/param.h> 674a144410SRobert Watson #include <sys/capsicum.h> 6896fcc75fSRobert Watson #include <sys/file.h> 6996fcc75fSRobert Watson #include <sys/filedesc.h> 7096fcc75fSRobert Watson #include <sys/kernel.h> 712609222aSPawel Jakub Dawidek #include <sys/limits.h> 7296fcc75fSRobert Watson #include <sys/lock.h> 7396fcc75fSRobert Watson #include <sys/mutex.h> 7496fcc75fSRobert Watson #include <sys/proc.h> 750dac22d8SPawel Jakub Dawidek #include <sys/syscallsubr.h> 7696fcc75fSRobert Watson #include <sys/sysproto.h> 7796fcc75fSRobert Watson #include <sys/sysctl.h> 7896fcc75fSRobert Watson #include <sys/systm.h> 7996fcc75fSRobert Watson #include <sys/ucred.h> 80c601ad8eSDag-Erling Smørgrav #include <sys/uio.h> 81c601ad8eSDag-Erling Smørgrav #include <sys/ktrace.h> 8296fcc75fSRobert Watson 8396fcc75fSRobert Watson #include <security/audit/audit.h> 8496fcc75fSRobert Watson 8596fcc75fSRobert Watson #include <vm/uma.h> 8696fcc75fSRobert Watson #include <vm/vm.h> 8796fcc75fSRobert Watson 8875e9b455SMateusz Guzik bool __read_frequently trap_enotcap; 89afde86ebSMark Johnston SYSCTL_BOOL(_kern, OID_AUTO, trap_enotcap, CTLFLAG_RWTUN, &trap_enotcap, 0, 90643f6f47SKonstantin Belousov "Deliver SIGTRAP on ENOTCAPABLE"); 91643f6f47SKonstantin Belousov 9224c1c3bfSJonathan Anderson #ifdef CAPABILITY_MODE 9396fcc75fSRobert Watson 944b83a776SMariusz Zaborski #define IOCTLS_MAX_COUNT 256 /* XXX: Is 256 sane? */ 954b83a776SMariusz Zaborski 96854d7b9fSRobert Watson FEATURE(security_capability_mode, "Capsicum Capability Mode"); 97d783bbd2SAlexander Leidinger 9896fcc75fSRobert Watson /* 9996fcc75fSRobert Watson * System call to enter capability mode for the process. 10096fcc75fSRobert Watson */ 10196fcc75fSRobert Watson int 1028451d0ddSKip Macy sys_cap_enter(struct thread *td, struct cap_enter_args *uap) 10396fcc75fSRobert Watson { 10496fcc75fSRobert Watson struct ucred *newcred, *oldcred; 10596fcc75fSRobert Watson struct proc *p; 10696fcc75fSRobert Watson 10796fcc75fSRobert Watson if (IN_CAPABILITY_MODE(td)) 10896fcc75fSRobert Watson return (0); 10996fcc75fSRobert Watson 11096fcc75fSRobert Watson newcred = crget(); 11196fcc75fSRobert Watson p = td->td_proc; 11296fcc75fSRobert Watson PROC_LOCK(p); 113bbd685e3SMark Johnston oldcred = crcopysafe(p, newcred); 11496fcc75fSRobert Watson newcred->cr_flags |= CRED_FLAG_CAPMODE; 115daf63fd2SMateusz Guzik proc_set_cred(p, newcred); 11696fcc75fSRobert Watson PROC_UNLOCK(p); 11796fcc75fSRobert Watson crfree(oldcred); 11896fcc75fSRobert Watson return (0); 11996fcc75fSRobert Watson } 12096fcc75fSRobert Watson 12196fcc75fSRobert Watson /* 12296fcc75fSRobert Watson * System call to query whether the process is in capability mode. 12396fcc75fSRobert Watson */ 12496fcc75fSRobert Watson int 1258451d0ddSKip Macy sys_cap_getmode(struct thread *td, struct cap_getmode_args *uap) 12696fcc75fSRobert Watson { 12796fcc75fSRobert Watson u_int i; 12896fcc75fSRobert Watson 12911b0cfe3SPawel Jakub Dawidek i = IN_CAPABILITY_MODE(td) ? 1 : 0; 13096fcc75fSRobert Watson return (copyout(&i, uap->modep, sizeof(i))); 13196fcc75fSRobert Watson } 13296fcc75fSRobert Watson 13324c1c3bfSJonathan Anderson #else /* !CAPABILITY_MODE */ 13496fcc75fSRobert Watson 13596fcc75fSRobert Watson int 1368451d0ddSKip Macy sys_cap_enter(struct thread *td, struct cap_enter_args *uap) 13796fcc75fSRobert Watson { 13896fcc75fSRobert Watson 13996fcc75fSRobert Watson return (ENOSYS); 14096fcc75fSRobert Watson } 14196fcc75fSRobert Watson 14296fcc75fSRobert Watson int 1438451d0ddSKip Macy sys_cap_getmode(struct thread *td, struct cap_getmode_args *uap) 14496fcc75fSRobert Watson { 14596fcc75fSRobert Watson 14696fcc75fSRobert Watson return (ENOSYS); 14796fcc75fSRobert Watson } 14896fcc75fSRobert Watson 14924c1c3bfSJonathan Anderson #endif /* CAPABILITY_MODE */ 150af098ed8SJonathan Anderson 151af098ed8SJonathan Anderson #ifdef CAPABILITIES 152af098ed8SJonathan Anderson 153854d7b9fSRobert Watson FEATURE(security_capabilities, "Capsicum Capabilities"); 154854d7b9fSRobert Watson 15592981fdfSPawel Jakub Dawidek MALLOC_DECLARE(M_FILECAPS); 15692981fdfSPawel Jakub Dawidek 1572609222aSPawel Jakub Dawidek static inline int 1587008be5bSPawel Jakub Dawidek _cap_check(const cap_rights_t *havep, const cap_rights_t *needp, 1597008be5bSPawel Jakub Dawidek enum ktr_cap_fail_type type) 160745bae37SJonathan Anderson { 161745bae37SJonathan Anderson 1627008be5bSPawel Jakub Dawidek if (!cap_rights_contains(havep, needp)) { 163c601ad8eSDag-Erling Smørgrav #ifdef KTRACE 164c601ad8eSDag-Erling Smørgrav if (KTRPOINT(curthread, KTR_CAPFAIL)) 1657008be5bSPawel Jakub Dawidek ktrcapfail(type, needp, havep); 166c601ad8eSDag-Erling Smørgrav #endif 167af098ed8SJonathan Anderson return (ENOTCAPABLE); 168c601ad8eSDag-Erling Smørgrav } 169af098ed8SJonathan Anderson return (0); 170af098ed8SJonathan Anderson } 171af098ed8SJonathan Anderson 172af098ed8SJonathan Anderson /* 1732609222aSPawel Jakub Dawidek * Test whether a capability grants the requested rights. 1742609222aSPawel Jakub Dawidek */ 1752609222aSPawel Jakub Dawidek int 1767008be5bSPawel Jakub Dawidek cap_check(const cap_rights_t *havep, const cap_rights_t *needp) 1772609222aSPawel Jakub Dawidek { 1782609222aSPawel Jakub Dawidek 1797008be5bSPawel Jakub Dawidek return (_cap_check(havep, needp, CAPFAIL_NOTCAPABLE)); 1802609222aSPawel Jakub Dawidek } 1812609222aSPawel Jakub Dawidek 182*bcd1cf4fSMateusz Guzik int 183*bcd1cf4fSMateusz Guzik cap_check_failed_notcapable(const cap_rights_t *havep, const cap_rights_t *needp) 184*bcd1cf4fSMateusz Guzik { 185*bcd1cf4fSMateusz Guzik 186*bcd1cf4fSMateusz Guzik #ifdef KTRACE 187*bcd1cf4fSMateusz Guzik if (KTRPOINT(curthread, KTR_CAPFAIL)) 188*bcd1cf4fSMateusz Guzik ktrcapfail(CAPFAIL_NOTCAPABLE, needp, havep); 189*bcd1cf4fSMateusz Guzik #endif 190*bcd1cf4fSMateusz Guzik return (ENOTCAPABLE); 191*bcd1cf4fSMateusz Guzik } 192*bcd1cf4fSMateusz Guzik 1932609222aSPawel Jakub Dawidek /* 1942609222aSPawel Jakub Dawidek * Convert capability rights into VM access flags. 1952609222aSPawel Jakub Dawidek */ 1962609222aSPawel Jakub Dawidek u_char 197acbde298SMatt Macy cap_rights_to_vmprot(const cap_rights_t *havep) 1982609222aSPawel Jakub Dawidek { 1992609222aSPawel Jakub Dawidek u_char maxprot; 2002609222aSPawel Jakub Dawidek 2012609222aSPawel Jakub Dawidek maxprot = VM_PROT_NONE; 2027008be5bSPawel Jakub Dawidek if (cap_rights_is_set(havep, CAP_MMAP_R)) 2032609222aSPawel Jakub Dawidek maxprot |= VM_PROT_READ; 2047008be5bSPawel Jakub Dawidek if (cap_rights_is_set(havep, CAP_MMAP_W)) 2052609222aSPawel Jakub Dawidek maxprot |= VM_PROT_WRITE; 2067008be5bSPawel Jakub Dawidek if (cap_rights_is_set(havep, CAP_MMAP_X)) 2072609222aSPawel Jakub Dawidek maxprot |= VM_PROT_EXECUTE; 2082609222aSPawel Jakub Dawidek 2092609222aSPawel Jakub Dawidek return (maxprot); 2102609222aSPawel Jakub Dawidek } 2112609222aSPawel Jakub Dawidek 2122609222aSPawel Jakub Dawidek /* 213745bae37SJonathan Anderson * Extract rights from a capability for monitoring purposes -- not for use in 214745bae37SJonathan Anderson * any other way, as we want to keep all capability permission evaluation in 215745bae37SJonathan Anderson * this one file. 216745bae37SJonathan Anderson */ 217a1bf8115SMateusz Guzik 218acbde298SMatt Macy const cap_rights_t * 219acbde298SMatt Macy cap_rights_fde(const struct filedescent *fdep) 220a1bf8115SMateusz Guzik { 221a1bf8115SMateusz Guzik 22298fca94dSMateusz Guzik return (cap_rights_fde_inline(fdep)); 223a1bf8115SMateusz Guzik } 224a1bf8115SMateusz Guzik 225acbde298SMatt Macy const cap_rights_t * 2262609222aSPawel Jakub Dawidek cap_rights(struct filedesc *fdp, int fd) 227745bae37SJonathan Anderson { 228745bae37SJonathan Anderson 229a1bf8115SMateusz Guzik return (cap_rights_fde(&fdp->fd_ofiles[fd])); 230745bae37SJonathan Anderson } 231745bae37SJonathan Anderson 232aa04a06dSEd Schouten int 233aa04a06dSEd Schouten kern_cap_rights_limit(struct thread *td, int fd, cap_rights_t *rights) 234aa04a06dSEd Schouten { 235aa04a06dSEd Schouten struct filedesc *fdp; 23620641651SMariusz Zaborski struct filedescent *fdep; 237aa04a06dSEd Schouten int error; 238aa04a06dSEd Schouten 239aa04a06dSEd Schouten fdp = td->td_proc->p_fd; 240aa04a06dSEd Schouten FILEDESC_XLOCK(fdp); 24120641651SMariusz Zaborski fdep = fdeget_locked(fdp, fd); 24220641651SMariusz Zaborski if (fdep == NULL) { 243aa04a06dSEd Schouten FILEDESC_XUNLOCK(fdp); 244aa04a06dSEd Schouten return (EBADF); 245aa04a06dSEd Schouten } 246aa04a06dSEd Schouten error = _cap_check(cap_rights(fdp, fd), rights, CAPFAIL_INCREASE); 247aa04a06dSEd Schouten if (error == 0) { 24820641651SMariusz Zaborski fdep->fde_rights = *rights; 249aa04a06dSEd Schouten if (!cap_rights_is_set(rights, CAP_IOCTL)) { 25020641651SMariusz Zaborski free(fdep->fde_ioctls, M_FILECAPS); 25120641651SMariusz Zaborski fdep->fde_ioctls = NULL; 25220641651SMariusz Zaborski fdep->fde_nioctls = 0; 253aa04a06dSEd Schouten } 254aa04a06dSEd Schouten if (!cap_rights_is_set(rights, CAP_FCNTL)) 25520641651SMariusz Zaborski fdep->fde_fcntls = 0; 256aa04a06dSEd Schouten } 257aa04a06dSEd Schouten FILEDESC_XUNLOCK(fdp); 258aa04a06dSEd Schouten return (error); 259aa04a06dSEd Schouten } 260aa04a06dSEd Schouten 261745bae37SJonathan Anderson /* 2622609222aSPawel Jakub Dawidek * System call to limit rights of the given capability. 263cfb9df55SJonathan Anderson */ 264cfb9df55SJonathan Anderson int 2652609222aSPawel Jakub Dawidek sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap) 266cfb9df55SJonathan Anderson { 2672609222aSPawel Jakub Dawidek cap_rights_t rights; 268aa04a06dSEd Schouten int error, version; 2697008be5bSPawel Jakub Dawidek 2707008be5bSPawel Jakub Dawidek cap_rights_init(&rights); 2717008be5bSPawel Jakub Dawidek 2727008be5bSPawel Jakub Dawidek error = copyin(uap->rightsp, &rights, sizeof(rights.cr_rights[0])); 2737008be5bSPawel Jakub Dawidek if (error != 0) 2747008be5bSPawel Jakub Dawidek return (error); 2757008be5bSPawel Jakub Dawidek version = CAPVER(&rights); 2767008be5bSPawel Jakub Dawidek if (version != CAP_RIGHTS_VERSION_00) 2777008be5bSPawel Jakub Dawidek return (EINVAL); 2787008be5bSPawel Jakub Dawidek 2797008be5bSPawel Jakub Dawidek error = copyin(uap->rightsp, &rights, 2807008be5bSPawel Jakub Dawidek sizeof(rights.cr_rights[0]) * CAPARSIZE(&rights)); 2817008be5bSPawel Jakub Dawidek if (error != 0) 2827008be5bSPawel Jakub Dawidek return (error); 2837008be5bSPawel Jakub Dawidek /* Check for race. */ 2847008be5bSPawel Jakub Dawidek if (CAPVER(&rights) != version) 2857008be5bSPawel Jakub Dawidek return (EINVAL); 2867008be5bSPawel Jakub Dawidek 2877008be5bSPawel Jakub Dawidek if (!cap_rights_is_valid(&rights)) 2887008be5bSPawel Jakub Dawidek return (EINVAL); 2897008be5bSPawel Jakub Dawidek 2907008be5bSPawel Jakub Dawidek if (version != CAP_RIGHTS_VERSION) { 2917008be5bSPawel Jakub Dawidek rights.cr_rights[0] &= ~(0x3ULL << 62); 2927008be5bSPawel Jakub Dawidek rights.cr_rights[0] |= ((uint64_t)CAP_RIGHTS_VERSION << 62); 2937008be5bSPawel Jakub Dawidek } 2947008be5bSPawel Jakub Dawidek #ifdef KTRACE 2957008be5bSPawel Jakub Dawidek if (KTRPOINT(td, KTR_STRUCT)) 2967008be5bSPawel Jakub Dawidek ktrcaprights(&rights); 2977008be5bSPawel Jakub Dawidek #endif 2982609222aSPawel Jakub Dawidek 299aa04a06dSEd Schouten AUDIT_ARG_FD(uap->fd); 3007008be5bSPawel Jakub Dawidek AUDIT_ARG_RIGHTS(&rights); 301aa04a06dSEd Schouten return (kern_cap_rights_limit(td, uap->fd, &rights)); 302cfb9df55SJonathan Anderson } 303cfb9df55SJonathan Anderson 304cfb9df55SJonathan Anderson /* 305cfb9df55SJonathan Anderson * System call to query the rights mask associated with a capability. 306cfb9df55SJonathan Anderson */ 307cfb9df55SJonathan Anderson int 3087008be5bSPawel Jakub Dawidek sys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap) 309cfb9df55SJonathan Anderson { 3102609222aSPawel Jakub Dawidek struct filedesc *fdp; 3112609222aSPawel Jakub Dawidek cap_rights_t rights; 3127008be5bSPawel Jakub Dawidek int error, fd, i, n; 3137008be5bSPawel Jakub Dawidek 3147008be5bSPawel Jakub Dawidek if (uap->version != CAP_RIGHTS_VERSION_00) 3157008be5bSPawel Jakub Dawidek return (EINVAL); 316cfb9df55SJonathan Anderson 3172609222aSPawel Jakub Dawidek fd = uap->fd; 3182609222aSPawel Jakub Dawidek 3192609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 3202609222aSPawel Jakub Dawidek 3212609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 3222609222aSPawel Jakub Dawidek FILEDESC_SLOCK(fdp); 3232609222aSPawel Jakub Dawidek if (fget_locked(fdp, fd) == NULL) { 3242609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 3252609222aSPawel Jakub Dawidek return (EBADF); 3262609222aSPawel Jakub Dawidek } 3277008be5bSPawel Jakub Dawidek rights = *cap_rights(fdp, fd); 3282609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 3297008be5bSPawel Jakub Dawidek n = uap->version + 2; 3307008be5bSPawel Jakub Dawidek if (uap->version != CAPVER(&rights)) { 3317008be5bSPawel Jakub Dawidek /* 3327008be5bSPawel Jakub Dawidek * For older versions we need to check if the descriptor 3337008be5bSPawel Jakub Dawidek * doesn't contain rights not understood by the caller. 3347008be5bSPawel Jakub Dawidek * If it does, we have to return an error. 3357008be5bSPawel Jakub Dawidek */ 3367008be5bSPawel Jakub Dawidek for (i = n; i < CAPARSIZE(&rights); i++) { 3377008be5bSPawel Jakub Dawidek if ((rights.cr_rights[i] & ~(0x7FULL << 57)) != 0) 3387008be5bSPawel Jakub Dawidek return (EINVAL); 3397008be5bSPawel Jakub Dawidek } 3407008be5bSPawel Jakub Dawidek } 3417008be5bSPawel Jakub Dawidek error = copyout(&rights, uap->rightsp, sizeof(rights.cr_rights[0]) * n); 3427008be5bSPawel Jakub Dawidek #ifdef KTRACE 3437008be5bSPawel Jakub Dawidek if (error == 0 && KTRPOINT(td, KTR_STRUCT)) 3447008be5bSPawel Jakub Dawidek ktrcaprights(&rights); 3457008be5bSPawel Jakub Dawidek #endif 3467008be5bSPawel Jakub Dawidek return (error); 347cfb9df55SJonathan Anderson } 348cfb9df55SJonathan Anderson 349cfb9df55SJonathan Anderson /* 3502609222aSPawel Jakub Dawidek * Test whether a capability grants the given ioctl command. 3512609222aSPawel Jakub Dawidek * If descriptor doesn't have CAP_IOCTL, then ioctls list is empty and 3522609222aSPawel Jakub Dawidek * ENOTCAPABLE will be returned. 353745bae37SJonathan Anderson */ 354745bae37SJonathan Anderson int 3552609222aSPawel Jakub Dawidek cap_ioctl_check(struct filedesc *fdp, int fd, u_long cmd) 356745bae37SJonathan Anderson { 35720641651SMariusz Zaborski struct filedescent *fdep; 3582609222aSPawel Jakub Dawidek u_long *cmds; 3592609222aSPawel Jakub Dawidek ssize_t ncmds; 3602609222aSPawel Jakub Dawidek long i; 361745bae37SJonathan Anderson 3622609222aSPawel Jakub Dawidek KASSERT(fd >= 0 && fd < fdp->fd_nfiles, 3632609222aSPawel Jakub Dawidek ("%s: invalid fd=%d", __func__, fd)); 364745bae37SJonathan Anderson 36520641651SMariusz Zaborski fdep = fdeget_locked(fdp, fd); 366965cd211SMariusz Zaborski KASSERT(fdep != NULL, 36720641651SMariusz Zaborski ("%s: invalid fd=%d", __func__, fd)); 36820641651SMariusz Zaborski 36920641651SMariusz Zaborski ncmds = fdep->fde_nioctls; 3702609222aSPawel Jakub Dawidek if (ncmds == -1) 3712609222aSPawel Jakub Dawidek return (0); 3722609222aSPawel Jakub Dawidek 37320641651SMariusz Zaborski cmds = fdep->fde_ioctls; 3742609222aSPawel Jakub Dawidek for (i = 0; i < ncmds; i++) { 3752609222aSPawel Jakub Dawidek if (cmds[i] == cmd) 3762609222aSPawel Jakub Dawidek return (0); 3772609222aSPawel Jakub Dawidek } 3782609222aSPawel Jakub Dawidek 379745bae37SJonathan Anderson return (ENOTCAPABLE); 380745bae37SJonathan Anderson } 3812609222aSPawel Jakub Dawidek 3822609222aSPawel Jakub Dawidek /* 3832609222aSPawel Jakub Dawidek * Check if the current ioctls list can be replaced by the new one. 3842609222aSPawel Jakub Dawidek */ 3852609222aSPawel Jakub Dawidek static int 38620641651SMariusz Zaborski cap_ioctl_limit_check(struct filedescent *fdep, const u_long *cmds, 3872609222aSPawel Jakub Dawidek size_t ncmds) 3882609222aSPawel Jakub Dawidek { 3892609222aSPawel Jakub Dawidek u_long *ocmds; 3902609222aSPawel Jakub Dawidek ssize_t oncmds; 3912609222aSPawel Jakub Dawidek u_long i; 3922609222aSPawel Jakub Dawidek long j; 3932609222aSPawel Jakub Dawidek 39420641651SMariusz Zaborski oncmds = fdep->fde_nioctls; 3952609222aSPawel Jakub Dawidek if (oncmds == -1) 3962609222aSPawel Jakub Dawidek return (0); 3972609222aSPawel Jakub Dawidek if (oncmds < (ssize_t)ncmds) 3982609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 3992609222aSPawel Jakub Dawidek 40020641651SMariusz Zaborski ocmds = fdep->fde_ioctls; 4012609222aSPawel Jakub Dawidek for (i = 0; i < ncmds; i++) { 4022609222aSPawel Jakub Dawidek for (j = 0; j < oncmds; j++) { 4032609222aSPawel Jakub Dawidek if (cmds[i] == ocmds[j]) 4042609222aSPawel Jakub Dawidek break; 4052609222aSPawel Jakub Dawidek } 4062609222aSPawel Jakub Dawidek if (j == oncmds) 4072609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 4082609222aSPawel Jakub Dawidek } 4092609222aSPawel Jakub Dawidek 4102609222aSPawel Jakub Dawidek return (0); 4112609222aSPawel Jakub Dawidek } 4122609222aSPawel Jakub Dawidek 4132609222aSPawel Jakub Dawidek int 4140dac22d8SPawel Jakub Dawidek kern_cap_ioctls_limit(struct thread *td, int fd, u_long *cmds, size_t ncmds) 4152609222aSPawel Jakub Dawidek { 4162609222aSPawel Jakub Dawidek struct filedesc *fdp; 41720641651SMariusz Zaborski struct filedescent *fdep; 4180dac22d8SPawel Jakub Dawidek u_long *ocmds; 4190dac22d8SPawel Jakub Dawidek int error; 4202609222aSPawel Jakub Dawidek 4212609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 4222609222aSPawel Jakub Dawidek 4234b83a776SMariusz Zaborski if (ncmds > IOCTLS_MAX_COUNT) { 4244b83a776SMariusz Zaborski error = EINVAL; 4254b83a776SMariusz Zaborski goto out_free; 4264b83a776SMariusz Zaborski } 4274b83a776SMariusz Zaborski 4282609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 4292609222aSPawel Jakub Dawidek FILEDESC_XLOCK(fdp); 4302609222aSPawel Jakub Dawidek 43120641651SMariusz Zaborski fdep = fdeget_locked(fdp, fd); 43220641651SMariusz Zaborski if (fdep == NULL) { 4332609222aSPawel Jakub Dawidek error = EBADF; 4342609222aSPawel Jakub Dawidek goto out; 4352609222aSPawel Jakub Dawidek } 4362609222aSPawel Jakub Dawidek 43720641651SMariusz Zaborski error = cap_ioctl_limit_check(fdep, cmds, ncmds); 4382609222aSPawel Jakub Dawidek if (error != 0) 4392609222aSPawel Jakub Dawidek goto out; 4402609222aSPawel Jakub Dawidek 44120641651SMariusz Zaborski ocmds = fdep->fde_ioctls; 44220641651SMariusz Zaborski fdep->fde_ioctls = cmds; 44320641651SMariusz Zaborski fdep->fde_nioctls = ncmds; 4442609222aSPawel Jakub Dawidek 4452609222aSPawel Jakub Dawidek cmds = ocmds; 4462609222aSPawel Jakub Dawidek error = 0; 4472609222aSPawel Jakub Dawidek out: 4482609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 4494b83a776SMariusz Zaborski out_free: 45092981fdfSPawel Jakub Dawidek free(cmds, M_FILECAPS); 4512609222aSPawel Jakub Dawidek return (error); 4522609222aSPawel Jakub Dawidek } 4532609222aSPawel Jakub Dawidek 4542609222aSPawel Jakub Dawidek int 4550dac22d8SPawel Jakub Dawidek sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap) 4560dac22d8SPawel Jakub Dawidek { 4570dac22d8SPawel Jakub Dawidek u_long *cmds; 4580dac22d8SPawel Jakub Dawidek size_t ncmds; 4590dac22d8SPawel Jakub Dawidek int error; 4600dac22d8SPawel Jakub Dawidek 4610dac22d8SPawel Jakub Dawidek ncmds = uap->ncmds; 4620dac22d8SPawel Jakub Dawidek 4634b83a776SMariusz Zaborski if (ncmds > IOCTLS_MAX_COUNT) 4640dac22d8SPawel Jakub Dawidek return (EINVAL); 4650dac22d8SPawel Jakub Dawidek 4660dac22d8SPawel Jakub Dawidek if (ncmds == 0) { 4670dac22d8SPawel Jakub Dawidek cmds = NULL; 4680dac22d8SPawel Jakub Dawidek } else { 4690dac22d8SPawel Jakub Dawidek cmds = malloc(sizeof(cmds[0]) * ncmds, M_FILECAPS, M_WAITOK); 4700dac22d8SPawel Jakub Dawidek error = copyin(uap->cmds, cmds, sizeof(cmds[0]) * ncmds); 4710dac22d8SPawel Jakub Dawidek if (error != 0) { 4720dac22d8SPawel Jakub Dawidek free(cmds, M_FILECAPS); 4730dac22d8SPawel Jakub Dawidek return (error); 4740dac22d8SPawel Jakub Dawidek } 4750dac22d8SPawel Jakub Dawidek } 4760dac22d8SPawel Jakub Dawidek 4770dac22d8SPawel Jakub Dawidek return (kern_cap_ioctls_limit(td, uap->fd, cmds, ncmds)); 4780dac22d8SPawel Jakub Dawidek } 4790dac22d8SPawel Jakub Dawidek 4800dac22d8SPawel Jakub Dawidek int 4812609222aSPawel Jakub Dawidek sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap) 4822609222aSPawel Jakub Dawidek { 4832609222aSPawel Jakub Dawidek struct filedesc *fdp; 4842609222aSPawel Jakub Dawidek struct filedescent *fdep; 4854b83a776SMariusz Zaborski u_long *cmdsp, *dstcmds; 4864b83a776SMariusz Zaborski size_t maxcmds, ncmds; 4874b83a776SMariusz Zaborski int16_t count; 4882609222aSPawel Jakub Dawidek int error, fd; 4892609222aSPawel Jakub Dawidek 4902609222aSPawel Jakub Dawidek fd = uap->fd; 4914b83a776SMariusz Zaborski dstcmds = uap->cmds; 4922609222aSPawel Jakub Dawidek maxcmds = uap->maxcmds; 4932609222aSPawel Jakub Dawidek 4942609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 4952609222aSPawel Jakub Dawidek 4962609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 4972609222aSPawel Jakub Dawidek 4984b83a776SMariusz Zaborski cmdsp = NULL; 4994b83a776SMariusz Zaborski if (dstcmds != NULL) { 5004b83a776SMariusz Zaborski cmdsp = malloc(sizeof(cmdsp[0]) * IOCTLS_MAX_COUNT, M_FILECAPS, 5014b83a776SMariusz Zaborski M_WAITOK | M_ZERO); 5024b83a776SMariusz Zaborski } 5034b83a776SMariusz Zaborski 5044b83a776SMariusz Zaborski FILEDESC_SLOCK(fdp); 5054b83a776SMariusz Zaborski fdep = fdeget_locked(fdp, fd); 5064b83a776SMariusz Zaborski if (fdep == NULL) { 5072609222aSPawel Jakub Dawidek error = EBADF; 5084b83a776SMariusz Zaborski FILEDESC_SUNLOCK(fdp); 5092609222aSPawel Jakub Dawidek goto out; 510e141be6fSDag-Erling Smørgrav } 5114b83a776SMariusz Zaborski count = fdep->fde_nioctls; 5124b83a776SMariusz Zaborski if (count != -1 && cmdsp != NULL) { 5134b83a776SMariusz Zaborski ncmds = MIN(count, maxcmds); 5144b83a776SMariusz Zaborski memcpy(cmdsp, fdep->fde_ioctls, sizeof(cmdsp[0]) * ncmds); 5154b83a776SMariusz Zaborski } 5164b83a776SMariusz Zaborski FILEDESC_SUNLOCK(fdp); 517745bae37SJonathan Anderson 518745bae37SJonathan Anderson /* 5192609222aSPawel Jakub Dawidek * If all ioctls are allowed (fde_nioctls == -1 && fde_ioctls == NULL) 5202609222aSPawel Jakub Dawidek * the only sane thing we can do is to not populate the given array and 5212609222aSPawel Jakub Dawidek * return CAP_IOCTLS_ALL. 522745bae37SJonathan Anderson */ 5234b83a776SMariusz Zaborski if (count != -1) { 5244b83a776SMariusz Zaborski if (cmdsp != NULL) { 5254b83a776SMariusz Zaborski error = copyout(cmdsp, dstcmds, 5264b83a776SMariusz Zaborski sizeof(cmdsp[0]) * ncmds); 5272609222aSPawel Jakub Dawidek if (error != 0) 5282609222aSPawel Jakub Dawidek goto out; 5292609222aSPawel Jakub Dawidek } 5304b83a776SMariusz Zaborski td->td_retval[0] = count; 5314b83a776SMariusz Zaborski } else { 5322609222aSPawel Jakub Dawidek td->td_retval[0] = CAP_IOCTLS_ALL; 5334b83a776SMariusz Zaborski } 534745bae37SJonathan Anderson 5352609222aSPawel Jakub Dawidek error = 0; 5362609222aSPawel Jakub Dawidek out: 5374b83a776SMariusz Zaborski free(cmdsp, M_FILECAPS); 538af098ed8SJonathan Anderson return (error); 539af098ed8SJonathan Anderson } 540af098ed8SJonathan Anderson 541af098ed8SJonathan Anderson /* 5422609222aSPawel Jakub Dawidek * Test whether a capability grants the given fcntl command. 543af098ed8SJonathan Anderson */ 544af098ed8SJonathan Anderson int 54520641651SMariusz Zaborski cap_fcntl_check_fde(struct filedescent *fdep, int cmd) 546af098ed8SJonathan Anderson { 5472609222aSPawel Jakub Dawidek uint32_t fcntlcap; 548af098ed8SJonathan Anderson 5492609222aSPawel Jakub Dawidek fcntlcap = (1 << cmd); 5502609222aSPawel Jakub Dawidek KASSERT((CAP_FCNTL_ALL & fcntlcap) != 0, 5512609222aSPawel Jakub Dawidek ("Unsupported fcntl=%d.", cmd)); 5522609222aSPawel Jakub Dawidek 55320641651SMariusz Zaborski if ((fdep->fde_fcntls & fcntlcap) != 0) 5542609222aSPawel Jakub Dawidek return (0); 5552609222aSPawel Jakub Dawidek 5562609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 5572609222aSPawel Jakub Dawidek } 5582609222aSPawel Jakub Dawidek 5592609222aSPawel Jakub Dawidek int 560a1bf8115SMateusz Guzik cap_fcntl_check(struct filedesc *fdp, int fd, int cmd) 561a1bf8115SMateusz Guzik { 562a1bf8115SMateusz Guzik 563a1bf8115SMateusz Guzik KASSERT(fd >= 0 && fd < fdp->fd_nfiles, 564a1bf8115SMateusz Guzik ("%s: invalid fd=%d", __func__, fd)); 565a1bf8115SMateusz Guzik 566a1bf8115SMateusz Guzik return (cap_fcntl_check_fde(&fdp->fd_ofiles[fd], cmd)); 567a1bf8115SMateusz Guzik } 568a1bf8115SMateusz Guzik 569a1bf8115SMateusz Guzik int 5702609222aSPawel Jakub Dawidek sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap) 5712609222aSPawel Jakub Dawidek { 5722609222aSPawel Jakub Dawidek struct filedesc *fdp; 57320641651SMariusz Zaborski struct filedescent *fdep; 5742609222aSPawel Jakub Dawidek uint32_t fcntlrights; 5752609222aSPawel Jakub Dawidek int fd; 5762609222aSPawel Jakub Dawidek 5772609222aSPawel Jakub Dawidek fd = uap->fd; 5782609222aSPawel Jakub Dawidek fcntlrights = uap->fcntlrights; 5792609222aSPawel Jakub Dawidek 5802609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 5812609222aSPawel Jakub Dawidek AUDIT_ARG_FCNTL_RIGHTS(fcntlrights); 5822609222aSPawel Jakub Dawidek 5832609222aSPawel Jakub Dawidek if ((fcntlrights & ~CAP_FCNTL_ALL) != 0) 5842609222aSPawel Jakub Dawidek return (EINVAL); 5852609222aSPawel Jakub Dawidek 5862609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 5872609222aSPawel Jakub Dawidek FILEDESC_XLOCK(fdp); 5882609222aSPawel Jakub Dawidek 58920641651SMariusz Zaborski fdep = fdeget_locked(fdp, fd); 59020641651SMariusz Zaborski if (fdep == NULL) { 5912609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 5922609222aSPawel Jakub Dawidek return (EBADF); 5932609222aSPawel Jakub Dawidek } 5942609222aSPawel Jakub Dawidek 59520641651SMariusz Zaborski if ((fcntlrights & ~fdep->fde_fcntls) != 0) { 5962609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 5972609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 5982609222aSPawel Jakub Dawidek } 5992609222aSPawel Jakub Dawidek 60020641651SMariusz Zaborski fdep->fde_fcntls = fcntlrights; 6012609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 6022609222aSPawel Jakub Dawidek 603af098ed8SJonathan Anderson return (0); 604af098ed8SJonathan Anderson } 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 struct filedesc *fdp; 61020641651SMariusz Zaborski struct filedescent *fdep; 6112609222aSPawel Jakub Dawidek uint32_t rights; 6122609222aSPawel Jakub Dawidek int fd; 6132609222aSPawel Jakub Dawidek 6142609222aSPawel Jakub Dawidek fd = uap->fd; 6152609222aSPawel Jakub Dawidek 6162609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 6172609222aSPawel Jakub Dawidek 6182609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 6192609222aSPawel Jakub Dawidek FILEDESC_SLOCK(fdp); 62020641651SMariusz Zaborski fdep = fdeget_locked(fdp, fd); 62120641651SMariusz Zaborski if (fdep == NULL) { 6222609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 6232609222aSPawel Jakub Dawidek return (EBADF); 6242609222aSPawel Jakub Dawidek } 62520641651SMariusz Zaborski rights = fdep->fde_fcntls; 6262609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 6272609222aSPawel Jakub Dawidek 6282609222aSPawel Jakub Dawidek return (copyout(&rights, uap->fcntlrightsp, sizeof(rights))); 6292609222aSPawel Jakub Dawidek } 6302609222aSPawel Jakub Dawidek 631af098ed8SJonathan Anderson #else /* !CAPABILITIES */ 632af098ed8SJonathan Anderson 633af098ed8SJonathan Anderson /* 634af098ed8SJonathan Anderson * Stub Capability functions for when options CAPABILITIES isn't compiled 635af098ed8SJonathan Anderson * into the kernel. 636af098ed8SJonathan Anderson */ 6372609222aSPawel Jakub Dawidek 6382609222aSPawel Jakub Dawidek int 6392609222aSPawel Jakub Dawidek sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap) 6402609222aSPawel Jakub Dawidek { 6412609222aSPawel Jakub Dawidek 6422609222aSPawel Jakub Dawidek return (ENOSYS); 6432609222aSPawel Jakub Dawidek } 6442609222aSPawel Jakub Dawidek 6452609222aSPawel Jakub Dawidek int 64688eb5488SSean Bruno sys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap) 6472609222aSPawel Jakub Dawidek { 6482609222aSPawel Jakub Dawidek 6492609222aSPawel Jakub Dawidek return (ENOSYS); 6502609222aSPawel Jakub Dawidek } 6512609222aSPawel Jakub Dawidek 6522609222aSPawel Jakub Dawidek int 6532609222aSPawel Jakub Dawidek sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap) 6542609222aSPawel Jakub Dawidek { 6552609222aSPawel Jakub Dawidek 6562609222aSPawel Jakub Dawidek return (ENOSYS); 6572609222aSPawel Jakub Dawidek } 6582609222aSPawel Jakub Dawidek 6592609222aSPawel Jakub Dawidek int 6602609222aSPawel Jakub Dawidek sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap) 6612609222aSPawel Jakub Dawidek { 6622609222aSPawel Jakub Dawidek 6632609222aSPawel Jakub Dawidek return (ENOSYS); 6642609222aSPawel Jakub Dawidek } 6652609222aSPawel Jakub Dawidek 6662609222aSPawel Jakub Dawidek int 6672609222aSPawel Jakub Dawidek sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap) 6682609222aSPawel Jakub Dawidek { 6692609222aSPawel Jakub Dawidek 6702609222aSPawel Jakub Dawidek return (ENOSYS); 6712609222aSPawel Jakub Dawidek } 6722609222aSPawel Jakub Dawidek 6732609222aSPawel Jakub Dawidek int 6742609222aSPawel Jakub Dawidek sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap) 6752609222aSPawel Jakub Dawidek { 6762609222aSPawel Jakub Dawidek 6772609222aSPawel Jakub Dawidek return (ENOSYS); 6782609222aSPawel Jakub Dawidek } 6792609222aSPawel Jakub Dawidek 680af098ed8SJonathan Anderson #endif /* CAPABILITIES */ 681