[PATCH 15/17] AFS: Implement the PSetTokens pioctl

David Howells dhowells at redhat.com
Tue Jun 16 16:40:02 EDT 2009


Implement the PSetTokens pioctl for AFS.  This will submit a security token for
caching.

This can be tested with the OpenAFS userspace tools using the klog program,
which should add a key to the session keyring with something like:

	[root at andromeda ~]# echo password | klog -pipe admin
	[root at andromeda ~]# keyctl show
	Session Keyring
	       -3 --alswrv      0     0  keyring: _ses
	147139749 --alswrv      0    -1   \_ keyring: _uid.0
	457362442 --als--v      0     0   \_ rxrpc: cambridge.redhat.com

Note that 'klog -setpag' is not supported by this patch as there's currently
no way for a process to replace its parent process's session keyring.

Signed-off-by: David Howells <dhowells at redhat.com>
---

 fs/afs/cell.c           |   15 ++++
 fs/afs/internal.h       |    1 
 fs/afs/pioctl.c         |  177 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/afscall.h |   14 ++++
 include/linux/venus.h   |    1 
 5 files changed, 208 insertions(+), 0 deletions(-)


diff --git a/fs/afs/cell.c b/fs/afs/cell.c
index e19c13f..b900fc7 100644
--- a/fs/afs/cell.c
+++ b/fs/afs/cell.c
@@ -227,6 +227,21 @@ int afs_cell_init(char *rootcell)
 }
 
 /*
+ * get a reference to the root cell
+ */
+struct afs_cell *afs_get_root_cell(void)
+{
+	struct afs_cell *cell;
+
+	read_lock(&afs_cells_lock);
+	cell = afs_cell_root;
+	afs_get_cell(cell);
+	read_unlock(&afs_cells_lock);
+
+	return cell;
+}
+
+/*
  * lookup a cell record
  */
 struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz)
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 9a8e8a2..cf08782 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -467,6 +467,7 @@ extern struct list_head afs_proc_cells;
 
 #define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0)
 extern int afs_cell_init(char *);
+extern struct afs_cell *afs_get_root_cell(void);
 extern struct afs_cell *afs_cell_create(const char *, char *);
 extern struct afs_cell *afs_cell_lookup(const char *, unsigned);
 extern struct afs_cell *afs_grab_cell(struct afs_cell *);
diff --git a/fs/afs/pioctl.c b/fs/afs/pioctl.c
index ffbec0c..e6ea69f 100644
--- a/fs/afs/pioctl.c
+++ b/fs/afs/pioctl.c
@@ -2,6 +2,8 @@
  *
  * Copyright (C) 2008 Jacob Thebault-Spieker <summatusmentis at gmail.com>
  *
+ * Modified by David Howells <dhowells at redhat.com>
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version
@@ -12,6 +14,10 @@
 #include <linux/pioctl.h>
 #include <linux/venus.h>
 #include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/key.h>
