Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 24 Jan 2008 13:09:07 GMT
From:      Robert Watson <rwatson@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 134005 for review
Message-ID:  <200801241309.m0OD97gB054326@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 <sys/cdefs.h>
 __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 <sys/param.h>
 #include <sys/systm.h>
 
+#include <sys/capability.h>
 #include <sys/conf.h>
 #include <sys/domain.h>
 #include <sys/fcntl.h>
@@ -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 <sys/cdefs.h>
-__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 <sys/param.h>
 #include <sys/capability.h>
@@ -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 <sys/fcntl.h>
 #include <sys/unistd.h>
 #else
+#include <sys/capability.h>
 #include <sys/queue.h>
 #include <sys/_lock.h>
 #include <sys/_mutex.h>
@@ -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)



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200801241309.m0OD97gB054326>