Merge Capsicum overhaul:

- Capability is no longer separate descriptor type. Now every descriptor
  has set of its own capability rights.

- The cap_new(2) system call is left, but it is no longer documented and
  should not be used in new code.

- The new syscall cap_rights_limit(2) should be used instead of
  cap_new(2), which limits capability rights of the given descriptor
  without creating a new one.

- The cap_getrights(2) syscall is renamed to cap_rights_get(2).

- If CAP_IOCTL capability right is present we can further reduce allowed
  ioctls list with the new cap_ioctls_limit(2) syscall. List of allowed
  ioctls can be retrived with cap_ioctls_get(2) syscall.

- If CAP_FCNTL capability right is present we can further reduce fcntls
  that can be used with the new cap_fcntls_limit(2) syscall and retrive
  them with cap_fcntls_get(2).

- To support ioctl and fcntl white-listing the filedesc structure was
  heavly modified.

- The audit subsystem, kdump and procstat tools were updated to
  recognize new syscalls.

- Capability rights were revised and eventhough I tried hard to provide
  backward API and ABI compatibility there are some incompatible changes
  that are described in detail below:

	CAP_CREATE old behaviour:
	- Allow for openat(2)+O_CREAT.
	- Allow for linkat(2).
	- Allow for symlinkat(2).
	CAP_CREATE new behaviour:
	- Allow for openat(2)+O_CREAT.

	Added CAP_LINKAT:
	- Allow for linkat(2). ABI: Reuses CAP_RMDIR bit.
	- Allow to be target for renameat(2).

	Added CAP_SYMLINKAT:
	- Allow for symlinkat(2).

	Removed CAP_DELETE. Old behaviour:
	- Allow for unlinkat(2) when removing non-directory object.
	- Allow to be source for renameat(2).

	Removed CAP_RMDIR. Old behaviour:
	- Allow for unlinkat(2) when removing directory.

	Added CAP_RENAMEAT:
	- Required for source directory for the renameat(2) syscall.

	Added CAP_UNLINKAT (effectively it replaces CAP_DELETE and CAP_RMDIR):
	- Allow for unlinkat(2) on any object.
	- Required if target of renameat(2) exists and will be removed by this
	  call.

	Removed CAP_MAPEXEC.

	CAP_MMAP old behaviour:
	- Allow for mmap(2) with any combination of PROT_NONE, PROT_READ and
	  PROT_WRITE.
	CAP_MMAP new behaviour:
	- Allow for mmap(2)+PROT_NONE.

	Added CAP_MMAP_R:
	- Allow for mmap(PROT_READ).
	Added CAP_MMAP_W:
	- Allow for mmap(PROT_WRITE).
	Added CAP_MMAP_X:
	- Allow for mmap(PROT_EXEC).
	Added CAP_MMAP_RW:
	- Allow for mmap(PROT_READ | PROT_WRITE).
	Added CAP_MMAP_RX:
	- Allow for mmap(PROT_READ | PROT_EXEC).
	Added CAP_MMAP_WX:
	- Allow for mmap(PROT_WRITE | PROT_EXEC).
	Added CAP_MMAP_RWX:
	- Allow for mmap(PROT_READ | PROT_WRITE | PROT_EXEC).

	Renamed CAP_MKDIR to CAP_MKDIRAT.
	Renamed CAP_MKFIFO to CAP_MKFIFOAT.
	Renamed CAP_MKNODE to CAP_MKNODEAT.

	CAP_READ old behaviour:
	- Allow pread(2).
	- Disallow read(2), readv(2) (if there is no CAP_SEEK).
	CAP_READ new behaviour:
	- Allow read(2), readv(2).
	- Disallow pread(2) (CAP_SEEK was also required).

	CAP_WRITE old behaviour:
	- Allow pwrite(2).
	- Disallow write(2), writev(2) (if there is no CAP_SEEK).
	CAP_WRITE new behaviour:
	- Allow write(2), writev(2).
	- Disallow pwrite(2) (CAP_SEEK was also required).

	Added convinient defines:

	#define	CAP_PREAD		(CAP_SEEK | CAP_READ)
	#define	CAP_PWRITE		(CAP_SEEK | CAP_WRITE)
	#define	CAP_MMAP_R		(CAP_MMAP | CAP_SEEK | CAP_READ)
	#define	CAP_MMAP_W		(CAP_MMAP | CAP_SEEK | CAP_WRITE)
	#define	CAP_MMAP_X		(CAP_MMAP | CAP_SEEK | 0x0000000000000008ULL)
	#define	CAP_MMAP_RW		(CAP_MMAP_R | CAP_MMAP_W)
	#define	CAP_MMAP_RX		(CAP_MMAP_R | CAP_MMAP_X)
	#define	CAP_MMAP_WX		(CAP_MMAP_W | CAP_MMAP_X)
	#define	CAP_MMAP_RWX		(CAP_MMAP_R | CAP_MMAP_W | CAP_MMAP_X)
	#define	CAP_RECV		CAP_READ
	#define	CAP_SEND		CAP_WRITE

	#define	CAP_SOCK_CLIENT \
		(CAP_CONNECT | CAP_GETPEERNAME | CAP_GETSOCKNAME | CAP_GETSOCKOPT | \
		 CAP_PEELOFF | CAP_RECV | CAP_SEND | CAP_SETSOCKOPT | CAP_SHUTDOWN)
	#define	CAP_SOCK_SERVER \
		(CAP_ACCEPT | CAP_BIND | CAP_GETPEERNAME | CAP_GETSOCKNAME | \
		 CAP_GETSOCKOPT | CAP_LISTEN | CAP_PEELOFF | CAP_RECV | CAP_SEND | \
		 CAP_SETSOCKOPT | CAP_SHUTDOWN)

	Added defines for backward API compatibility:

	#define	CAP_MAPEXEC		CAP_MMAP_X
	#define	CAP_DELETE		CAP_UNLINKAT
	#define	CAP_MKDIR		CAP_MKDIRAT
	#define	CAP_RMDIR		CAP_UNLINKAT
	#define	CAP_MKFIFO		CAP_MKFIFOAT
	#define	CAP_MKNOD		CAP_MKNODAT
	#define	CAP_SOCK_ALL		(CAP_SOCK_CLIENT | CAP_SOCK_SERVER)

Sponsored by:	The FreeBSD Foundation
Reviewed by:	Christoph Mallon <christoph.mallon@gmx.de>
Many aspects discussed with:	rwatson, benl, jonathan
ABI compatibility discussed with:	kib
This commit is contained in:
Pawel Jakub Dawidek 2013-03-02 00:53:12 +00:00
parent 11be09b056
commit 2609222ab4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=247602
61 changed files with 1887 additions and 1171 deletions

View file

@ -548,7 +548,7 @@
43184:AUE_OPENAT:openat(2) - attr only:fa
43185:AUE_POSIX_OPENPT:posix_openpt(2):ip
43186:AUE_CAP_NEW:cap_new(2):fm
43187:AUE_CAP_GETRIGHTS:cap_getrights(2):fm
43187:AUE_CAP_RIGHTS_GET:cap_rights_get(2):fm
43188:AUE_CAP_ENTER:cap_enter(2):pc
43189:AUE_CAP_GETMODE:cap_getmode(2):pc
43190:AUE_POSIX_SPAWN:posix_spawn(2):pc
@ -563,6 +563,11 @@
43199:AUE_PDGETPID:pdgetpid(2):pc
43200:AUE_PDWAIT:pdwait(2):pc
43201:AUE_WAIT6:wait6(2):pc
43202:AUE_CAP_RIGHTS_LIMIT:cap_rights_limit(2):fm
43203:AUE_CAP_IOCTLS_LIMIT:cap_ioctls_limit(2):fm
43204:AUE_CAP_IOCTLS_GET:cap_ioctls_get(2):fm
43205:AUE_CAP_FCNTLS_LIMIT:cap_fcntls_limit(2):fm
43206:AUE_CAP_FCNTLS_GET:cap_fcntls_get(2):fm
#
# Solaris userspace events.
#

View file

@ -42,6 +42,8 @@ __sym_compat(__semctl, freebsd7___semctl, FBSD_1.0);
__sym_compat(msgctl, freebsd7_msgctl, FBSD_1.0);
__sym_compat(shmctl, freebsd7_shmctl, FBSD_1.0);
__sym_compat(cap_getrights, cap_rights_get, FBSD_1.2);
#undef __sym_compat
#endif /* __LIBC_COMPAT_H__ */

View file

@ -93,7 +93,9 @@ MAN+= abort2.2 \
bind.2 \
brk.2 \
cap_enter.2 \
cap_new.2 \
cap_fcntls_limit.2 \
cap_ioctls_limit.2 \
cap_rights_limit.2 \
chdir.2 \
chflags.2 \
chmod.2 \
@ -270,7 +272,9 @@ MLINKS+=access.2 eaccess.2 \
access.2 faccessat.2
MLINKS+=brk.2 sbrk.2
MLINKS+=cap_enter.2 cap_getmode.2
MLINKS+=cap_new.2 cap_getrights.2
MLINKS+=cap_fcntls_limit.2 cap_fcntls_get.2
MLINKS+=cap_ioctls_limit.2 cap_ioctls_get.2
MLINKS+=cap_rights_limit.2 cap_rights_get.2
MLINKS+=chdir.2 fchdir.2
MLINKS+=chflags.2 fchflags.2 \
chflags.2 lchflags.2

View file

