1219129Srwatson/*-
2219129Srwatson * Copyright (c) 2008-2011 Robert N. M. Watson
3219129Srwatson * Copyright (c) 2010-2011 Jonathan Anderson
4247602Spjd * Copyright (c) 2012 FreeBSD Foundation
5219129Srwatson * All rights reserved.
6219129Srwatson *
7219129Srwatson * This software was developed at the University of Cambridge Computer
8219129Srwatson * Laboratory with support from a grant from Google, Inc.
9219129Srwatson *
10247602Spjd * Portions of this software were developed by Pawel Jakub Dawidek under
11247602Spjd * sponsorship from the FreeBSD Foundation.
12247602Spjd *
13219129Srwatson * Redistribution and use in source and binary forms, with or without
14219129Srwatson * modification, are permitted provided that the following conditions
15219129Srwatson * are met:
16219129Srwatson * 1. Redistributions of source code must retain the above copyright
17219129Srwatson *    notice, this list of conditions and the following disclaimer.
18219129Srwatson * 2. Redistributions in binary form must reproduce the above copyright
19219129Srwatson *    notice, this list of conditions and the following disclaimer in the
20219129Srwatson *    documentation and/or other materials provided with the distribution.
21219129Srwatson *
22219129Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23219129Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24219129Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25219129Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26219129Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27219129Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28219129Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29219129Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30219129Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31219129Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32219129Srwatson * SUCH DAMAGE.
33219129Srwatson */
34219129Srwatson
35219129Srwatson/*
36219129Srwatson * FreeBSD kernel capability facility.
37219129Srwatson *
38224839Srwatson * Two kernel features are implemented here: capability mode, a sandboxed mode
39224839Srwatson * of execution for processes, and capabilities, a refinement on file
40224839Srwatson * descriptors that allows fine-grained control over operations on the file
41224839Srwatson * descriptor.  Collectively, these allow processes to run in the style of a
42224839Srwatson * historic "capability system" in which they can use only resources
43224839Srwatson * explicitly delegated to them.  This model is enforced by restricting access
44224839Srwatson * to global namespaces in capability mode.
45219129Srwatson *
46224839Srwatson * Capabilities wrap other file descriptor types, binding them to a constant
47224839Srwatson * rights mask set when the capability is created.  New capabilities may be
48224839Srwatson * derived from existing capabilities, but only if they have the same or a
49224839Srwatson * strict subset of the rights on the original capability.
50224839Srwatson *
51224839Srwatson * System calls permitted in capability mode are defined in capabilities.conf;
52224839Srwatson * calls must be carefully audited for safety to ensure that they don't allow
53224839Srwatson * escape from a sandbox.  Some calls permit only a subset of operations in
54224839Srwatson * capability mode -- for example, shm_open(2) is limited to creating
55224839Srwatson * anonymous, rather than named, POSIX shared memory objects.
56219129Srwatson */
57219129Srwatson
58236858Spjd#include <sys/cdefs.h>
59236858Spjd__FBSDID("$FreeBSD$");
60236858Spjd
61223668Sjonathan#include "opt_capsicum.h"
62226269Sdes#include "opt_ktrace.h"
63219129Srwatson
64219129Srwatson#include <sys/param.h>
65219129Srwatson#include <sys/capability.h>
66219129Srwatson#include <sys/file.h>
67219129Srwatson#include <sys/filedesc.h>
68219129Srwatson#include <sys/kernel.h>
69247602Spjd#include <sys/limits.h>
70219129Srwatson#include <sys/lock.h>
71219129Srwatson#include <sys/mutex.h>
72219129Srwatson#include <sys/proc.h>
73254481Spjd#include <sys/syscallsubr.h>
74219129Srwatson#include <sys/sysproto.h>
75219129Srwatson#include <sys/sysctl.h>
76219129Srwatson#include <sys/systm.h>
77219129Srwatson#include <sys/ucred.h>
78226269Sdes#include <sys/uio.h>
79226269Sdes#include <sys/ktrace.h>
80219129Srwatson
81219129Srwatson#include <security/audit/audit.h>
82219129Srwatson
83219129Srwatson#include <vm/uma.h>
84219129Srwatson#include <vm/vm.h>
85219129Srwatson
86223668Sjonathan#ifdef CAPABILITY_MODE
87219129Srwatson
88224840SrwatsonFEATURE(security_capability_mode, "Capsicum Capability Mode");
89219258Snetchild
90219129Srwatson/*
91219129Srwatson * System call to enter capability mode for the process.
92219129Srwatson */
93219129Srwatsonint
94225617Skmacysys_cap_enter(struct thread *td, struct cap_enter_args *uap)
95219129Srwatson{
96219129Srwatson	struct ucred *newcred, *oldcred;
97219129Srwatson	struct proc *p;
98219129Srwatson
99219129Srwatson	if (IN_CAPABILITY_MODE(td))
100219129Srwatson		return (0);
101219129Srwatson
102219129Srwatson	newcred = crget();
103219129Srwatson	p = td->td_proc;
104219129Srwatson	PROC_LOCK(p);
105219129Srwatson	oldcred = p->p_ucred;
106219129Srwatson	crcopy(newcred, oldcred);
107219129Srwatson	newcred->cr_flags |= CRED_FLAG_CAPMODE;
108219129Srwatson	p->p_ucred = newcred;
109219129Srwatson	PROC_UNLOCK(p);
110219129Srwatson	crfree(oldcred);
111219129Srwatson	return (0);
112219129Srwatson}
113219129Srwatson
114219129Srwatson/*
115219129Srwatson * System call to query whether the process is in capability mode.
116219129Srwatson */
117219129Srwatsonint
118225617Skmacysys_cap_getmode(struct thread *td, struct cap_getmode_args *uap)
119219129Srwatson{
120219129Srwatson	u_int i;
121219129Srwatson
122246908Spjd	i = IN_CAPABILITY_MODE(td) ? 1 : 0;
123219129Srwatson	return (copyout(&i, uap->modep, sizeof(i)));
124219129Srwatson}
125219129Srwatson
126223668Sjonathan#else /* !CAPABILITY_MODE */
127219129Srwatson
128219129Srwatsonint
129225617Skmacysys_cap_enter(struct thread *td, struct cap_enter_args *uap)
130219129Srwatson{
131219129Srwatson
132219129Srwatson	return (ENOSYS);
133219129Srwatson}
134219129Srwatson
135219129Srwatsonint
136225617Skmacysys_cap_getmode(struct thread *td, struct cap_getmode_args *uap)
137219129Srwatson{
138219129Srwatson
139219129Srwatson	return (ENOSYS);
140219129Srwatson}
141219129Srwatson
142223668Sjonathan#endif /* CAPABILITY_MODE */
143223762Sjonathan
144223762Sjonathan#ifdef CAPABILITIES
145223762Sjonathan
146224840SrwatsonFEATURE(security_capabilities, "Capsicum Capabilities");
147224840Srwatson
148250944SpjdMALLOC_DECLARE(M_FILECAPS);
149250944Spjd
150247602Spjdstatic inline int
151255219Spjd_cap_check(const cap_rights_t *havep, const cap_rights_t *needp,
152255219Spjd    enum ktr_cap_fail_type type)
153247602Spjd{
154255219Spjd	int i;
155223762Sjonathan
156255219Spjd	for (i = 0; i < nitems(havep->cr_rights); i++) {
157255219Spjd		if (!cap_rights_contains(havep, needp)) {
158247602Spjd#ifdef KTRACE
159255219Spjd			if (KTRPOINT(curthread, KTR_CAPFAIL))
160255219Spjd				ktrcapfail(type, needp, havep);
161247602Spjd#endif
162255219Spjd			return (ENOTCAPABLE);
163255219Spjd		}
164247602Spjd	}
165247602Spjd	return (0);
166247602Spjd}
167247602Spjd
168223762Sjonathan/*
169247602Spjd * Test whether a capability grants the requested rights.
170224056Sjonathan */
171247602Spjdint
172255219Spjdcap_check(const cap_rights_t *havep, const cap_rights_t *needp)
173224056Sjonathan{
174224056Sjonathan
175255219Spjd	return (_cap_check(havep, needp, CAPFAIL_NOTCAPABLE));
176224056Sjonathan}
177224056Sjonathan
178224056Sjonathan/*
179247602Spjd * Convert capability rights into VM access flags.
180223762Sjonathan */
181247602Spjdu_char
182255219Spjdcap_rights_to_vmprot(cap_rights_t *havep)
183223762Sjonathan{
184247602Spjd	u_char maxprot;
185223762Sjonathan
186247602Spjd	maxprot = VM_PROT_NONE;
187255219Spjd	if (cap_rights_is_set(havep, CAP_MMAP_R))
188247602Spjd		maxprot |= VM_PROT_READ;
189255219Spjd	if (cap_rights_is_set(havep, CAP_MMAP_W))
190247602Spjd		maxprot |= VM_PROT_WRITE;
191255219Spjd	if (cap_rights_is_set(havep, CAP_MMAP_X))
192247602Spjd		maxprot |= VM_PROT_EXECUTE;
193247602Spjd
194247602Spjd	return (maxprot);
195223762Sjonathan}
196223762Sjonathan
197223762Sjonathan/*
198224056Sjonathan * Extract rights from a capability for monitoring purposes -- not for use in
199224056Sjonathan * any other way, as we want to keep all capability permission evaluation in
200224056Sjonathan * this one file.
201224056Sjonathan */
202273137Smjg
203255219Spjdcap_rights_t *
204273137Smjgcap_rights_fde(struct filedescent *fde)
205273137Smjg{
206273137Smjg
207273137Smjg	return (&fde->fde_rights);
208273137Smjg}
209273137Smjg
210273137Smjgcap_rights_t *
211247602Spjdcap_rights(struct filedesc *fdp, int fd)
212224056Sjonathan{
213224056Sjonathan
214273137Smjg	return (cap_rights_fde(&fdp->fd_ofiles[fd]));
215224056Sjonathan}
216224056Sjonathan
217224056Sjonathan/*
218247602Spjd * System call to limit rights of the given capability.
219224066Sjonathan */
220224066Sjonathanint
221247602Spjdsys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap)
222224066Sjonathan{
223247602Spjd	struct filedesc *fdp;
224247602Spjd	cap_rights_t rights;
225255219Spjd	int error, fd, version;
226224066Sjonathan
227255219Spjd	cap_rights_init(&rights);
228255219Spjd
229255219Spjd	error = copyin(uap->rightsp, &rights, sizeof(rights.cr_rights[0]));
230255219Spjd	if (error != 0)
231255219Spjd		return (error);
232255219Spjd	version = CAPVER(&rights);
233255219Spjd	if (version != CAP_RIGHTS_VERSION_00)
234255219Spjd		return (EINVAL);
235255219Spjd
236255219Spjd	error = copyin(uap->rightsp, &rights,
237255219Spjd	    sizeof(rights.cr_rights[0]) * CAPARSIZE(&rights));
238255219Spjd	if (error != 0)
239255219Spjd		return (error);
240255219Spjd	/* Check for race. */
241255219Spjd	if (CAPVER(&rights) != version)
242255219Spjd		return (EINVAL);
243255219Spjd
244255219Spjd	if (!cap_rights_is_valid(&rights))
245255219Spjd		return (EINVAL);
246255219Spjd
247255219Spjd	if (version != CAP_RIGHTS_VERSION) {
248255219Spjd		rights.cr_rights[0] &= ~(0x3ULL << 62);
249255219Spjd		rights.cr_rights[0] |= ((uint64_t)CAP_RIGHTS_VERSION << 62);
250255219Spjd	}
251255219Spjd#ifdef KTRACE
252255219Spjd	if (KTRPOINT(td, KTR_STRUCT))
253255219Spjd		ktrcaprights(&rights);
254255219Spjd#endif
255255219Spjd
256247602Spjd	fd = uap->fd;
257247602Spjd
258224066Sjonathan	AUDIT_ARG_FD(fd);
259255219Spjd	AUDIT_ARG_RIGHTS(&rights);
260247602Spjd
261247602Spjd	fdp = td->td_proc->p_fd;
262247602Spjd	FILEDESC_XLOCK(fdp);
263247602Spjd	if (fget_locked(fdp, fd) == NULL) {
264247602Spjd		FILEDESC_XUNLOCK(fdp);
265247602Spjd		return (EBADF);
266247602Spjd	}
267255219Spjd	error = _cap_check(cap_rights(fdp, fd), &rights, CAPFAIL_INCREASE);
268247602Spjd	if (error == 0) {
269247602Spjd		fdp->fd_ofiles[fd].fde_rights = rights;
270255219Spjd		if (!cap_rights_is_set(&rights, CAP_IOCTL)) {
271250944Spjd			free(fdp->fd_ofiles[fd].fde_ioctls, M_FILECAPS);
272247602Spjd			fdp->fd_ofiles[fd].fde_ioctls = NULL;
273247602Spjd			fdp->fd_ofiles[fd].fde_nioctls = 0;
274247602Spjd		}
275255219Spjd		if (!cap_rights_is_set(&rights, CAP_FCNTL))
276247602Spjd			fdp->fd_ofiles[fd].fde_fcntls = 0;
277247602Spjd	}
278247602Spjd	FILEDESC_XUNLOCK(fdp);
279232860Spho	return (error);
280224066Sjonathan}
281224066Sjonathan
282224066Sjonathan/*
283224066Sjonathan * System call to query the rights mask associated with a capability.
284224066Sjonathan */
285224066Sjonathanint
286255219Spjdsys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap)
287224066Sjonathan{
288247602Spjd	struct filedesc *fdp;
289247602Spjd	cap_rights_t rights;
290255219Spjd	int error, fd, i, n;
291224066Sjonathan
292255219Spjd	if (uap->version != CAP_RIGHTS_VERSION_00)
293255219Spjd		return (EINVAL);
294255219Spjd
295247602Spjd	fd = uap->fd;
296247602Spjd
297247602Spjd	AUDIT_ARG_FD(fd);
298247602Spjd
299247602Spjd	fdp = td->td_proc->p_fd;
300247602Spjd	FILEDESC_SLOCK(fdp);
301247602Spjd	if (fget_locked(fdp, fd) == NULL) {
302247602Spjd		FILEDESC_SUNLOCK(fdp);
303247602Spjd		return (EBADF);
304247602Spjd	}
305255219Spjd	rights = *cap_rights(fdp, fd);
306247602Spjd	FILEDESC_SUNLOCK(fdp);
307255219Spjd	n = uap->version + 2;
308255219Spjd	if (uap->version != CAPVER(&rights)) {
309255219Spjd		/*
310255219Spjd		 * For older versions we need to check if the descriptor
311255219Spjd		 * doesn't contain rights not understood by the caller.
312255219Spjd		 * If it does, we have to return an error.
313255219Spjd		 */
314255219Spjd		for (i = n; i < CAPARSIZE(&rights); i++) {
315255219Spjd			if ((rights.cr_rights[i] & ~(0x7FULL << 57)) != 0)
316255219Spjd				return (EINVAL);
317255219Spjd		}
318255219Spjd	}
319255219Spjd	error = copyout(&rights, uap->rightsp, sizeof(rights.cr_rights[0]) * n);
320255219Spjd#ifdef KTRACE
321255219Spjd	if (error == 0 && KTRPOINT(td, KTR_STRUCT))
322255219Spjd		ktrcaprights(&rights);
323255219Spjd#endif
324255219Spjd	return (error);
325224066Sjonathan}
326224066Sjonathan
327224066Sjonathan/*
328247602Spjd * Test whether a capability grants the given ioctl command.
329247602Spjd * If descriptor doesn't have CAP_IOCTL, then ioctls list is empty and
330247602Spjd * ENOTCAPABLE will be returned.
331224056Sjonathan */
332224056Sjonathanint
333247602Spjdcap_ioctl_check(struct filedesc *fdp, int fd, u_long cmd)
334224056Sjonathan{
335247602Spjd	u_long *cmds;
336247602Spjd	ssize_t ncmds;
337247602Spjd	long i;
338224056Sjonathan
339247602Spjd	FILEDESC_LOCK_ASSERT(fdp);
340247602Spjd	KASSERT(fd >= 0 && fd < fdp->fd_nfiles,
341247602Spjd	    ("%s: invalid fd=%d", __func__, fd));
342224056Sjonathan
343247602Spjd	ncmds = fdp->fd_ofiles[fd].fde_nioctls;
344247602Spjd	if (ncmds == -1)
345247602Spjd		return (0);
346247602Spjd
347247602Spjd	cmds = fdp->fd_ofiles[fd].fde_ioctls;
348247602Spjd	for (i = 0; i < ncmds; i++) {
349247602Spjd		if (cmds[i] == cmd)
350247602Spjd			return (0);
351224056Sjonathan	}
352224056Sjonathan
353247602Spjd	return (ENOTCAPABLE);
354224056Sjonathan}
355224056Sjonathan
356224056Sjonathan/*
357247602Spjd * Check if the current ioctls list can be replaced by the new one.
358223762Sjonathan */
359247602Spjdstatic int
360247602Spjdcap_ioctl_limit_check(struct filedesc *fdp, int fd, const u_long *cmds,
361247602Spjd    size_t ncmds)
362223762Sjonathan{
363247602Spjd	u_long *ocmds;
364247602Spjd	ssize_t oncmds;
365247602Spjd	u_long i;
366247602Spjd	long j;
367223762Sjonathan
368247602Spjd	oncmds = fdp->fd_ofiles[fd].fde_nioctls;
369247602Spjd	if (oncmds == -1)
370223762Sjonathan		return (0);
371247602Spjd	if (oncmds < (ssize_t)ncmds)
372247602Spjd		return (ENOTCAPABLE);
373247602Spjd
374247602Spjd	ocmds = fdp->fd_ofiles[fd].fde_ioctls;
375247602Spjd	for (i = 0; i < ncmds; i++) {
376247602Spjd		for (j = 0; j < oncmds; j++) {
377247602Spjd			if (cmds[i] == ocmds[j])
378247602Spjd				break;
379247602Spjd		}
380247602Spjd		if (j == oncmds)
381247602Spjd			return (ENOTCAPABLE);
382223762Sjonathan	}
383247602Spjd
384223762Sjonathan	return (0);
385223762Sjonathan}
386223762Sjonathan
387223762Sjonathanint
388254481Spjdkern_cap_ioctls_limit(struct thread *td, int fd, u_long *cmds, size_t ncmds)
389223762Sjonathan{
390247602Spjd	struct filedesc *fdp;
391254481Spjd	u_long *ocmds;
392254481Spjd	int error;
393223762Sjonathan
394247602Spjd	AUDIT_ARG_FD(fd);
395247602Spjd
396247602Spjd	fdp = td->td_proc->p_fd;
397247602Spjd	FILEDESC_XLOCK(fdp);
398247602Spjd
399247602Spjd	if (fget_locked(fdp, fd) == NULL) {
400247602Spjd		error = EBADF;
401247602Spjd		goto out;
402247602Spjd	}
403247602Spjd
404247602Spjd	error = cap_ioctl_limit_check(fdp, fd, cmds, ncmds);
405247602Spjd	if (error != 0)
406247602Spjd		goto out;
407247602Spjd
408247602Spjd	ocmds = fdp->fd_ofiles[fd].fde_ioctls;
409247602Spjd	fdp->fd_ofiles[fd].fde_ioctls = cmds;
410247602Spjd	fdp->fd_ofiles[fd].fde_nioctls = ncmds;
411247602Spjd
412247602Spjd	cmds = ocmds;
413247602Spjd	error = 0;
414247602Spjdout:
415247602Spjd	FILEDESC_XUNLOCK(fdp);
416250944Spjd	free(cmds, M_FILECAPS);
417247602Spjd	return (error);
418223762Sjonathan}
419223762Sjonathan
420247602Spjdint
421254481Spjdsys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap)
422254481Spjd{
423254481Spjd	u_long *cmds;
424254481Spjd	size_t ncmds;
425254481Spjd	int error;
426254481Spjd
427254481Spjd	ncmds = uap->ncmds;
428254481Spjd
429254481Spjd	if (ncmds > 256)	/* XXX: Is 256 sane? */
430254481Spjd		return (EINVAL);
431254481Spjd
432254481Spjd	if (ncmds == 0) {
433254481Spjd		cmds = NULL;
434254481Spjd	} else {
435254481Spjd		cmds = malloc(sizeof(cmds[0]) * ncmds, M_FILECAPS, M_WAITOK);
436254481Spjd		error = copyin(uap->cmds, cmds, sizeof(cmds[0]) * ncmds);
437254481Spjd		if (error != 0) {
438254481Spjd			free(cmds, M_FILECAPS);
439254481Spjd			return (error);
440254481Spjd		}
441254481Spjd	}
442254481Spjd
443254481Spjd	return (kern_cap_ioctls_limit(td, uap->fd, cmds, ncmds));
444254481Spjd}
445254481Spjd
446254481Spjdint
447247602Spjdsys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap)
448224056Sjonathan{
449247602Spjd	struct filedesc *fdp;
450247602Spjd	struct filedescent *fdep;
451247602Spjd	u_long *cmds;
452247602Spjd	size_t maxcmds;
453247602Spjd	int error, fd;
454224056Sjonathan
455247602Spjd	fd = uap->fd;
456247602Spjd	cmds = uap->cmds;
457247602Spjd	maxcmds = uap->maxcmds;
458224056Sjonathan
459247602Spjd	AUDIT_ARG_FD(fd);
460247602Spjd
461247602Spjd	fdp = td->td_proc->p_fd;
462247602Spjd	FILEDESC_SLOCK(fdp);
463247602Spjd
464247602Spjd	if (fget_locked(fdp, fd) == NULL) {
465247602Spjd		error = EBADF;
466247602Spjd		goto out;
467247602Spjd	}
468247602Spjd
469247602Spjd	/*
470247602Spjd	 * If all ioctls are allowed (fde_nioctls == -1 && fde_ioctls == NULL)
471247602Spjd	 * the only sane thing we can do is to not populate the given array and
472247602Spjd	 * return CAP_IOCTLS_ALL.
473247602Spjd	 */
474247602Spjd
475247602Spjd	fdep = &fdp->fd_ofiles[fd];
476247602Spjd	if (cmds != NULL && fdep->fde_ioctls != NULL) {
477247602Spjd		error = copyout(fdep->fde_ioctls, cmds,
478247602Spjd		    sizeof(cmds[0]) * MIN(fdep->fde_nioctls, maxcmds));
479247602Spjd		if (error != 0)
480247602Spjd			goto out;
481247602Spjd	}
482247602Spjd	if (fdep->fde_nioctls == -1)
483247602Spjd		td->td_retval[0] = CAP_IOCTLS_ALL;
484247602Spjd	else
485247602Spjd		td->td_retval[0] = fdep->fde_nioctls;
486247602Spjd
487247602Spjd	error = 0;
488247602Spjdout:
489247602Spjd	FILEDESC_SUNLOCK(fdp);
490247602Spjd	return (error);
491224056Sjonathan}
492224056Sjonathan
493224056Sjonathan/*
494247602Spjd * Test whether a capability grants the given fcntl command.
495224056Sjonathan */
496247602Spjdint
497273137Smjgcap_fcntl_check_fde(struct filedescent *fde, int cmd)
498224056Sjonathan{
499247602Spjd	uint32_t fcntlcap;
500224056Sjonathan
501247602Spjd	fcntlcap = (1 << cmd);
502247602Spjd	KASSERT((CAP_FCNTL_ALL & fcntlcap) != 0,
503247602Spjd	    ("Unsupported fcntl=%d.", cmd));
504224056Sjonathan
505273137Smjg	if ((fde->fde_fcntls & fcntlcap) != 0)
506247602Spjd		return (0);
507247602Spjd
508247602Spjd	return (ENOTCAPABLE);
509224056Sjonathan}
510224056Sjonathan
511247602Spjdint
512273137Smjgcap_fcntl_check(struct filedesc *fdp, int fd, int cmd)
513273137Smjg{
514273137Smjg
515273137Smjg	KASSERT(fd >= 0 && fd < fdp->fd_nfiles,
516273137Smjg	    ("%s: invalid fd=%d", __func__, fd));
517273137Smjg
518273137Smjg	return (cap_fcntl_check_fde(&fdp->fd_ofiles[fd], cmd));
519273137Smjg}
520273137Smjg
521273137Smjgint
522247602Spjdsys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap)
523224056Sjonathan{
524247602Spjd	struct filedesc *fdp;
525247602Spjd	uint32_t fcntlrights;
526247602Spjd	int fd;
527224056Sjonathan
528247602Spjd	fd = uap->fd;
529247602Spjd	fcntlrights = uap->fcntlrights;
530224056Sjonathan
531247602Spjd	AUDIT_ARG_FD(fd);
532247602Spjd	AUDIT_ARG_FCNTL_RIGHTS(fcntlrights);
533224056Sjonathan
534247602Spjd	if ((fcntlrights & ~CAP_FCNTL_ALL) != 0)
535247602Spjd		return (EINVAL);
536224056Sjonathan
537247602Spjd	fdp = td->td_proc->p_fd;
538247602Spjd	FILEDESC_XLOCK(fdp);
539224056Sjonathan
540247602Spjd	if (fget_locked(fdp, fd) == NULL) {
541247602Spjd		FILEDESC_XUNLOCK(fdp);
542247602Spjd		return (EBADF);
543247602Spjd	}
544224056Sjonathan
545247602Spjd	if ((fcntlrights & ~fdp->fd_ofiles[fd].fde_fcntls) != 0) {
546247602Spjd		FILEDESC_XUNLOCK(fdp);
547247602Spjd		return (ENOTCAPABLE);
548247602Spjd	}
549224056Sjonathan
550247602Spjd	fdp->fd_ofiles[fd].fde_fcntls = fcntlrights;
551247602Spjd	FILEDESC_XUNLOCK(fdp);
552224056Sjonathan
553247602Spjd	return (0);
554224056Sjonathan}
555224056Sjonathan
556224914Skibint
557247602Spjdsys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap)
558224914Skib{
559247602Spjd	struct filedesc *fdp;
560247602Spjd	uint32_t rights;
561247602Spjd	int fd;
562224914Skib
563247602Spjd	fd = uap->fd;
564247602Spjd
565247602Spjd	AUDIT_ARG_FD(fd);
566247602Spjd
567247602Spjd	fdp = td->td_proc->p_fd;
568247602Spjd	FILEDESC_SLOCK(fdp);
569247602Spjd	if (fget_locked(fdp, fd) == NULL) {
570247602Spjd		FILEDESC_SUNLOCK(fdp);
571247602Spjd		return (EBADF);
572247602Spjd	}
573247602Spjd	rights = fdp->fd_ofiles[fd].fde_fcntls;
574247602Spjd	FILEDESC_SUNLOCK(fdp);
575247602Spjd
576247602Spjd	return (copyout(&rights, uap->fcntlrightsp, sizeof(rights)));
577224914Skib}
578224914Skib
579223762Sjonathan#else /* !CAPABILITIES */
580223762Sjonathan
581223762Sjonathan/*
582223762Sjonathan * Stub Capability functions for when options CAPABILITIES isn't compiled
583223762Sjonathan * into the kernel.
584223762Sjonathan */
585247602Spjd
586223762Sjonathanint
587247602Spjdsys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap)
588224066Sjonathan{
589224066Sjonathan
590224066Sjonathan	return (ENOSYS);
591224066Sjonathan}
592224066Sjonathan
593224066Sjonathanint
594255229Ssbrunosys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap)
595224066Sjonathan{
596224066Sjonathan
597224066Sjonathan	return (ENOSYS);
598224066Sjonathan}
599224066Sjonathan
600224066Sjonathanint
601247602Spjdsys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap)
602223762Sjonathan{
603223762Sjonathan
604247602Spjd	return (ENOSYS);
605247602Spjd}
606223762Sjonathan
607247602Spjdint
608247602Spjdsys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap)
609247602Spjd{
610247602Spjd
611247602Spjd	return (ENOSYS);
612223762Sjonathan}
613223762Sjonathan
614223762Sjonathanint
615247602Spjdsys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap)
616223762Sjonathan{
617223762Sjonathan
618247602Spjd	return (ENOSYS);
619247602Spjd}
620223762Sjonathan
621247602Spjdint
622247602Spjdsys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap)
623247602Spjd{
624247602Spjd
625247602Spjd	return (ENOSYS);
626223762Sjonathan}
627223762Sjonathan
628223762Sjonathan#endif /* CAPABILITIES */
629