xref: /f-stack/freebsd/kern/sys_capability.c (revision 22ce4aff)
1a9643ea8Slogwang /*-
2*22ce4affSfengbojiang  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*22ce4affSfengbojiang  *
4a9643ea8Slogwang  * Copyright (c) 2008-2011 Robert N. M. Watson
5a9643ea8Slogwang  * Copyright (c) 2010-2011 Jonathan Anderson
6a9643ea8Slogwang  * Copyright (c) 2012 FreeBSD Foundation
7a9643ea8Slogwang  * All rights reserved.
8a9643ea8Slogwang  *
9a9643ea8Slogwang  * This software was developed at the University of Cambridge Computer
10a9643ea8Slogwang  * Laboratory with support from a grant from Google, Inc.
11a9643ea8Slogwang  *
12a9643ea8Slogwang  * Portions of this software were developed by Pawel Jakub Dawidek under
13a9643ea8Slogwang  * sponsorship from the FreeBSD Foundation.
14a9643ea8Slogwang  *
15a9643ea8Slogwang  * Redistribution and use in source and binary forms, with or without
16a9643ea8Slogwang  * modification, are permitted provided that the following conditions
17a9643ea8Slogwang  * are met:
18a9643ea8Slogwang  * 1. Redistributions of source code must retain the above copyright
19a9643ea8Slogwang  *    notice, this list of conditions and the following disclaimer.
20a9643ea8Slogwang  * 2. Redistributions in binary form must reproduce the above copyright
21a9643ea8Slogwang  *    notice, this list of conditions and the following disclaimer in the
22a9643ea8Slogwang  *    documentation and/or other materials provided with the distribution.
23a9643ea8Slogwang  *
24a9643ea8Slogwang  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25a9643ea8Slogwang  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26a9643ea8Slogwang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27a9643ea8Slogwang  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28a9643ea8Slogwang  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29a9643ea8Slogwang  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30a9643ea8Slogwang  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31a9643ea8Slogwang  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32a9643ea8Slogwang  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33a9643ea8Slogwang  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34a9643ea8Slogwang  * SUCH DAMAGE.
35a9643ea8Slogwang  */
36a9643ea8Slogwang 
37a9643ea8Slogwang /*
38a9643ea8Slogwang  * FreeBSD kernel capability facility.
39a9643ea8Slogwang  *
40a9643ea8Slogwang  * Two kernel features are implemented here: capability mode, a sandboxed mode
41a9643ea8Slogwang  * of execution for processes, and capabilities, a refinement on file
42a9643ea8Slogwang  * descriptors that allows fine-grained control over operations on the file
43a9643ea8Slogwang  * descriptor.  Collectively, these allow processes to run in the style of a
44a9643ea8Slogwang  * historic "capability system" in which they can use only resources
45a9643ea8Slogwang  * explicitly delegated to them.  This model is enforced by restricting access
46a9643ea8Slogwang  * to global namespaces in capability mode.
47a9643ea8Slogwang  *
48a9643ea8Slogwang  * Capabilities wrap other file descriptor types, binding them to a constant
49a9643ea8Slogwang  * rights mask set when the capability is created.  New capabilities may be
50a9643ea8Slogwang  * derived from existing capabilities, but only if they have the same or a
51a9643ea8Slogwang  * strict subset of the rights on the original capability.
52a9643ea8Slogwang  *
53a9643ea8Slogwang  * System calls permitted in capability mode are defined in capabilities.conf;
54a9643ea8Slogwang  * calls must be carefully audited for safety to ensure that they don't allow
55a9643ea8Slogwang  * escape from a sandbox.  Some calls permit only a subset of operations in
56a9643ea8Slogwang  * capability mode -- for example, shm_open(2) is limited to creating
57a9643ea8Slogwang  * anonymous, rather than named, POSIX shared memory objects.
58a9643ea8Slogwang  */
59a9643ea8Slogwang 
60a9643ea8Slogwang #include <sys/cdefs.h>
61a9643ea8Slogwang __FBSDID("$FreeBSD$");
62a9643ea8Slogwang 
63a9643ea8Slogwang #include "opt_capsicum.h"
64a9643ea8Slogwang #include "opt_ktrace.h"
65a9643ea8Slogwang 
66a9643ea8Slogwang #include <sys/param.h>
67a9643ea8Slogwang #include <sys/capsicum.h>
68a9643ea8Slogwang #include <sys/file.h>
69a9643ea8Slogwang #include <sys/filedesc.h>
70a9643ea8Slogwang #include <sys/kernel.h>
71a9643ea8Slogwang #include <sys/limits.h>
72a9643ea8Slogwang #include <sys/lock.h>
73a9643ea8Slogwang #include <sys/mutex.h>
74a9643ea8Slogwang #include <sys/proc.h>
75a9643ea8Slogwang #include <sys/syscallsubr.h>
76a9643ea8Slogwang #include <sys/sysproto.h>
77a9643ea8Slogwang #include <sys/sysctl.h>
78a9643ea8Slogwang #include <sys/systm.h>
79a9643ea8Slogwang #include <sys/ucred.h>
80a9643ea8Slogwang #include <sys/uio.h>
81a9643ea8Slogwang #include <sys/ktrace.h>
82a9643ea8Slogwang 
83a9643ea8Slogwang #include <security/audit/audit.h>
84a9643ea8Slogwang 
85a9643ea8Slogwang #include <vm/uma.h>
86a9643ea8Slogwang #include <vm/vm.h>
87a9643ea8Slogwang 
88*22ce4affSfengbojiang bool __read_frequently trap_enotcap;
89*22ce4affSfengbojiang SYSCTL_BOOL(_kern, OID_AUTO, trap_enotcap, CTLFLAG_RWTUN, &trap_enotcap, 0,
90*22ce4affSfengbojiang     "Deliver SIGTRAP on ENOTCAPABLE");
91*22ce4affSfengbojiang 
92a9643ea8Slogwang #ifdef CAPABILITY_MODE
93a9643ea8Slogwang 
94*22ce4affSfengbojiang #define        IOCTLS_MAX_COUNT        256     /* XXX: Is 256 sane? */
95*22ce4affSfengbojiang 
96a9643ea8Slogwang FEATURE(security_capability_mode, "Capsicum Capability Mode");
97a9643ea8Slogwang 
98a9643ea8Slogwang /*
99a9643ea8Slogwang  * System call to enter capability mode for the process.
100a9643ea8Slogwang  */
101a9643ea8Slogwang int
sys_cap_enter(struct thread * td,struct cap_enter_args * uap)102a9643ea8Slogwang sys_cap_enter(struct thread *td, struct cap_enter_args *uap)
103a9643ea8Slogwang {
104a9643ea8Slogwang 	struct ucred *newcred, *oldcred;
105a9643ea8Slogwang 	struct proc *p;
106a9643ea8Slogwang 
107a9643ea8Slogwang 	if (IN_CAPABILITY_MODE(td))
108a9643ea8Slogwang 		return (0);
109a9643ea8Slogwang 
110a9643ea8Slogwang 	newcred = crget();
111a9643ea8Slogwang 	p = td->td_proc;
112a9643ea8Slogwang 	PROC_LOCK(p);
113a9643ea8Slogwang 	oldcred = crcopysafe(p, newcred);
114a9643ea8Slogwang 	newcred->cr_flags |= CRED_FLAG_CAPMODE;
115a9643ea8Slogwang 	proc_set_cred(p, newcred);
116a9643ea8Slogwang 	PROC_UNLOCK(p);
117a9643ea8Slogwang 	crfree(oldcred);
118a9643ea8Slogwang 	return (0);
119a9643ea8Slogwang }
120a9643ea8Slogwang 
121a9643ea8Slogwang /*
122a9643ea8Slogwang  * System call to query whether the process is in capability mode.
123a9643ea8Slogwang  */
124a9643ea8Slogwang int
sys_cap_getmode(struct thread * td,struct cap_getmode_args * uap)125a9643ea8Slogwang sys_cap_getmode(struct thread *td, struct cap_getmode_args *uap)
126a9643ea8Slogwang {
127a9643ea8Slogwang 	u_int i;
128a9643ea8Slogwang 
129a9643ea8Slogwang 	i = IN_CAPABILITY_MODE(td) ? 1 : 0;
130a9643ea8Slogwang 	return (copyout(&i, uap->modep, sizeof(i)));
131a9643ea8Slogwang }
132a9643ea8Slogwang 
133a9643ea8Slogwang #else /* !CAPABILITY_MODE */
134a9643ea8Slogwang 
135a9643ea8Slogwang int
sys_cap_enter(struct thread * td,struct cap_enter_args * uap)136a9643ea8Slogwang sys_cap_enter(struct thread *td, struct cap_enter_args *uap)
137a9643ea8Slogwang {
138a9643ea8Slogwang 
139a9643ea8Slogwang 	return (ENOSYS);
140a9643ea8Slogwang }
141a9643ea8Slogwang 
142a9643ea8Slogwang int
sys_cap_getmode(struct thread * td,struct cap_getmode_args * uap)143a9643ea8Slogwang sys_cap_getmode(struct thread *td, struct cap_getmode_args *uap)
144a9643ea8Slogwang {
145a9643ea8Slogwang 
146a9643ea8Slogwang 	return (ENOSYS);
147a9643ea8Slogwang }
148a9643ea8Slogwang 
149a9643ea8Slogwang #endif /* CAPABILITY_MODE */
150a9643ea8Slogwang 
151a9643ea8Slogwang #ifdef CAPABILITIES
152a9643ea8Slogwang 
153a9643ea8Slogwang FEATURE(security_capabilities, "Capsicum Capabilities");
154a9643ea8Slogwang 
155a9643ea8Slogwang MALLOC_DECLARE(M_FILECAPS);
156a9643ea8Slogwang 
157a9643ea8Slogwang static inline int
_cap_check(const cap_rights_t * havep,const cap_rights_t * needp,enum ktr_cap_fail_type type)158a9643ea8Slogwang _cap_check(const cap_rights_t *havep, const cap_rights_t *needp,
159a9643ea8Slogwang     enum ktr_cap_fail_type type)
160a9643ea8Slogwang {
161a9643ea8Slogwang 
162a9643ea8Slogwang 	if (!cap_rights_contains(havep, needp)) {
163a9643ea8Slogwang #ifdef KTRACE
164a9643ea8Slogwang 		if (KTRPOINT(curthread, KTR_CAPFAIL))
165a9643ea8Slogwang 			ktrcapfail(type, needp, havep);
166a9643ea8Slogwang #endif
167a9643ea8Slogwang 		return (ENOTCAPABLE);
168a9643ea8Slogwang 	}
169a9643ea8Slogwang 	return (0);
170a9643ea8Slogwang }
171a9643ea8Slogwang 
172a9643ea8Slogwang /*
173a9643ea8Slogwang  * Test whether a capability grants the requested rights.
174a9643ea8Slogwang  */
175a9643ea8Slogwang int
cap_check(const cap_rights_t * havep,const cap_rights_t * needp)176a9643ea8Slogwang cap_check(const cap_rights_t *havep, const cap_rights_t *needp)
177a9643ea8Slogwang {
178a9643ea8Slogwang 
179a9643ea8Slogwang 	return (_cap_check(havep, needp, CAPFAIL_NOTCAPABLE));
180a9643ea8Slogwang }
181a9643ea8Slogwang 
182*22ce4affSfengbojiang int
cap_check_failed_notcapable(const cap_rights_t * havep,const cap_rights_t * needp)183*22ce4affSfengbojiang cap_check_failed_notcapable(const cap_rights_t *havep, const cap_rights_t *needp)
184*22ce4affSfengbojiang {
185*22ce4affSfengbojiang 
186*22ce4affSfengbojiang #ifdef KTRACE
187*22ce4affSfengbojiang 	if (KTRPOINT(curthread, KTR_CAPFAIL))
188*22ce4affSfengbojiang 		ktrcapfail(CAPFAIL_NOTCAPABLE, needp, havep);
189*22ce4affSfengbojiang #endif
190*22ce4affSfengbojiang 	return (ENOTCAPABLE);
191*22ce4affSfengbojiang }
192*22ce4affSfengbojiang 
193a9643ea8Slogwang /*
194a9643ea8Slogwang  * Convert capability rights into VM access flags.
195a9643ea8Slogwang  */
196*22ce4affSfengbojiang vm_prot_t
cap_rights_to_vmprot(const cap_rights_t * havep)197*22ce4affSfengbojiang cap_rights_to_vmprot(const cap_rights_t *havep)
198a9643ea8Slogwang {
199*22ce4affSfengbojiang 	vm_prot_t maxprot;
200a9643ea8Slogwang 
201a9643ea8Slogwang 	maxprot = VM_PROT_NONE;
202a9643ea8Slogwang 	if (cap_rights_is_set(havep, CAP_MMAP_R))
203a9643ea8Slogwang 		maxprot |= VM_PROT_READ;
204a9643ea8Slogwang 	if (cap_rights_is_set(havep, CAP_MMAP_W))
205a9643ea8Slogwang 		maxprot |= VM_PROT_WRITE;
206a9643ea8Slogwang 	if (cap_rights_is_set(havep, CAP_MMAP_X))
207a9643ea8Slogwang 		maxprot |= VM_PROT_EXECUTE;
208a9643ea8Slogwang 
209a9643ea8Slogwang 	return (maxprot);
210a9643ea8Slogwang }
211a9643ea8Slogwang 
212a9643ea8Slogwang /*
213a9643ea8Slogwang  * Extract rights from a capability for monitoring purposes -- not for use in
214a9643ea8Slogwang  * any other way, as we want to keep all capability permission evaluation in
215a9643ea8Slogwang  * this one file.
216a9643ea8Slogwang  */
217a9643ea8Slogwang 
218*22ce4affSfengbojiang const cap_rights_t *
cap_rights_fde(const struct filedescent * fdep)219*22ce4affSfengbojiang cap_rights_fde(const struct filedescent *fdep)
220a9643ea8Slogwang {
221a9643ea8Slogwang 
222*22ce4affSfengbojiang 	return (cap_rights_fde_inline(fdep));
223a9643ea8Slogwang }
224a9643ea8Slogwang 
225*22ce4affSfengbojiang const cap_rights_t *
cap_rights(struct filedesc * fdp,int fd)226a9643ea8Slogwang cap_rights(struct filedesc *fdp, int fd)
227a9643ea8Slogwang {
228a9643ea8Slogwang 
229a9643ea8Slogwang 	return (cap_rights_fde(&fdp->fd_ofiles[fd]));
230a9643ea8Slogwang }
231a9643ea8Slogwang 
232a9643ea8Slogwang int
kern_cap_rights_limit(struct thread * td,int fd,cap_rights_t * rights)233a9643ea8Slogwang kern_cap_rights_limit(struct thread *td, int fd, cap_rights_t *rights)
234a9643ea8Slogwang {
235a9643ea8Slogwang 	struct filedesc *fdp;
236*22ce4affSfengbojiang 	struct filedescent *fdep;
237*22ce4affSfengbojiang 	u_long *ioctls;
238a9643ea8Slogwang 	int error;
239a9643ea8Slogwang 
240a9643ea8Slogwang 	fdp = td->td_proc->p_fd;
241a9643ea8Slogwang 	FILEDESC_XLOCK(fdp);
242*22ce4affSfengbojiang 	fdep = fdeget_locked(fdp, fd);
243*22ce4affSfengbojiang 	if (fdep == NULL) {
244a9643ea8Slogwang 		FILEDESC_XUNLOCK(fdp);
245a9643ea8Slogwang 		return (EBADF);
246a9643ea8Slogwang 	}
247*22ce4affSfengbojiang 	ioctls = NULL;
248a9643ea8Slogwang 	error = _cap_check(cap_rights(fdp, fd), rights, CAPFAIL_INCREASE);
249a9643ea8Slogwang 	if (error == 0) {
250*22ce4affSfengbojiang 		seqc_write_begin(&fdep->fde_seqc);
251*22ce4affSfengbojiang 		fdep->fde_rights = *rights;
252a9643ea8Slogwang 		if (!cap_rights_is_set(rights, CAP_IOCTL)) {
253*22ce4affSfengbojiang 			ioctls = fdep->fde_ioctls;
254*22ce4affSfengbojiang 			fdep->fde_ioctls = NULL;
255*22ce4affSfengbojiang 			fdep->fde_nioctls = 0;
256a9643ea8Slogwang 		}
257a9643ea8Slogwang 		if (!cap_rights_is_set(rights, CAP_FCNTL))
258*22ce4affSfengbojiang 			fdep->fde_fcntls = 0;
259*22ce4affSfengbojiang 		seqc_write_end(&fdep->fde_seqc);
260a9643ea8Slogwang 	}
261a9643ea8Slogwang 	FILEDESC_XUNLOCK(fdp);
262*22ce4affSfengbojiang 	free(ioctls, M_FILECAPS);
263a9643ea8Slogwang 	return (error);
264a9643ea8Slogwang }
265a9643ea8Slogwang 
266a9643ea8Slogwang /*
267a9643ea8Slogwang  * System call to limit rights of the given capability.
268a9643ea8Slogwang  */
269a9643ea8Slogwang int
sys_cap_rights_limit(struct thread * td,struct cap_rights_limit_args * uap)270a9643ea8Slogwang sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap)
271a9643ea8Slogwang {
272a9643ea8Slogwang 	cap_rights_t rights;
273a9643ea8Slogwang 	int error, version;
274a9643ea8Slogwang 
275*22ce4affSfengbojiang 	cap_rights_init_zero(&rights);
276a9643ea8Slogwang 
277a9643ea8Slogwang 	error = copyin(uap->rightsp, &rights, sizeof(rights.cr_rights[0]));
278a9643ea8Slogwang 	if (error != 0)
279a9643ea8Slogwang 		return (error);
280a9643ea8Slogwang 	version = CAPVER(&rights);
281a9643ea8Slogwang 	if (version != CAP_RIGHTS_VERSION_00)
282a9643ea8Slogwang 		return (EINVAL);
283a9643ea8Slogwang 
284a9643ea8Slogwang 	error = copyin(uap->rightsp, &rights,
285a9643ea8Slogwang 	    sizeof(rights.cr_rights[0]) * CAPARSIZE(&rights));
286a9643ea8Slogwang 	if (error != 0)
287a9643ea8Slogwang 		return (error);
288a9643ea8Slogwang 	/* Check for race. */
289a9643ea8Slogwang 	if (CAPVER(&rights) != version)
290a9643ea8Slogwang 		return (EINVAL);
291a9643ea8Slogwang 
292a9643ea8Slogwang 	if (!cap_rights_is_valid(&rights))
293a9643ea8Slogwang 		return (EINVAL);
294a9643ea8Slogwang 
295a9643ea8Slogwang 	if (version != CAP_RIGHTS_VERSION) {
296a9643ea8Slogwang 		rights.cr_rights[0] &= ~(0x3ULL << 62);
297a9643ea8Slogwang 		rights.cr_rights[0] |= ((uint64_t)CAP_RIGHTS_VERSION << 62);
298a9643ea8Slogwang 	}
299a9643ea8Slogwang #ifdef KTRACE
300a9643ea8Slogwang 	if (KTRPOINT(td, KTR_STRUCT))
301a9643ea8Slogwang 		ktrcaprights(&rights);
302a9643ea8Slogwang #endif
303a9643ea8Slogwang 
304a9643ea8Slogwang 	AUDIT_ARG_FD(uap->fd);
305a9643ea8Slogwang 	AUDIT_ARG_RIGHTS(&rights);
306a9643ea8Slogwang 	return (kern_cap_rights_limit(td, uap->fd, &rights));
307a9643ea8Slogwang }
308a9643ea8Slogwang 
309a9643ea8Slogwang /*
310a9643ea8Slogwang  * System call to query the rights mask associated with a capability.
311a9643ea8Slogwang  */
312a9643ea8Slogwang int
sys___cap_rights_get(struct thread * td,struct __cap_rights_get_args * uap)313a9643ea8Slogwang sys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap)
314a9643ea8Slogwang {
315a9643ea8Slogwang 	struct filedesc *fdp;
316a9643ea8Slogwang 	cap_rights_t rights;
317a9643ea8Slogwang 	int error, fd, i, n;
318a9643ea8Slogwang 
319a9643ea8Slogwang 	if (uap->version != CAP_RIGHTS_VERSION_00)
320a9643ea8Slogwang 		return (EINVAL);
321a9643ea8Slogwang 
322a9643ea8Slogwang 	fd = uap->fd;
323a9643ea8Slogwang 
324a9643ea8Slogwang 	AUDIT_ARG_FD(fd);
325a9643ea8Slogwang 
326a9643ea8Slogwang 	fdp = td->td_proc->p_fd;
327a9643ea8Slogwang 	FILEDESC_SLOCK(fdp);
328a9643ea8Slogwang 	if (fget_locked(fdp, fd) == NULL) {
329a9643ea8Slogwang 		FILEDESC_SUNLOCK(fdp);
330a9643ea8Slogwang 		return (EBADF);
331a9643ea8Slogwang 	}
332a9643ea8Slogwang 	rights = *cap_rights(fdp, fd);
333a9643ea8Slogwang 	FILEDESC_SUNLOCK(fdp);
334a9643ea8Slogwang 	n = uap->version + 2;
335a9643ea8Slogwang 	if (uap->version != CAPVER(&rights)) {
336a9643ea8Slogwang 		/*
337a9643ea8Slogwang 		 * For older versions we need to check if the descriptor
338a9643ea8Slogwang 		 * doesn't contain rights not understood by the caller.
339a9643ea8Slogwang 		 * If it does, we have to return an error.
340a9643ea8Slogwang 		 */
341a9643ea8Slogwang 		for (i = n; i < CAPARSIZE(&rights); i++) {
342a9643ea8Slogwang 			if ((rights.cr_rights[i] & ~(0x7FULL << 57)) != 0)
343a9643ea8Slogwang 				return (EINVAL);
344a9643ea8Slogwang 		}
345a9643ea8Slogwang 	}
346a9643ea8Slogwang 	error = copyout(&rights, uap->rightsp, sizeof(rights.cr_rights[0]) * n);
347a9643ea8Slogwang #ifdef KTRACE
348a9643ea8Slogwang 	if (error == 0 && KTRPOINT(td, KTR_STRUCT))
349a9643ea8Slogwang 		ktrcaprights(&rights);
350a9643ea8Slogwang #endif
351a9643ea8Slogwang 	return (error);
352a9643ea8Slogwang }
353a9643ea8Slogwang 
354a9643ea8Slogwang /*
355a9643ea8Slogwang  * Test whether a capability grants the given ioctl command.
356a9643ea8Slogwang  * If descriptor doesn't have CAP_IOCTL, then ioctls list is empty and
357a9643ea8Slogwang  * ENOTCAPABLE will be returned.
358a9643ea8Slogwang  */
359a9643ea8Slogwang int
cap_ioctl_check(struct filedesc * fdp,int fd,u_long cmd)360a9643ea8Slogwang cap_ioctl_check(struct filedesc *fdp, int fd, u_long cmd)
361a9643ea8Slogwang {
362*22ce4affSfengbojiang 	struct filedescent *fdep;
363a9643ea8Slogwang 	u_long *cmds;
364a9643ea8Slogwang 	ssize_t ncmds;
365a9643ea8Slogwang 	long i;
366a9643ea8Slogwang 
367a9643ea8Slogwang 	KASSERT(fd >= 0 && fd < fdp->fd_nfiles,
368a9643ea8Slogwang 		("%s: invalid fd=%d", __func__, fd));
369a9643ea8Slogwang 
370*22ce4affSfengbojiang 	fdep = fdeget_locked(fdp, fd);
371*22ce4affSfengbojiang 	KASSERT(fdep != NULL,
372*22ce4affSfengbojiang 	    ("%s: invalid fd=%d", __func__, fd));
373*22ce4affSfengbojiang 
374*22ce4affSfengbojiang 	ncmds = fdep->fde_nioctls;
375a9643ea8Slogwang 	if (ncmds == -1)
376a9643ea8Slogwang 		return (0);
377a9643ea8Slogwang 
378*22ce4affSfengbojiang 	cmds = fdep->fde_ioctls;
379a9643ea8Slogwang 	for (i = 0; i < ncmds; i++) {
380a9643ea8Slogwang 		if (cmds[i] == cmd)
381a9643ea8Slogwang 			return (0);
382a9643ea8Slogwang 	}
383a9643ea8Slogwang 
384a9643ea8Slogwang 	return (ENOTCAPABLE);
385a9643ea8Slogwang }
386a9643ea8Slogwang 
387a9643ea8Slogwang /*
388a9643ea8Slogwang  * Check if the current ioctls list can be replaced by the new one.
389a9643ea8Slogwang  */
390a9643ea8Slogwang static int
cap_ioctl_limit_check(struct filedescent * fdep,const u_long * cmds,size_t ncmds)391*22ce4affSfengbojiang cap_ioctl_limit_check(struct filedescent *fdep, const u_long *cmds,
392a9643ea8Slogwang     size_t ncmds)
393a9643ea8Slogwang {
394a9643ea8Slogwang 	u_long *ocmds;
395a9643ea8Slogwang 	ssize_t oncmds;
396a9643ea8Slogwang 	u_long i;
397a9643ea8Slogwang 	long j;
398a9643ea8Slogwang 
399*22ce4affSfengbojiang 	oncmds = fdep->fde_nioctls;
400a9643ea8Slogwang 	if (oncmds == -1)
401a9643ea8Slogwang 		return (0);
402a9643ea8Slogwang 	if (oncmds < (ssize_t)ncmds)
403a9643ea8Slogwang 		return (ENOTCAPABLE);
404a9643ea8Slogwang 
405*22ce4affSfengbojiang 	ocmds = fdep->fde_ioctls;
406a9643ea8Slogwang 	for (i = 0; i < ncmds; i++) {
407a9643ea8Slogwang 		for (j = 0; j < oncmds; j++) {
408a9643ea8Slogwang 			if (cmds[i] == ocmds[j])
409a9643ea8Slogwang 				break;
410a9643ea8Slogwang 		}
411a9643ea8Slogwang 		if (j == oncmds)
412a9643ea8Slogwang 			return (ENOTCAPABLE);
413a9643ea8Slogwang 	}
414a9643ea8Slogwang 
415a9643ea8Slogwang 	return (0);
416a9643ea8Slogwang }
417a9643ea8Slogwang 
418a9643ea8Slogwang int
kern_cap_ioctls_limit(struct thread * td,int fd,u_long * cmds,size_t ncmds)419a9643ea8Slogwang kern_cap_ioctls_limit(struct thread *td, int fd, u_long *cmds, size_t ncmds)
420a9643ea8Slogwang {
421a9643ea8Slogwang 	struct filedesc *fdp;
422*22ce4affSfengbojiang 	struct filedescent *fdep;
423a9643ea8Slogwang 	u_long *ocmds;
424a9643ea8Slogwang 	int error;
425a9643ea8Slogwang 
426a9643ea8Slogwang 	AUDIT_ARG_FD(fd);
427a9643ea8Slogwang 
428*22ce4affSfengbojiang 	if (ncmds > IOCTLS_MAX_COUNT) {
429*22ce4affSfengbojiang 		error = EINVAL;
430*22ce4affSfengbojiang 		goto out_free;
431*22ce4affSfengbojiang 	}
432*22ce4affSfengbojiang 
433a9643ea8Slogwang 	fdp = td->td_proc->p_fd;
434a9643ea8Slogwang 	FILEDESC_XLOCK(fdp);
435a9643ea8Slogwang 
436*22ce4affSfengbojiang 	fdep = fdeget_locked(fdp, fd);
437*22ce4affSfengbojiang 	if (fdep == NULL) {
438a9643ea8Slogwang 		error = EBADF;
439a9643ea8Slogwang 		goto out;
440a9643ea8Slogwang 	}
441a9643ea8Slogwang 
442*22ce4affSfengbojiang 	error = cap_ioctl_limit_check(fdep, cmds, ncmds);
443a9643ea8Slogwang 	if (error != 0)
444a9643ea8Slogwang 		goto out;
445a9643ea8Slogwang 
446*22ce4affSfengbojiang 	ocmds = fdep->fde_ioctls;
447*22ce4affSfengbojiang 	seqc_write_begin(&fdep->fde_seqc);
448*22ce4affSfengbojiang 	fdep->fde_ioctls = cmds;
449*22ce4affSfengbojiang 	fdep->fde_nioctls = ncmds;
450*22ce4affSfengbojiang 	seqc_write_end(&fdep->fde_seqc);
451a9643ea8Slogwang 
452a9643ea8Slogwang 	cmds = ocmds;
453a9643ea8Slogwang 	error = 0;
454a9643ea8Slogwang out:
455a9643ea8Slogwang 	FILEDESC_XUNLOCK(fdp);
456*22ce4affSfengbojiang out_free:
457a9643ea8Slogwang 	free(cmds, M_FILECAPS);
458a9643ea8Slogwang 	return (error);
459a9643ea8Slogwang }
460a9643ea8Slogwang 
461a9643ea8Slogwang int
sys_cap_ioctls_limit(struct thread * td,struct cap_ioctls_limit_args * uap)462a9643ea8Slogwang sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap)
463a9643ea8Slogwang {
464a9643ea8Slogwang 	u_long *cmds;
465a9643ea8Slogwang 	size_t ncmds;
466a9643ea8Slogwang 	int error;
467a9643ea8Slogwang 
468a9643ea8Slogwang 	ncmds = uap->ncmds;
469a9643ea8Slogwang 
470*22ce4affSfengbojiang 	if (ncmds > IOCTLS_MAX_COUNT)
471a9643ea8Slogwang 		return (EINVAL);
472a9643ea8Slogwang 
473a9643ea8Slogwang 	if (ncmds == 0) {
474a9643ea8Slogwang 		cmds = NULL;
475a9643ea8Slogwang 	} else {
476a9643ea8Slogwang 		cmds = malloc(sizeof(cmds[0]) * ncmds, M_FILECAPS, M_WAITOK);
477a9643ea8Slogwang 		error = copyin(uap->cmds, cmds, sizeof(cmds[0]) * ncmds);
478a9643ea8Slogwang 		if (error != 0) {
479a9643ea8Slogwang 			free(cmds, M_FILECAPS);
480a9643ea8Slogwang 			return (error);
481a9643ea8Slogwang 		}
482a9643ea8Slogwang 	}
483a9643ea8Slogwang 
484a9643ea8Slogwang 	return (kern_cap_ioctls_limit(td, uap->fd, cmds, ncmds));
485a9643ea8Slogwang }
486a9643ea8Slogwang 
487a9643ea8Slogwang int
sys_cap_ioctls_get(struct thread * td,struct cap_ioctls_get_args * uap)488a9643ea8Slogwang sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap)
489a9643ea8Slogwang {
490a9643ea8Slogwang 	struct filedesc *fdp;
491a9643ea8Slogwang 	struct filedescent *fdep;
492*22ce4affSfengbojiang 	u_long *cmdsp, *dstcmds;
493*22ce4affSfengbojiang 	size_t maxcmds, ncmds;
494*22ce4affSfengbojiang 	int16_t count;
495a9643ea8Slogwang 	int error, fd;
496a9643ea8Slogwang 
497a9643ea8Slogwang 	fd = uap->fd;
498*22ce4affSfengbojiang 	dstcmds = uap->cmds;
499a9643ea8Slogwang 	maxcmds = uap->maxcmds;
500a9643ea8Slogwang 
501a9643ea8Slogwang 	AUDIT_ARG_FD(fd);
502a9643ea8Slogwang 
503a9643ea8Slogwang 	fdp = td->td_proc->p_fd;
504a9643ea8Slogwang 
505*22ce4affSfengbojiang 	cmdsp = NULL;
506*22ce4affSfengbojiang 	if (dstcmds != NULL) {
507*22ce4affSfengbojiang 		cmdsp = malloc(sizeof(cmdsp[0]) * IOCTLS_MAX_COUNT, M_FILECAPS,
508*22ce4affSfengbojiang 		    M_WAITOK | M_ZERO);
509*22ce4affSfengbojiang 	}
510*22ce4affSfengbojiang 
511*22ce4affSfengbojiang 	FILEDESC_SLOCK(fdp);
512*22ce4affSfengbojiang 	fdep = fdeget_locked(fdp, fd);
513*22ce4affSfengbojiang 	if (fdep == NULL) {
514a9643ea8Slogwang 		error = EBADF;
515*22ce4affSfengbojiang 		FILEDESC_SUNLOCK(fdp);
516a9643ea8Slogwang 		goto out;
517a9643ea8Slogwang 	}
518*22ce4affSfengbojiang 	count = fdep->fde_nioctls;
519*22ce4affSfengbojiang 	if (count != -1 && cmdsp != NULL) {
520*22ce4affSfengbojiang 		ncmds = MIN(count, maxcmds);
521*22ce4affSfengbojiang 		memcpy(cmdsp, fdep->fde_ioctls, sizeof(cmdsp[0]) * ncmds);
522*22ce4affSfengbojiang 	}
523*22ce4affSfengbojiang 	FILEDESC_SUNLOCK(fdp);
524a9643ea8Slogwang 
525a9643ea8Slogwang 	/*
526a9643ea8Slogwang 	 * If all ioctls are allowed (fde_nioctls == -1 && fde_ioctls == NULL)
527a9643ea8Slogwang 	 * the only sane thing we can do is to not populate the given array and
528a9643ea8Slogwang 	 * return CAP_IOCTLS_ALL.
529a9643ea8Slogwang 	 */
530*22ce4affSfengbojiang 	if (count != -1) {
531*22ce4affSfengbojiang 		if (cmdsp != NULL) {
532*22ce4affSfengbojiang 			error = copyout(cmdsp, dstcmds,
533*22ce4affSfengbojiang 			    sizeof(cmdsp[0]) * ncmds);
534a9643ea8Slogwang 			if (error != 0)
535a9643ea8Slogwang 				goto out;
536a9643ea8Slogwang 		}
537*22ce4affSfengbojiang 		td->td_retval[0] = count;
538*22ce4affSfengbojiang 	} else {
539a9643ea8Slogwang 		td->td_retval[0] = CAP_IOCTLS_ALL;
540*22ce4affSfengbojiang 	}
541a9643ea8Slogwang 
542a9643ea8Slogwang 	error = 0;
543a9643ea8Slogwang out:
544*22ce4affSfengbojiang 	free(cmdsp, M_FILECAPS);
545a9643ea8Slogwang 	return (error);
546a9643ea8Slogwang }
547a9643ea8Slogwang 
548a9643ea8Slogwang /*
549a9643ea8Slogwang  * Test whether a capability grants the given fcntl command.
550a9643ea8Slogwang  */
551a9643ea8Slogwang int
cap_fcntl_check_fde(struct filedescent * fdep,int cmd)552*22ce4affSfengbojiang cap_fcntl_check_fde(struct filedescent *fdep, int cmd)
553a9643ea8Slogwang {
554a9643ea8Slogwang 	uint32_t fcntlcap;
555a9643ea8Slogwang 
556a9643ea8Slogwang 	fcntlcap = (1 << cmd);
557a9643ea8Slogwang 	KASSERT((CAP_FCNTL_ALL & fcntlcap) != 0,
558a9643ea8Slogwang 	    ("Unsupported fcntl=%d.", cmd));
559a9643ea8Slogwang 
560*22ce4affSfengbojiang 	if ((fdep->fde_fcntls & fcntlcap) != 0)
561a9643ea8Slogwang 		return (0);
562a9643ea8Slogwang 
563a9643ea8Slogwang 	return (ENOTCAPABLE);
564a9643ea8Slogwang }
565a9643ea8Slogwang 
566a9643ea8Slogwang int
cap_fcntl_check(struct filedesc * fdp,int fd,int cmd)567a9643ea8Slogwang cap_fcntl_check(struct filedesc *fdp, int fd, int cmd)
568a9643ea8Slogwang {
569a9643ea8Slogwang 
570a9643ea8Slogwang 	KASSERT(fd >= 0 && fd < fdp->fd_nfiles,
571a9643ea8Slogwang 	    ("%s: invalid fd=%d", __func__, fd));
572a9643ea8Slogwang 
573a9643ea8Slogwang 	return (cap_fcntl_check_fde(&fdp->fd_ofiles[fd], cmd));
574a9643ea8Slogwang }
575a9643ea8Slogwang 
576a9643ea8Slogwang int
sys_cap_fcntls_limit(struct thread * td,struct cap_fcntls_limit_args * uap)577a9643ea8Slogwang sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap)
578a9643ea8Slogwang {
579a9643ea8Slogwang 	struct filedesc *fdp;
580*22ce4affSfengbojiang 	struct filedescent *fdep;
581a9643ea8Slogwang 	uint32_t fcntlrights;
582a9643ea8Slogwang 	int fd;
583a9643ea8Slogwang 
584a9643ea8Slogwang 	fd = uap->fd;
585a9643ea8Slogwang 	fcntlrights = uap->fcntlrights;
586a9643ea8Slogwang 
587a9643ea8Slogwang 	AUDIT_ARG_FD(fd);
588a9643ea8Slogwang 	AUDIT_ARG_FCNTL_RIGHTS(fcntlrights);
589a9643ea8Slogwang 
590a9643ea8Slogwang 	if ((fcntlrights & ~CAP_FCNTL_ALL) != 0)
591a9643ea8Slogwang 		return (EINVAL);
592a9643ea8Slogwang 
593a9643ea8Slogwang 	fdp = td->td_proc->p_fd;
594a9643ea8Slogwang 	FILEDESC_XLOCK(fdp);
595a9643ea8Slogwang 
596*22ce4affSfengbojiang 	fdep = fdeget_locked(fdp, fd);
597*22ce4affSfengbojiang 	if (fdep == NULL) {
598a9643ea8Slogwang 		FILEDESC_XUNLOCK(fdp);
599a9643ea8Slogwang 		return (EBADF);
600a9643ea8Slogwang 	}
601a9643ea8Slogwang 
602*22ce4affSfengbojiang 	if ((fcntlrights & ~fdep->fde_fcntls) != 0) {
603a9643ea8Slogwang 		FILEDESC_XUNLOCK(fdp);
604a9643ea8Slogwang 		return (ENOTCAPABLE);
605a9643ea8Slogwang 	}
606a9643ea8Slogwang 
607*22ce4affSfengbojiang 	seqc_write_begin(&fdep->fde_seqc);
608*22ce4affSfengbojiang 	fdep->fde_fcntls = fcntlrights;
609*22ce4affSfengbojiang 	seqc_write_end(&fdep->fde_seqc);
610a9643ea8Slogwang 	FILEDESC_XUNLOCK(fdp);
611a9643ea8Slogwang 
612a9643ea8Slogwang 	return (0);
613a9643ea8Slogwang }
614a9643ea8Slogwang 
615a9643ea8Slogwang int
sys_cap_fcntls_get(struct thread * td,struct cap_fcntls_get_args * uap)616a9643ea8Slogwang sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap)
617a9643ea8Slogwang {
618a9643ea8Slogwang 	struct filedesc *fdp;
619*22ce4affSfengbojiang 	struct filedescent *fdep;
620a9643ea8Slogwang 	uint32_t rights;
621a9643ea8Slogwang 	int fd;
622a9643ea8Slogwang 
623a9643ea8Slogwang 	fd = uap->fd;
624a9643ea8Slogwang 
625a9643ea8Slogwang 	AUDIT_ARG_FD(fd);
626a9643ea8Slogwang 
627a9643ea8Slogwang 	fdp = td->td_proc->p_fd;
628a9643ea8Slogwang 	FILEDESC_SLOCK(fdp);
629*22ce4affSfengbojiang 	fdep = fdeget_locked(fdp, fd);
630*22ce4affSfengbojiang 	if (fdep == NULL) {
631a9643ea8Slogwang 		FILEDESC_SUNLOCK(fdp);
632a9643ea8Slogwang 		return (EBADF);
633a9643ea8Slogwang 	}
634*22ce4affSfengbojiang 	rights = fdep->fde_fcntls;
635a9643ea8Slogwang 	FILEDESC_SUNLOCK(fdp);
636a9643ea8Slogwang 
637a9643ea8Slogwang 	return (copyout(&rights, uap->fcntlrightsp, sizeof(rights)));
638a9643ea8Slogwang }
639a9643ea8Slogwang 
640a9643ea8Slogwang #else /* !CAPABILITIES */
641a9643ea8Slogwang 
642a9643ea8Slogwang /*
643a9643ea8Slogwang  * Stub Capability functions for when options CAPABILITIES isn't compiled
644a9643ea8Slogwang  * into the kernel.
645a9643ea8Slogwang  */
646a9643ea8Slogwang 
647a9643ea8Slogwang int
sys_cap_rights_limit(struct thread * td,struct cap_rights_limit_args * uap)648a9643ea8Slogwang sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap)
649a9643ea8Slogwang {
650a9643ea8Slogwang 
651a9643ea8Slogwang 	return (ENOSYS);
652a9643ea8Slogwang }
653a9643ea8Slogwang 
654a9643ea8Slogwang int
sys___cap_rights_get(struct thread * td,struct __cap_rights_get_args * uap)655a9643ea8Slogwang sys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap)
656a9643ea8Slogwang {
657a9643ea8Slogwang 
658a9643ea8Slogwang 	return (ENOSYS);
659a9643ea8Slogwang }
660a9643ea8Slogwang 
661a9643ea8Slogwang int
sys_cap_ioctls_limit(struct thread * td,struct cap_ioctls_limit_args * uap)662a9643ea8Slogwang sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap)
663a9643ea8Slogwang {
664a9643ea8Slogwang 
665a9643ea8Slogwang 	return (ENOSYS);
666a9643ea8Slogwang }
667a9643ea8Slogwang 
668a9643ea8Slogwang int
sys_cap_ioctls_get(struct thread * td,struct cap_ioctls_get_args * uap)669a9643ea8Slogwang sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap)
670a9643ea8Slogwang {
671a9643ea8Slogwang 
672a9643ea8Slogwang 	return (ENOSYS);
673a9643ea8Slogwang }
674a9643ea8Slogwang 
675a9643ea8Slogwang int
sys_cap_fcntls_limit(struct thread * td,struct cap_fcntls_limit_args * uap)676a9643ea8Slogwang sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap)
677a9643ea8Slogwang {
678a9643ea8Slogwang 
679a9643ea8Slogwang 	return (ENOSYS);
680a9643ea8Slogwang }
681a9643ea8Slogwang 
682a9643ea8Slogwang int
sys_cap_fcntls_get(struct thread * td,struct cap_fcntls_get_args * uap)683a9643ea8Slogwang sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap)
684a9643ea8Slogwang {
685a9643ea8Slogwang 
686a9643ea8Slogwang 	return (ENOSYS);
687a9643ea8Slogwang }
688a9643ea8Slogwang 
689a9643ea8Slogwang #endif /* CAPABILITIES */
690