@ -364,7 +364,6 @@ FBSD_1.2 {
cap_enter;
cap_getmode;
cap_new;
cap_getrights;
getloginclass;
pdfork;
pdgetpid;
@ -379,6 +378,12 @@ FBSD_1.2 {
};
FBSD_1.3 {
cap_fcntls_get;
cap_fcntls_limit;
cap_ioctls_get;
cap_ioctls_limit;
cap_rights_get;
cap_rights_limit;
cap_sandboxed;
clock_getcpuclockid2;
ffclock_getcounter;

View file

@ -58,8 +58,10 @@ or
.Xr pdfork 2
will be placed in capability mode from inception.
.Pp
When combined with capabilities created with
.Xr cap_new 2 ,
When combined with
.Xr cap_rights_limit 2 ,
.Xr cap_ioctls_limit 2 ,
.Xr cap_fcntls_limit 2 ,
.Fn cap_enter
may be used to create kernel-enforced sandboxes in which
appropriately-crafted applications or application components may be run.
@ -71,11 +73,6 @@ sandbox.
Creating effective process sandboxes is a tricky process that involves
identifying the least possible rights required by the process and then
passing those rights into the process in a safe manner.
See the CAVEAT
section of
.Xr cap_new 2
for why this is particularly tricky with UNIX file descriptors as the
canonical representation of a right.
Consumers of
.Fn cap_enter
should also be aware of other inherited rights, such as access to VM
@ -87,8 +84,33 @@ to create a runtime environment inside the sandbox that has as few implicitly
acquired rights as possible.
.Sh RETURN VALUES
.Rv -std cap_enter cap_getmode
.Sh ERRORS
The
.Fn cap_enter
and
.Fn cap_getmode
system calls
will fail if:
.Bl -tag -width Er
.It Bq Er ENOSYS
The kernel is compiled without:
.Pp
.Cd "options CAPABILITY_MODE"
.El
.Pp
The
.Fn cap_getmode
system call may also return the following error:
.Bl -tag -width Er
.It Bq Er EFAULT
Pointer
.Fa modep
points outside the process's allocated address space.
.El
.Sh SEE ALSO
.Xr cap_new 2 ,
.Xr cap_fcntls_limit 2 ,
.Xr cap_ioctls_limit 2 ,
.Xr cap_rights_limit 2 ,
.Xr fexecve 2 ,
.Xr cap_sandboxed 3 ,
.Xr capsicum 4

View file

@ -0,0 +1,127 @@
.\"
.\" Copyright (c) 2012 The FreeBSD Foundation
.\" All rights reserved.
.\"
.\" This documentation was written by Pawel Jakub Dawidek under sponsorship
.\" the FreeBSD Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd September 20, 2012
.Dt CAP_FCNTLS_LIMIT 2
.Os
.Sh NAME
.Nm cap_fcntls_limit ,
.Nm cap_fcntls_get
.Nd manage allowed fcntl commands
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
.In sys/capability.h
.Ft int
.Fn cap_fcntls_limit "int fd" "uint32_t fcntlrights"
.Ft int
.Fn cap_fcntls_get "int fd" "uint32_t *fcntlrightsp"
.Sh DESCRIPTION
If a file descriptor is granted the
.Dv CAP_FCNTL
capability right, the list of allowed
.Xr fcntl 2
commands can be selectively reduced (but never expanded) with the
.Fn cap_fcntls_limit
system call.
.Pp
A bitmask of allowed fcntls commands for a given file descriptor can be obtained
with the
.Fn cap_fcntls_get
system call.
.Sh FLAGS
The following flags may be specified in the
.Fa fcntlrights
argument or returned in the
.Fa fcntlrightsp
argument:
.Bl -tag -width CAP_FCNTL_GETOWN
.It Dv CAP_FCNTL_GETFL
Permit
.Dv F_GETFL
command.
.It Dv CAP_FCNTL_SETFL
Permit
.Dv F_SETFL
command.
.It Dv CAP_FCNTL_GETOWN
Permit
.Dv F_GETOWN
command.
.It Dv CAP_FCNTL_SETOWN
Permit
.Dv F_SETOWN
command.
.El
.Sh RETURN VALUES
.Rv -std
.Sh ERRORS
.Fn cap_fcntls_limit
succeeds unless:
.Bl -tag -width Er
.It Bq Er EBADF
The
.Fa fd
argument is not a valid descriptor.
.It Bq Er EINVAL
An invalid flag has been passed in
.Fa fcntlrights .
.It Bq Er ENOTCAPABLE
.Fa fcntlrights
would expand the list of allowed
.Xr fcntl 2
commands.
.El
.Pp
.Fn cap_fcntls_get
succeeds unless:
.Bl -tag -width Er
.It Bq Er EBADF
The
.Fa fd
argument is not a valid descriptor.
.It Bq Er EFAULT
The
.Fa fcntlrightsp
argument points at an invalid address.
.El
.Sh SEE ALSO
.Xr cap_ioctls_limit 2 ,
.Xr cap_rights_limit 2 ,
.Xr fcntl 2
.Sh HISTORY
Support for capabilities and capabilities mode was developed as part of the
.Tn TrustedBSD
Project.
.Pp
.Sh AUTHORS
This function was created by
.An Pawel Jakub Dawidek Aq pawel@dawidek.net
under sponsorship of the FreeBSD Foundation.

View file

@ -0,0 +1,158 @@
.\"
.\" Copyright (c) 2012 The FreeBSD Foundation
.\" All rights reserved.
.\"
.\" This documentation was written by Pawel Jakub Dawidek under sponsorship
.\" the FreeBSD Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd September 20, 2012
.Dt CAP_IOCTLS_LIMIT 2
.Os
.Sh NAME
.Nm cap_ioctls_limit ,
.Nm cap_ioctls_get
.Nd manage allowed ioctl commands
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
.In sys/capability.h
.Ft int
.Fn cap_ioctls_limit "int fd" "const unsigned long *cmds" "size_t ncmds"
.Ft ssize_t
.Fn cap_ioctls_get "int fd" "unsigned long *cmds" "size_t maxcmds"
.Sh DESCRIPTION
If a file descriptor is granted the
.Dv CAP_IOCTL
capability right, the list of allowed
.Xr ioctl 2
commands can be selectively reduced (but never expanded) with the
.Fn cap_ioctls_limit
system call.
The
.Fa cmds
argument is an array of
.Xr ioctl 2
commands and the
.Fa ncmds
argument specifies the number of elements in the array.
There might be up to
.Va 256
elements in the array.
.Pp
The list of allowed ioctl commands for a given file descriptor can be obtained
with the
.Fn cap_ioctls_get
system call.
The
.Fa cmds
argument points at memory that can hold up to
.Fa maxcmds
values.
The function populates the provided buffer with up to
.Fa maxcmds
elements, but always returns the total number of ioctl commands allowed for the
given file descriptor.
The total number of ioctls commands for the given file descriptor can be
obtained by passing
.Dv NULL as the
.Fa cmds
argument and
.Va 0
as the
.Fa maxcmds
argument.
If all ioctl commands are allowed
.Dv ( CAP_IOCTL
capability right is assigned to the file descriptor and the
.Fn cap_ioctls_limit
system call was never called for this file descriptor), the
.Fn cap_ioctls_get
system call will return
.Dv CAP_IOCTLS_ALL
and won't modify the buffer pointed out by the
.Fa cmds
argument.
.Sh RETURN VALUES
.Rv -std cap_ioctls_limit
.Pp
The
.Fn cap_ioctls_limit
function, if successfull, returns the total number of allowed ioctl commands or
the value
.Dv INT_MAX
if all ioctls commands are allowed.
On failure the value
.Va -1
is returned and the global variable errno is set to indicate the error.
.Sh ERRORS
.Fn cap_ioctls_limit
succeeds unless:
.Bl -tag -width Er
.It Bq Er EBADF
The
.Fa fd
argument is not a valid descriptor.
.It Bq Er EFAULT
The
.Fa cmds
argument points at an invalid address.
.It Bq Er EINVAL
The
.Fa ncmds
argument is greater than
.Va 256 .
.It Bq Er ENOTCAPABLE
.Fa cmds
would expand the list of allowed
.Xr ioctl 2
commands.
.El
.Pp
.Fn cap_ioctls_get
succeeds unless:
.Bl -tag -width Er
.It Bq Er EBADF
The
.Fa fd
argument is not a valid descriptor.
.It Bq Er EFAULT
The
.Fa cmds
argument points at invalid address.
.El
.Sh SEE ALSO
.Xr cap_fcntls_limit 2 ,
.Xr cap_rights_limit 2 ,
.Xr ioctl 2
.Sh HISTORY
Support for capabilities and capabilities mode was developed as part of the
.Tn TrustedBSD
Project.
.Pp
.Sh AUTHORS
This function was created by
.An Pawel Jakub Dawidek Aq pawel@dawidek.net
under sponsorship of the FreeBSD Foundation.

View file

@ -1,10 +1,14 @@
.\"
.\" Copyright (c) 2008-2010 Robert N. M. Watson
.\" Copyright (c) 2012-2013 The FreeBSD Foundation
.\" All rights reserved.
.\"
.\" This software was developed at the University of Cambridge Computer
.\" Laboratory with support from a grant from Google, Inc.
.\"
.\" Portions of this documentation were written by Pawel Jakub Dawidek
.\" under sponsorship from the FreeBSD Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
@ -28,76 +32,48 @@
.\"
.\" $FreeBSD$
.\"
.Dd July 20, 2011
.Dt CAP_NEW 2
.Dd February 23, 2013
.Dt CAP_RIGHTS_LIMIT 2
.Os
.Sh NAME
.Nm cap_new ,
.Nm cap_getrights
.Nd System calls to manipulate capabilities
.Nm cap_rights_limit ,
.Nm cap_rights_get
.Nd manage capability rights
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
.In sys/capability.h
.Ft int
.Fn cap_new "int fd" "cap_rights_t rights"
.Fn cap_rights_limit "int fd" "cap_rights_t rights"
.Ft int
.Fn cap_getrights "int fd" "cap_rights_t *rightsp"
.Fn cap_rights_get "int fd" "cap_rights_t *rightsp"
.Sh DESCRIPTION
Capabilities are special file descriptors derived from an existing file
descriptor, such as one returned by
When a file descriptor is created by a function such as
.Xr fhopen 2 ,
.Xr kqueue 2 ,
.Xr mq_open 2 ,
.Xr open 2 ,
.Xr openat 2 ,
.Xr pdfork 2 ,
.Xr pipe 2 ,
.Xr shm_open 2 ,
.Xr socket 2 ,
or
.Xr socketpair 2 ,
but with a restricted set of permitted operations determined by a rights
mask set when the capability is created.
These restricted rights cannot be changed after the capability is created,
although further capabilities with yet more restricted rights may be created
from an existing capability.
In every other sense, a capability behaves in the same way as the file
descriptor it was created from.
it is assigned all capability rights.
Those rights can be reduced (but never expanded) by using the
.Fn cap_rights_limit
system call.
Once capability rights are reduced, operations on the file descriptor will be
limited to those permitted by
.Fa rights .
.Pp
.Fn cap_new
creates a new capability for the existing file descriptor
.Fa fd ,
and returns a file descriptor for it.
Operations on the capability will be limited to those permitted by
.Fa rights ,
which is static for the lifetime of the capability.
If
.Fa fd
refers to an existing capability, then
.Fa rights
must be equal to or a subset of the rights on that capability.
As with
.Xr dup 2
and
.Xr dup2 2 ,
many properties are shared between the new capability and the existing file
descriptor, including open file flags, blocking disposition, and file offset.
Many applications will prefer to use the
.Xr cap_limitfd 3
library call, part of
.Xr libcapsicum 3 ,
as it offers a more convenient interface.
.Pp
.Fn cap_getrights
queries the rights associated with the capability referred to by file
descriptor
.Fa fd .
.Pp
These system calls, when combined with
.Xr cap_enter 2 ,
may be used to construct process sandboxes with highly granular rights
assignment.
A bitmask of capability rights assigned to a file descriptor can be obtained with
the
.Fn cap_rights_get
system call.
.Sh RIGHTS
The following rights may be specified in a new capability rights mask:
The following rights may be specified in a rights mask:
.Bl -tag -width CAP_EXTATTR_DELETE
.It Dv CAP_ACCEPT
Permit
@ -134,6 +110,13 @@ Permit
also required for
.Xr sendto 2
with a non-NULL destination address.
.It Dv CAP_CREATE
Permit
.Xr openat 2
with the
.Dv O_CREAT
flag.
.\" XXXPJD: Doesn't exist anymore.
.It Dv CAP_EVENT
Permit
.Xr select 2 ,
@ -143,7 +126,12 @@ and
to be used in monitoring the file descriptor for events.
.It Dv CAP_FEXECVE
Permit
.Xr fexecve 2 ;
.Xr fexecve 2
and
.Xr openat 2
with the
.Dv O_EXEC
flag;
.Dv CAP_READ
will also be required.
.It Dv CAP_EXTATTR_DELETE
@ -166,19 +154,49 @@ Permit
.Xr fchflags 2 .
.It Dv CAP_FCHMOD
Permit
.Xr fchmod 2 .
.Xr fchmod 2
and
.Xr fchmodat 2 .
.It Dv CAP_FCHMODAT
An alias to
.Dv CAP_FCHMOD .
.It Dv CAP_FCHOWN
Permit
.Xr fchown 2 .
.Xr fchown 2
and
.Xr fchownat 2 .
.It Dv CAP_FCHOWNAT
An alias to
.Dv CAP_FCHOWN .
.It Dv CAP_FCNTL
Permit
.Xr fcntl 2 ;
be aware that this call provides indirect access to other operations, such as
.Xr flock 2 .
.Xr fcntl 2 .
Note that only the
.Dv F_GETFL ,
.Dv F_SETFL ,
.Dv F_GETOWN
and
.Dv F_SETOWN
commands require this capability right.
Also note that the list of permitted commands can be further limited with the
.Xr cap_fcntls_limit 2
system call.
.It Dv CAP_FLOCK
Permit
.Xr flock 2
and related calls.
.Xr flock 2 ,
.Xr fcntl 2
(with
.Dv F_GETLK ,
.Dv F_SETLK
or
.Dv F_SETLKW
flag) and
.Xr openat 2
(with
.Dv O_EXLOCK
or
.Dv O_SHLOCK
flag).
.It Dv CAP_FPATHCONF
Permit
.Xr fpathconf 2 .
@ -186,22 +204,42 @@ Permit
Permit UFS background-fsck operations on the descriptor.
.It Dv CAP_FSTAT
Permit
.Xr fstat 2 .
.Xr fstat 2
and
.Xr fstatat 2 .
.It Dv CAP_FSTATAT
An alias to
.Dv CAP_FSTAT .
.It Dv CAP_FSTATFS
Permit
.Xr fstatfs 2 .
.It Dv CAP_FSYNC
Permit
.Xr aio_fsync 2
.Xr aio_fsync 2 ,
.Xr fsync 2
and
.Xr fsync 2 .
.Pp
.Xr openat 2
with
.Dv O_FSYNC
or
.Dv O_SYNC
flag.
.It Dv CAP_FTRUNCATE
Permit
.Xr ftruncate 2 .
.Xr ftruncate 2
and
.Xr openat 2
with the
.Dv O_TRUNC
flag.
.It Dv CAP_FUTIMES
Permit
.Xr futimes 2 .
.Xr futimes 2
and
.Xr futimesat 2 .
.It Dv CAP_FUTIMESAT
An alias to
.Dv CAP_FUTIMES .
.It Dv CAP_GETPEERNAME
Permit
.Xr getpeername 2 .
@ -216,42 +254,106 @@ Permit
.Xr ioctl 2 .
Be aware that this system call has enormous scope, including potentially
global scope for some objects.
The list of permitted ioctl commands can be further limited with the
.Xr cap_ioctls_limit 2
system call.
.\" XXXPJD: Doesn't exist anymore.
.It Dv CAP_KEVENT
Permit
.Xr kevent 2 ;
.Dv CAP_EVENT
is also required on file descriptors that will be monitored using
.Xr kevent 2 .
.It Dv CAP_LINKAT
Permit
.Xr linkat 2
and
.Xr renameat 2 .
This right is required for the destination directory descriptor.
.It Dv CAP_LISTEN
Permit
.Xr listen 2 ;
not much use (generally) without
.Dv CAP_BIND .
.It Dv CAP_LOOKUP
Permit the file descriptor to be used as a starting directory for calls such
as
Permit the file descriptor to be used as a starting directory for calls such as
.Xr linkat 2 ,
.Xr openat 2 ,
and
.Xr unlinkat 2 .
Note that these calls are not available in capability mode as they manipulate
a global name space; see
.Xr cap_enter 2
for details.
.It Dv CAP_MAC_GET
Permit
.Xr mac_get_fd 3 .
.It Dv CAP_MAC_SET
Permit
.Xr mac_set_fd 3 .
.It Dv CAP_MKDIRAT
Permit
.Xr mkdirat 2 .
.It Dv CAP_MKFIFOAT
Permit
.Xr mkfifoat 2 .
.It Dv CAP_MKNODAT
Permit
.Xr mknodat 2 .
.It Dv CAP_MMAP
Permit
.Xr mmap 2 ;
specific invocations may also require
.Xr mmap 2
with the
.Dv PROT_NONE
protection.
.It Dv CAP_MMAP_R
Permit
.Xr mmap 2
with the
.Dv PROT_READ
protection.
This also implies
.Dv CAP_READ
or
.Dv CAP_WRITE .
.Pp
and
.Dv CAP_SEEK
rights.
.It Dv CAP_MMAP_W
Permit
.Xr mmap 2
with the
.Dv PROT_WRITE
protection.
This also implies
.Dv CAP_WRITE
and
.Dv CAP_SEEK
rights.
.It Dv CAP_MMAP_X
Permit
.Xr mmap 2
with the
.Dv PROT_EXEC
protection.
This also implies
.Dv CAP_SEEK
right.
.It Dv CAP_MMAP_RW
Implies
.Dv CAP_MMAP_R
and
.Dv CAP_MMAP_W .
.It Dv CAP_MMAP_RX
Implies
.Dv CAP_MMAP_R
and
.Dv CAP_MMAP_X .
.It Dv CAP_MMAP_WX
Implies
.Dv CAP_MMAP_W
and
.Dv CAP_MMAP_X .
.It Dv CAP_MMAP_RWX
Implies
.Dv CAP_MMAP_R ,
.Dv CAP_MMAP_W
and
.Dv CAP_MMAP_X .
.It Dv CAP_PDGETPID
Permit
.Xr pdgetpid 2 .
@ -264,30 +366,46 @@ Permit
.It Dv CAP_PEELOFF
Permit
.Xr sctp_peeloff 2 .
.\" XXXPJD: Not documented.
.It Dv CAP_POLL_EVENT
.\" XXXPJD: Not documented.
.It Dv CAP_POST_EVENT
.It Dv CAP_PREAD
Implies
.Dv CAP_SEEK
and
.Dv CAP_READ .
.It Dv CAP_PWRITE
Implies
.Dv CAP_SEEK
and
.Dv CAP_WRITE .
.It Dv CAP_READ
Allow
.Xr aio_read 2 ,
.Xr pread 2 ,
.Xr openat
with the
.Dv O_RDONLY flag,
.Xr read 2 ,
.Xr recv 2 ,
.Xr recvfrom 2 ,
.Xr recvmsg 2 ,
.Xr recvmsg 2
and related system calls.
.Pp
For files and other seekable objects,
.Dv CAP_SEEK
may also be required.
.It Dv CAP_REVOKE
.It Dv CAP_RECV
An alias to
.Dv CAP_READ .
.It Dv CAP_RENAMEAT
Permit
.Xr frevoke 2
in certain ABI compatibility modes that support this system call.
.Xr renameat 2 .
This right is required for the source directory descriptor.
.It Dv CAP_SEEK
Permit operations that seek on the file descriptor, such as
.Xr lseek 2 ,
but also required for I/O system calls that modify the file offset, such as
.Xr read 2
but also required for I/O system calls that can read or write at any position
in the file, such as
.Xr pread 2
and
.Xr write 2 .
.Xr pwrite 2 .
.It Dv CAP_SEM_GETVALUE
Permit
.Xr sem_getvalue 3 .
@ -299,6 +417,9 @@ Permit
.Xr sem_wait 3
and
.Xr sem_trywait 3 .
.It Dv CAP_SEND
An alias to
.Dv CAP_WRITE .
.It Dv CAP_SETSOCKOPT
Permit
.Xr setsockopt 2 ;
@ -308,49 +429,56 @@ connecting, and other behaviors with global scope.
Permit explicit
.Xr shutdown 2 ;
closing the socket will also generally shut down any connections on it.
.It Dv CAP_SYMLINKAT
Permit
.Xr symlinkat 2 .
.It Dv CAP_TTYHOOK
Allow configuration of TTY hooks, such as
.Xr snp 4 ,
on the file descriptor.
.It Dv CAP_UNLINKAT
Permit
.Xr unlinkat 2
and
.Xr renameat 2 .
This right is only required for
.Xr renameat 2
on the destination directory descriptor if the destination object already
exists and will be removed by the rename.
.It Dv CAP_WRITE
Allow
.Xr aio_write 2 ,
.Xr pwrite 2 ,
.Xr openat 2
with
.Dv O_WRONLY
and
.Dv O_APPEND
flags,
.Xr send 2 ,
.Xr sendmsg 2 ,
.Xr sendto 2 ,
.Xr write 2 ,
and related system calls.
.Pp
For files and other seekable objects,
.Dv CAP_SEEK
may also be required.
.Pp
For
.Xr sendto 2
with a non-NULL connection address,
.Dv CAP_CONNECT
is also required.
For
.Xr openat 2
with the
.Dv O_WRONLY
flag, but without the
.Dv O_APPEND
flag,
.Dv CAP_SEEK
is also required.
.El
.Sh CAVEAT
The
.Fn cap_new
system call and the capabilities it creates may be used to assign
fine-grained rights to sandboxed processes running in capability mode.
However, the semantics of objects accessed via file descriptors are complex,
so caution should be exercised in passing object capabilities into sandboxes.
.Sh RETURN VALUES
If successful,
.Fn cap_new
returns a non-negative integer, termed a file descriptor.
It returns -1 on failure, and sets
.Va errno
to indicate the error.
.Pp
.Rv -std cap_getrights
.Rv -std
.Sh ERRORS
.Fn cap_new
may return the following errors:
.Fn cap_rights_limit
succeeds unless:
.Bl -tag -width Er
.It Bq Er EBADF
The
@ -359,29 +487,23 @@ argument is not a valid active descriptor.
.It Bq Er EINVAL
An invalid right has been requested in
.Fa rights .
.It Bq Er EMFILE
The process has already reached its limit for open file descriptors.
.It Bq Er ENFILE
The system file table is full.
.It Bq Er EPERM
.It Bq Er ENOTCAPABLE
.Fa rights
contains requested rights not present in the current rights mask associated
with the capability referenced by
.Fa fd ,
if any.
with the given file descriptor.
.El
.Pp
.Fn cap_getrights
may return the following errors:
.Fn cap_rights_get
succeeds unless:
.Bl -tag -width Er
.It Bq Er EBADF
The
.Fa fd
argument is not a valid active descriptor.
.It Bq Er EINVAL
.It Bq Er EFAULT
The
.Fa fd
argument is not a capability.
.Fa rightsp
argument points at an invalid address.
.El
.Sh SEE ALSO
.Xr accept 2 ,
@ -390,6 +512,9 @@ argument is not a capability.
.Xr aio_write 2 ,
.Xr bind 2 ,
.Xr cap_enter 2 ,
.Xr cap_fcntls_limit 2 ,
.Xr cap_ioctls_limit 2 ,
.Xr cap_rights_limit 2 ,
.Xr connect 2 ,
.Xr dup 2 ,
.Xr dup2 2 ,
@ -421,6 +546,7 @@ argument is not a capability.
.Xr mq_open 2 ,
.Xr open 2 ,
.Xr openat 2 ,
.Xr pdfork 2 ,
.Xr pdgetpid 2 ,
.Xr pdkill 2 ,
.Xr pdwait4 2 ,
@ -432,6 +558,7 @@ argument is not a capability.
.Xr recv 2 ,
.Xr recvfrom 2 ,
.Xr recvmsg 2 ,
.Xr renameat 2 ,
.Xr sctp_peeloff 2 ,
.Xr select 2 ,
.Xr send 2 ,
@ -442,6 +569,7 @@ argument is not a capability.
.Xr shutdown 2 ,
.Xr socket 2 ,
.Xr socketpair 2 ,
.Xr symlinkat 2 ,
.Xr unlinkat 2 ,
.Xr write 2 ,
.Xr acl_delete_fd_np 3 ,
@ -462,11 +590,11 @@ argument is not a capability.
Support for capabilities and capabilities mode was developed as part of the
.Tn TrustedBSD
Project.
.Pp
.Sh AUTHORS
These functions and the capability facility were created by
.An "Robert N. M. Watson"
at the University of Cambridge Computer Laboratory with support from a grant
from Google, Inc.
This function was created by
.An Pawel Jakub Dawidek Aq pawel@dawidek.net
under sponsorship of the FreeBSD Foundation.
.Sh BUGS
This man page should list the set of permitted system calls more specifically
for each capability right.

View file

@ -115,11 +115,6 @@ and
is a valid descriptor, then
.Fn dup2
is successful, and does nothing.
.Pp
The related
.Xr cap_new 2
system call allows file descriptors to be duplicated with restrictions on
their use.
.Sh RETURN VALUES
The value -1 is returned if an error occurs in either call.
The external variable
@ -152,7 +147,6 @@ argument is negative or exceeds the maximum allowable descriptor number
.El
.Sh SEE ALSO
.Xr accept 2 ,
.Xr cap_new 2 ,
.Xr close 2 ,
.Xr fcntl 2 ,
.Xr getdtablesize 2 ,

View file

@ -600,7 +600,6 @@ kinfo_fflags2fst(int kfflags)
} kfflags2fst[] = {
{ KF_FLAG_APPEND, PS_FST_FFLAG_APPEND },
{ KF_FLAG_ASYNC, PS_FST_FFLAG_ASYNC },
{ KF_FLAG_CAPABILITY, PS_FST_FFLAG_CAPABILITY },
{ KF_FLAG_CREAT, PS_FST_FFLAG_CREAT },
{ KF_FLAG_DIRECT, PS_FST_FFLAG_DIRECT },
{ KF_FLAG_EXCL, PS_FST_FFLAG_EXCL },

View file

@ -88,7 +88,6 @@
#define PS_FST_FFLAG_DIRECT 0x1000
#define PS_FST_FFLAG_EXEC 0x2000
#define PS_FST_FFLAG_HASLOCK 0x4000
#define PS_FST_FFLAG_CAPABILITY 0x8000
struct procstat;
struct filestat {

View file

@ -588,7 +588,7 @@
#define AUE_OPENAT 43184 /* FreeBSD. */
#define AUE_POSIX_OPENPT 43185 /* FreeBSD. */
#define AUE_CAP_NEW 43186 /* TrustedBSD. */
#define AUE_CAP_GETRIGHTS 43187 /* TrustedBSD. */
#define AUE_CAP_RIGHTS_GET 43187 /* TrustedBSD. */
#define AUE_CAP_ENTER 43188 /* TrustedBSD. */
#define AUE_CAP_GETMODE 43189 /* TrustedBSD. */
#define AUE_POSIX_SPAWN 43190 /* Darwin. */
@ -603,6 +603,11 @@
#define AUE_PDGETPID 43199 /* FreeBSD. */
#define AUE_PDWAIT 43200 /* FreeBSD. */
#define AUE_WAIT6 43201 /* FreeBSD. */
#define AUE_CAP_RIGHTS_LIMIT 43202 /* TrustedBSD. */
#define AUE_CAP_IOCTLS_LIMIT 43203 /* TrustedBSD. */
#define AUE_CAP_IOCTLS_GET 43204 /* TrustedBSD. */
#define AUE_CAP_FCNTLS_LIMIT 43205 /* TrustedBSD. */
#define AUE_CAP_FCNTLS_GET 43206 /* TrustedBSD. */
/*
* Darwin BSM uses a number of AUE_O_* definitions, which are aliased to the

View file

@ -39,15 +39,11 @@ typedef struct file file_t;
#include <sys/capability.h>
static __inline file_t *
getf(int fd)
getf(int fd, cap_rights_t rights)
{
struct file *fp;
/*
* We wouldn't need all of these rights on every invocation
* if we had more information about intent.
*/
if (fget(curthread, fd, CAP_READ | CAP_WRITE | CAP_SEEK, &fp) == 0)
if (fget(curthread, fd, rights, &fp) == 0)
return (fp);
return (NULL);
}

View file

@ -3822,7 +3822,7 @@ zfs_ioc_recv(zfs_cmd_t *zc)
return (error);
fd = zc->zc_cookie;
fp = getf(fd);
fp = getf(fd, CAP_PREAD);
if (fp == NULL) {
nvlist_free(props);
return (EBADF);
@ -4079,7 +4079,7 @@ zfs_ioc_send(zfs_cmd_t *zc)
error = dmu_send_estimate(tosnap, fromsnap, zc->zc_obj,
&zc->zc_objset_type);
} else {
file_t *fp = getf(zc->zc_cookie);
file_t *fp = getf(zc->zc_cookie, CAP_WRITE);
if (fp == NULL) {
dsl_dataset_rele(ds, FTAG);
if (dsfrom)
@ -4675,7 +4675,7 @@ zfs_ioc_diff(zfs_cmd_t *zc)
return (error);
}
fp = getf(zc->zc_cookie);
fp = getf(zc->zc_cookie, CAP_WRITE);
if (fp == NULL) {
dmu_objset_rele(fromsnap, FTAG);
dmu_objset_rele(tosnap, FTAG);

View file

@ -124,7 +124,7 @@ zfs_onexit_fd_hold(int fd, minor_t *minorp)
void *data;
int error;
fp = getf(fd);
fp = getf(fd, CAP_NONE);
if (fp == NULL)
return (EBADF);

View file

@ -963,7 +963,7 @@
struct shmid_ds32 *buf); }
513 AUE_LPATHCONF NOPROTO { int lpathconf(char *path, int name); }
514 AUE_CAP_NEW NOPROTO { int cap_new(int fd, uint64_t rights); }
515 AUE_CAP_GETRIGHTS NOPROTO { int cap_getrights(int fd, \
515 AUE_CAP_RIGHTS_GET NOPROTO { int cap_rights_get(int fd, \
uint64_t *rightsp); }
516 AUE_CAP_ENTER NOPROTO { int cap_enter(void); }
517 AUE_CAP_GETMODE NOPROTO { int cap_getmode(u_int *modep); }
@ -1005,3 +1005,13 @@
struct wrusage32 *wrusage, \
siginfo_t *info); }
533 AUE_CAP_RIGHTS_LIMIT NOPROTO { int cap_rights_limit(int fd, \
uint64_t rights); }
534 AUE_CAP_IOCTLS_LIMIT NOPROTO { int cap_ioctls_limit(int fd, \
const u_long *cmds, size_t ncmds); }
535 AUE_CAP_IOCTLS_GET NOPROTO { ssize_t cap_ioctls_get(int fd, \
u_long *cmds, size_t maxcmds); }
536 AUE_CAP_FCNTLS_LIMIT NOPROTO { int cap_fcntls_limit(int fd, \
uint32_t fcntlrights); }
537 AUE_CAP_FCNTLS_GET NOPROTO { int cap_fcntls_get(int fd, \
uint32_t *fcntlrightsp); }

View file

@ -154,6 +154,7 @@ linux_common_open(struct thread *td, int dirfd, char *path, int l_flags, int mod
SESS_LEADER(p) && !(p->p_flag & P_CONTROLT)) {
PROC_UNLOCK(p);
sx_unlock(&proctree_lock);
/* XXXPJD: Verify if TIOCSCTTY is allowed. */
if (fp->f_type == DTYPE_VNODE)
(void) fo_ioctl(fp, TIOCSCTTY, (caddr_t) 0,
td->td_ucred, td);
@ -1038,11 +1039,11 @@ linux_pread(td, uap)
error = sys_pread(td, &bsd);
if (error == 0) {
/* This seems to violate POSIX but linux does it */
if ((error = fgetvp(td, uap->fd, CAP_READ, &vp)) != 0)
return (error);
/* This seems to violate POSIX but linux does it */
if ((error = fgetvp(td, uap->fd, CAP_PREAD, &vp)) != 0)
return (error);
if (vp->v_type == VDIR) {
vrele(vp);
vrele(vp);
return (EISDIR);
}
vrele(vp);

View file

@ -265,14 +265,14 @@ fd_revoke(td, fd)
/*
* If we ever want to support Capsicum on SVR4 processes (unlikely)
* or FreeBSD grows a native frevoke() (more likely), we will need a
* CAP_REVOKE here.
* CAP_FREVOKE here.
*
* In the meantime, use CAP_MASK_VALID: if a SVR4 process wants to
* In the meantime, use CAP_ALL: if a SVR4 process wants to
* do an frevoke(), it needs to do it on either a regular file
* descriptor or a fully-privileged capability (which is effectively
* the same as a non-capability-restricted file descriptor).
*/
if ((error = fgetvp(td, fd, CAP_MASK_VALID, &vp)) != 0)
if ((error = fgetvp(td, fd, CAP_ALL, &vp)) != 0)
return (error);
if (vp->v_type != VCHR && vp->v_type != VBLK) {

View file

@ -197,22 +197,24 @@ svr4_fil_ioctl(fp, td, retval, fd, cmd, data)
u_long cmd;
caddr_t data;
{
int error;
int num;
struct filedesc *fdp = td->td_proc->p_fd;
struct filedescent *fde;
int error, num;
*retval = 0;
switch (cmd) {
case SVR4_FIOCLEX:
FILEDESC_XLOCK(fdp);
fdp->fd_ofileflags[fd] |= UF_EXCLOSE;
fde = &fdp->fd_ofiles[fd];
fde->fde_flags |= UF_EXCLOSE;
FILEDESC_XUNLOCK(fdp);
return 0;
case SVR4_FIONCLEX:
FILEDESC_XLOCK(fdp);
fdp->fd_ofileflags[fd] &= ~UF_EXCLOSE;
fde = &fdp->fd_ofiles[fd];
fde->fde_flags &= ~UF_EXCLOSE;
FILEDESC_XUNLOCK(fdp);
return 0;

View file

@ -247,10 +247,8 @@ svr4_sys_getdents64(td, uap)
DPRINTF(("svr4_sys_getdents64(%d, *, %d)\n",
uap->fd, uap->nbytes));
if ((error = getvnode(td->td_proc->p_fd, uap->fd,
CAP_READ | CAP_SEEK, &fp)) != 0) {
if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_READ, &fp)) != 0)
return (error);
}
if ((fp->f_flag & FREAD) == 0) {
fdrop(fp, td);
@ -426,8 +424,7 @@ svr4_sys_getdents(td, uap)
if (uap->nbytes < 0)
return (EINVAL);
if ((error = getvnode(td->td_proc->p_fd, uap->fd,
CAP_READ | CAP_SEEK, &fp)) != 0)
if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_READ, &fp)) != 0)
return (error);
if ((fp->f_flag & FREAD) == 0) {

View file

@ -1449,7 +1449,7 @@ svr4_sys_putmsg(td, uap)
struct file *fp;
int error;
if ((error = fget(td, uap->fd, CAP_WRITE, &fp)) != 0) {
if ((error = fget(td, uap->fd, CAP_SEND, &fp)) != 0) {
#ifdef DEBUG_SVR4
uprintf("putmsg: bad fp\n");
#endif
@ -1621,7 +1621,7 @@ svr4_sys_getmsg(td, uap)
struct file *fp;
int error;
if ((error = fget(td, uap->fd, CAP_READ, &fp)) != 0) {
if ((error = fget(td, uap->fd, CAP_RECV, &fp)) != 0) {
#ifdef DEBUG_SVR4
uprintf("getmsg: bad fp\n");
#endif

View file

@ -387,11 +387,11 @@ i_setsoc(isc_session_t *sp, int fd, struct thread *td)
if(sp->soc != NULL)
isc_stop_receiver(sp);
error = fget(td, fd, CAP_SOCK_ALL, &sp->fp);
error = fget(td, fd, CAP_SOCK_CLIENT, &sp->fp);
if(error)
return error;
if((error = fgetsock(td, fd, CAP_SOCK_ALL, &sp->soc, 0)) == 0) {
if((error = fgetsock(td, fd, CAP_SOCK_CLIENT, &sp->soc, 0)) == 0) {
sp->td = td;
isc_start_receiver(sp);
}

View file

@ -205,7 +205,7 @@ fdesc_statfs(mp, sbp)
last = min(fdp->fd_nfiles, lim);
freefd = 0;
for (i = fdp->fd_freefile; i < last; i++)
if (fdp->fd_ofiles[i] == NULL)
if (fdp->fd_ofiles[i].fde_file == NULL)
freefd++;
/*

View file

@ -534,7 +534,7 @@ fdesc_readdir(ap)
dp->d_type = DT_DIR;
break;
default:
if (fdp->fd_ofiles[fcnt] == NULL)
if (fdp->fd_ofiles[fcnt].fde_file == NULL)
break;
dp->d_namlen = sprintf(dp->d_name, "%d", fcnt);
dp->d_reclen = UIO_MX;

View file

@ -94,8 +94,6 @@ struct nfsexstuff {
#define NFSFPCRED(f) ((f)->f_cred)
#define NFSFPFLAG(f) ((f)->f_flag)
int fp_getfvp(NFSPROC_T *, int, struct file **, struct vnode **);
#define NFSNAMEICNDSET(n, c, o, f) do { \
(n)->cn_cred = (c); \
(n)->cn_nameiop = (o); \

View file

@ -1215,7 +1215,7 @@ nfssvc_nfscl(struct thread *td, struct nfssvc_args *uap)
* pretend that we need them all. It is better to be too
* careful than too reckless.
*/
if ((error = fget(td, nfscbdarg.sock, CAP_SOCK_ALL, &fp))
if ((error = fget(td, nfscbdarg.sock, CAP_SOCK_CLIENT, &fp))
!= 0) {
return (error);
}

View file

@ -2767,7 +2767,7 @@ nfsd_fhtovp(struct nfsrv_descript *nd, struct nfsrvfh *nfp, int lktype,
/*
* glue for fp.
*/
int
static int
fp_getfvp(struct thread *p, int fd, struct file **fpp, struct vnode **vpp)
{
struct filedesc *fdp;
@ -2775,8 +2775,8 @@ fp_getfvp(struct thread *p, int fd, struct file **fpp, struct vnode **vpp)
int error = 0;
fdp = p->td_proc->p_fd;
if (fd >= fdp->fd_nfiles ||
(fp = fdp->fd_ofiles[fd]) == NULL) {
if (fd < 0 || fd >= fdp->fd_nfiles ||
(fp = fdp->fd_ofiles[fd].fde_file) == NULL) {
error = EBADF;
goto out;
}
@ -3041,7 +3041,7 @@ nfssvc_nfsd(struct thread *td, struct nfssvc_args *uap)
* pretend that we need them all. It is better to be too
* careful than too reckless.
*/
if ((error = fget(td, sockarg.sock, CAP_SOCK_ALL, &fp)) != 0)
if ((error = fget(td, sockarg.sock, CAP_SOCK_SERVER, &fp)) != 0)
goto out;
if (fp->f_type != DTYPE_SOCKET) {
fdrop(fp, td);

View file

@ -337,8 +337,7 @@ ibcs2_getdents(td, uap)
#define BSD_DIRENT(cp) ((struct dirent *)(cp))
#define IBCS2_RECLEN(reclen) (reclen + sizeof(u_short))
if ((error = getvnode(td->td_proc->p_fd, uap->fd,
CAP_READ | CAP_SEEK, &fp)) != 0)
if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_READ, &fp)) != 0)
return (error);
if ((fp->f_flag & FREAD) == 0) {
fdrop(fp, td);
@ -491,8 +490,8 @@ ibcs2_read(td, uap)
u_long *cookies = NULL, *cookiep;
int ncookies;
if ((error = getvnode(td->td_proc->p_fd, uap->fd,
CAP_READ | CAP_SEEK, &fp)) != 0) {
if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_READ,
&fp)) != 0) {
if (error == EINVAL)
return sys_read(td, (struct read_args *)uap);
else

View file

@ -110,9 +110,14 @@ aio_write
## Allow capability mode and capability system calls.
##
cap_enter
cap_fcntls_get
cap_fcntls_limit
cap_getmode
cap_getrights
cap_ioctls_get
cap_ioctls_limit
cap_new
cap_rights_get
cap_rights_limit
##
## Allow read-only clock operations.
@ -239,7 +244,7 @@ getcontext
## Allow directory I/O on a file descriptor, subject to capability rights.
## Originally we had separate capabilities for directory-specific read
## operations, but on BSD we allow reading the raw directory data, so we just
## rely on CAP_READ and CAP_SEEK now.
## rely on CAP_READ now.
##
getdents
getdirentries
@ -317,13 +322,10 @@ gettimeofday
getuid
##
## Disallow ioctl(2) for now, as frequently ioctl(2) operations have global
## scope, but this is a tricky one as it is also required for tty control.
## We do have a capability right for this operation.
## Allow ioctl(2), which hopefully will be limited by applications only to
## required commands with cap_ioctls_limit(2) syscall.
##
## XXXRW: This needs to be revisited.
##
#ioctl
ioctl
##
## Allow querying current process credential state.

File diff suppressed because it is too large Load diff

View file

@ -438,9 +438,6 @@ do_execve(td, args, mac_p)
} else {
AUDIT_ARG_FD(args->fd);
/*
* Some might argue that CAP_READ and/or CAP_MMAP should also
* be required here; such arguments will be entertained.
*
* Descriptors opened only with O_EXEC or O_RDONLY are allowed.
*/
error = fgetvp_exec(td, args->fd, CAP_FEXECVE, &binvp);

View file

@ -297,7 +297,7 @@ exit1(struct thread *td, int rv)
* Close open files and release open-file table.
* This may block!
*/
fdfree(td);
fdescfree(td);
/*
* If this thread tickled GEOM, we need to wait for the giggling to

View file

@ -342,7 +342,7 @@ fork_norfproc(struct thread *td, int flags)
if (flags & RFCFDG) {
struct filedesc *fdtmp;
fdtmp = fdinit(td->td_proc->p_fd);
fdfree(td);
fdescfree(td);
p1->p_fd = fdtmp;
}

View file

@ -1,11 +1,15 @@
/*-
* Copyright (c) 2008-2011 Robert N. M. Watson
* Copyright (c) 2010-2011 Jonathan Anderson
* Copyright (c) 2012 FreeBSD Foundation
* All rights reserved.
*
* This software was developed at the University of Cambridge Computer
* Laboratory with support from a grant from Google, Inc.
*
* Portions of this software were developed by Pawel Jakub Dawidek under
* sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -62,6 +66,7 @@ __FBSDID("$FreeBSD$");
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/kernel.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/proc.h>
@ -139,135 +144,99 @@ sys_cap_getmode(struct thread *td, struct cap_getmode_args *uap)
FEATURE(security_capabilities, "Capsicum Capabilities");
/*
* struct capability describes a capability, and is hung off of its struct
* file f_data field. cap_file and cap_rightss are static once hooked up, as
* neither the object it references nor the rights it encapsulates are
* permitted to change.
*/
struct capability {
struct file *cap_object; /* Underlying object's file. */
struct file *cap_file; /* Back-pointer to cap's file. */
cap_rights_t cap_rights; /* Mask of rights on object. */
};
/*
* Capabilities have a fileops vector, but in practice none should ever be
* called except for fo_close, as the capability will normally not be
* returned during a file descriptor lookup in the system call code.
*/
static fo_rdwr_t capability_read;
static fo_rdwr_t capability_write;
static fo_truncate_t capability_truncate;
static fo_ioctl_t capability_ioctl;
static fo_poll_t capability_poll;
static fo_kqfilter_t capability_kqfilter;
static fo_stat_t capability_stat;
static fo_close_t capability_close;
static fo_chmod_t capability_chmod;
static fo_chown_t capability_chown;
static struct fileops capability_ops = {
.fo_read = capability_read,
.fo_write = capability_write,
.fo_truncate = capability_truncate,
.fo_ioctl = capability_ioctl,
.fo_poll = capability_poll,
.fo_kqfilter = capability_kqfilter,
.fo_stat = capability_stat,
.fo_close = capability_close,
.fo_chmod = capability_chmod,
.fo_chown = capability_chown,
.fo_flags = DFLAG_PASSABLE,
};
static struct fileops capability_ops_unpassable = {
.fo_read = capability_read,
.fo_write = capability_write,
.fo_truncate = capability_truncate,
.fo_ioctl = capability_ioctl,
.fo_poll = capability_poll,
.fo_kqfilter = capability_kqfilter,
.fo_stat = capability_stat,
.fo_close = capability_close,
.fo_chmod = capability_chmod,
.fo_chown = capability_chown,
.fo_flags = 0,
};
static uma_zone_t capability_zone;
static void
capability_init(void *dummy __unused)
static inline int
_cap_check(cap_rights_t have, cap_rights_t need, enum ktr_cap_fail_type type)
{
capability_zone = uma_zcreate("capability", sizeof(struct capability),
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
if (capability_zone == NULL)
panic("capability_init: capability_zone not initialized");
}
SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_ANY, capability_init, NULL);
/*
* Test whether a capability grants the requested rights.
*/
static int
cap_check(struct capability *c, cap_rights_t rights)
{
if ((c->cap_rights | rights) != c->cap_rights) {
if ((need & ~have) != 0) {
#ifdef KTRACE
if (KTRPOINT(curthread, KTR_CAPFAIL))
ktrcapfail(CAPFAIL_NOTCAPABLE, rights, c->cap_rights);
ktrcapfail(type, need, have);
#endif
return (ENOTCAPABLE);
}
return (0);
}
/*
* Test whether a capability grants the requested rights.
*/
int
cap_check(cap_rights_t have, cap_rights_t need)
{
return (_cap_check(have, need, CAPFAIL_NOTCAPABLE));
}
/*
* Convert capability rights into VM access flags.
*/
u_char
cap_rights_to_vmprot(cap_rights_t have)
{
u_char maxprot;
maxprot = VM_PROT_NONE;
if (have & CAP_MMAP_R)
maxprot |= VM_PROT_READ;
if (have & CAP_MMAP_W)
maxprot |= VM_PROT_WRITE;
if (have & CAP_MMAP_X)
maxprot |= VM_PROT_EXECUTE;
return (maxprot);
}
/*
* Extract rights from a capability for monitoring purposes -- not for use in
* any other way, as we want to keep all capability permission evaluation in
* this one file.
*/
cap_rights_t
cap_rights(struct file *fp_cap)
cap_rights(struct filedesc *fdp, int fd)
{
struct capability *c;
KASSERT(fp_cap->f_type == DTYPE_CAPABILITY,
("cap_rights: !capability"));
c = fp_cap->f_data;
return (c->cap_rights);
return (fdp->fd_ofiles[fd].fde_rights);
}
/*
* System call to create a new capability reference to either an existing
* file object or an an existing capability.
* System call to limit rights of the given capability.
*/
int
sys_cap_new(struct thread *td, struct cap_new_args *uap)
sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap)
{
int error, capfd;
int fd = uap->fd;
struct file *fp;
cap_rights_t rights = uap->rights;
struct filedesc *fdp;
cap_rights_t rights;
int error, fd;
fd = uap->fd;
rights = uap->rights;
AUDIT_ARG_FD(fd);
AUDIT_ARG_RIGHTS(rights);
error = fget(td, fd, rights, &fp);
if (error)
return (error);
AUDIT_ARG_FILE(td->td_proc, fp);
error = kern_capwrap(td, fp, rights, &capfd);
/*
* Release our reference to the file (kern_capwrap has held a reference
* for the filedesc array).
*/
fdrop(fp, td);
if (error == 0)
td->td_retval[0] = capfd;
if ((rights & ~CAP_ALL) != 0)
return (EINVAL);
fdp = td->td_proc->p_fd;
FILEDESC_XLOCK(fdp);
if (fget_locked(fdp, fd) == NULL) {
FILEDESC_XUNLOCK(fdp);
return (EBADF);
}
error = _cap_check(cap_rights(fdp, fd), rights, CAPFAIL_INCREASE);
if (error == 0) {
fdp->fd_ofiles[fd].fde_rights = rights;
if ((rights & CAP_IOCTL) == 0) {
free(fdp->fd_ofiles[fd].fde_ioctls, M_TEMP);
fdp->fd_ofiles[fd].fde_ioctls = NULL;
fdp->fd_ofiles[fd].fde_nioctls = 0;
}
if ((rights & CAP_FCNTL) == 0)
fdp->fd_ofiles[fd].fde_fcntls = 0;
}
FILEDESC_XUNLOCK(fdp);
return (error);
}
@ -275,255 +244,372 @@ sys_cap_new(struct thread *td, struct cap_new_args *uap)
* System call to query the rights mask associated with a capability.
*/
int
sys_cap_getrights(struct thread *td, struct cap_getrights_args *uap)
sys_cap_rights_get(struct thread *td, struct cap_rights_get_args *uap)
{
struct capability *cp;
struct file *fp;
int error;
struct filedesc *fdp;
cap_rights_t rights;
int fd;
AUDIT_ARG_FD(uap->fd);
error = fgetcap(td, uap->fd, &fp);
if (error)
return (error);
cp = fp->f_data;
error = copyout(&cp->cap_rights, uap->rightsp, sizeof(*uap->rightsp));
fdrop(fp, td);
fd = uap->fd;
AUDIT_ARG_FD(fd);
fdp = td->td_proc->p_fd;
FILEDESC_SLOCK(fdp);
if (fget_locked(fdp, fd) == NULL) {
FILEDESC_SUNLOCK(fdp);
return (EBADF);
}
rights = cap_rights(fdp, fd);
FILEDESC_SUNLOCK(fdp);
return (copyout(&rights, uap->rightsp, sizeof(*uap->rightsp)));
}
/*
* Test whether a capability grants the given ioctl command.
* If descriptor doesn't have CAP_IOCTL, then ioctls list is empty and
* ENOTCAPABLE will be returned.
*/
int
cap_ioctl_check(struct filedesc *fdp, int fd, u_long cmd)
{
u_long *cmds;
ssize_t ncmds;
long i;
FILEDESC_LOCK_ASSERT(fdp);
KASSERT(fd >= 0 && fd < fdp->fd_nfiles,
("%s: invalid fd=%d", __func__, fd));
ncmds = fdp->fd_ofiles[fd].fde_nioctls;
if (ncmds == -1)
return (0);
cmds = fdp->fd_ofiles[fd].fde_ioctls;
for (i = 0; i < ncmds; i++) {
if (cmds[i] == cmd)
return (0);
}
return (ENOTCAPABLE);
}
/*
* Check if the current ioctls list can be replaced by the new one.
*/
static int
cap_ioctl_limit_check(struct filedesc *fdp, int fd, const u_long *cmds,
size_t ncmds)
{
u_long *ocmds;
ssize_t oncmds;
u_long i;
long j;
oncmds = fdp->fd_ofiles[fd].fde_nioctls;
if (oncmds == -1)
return (0);
if (oncmds < (ssize_t)ncmds)
return (ENOTCAPABLE);
ocmds = fdp->fd_ofiles[fd].fde_ioctls;
for (i = 0; i < ncmds; i++) {
for (j = 0; j < oncmds; j++) {
if (cmds[i] == ocmds[j])
break;
}
if (j == oncmds)
return (ENOTCAPABLE);
}
return (0);
}
int
sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap)
{
struct filedesc *fdp;
u_long *cmds, *ocmds;
size_t ncmds;
int error, fd;
fd = uap->fd;
ncmds = uap->ncmds;
AUDIT_ARG_FD(fd);
if (ncmds > 256) /* XXX: Is 256 sane? */
return (EINVAL);
if (ncmds == 0) {
cmds = NULL;
} else {
cmds = malloc(sizeof(cmds[0]) * ncmds, M_TEMP, M_WAITOK);
error = copyin(uap->cmds, cmds, sizeof(cmds[0]) * ncmds);
if (error != 0) {
free(cmds, M_TEMP);
return (error);
}
}
fdp = td->td_proc->p_fd;
FILEDESC_XLOCK(fdp);
if (fget_locked(fdp, fd) == NULL) {
error = EBADF;
goto out;
}
error = cap_ioctl_limit_check(fdp, fd, cmds, ncmds);
if (error != 0)
goto out;
ocmds = fdp->fd_ofiles[fd].fde_ioctls;
fdp->fd_ofiles[fd].fde_ioctls = cmds;
fdp->fd_ofiles[fd].fde_nioctls = ncmds;
cmds = ocmds;
error = 0;
out:
FILEDESC_XUNLOCK(fdp);
free(cmds, M_TEMP);
return (error);
}
int
sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap)
{
struct filedesc *fdp;
struct filedescent *fdep;
u_long *cmds;
size_t maxcmds;
int error, fd;
fd = uap->fd;
cmds = uap->cmds;
maxcmds = uap->maxcmds;
AUDIT_ARG_FD(fd);
fdp = td->td_proc->p_fd;
FILEDESC_SLOCK(fdp);
if (fget_locked(fdp, fd) == NULL) {
error = EBADF;
goto out;
}
/*
* If all ioctls are allowed (fde_nioctls == -1 && fde_ioctls == NULL)
* the only sane thing we can do is to not populate the given array and
* return CAP_IOCTLS_ALL.
*/
fdep = &fdp->fd_ofiles[fd];
if (cmds != NULL && fdep->fde_ioctls != NULL) {
error = copyout(fdep->fde_ioctls, cmds,
sizeof(cmds[0]) * MIN(fdep->fde_nioctls, maxcmds));
if (error != 0)
goto out;
}
if (fdep->fde_nioctls == -1)
td->td_retval[0] = CAP_IOCTLS_ALL;
else
td->td_retval[0] = fdep->fde_nioctls;
error = 0;
out:
FILEDESC_SUNLOCK(fdp);
return (error);
}
/*
* Create a capability to wrap around an existing file.
* Test whether a capability grants the given fcntl command.
*/
int
kern_capwrap(struct thread *td, struct file *fp, cap_rights_t rights,
int *capfdp)
cap_fcntl_check(struct filedesc *fdp, int fd, int cmd)
{
struct capability *cp, *cp_old;
struct file *fp_object, *fcapp;
int error;
uint32_t fcntlcap;
if ((rights | CAP_MASK_VALID) != CAP_MASK_VALID)
KASSERT(fd >= 0 && fd < fdp->fd_nfiles,
("%s: invalid fd=%d", __func__, fd));
fcntlcap = (1 << cmd);
KASSERT((CAP_FCNTL_ALL & fcntlcap) != 0,
("Unsupported fcntl=%d.", cmd));
if ((fdp->fd_ofiles[fd].fde_fcntls & fcntlcap) != 0)
return (0);
return (ENOTCAPABLE);
}
int
sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap)
{
struct filedesc *fdp;
uint32_t fcntlrights;
int fd;
fd = uap->fd;
fcntlrights = uap->fcntlrights;
AUDIT_ARG_FD(fd);
AUDIT_ARG_FCNTL_RIGHTS(fcntlrights);
if ((fcntlrights & ~CAP_FCNTL_ALL) != 0)
return (EINVAL);
/*
* If a new capability is being derived from an existing capability,
* then the new capability rights must be a subset of the existing
* rights.
*/
if (fp->f_type == DTYPE_CAPABILITY) {
cp_old = fp->f_data;
if ((cp_old->cap_rights | rights) != cp_old->cap_rights) {
#ifdef KTRACE
if (KTRPOINT(curthread, KTR_CAPFAIL))
ktrcapfail(CAPFAIL_INCREASE,
rights, cp_old->cap_rights);
#endif
return (ENOTCAPABLE);
}
fdp = td->td_proc->p_fd;
FILEDESC_XLOCK(fdp);
if (fget_locked(fdp, fd) == NULL) {
FILEDESC_XUNLOCK(fdp);
return (EBADF);
}
/*
* Allocate a new file descriptor to hang the capability off of.
*/
error = falloc(td, &fcapp, capfdp, fp->f_flag);
if (error)
return (error);
if ((fcntlrights & ~fdp->fd_ofiles[fd].fde_fcntls) != 0) {
FILEDESC_XUNLOCK(fdp);
return (ENOTCAPABLE);
}
/*
* Rather than nesting capabilities, directly reference the object an
* existing capability references. There's nothing else interesting
* to preserve for future use, as we've incorporated the previous
* rights mask into the new one. This prevents us from having to
* deal with capability chains.
*/
if (fp->f_type == DTYPE_CAPABILITY)
fp_object = ((struct capability *)fp->f_data)->cap_object;
else
fp_object = fp;
fhold(fp_object);
cp = uma_zalloc(capability_zone, M_WAITOK | M_ZERO);
cp->cap_rights = rights;
cp->cap_object = fp_object;
cp->cap_file = fcapp;
if (fp->f_flag & DFLAG_PASSABLE)
finit(fcapp, fp->f_flag, DTYPE_CAPABILITY, cp,
&capability_ops);
else
finit(fcapp, fp->f_flag, DTYPE_CAPABILITY, cp,
&capability_ops_unpassable);
fdp->fd_ofiles[fd].fde_fcntls = fcntlrights;
FILEDESC_XUNLOCK(fdp);
/*
* Release our private reference (the proc filedesc still has one).
*/
fdrop(fcapp, td);
return (0);
}
/*
* Given a file descriptor, test it against a capability rights mask and then
* return the file descriptor on which to actually perform the requested
* operation. As long as the reference to fp_cap remains valid, the returned
* pointer in *fp will remain valid, so no extra reference management is
* required, and the caller should fdrop() fp_cap as normal when done with
* both.
*/
int
cap_funwrap(struct file *fp_cap, cap_rights_t rights, struct file **fpp)
sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap)
{
struct capability *c;
int error;
struct filedesc *fdp;
uint32_t rights;
int fd;
if (fp_cap->f_type != DTYPE_CAPABILITY) {
*fpp = fp_cap;
return (0);
fd = uap->fd;
AUDIT_ARG_FD(fd);
fdp = td->td_proc->p_fd;
FILEDESC_SLOCK(fdp);
if (fget_locked(fdp, fd) == NULL) {
FILEDESC_SUNLOCK(fdp);
return (EBADF);
}
c = fp_cap->f_data;
error = cap_check(c, rights);
if (error)
return (error);
*fpp = c->cap_object;
return (0);
rights = fdp->fd_ofiles[fd].fde_fcntls;
FILEDESC_SUNLOCK(fdp);
return (copyout(&rights, uap->fcntlrightsp, sizeof(rights)));
}
/*
* Slightly different routine for memory mapping file descriptors: unwrap the
* capability and check CAP_MMAP, but also return a bitmask representing the
* maximum mapping rights the capability allows on the object.
* For backward compatibility.
*/
int
cap_funwrap_mmap(struct file *fp_cap, cap_rights_t rights, u_char *maxprotp,
struct file **fpp)
sys_cap_new(struct thread *td, struct cap_new_args *uap)
{
struct capability *c;
u_char maxprot;
int error;
struct filedesc *fdp;
cap_rights_t rights;
register_t newfd;
int error, fd;
if (fp_cap->f_type != DTYPE_CAPABILITY) {
*fpp = fp_cap;
*maxprotp = VM_PROT_ALL;
return (0);
fd = uap->fd;
rights = uap->rights;
AUDIT_ARG_FD(fd);
AUDIT_ARG_RIGHTS(rights);
if ((rights & ~CAP_ALL) != 0)
return (EINVAL);
fdp = td->td_proc->p_fd;
FILEDESC_SLOCK(fdp);
if (fget_locked(fdp, fd) == NULL) {
FILEDESC_SUNLOCK(fdp);
return (EBADF);
}
c = fp_cap->f_data;
error = cap_check(c, rights | CAP_MMAP);
if (error)
error = _cap_check(cap_rights(fdp, fd), rights, CAPFAIL_INCREASE);
FILEDESC_SUNLOCK(fdp);
if (error != 0)
return (error);
*fpp = c->cap_object;
maxprot = 0;
if (c->cap_rights & CAP_READ)
maxprot |= VM_PROT_READ;
if (c->cap_rights & CAP_WRITE)
maxprot |= VM_PROT_WRITE;
if (c->cap_rights & CAP_MAPEXEC)
maxprot |= VM_PROT_EXECUTE;
*maxprotp = maxprot;
error = do_dup(td, 0, fd, 0, &newfd);
if (error != 0)
return (error);
FILEDESC_XLOCK(fdp);
/*
* We don't really care about the race between checking capability
* rights for the source descriptor and now. If capability rights
* were ok at that earlier point, the process had this descriptor
* with those rights, so we don't increase them in security sense,
* the process might have done the cap_new(2) a bit earlier to get
* the same effect.
*/
fdp->fd_ofiles[newfd].fde_rights = rights;
if ((rights & CAP_IOCTL) == 0) {
free(fdp->fd_ofiles[newfd].fde_ioctls, M_TEMP);
fdp->fd_ofiles[newfd].fde_ioctls = NULL;
fdp->fd_ofiles[newfd].fde_nioctls = 0;
}
if ((rights & CAP_FCNTL) == 0)
fdp->fd_ofiles[newfd].fde_fcntls = 0;
FILEDESC_XUNLOCK(fdp);
td->td_retval[0] = newfd;
return (0);
}
/*
* When a capability is closed, simply drop the reference on the underlying
* object and free the capability. fdrop() will handle the case where the
* underlying object also needs to close, and the caller will have already
* performed any object-specific lock or mqueue handling.
*/
static int
capability_close(struct file *fp, struct thread *td)
{
struct capability *c;
struct file *fp_object;
KASSERT(fp->f_type == DTYPE_CAPABILITY,
("capability_close: !capability"));
c = fp->f_data;
fp->f_ops = &badfileops;
fp->f_data = NULL;
fp_object = c->cap_object;
uma_zfree(capability_zone, c);
return (fdrop(fp_object, td));
}
/*
* In general, file descriptor operations should never make it to the
* capability, only the underlying file descriptor operation vector, so panic
* if we do turn up here.
*/
static int
capability_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
int flags, struct thread *td)
{
panic("capability_read");
}
static int
capability_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
int flags, struct thread *td)
{
panic("capability_write");
}
static int
capability_truncate(struct file *fp, off_t length, struct ucred *active_cred,
struct thread *td)
{
panic("capability_truncate");
}
static int
capability_ioctl(struct file *fp, u_long com, void *data,
struct ucred *active_cred, struct thread *td)
{
panic("capability_ioctl");
}
static int
capability_poll(struct file *fp, int events, struct ucred *active_cred,
struct thread *td)
{
panic("capability_poll");
}
static int
capability_kqfilter(struct file *fp, struct knote *kn)
{
panic("capability_kqfilter");
}
static int
capability_stat(struct file *fp, struct stat *sb, struct ucred *active_cred,
struct thread *td)
{
panic("capability_stat");
}
int
capability_chmod(struct file *fp, mode_t mode, struct ucred *active_cred,
struct thread *td)
{
panic("capability_chmod");
}
int
capability_chown(struct file *fp, uid_t uid, gid_t gid,
struct ucred *active_cred, struct thread *td)
{
panic("capability_chown");
}
#else /* !CAPABILITIES */
/*
* Stub Capability functions for when options CAPABILITIES isn't compiled
* into the kernel.
*/
int
sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap)
{
return (ENOSYS);
}
int
sys_cap_rights_get(struct thread *td, struct cap_rights_get_args *uap)
{
return (ENOSYS);
}
int
sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap)
{
return (ENOSYS);
}
int
sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap)
{
return (ENOSYS);
}
int
sys_cap_fcntls_limit(struct thread *td, struct cap_fcntls_limit_args *uap)
{
return (ENOSYS);
}
int
sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap)
{
return (ENOSYS);
}
int
sys_cap_new(struct thread *td, struct cap_new_args *uap)
{
@ -531,35 +617,4 @@ sys_cap_new(struct thread *td, struct cap_new_args *uap)
return (ENOSYS);
}
int
sys_cap_getrights(struct thread *td, struct cap_getrights_args *uap)
{
return (ENOSYS);
}
int
cap_funwrap(struct file *fp_cap, cap_rights_t rights, struct file **fpp)
{
KASSERT(fp_cap->f_type != DTYPE_CAPABILITY,
("cap_funwrap: saw capability"));
*fpp = fp_cap;
return (0);
}
int
cap_funwrap_mmap(struct file *fp_cap, cap_rights_t rights, u_char *maxprotp,
struct file **fpp)
{
KASSERT(fp_cap->f_type != DTYPE_CAPABILITY,
("cap_funwrap_mmap: saw capability"));
*fpp = fp_cap;
*maxprotp = VM_PROT_ALL;
return (0);
}
#endif /* CAPABILITIES */

View file

@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
#include <sys/filio.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/lock.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/socketvar.h>
@ -244,7 +245,7 @@ kern_readv(struct thread *td, int fd, struct uio *auio)
struct file *fp;
int error;
error = fget_read(td, fd, CAP_READ | CAP_SEEK, &fp);
error = fget_read(td, fd, CAP_READ, &fp);
if (error)
return (error);
error = dofileread(td, fd, fp, auio, (off_t)-1, 0);
@ -287,7 +288,7 @@ kern_preadv(td, fd, auio, offset)
struct file *fp;
int error;
error = fget_read(td, fd, CAP_READ, &fp);
error = fget_read(td, fd, CAP_PREAD, &fp);
if (error)
return (error);
if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE))
@ -453,7 +454,7 @@ kern_writev(struct thread *td, int fd, struct uio *auio)
struct file *fp;
int error;
error = fget_write(td, fd, CAP_WRITE | CAP_SEEK, &fp);
error = fget_write(td, fd, CAP_WRITE, &fp);
if (error)
return (error);
error = dofilewrite(td, fd, fp, auio, (off_t)-1, 0);
@ -496,7 +497,7 @@ kern_pwritev(td, fd, auio, offset)
struct file *fp;
int error;
error = fget_write(td, fd, CAP_WRITE, &fp);
error = fget_write(td, fd, CAP_PWRITE, &fp);
if (error)
return (error);
if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE))
@ -704,28 +705,60 @@ kern_ioctl(struct thread *td, int fd, u_long com, caddr_t data)
{
struct file *fp;
struct filedesc *fdp;
int error;
int tmp;
int error, tmp, locked;
AUDIT_ARG_FD(fd);
AUDIT_ARG_CMD(com);
if ((error = fget(td, fd, CAP_IOCTL, &fp)) != 0)
return (error);
if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
fdrop(fp, td);
return (EBADF);
}
fdp = td->td_proc->p_fd;
switch (com) {
case FIONCLEX:
FILEDESC_XLOCK(fdp);
fdp->fd_ofileflags[fd] &= ~UF_EXCLOSE;
FILEDESC_XUNLOCK(fdp);
goto out;
case FIOCLEX:
FILEDESC_XLOCK(fdp);
fdp->fd_ofileflags[fd] |= UF_EXCLOSE;
FILEDESC_XUNLOCK(fdp);
locked = LA_XLOCKED;
break;
default:
#ifdef CAPABILITIES
FILEDESC_SLOCK(fdp);
locked = LA_SLOCKED;
#else
locked = LA_UNLOCKED;
#endif
break;
}
#ifdef CAPABILITIES
if ((fp = fget_locked(fdp, fd)) == NULL) {
error = EBADF;
goto out;
}
if ((error = cap_ioctl_check(fdp, fd, com)) != 0) {
fp = NULL; /* fhold() was not called yet */
goto out;
}
fhold(fp);
if (locked == LA_SLOCKED) {
FILEDESC_SUNLOCK(fdp);
locked = LA_UNLOCKED;
}
#else
if ((error = fget(td, fd, CAP_IOCTL, &fp)) != 0) {
fp = NULL;
goto out;
}
#endif
if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
error = EBADF;
goto out;
}
switch (com) {
case FIONCLEX:
fdp->fd_ofiles[fd].fde_flags &= ~UF_EXCLOSE;
goto out;
case FIOCLEX:
fdp->fd_ofiles[fd].fde_flags |= UF_EXCLOSE;
goto out;
case FIONBIO:
if ((tmp = *(int *)data))
@ -745,7 +778,21 @@ kern_ioctl(struct thread *td, int fd, u_long com, caddr_t data)
error = fo_ioctl(fp, com, data, td->td_ucred, td);
out:
fdrop(fp, td);
switch (locked) {
case LA_XLOCKED:
FILEDESC_XUNLOCK(fdp);
break;
#ifdef CAPABILITIES
case LA_SLOCKED:
FILEDESC_SUNLOCK(fdp);
break;
#endif
default:
FILEDESC_UNLOCK_ASSERT(fdp);
break;
}
if (fp != NULL)
fdrop(fp, td);
return (error);
}
@ -1130,32 +1177,8 @@ selsetbits(fd_mask **ibits, fd_mask **obits, int idx, fd_mask bit, int events)
static __inline int
getselfd_cap(struct filedesc *fdp, int fd, struct file **fpp)
{
struct file *fp;
#ifdef CAPABILITIES
struct file *fp_fromcap;
int error;
#endif
if ((fp = fget_unlocked(fdp, fd)) == NULL)
return (EBADF);
#ifdef CAPABILITIES
/*
* If the file descriptor is for a capability, test rights and use
* the file descriptor references by the capability.
*/
error = cap_funwrap(fp, CAP_POLL_EVENT, &fp_fromcap);
if (error) {
fdrop(fp, curthread);
return (error);
}
if (fp != fp_fromcap) {
fhold(fp_fromcap);
fdrop(fp, curthread);
fp = fp_fromcap;
}
#endif /* CAPABILITIES */
*fpp = fp;
return (0);
return (fget_unlocked(fdp, fd, CAP_POLL_EVENT, 0, fpp, NULL));
}
/*
@ -1349,13 +1372,14 @@ pollrescan(struct thread *td)
/* If the selinfo wasn't cleared the event didn't fire. */
if (si != NULL)
continue;
fp = fdp->fd_ofiles[fd->fd];
fp = fdp->fd_ofiles[fd->fd].fde_file;
#ifdef CAPABILITIES
if ((fp == NULL)
|| (cap_funwrap(fp, CAP_POLL_EVENT, &fp) != 0)) {
if (fp == NULL ||
cap_check(cap_rights(fdp, fd->fd), CAP_POLL_EVENT) != 0)
#else
if (fp == NULL) {
if (fp == NULL)
#endif
{
fd->revents = POLLNVAL;
n++;
continue;
@ -1408,9 +1432,8 @@ pollscan(td, fds, nfd)
u_int nfd;
{
struct filedesc *fdp = td->td_proc->p_fd;
int i;
struct file *fp;
int n = 0;
int i, n = 0;
FILEDESC_SLOCK(fdp);
for (i = 0; i < nfd; i++, fds++) {
@ -1420,13 +1443,15 @@ pollscan(td, fds, nfd)
} else if (fds->fd < 0) {
fds->revents = 0;
} else {
fp = fdp->fd_ofiles[fds->fd];
fp = fdp->fd_ofiles[fds->fd].fde_file;
#ifdef CAPABILITIES
if ((fp == NULL)
|| (cap_funwrap(fp, CAP_POLL_EVENT, &fp) != 0)) {
if (fp == NULL ||
cap_check(cap_rights(fdp, fds->fd),
CAP_POLL_EVENT) != 0)
#else
if (fp == NULL) {
if (fp == NULL)
#endif
{
fds->revents = POLLNVAL;
n++;
} else {

View file

@ -917,7 +917,7 @@
struct shmid_ds *buf); }
513 AUE_LPATHCONF STD { int lpathconf(char *path, int name); }
514 AUE_CAP_NEW STD { int cap_new(int fd, uint64_t rights); }
515 AUE_CAP_GETRIGHTS STD { int cap_getrights(int fd, \
515 AUE_CAP_RIGHTS_GET STD { int cap_rights_get(int fd, \
uint64_t *rightsp); }
516 AUE_CAP_ENTER STD { int cap_enter(void); }
517 AUE_CAP_GETMODE STD { int cap_getmode(u_int *modep); }
@ -955,5 +955,15 @@
int *status, int options, \
struct __wrusage *wrusage, \
siginfo_t *info); }
533 AUE_CAP_RIGHTS_LIMIT STD { int cap_rights_limit(int fd, \
uint64_t rights); }
534 AUE_CAP_IOCTLS_LIMIT STD { int cap_ioctls_limit(int fd, \
const u_long *cmds, size_t ncmds); }
535 AUE_CAP_IOCTLS_GET STD { ssize_t cap_ioctls_get(int fd, \
u_long *cmds, size_t maxcmds); }
536 AUE_CAP_FCNTLS_LIMIT STD { int cap_fcntls_limit(int fd, \
uint32_t fcntlrights); }
537 AUE_CAP_FCNTLS_GET STD { int cap_fcntls_get(int fd, \
uint32_t *fcntlrightsp); }
; Please copy any additions and changes to the following compatability tables:
; sys/compat/freebsd32/syscalls.master

View file

@ -1840,23 +1840,15 @@ ttyhook_register(struct tty **rtp, struct proc *p, int fd,
int error, ref;
/* Validate the file descriptor. */
if ((fdp = p->p_fd) == NULL)
return (EBADF);
fp = fget_unlocked(fdp, fd);
if (fp == NULL)
return (EBADF);
fdp = p->p_fd;
error = fget_unlocked(fdp, fd, CAP_TTYHOOK, 0, &fp, NULL);
if (error != 0)
return (error);
if (fp->f_ops == &badfileops) {
error = EBADF;
goto done1;
}
#ifdef CAPABILITIES
error = cap_funwrap(fp, CAP_TTYHOOK, &fp);
if (error)
goto done1;
#endif
/*
* Make sure the vnode is bound to a character device.
* Unlocked check for the vnode type is ok there, because we

View file

@ -45,6 +45,7 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_capsicum.h"
#include "opt_compat.h"
#include <sys/param.h>
@ -2032,8 +2033,8 @@ kern_kmq_open(struct thread *td, const char *upath, int flags, mode_t mode,
&mqueueops);
FILEDESC_XLOCK(fdp);
if (fdp->fd_ofiles[fd] == fp)
fdp->fd_ofileflags[fd] |= UF_EXCLOSE;
if (fdp->fd_ofiles[fd].fde_file == fp)
fdp->fd_ofiles[fd].fde_flags |= UF_EXCLOSE;
FILEDESC_XUNLOCK(fdp);
td->td_retval[0] = fd;
fdrop(fp, td);
@ -2275,11 +2276,13 @@ sys_kmq_notify(struct thread *td, struct kmq_notify_args *uap)
error = EBADF;
goto out;
}
error = cap_funwrap(fp2, CAP_POLL_EVENT, &fp2);
#ifdef CAPABILITIES
error = cap_check(cap_rights(fdp, uap->mqd), CAP_POLL_EVENT);
if (error) {
FILEDESC_SUNLOCK(fdp);
goto out;
}
#endif
if (fp2 != fp) {
FILEDESC_SUNLOCK(fdp);
error = EBADF;

View file

@ -579,8 +579,8 @@ ksem_create(struct thread *td, const char *name, semid_t *semidp, mode_t mode,
finit(fp, FREAD | FWRITE, DTYPE_SEM, ks, &ksem_ops);
FILEDESC_XLOCK(fdp);
if (fdp->fd_ofiles[fd] == fp)
fdp->fd_ofileflags[fd] |= UF_EXCLOSE;
if (fdp->fd_ofiles[fd].fde_file == fp)
fdp->fd_ofiles[fd].fde_flags |= UF_EXCLOSE;
FILEDESC_XUNLOCK(fdp);
fdrop(fp, td);

View file

@ -629,8 +629,8 @@ sys_shm_open(struct thread *td, struct shm_open_args *uap)
finit(fp, FFLAGS(uap->flags & O_ACCMODE), DTYPE_SHM, shmfd, &shm_ops);
FILEDESC_XLOCK(fdp);
if (fdp->fd_ofiles[fd] == fp)
fdp->fd_ofileflags[fd] |= UF_EXCLOSE;
if (fdp->fd_ofiles[fd].fde_file == fp)
fdp->fd_ofiles[fd].fde_flags |= UF_EXCLOSE;
FILEDESC_XUNLOCK(fdp);
td->td_retval[0] = fd;
fdrop(fp, td);

View file

@ -121,38 +121,20 @@ SYSCTL_INT(_kern_ipc, OID_AUTO, nsfbufsused, CTLFLAG_RD, &nsfbufsused, 0,
"Number of sendfile(2) sf_bufs in use");
/*
* Convert a user file descriptor to a kernel file entry and check that, if
* it is a capability, the right rights are present. A reference on the file
* entry is held upon returning.
* Convert a user file descriptor to a kernel file entry and check if required
* capability rights are present.
* A reference on the file entry is held upon returning.
*/
static int
getsock_cap(struct filedesc *fdp, int fd, cap_rights_t rights,
struct file **fpp, u_int *fflagp)
{
struct file *fp;
#ifdef CAPABILITIES
struct file *fp_fromcap;
int error;
#endif
if (fdp == NULL || (fp = fget_unlocked(fdp, fd)) == NULL)
return (EBADF);
#ifdef CAPABILITIES
/*
* If the file descriptor is for a capability, test rights and use
* the file descriptor referenced by the capability.
*/
error = cap_funwrap(fp, rights, &fp_fromcap);
if (error) {
fdrop(fp, curthread);
error = fget_unlocked(fdp, fd, rights, 0, &fp, NULL);
if (error != 0)
return (error);
}
if (fp != fp_fromcap) {
fhold(fp_fromcap);
fdrop(fp, curthread);
fp = fp_fromcap;
}
#endif /* CAPABILITIES */
if (fp->f_type != DTYPE_SOCKET) {
fdrop(fp, curthread);
return (ENOTSOCK);
@ -765,7 +747,7 @@ kern_sendit(td, s, mp, flags, control, segflg)
#endif
AUDIT_ARG_FD(s);
rights = CAP_WRITE;
rights = CAP_SEND;
if (mp->msg_name != NULL) {
AUDIT_ARG_SOCKADDR(td, mp->msg_name);
rights |= CAP_CONNECT;
@ -974,7 +956,7 @@ kern_recvit(td, s, mp, fromseg, controlp)
*controlp = NULL;
AUDIT_ARG_FD(s);
error = getsock_cap(td->td_proc->p_fd, s, CAP_READ, &fp, NULL);
error = getsock_cap(td->td_proc->p_fd, s, CAP_RECV, &fp, NULL);
if (error)
return (error);
so = fp->f_data;
@ -1850,7 +1832,11 @@ kern_sendfile(struct thread *td, struct sendfile_args *uap,
* we send only the header/trailer and no payload data.
*/
AUDIT_ARG_FD(uap->fd);
if ((error = fgetvp_read(td, uap->fd, CAP_READ, &vp)) != 0)
/*
* sendfile(2) can start at any offset within a file so we require
* CAP_READ+CAP_SEEK = CAP_PREAD.
*/
if ((error = fgetvp_read(td, uap->fd, CAP_PREAD, &vp)) != 0)
goto out;
vn_lock(vp, LK_SHARED | LK_RETRY);
if (vp->v_type == VREG) {
@ -1886,7 +1872,7 @@ kern_sendfile(struct thread *td, struct sendfile_args *uap,
* The socket must be a stream socket and connected.
* Remember if it a blocking or non-blocking socket.
*/
if ((error = getsock_cap(td->td_proc->p_fd, uap->s, CAP_WRITE,
if ((error = getsock_cap(td->td_proc->p_fd, uap->s, CAP_SEND,
&sock_fp, NULL)) != 0)
goto out;
so = sock_fp->f_data;
@ -2423,7 +2409,7 @@ sys_sctp_generic_sendmsg (td, uap)
u_sinfo = &sinfo;
}
rights = CAP_WRITE;
rights = CAP_SEND;
if (uap->tolen) {
error = getsockaddr(&to, uap->to, uap->tolen);
if (error) {
@ -2534,7 +2520,7 @@ sys_sctp_generic_sendmsg_iov(td, uap)
return (error);
u_sinfo = &sinfo;
}
rights = CAP_WRITE;
rights = CAP_SEND;
if (uap->tolen) {
error = getsockaddr(&to, uap->to, uap->tolen);
if (error) {
@ -2658,7 +2644,7 @@ sys_sctp_generic_recvmsg(td, uap)
#endif
AUDIT_ARG_FD(uap->sd);
error = getsock_cap(td->td_proc->p_fd, uap->sd, CAP_READ, &fp, NULL);
error = getsock_cap(td->td_proc->p_fd, uap->sd, CAP_RECV, &fp, NULL);
if (error) {
return (error);
}

View file

@ -279,7 +279,7 @@ static void unp_drop(struct unpcb *, int);
static void unp_gc(__unused void *, int);
static void unp_scan(struct mbuf *, void (*)(struct file *));
static void unp_discard(struct file *);
static void unp_freerights(struct file **, int);
static void unp_freerights(struct filedescent *, int);
static void unp_init(void);
static int unp_internalize(struct mbuf **, struct thread *);
static void unp_internalize_fp(struct file *);
@ -1642,14 +1642,14 @@ unp_drop(struct unpcb *unp, int errno)
}
static void
unp_freerights(struct file **rp, int fdcount)
unp_freerights(struct filedescent *fde, int fdcount)
{
int i;
struct file *fp;
int i;
for (i = 0; i < fdcount; i++) {
fp = *rp;
*rp++ = NULL;
for (i = 0; i < fdcount; i++, fde++) {
fp = fde->fde_file;
bzero(fde, sizeof(*fde));
unp_discard(fp);
}
}
@ -1661,8 +1661,8 @@ unp_externalize(struct mbuf *control, struct mbuf **controlp)
struct cmsghdr *cm = mtod(control, struct cmsghdr *);
int i;
int *fdp;
struct file **rp;
struct file *fp;
struct filedesc *fdesc = td->td_proc->p_fd;
struct filedescent *fde, *fdep;
void *data;
socklen_t clen = control->m_len, datalen;
int error, newfds;
@ -1683,20 +1683,20 @@ unp_externalize(struct mbuf *control, struct mbuf **controlp)
datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
if (cm->cmsg_level == SOL_SOCKET
&& cm->cmsg_type == SCM_RIGHTS) {
newfds = datalen / sizeof(struct file *);
rp = data;
newfds = datalen / sizeof(*fdep);
fdep = data;
/* If we're not outputting the descriptors free them. */
if (error || controlp == NULL) {
unp_freerights(rp, newfds);
unp_freerights(fdep, newfds);
goto next;
}
FILEDESC_XLOCK(td->td_proc->p_fd);
FILEDESC_XLOCK(fdesc);
/* if the new FD's will not fit free them. */
if (!fdavail(td, newfds)) {
FILEDESC_XUNLOCK(td->td_proc->p_fd);
FILEDESC_XUNLOCK(fdesc);
error = EMSGSIZE;
unp_freerights(rp, newfds);
unp_freerights(fdep, newfds);
goto next;
}
@ -1710,23 +1710,24 @@ unp_externalize(struct mbuf *control, struct mbuf **controlp)
*controlp = sbcreatecontrol(NULL, newlen,
SCM_RIGHTS, SOL_SOCKET);
if (*controlp == NULL) {
FILEDESC_XUNLOCK(td->td_proc->p_fd);
FILEDESC_XUNLOCK(fdesc);
error = E2BIG;
unp_freerights(rp, newfds);
unp_freerights(fdep, newfds);
goto next;
}
fdp = (int *)
CMSG_DATA(mtod(*controlp, struct cmsghdr *));
for (i = 0; i < newfds; i++) {
for (i = 0; i < newfds; i++, fdep++, fdp++) {
if (fdalloc(td, 0, &f))
panic("unp_externalize fdalloc failed");
fp = *rp++;
td->td_proc->p_fd->fd_ofiles[f] = fp;
unp_externalize_fp(fp);
*fdp++ = f;
fde = &fdesc->fd_ofiles[f];
fde->fde_file = fdep->fde_file;
filecaps_copy(&fdep->fde_caps, &fde->fde_caps);
unp_externalize_fp(fde->fde_file);
*fdp = f;
}
FILEDESC_XUNLOCK(td->td_proc->p_fd);
FILEDESC_XUNLOCK(fdesc);
} else {
/* We can just copy anything else across. */
if (error || controlp == NULL)
@ -1797,11 +1798,11 @@ unp_internalize(struct mbuf **controlp, struct thread *td)
{
struct mbuf *control = *controlp;
struct proc *p = td->td_proc;
struct filedesc *fdescp = p->p_fd;
struct filedesc *fdesc = p->p_fd;
struct bintime *bt;
struct cmsghdr *cm = mtod(control, struct cmsghdr *);
struct cmsgcred *cmcred;
struct file **rp;
struct filedescent *fde, *fdep;
struct file *fp;
struct timeval *tv;
int i, fd, *fdp;
@ -1854,18 +1855,17 @@ unp_internalize(struct mbuf **controlp, struct thread *td)
* files. If not, reject the entire operation.
*/
fdp = data;
FILEDESC_SLOCK(fdescp);
FILEDESC_SLOCK(fdesc);
for (i = 0; i < oldfds; i++) {
fd = *fdp++;
if (fd < 0 || fd >= fdescp->fd_nfiles ||
fdescp->fd_ofiles[fd] == NULL) {
FILEDESC_SUNLOCK(fdescp);
if (fget_locked(fdesc, fd) == NULL) {
FILEDESC_SUNLOCK(fdesc);
error = EBADF;
goto out;
}
fp = fdescp->fd_ofiles[fd];
fp = fdesc->fd_ofiles[fd].fde_file;
if (!(fp->f_ops->fo_flags & DFLAG_PASSABLE)) {
FILEDESC_SUNLOCK(fdescp);
FILEDESC_SUNLOCK(fdesc);
error = EOPNOTSUPP;
goto out;
}
@ -1874,25 +1874,26 @@ unp_internalize(struct mbuf **controlp, struct thread *td)
/*
* Now replace the integer FDs with pointers to the
* associated global file table entry..
* file structure and capability rights.
*/
newlen = oldfds * sizeof(struct file *);
newlen = oldfds * sizeof(*fdep);
*controlp = sbcreatecontrol(NULL, newlen,
SCM_RIGHTS, SOL_SOCKET);
if (*controlp == NULL) {
FILEDESC_SUNLOCK(fdescp);
FILEDESC_SUNLOCK(fdesc);
error = E2BIG;
goto out;
}
fdp = data;
rp = (struct file **)
fdep = (struct filedescent *)
CMSG_DATA(mtod(*controlp, struct cmsghdr *));
for (i = 0; i < oldfds; i++) {
fp = fdescp->fd_ofiles[*fdp++];
*rp++ = fp;
unp_internalize_fp(fp);
for (i = 0; i < oldfds; i++, fdep++, fdp++) {
fde = &fdesc->fd_ofiles[*fdp];
fdep->fde_file = fde->fde_file;
filecaps_copy(&fde->fde_caps, &fdep->fde_caps);
unp_internalize_fp(fdep->fde_file);
}
FILEDESC_SUNLOCK(fdescp);
FILEDESC_SUNLOCK(fdesc);
break;
case SCM_TIMESTAMP:
@ -2252,7 +2253,7 @@ static void
unp_scan(struct mbuf *m0, void (*op)(struct file *))
{
struct mbuf *m;
struct file **rp;
struct filedescent *fdep;
struct cmsghdr *cm;
void *data;
int i;
@ -2277,10 +2278,10 @@ unp_scan(struct mbuf *m0, void (*op)(struct file *))
if (cm->cmsg_level == SOL_SOCKET &&
cm->cmsg_type == SCM_RIGHTS) {
qfds = datalen / sizeof (struct file *);
rp = data;
for (i = 0; i < qfds; i++)
(*op)(*rp++);
qfds = datalen / sizeof(*fdep);
fdep = data;
for (i = 0; i < qfds; i++, fdep++)
(*op)(fdep->fde_file);
}
if (CMSG_SPACE(datalen) < clen) {

View file

@ -1593,16 +1593,16 @@ aio_aqueue(struct thread *td, struct aiocb *job, struct aioliojob *lj,
fd = aiocbe->uaiocb.aio_fildes;
switch (opcode) {
case LIO_WRITE:
error = fget_write(td, fd, CAP_WRITE | CAP_SEEK, &fp);
error = fget_write(td, fd, CAP_PWRITE, &fp);
break;
case LIO_READ:
error = fget_read(td, fd, CAP_READ | CAP_SEEK, &fp);
error = fget_read(td, fd, CAP_PREAD, &fp);
break;
case LIO_SYNC:
error = fget(td, fd, CAP_FSYNC, &fp);
break;
case LIO_NOP:
error = fget(td, fd, 0, &fp);
error = fget(td, fd, CAP_NONE, &fp);
break;
default:
error = EINVAL;

View file

@ -227,17 +227,18 @@ namei(struct nameidata *ndp)
AUDIT_ARG_ATFD2(ndp->ni_dirfd);
error = fgetvp_rights(td, ndp->ni_dirfd,
ndp->ni_rightsneeded | CAP_LOOKUP,
&(ndp->ni_baserights), &dp);
&ndp->ni_filecaps, &dp);
#ifdef CAPABILITIES
/*
* Lookups relative to a capability must also be
* If file descriptor doesn't have all rights,
* all lookups relative to it must also be
* strictly relative.
*
* Note that a capability with rights CAP_MASK_VALID
* is treated exactly like a regular file descriptor.
*/
if (ndp->ni_baserights != CAP_MASK_VALID)
if (ndp->ni_filecaps.fc_rights != CAP_ALL ||
ndp->ni_filecaps.fc_fcntls != CAP_FCNTL_ALL ||
ndp->ni_filecaps.fc_nioctls != -1) {
ndp->ni_strictrelative = 1;
}
#endif
}
if (error != 0 || dp != NULL) {

View file

@ -970,6 +970,8 @@ flags_to_rights(int flags)
/* FALLTHROUGH */
case O_WRONLY:
rights |= CAP_WRITE;
if (!(flags & O_APPEND))
rights |= CAP_SEEK;
break;
}
}
@ -1143,19 +1145,22 @@ kern_openat(struct thread *td, int fd, char *path, enum uio_seg pathseg,
* If we haven't already installed the FD (for dupfdopen), do so now.
*/
if (indx == -1) {
struct filecaps *fcaps;
#ifdef CAPABILITIES
if (nd.ni_strictrelative == 1) {
/*
* We are doing a strict relative lookup; wrap the
* result in a capability.
*/
if ((error = kern_capwrap(td, fp, nd.ni_baserights,
&indx)) != 0)
goto bad;
} else
if (nd.ni_strictrelative == 1)
fcaps = &nd.ni_filecaps;
else
#endif
if ((error = finstall(td, fp, &indx, flags)) != 0)
goto bad;
fcaps = NULL;
error = finstall(td, fp, &indx, flags, fcaps);
/* On success finstall() consumes fcaps. */
if (error != 0) {
filecaps_free(&nd.ni_filecaps);
goto bad;
}
} else {
filecaps_free(&nd.ni_filecaps);
}
/*
@ -1279,7 +1284,7 @@ kern_mknodat(struct thread *td, int fd, char *path, enum uio_seg pathseg,
restart:
bwillwrite();
NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE1,
pathseg, path, fd, CAP_MKNOD, td);
pathseg, path, fd, CAP_MKNODAT, td);
if ((error = namei(&nd)) != 0)
return (error);
vp = nd.ni_vp;
@ -1399,7 +1404,7 @@ kern_mkfifoat(struct thread *td, int fd, char *path, enum uio_seg pathseg,
restart:
bwillwrite();
NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE1,
pathseg, path, fd, CAP_MKFIFO, td);
pathseg, path, fd, CAP_MKFIFOAT, td);
if ((error = namei(&nd)) != 0)
return (error);
if (nd.ni_vp != NULL) {
@ -1553,7 +1558,7 @@ kern_linkat(struct thread *td, int fd1, int fd2, char *path1, char *path2,
return (error);
}
NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE2,
segflg, path2, fd2, CAP_CREATE, td);
segflg, path2, fd2, CAP_LINKAT, td);
if ((error = namei(&nd)) == 0) {
if (nd.ni_vp != NULL) {
if (nd.ni_dvp == nd.ni_vp)
@ -1646,7 +1651,7 @@ kern_symlinkat(struct thread *td, char *path1, int fd, char *path2,
restart:
bwillwrite();
NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE1,
segflg, path2, fd, CAP_CREATE, td);
segflg, path2, fd, CAP_SYMLINKAT, td);
if ((error = namei(&nd)) != 0)
goto out;
if (nd.ni_vp) {
@ -1798,7 +1803,7 @@ kern_unlinkat(struct thread *td, int fd, char *path, enum uio_seg pathseg,
restart:
bwillwrite();
NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1,
pathseg, path, fd, CAP_DELETE, td);
pathseg, path, fd, CAP_UNLINKAT, td);
if ((error = namei(&nd)) != 0)
return (error == EINVAL ? EPERM : error);
vp = nd.ni_vp;
@ -3502,10 +3507,10 @@ kern_renameat(struct thread *td, int oldfd, char *old, int newfd, char *new,
bwillwrite();
#ifdef MAC
NDINIT_ATRIGHTS(&fromnd, DELETE, LOCKPARENT | LOCKLEAF | SAVESTART |
AUDITVNODE1, pathseg, old, oldfd, CAP_DELETE, td);
AUDITVNODE1, pathseg, old, oldfd, CAP_RENAMEAT, td);
#else
NDINIT_ATRIGHTS(&fromnd, DELETE, WANTPARENT | SAVESTART | AUDITVNODE1,
pathseg, old, oldfd, CAP_DELETE, td);
pathseg, old, oldfd, CAP_RENAMEAT, td);
#endif
if ((error = namei(&fromnd)) != 0)
@ -3527,7 +3532,7 @@ kern_renameat(struct thread *td, int oldfd, char *old, int newfd, char *new,
goto out1;
}
NDINIT_ATRIGHTS(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE |
SAVESTART | AUDITVNODE2, pathseg, new, newfd, CAP_CREATE, td);
SAVESTART | AUDITVNODE2, pathseg, new, newfd, CAP_LINKAT, td);
if (fromnd.ni_vp->v_type == VDIR)
tond.ni_cnd.cn_flags |= WILLBEDIR;
if ((error = namei(&tond)) != 0) {
@ -3550,6 +3555,15 @@ kern_renameat(struct thread *td, int oldfd, char *old, int newfd, char *new,
error = EISDIR;
goto out;
}
#ifdef CAPABILITIES
/*
* If the target already exists we require CAP_UNLINKAT
* from 'newfd'.
*/
error = cap_check(tond.ni_filecaps.fc_rights, CAP_UNLINKAT);
if (error != 0)
goto out;
#endif
}
if (fvp == tdvp) {
error = EINVAL;
@ -3650,7 +3664,7 @@ kern_mkdirat(struct thread *td, int fd, char *path, enum uio_seg segflg,
restart:
bwillwrite();
NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE1,
segflg, path, fd, CAP_MKDIR, td);
segflg, path, fd, CAP_MKDIRAT, td);
nd.ni_cnd.cn_flags |= WILLBEDIR;
if ((error = namei(&nd)) != 0)
return (error);
@ -3734,7 +3748,7 @@ kern_rmdirat(struct thread *td, int fd, char *path, enum uio_seg pathseg)
restart:
bwillwrite();
NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1,
pathseg, path, fd, CAP_RMDIR, td);
pathseg, path, fd, CAP_UNLINKAT, td);
if ((error = namei(&nd)) != 0)
return (error);
vp = nd.ni_vp;
@ -3987,8 +4001,7 @@ kern_getdirentries(struct thread *td, int fd, char *buf, u_int count,
if (count > IOSIZE_MAX)
return (EINVAL);
auio.uio_resid = count;
if ((error = getvnode(td->td_proc->p_fd, fd, CAP_READ | CAP_SEEK,
&fp)) != 0)
if ((error = getvnode(td->td_proc->p_fd, fd, CAP_READ, &fp)) != 0)
return (error);
if ((fp->f_flag & FREAD) == 0) {
fdrop(fp, td);
@ -4151,33 +4164,14 @@ sys_revoke(td, uap)
* entry is held upon returning.
*/
int
getvnode(struct filedesc *fdp, int fd, cap_rights_t rights,
struct file **fpp)
getvnode(struct filedesc *fdp, int fd, cap_rights_t rights, struct file **fpp)
{
struct file *fp;
#ifdef CAPABILITIES
struct file *fp_fromcap;
int error;
#endif
if (fdp == NULL || (fp = fget_unlocked(fdp, fd)) == NULL)
return (EBADF);
#ifdef CAPABILITIES
/*
* If the file descriptor is for a capability, test rights and use the
* file descriptor referenced by the capability.
*/
error = cap_funwrap(fp, rights, &fp_fromcap);
if (error) {
fdrop(fp, curthread);
error = fget_unlocked(fdp, fd, rights, 0, &fp, NULL);
if (error != 0)
return (error);
}
if (fp != fp_fromcap) {
fhold(fp_fromcap);
fdrop(fp, curthread);
fp = fp_fromcap;
}
#endif /* CAPABILITIES */
/*
* The file could be not of the vnode type, or it may be not
@ -4361,7 +4355,7 @@ sys_fhopen(td, uap)
goto bad;
}
error = finstall(td, fp, &indx, fmode);
error = finstall(td, fp, &indx, fmode, NULL);
bad:
fdrop(fp, td);
td->td_retval[0] = indx;
@ -4614,7 +4608,7 @@ kern_posix_fadvise(struct thread *td, int fd, off_t offset, off_t len,
return (EINVAL);
}
/* XXX: CAP_POSIX_FADVISE? */
error = fget(td, fd, 0, &fp);
error = fget(td, fd, CAP_NONE, &fp);
if (error != 0)
goto out;

View file

@ -399,9 +399,7 @@ nsmb_getfp(struct filedesc* fdp, int fd, int flag)
struct file* fp;
FILEDESC_SLOCK(fdp);
if (fd < 0 || fd >= fdp->fd_nfiles ||
(fp = fdp->fd_ofiles[fd]) == NULL ||
(fp->f_flag & flag) == 0) {
if ((fp = fget_locked(fdp, fd)) == NULL || (fp->f_flag & flag) == 0) {
FILEDESC_SUNLOCK(fdp);
return (NULL);
}

View file

@ -174,7 +174,8 @@ nfssvc_nfsserver(struct thread *td, struct nfssvc_args *uap)
sizeof(addsockarg));
if (error)
return (error);
if ((error = fget(td, addsockarg.sock, CAP_SOCK_ALL, &fp)) != 0)
error = fget(td, addsockarg.sock, CAP_SOCK_SERVER, &fp);
if (error)
return (error);
if (fp->f_type != DTYPE_SOCKET) {
fdrop(fp, td);

View file

@ -47,7 +47,8 @@ linux_fget(unsigned int fd)
{
struct file *file;
file = fget_unlocked(curthread->td_proc->p_fd, fd);
if (fget_unlocked(curthread->td_proc->p_fd, fd, 0, 0, &file, NULL) != 0)
return (NULL);
return (struct linux_file *)file->f_data;
}
@ -69,8 +70,7 @@ put_unused_fd(unsigned int fd)
{
struct file *file;
file = fget_unlocked(curthread->td_proc->p_fd, fd);
if (file == NULL)
if (fget_unlocked(curthread->td_proc->p_fd, fd, 0, 0, &file, NULL) != 0)
return;
fdclose(curthread->td_proc->p_fd, file, fd, curthread);
}
@ -80,7 +80,8 @@ fd_install(unsigned int fd, struct linux_file *filp)
{
struct file *file;
file = fget_unlocked(curthread->td_proc->p_fd, fd);
if (fget_unlocked(curthread->td_proc->p_fd, fd, 0, 0, &file, NULL) != 0)
file = NULL;
filp->_file = file;
finit(file, filp->f_mode, DTYPE_DEV, filp, &linuxfileops);
}

View file

@ -115,6 +115,7 @@ void audit_arg_file(struct proc *p, struct file *fp);
void audit_arg_argv(char *argv, int argc, int length);
void audit_arg_envv(char *envv, int envc, int length);
void audit_arg_rights(cap_rights_t rights);
void audit_arg_fcntl_rights(uint32_t fcntlrights);
void audit_sysclose(struct thread *td, int fd);
void audit_cred_copy(struct ucred *src, struct ucred *dest);
void audit_cred_destroy(struct ucred *cred);
@ -241,6 +242,11 @@ void audit_thread_free(struct thread *td);
audit_arg_rights((rights)); \
} while (0)
#define AUDIT_ARG_FCNTL_RIGHTS(fcntlrights) do { \
if (AUDITING_TD(curthread)) \
audit_arg_fcntl_rights((fcntlrights)); \
} while (0)
#define AUDIT_ARG_RUID(ruid) do { \
if (AUDITING_TD(curthread)) \
audit_arg_ruid((ruid)); \
@ -354,6 +360,7 @@ void audit_thread_free(struct thread *td);
#define AUDIT_ARG_PROCESS(p)
#define AUDIT_ARG_RGID(rgid)
#define AUDIT_ARG_RIGHTS(rights)
#define AUDIT_ARG_FCNTL_RIGHTS(fcntlrights)
#define AUDIT_ARG_RUID(ruid)
#define AUDIT_ARG_SIGNUM(signum)
#define AUDIT_ARG_SGID(sgid)

View file

@ -871,6 +871,19 @@ audit_arg_rights(cap_rights_t rights)
ARG_SET_VALID(ar, ARG_RIGHTS);
}
void
audit_arg_fcntl_rights(uint32_t fcntlrights)
{
struct kaudit_record *ar;
ar = currecord();
if (ar == NULL)
return;
ar->k_ar.ar_arg_fcntl_rights = fcntlrights;
ARG_SET_VALID(ar, ARG_FCNTL_RIGHTS);
}
/*
* The close() system call uses it's own audit call to capture the path/vnode
* information because those pieces are not easily obtained within the system

View file

@ -1597,6 +1597,7 @@ kaudit_to_bsm(struct kaudit_record *kar, struct au_record **pau)
break;
case AUE_CAP_NEW:
case AUE_CAP_RIGHTS_LIMIT:
/*
* XXXRW/XXXJA: Would be nice to audit socket/etc information.
*/
@ -1607,13 +1608,25 @@ kaudit_to_bsm(struct kaudit_record *kar, struct au_record **pau)
}
break;
case AUE_CAP_GETRIGHTS:
case AUE_CAP_FCNTLS_GET:
case AUE_CAP_IOCTLS_GET:
case AUE_CAP_IOCTLS_LIMIT:
case AUE_CAP_RIGHTS_GET:
if (ARG_IS_VALID(kar, ARG_FD)) {
tok = au_to_arg32(1, "fd", ar->ar_arg_fd);
kau_write(rec, tok);
}
break;
case AUE_CAP_FCNTLS_LIMIT:
FD_VNODE1_TOKENS;
if (ARG_IS_VALID(kar, ARG_FCNTL_RIGHTS)) {
tok = au_to_arg32(2, "fcntlrights",
ar->ar_arg_fcntl_rights);
kau_write(rec, tok);
}
break;
case AUE_CAP_ENTER:
case AUE_CAP_GETMODE:
break;

View file

@ -230,6 +230,7 @@ struct audit_record {
int ar_arg_exitretval;
struct sockaddr_storage ar_arg_sockaddr;
cap_rights_t ar_arg_rights;
uint32_t ar_arg_fcntl_rights;
char ar_jailname[MAXHOSTNAMELEN];
};
@ -291,6 +292,7 @@ struct audit_record {
#define ARG_ATFD1 0x0004000000000000ULL
#define ARG_ATFD2 0x0008000000000000ULL
#define ARG_RIGHTS 0x0010000000000000ULL
#define ARG_FCNTL_RIGHTS 0x0020000000000000ULL
#define ARG_NONE 0x0000000000000000ULL
#define ARG_ALL 0xFFFFFFFFFFFFFFFFULL

View file

@ -1,10 +1,14 @@
/*-
* Copyright (c) 2008-2010 Robert N. M. Watson
* Copyright (c) 2012 FreeBSD Foundation
* All rights reserved.
*
* This software was developed at the University of Cambridge Computer
* Laboratory with support from a grant from Google, Inc.
*
* Portions of this software were developed by Pawel Jakub Dawidek under
* sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -36,9 +40,10 @@
#define _SYS_CAPABILITY_H_
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/file.h>
#include <sys/fcntl.h>
/*
* Possible rights on capabilities.
@ -54,34 +59,69 @@
* involve reads or writes depending a great deal on context.
*/
/* General file I/O. */
#define CAP_READ 0x0000000000000001ULL /* read/recv */
#define CAP_WRITE 0x0000000000000002ULL /* write/send */
#define CAP_MMAP 0x0000000000000004ULL /* mmap */
#define CAP_MAPEXEC 0x0000000000000008ULL /* mmap(2) as exec */
#define CAP_FEXECVE 0x0000000000000010ULL
#define CAP_FSYNC 0x0000000000000020ULL
#define CAP_FTRUNCATE 0x0000000000000040ULL
#define CAP_NONE 0x0000000000000000ULL
/*
* General file I/O.
*/
/* Allows for openat(O_RDONLY), read(2), readv(2). */
#define CAP_READ 0x0000000000000001ULL
/* Allows for openat(O_WRONLY | O_APPEND), write(2), writev(2). */
#define CAP_WRITE 0x0000000000000002ULL
/* Allows for lseek(2). */
#define CAP_SEEK 0x0000000000000080ULL
/* Allows for pread(2), preadv(2). */
#define CAP_PREAD (CAP_SEEK | CAP_READ)
/* Allows for openat(O_WRONLY) (without O_APPEND), pwrite(2), pwritev(2). */
#define CAP_PWRITE (CAP_SEEK | CAP_WRITE)
/* Allows for mmap(PROT_NONE). */
#define CAP_MMAP 0x0000000000000004ULL
/* Allows for mmap(PROT_READ). */
#define CAP_MMAP_R (CAP_MMAP | CAP_SEEK | CAP_READ)
/* Allows for mmap(PROT_WRITE). */
#define CAP_MMAP_W (CAP_MMAP | CAP_SEEK | CAP_WRITE)
/* Allows for mmap(PROT_EXEC). */
#define CAP_MMAP_X (CAP_MMAP | CAP_SEEK | 0x0000000000000008ULL)
/* Allows for mmap(PROT_READ | PROT_WRITE). */
#define CAP_MMAP_RW (CAP_MMAP_R | CAP_MMAP_W)
/* Allows for mmap(PROT_READ | PROT_EXEC). */
#define CAP_MMAP_RX (CAP_MMAP_R | CAP_MMAP_X)
/* Allows for mmap(PROT_WRITE | PROT_EXEC). */
#define CAP_MMAP_WX (CAP_MMAP_W | CAP_MMAP_X)
/* Allows for mmap(PROT_READ | PROT_WRITE | PROT_EXEC). */
#define CAP_MMAP_RWX (CAP_MMAP_R | CAP_MMAP_W | CAP_MMAP_X)
/* Allows for openat(O_CREAT). */
#define CAP_CREATE 0x0000000000080000ULL
/* Allows for openat(O_EXEC) and fexecve(2) in turn. */
#define CAP_FEXECVE 0x0000000000000010ULL
/* Allows for openat(O_SYNC), openat(O_FSYNC), fsync(2). */
#define CAP_FSYNC 0x0000000000000020ULL
/* Allows for openat(O_TRUNC), ftruncate(2). */
#define CAP_FTRUNCATE 0x0000000000000040ULL
/* VFS methods. */
#define CAP_FCHFLAGS 0x0000000000000100ULL
#define CAP_FCHDIR 0x0000000000000200ULL
#define CAP_FCHFLAGS 0x0000000000000100ULL
#define CAP_FCHMOD 0x0000000000000400ULL
#define CAP_FCHMODAT CAP_FCHMOD
#define CAP_FCHOWN 0x0000000000000800ULL
#define CAP_FCHOWNAT CAP_FCHOWN
#define CAP_FCNTL 0x0000000000001000ULL
#define CAP_FPATHCONF 0x0000000000002000ULL
#define CAP_FLOCK 0x0000000000004000ULL
#define CAP_FPATHCONF 0x0000000000002000ULL
#define CAP_FSCK 0x0000000000008000ULL
#define CAP_FSTAT 0x0000000000010000ULL
#define CAP_FSTATAT CAP_FSTAT
#define CAP_FSTATFS 0x0000000000020000ULL
#define CAP_FUTIMES 0x0000000000040000ULL
#define CAP_CREATE 0x0000000000080000ULL
#define CAP_DELETE 0x0000000000100000ULL
#define CAP_MKDIR 0x0000000000200000ULL
#define CAP_RMDIR 0x0000000000400000ULL
#define CAP_MKFIFO 0x0000000000800000ULL
#define CAP_MKNOD 0x0080000000000000ULL
#define CAP_FUTIMESAT CAP_FUTIMES
#define CAP_LINKAT 0x0000000000400000ULL
#define CAP_MKDIRAT 0x0000000000200000ULL
#define CAP_MKFIFOAT 0x0000000000800000ULL
#define CAP_MKNODAT 0x0080000000000000ULL
#define CAP_RENAMEAT 0x0200000000000000ULL
#define CAP_SYMLINKAT 0x0100000000000000ULL
#define CAP_UNLINKAT 0x0000000000100000ULL
/* Lookups - used to constrain *at() calls. */
#define CAP_LOOKUP 0x0000000001000000ULL
@ -107,13 +147,18 @@
#define CAP_GETSOCKOPT 0x0000004000000000ULL
#define CAP_LISTEN 0x0000008000000000ULL
#define CAP_PEELOFF 0x0000010000000000ULL
#define CAP_RECV CAP_READ
#define CAP_SEND CAP_WRITE
#define CAP_SETSOCKOPT 0x0000020000000000ULL
#define CAP_SHUTDOWN 0x0000040000000000ULL
#define CAP_SOCK_ALL \
(CAP_ACCEPT | CAP_BIND | CAP_CONNECT \
| CAP_GETPEERNAME | CAP_GETSOCKNAME | CAP_GETSOCKOPT \
| CAP_LISTEN | CAP_PEELOFF | CAP_SETSOCKOPT | CAP_SHUTDOWN)
#define CAP_SOCK_CLIENT \
(CAP_CONNECT | CAP_GETPEERNAME | CAP_GETSOCKNAME | CAP_GETSOCKOPT | \
CAP_PEELOFF | CAP_RECV | CAP_SEND | CAP_SETSOCKOPT | CAP_SHUTDOWN)
#define CAP_SOCK_SERVER \
(CAP_ACCEPT | CAP_BIND | CAP_GETPEERNAME | CAP_GETSOCKNAME | \
CAP_GETSOCKOPT | CAP_LISTEN | CAP_PEELOFF | CAP_RECV | CAP_SEND | \
CAP_SETSOCKOPT | CAP_SHUTDOWN)
/* Mandatory Access Control. */
#define CAP_MAC_GET 0x0000080000000000ULL
@ -138,40 +183,77 @@
#define CAP_PDKILL 0x0040000000000000ULL
/* The mask of all valid method rights. */
#define CAP_MASK_VALID 0x00ffffffffffffffULL
#define CAP_MASK_VALID 0x03ffffffffffffffULL
#define CAP_ALL CAP_MASK_VALID
/* Available bits. */
#define CAP_UNUSED5 0x0400000000000000ULL
#define CAP_UNUSED4 0x0800000000000000ULL
#define CAP_UNUSED3 0x1000000000000000ULL
#define CAP_UNUSED2 0x2000000000000000ULL
#define CAP_UNUSED1 0x4000000000000000ULL
#define CAP_UNUSED0 0x8000000000000000ULL
/*
* The following defines are provided for backward API compatibility and
* should not be used in new code.
*/
#define CAP_MAPEXEC CAP_MMAP_X
#define CAP_DELETE CAP_UNLINKAT
#define CAP_MKDIR CAP_MKDIRAT
#define CAP_RMDIR CAP_UNLINKAT
#define CAP_MKFIFO CAP_MKFIFOAT
#define CAP_MKNOD CAP_MKNODAT
#define CAP_SOCK_ALL (CAP_SOCK_CLIENT | CAP_SOCK_SERVER)
/*
* Allowed fcntl(2) commands.
*/
#define CAP_FCNTL_GETFL (1 << F_GETFL)
#define CAP_FCNTL_SETFL (1 << F_SETFL)
#if __BSD_VISIBLE || __XSI_VISIBLE || __POSIX_VISIBLE >= 200112
#define CAP_FCNTL_GETOWN (1 << F_GETOWN)
#define CAP_FCNTL_SETOWN (1 << F_SETOWN)
#endif
#if __BSD_VISIBLE || __XSI_VISIBLE || __POSIX_VISIBLE >= 200112
#define CAP_FCNTL_ALL (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL | \
CAP_FCNTL_GETOWN | CAP_FCNTL_SETOWN)
#else
#define CAP_FCNTL_ALL (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL)
#endif
#define CAP_IOCTLS_ALL SSIZE_MAX
#ifdef _KERNEL
#define IN_CAPABILITY_MODE(td) (td->td_ucred->cr_flags & CRED_FLAG_CAPMODE)
#include <sys/systm.h>
#define IN_CAPABILITY_MODE(td) ((td->td_ucred->cr_flags & CRED_FLAG_CAPMODE) != 0)
struct filedesc;
/*
* Create a capability to wrap a file object.
* Test whether a capability grants the requested rights.
*/
int kern_capwrap(struct thread *td, struct file *fp, cap_rights_t rights,
int *capfd);
int cap_check(cap_rights_t have, cap_rights_t need);
/*
* Unwrap a capability if its rights mask is a superset of 'rights'.
*
* Unwrapping a non-capability is effectively a no-op; the value of fp_cap
* is simply copied into fpp.
* Convert capability rights into VM access flags.
*/
int cap_funwrap(struct file *fp_cap, cap_rights_t rights,
struct file **fpp);
int cap_funwrap_mmap(struct file *fp_cap, cap_rights_t rights,
u_char *maxprotp, struct file **fpp);
u_char cap_rights_to_vmprot(cap_rights_t have);
/*
* For the purposes of procstat(1) and similar tools, allow kern_descrip.c to
* extract the rights from a capability. However, this should not be used by
* kernel code generally, instead cap_funwrap() should be used in order to
* keep all access control in one place.
* extract the rights from a capability.
*/
cap_rights_t cap_rights(struct file *fp_cap);
cap_rights_t cap_rights(struct filedesc *fdp, int fd);
int cap_ioctl_check(struct filedesc *fdp, int fd, u_long cmd);
int cap_fcntl_check(struct filedesc *fdp, int fd, int cmd);
#else /* !_KERNEL */
__BEGIN_DECLS
#include <stdbool.h>
/*
* cap_enter(): Cause the process to enter capability mode, which will
@ -186,22 +268,47 @@ __BEGIN_DECLS
*/
int cap_enter(void);
/*
* Are we sandboxed (in capability mode)?
* This is a libc wrapper around the cap_getmode(2) system call.
*/
bool cap_sandboxed(void);
/*
* cap_getmode(): Are we in capability mode?
*/
int cap_getmode(u_int* modep);
int cap_getmode(u_int *modep);
/*
* cap_new(): Create a new capability derived from an existing file
* descriptor with the specified rights. If the existing file descriptor is
* a capability, then the new rights must be a subset of the existing rights.
* Limits capability rights for the given descriptor (CAP_*).
*/
int cap_new(int fd, cap_rights_t rights);
int cap_rights_limit(int fd, cap_rights_t rights);
/*
* cap_getrights(): Query the rights on a capability.
* Returns bitmask of capability rights for the given descriptor.
*/
int cap_getrights(int fd, cap_rights_t *rightsp);
int cap_rights_get(int fd, cap_rights_t *rightsp);
/*
* Limits allowed ioctls for the given descriptor.
*/
int cap_ioctls_limit(int fd, const unsigned long *cmds, size_t ncmds);
/*
* Returns array of allowed ioctls for the given descriptor.
* If all ioctls are allowed, the cmds array is not populated and
* the function returns CAP_IOCTLS_ALL.
*/
ssize_t cap_ioctls_get(int fd, unsigned long *cmds, size_t maxcmds);
/*
* Limits allowed fcntls for the given descriptor (CAP_FCNTL_*).
*/
int cap_fcntls_limit(int fd, uint32_t fcntlrights);
/*
* Returns bitmask of allowed fcntls for the given descriptor.
*/
int cap_fcntls_get(int fd, uint32_t *fcntlrightsp);
/* For backward compatibility. */
int cap_new(int fd, cap_rights_t rights);
#define cap_getrights(fd, rightsp) cap_rights_get((fd), (rightsp))
__END_DECLS

View file

@ -64,12 +64,12 @@ struct socket;
#define DTYPE_SEM 9 /* posix semaphore */
#define DTYPE_PTS 10 /* pseudo teletype master device */
#define DTYPE_DEV 11 /* Device specific fd type */
#define DTYPE_CAPABILITY 12 /* capability */
#define DTYPE_PROCDESC 13 /* process descriptor */
#define DTYPE_PROCDESC 12 /* process descriptor */
#ifdef _KERNEL
struct file;
struct filecaps;
struct ucred;
#define FOF_OFFSET 0x01 /* Use the offset in uio argument */
@ -217,7 +217,6 @@ int fget_read(struct thread *td, int fd, cap_rights_t rights,
struct file **fpp);
int fget_write(struct thread *td, int fd, cap_rights_t rights,
struct file **fpp);
int fgetcap(struct thread *td, int fd, struct file **fpp);
int _fdrop(struct file *fp, struct thread *td);
/*
@ -242,7 +241,7 @@ int fgetvp(struct thread *td, int fd, cap_rights_t rights, struct vnode **vpp);
int fgetvp_exec(struct thread *td, int fd, cap_rights_t rights,
struct vnode **vpp);
int fgetvp_rights(struct thread *td, int fd, cap_rights_t need,
cap_rights_t *have, struct vnode **vpp);
struct filecaps *havecaps, struct vnode **vpp);
int fgetvp_read(struct thread *td, int fd, cap_rights_t rights,
struct vnode **vpp);
int fgetvp_write(struct thread *td, int fd, cap_rights_t rights,

View file

@ -41,6 +41,23 @@
#include <machine/_limits.h>
struct filecaps {
cap_rights_t fc_rights; /* per-descriptor capability rights */
uint32_t fc_fcntls; /* per-descriptor allowed fcntls */
u_long *fc_ioctls; /* per-descriptor allowed ioctls */
int16_t fc_nioctls; /* fc_ioctls array size */
};
struct filedescent {
struct file *fde_file; /* file structure for open file */
struct filecaps fde_caps; /* per-descriptor rights */
uint8_t fde_flags; /* per-process open file flags */
};
#define fde_rights fde_caps.fc_rights
#define fde_fcntls fde_caps.fc_fcntls
#define fde_ioctls fde_caps.fc_ioctls
#define fde_nioctls fde_caps.fc_nioctls
/*
* This structure is used for the management of descriptors. It may be
* shared by multiple processes.
@ -48,8 +65,7 @@
#define NDSLOTTYPE u_long
struct filedesc {
struct file **fd_ofiles; /* file structures for open files */
char *fd_ofileflags; /* per-process open file flags */
struct filedescent *fd_ofiles; /* open files */
struct vnode *fd_cdir; /* current directory */
struct vnode *fd_rdir; /* root directory */
struct vnode *fd_jdir; /* jail root directory */
@ -92,6 +108,15 @@ struct filedesc_to_leader {
#ifdef _KERNEL
#include <sys/systm.h> /* CTASSERT() */
CTASSERT(sizeof(cap_rights_t) == sizeof(uint64_t));
/* Flags for do_dup() */
#define DUP_FIXED 0x1 /* Force fixed allocation. */
#define DUP_FCNTL 0x2 /* fcntl()-style errors. */
#define DUP_CLOEXEC 0x4 /* Atomically set FD_CLOEXEC. */
/* Lock a file descriptor table. */
#define FILEDESC_LOCK_INIT(fdp) sx_init(&(fdp)->fd_sx, "filedesc structure")
#define FILEDESC_LOCK_DESTROY(fdp) sx_destroy(&(fdp)->fd_sx)
@ -109,13 +134,20 @@ struct filedesc_to_leader {
struct thread;
void filecaps_init(struct filecaps *fcaps);
void filecaps_copy(const struct filecaps *src, struct filecaps *dst);
void filecaps_free(struct filecaps *fcaps);
int closef(struct file *fp, struct thread *td);
int do_dup(struct thread *td, int flags, int old, int new,
register_t *retval);
int dupfdopen(struct thread *td, struct filedesc *fdp, int dfd, int mode,
int openerror, int *indxp);
int falloc(struct thread *td, struct file **resultfp, int *resultfd,
int flags);
int falloc_noinstall(struct thread *td, struct file **resultfp);
int finstall(struct thread *td, struct file *fp, int *resultfp, int flags);
int finstall(struct thread *td, struct file *fp, int *resultfp, int flags,
struct filecaps *fcaps);
int fdalloc(struct thread *td, int minfd, int *result);
int fdavail(struct thread *td, int n);
int fdcheckstd(struct thread *td);
@ -123,7 +155,7 @@ void fdclose(struct filedesc *fdp, struct file *fp, int idx, struct thread *td);
void fdcloseexec(struct thread *td);
struct filedesc *fdcopy(struct filedesc *fdp);
void fdunshare(struct proc *p, struct thread *td);
void fdfree(struct thread *td);
void fdescfree(struct thread *td);
struct filedesc *fdinit(struct filedesc *fdp);
struct filedesc *fdshare(struct filedesc *fdp);
struct filedesc_to_leader *
@ -135,7 +167,8 @@ void mountcheckdirs(struct vnode *olddp, struct vnode *newdp);
void setugidsafety(struct thread *td);
/* Return a referenced file from an unlocked descriptor. */
struct file *fget_unlocked(struct filedesc *fdp, int fd);
int fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t needrights,
int needfcntl, struct file **fpp, cap_rights_t *haverightsp);
/* Requires a FILEDESC_{S,X}LOCK held and returns without a ref. */
static __inline struct file *
@ -147,7 +180,7 @@ fget_locked(struct filedesc *fdp, int fd)
if (fd < 0 || fd >= fdp->fd_nfiles)
return (NULL);
return (fdp->fd_ofiles[fd]);
return (fdp->fd_ofiles[fd].fde_file);
}
#endif /* _KERNEL */

View file

@ -33,6 +33,7 @@
#ifndef _SYS_NAMEI_H_
#define _SYS_NAMEI_H_
#include <sys/filedesc.h>
#include <sys/queue.h>
#include <sys/uio.h>
@ -75,7 +76,7 @@ struct nameidata {
/*
* Results: returned from namei
*/
cap_rights_t ni_baserights; /* rights the *at base has (or -1) */
struct filecaps ni_filecaps; /* rights the *at base has */
/*
* Results: returned from/manipulated by lookup
*/
@ -180,7 +181,7 @@ NDINIT_ALL(struct nameidata *ndp,
ndp->ni_startdir = startdir;
ndp->ni_strictrelative = 0;
ndp->ni_rightsneeded = rights;
ndp->ni_baserights = 0;
filecaps_init(&ndp->ni_filecaps);
ndp->ni_cnd.cn_thread = td;
}

View file

@ -251,8 +251,7 @@ struct user {
#define KF_TYPE_SHM 8
#define KF_TYPE_SEM 9
#define KF_TYPE_PTS 10
/* no KF_TYPE_CAPABILITY (11), since capabilities wrap other file objects */
#define KF_TYPE_PROCDESC 12
#define KF_TYPE_PROCDESC 11
#define KF_TYPE_UNKNOWN 255
#define KF_VTYPE_VNON 0
@ -288,7 +287,6 @@ struct user {
#define KF_FLAG_TRUNC 0x00001000
#define KF_FLAG_EXCL 0x00002000
#define KF_FLAG_EXEC 0x00004000
#define KF_FLAG_CAPABILITY 0x00008000
/*
* Old format. Has variable hidden padding due to alignment.

View file

@ -305,13 +305,13 @@ sys_mmap(td, uap)
*/
rights = CAP_MMAP;
if (prot & PROT_READ)
rights |= CAP_READ;
rights |= CAP_MMAP_R;
if ((flags & MAP_SHARED) != 0) {
if (prot & PROT_WRITE)
rights |= CAP_WRITE;
rights |= CAP_MMAP_W;
}
if (prot & PROT_EXEC)
rights |= CAP_MAPEXEC;
rights |= CAP_MMAP_X;
if ((error = fget_mmap(td, uap->fd, rights, &cap_maxprot,
&fp)) != 0)
goto done;

View file

@ -1008,6 +1008,7 @@ ktrsyscall(struct ktr_syscall *ktr, u_int flags)
narg--;
break;
case SYS_cap_new:
case SYS_cap_rights_limit:
print_number(ip, narg, c);
putchar(',');
arg = *ip;
@ -1035,6 +1036,14 @@ ktrsyscall(struct ktr_syscall *ktr, u_int flags)
}
capname(arg);
break;
case SYS_cap_fcntls_limit:
print_number(ip, narg, c);
putchar(',');
arg = *ip;
ip++;
narg--;
capfcntlname(arg);
break;
case SYS_posix_fadvise:
print_number(ip, narg, c);
print_number(ip, narg, c);

View file

@ -361,6 +361,7 @@ _EOF_
auto_or_type "accessmodename" "[A-Z]_OK[[:space:]]+0?x?[0-9A-Fa-f]+" "sys/unistd.h"
auto_switch_type "acltypename" "ACL_TYPE_[A-Z4_]+[[:space:]]+0x[0-9]+" "sys/acl.h"
auto_or_type "capname" "CAP_[A-Z]+[[:space:]]+0x[01248]{16}ULL" "sys/capability.h"
auto_or_type "capfcntlname" "CAP_FCNTL_[A-Z]+[[:space:]]+\(1" "sys/capability.h"
auto_switch_type "extattrctlname" "EXTATTR_NAMESPACE_[A-Z]+[[:space:]]+0x[0-9]+" "sys/extattr.h"
auto_switch_type "fadvisebehavname" "POSIX_FADV_[A-Z]+[[:space:]]+[0-9]+" "sys/fcntl.h"
auto_or_type "flagsname" "O_[A-Z]+[[:space:]]+0x[0-9A-Fa-f]+" "sys/fcntl.h"

View file

@ -139,33 +139,34 @@ static struct cap_desc {
/* General file I/O. */
{ CAP_READ, "rd" },
{ CAP_WRITE, "wr" },
{ CAP_SEEK, "se" },
{ CAP_MMAP, "mm" },
{ CAP_MAPEXEC, "me" },
{ CAP_CREATE, "cr" },
{ CAP_FEXECVE, "fe" },
{ CAP_FSYNC, "fy" },
{ CAP_FTRUNCATE, "ft" },
{ CAP_SEEK, "se" },
/* VFS methods. */
{ CAP_FCHFLAGS, "cf" },
{ CAP_FCHDIR, "cd" },
{ CAP_FCHFLAGS, "cf" },
{ CAP_FCHMOD, "cm" },
{ CAP_FCHOWN, "cn" },
{ CAP_FCNTL, "fc" },
{ CAP_FPATHCONF, "fp" },
{ CAP_FLOCK, "fl" },
{ CAP_FPATHCONF, "fp" },
{ CAP_FSCK, "fk" },
{ CAP_FSTAT, "fs" },
{ CAP_FSTATFS, "sf" },
{ CAP_FUTIMES, "fu" },
{ CAP_CREATE, "cr" },
{ CAP_DELETE, "de" },
{ CAP_MKDIR, "md" },
{ CAP_RMDIR, "rm" },
{ CAP_MKFIFO, "mf" },
{ CAP_MKNOD, "mn" },
{ CAP_LINKAT, "li" },
{ CAP_MKDIRAT, "md" },
{ CAP_MKFIFOAT, "mf" },
{ CAP_MKNODAT, "mn" },
{ CAP_RENAMEAT, "rn" },
{ CAP_SYMLINKAT, "sl" },
{ CAP_UNLINKAT, "un" },
/* Lookups - used to constraint *at() calls. */
/* Lookups - used to constrain *at() calls. */
{ CAP_LOOKUP, "lo" },
/* Extended attributes. */
@ -213,6 +214,24 @@ static struct cap_desc {
{ CAP_PDGETPID, "pg" },
{ CAP_PDWAIT, "pw" },
{ CAP_PDKILL, "pk" },
/* Aliases and defines that combine multiple rights. */
{ CAP_PREAD, "prd" },
{ CAP_PWRITE, "pwr" },
{ CAP_MMAP_R, "mmr" },
{ CAP_MMAP_W, "mmw" },
{ CAP_MMAP_X, "mmx" },
{ CAP_MMAP_RW, "mrw" },
{ CAP_MMAP_RX, "mrx" },
{ CAP_MMAP_WX, "mwx" },
{ CAP_MMAP_RWX, "mma" },
{ CAP_RECV, "re" },
{ CAP_SEND, "sd" },
{ CAP_SOCK_CLIENT, "scl" },
{ CAP_SOCK_SERVER, "ssr" },
};
static const u_int cap_desc_count = sizeof(cap_desc) /
sizeof(cap_desc[0]);
@ -225,7 +244,7 @@ width_capability(cap_rights_t rights)
count = 0;
width = 0;
for (i = 0; i < cap_desc_count; i++) {
if (rights & cap_desc[i].cd_right) {
if ((cap_desc[i].cd_right & ~rights) == 0) {
width += strlen(cap_desc[i].cd_desc);
if (count)
width++;
@ -249,7 +268,7 @@ print_capability(cap_rights_t rights, u_int capwidth)
printf("-");
}
for (i = 0; i < cap_desc_count; i++) {
if (rights & cap_desc[i].cd_right) {
if ((cap_desc[i].cd_right & ~rights) == 0) {
printf("%s%s", count ? "," : "", cap_desc[i].cd_desc);
width += strlen(cap_desc[i].cd_desc);
if (count)
@ -261,7 +280,7 @@ print_capability(cap_rights_t rights, u_int capwidth)
void
procstat_files(struct procstat *procstat, struct kinfo_proc *kipp)
{
{
struct sockstat sock;
struct filestat_list *head;
struct filestat *fst;
@ -423,8 +442,6 @@ procstat_files(struct procstat *procstat, struct kinfo_proc *kipp)
printf("%s", fst->fs_fflags & PS_FST_FFLAG_NONBLOCK ? "n" : "-");
printf("%s", fst->fs_fflags & PS_FST_FFLAG_DIRECT ? "d" : "-");
printf("%s", fst->fs_fflags & PS_FST_FFLAG_HASLOCK ? "l" : "-");
printf("%s ", fst->fs_fflags & PS_FST_FFLAG_CAPABILITY ?
"c" : "-");
if (!Cflag) {
if (fst->fs_ref_count > -1)
printf("%3d ", fst->fs_ref_count);