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 182bcd1cf4fSMateusz Guzik int 183bcd1cf4fSMateusz Guzik cap_check_failed_notcapable(const cap_rights_t *havep, const cap_rights_t *needp) 184bcd1cf4fSMateusz Guzik { 185bcd1cf4fSMateusz Guzik 186bcd1cf4fSMateusz Guzik #ifdef KTRACE 187bcd1cf4fSMateusz Guzik if (KTRPOINT(curthread, KTR_CAPFAIL)) 188bcd1cf4fSMateusz Guzik ktrcapfail(CAPFAIL_NOTCAPABLE, needp, havep); 189bcd1cf4fSMateusz Guzik #endif 190bcd1cf4fSMateusz Guzik return (ENOTCAPABLE); 191bcd1cf4fSMateusz Guzik } 192bcd1cf4fSMateusz Guzik 1932609222aSPawel Jakub Dawidek /* 1942609222aSPawel Jakub Dawidek * Convert capability rights into VM access flags. 1952609222aSPawel Jakub Dawidek */ 1960f5f49efSKyle Evans vm_prot_t 197acbde298SMatt Macy cap_rights_to_vmprot(const cap_rights_t *havep) 1982609222aSPawel Jakub Dawidek { 1990f5f49efSKyle Evans vm_prot_t 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; 2372d896b81SMark Johnston u_long *ioctls; 238aa04a06dSEd Schouten int error; 239aa04a06dSEd Schouten 240aa04a06dSEd Schouten fdp = td->td_proc->p_fd; 241aa04a06dSEd Schouten FILEDESC_XLOCK(fdp); 242*f17ef286SMateusz Guzik fdep = fdeget_noref(fdp, fd); 24320641651SMariusz Zaborski if (fdep == NULL) { 244aa04a06dSEd Schouten FILEDESC_XUNLOCK(fdp); 245aa04a06dSEd Schouten return (EBADF); 246aa04a06dSEd Schouten } 2472d896b81SMark Johnston ioctls = NULL; 248aa04a06dSEd Schouten error = _cap_check(cap_rights(fdp, fd), rights, CAPFAIL_INCREASE); 249aa04a06dSEd Schouten if (error == 0) { 2502d896b81SMark Johnston seqc_write_begin(&fdep->fde_seqc); 25120641651SMariusz Zaborski fdep->fde_rights = *rights; 252aa04a06dSEd Schouten if (!cap_rights_is_set(rights, CAP_IOCTL)) { 2532d896b81SMark Johnston ioctls = fdep->fde_ioctls; 25420641651SMariusz Zaborski fdep->fde_ioctls = NULL; 25520641651SMariusz Zaborski fdep->fde_nioctls = 0; 256aa04a06dSEd Schouten } 257aa04a06dSEd Schouten if (!cap_rights_is_set(rights, CAP_FCNTL)) 25820641651SMariusz Zaborski fdep->fde_fcntls = 0; 2592d896b81SMark Johnston seqc_write_end(&fdep->fde_seqc); 260aa04a06dSEd Schouten } 261aa04a06dSEd Schouten FILEDESC_XUNLOCK(fdp); 2622d896b81SMark Johnston free(ioctls, M_FILECAPS); 263aa04a06dSEd Schouten return (error); 264aa04a06dSEd Schouten } 265aa04a06dSEd Schouten 266745bae37SJonathan Anderson /* 2672609222aSPawel Jakub Dawidek * System call to limit rights of the given capability. 268cfb9df55SJonathan Anderson */ 269cfb9df55SJonathan Anderson int 2702609222aSPawel Jakub Dawidek sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap) 271cfb9df55SJonathan Anderson { 2722609222aSPawel Jakub Dawidek cap_rights_t rights; 273aa04a06dSEd Schouten int error, version; 2747008be5bSPawel Jakub Dawidek 275eb40664dSMateusz Guzik cap_rights_init_zero(&rights); 2767008be5bSPawel Jakub Dawidek 2777008be5bSPawel Jakub Dawidek error = copyin(uap->rightsp, &rights, sizeof(rights.cr_rights[0])); 2787008be5bSPawel Jakub Dawidek if (error != 0) 2797008be5bSPawel Jakub Dawidek return (error); 2807008be5bSPawel Jakub Dawidek version = CAPVER(&rights); 2817008be5bSPawel Jakub Dawidek if (version != CAP_RIGHTS_VERSION_00) 2827008be5bSPawel Jakub Dawidek return (EINVAL); 2837008be5bSPawel Jakub Dawidek 2847008be5bSPawel Jakub Dawidek error = copyin(uap->rightsp, &rights, 2857008be5bSPawel Jakub Dawidek sizeof(rights.cr_rights[0]) * CAPARSIZE(&rights)); 2867008be5bSPawel Jakub Dawidek if (error != 0) 2877008be5bSPawel Jakub Dawidek return (error); 2887008be5bSPawel Jakub Dawidek /* Check for race. */ 2897008be5bSPawel Jakub Dawidek if (CAPVER(&rights) != version) 2907008be5bSPawel Jakub Dawidek return (EINVAL); 2917008be5bSPawel Jakub Dawidek 2927008be5bSPawel Jakub Dawidek if (!cap_rights_is_valid(&rights)) 2937008be5bSPawel Jakub Dawidek return (EINVAL); 2947008be5bSPawel Jakub Dawidek 2957008be5bSPawel Jakub Dawidek if (version != CAP_RIGHTS_VERSION) { 2967008be5bSPawel Jakub Dawidek rights.cr_rights[0] &= ~(0x3ULL << 62); 2977008be5bSPawel Jakub Dawidek rights.cr_rights[0] |= ((uint64_t)CAP_RIGHTS_VERSION << 62); 2987008be5bSPawel Jakub Dawidek } 2997008be5bSPawel Jakub Dawidek #ifdef KTRACE 3007008be5bSPawel Jakub Dawidek if (KTRPOINT(td, KTR_STRUCT)) 3017008be5bSPawel Jakub Dawidek ktrcaprights(&rights); 3027008be5bSPawel Jakub Dawidek #endif 3032609222aSPawel Jakub Dawidek 304aa04a06dSEd Schouten AUDIT_ARG_FD(uap->fd); 3057008be5bSPawel Jakub Dawidek AUDIT_ARG_RIGHTS(&rights); 306aa04a06dSEd Schouten return (kern_cap_rights_limit(td, uap->fd, &rights)); 307cfb9df55SJonathan Anderson } 308cfb9df55SJonathan Anderson 309cfb9df55SJonathan Anderson /* 310cfb9df55SJonathan Anderson * System call to query the rights mask associated with a capability. 311cfb9df55SJonathan Anderson */ 312cfb9df55SJonathan Anderson int 3137008be5bSPawel Jakub Dawidek sys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap) 314cfb9df55SJonathan Anderson { 3152609222aSPawel Jakub Dawidek struct filedesc *fdp; 3162609222aSPawel Jakub Dawidek cap_rights_t rights; 3177008be5bSPawel Jakub Dawidek int error, fd, i, n; 3187008be5bSPawel Jakub Dawidek 3197008be5bSPawel Jakub Dawidek if (uap->version != CAP_RIGHTS_VERSION_00) 3207008be5bSPawel Jakub Dawidek return (EINVAL); 321cfb9df55SJonathan Anderson 3222609222aSPawel Jakub Dawidek fd = uap->fd; 3232609222aSPawel Jakub Dawidek 3242609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 3252609222aSPawel Jakub Dawidek 3262609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 3272609222aSPawel Jakub Dawidek FILEDESC_SLOCK(fdp); 328*f17ef286SMateusz Guzik if (fget_noref(fdp, fd) == NULL) { 3292609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 3302609222aSPawel Jakub Dawidek return (EBADF); 3312609222aSPawel Jakub Dawidek } 3327008be5bSPawel Jakub Dawidek rights = *cap_rights(fdp, fd); 3332609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 3347008be5bSPawel Jakub Dawidek n = uap->version + 2; 3357008be5bSPawel Jakub Dawidek if (uap->version != CAPVER(&rights)) { 3367008be5bSPawel Jakub Dawidek /* 3377008be5bSPawel Jakub Dawidek * For older versions we need to check if the descriptor 3387008be5bSPawel Jakub Dawidek * doesn't contain rights not understood by the caller. 3397008be5bSPawel Jakub Dawidek * If it does, we have to return an error. 3407008be5bSPawel Jakub Dawidek */ 3417008be5bSPawel Jakub Dawidek for (i = n; i < CAPARSIZE(&rights); i++) { 3427008be5bSPawel Jakub Dawidek if ((rights.cr_rights[i] & ~(0x7FULL << 57)) != 0) 3437008be5bSPawel Jakub Dawidek return (EINVAL); 3447008be5bSPawel Jakub Dawidek } 3457008be5bSPawel Jakub Dawidek } 3467008be5bSPawel Jakub Dawidek error = copyout(&rights, uap->rightsp, sizeof(rights.cr_rights[0]) * n); 3477008be5bSPawel Jakub Dawidek #ifdef KTRACE 3487008be5bSPawel Jakub Dawidek if (error == 0 && KTRPOINT(td, KTR_STRUCT)) 3497008be5bSPawel Jakub Dawidek ktrcaprights(&rights); 3507008be5bSPawel Jakub Dawidek #endif 3517008be5bSPawel Jakub Dawidek return (error); 352cfb9df55SJonathan Anderson } 353cfb9df55SJonathan Anderson 354cfb9df55SJonathan Anderson /* 3552609222aSPawel Jakub Dawidek * Test whether a capability grants the given ioctl command. 3562609222aSPawel Jakub Dawidek * If descriptor doesn't have CAP_IOCTL, then ioctls list is empty and 3572609222aSPawel Jakub Dawidek * ENOTCAPABLE will be returned. 358745bae37SJonathan Anderson */ 359745bae37SJonathan Anderson int 3602609222aSPawel Jakub Dawidek cap_ioctl_check(struct filedesc *fdp, int fd, u_long cmd) 361745bae37SJonathan Anderson { 36220641651SMariusz Zaborski struct filedescent *fdep; 3632609222aSPawel Jakub Dawidek u_long *cmds; 3642609222aSPawel Jakub Dawidek ssize_t ncmds; 3652609222aSPawel Jakub Dawidek long i; 366745bae37SJonathan Anderson 3672609222aSPawel Jakub Dawidek KASSERT(fd >= 0 && fd < fdp->fd_nfiles, 3682609222aSPawel Jakub Dawidek ("%s: invalid fd=%d", __func__, fd)); 369745bae37SJonathan Anderson 370*f17ef286SMateusz Guzik fdep = fdeget_noref(fdp, fd); 371965cd211SMariusz Zaborski KASSERT(fdep != NULL, 37220641651SMariusz Zaborski ("%s: invalid fd=%d", __func__, fd)); 37320641651SMariusz Zaborski 37420641651SMariusz Zaborski ncmds = fdep->fde_nioctls; 3752609222aSPawel Jakub Dawidek if (ncmds == -1) 3762609222aSPawel Jakub Dawidek return (0); 3772609222aSPawel Jakub Dawidek 37820641651SMariusz Zaborski cmds = fdep->fde_ioctls; 3792609222aSPawel Jakub Dawidek for (i = 0; i < ncmds; i++) { 3802609222aSPawel Jakub Dawidek if (cmds[i] == cmd) 3812609222aSPawel Jakub Dawidek return (0); 3822609222aSPawel Jakub Dawidek } 3832609222aSPawel Jakub Dawidek 384745bae37SJonathan Anderson return (ENOTCAPABLE); 385745bae37SJonathan Anderson } 3862609222aSPawel Jakub Dawidek 3872609222aSPawel Jakub Dawidek /* 3882609222aSPawel Jakub Dawidek * Check if the current ioctls list can be replaced by the new one. 3892609222aSPawel Jakub Dawidek */ 3902609222aSPawel Jakub Dawidek static int 39120641651SMariusz Zaborski cap_ioctl_limit_check(struct filedescent *fdep, const u_long *cmds, 3922609222aSPawel Jakub Dawidek size_t ncmds) 3932609222aSPawel Jakub Dawidek { 3942609222aSPawel Jakub Dawidek u_long *ocmds; 3952609222aSPawel Jakub Dawidek ssize_t oncmds; 3962609222aSPawel Jakub Dawidek u_long i; 3972609222aSPawel Jakub Dawidek long j; 3982609222aSPawel Jakub Dawidek 39920641651SMariusz Zaborski oncmds = fdep->fde_nioctls; 4002609222aSPawel Jakub Dawidek if (oncmds == -1) 4012609222aSPawel Jakub Dawidek return (0); 4022609222aSPawel Jakub Dawidek if (oncmds < (ssize_t)ncmds) 4032609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 4042609222aSPawel Jakub Dawidek 40520641651SMariusz Zaborski ocmds = fdep->fde_ioctls; 4062609222aSPawel Jakub Dawidek for (i = 0; i < ncmds; i++) { 4072609222aSPawel Jakub Dawidek for (j = 0; j < oncmds; j++) { 4082609222aSPawel Jakub Dawidek if (cmds[i] == ocmds[j]) 4092609222aSPawel Jakub Dawidek break; 4102609222aSPawel Jakub Dawidek } 4112609222aSPawel Jakub Dawidek if (j == oncmds) 4122609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 4132609222aSPawel Jakub Dawidek } 4142609222aSPawel Jakub Dawidek 4152609222aSPawel Jakub Dawidek return (0); 4162609222aSPawel Jakub Dawidek } 4172609222aSPawel Jakub Dawidek 4182609222aSPawel Jakub Dawidek int 4190dac22d8SPawel Jakub Dawidek kern_cap_ioctls_limit(struct thread *td, int fd, u_long *cmds, size_t ncmds) 4202609222aSPawel Jakub Dawidek { 4212609222aSPawel Jakub Dawidek struct filedesc *fdp; 42220641651SMariusz Zaborski struct filedescent *fdep; 4230dac22d8SPawel Jakub Dawidek u_long *ocmds; 4240dac22d8SPawel Jakub Dawidek int error; 4252609222aSPawel Jakub Dawidek 4262609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 4272609222aSPawel Jakub Dawidek 4284b83a776SMariusz Zaborski if (ncmds > IOCTLS_MAX_COUNT) { 4294b83a776SMariusz Zaborski error = EINVAL; 4304b83a776SMariusz Zaborski goto out_free; 4314b83a776SMariusz Zaborski } 4324b83a776SMariusz Zaborski 4332609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 4342609222aSPawel Jakub Dawidek FILEDESC_XLOCK(fdp); 4352609222aSPawel Jakub Dawidek 436*f17ef286SMateusz Guzik fdep = fdeget_noref(fdp, fd); 43720641651SMariusz Zaborski if (fdep == NULL) { 4382609222aSPawel Jakub Dawidek error = EBADF; 4392609222aSPawel Jakub Dawidek goto out; 4402609222aSPawel Jakub Dawidek } 4412609222aSPawel Jakub Dawidek 44220641651SMariusz Zaborski error = cap_ioctl_limit_check(fdep, cmds, ncmds); 4432609222aSPawel Jakub Dawidek if (error != 0) 4442609222aSPawel Jakub Dawidek goto out; 4452609222aSPawel Jakub Dawidek 44620641651SMariusz Zaborski ocmds = fdep->fde_ioctls; 4472d896b81SMark Johnston seqc_write_begin(&fdep->fde_seqc); 44820641651SMariusz Zaborski fdep->fde_ioctls = cmds; 44920641651SMariusz Zaborski fdep->fde_nioctls = ncmds; 4502d896b81SMark Johnston seqc_write_end(&fdep->fde_seqc); 4512609222aSPawel Jakub Dawidek 4522609222aSPawel Jakub Dawidek cmds = ocmds; 4532609222aSPawel Jakub Dawidek error = 0; 4542609222aSPawel Jakub Dawidek out: 4552609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 4564b83a776SMariusz Zaborski out_free: 45792981fdfSPawel Jakub Dawidek free(cmds, M_FILECAPS); 4582609222aSPawel Jakub Dawidek return (error); 4592609222aSPawel Jakub Dawidek } 4602609222aSPawel Jakub Dawidek 4612609222aSPawel Jakub Dawidek int 4620dac22d8SPawel Jakub Dawidek sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap) 4630dac22d8SPawel Jakub Dawidek { 4640dac22d8SPawel Jakub Dawidek u_long *cmds; 4650dac22d8SPawel Jakub Dawidek size_t ncmds; 4660dac22d8SPawel Jakub Dawidek int error; 4670dac22d8SPawel Jakub Dawidek 4680dac22d8SPawel Jakub Dawidek ncmds = uap->ncmds; 4690dac22d8SPawel Jakub Dawidek 4704b83a776SMariusz Zaborski if (ncmds > IOCTLS_MAX_COUNT) 4710dac22d8SPawel Jakub Dawidek return (EINVAL); 4720dac22d8SPawel Jakub Dawidek 4730dac22d8SPawel Jakub Dawidek if (ncmds == 0) { 4740dac22d8SPawel Jakub Dawidek cmds = NULL; 4750dac22d8SPawel Jakub Dawidek } else { 4760dac22d8SPawel Jakub Dawidek cmds = malloc(sizeof(cmds[0]) * ncmds, M_FILECAPS, M_WAITOK); 4770dac22d8SPawel Jakub Dawidek error = copyin(uap->cmds, cmds, sizeof(cmds[0]) * ncmds); 4780dac22d8SPawel Jakub Dawidek if (error != 0) { 4790dac22d8SPawel Jakub Dawidek free(cmds, M_FILECAPS); 4800dac22d8SPawel Jakub Dawidek return (error); 4810dac22d8SPawel Jakub Dawidek } 4820dac22d8SPawel Jakub Dawidek } 4830dac22d8SPawel Jakub Dawidek 4840dac22d8SPawel Jakub Dawidek return (kern_cap_ioctls_limit(td, uap->fd, cmds, ncmds)); 4850dac22d8SPawel Jakub Dawidek } 4860dac22d8SPawel Jakub Dawidek 4870dac22d8SPawel Jakub Dawidek int 4882609222aSPawel Jakub Dawidek sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap) 4892609222aSPawel Jakub Dawidek { 4902609222aSPawel Jakub Dawidek struct filedesc *fdp; 4912609222aSPawel Jakub Dawidek struct filedescent *fdep; 4924b83a776SMariusz Zaborski u_long *cmdsp, *dstcmds; 4934b83a776SMariusz Zaborski size_t maxcmds, ncmds; 4944b83a776SMariusz Zaborski int16_t count; 4952609222aSPawel Jakub Dawidek int error, fd; 4962609222aSPawel Jakub Dawidek 4972609222aSPawel Jakub Dawidek fd = uap->fd; 4984b83a776SMariusz Zaborski dstcmds = uap->cmds; 4992609222aSPawel Jakub Dawidek maxcmds = uap->maxcmds; 5002609222aSPawel Jakub Dawidek 5012609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 5022609222aSPawel Jakub Dawidek 5032609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 5042609222aSPawel Jakub Dawidek 5054b83a776SMariusz Zaborski cmdsp = NULL; 5064b83a776SMariusz Zaborski if (dstcmds != NULL) { 5074b83a776SMariusz Zaborski cmdsp = malloc(sizeof(cmdsp[0]) * IOCTLS_MAX_COUNT, M_FILECAPS, 5084b83a776SMariusz Zaborski M_WAITOK | M_ZERO); 5094b83a776SMariusz Zaborski } 5104b83a776SMariusz Zaborski 5114b83a776SMariusz Zaborski FILEDESC_SLOCK(fdp); 512*f17ef286SMateusz Guzik fdep = fdeget_noref(fdp, fd); 5134b83a776SMariusz Zaborski if (fdep == NULL) { 5142609222aSPawel Jakub Dawidek error = EBADF; 5154b83a776SMariusz Zaborski FILEDESC_SUNLOCK(fdp); 5162609222aSPawel Jakub Dawidek goto out; 517e141be6fSDag-Erling Smørgrav } 5184b83a776SMariusz Zaborski count = fdep->fde_nioctls; 5194b83a776SMariusz Zaborski if (count != -1 && cmdsp != NULL) { 5204b83a776SMariusz Zaborski ncmds = MIN(count, maxcmds); 5214b83a776SMariusz Zaborski memcpy(cmdsp, fdep->fde_ioctls, sizeof(cmdsp[0]) * ncmds); 5224b83a776SMariusz Zaborski } 5234b83a776SMariusz Zaborski FILEDESC_SUNLOCK(fdp); 524745bae37SJonathan Anderson 525745bae37SJonathan Anderson /* 5262609222aSPawel Jakub Dawidek * If all ioctls are allowed (fde_nioctls == -1 && fde_ioctls == NULL) 5272609222aSPawel Jakub Dawidek * the only sane thing we can do is to not populate the given array and 5282609222aSPawel Jakub Dawidek * return CAP_IOCTLS_ALL. 529745bae37SJonathan Anderson */ 5304b83a776SMariusz Zaborski if (count != -1) { 5314b83a776SMariusz Zaborski if (cmdsp != NULL) { 5324b83a776SMariusz Zaborski error = copyout(cmdsp, dstcmds, 5334b83a776SMariusz Zaborski sizeof(cmdsp[0]) * ncmds); 5342609222aSPawel Jakub Dawidek if (error != 0) 5352609222aSPawel Jakub Dawidek goto out; 5362609222aSPawel Jakub Dawidek } 5374b83a776SMariusz Zaborski td->td_retval[0] = count; 5384b83a776SMariusz Zaborski } else { 5392609222aSPawel Jakub Dawidek td->td_retval[0] = CAP_IOCTLS_ALL; 5404b83a776SMariusz Zaborski } 541745bae37SJonathan Anderson 5422609222aSPawel Jakub Dawidek error = 0; 5432609222aSPawel Jakub Dawidek out: 5444b83a776SMariusz Zaborski free(cmdsp, M_FILECAPS); 545af098ed8SJonathan Anderson return (error); 546af098ed8SJonathan Anderson } 547af098ed8SJonathan Anderson 548af098ed8SJonathan Anderson /* 5492609222aSPawel Jakub Dawidek * Test whether a capability grants the given fcntl command. 550af098ed8SJonathan Anderson */ 551af098ed8SJonathan Anderson int 55220641651SMariusz Zaborski cap_fcntl_check_fde(struct filedescent *fdep, int cmd) 553af098ed8SJonathan Anderson { 5542609222aSPawel Jakub Dawidek uint32_t fcntlcap; 555af098ed8SJonathan Anderson 5562609222aSPawel Jakub Dawidek fcntlcap = (1 << cmd); 5572609222aSPawel Jakub Dawidek KASSERT((CAP_FCNTL_ALL & fcntlcap) != 0, 5582609222aSPawel Jakub Dawidek ("Unsupported fcntl=%d.", cmd)); 5592609222aSPawel Jakub Dawidek 56020641651SMariusz Zaborski if ((fdep->fde_fcntls & fcntlcap) != 0) 5612609222aSPawel Jakub Dawidek return (0); 5622609222aSPawel Jakub Dawidek 5632609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 5642609222aSPawel Jakub Dawidek } 5652609222aSPawel Jakub Dawidek 5662609222aSPawel Jakub Dawidek int 567a1bf8115SMateusz Guzik cap_fcntl_check(struct filedesc *fdp, int fd, int cmd) 568a1bf8115SMateusz Guzik { 569a1bf8115SMateusz Guzik 570a1bf8115SMateusz Guzik KASSERT(fd >= 0 && fd < fdp->fd_nfiles, 571a1bf8115SMateusz Guzik ("%s: invalid fd=%d", __func__, fd)); 572a1bf8115SMateusz Guzik 573a1bf8115SMateusz Guzik return (cap_fcntl_check_fde(&fdp->fd_ofiles[fd], cmd)); 574a1bf8115SMateusz Guzik } 575a1bf8115SMateusz Guzik 576a1bf8115SMateusz Guzik int 5772609222aSPawel Jakub Dawidek sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap) 5782609222aSPawel Jakub Dawidek { 5792609222aSPawel Jakub Dawidek struct filedesc *fdp; 58020641651SMariusz Zaborski struct filedescent *fdep; 5812609222aSPawel Jakub Dawidek uint32_t fcntlrights; 5822609222aSPawel Jakub Dawidek int fd; 5832609222aSPawel Jakub Dawidek 5842609222aSPawel Jakub Dawidek fd = uap->fd; 5852609222aSPawel Jakub Dawidek fcntlrights = uap->fcntlrights; 5862609222aSPawel Jakub Dawidek 5872609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 5882609222aSPawel Jakub Dawidek AUDIT_ARG_FCNTL_RIGHTS(fcntlrights); 5892609222aSPawel Jakub Dawidek 5902609222aSPawel Jakub Dawidek if ((fcntlrights & ~CAP_FCNTL_ALL) != 0) 5912609222aSPawel Jakub Dawidek return (EINVAL); 5922609222aSPawel Jakub Dawidek 5932609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 5942609222aSPawel Jakub Dawidek FILEDESC_XLOCK(fdp); 5952609222aSPawel Jakub Dawidek 596*f17ef286SMateusz Guzik fdep = fdeget_noref(fdp, fd); 59720641651SMariusz Zaborski if (fdep == NULL) { 5982609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 5992609222aSPawel Jakub Dawidek return (EBADF); 6002609222aSPawel Jakub Dawidek } 6012609222aSPawel Jakub Dawidek 60220641651SMariusz Zaborski if ((fcntlrights & ~fdep->fde_fcntls) != 0) { 6032609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 6042609222aSPawel Jakub Dawidek return (ENOTCAPABLE); 6052609222aSPawel Jakub Dawidek } 6062609222aSPawel Jakub Dawidek 6072d896b81SMark Johnston seqc_write_begin(&fdep->fde_seqc); 60820641651SMariusz Zaborski fdep->fde_fcntls = fcntlrights; 6092d896b81SMark Johnston seqc_write_end(&fdep->fde_seqc); 6102609222aSPawel Jakub Dawidek FILEDESC_XUNLOCK(fdp); 6112609222aSPawel Jakub Dawidek 612af098ed8SJonathan Anderson return (0); 613af098ed8SJonathan Anderson } 6142609222aSPawel Jakub Dawidek 6152609222aSPawel Jakub Dawidek int 6162609222aSPawel Jakub Dawidek sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap) 6172609222aSPawel Jakub Dawidek { 6182609222aSPawel Jakub Dawidek struct filedesc *fdp; 61920641651SMariusz Zaborski struct filedescent *fdep; 6202609222aSPawel Jakub Dawidek uint32_t rights; 6212609222aSPawel Jakub Dawidek int fd; 6222609222aSPawel Jakub Dawidek 6232609222aSPawel Jakub Dawidek fd = uap->fd; 6242609222aSPawel Jakub Dawidek 6252609222aSPawel Jakub Dawidek AUDIT_ARG_FD(fd); 6262609222aSPawel Jakub Dawidek 6272609222aSPawel Jakub Dawidek fdp = td->td_proc->p_fd; 6282609222aSPawel Jakub Dawidek FILEDESC_SLOCK(fdp); 629*f17ef286SMateusz Guzik fdep = fdeget_noref(fdp, fd); 63020641651SMariusz Zaborski if (fdep == NULL) { 6312609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 6322609222aSPawel Jakub Dawidek return (EBADF); 6332609222aSPawel Jakub Dawidek } 63420641651SMariusz Zaborski rights = fdep->fde_fcntls; 6352609222aSPawel Jakub Dawidek FILEDESC_SUNLOCK(fdp); 6362609222aSPawel Jakub Dawidek 6372609222aSPawel Jakub Dawidek return (copyout(&rights, uap->fcntlrightsp, sizeof(rights))); 6382609222aSPawel Jakub Dawidek } 6392609222aSPawel Jakub Dawidek 640af098ed8SJonathan Anderson #else /* !CAPABILITIES */ 641af098ed8SJonathan Anderson 642af098ed8SJonathan Anderson /* 643af098ed8SJonathan Anderson * Stub Capability functions for when options CAPABILITIES isn't compiled 644af098ed8SJonathan Anderson * into the kernel. 645af098ed8SJonathan Anderson */ 6462609222aSPawel Jakub Dawidek 6472609222aSPawel Jakub Dawidek int 6482609222aSPawel Jakub Dawidek sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap) 6492609222aSPawel Jakub Dawidek { 6502609222aSPawel Jakub Dawidek 6512609222aSPawel Jakub Dawidek return (ENOSYS); 6522609222aSPawel Jakub Dawidek } 6532609222aSPawel Jakub Dawidek 6542609222aSPawel Jakub Dawidek int 65588eb5488SSean Bruno sys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap) 6562609222aSPawel Jakub Dawidek { 6572609222aSPawel Jakub Dawidek 6582609222aSPawel Jakub Dawidek return (ENOSYS); 6592609222aSPawel Jakub Dawidek } 6602609222aSPawel Jakub Dawidek 6612609222aSPawel Jakub Dawidek int 6622609222aSPawel Jakub Dawidek sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap) 6632609222aSPawel Jakub Dawidek { 6642609222aSPawel Jakub Dawidek 6652609222aSPawel Jakub Dawidek return (ENOSYS); 6662609222aSPawel Jakub Dawidek } 6672609222aSPawel Jakub Dawidek 6682609222aSPawel Jakub Dawidek int 6692609222aSPawel Jakub Dawidek sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap) 6702609222aSPawel Jakub Dawidek { 6712609222aSPawel Jakub Dawidek 6722609222aSPawel Jakub Dawidek return (ENOSYS); 6732609222aSPawel Jakub Dawidek } 6742609222aSPawel Jakub Dawidek 6752609222aSPawel Jakub Dawidek int 6762609222aSPawel Jakub Dawidek sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap) 6772609222aSPawel Jakub Dawidek { 6782609222aSPawel Jakub Dawidek 6792609222aSPawel Jakub Dawidek return (ENOSYS); 6802609222aSPawel Jakub Dawidek } 6812609222aSPawel Jakub Dawidek 6822609222aSPawel Jakub Dawidek int 6832609222aSPawel Jakub Dawidek sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap) 6842609222aSPawel Jakub Dawidek { 6852609222aSPawel Jakub Dawidek 6862609222aSPawel Jakub Dawidek return (ENOSYS); 6872609222aSPawel Jakub Dawidek } 6882609222aSPawel Jakub Dawidek 689af098ed8SJonathan Anderson #endif /* CAPABILITIES */ 690