From owner-p4-projects@FreeBSD.ORG Thu Jan 24 13:09:08 2008 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 6362816A46B; Thu, 24 Jan 2008 13:09:08 +0000 (UTC) Delivered-To: perforce@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 2808516A421 for ; Thu, 24 Jan 2008 13:09:08 +0000 (UTC) (envelope-from bb+lists.freebsd.perforce@cyrus.watson.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id 18C9A13C468 for ; Thu, 24 Jan 2008 13:09:08 +0000 (UTC) (envelope-from bb+lists.freebsd.perforce@cyrus.watson.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.1/8.14.1) with ESMTP id m0OD97GP054329 for ; Thu, 24 Jan 2008 13:09:07 GMT (envelope-from bb+lists.freebsd.perforce@cyrus.watson.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.1/8.14.1/Submit) id m0OD97gB054326 for perforce@freebsd.org; Thu, 24 Jan 2008 13:09:07 GMT (envelope-from bb+lists.freebsd.perforce@cyrus.watson.org) Date: Thu, 24 Jan 2008 13:09:07 GMT Message-Id: <200801241309.m0OD97gB054326@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to bb+lists.freebsd.perforce@cyrus.watson.org using -f From: Robert Watson To: Perforce Change Reviews Cc: Subject: PERFORCE change 134005 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 24 Jan 2008 13:09:09 -0000 http://perforce.freebsd.org/chv.cgi?CH=134005 Change 134005 by rwatson@rwatson_freebsd_capabilities on 2008/01/24 13:09:02 Teach fget*() to accept a capability rights mask and process capabilities as part of retrieving a file descriptor. Add fgetcap() which retrieves the capability itself, if any, rather than what it refers to. Rename cap_fget() to cap_fextract() to make it more distinct namingwise from fgetcap(), which is named to match fgetsock(), etc. No need to check for DTYPE_CAPABILITY from fgetcap() since it only returns capabilities, so drop that from cap_getrights(). Add CAP_REVOKE, not needed for FreeBSD ABI that only has revoke() not frevoke(), but is needed for other ABIs. Affected files ... .. //depot/projects/trustedbsd/capabilities/src/sys/kern/kern_descrip.c#4 edit .. //depot/projects/trustedbsd/capabilities/src/sys/kern/sys_capability.c#7 edit .. //depot/projects/trustedbsd/capabilities/src/sys/sys/capability.h#8 edit .. //depot/projects/trustedbsd/capabilities/src/sys/sys/file.h#3 edit Differences ... ==== //depot/projects/trustedbsd/capabilities/src/sys/kern/kern_descrip.c#4 (text+ko) ==== @@ -37,12 +37,14 @@ #include __FBSDID("$FreeBSD: src/sys/kern/kern_descrip.c,v 1.321 2008/01/20 19:55:52 rwatson Exp $"); +#include "opt_capabilities.h" #include "opt_compat.h" #include "opt_ddb.h" #include #include +#include #include #include #include @@ -1119,7 +1121,7 @@ AUDIT_ARG(fd, fd); - if ((error = fget(td, fd, &fp)) != 0) + if ((error = fget(td, fd, CAP_FSTAT, &fp)) != 0) return (error); AUDIT_ARG(file, td->td_proc, fp); @@ -1171,7 +1173,7 @@ struct vnode *vp; int error; - if ((error = fget(td, uap->fd, &fp)) != 0) + if ((error = fget(td, uap->fd, CAP_FPATHCONF, &fp)) != 0) return (error); /* If asynchronous I/O is available, it works for all descriptors. */ @@ -1961,7 +1963,6 @@ atomic_store_rel_ptr((volatile uintptr_t *)&fp->f_ops, (uintptr_t)ops); } - /* * Extract the file pointer associated with the specified descriptor for the * current user process. @@ -1971,6 +1972,10 @@ * If the descriptor exists but doesn't match 'flags' then return EBADF for * read attempts and EINVAL for write attempts. * + * If the file is a capability, 'rights' will be checked against the + * capability rights mask, and the object decapsulated if the check passes. + * The capability itself will never be returned. + * * If 'hold' is set (non-zero) the file's refcount will be bumped on return. * It should be dropped with fdrop(). If it is not set, then the refcount * will not be bumped however the thread's filedesc struct will be returned @@ -1979,12 +1984,19 @@ * If an error occured the non-zero error is returned and *fpp is set to * NULL. Otherwise *fpp is set and zero is returned. */ +#define FGET_HOLD 0x00000001 +#define FGET_GETCAP 0x00000002 static __inline int -_fget(struct thread *td, int fd, struct file **fpp, int flags, int hold) +_fget(struct thread *td, int fd, struct file **fpp, int flags, + cap_rights_t rights, int fget_flags) { struct filedesc *fdp; struct file *fp; + int error; + /* + * Validate the file descriptor number and find the struct file. + */ *fpp = NULL; if (td == NULL || (fdp = td->td_proc->p_fd) == NULL) return (EBADF); @@ -1995,19 +2007,42 @@ } /* - * FREAD and FWRITE failure return EBADF as per POSIX. - * - * Only one flag, or 0, may be specified. + * If a capability has been requested, return the capability + * directly. Otherwise, check capability rights, extract the + * underlying object, and check its access flags. */ - if (flags == FREAD && (fp->f_flag & FREAD) == 0) { - FILEDESC_SUNLOCK(fdp); - return (EBADF); + if (fget_flags & FGET_GETCAP) { + if (fp->f_type != DTYPE_CAPABILITY) { + FILEDESC_SUNLOCK(fdp); + return (EINVAL); + } + } else { + /* + * If a capability hasn't been requested, then validate the + * capability and find the underlying object. From now on + * 'fp' refers to the actual object of interest. + */ + error = cap_fextract(fp, rights, &fp); + if (error) { + FILEDESC_SUNLOCK(fdp); + return (error); + } + + /* + * FREAD and FWRITE failure return EBADF as per POSIX. + * + * Only one flag, or 0, may be specified. + */ + if (flags == FREAD && (fp->f_flag & FREAD) == 0) { + FILEDESC_SUNLOCK(fdp); + return (EBADF); + } + if (flags == FWRITE && (fp->f_flag & FWRITE) == 0) { + FILEDESC_SUNLOCK(fdp); + return (EBADF); + } } - if (flags == FWRITE && (fp->f_flag & FWRITE) == 0) { - FILEDESC_SUNLOCK(fdp); - return (EBADF); - } - if (hold) { + if (fget_flags & FGET_HOLD) { fhold(fp); FILEDESC_SUNLOCK(fdp); } @@ -2016,24 +2051,36 @@ } int -fget(struct thread *td, int fd, struct file **fpp) +fget(struct thread *td, int fd, cap_rights_t rights, struct file **fpp) +{ + + return(_fget(td, fd, fpp, 0, rights, FGET_HOLD)); +} + +int +fget_read(struct thread *td, int fd, cap_rights_t rights, struct file **fpp) { - return(_fget(td, fd, fpp, 0, 1)); + return(_fget(td, fd, fpp, FREAD, rights, FGET_HOLD)); } int -fget_read(struct thread *td, int fd, struct file **fpp) +fget_write(struct thread *td, int fd, cap_rights_t rights, struct file **fpp) { - return(_fget(td, fd, fpp, FREAD, 1)); + return(_fget(td, fd, fpp, FWRITE, rights, FGET_HOLD)); } +/* + * Unlike the other fget() calls, which accept and check capability rights + * but never return capabilities, fgetcap() returns the capability but + * doesn't check capability rights. + */ int -fget_write(struct thread *td, int fd, struct file **fpp) +fgetcap(struct thread *td, int fd, struct file **fpp) { - return(_fget(td, fd, fpp, FWRITE, 1)); + return (_fget(td, fd, fpp, 0, 0, FGET_GETCAP)); } /* @@ -2044,13 +2091,14 @@ * XXX: what about the unused flags ? */ static __inline int -_fgetvp(struct thread *td, int fd, struct vnode **vpp, int flags) +_fgetvp(struct thread *td, int fd, int flags, cap_rights_t rights, + struct vnode **vpp) { struct file *fp; int error; *vpp = NULL; - if ((error = _fget(td, fd, &fp, 0, 0)) != 0) + if ((error = _fget(td, fd, &fp, 0, rights, 0)) != 0) return (error); if (fp->f_vnode == NULL) { error = EINVAL; @@ -2063,25 +2111,27 @@ } int -fgetvp(struct thread *td, int fd, struct vnode **vpp) +fgetvp(struct thread *td, int fd, cap_rights_t rights, struct vnode **vpp) { - return (_fgetvp(td, fd, vpp, 0)); + return (_fgetvp(td, fd, 0, rights, vpp)); } int -fgetvp_read(struct thread *td, int fd, struct vnode **vpp) +fgetvp_read(struct thread *td, int fd, cap_rights_t rights, + struct vnode **vpp) { - return (_fgetvp(td, fd, vpp, FREAD)); + return (_fgetvp(td, fd, FREAD, rights, vpp)); } #ifdef notyet int -fgetvp_write(struct thread *td, int fd, struct vnode **vpp) +fgetvp_write(struct thread *td, int fd, cap_rights_t rights, + struct vnode **vpp) { - return (_fgetvp(td, fd, vpp, FWRITE)); + return (_fgetvp(td, fd, FWRITE, rights, vpp)); } #endif @@ -2097,7 +2147,8 @@ * during use. */ int -fgetsock(struct thread *td, int fd, struct socket **spp, u_int *fflagp) +fgetsock(struct thread *td, int fd, cap_rights_t rights, struct socket **spp, + u_int *fflagp) { struct file *fp; int error; @@ -2105,7 +2156,7 @@ *spp = NULL; if (fflagp != NULL) *fflagp = 0; - if ((error = _fget(td, fd, &fp, 0, 0)) != 0) + if ((error = _fget(td, fd, &fp, 0, rights, 0)) != 0) return (error); if (fp->f_type != DTYPE_SOCKET) { error = ENOTSOCK; @@ -2178,7 +2229,7 @@ int vfslocked; int error; - if ((error = fget(td, uap->fd, &fp)) != 0) + if ((error = fget(td, uap->fd, CAP_FLOCK, &fp)) != 0) return (error); if (fp->f_type != DTYPE_VNODE) { fdrop(fp, td); ==== //depot/projects/trustedbsd/capabilities/src/sys/kern/sys_capability.c#7 (text+ko) ==== @@ -59,10 +59,12 @@ * - masking in fo_read/fo_write/etc is undesirable because really we want * only the original file to be used, as it might have state (cred, flags, * etc) that should be used instead. seekable is a particular issue. + * - mmap should incorporate capability rights into maxprot, not just file + * flags. */ #include -__FBSDID("$P4: //depot/projects/trustedbsd/capabilities/src/sys/kern/sys_capability.c#6 $"); +__FBSDID("$P4: //depot/projects/trustedbsd/capabilities/src/sys/kern/sys_capability.c#7 $"); #include #include @@ -151,7 +153,7 @@ * XXXRW: This will almost certainly change. */ int -cap_fget(struct file *fp_cap, cap_rights_t rights, struct file **fpp) +cap_fextract(struct file *fp_cap, cap_rights_t rights, struct file **fpp) { struct capability *c; int error; @@ -219,7 +221,13 @@ return (EINVAL); c = uma_zalloc(capability_zone, M_WAITOK | M_ZERO); - error = fget(td, uap->fd, &fp); + + /* + * We always allow creating a capability referencing an existing + * descriptor or capability, even if it's not of much use to the + * application. + */ + error = fget(td, uap->fd, 0, &fp); if (error) goto fail; @@ -279,13 +287,9 @@ struct file *fp; int error; - error = fget(td, uap->fd, &fp); + error = fgetcap(td, uap->fd, &fp); if (error) return (error); - if (fp->f_type != DTYPE_CAPABILITY) { - fdrop(fp, td); - return (EINVAL); - } c = fp->f_data; error = copyout(&c->cap_rights, uap->rightsp, sizeof(*uap->rightsp)); fdrop(fp, td); ==== //depot/projects/trustedbsd/capabilities/src/sys/sys/capability.h#8 (text+ko) ==== @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $P4: //depot/projects/trustedbsd/capabilities/src/sys/sys/capability.h#7 $ + * $P4: //depot/projects/trustedbsd/capabilities/src/sys/sys/capability.h#8 $ */ /* @@ -61,7 +61,7 @@ #define CAP_FLOCK 0x0000000000010000ULL /* flock */ #define CAP_GETDIRENTRIES 0x0000000000020000ULL /* getdirentries */ #define CAP_FSTATFS 0x0000000000040000ULL /* fstatfs */ -#define _CAP_UNUSED0 0x0000000000080000ULL +#define CAP_REVOKE 0x0000000000080000ULL /* revoke */ #define _CAP_UNUSED1 0x0000000000100000ULL #define CAP_FPATHCONF 0x0000000000200000ULL /* fpathconf */ #define CAP_FUTIMES 0x0000000000400000ULL /* futimes */ @@ -110,11 +110,20 @@ * * mmap() and aio*() system calls will need special attention as they may * involve reads or writes depending a great deal on context. + * + * Socket checks don't generally pass CAP_SEEK but perhaps should? */ #ifdef _KERNEL struct file; -int cap_fget(struct file *fp_cap, cap_rights_t rights, + +/* + * Given a file descriptor that may be a capability, check the requested + * rights and extract the underlying object. Assumes a valid reference is + * held to fp_cap, and returns a pointer via fpp under that assumption. The + * caller invokes fhold(*fpp) if required. + */ +int cap_fextract(struct file *fp_cap, cap_rights_t rights, struct file **fpp); #else /* !_KERNEL */ ==== //depot/projects/trustedbsd/capabilities/src/sys/sys/file.h#3 (text+ko) ==== @@ -38,6 +38,7 @@ #include #include #else +#include #include #include #include @@ -168,9 +169,12 @@ extern int maxfilesperproc; /* per process limit on number of open files */ extern volatile int openfiles; /* actual number of open files */ -int fget(struct thread *td, int fd, struct file **fpp); -int fget_read(struct thread *td, int fd, struct file **fpp); -int fget_write(struct thread *td, int fd, struct file **fpp); +int fget(struct thread *td, int fd, cap_rights_t rights, struct file **fpp); +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); /* @@ -188,11 +192,15 @@ fo_close_t soo_close; void finit(struct file *, u_int, short, void *, struct fileops *); -int fgetvp(struct thread *td, int fd, struct vnode **vpp); -int fgetvp_read(struct thread *td, int fd, struct vnode **vpp); -int fgetvp_write(struct thread *td, int fd, struct vnode **vpp); +int fgetvp(struct thread *td, int fd, cap_rights_t rights, + 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, + struct vnode **vpp); -int fgetsock(struct thread *td, int fd, struct socket **spp, u_int *fflagp); +int fgetsock(struct thread *td, int fd, cap_rights_t rights, + struct socket **spp, u_int *fflagp); void fputsock(struct socket *sp); #define fhold(fp) atomic_add_int(&(fp)->f_count, 1)