PERFORCE change 134005 for review
Robert Watson
rwatson at FreeBSD.org
Thu Jan 24 05:09:09 PST 2008
http://perforce.freebsd.org/chv.cgi?CH=134005
Change 134005 by rwatson at 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)
More information about the p4-projects
mailing list