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