lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20090703134955.31139.64160.stgit@warthog.procyon.org.uk>
Date:	Fri, 03 Jul 2009 14:49:55 +0100
From:	David Howells <dhowells@...hat.com>
To:	torvalds@...l.org, hch@...radead.org, wang840925@...il.com,
	viro@...IV.linux.org.uk
Cc:	miklos@...redi.hu, dhowells@...hat.com, alan@...rguk.ukuu.org.uk,
	linux-fsdevel@...r.kernel.org, linux-kernel@...r.kernel.org,
	linux-afs@...ts.infradead.org
Subject: [PATCH 2/2] AFS: Export some AFS-specific information through xattrs

Export some AFS-specific information through xattrs with an "afs." prefix.
This allows some pioctls to be emulated in userspace.

This can be tested directly using the getfattr program:

	[root@...romeda ~]# getfattr -d -m - /afs
	getfattr: Removing leading '/' from absolute path names
	# file: afs
	security.selinux="system_u:object_r:nfs_t:s0\000"
	system.afs.cell="procyon.org.uk"
	system.afs.fid="20000000:00000001:00000001"

A tentative wrapper for partially implementing pioctl in userspace could look
like (this implementation is designed for LD_PRELOAD overloading of syscall()
for simplicity, but should be linked in properly):

/* Userspace version of pioctl
 *
 * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@...hat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public Licence
 * as published by the Free Software Foundation; either version
 * 2 of the Licence, or (at your option) any later version.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define syscall _X_syscall
#include <unistd.h>
#undef syscall
#include <syscall.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/xattr.h>
#include <errno.h>
#include <dlfcn.h>
#define RTLD_NEXT      ((void *) -1l)

#define O_NOACCESS	00000003
#define O_NOFOLLOW	00400000	/* don't follow links */
#define O_NODE		04000000	/* open filesystem node, not device */

struct ViceIoctl {
	caddr_t		in;
	caddr_t		out;
	short		in_size;
	short		out_size;
};

#define _VICEIOCTL(nr)		_IOW('V', nr, struct ViceIoctl)
#define _CVICEIOCTL(nr)		_IOW('C', nr, struct ViceIoctl)
#define _OVICEIOCTL(nr)		_IOW('O', nr, struct ViceIoctl)

#define AFS_PIOCTL	0x14
#define VIOCGETFID	_VICEIOCTL(22)	/* get file ID */

#define AFS_XATTR_PREFIX "system.afs."

struct afs_fid {
	unsigned	vid;		/* volume ID */
	unsigned	vnode;		/* file index within volume */
	unsigned	unique;		/* unique ID number (file index version) */
};

static long (*libc_syscall)(int number, long a, long b, long c, long d, long e, long f);

/*
 * VIOCGETFID
 */
static int afs_viocgetfid(int fd, int cmd, struct ViceIoctl *args)
{
	struct afs_fid *fid = (void *) args->out;
	char buf[8*3];
	int ret;

	if (args->out_size < sizeof(struct afs_fid) || !args->out) {
		errno = EINVAL;
		return -1;
	}

	ret = fgetxattr(fd, AFS_XATTR_PREFIX "fid", buf, sizeof(buf) - 1);
	if (ret == -1)
		return ret;

	buf[23] = 0;
	if (sscanf(buf, "%x:%x:%x", &fid->vid, &fid->vnode, &fid->unique) != 3) {
		errno = EIO;
		return -1;
	}

	args->out_size = sizeof(struct afs_fid);
	return 0;
}

/*
 * the main pioctl switch
 */
static long pioctl(const char *path, int cmd, struct ViceIoctl *args,
		   int follow)
{
	long ret;
	int fd, ret2;

	printf("pioctl(%s,%x,%p,%d)\n", path, _IOC_NR(cmd), args, follow);

	if (args->in_size < 0 || args->out_size < 0)
		goto invalid_arg;

	/* handle pathless pioctls */
	switch (cmd) {
	default:
		if (!path) {
			errno = EINVAL;
			return -1;
		}
		break;
	}

	fd = open(path, O_NODE | O_NOACCESS | (follow ? 0 : O_NOFOLLOW), 0);
	if (fd == -1)
		return -1;

	switch (cmd) {
	case VIOCGETFID:
		ret = afs_viocgetfid(fd, cmd, args);
		break;
	default:
		errno = EOPNOTSUPP;
		ret = -1;
		break;
	}

	ret2 = close(fd);
	if (ret != -1 && ret2 == -1)
		ret = ret2;
	return ret;

invalid_arg:
	errno = EINVAL;
	return -1;
}

