Add kernel functions to unwrap capabilities.

cap_funwrap() and cap_funwrap_mmap() unwrap capabilities, exposing the
underlying object. Attempting to unwrap a capability with an inadequate
rights mask (e.g. calling cap_funwrap(fp, CAP_WRITE | CAP_MMAP, &result)
on a capability whose rights mask is CAP_READ | CAP_MMAP) will result in
ENOTCAPABLE.

Unwrapping a non-capability is effectively a no-op.

These functions will be used by Capsicum-aware versions of _fget(), etc.

Approved by: mentor (rwatson), re (Capsicum blanket)
Sponsored by: Google Inc
This commit is contained in:
Jonathan Anderson 2011-07-04 14:40:32 +00:00
parent f8dd68c912
commit af098ed8e7
2 changed files with 162 additions and 0 deletions

View File

@ -116,3 +116,125 @@ cap_getmode(struct thread *td, struct cap_getmode_args *uap)
}
#endif /* CAPABILITY_MODE */
#ifdef 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. cap_filelist may change when other capabilites are
* added or removed from the same file, and is currently protected by the
* pool mutex for the object file descriptor.
*/
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. */
LIST_ENTRY(capability) cap_filelist; /* Object's cap list. */
};
/*
* 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)
return (ENOTCAPABLE);
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)
{
struct capability *c;
int error;
if (fp_cap->f_type != DTYPE_CAPABILITY) {
*fpp = fp_cap;
return (0);
}
c = fp_cap->f_data;
error = cap_check(c, rights);
if (error)
return (error);
*fpp = c->cap_object;
return (0);
}
/*
* 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.
*/
int
cap_funwrap_mmap(struct file *fp_cap, cap_rights_t rights, u_char *maxprotp,
struct file **fpp)
{
struct capability *c;
u_char maxprot;
int error;
if (fp_cap->f_type != DTYPE_CAPABILITY) {
*fpp = fp_cap;
*maxprotp = VM_PROT_ALL;
return (0);
}
c = fp_cap->f_data;
error = cap_check(c, rights | CAP_MMAP);
if (error)
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;
return (0);
}
#else /* !CAPABILITIES */
/*
* Stub Capability functions for when options CAPABILITIES isn't compiled
* into the kernel.
*/
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

@ -38,10 +38,50 @@
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/file.h>
/*
* Possible rights on capabilities.
*
* Notes:
* Some system calls don't require a capability in order to perform an
* operation on an fd. These include: close, dup, dup2.
*
* sendfile is authorized using CAP_READ on the file and CAP_WRITE on the
* socket.
*
* mmap() and aio*() system calls will need special attention as they may
* involve reads or writes depending a great deal on context.
*/
#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_MASK_VALID 0x000000000000000fULL
#ifdef _KERNEL
#define IN_CAPABILITY_MODE(td) (td->td_ucred->cr_flags & CRED_FLAG_CAPMODE)
/*
* 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.
*/
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);
/*
* 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.
*/
cap_rights_t cap_rights(struct file *fp_cap);
#else /* !_KERNEL */
__BEGIN_DECLS