From owner-svn-src-all@FreeBSD.ORG Fri Jul 15 09:37:15 2011 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 24C00106564A; Fri, 15 Jul 2011 09:37:15 +0000 (UTC) (envelope-from jonathan@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 0A7F88FC12; Fri, 15 Jul 2011 09:37:15 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id p6F9bELZ069523; Fri, 15 Jul 2011 09:37:14 GMT (envelope-from jonathan@svn.freebsd.org) Received: (from jonathan@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id p6F9bEmF069519; Fri, 15 Jul 2011 09:37:14 GMT (envelope-from jonathan@svn.freebsd.org) Message-Id: <201107150937.p6F9bEmF069519@svn.freebsd.org> From: Jonathan Anderson Date: Fri, 15 Jul 2011 09:37:14 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r224056 - in head/sys: kern sys X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 15 Jul 2011 09:37:15 -0000 Author: jonathan Date: Fri Jul 15 09:37:14 2011 New Revision: 224056 URL: http://svn.freebsd.org/changeset/base/224056 Log: 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 Modified: head/sys/kern/kern_descrip.c head/sys/kern/sys_capability.c head/sys/sys/capability.h Modified: head/sys/kern/kern_descrip.c ============================================================================== --- head/sys/kern/kern_descrip.c Fri Jul 15 06:54:21 2011 (r224055) +++ head/sys/kern/kern_descrip.c Fri Jul 15 09:37:14 2011 (r224056) @@ -1160,7 +1160,7 @@ kern_close(td, fd) int fd; { struct filedesc *fdp; - struct file *fp; + struct file *fp, *fp_object; int error; int holdleaders; @@ -1195,8 +1195,14 @@ kern_close(td, fd) * added, and deleteing a knote for the new fd. */ knote_fdclose(td, fd); - if (fp->f_type == DTYPE_MQUEUE) - mq_fdclose(td, fd, fp); + + /* + * When we're closing an fd with a capability, we need to notify + * mqueue if the underlying object is of type mqueue. + */ + (void)cap_funwrap(fp, 0, &fp_object); + if (fp_object->f_type == DTYPE_MQUEUE) + mq_fdclose(td, fd, fp_object); FILEDESC_XUNLOCK(fdp); error = closef(fp, td); @@ -2146,6 +2152,7 @@ closef(struct file *fp, struct thread *t struct flock lf; struct filedesc_to_leader *fdtol; struct filedesc *fdp; + struct file *fp_object; /* * POSIX record locking dictates that any close releases ALL @@ -2158,11 +2165,15 @@ closef(struct file *fp, struct thread *t * NULL thread pointer when there really is no owning * context that might have locks, or the locks will be * leaked. + * + * If this is a capability, we do lock processing under the underlying + * node, not the capability itself. */ - if (fp->f_type == DTYPE_VNODE && td != NULL) { + (void)cap_funwrap(fp, 0, &fp_object); + if ((fp_object->f_type == DTYPE_VNODE) && (td != NULL)) { int vfslocked; - vp = fp->f_vnode; + vp = fp_object->f_vnode; vfslocked = VFS_LOCK_GIANT(vp->v_mount); if ((td->td_proc->p_leader->p_flag & P_ADVLOCK) != 0) { lf.l_whence = SEEK_SET; @@ -2192,7 +2203,7 @@ closef(struct file *fp, struct thread *t lf.l_start = 0; lf.l_len = 0; lf.l_type = F_UNLCK; - vp = fp->f_vnode; + vp = fp_object->f_vnode; (void) VOP_ADVLOCK(vp, (caddr_t)fdtol->fdl_leader, F_UNLCK, &lf, F_POSIX); @@ -2497,6 +2508,9 @@ fputsock(struct socket *so) /* * Handle the last reference to a file being closed. + * + * No special capability handling here, as the capability's fo_close will run + * instead of the object here, and perform any necessary drop on the object. */ int _fdrop(struct file *fp, struct thread *td) Modified: head/sys/kern/sys_capability.c ============================================================================== --- head/sys/kern/sys_capability.c Fri Jul 15 06:54:21 2011 (r224055) +++ head/sys/kern/sys_capability.c Fri Jul 15 09:37:14 2011 (r224056) @@ -123,18 +123,66 @@ cap_getmode(struct thread *td, struct ca * 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_righ } /* + * 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, ca 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 */ /* Modified: head/sys/sys/capability.h ============================================================================== --- head/sys/sys/capability.h Fri Jul 15 06:54:21 2011 (r224055) +++ head/sys/sys/capability.h Fri Jul 15 09:37:14 2011 (r224056) @@ -64,6 +64,12 @@ #define IN_CAPABILITY_MODE(td) (td->td_ucred->cr_flags & CRED_FLAG_CAPMODE) /* + * Create a capability to wrap a file object. + */ +int kern_capwrap(struct thread *td, struct file *fp, cap_rights_t rights, + struct file **cap, int *capfd); + +/* * 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