static long afs(long function, long b, long c, long d, long e, long f)
{
	if (function == AFS_PIOCTL) {
		return pioctl((const char *) b,
			      (int) c,
			      (struct ViceIoctl *) d,
			      (int) e);
	} else {
		printf("afs(%ld,%lx)\n", function, b);
		errno = ENOSYS;
		return -1;
	}
}

long syscall(int number, long a, long b, long c, long d, long e, long f)
{
	long ret;

	if (!libc_syscall) {
		libc_syscall = dlsym(RTLD_NEXT, "syscall");
		if (!libc_syscall) {
			fprintf(stderr, "Cannot get syscall\n");
			abort();
		}
	}

	ret = libc_syscall(number, a, b, c, d, e, f);
	if (ret != -1)
		return ret;

	if (errno == ENOSYS && number == SYS_afs_syscall)
		return afs(a, b, c, d, e, f);

	return -1;
}

This can be tested like this:

	gcc -gstabs+ -fpic -Wall   -c -o pioctl.o pioctl.c
	gcc  -shared -o libpioctl.so pioctl.o -ldl -lc
	[root@...romeda ~]# LD_PRELOAD=/tmp/libpioctl.so fs getfid /afs
	pioctl(/afs,16,0x7fffdadc6560,1)
	File /afs (1.0.0) contained in volume 1

Signed-off-by: David Howells <dhowells@...hat.com>
---

 fs/afs/Makefile     |    3 +
 fs/afs/dir.c        |    2 +
 fs/afs/file.c       |    2 +
 fs/afs/inode.c      |    8 +++
 fs/afs/internal.h   |    5 ++
 fs/afs/mntpt.c      |    2 +
 fs/afs/security.c   |   18 ++++--
 fs/afs/xattr.c      |  162 +++++++++++++++++++++++++++++++++++++++++++++++++++
 security/security.c |    1 
 9 files changed, 197 insertions(+), 6 deletions(-)
 create mode 100644 fs/afs/xattr.c


diff --git a/fs/afs/Makefile b/fs/afs/Makefile
index 4f64b95..095c541 100644
--- a/fs/afs/Makefile
+++ b/fs/afs/Makefile
@@ -27,6 +27,7 @@ kafs-objs := \
 	vlocation.o \
 	vnode.o \
 	volume.o \
-	write.o
+	write.o \
+	xattr.o
 
 obj-$(CONFIG_AFS_FS)  := kafs.o
diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 9bd7577..5272872 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -60,6 +60,8 @@ const struct inode_operations afs_dir_inode_operations = {
 	.permission	= afs_permission,
 	.getattr	= afs_getattr,
 	.setattr	= afs_setattr,
+	.getxattr	= afs_getxattr,
+	.listxattr	= afs_listxattr,
 };
 
 static const struct dentry_operations afs_fs_dentry_operations = {
diff --git a/fs/afs/file.c b/fs/afs/file.c
index 0149dab..1586496 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -45,6 +45,8 @@ const struct inode_operations afs_file_inode_operations = {
 	.getattr	= afs_getattr,
 	.setattr	= afs_setattr,
 	.permission	= afs_permission,
+	.getxattr	= afs_getxattr,
+	.listxattr	= afs_listxattr,
 };
 
 const struct address_space_operations afs_fs_aops = {
diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index c048f06..8fe6192 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -27,6 +27,14 @@ struct afs_iget_data {
 	struct afs_volume	*volume;	/* volume on which resides */
 };
 
+const struct inode_operations afs_symlink_inode_operations = {
+	.readlink	= generic_readlink,
+	.follow_link	= page_follow_link_light,
+	.put_link	= page_put_link,
+	.getxattr	= afs_getxattr,
+	.listxattr	= afs_listxattr,
+};
+
 /*
  * map the AFS file status to the inode member variables
  */
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 59049b0..5a815e8 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -741,6 +741,11 @@ extern ssize_t afs_file_write(struct kiocb *, const struct iovec *,
 extern int afs_writeback_all(struct afs_vnode *);
 extern int afs_fsync(struct file *, struct dentry *, int);
 
+/*
+ * xattr.c
+ */
+extern ssize_t afs_listxattr(struct dentry *, char *, size_t);
+extern ssize_t afs_getxattr(struct dentry *, const char *, void *, size_t);
 
 /*****************************************************************************/
 /*
diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c
index c52be53..994f75d 100644
--- a/fs/afs/mntpt.c
+++ b/fs/afs/mntpt.c
@@ -37,6 +37,8 @@ const struct inode_operations afs_mntpt_inode_operations = {
 	.follow_link	= afs_mntpt_follow_link,
 	.readlink	= page_readlink,
 	.getattr	= afs_getattr,
+	.getxattr	= afs_getxattr,
+	.listxattr	= afs_listxattr,
 };
 
 static LIST_HEAD(afs_vfsmounts);
diff --git a/fs/afs/security.c b/fs/afs/security.c
index 3ef5043..6c9e011 100644
--- a/fs/afs/security.c
+++ b/fs/afs/security.c
@@ -291,7 +291,7 @@ int afs_permission(struct inode *inode, int mask)
 	struct key *key;
 	int ret;
 
-	_enter("{{%x:%u},%lx},%x,",
+	kenter("{{%x:%u},%lx},%x,",
 	       vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
 
 	key = afs_request_key(vnode->volume->cell);
@@ -315,7 +315,7 @@ int afs_permission(struct inode *inode, int mask)
 		goto error;
 
 	/* interpret the access mask */
-	_debug("REQ %x ACC %x on %s",
+	kdebug("REQ %x ACC %x on %s",
 	       mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file");
 
 	if (S_ISDIR(inode->i_mode)) {
@@ -330,8 +330,11 @@ int afs_permission(struct inode *inode, int mask)
 					AFS_ACE_INSERT | /* create, mkdir, symlink, rename to */
 					AFS_ACE_WRITE))) /* chmod */
 				goto permission_denied;
+		} else if (mask & MAY_OPEN) {
 		} else {
-			BUG();
+			printk(KERN_WARNING "AFS: Unhandled perm mask %x\n",
+			       mask);
+			goto permission_denied;
 		}
 	} else {
 		if (!(access & AFS_ACE_LOOKUP))
@@ -342,18 +345,23 @@ int afs_permission(struct inode *inode, int mask)
 		} else if (mask & MAY_WRITE) {
 			if (!(access & AFS_ACE_WRITE))
 				goto permission_denied;
+		} else if (mask & MAY_OPEN) {
+		} else {
+			printk(KERN_WARNING "AFS: Unhandled perm mask %x\n",
+			       mask);
+			goto permission_denied;
 		}
 	}
 
 	key_put(key);
 	ret = generic_permission(inode, mask, NULL);
-	_leave(" = %d", ret);
+	kleave(" = %d", ret);
 	return ret;
 
 permission_denied:
 	ret = -EACCES;
 error:
 	key_put(key);
-	_leave(" = %d", ret);
+	kleave(" = %d [error]", ret);
 	return ret;
 }
