diff options
author | Jonathan Anderson <jonathan@FreeBSD.org> | 2011-07-15 09:37:14 +0000 |
---|---|---|
committer | Jonathan Anderson <jonathan@FreeBSD.org> | 2011-07-15 09:37:14 +0000 |
commit | 745bae379d1c9f7946219fd17cfce6f16498c86f (patch) | |
tree | 9db0c82a705512fa5a96522d6f0cde4c6be56a67 /sys/kern/sys_capability.c | |
parent | cb065959480cbd561b8953fb536c2ae2b8411abe (diff) | |
download | src-745bae379d1c9f7946219fd17cfce6f16498c86f.tar.gz src-745bae379d1c9f7946219fd17cfce6f16498c86f.zip |
Add implementation for capabilities.
Code to actually implement Capsicum capabilities, including fileops and
kern_capwrap(), which creates a capability to wrap an existing file
descriptor.
We also modify kern_close() and closef() to handle capabilities.
Finally, remove cap_filelist from struct capability, since we don't
actually need it.
Approved by: mentor (rwatson), re (Capsicum blanket)
Sponsored by: Google Inc
Notes
Notes:
svn path=/head/; revision=224056
Diffstat (limited to 'sys/kern/sys_capability.c')
-rw-r--r-- | sys/kern/sys_capability.c | 218 |
1 files changed, 214 insertions, 4 deletions
diff --git a/sys/kern/sys_capability.c b/sys/kern/sys_capability.c index 8216beada342..9cbe4627dea1 100644 --- a/sys/kern/sys_capability.c +++ b/sys/kern/sys_capability.c @@ -123,18 +123,66 @@ cap_getmode(struct thread *td, struct cap_getmode_args *uap) * 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. + * 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. */ - LIST_ENTRY(capability) cap_filelist; /* Object's cap list. */ }; /* + * 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 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_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_flags = 0, +}; + +static uma_zone_t capability_zone; + +static void +capability_init(void *dummy __unused) +{ + + 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 @@ -147,6 +195,85 @@ cap_check(struct capability *c, cap_rights_t rights) } /* + * 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) +{ + struct capability *c; + + KASSERT(fp_cap->f_type == DTYPE_CAPABILITY, + ("cap_rights: !capability")); + + c = fp_cap->f_data; + return (c->cap_rights); +} + +/* + * Create a capability to wrap around an existing file. + */ +int +kern_capwrap(struct thread *td, struct file *fp, cap_rights_t rights, + struct file **fcappp, int *capfdp) +{ + struct capability *cp, *cp_old; + struct file *fp_object; + int error; + + if ((rights | CAP_MASK_VALID) != CAP_MASK_VALID) + 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) + return (ENOTCAPABLE); + } + + /* + * Allocate a new file descriptor to hang the capability off of. + */ + error = falloc(td, fcappp, capfdp, fp->f_flag); + if (error) + return (error); + + /* + * 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 = *fcappp; + if (fp->f_flag & DFLAG_PASSABLE) + finit(*fcappp, fp->f_flag, DTYPE_CAPABILITY, cp, + &capability_ops); + else + finit(*fcappp, fp->f_flag, DTYPE_CAPABILITY, cp, + &capability_ops_unpassable); + + /* + * Release our private reference (the proc filedesc still has one). + */ + fdrop(*fcappp, 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 @@ -206,6 +333,89 @@ cap_funwrap_mmap(struct file *fp_cap, cap_rights_t rights, u_char *maxprotp, 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"); +} + #else /* !CAPABILITIES */ /* |