+#include <linux/keyctl.h>
+#include <keys/rxrpc-type.h>
 #include "internal.h"
 
 /*
@@ -219,6 +225,173 @@ long afs_pioctl(struct dentry *dentry, int cmd, struct vice_ioctl *arg)
 }
 
 /*
+ * Set a user's rxkad authentication tokens
+ */
+static long afs_PSetTokens(struct vice_ioctl *arg)
+{
+	struct rxrpc_key_data_v2 *payload;
+	struct clear_token details;
+	struct afs_cell *cell;
+	const char *cp;
+	key_ref_t keyring_r, key_r;
+	size_t in_size, loop;
+	void *in, *in_next, *ticket;
+	char *cellname, *keyname, *dp;
+	long ret;
+	u32 tktlen, tmp, flag;
+
+	_enter("");
+
+	/* decode the argument block */
+	in_next = arg->in;
+	in_size = arg->in_size;
+
+#define CHECK(n)				\
+	do {					\
+		if (in_size < (n))		\
+			goto underflow;		\
+		in = in_next;			\
+		in_size -= (n);			\
+		in_next += (n);			\
+	} while(0)
+
+#define DECODE(to)				\
+	do {					\
+		CHECK(sizeof(*(to)));		\
+		memcpy(to, in, sizeof(*(to)));	\
+	} while(0)
+
+	DECODE(&tktlen);
+	_debug("tktlen: %u", tktlen);
+	if (tktlen > INT_MAX)
+		goto invalid;
+	CHECK(tktlen);
+	ticket = in;
+	DECODE(&tmp);
+	_debug("clear token %u", tmp);
+	if (tmp != sizeof(struct clear_token))
+		goto invalid;
+	DECODE(&details);
+	_debug("ah:%x vi:%x bts:%x ets:%x (e-b:%u) (CT:%lx)",
+	       details.auth_handle, details.vice_id,
+	       details.begin_timestamp, details.end_timestamp,
+	       details.end_timestamp - details.begin_timestamp,
+	       CURRENT_TIME.tv_sec);
+	if (details.vice_id == UINT_MAX)
+		goto invalid;
+	if (details.auth_handle == UINT_MAX)
+		details.auth_handle = 999;
+
+	/* flags and cellname are optional, defaulting to the root cell */
+	_debug("in_size: %zu", in_size);
+	if (in_size != 0) {
+		DECODE(&flag);
+		_debug("flag: %x", flag);
+
+		if (flag & 0x8000) {
+			/* the caller wants us to give our parent a new PAG
+			 * - we don't support this currently
+			 */
+			_leave(" = -EACCES");
+			return -EACCES;
+		}
+
+		/* remainder is cell name */
+		CHECK(sizeof(char));
+		cellname = in;
+		for (loop = 0; loop < in_size; loop++)
+			if (!isprint(cellname[loop]))
+				goto invalid;
+
+		if (cellname[loop] != '\0')
+			goto invalid;
+		cell = NULL;
+
+		_debug("cellname: %s", cellname);
+	} else {
+		cell = afs_get_root_cell();
+		cellname = cell->name;
+		flag = 1;
+	}
+
+#undef DECODE
+#undef CHECK
+
+	/* construct the key name */
+	ret = -ENOMEM;
+	keyname = kmalloc(4 + strlen(cellname) + 1, GFP_KERNEL);
+	if (!keyname)
+		goto error_nokeyname;
+
+	memcpy(keyname, "afs@", 4);
+	dp = keyname + 4;
+	cp = cellname;
+	while (*cp)
+		*dp++ = toupper(*cp++);
+	*dp = 0;
+
+	/* we install the authentication token as a key */
+	payload = kmalloc(sizeof(*payload) + tktlen, GFP_KERNEL);
+	if (!payload)
+		goto error_nopayload;
+
+	payload->kif_version	= 2;
+	payload->security_index	= RXRPC_SECURITY_RXKAD;
+	payload->ticket_length	= tktlen;
+	payload->vice_id	= details.vice_id;
+	payload->start		= details.begin_timestamp;
+	payload->expiry		= details.end_timestamp;
+	payload->kvno		= details.auth_handle;
+	memcpy(payload->session_key, details.session_key, 8);
+	memcpy(payload->ticket, ticket, tktlen);
+
+	/* add the key to the session keyring */
+	keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 1, 0,
+				    WANT_KEY_WRITE);
+	if (IS_ERR(keyring_r)) {
+		_debug("keyring lookup failed");
+		ret = PTR_ERR(keyring_r);
+		goto error;
+	}
+
+	/* create or update the requested key and add it to the target
+	 * keyring */
+	key_r = key_create_or_update(keyring_r, "rxrpc", keyname,
+				     payload, sizeof(*payload) + tktlen,
+				     KEY_PERM_UNDEF, KEY_ALLOC_IN_QUOTA);
+	key_ref_put(keyring_r);
+
+	if (IS_ERR(key_r)) {
+		_debug("key create failed");
+		ret = PTR_ERR(key_r);
+		goto error;
+	}
+
+	_debug("key serial: %x", key_ref_to_ptr(key_r)->serial);
+
+	key_ref_put(key_r);
+	arg->out_size = 0;
+	ret = 0;
+
+error:
+	kfree(payload);
+error_nopayload:
+	kfree(keyname);
+error_nokeyname:
+	afs_put_cell(cell);
+	_leave(" = %ld", ret);
+	return ret;
+
+underflow:
+	_leave(" = -EINVAL [short arg]");
+	return -EINVAL;
+
+invalid:
+	_leave(" = -EINVAL [invalid arg]");
+	return -EINVAL;
+}
+
+/*
  * The AFS pathless pioctl handler
  */
 long afs_pathless_pioctl(int cmd, struct vice_ioctl *arg)
@@ -231,6 +404,10 @@ long afs_pathless_pioctl(int cmd, struct vice_ioctl *arg)
 #define VIOC_COMMAND(nr) (_VICEIOCTL(nr) & ~IOCSIZE_MASK)
 
 	switch (cmd & ~IOCSIZE_MASK) {
+	case VIOC_COMMAND(PSetTokens):
+		ret = afs_PSetTokens(arg);
+		break;
+
 	default:
 		printk(KERN_DEBUG "AFS: Unsupported pioctl command %x\n", cmd);
 		ret = -EOPNOTSUPP;
diff --git a/include/linux/afscall.h b/include/linux/afscall.h
index 00054f0..7635aab 100644
--- a/include/linux/afscall.h
+++ b/include/linux/afscall.h
@@ -2,6 +2,8 @@
  *
  * Copyright (C) 2008 Jacob Thebault-Spieker <summatusmentis at gmail.com>
  *
+ * Modified by David Howells <dhowells at redhat.com>
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version
@@ -14,6 +16,7 @@
 #define AFSCALL_PIOCTL 0x14
 
 /* pioctl commands */
+#define PSetTokens	3		/* get authentication tokens for user */
 #define PGetVolStat	4		/* get volume status */
 #define PWhereIs	14		/* find out where a volume is located */
 #define PGetFID		22		/* get file ID */
@@ -40,4 +43,15 @@ struct VolumeStatus {
 	int	PartMaxBlocks;	/* size of volume's partition */
 };
 
+/*
+ * User details when getting or submitting a token
+ */
+struct clear_token {
+	u32	auth_handle;		/* key version number */
+	u8	session_key[8];		/* session encryption key */
+	u32	vice_id;		/* client/user ID */
+	u32	begin_timestamp;	/* time_t at which ticket starts */
+	u32	end_timestamp;		/* time_t at which ticket expires */
+};
+
 #endif /* _LINUX_AFSCALL_H */
diff --git a/include/linux/venus.h b/include/linux/venus.h
index ea8e468..b90e5f2 100644
--- a/include/linux/venus.h
+++ b/include/linux/venus.h
@@ -17,6 +17,7 @@
 /*
  * pioctl commands (not usable as ioctls)
  */
+#define VIOCSETTOK		_VICEIOCTL(PSetTokens)
 #define VIOCGETVOLSTAT		_VICEIOCTL(PGetVolStat)
 #define VIOCWHEREIS		_VICEIOCTL(PWhereIs)
 #define VIOCGETFID		_VICEIOCTL(PGetFID)




More information about the linux-afs mailing list