diff --git a/fs/afs/xattr.c b/fs/afs/xattr.c
new file mode 100644
index 0000000..6e9d832
--- /dev/null
+++ b/fs/afs/xattr.c
@@ -0,0 +1,162 @@
+/* AFS extended attribute support
+ *
+ * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@...hat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/xattr.h>
+#include <linux/security.h>
+#include "internal.h"
+
+static const char afs_xattr_prefix[] = XATTR_SYSTEM_PREFIX "afs.";
+
+typedef ssize_t (*afs_xattr_get_t)(struct afs_vnode *vnode, struct key *key,
+				   void *value, size_t size);
+
+struct afs_xattr {
+	const char	*name;
+	afs_xattr_get_t	get;
+};
+
+/*
+ * Get the AFS file identifier of a file
+ */
+static ssize_t afs_xattr_get_fid(struct afs_vnode *vnode, struct key *key,
+				 void *value, size_t size)
+{
+	ssize_t ret;
+
+	_enter("");
+
+	ret = snprintf(value, size, "%08x:%08x:%08x",
+		       vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
+
+	_leave(" = %zd", ret);
+	return ret;
+}
+
+/*
+ * Get the AFS cell name of a file
+ */
+static ssize_t afs_xattr_get_cell(struct afs_vnode *vnode, struct key *key,
+				  void *value, size_t size)
+{
+	ssize_t ret;
+
+	_enter("");
+
+	ret = snprintf(value, size, "%s", vnode->volume->vlocation->cell->name);
+
+	_leave(" = %zd", ret);
+	return ret;
+}
+
+/*
+ * ordered list of AFS attributes
+ */
+static const struct afs_xattr afs_xattrs[] = {
+	{ "cell",	afs_xattr_get_cell },
+	{ "fid",	afs_xattr_get_fid },
+	{ NULL }
+};
+
+/*
+ * list extended attributes for an AFS file
+ */
+ssize_t afs_listxattr(struct dentry *dentry, char *list, size_t size)
+{
+	const struct afs_xattr *p;
+	struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode);
+	struct key *key;
+	ssize_t total, nlen, len, plen, ret;
+	char *to = list;
+
+	_enter(",%p,%zu", list, size);
+
+	key = afs_request_key(vnode->volume->cell);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		_leave(" = %ld [no key]", ret);
+		return ret;
+	}
+
+	/* list the AFS attributes */
+	_debug("afs attr");
+	plen = sizeof(afs_xattr_prefix);
+	total = 0;
+	for (p = afs_xattrs; p->name; p++) {
+		nlen = strlen(p->name);
+		len = nlen + sizeof(afs_xattr_prefix);
+		total += len;
+		if (len <= size && list) {
+			size -= len;
+			memcpy(to, afs_xattr_prefix, plen - 1);
+			to += plen - 1;
+			memcpy(to, p->name, nlen + 1);
+			to += nlen + 1;
+		}
+	}
+
+	/* TODO: tag on user attributes when they become available */
+	_debug("user attr");
+
+	/* tag on the security subsys's attributes */
+	_debug("sec attr");
+	len = security_inode_listsecurity(dentry->d_inode, to, size);
+	if (IS_ERR_VALUE(len))
+		total = len;
+	else
+		total += len;
+
+	key_put(key);
+	_leave(" = %zd [%zd]", total, to - list);
+	return total;
+}
+
+/*
+ * get extended attributes for an AFS file
+ */
+ssize_t afs_getxattr(struct dentry *dentry, const char *name,
+		     void *value, size_t size)
+{
+	const struct afs_xattr *p;
+	struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode);
+	struct key *key;
+	ssize_t ret;
+
+	_enter(",%s,,%zu", name, size);
+
+	key = afs_request_key(vnode->volume->cell);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		_leave(" = %ld [no key]", ret);
+		return ret;
+	}
+
+	/* get AFS attributes */
+	if (memcmp(name, afs_xattr_prefix, sizeof(afs_xattr_prefix) - 1) == 0) {
+		name += sizeof(afs_xattr_prefix) - 1;
+
+		for (p = afs_xattrs; p->name; p++) {
+			if (strcmp(name, p->name) == 0) {
+				ret = p->get(vnode, key, value, size);
+				goto out;
+			}
+		}
+		goto noattr;
+	}
+
+	/* TODO: get user attributes when they become available */
+
+noattr:
+	ret = -ENODATA;
+out:
+	key_put(key);
+	_leave(" = %zd", ret);
+	return ret;
+}
diff --git a/security/security.c b/security/security.c
index dc7674f..fe6b812 100644
--- a/security/security.c
+++ b/security/security.c
@@ -609,6 +609,7 @@ int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer
 		return 0;
 	return security_ops->inode_listsecurity(inode, buffer, buffer_size);
 }
+EXPORT_SYMBOL(security_inode_listsecurity);
 
 void security_inode_getsecid(const struct inode *inode, u32 *secid)
 {

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