ee90389
From 64160c504842a359801cff17464931fa028ff164 Mon Sep 17 00:00:00 2001
ee90389
From: David Howells <dhowells@redhat.com>
ee90389
Date: Fri, 30 Aug 2013 15:37:54 +0100
ee90389
Subject: [PATCH 1/2] KEYS: Implement a big key type that can save to tmpfs
ee90389
ee90389
Implement a big key type that can save its contents to tmpfs and thus
ee90389
swapspace when memory is tight.  This is useful for Kerberos ticket caches.
ee90389
ee90389
Signed-off-by: David Howells <dhowells@redhat.com>
ee90389
Tested-by: Simo Sorce <simo@redhat.com>
ee90389
---
ee90389
 include/keys/big_key-type.h |  25 ++++++
ee90389
 include/linux/key.h         |   1 +
ee90389
 security/keys/Kconfig       |  11 +++
ee90389
 security/keys/Makefile      |   1 +
ee90389
 security/keys/big_key.c     | 204 ++++++++++++++++++++++++++++++++++++++++++++
ee90389
 5 files changed, 242 insertions(+)
ee90389
 create mode 100644 include/keys/big_key-type.h
ee90389
 create mode 100644 security/keys/big_key.c
ee90389
ee90389
diff --git a/include/keys/big_key-type.h b/include/keys/big_key-type.h
ee90389
new file mode 100644
ee90389
index 0000000..d69bc8a
ee90389
--- /dev/null
ee90389
+++ b/include/keys/big_key-type.h
ee90389
@@ -0,0 +1,25 @@
ee90389
+/* Big capacity key type.
ee90389
+ *
ee90389
+ * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
ee90389
+ * Written by David Howells (dhowells@redhat.com)
ee90389
+ *
ee90389
+ * This program is free software; you can redistribute it and/or
ee90389
+ * modify it under the terms of the GNU General Public License
ee90389
+ * as published by the Free Software Foundation; either version
ee90389
+ * 2 of the License, or (at your option) any later version.
ee90389
+ */
ee90389
+
ee90389
+#ifndef _KEYS_BIG_KEY_TYPE_H
ee90389
+#define _KEYS_BIG_KEY_TYPE_H
ee90389
+
ee90389
+#include <linux/key-type.h>
ee90389
+
ee90389
+extern struct key_type key_type_big_key;
ee90389
+
ee90389
+extern int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep);
ee90389
+extern void big_key_revoke(struct key *key);
ee90389
+extern void big_key_destroy(struct key *key);
ee90389
+extern void big_key_describe(const struct key *big_key, struct seq_file *m);
ee90389
+extern long big_key_read(const struct key *key, char __user *buffer, size_t buflen);
ee90389
+
ee90389
+#endif /* _KEYS_BIG_KEY_TYPE_H */
ee90389
diff --git a/include/linux/key.h b/include/linux/key.h
ee90389
index 2417f78..010dbb6 100644
ee90389
--- a/include/linux/key.h
ee90389
+++ b/include/linux/key.h
ee90389
@@ -201,6 +201,7 @@ struct key {
ee90389
 			unsigned long		value;
ee90389
 			void __rcu		*rcudata;
ee90389
 			void			*data;
ee90389
+			void			*data2[2];
ee90389
 		} payload;
ee90389
 		struct assoc_array keys;
ee90389
 	};
ee90389
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
ee90389
index 15e0dfe..b563622 100644
ee90389
--- a/security/keys/Kconfig
ee90389
+++ b/security/keys/Kconfig
ee90389
@@ -20,6 +20,17 @@ config KEYS
ee90389
 
ee90389
 	  If you are unsure as to whether this is required, answer N.
ee90389
 
ee90389
+config BIG_KEYS
ee90389
+	tristate "Large payload keys"
ee90389
+	depends on KEYS
ee90389
+	depends on TMPFS
ee90389
+	help
ee90389
+	  This option provides support for holding large keys within the kernel
ee90389
+	  (for example Kerberos ticket caches).  The data may be stored out to
ee90389
+	  swapspace by tmpfs.
ee90389
+
ee90389
+	  If you are unsure as to whether this is required, answer N.
ee90389
+
ee90389
 config TRUSTED_KEYS
ee90389
 	tristate "TRUSTED KEYS"
ee90389
 	depends on KEYS && TCG_TPM
ee90389
diff --git a/security/keys/Makefile b/security/keys/Makefile
ee90389
index 504aaa0..c487c77 100644
ee90389
--- a/security/keys/Makefile
ee90389
+++ b/security/keys/Makefile
ee90389
@@ -22,5 +22,6 @@ obj-$(CONFIG_SYSCTL) += sysctl.o
ee90389
 #
ee90389
 # Key types
ee90389
 #
ee90389
+obj-$(CONFIG_BIG_KEYS) += big_key.o
ee90389
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
ee90389
 obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
ee90389
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
ee90389
new file mode 100644
ee90389
index 0000000..5f9defc
ee90389
--- /dev/null
ee90389
+++ b/security/keys/big_key.c
ee90389
@@ -0,0 +1,204 @@
ee90389
+/* Large capacity key type
ee90389
+ *
ee90389
+ * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
ee90389
+ * Written by David Howells (dhowells@redhat.com)
ee90389
+ *
ee90389
+ * This program is free software; you can redistribute it and/or
ee90389
+ * modify it under the terms of the GNU General Public Licence
ee90389
+ * as published by the Free Software Foundation; either version
ee90389
+ * 2 of the Licence, or (at your option) any later version.
ee90389
+ */
ee90389
+
ee90389
+#include <linux/module.h>
ee90389
+#include <linux/init.h>
ee90389
+#include <linux/seq_file.h>
ee90389
+#include <linux/file.h>
ee90389
+#include <linux/shmem_fs.h>
ee90389
+#include <linux/err.h>
ee90389
+#include <keys/user-type.h>
ee90389
+#include <keys/big_key-type.h>
ee90389
+
ee90389
+MODULE_LICENSE("GPL");
ee90389
+
ee90389
+/*
ee90389
+ * If the data is under this limit, there's no point creating a shm file to
ee90389
+ * hold it as the permanently resident metadata for the shmem fs will be at
ee90389
+ * least as large as the data.
ee90389
+ */
ee90389
+#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
ee90389
+
ee90389
+/*
ee90389
+ * big_key defined keys take an arbitrary string as the description and an
ee90389
+ * arbitrary blob of data as the payload
ee90389
+ */
ee90389
+struct key_type key_type_big_key = {
ee90389
+	.name			= "big_key",
ee90389
+	.def_lookup_type	= KEYRING_SEARCH_LOOKUP_DIRECT,
ee90389
+	.instantiate		= big_key_instantiate,
ee90389
+	.match			= user_match,
ee90389
+	.revoke			= big_key_revoke,
ee90389
+	.destroy		= big_key_destroy,
ee90389
+	.describe		= big_key_describe,
ee90389
+	.read			= big_key_read,
ee90389
+};
ee90389
+
ee90389
+/*
ee90389
+ * Instantiate a big key
ee90389
+ */
ee90389
+int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
ee90389
+{
ee90389
+	struct path *path = (struct path *)&key->payload.data2;
ee90389
+	struct file *file;
ee90389
+	ssize_t written;
ee90389
+	size_t datalen = prep->datalen;
ee90389
+	int ret;
ee90389
+
ee90389
+	ret = -EINVAL;
ee90389
+	if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data)
ee90389
+		goto error;
ee90389
+
ee90389
+	/* Set an arbitrary quota */
ee90389
+	ret = key_payload_reserve(key, 16);
ee90389
+	if (ret < 0)
ee90389
+		goto error;
ee90389
+
ee90389
+	key->type_data.x[1] = datalen;
ee90389
+
ee90389
+	if (datalen > BIG_KEY_FILE_THRESHOLD) {
ee90389
+		/* Create a shmem file to store the data in.  This will permit the data
ee90389
+		 * to be swapped out if needed.
ee90389
+		 *
ee90389
+		 * TODO: Encrypt the stored data with a temporary key.
ee90389
+		 */
ee90389
+		file = shmem_file_setup("", datalen, 0);
ee90389
+		if (IS_ERR(file))
ee90389
+			goto err_quota;
ee90389
+
ee90389
+		written = kernel_write(file, prep->data, prep->datalen, 0);
ee90389
+		if (written != datalen) {
ee90389
+			if (written >= 0)
ee90389
+				ret = -ENOMEM;
ee90389
+			goto err_fput;
ee90389
+		}
ee90389
+
ee90389
+		/* Pin the mount and dentry to the key so that we can open it again
ee90389
+		 * later
ee90389
+		 */
ee90389
+		*path = file->f_path;
ee90389
+		path_get(path);
ee90389
+		fput(file);
ee90389
+	} else {
ee90389
+		/* Just store the data in a buffer */
ee90389
+		void *data = kmalloc(datalen, GFP_KERNEL);
ee90389
+		if (!data) {
ee90389
+			ret = -ENOMEM;
ee90389
+			goto err_quota;
ee90389
+		}
ee90389
+
ee90389
+		key->payload.data = memcpy(data, prep->data, prep->datalen);
ee90389
+	}
ee90389
+	return 0;
ee90389
+
ee90389
+err_fput:
ee90389
+	fput(file);
ee90389
+err_quota:
ee90389
+	key_payload_reserve(key, 0);
ee90389
+error:
ee90389
+	return ret;
ee90389
+}
ee90389
+
ee90389
+/*
ee90389
+ * dispose of the links from a revoked keyring
ee90389
+ * - called with the key sem write-locked
ee90389
+ */
ee90389
+void big_key_revoke(struct key *key)
ee90389
+{
ee90389
+	struct path *path = (struct path *)&key->payload.data2;
ee90389
+
ee90389
+	/* clear the quota */
ee90389
+	key_payload_reserve(key, 0);
ee90389
+	if (key_is_instantiated(key) && key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD)
ee90389
+		vfs_truncate(path, 0);
ee90389
+}
ee90389
+
ee90389
+/*
ee90389
+ * dispose of the data dangling from the corpse of a big_key key
ee90389
+ */
ee90389
+void big_key_destroy(struct key *key)
ee90389
+{
ee90389
+	if (key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) {
ee90389
+		struct path *path = (struct path *)&key->payload.data2;
ee90389
+		path_put(path);
ee90389
+		path->mnt = NULL;
ee90389
+		path->dentry = NULL;
ee90389
+	} else {
ee90389
+		kfree(key->payload.data);
ee90389
+		key->payload.data = NULL;
ee90389
+	}
ee90389
+}
ee90389
+
ee90389
+/*
ee90389
+ * describe the big_key key
ee90389
+ */
ee90389
+void big_key_describe(const struct key *key, struct seq_file *m)
ee90389
+{
ee90389
+	unsigned long datalen = key->type_data.x[1];
ee90389
+
ee90389
+	seq_puts(m, key->description);
ee90389
+
ee90389
+	if (key_is_instantiated(key))
ee90389
+		seq_printf(m, ": %lu [%s]",
ee90389
+			   datalen,
ee90389
+			   datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
ee90389
+}
ee90389
+
ee90389
+/*
ee90389
+ * read the key data
ee90389
+ * - the key's semaphore is read-locked
ee90389
+ */
ee90389
+long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
ee90389
+{
ee90389
+	unsigned long datalen = key->type_data.x[1];
ee90389
+	long ret;
ee90389
+
ee90389
+	if (!buffer || buflen < datalen)
ee90389
+		return datalen;
ee90389
+
ee90389
+	if (datalen > BIG_KEY_FILE_THRESHOLD) {
ee90389
+		struct path *path = (struct path *)&key->payload.data2;
ee90389
+		struct file *file;
ee90389
+		loff_t pos;
ee90389
+
ee90389
+		file = dentry_open(path, O_RDONLY, current_cred());
ee90389
+		if (IS_ERR(file))
ee90389
+			return PTR_ERR(file);
ee90389
+
ee90389
+		pos = 0;
ee90389
+		ret = vfs_read(file, buffer, datalen, &pos;;
ee90389
+		fput(file);
ee90389
+		if (ret >= 0 && ret != datalen)
ee90389
+			ret = -EIO;
ee90389
+	} else {
ee90389
+		ret = datalen;
ee90389
+		if (copy_to_user(buffer, key->payload.data, datalen) != 0)
ee90389
+			ret = -EFAULT;
ee90389
+	}
ee90389
+
ee90389
+	return ret;
ee90389
+}
ee90389
+
ee90389
+/*
ee90389
+ * Module stuff
ee90389
+ */
ee90389
+static int __init big_key_init(void)
ee90389
+{
ee90389
+	return register_key_type(&key_type_big_key);
ee90389
+}
ee90389
+
ee90389
+static void __exit big_key_cleanup(void)
ee90389
+{
ee90389
+	unregister_key_type(&key_type_big_key);
ee90389
+}
ee90389
+
ee90389
+module_init(big_key_init);
ee90389
+module_exit(big_key_cleanup);
ee90389
-- 
ee90389
1.8.3.1
ee90389
ee90389
ee90389
From b1e5b74e060add16de8d6005802644fa1700167f Mon Sep 17 00:00:00 2001
ee90389
From: David Howells <dhowells@redhat.com>
ee90389
Date: Fri, 30 Aug 2013 15:37:54 +0100
ee90389
Subject: [PATCH 2/2] KEYS: Add per-user_namespace registers for persistent
ee90389
 per-UID kerberos caches
ee90389
ee90389
Add support for per-user_namespace registers of persistent per-UID kerberos
ee90389
caches held within the kernel.
ee90389
ee90389
This allows the kerberos cache to be retained beyond the life of all a user's
ee90389
processes so that the user's cron jobs can work.
ee90389
ee90389
The kerberos cache is envisioned as a keyring/key tree looking something like:
ee90389
ee90389
	struct user_namespace
ee90389
	  \___ .krb_cache keyring		- The register
ee90389
		\___ _krb.0 keyring		- Root's Kerberos cache
ee90389
		\___ _krb.5000 keyring		- User 5000's Kerberos cache
ee90389
		\___ _krb.5001 keyring		- User 5001's Kerberos cache
ee90389
			\___ tkt785 big_key	- A ccache blob
ee90389
			\___ tkt12345 big_key	- Another ccache blob
ee90389
ee90389
Or possibly:
ee90389
ee90389
	struct user_namespace
ee90389
	  \___ .krb_cache keyring		- The register
ee90389
		\___ _krb.0 keyring		- Root's Kerberos cache
ee90389
		\___ _krb.5000 keyring		- User 5000's Kerberos cache
ee90389
		\___ _krb.5001 keyring		- User 5001's Kerberos cache
ee90389
			\___ tkt785 keyring	- A ccache
ee90389
				\___ krbtgt/REDHAT.COM@REDHAT.COM big_key
ee90389
				\___ http/REDHAT.COM@REDHAT.COM user
ee90389
				\___ afs/REDHAT.COM@REDHAT.COM user
ee90389
				\___ nfs/REDHAT.COM@REDHAT.COM user
ee90389
				\___ krbtgt/KERNEL.ORG@KERNEL.ORG big_key
ee90389
				\___ http/KERNEL.ORG@KERNEL.ORG big_key
ee90389
ee90389
What goes into a particular Kerberos cache is entirely up to userspace.  Kernel
ee90389
support is limited to giving you the Kerberos cache keyring that you want.
ee90389
ee90389
The user asks for their Kerberos cache by:
ee90389
ee90389
	krb_cache = keyctl_get_krbcache(uid, dest_keyring);
ee90389
ee90389
The uid is -1 or the user's own UID for the user's own cache or the uid of some
ee90389
other user's cache (requires CAP_SETUID).  This permits rpc.gssd or whatever to
ee90389
mess with the cache.
ee90389
ee90389
The cache returned is a keyring named "_krb.<uid>" that the possessor can read,
ee90389
search, clear, invalidate, unlink from and add links to.  Active LSMs get a
ee90389
chance to rule on whether the caller is permitted to make a link.
ee90389
ee90389
Each uid's cache keyring is created when it first accessed and is given a
ee90389
timeout that is extended each time this function is called so that the keyring
ee90389
goes away after a while.  The timeout is configurable by sysctl but defaults to
ee90389
three days.
ee90389
ee90389
Each user_namespace struct gets a lazily-created keyring that serves as the
ee90389
register.  The cache keyrings are added to it.  This means that standard key
ee90389
search and garbage collection facilities are available.
ee90389
ee90389
The user_namespace struct's register goes away when it does and anything left
ee90389
in it is then automatically gc'd.
ee90389
ee90389
Signed-off-by: David Howells <dhowells@redhat.com>
ee90389
Tested-by: Simo Sorce <simo@redhat.com>
ee90389
cc: Serge E. Hallyn <serge.hallyn@ubuntu.com>
ee90389
cc: Eric W. Biederman <ebiederm@xmission.com>
ee90389
---
ee90389
 include/linux/user_namespace.h |   6 ++
ee90389
 include/uapi/linux/keyctl.h    |   1 +
ee90389
 kernel/user.c                  |   4 +
ee90389
 kernel/user_namespace.c        |   6 ++
ee90389
 security/keys/Kconfig          |  17 +++++
ee90389
 security/keys/Makefile         |   1 +
ee90389
 security/keys/compat.c         |   3 +
ee90389
 security/keys/internal.h       |   9 +++
ee90389
 security/keys/keyctl.c         |   3 +
ee90389
 security/keys/persistent.c     | 169 +++++++++++++++++++++++++++++++++++++++++
ee90389
 security/keys/sysctl.c         |  11 +++
ee90389
 11 files changed, 230 insertions(+)
ee90389
 create mode 100644 security/keys/persistent.c
ee90389
ee90389
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
ee90389
index b6b215f..cf21958 100644
ee90389
--- a/include/linux/user_namespace.h
ee90389
+++ b/include/linux/user_namespace.h
ee90389
@@ -28,6 +28,12 @@ struct user_namespace {
ee90389
 	unsigned int		proc_inum;
ee90389
 	bool			may_mount_sysfs;
ee90389
 	bool			may_mount_proc;
ee90389
+
ee90389
+	/* Register of per-UID persistent keyrings for this namespace */
ee90389
+#ifdef CONFIG_PERSISTENT_KEYRINGS
ee90389
+	struct key		*persistent_keyring_register;
ee90389
+	struct rw_semaphore	persistent_keyring_register_sem;
ee90389
+#endif
ee90389
 };
ee90389
 
ee90389
 extern struct user_namespace init_user_ns;
ee90389
diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h
ee90389
index c9b7f4fa..840cb99 100644
ee90389
--- a/include/uapi/linux/keyctl.h
ee90389
+++ b/include/uapi/linux/keyctl.h
ee90389
@@ -56,5 +56,6 @@
ee90389
 #define KEYCTL_REJECT			19	/* reject a partially constructed key */
ee90389
 #define KEYCTL_INSTANTIATE_IOV		20	/* instantiate a partially constructed key */
ee90389
 #define KEYCTL_INVALIDATE		21	/* invalidate a key */
ee90389
+#define KEYCTL_GET_PERSISTENT		22	/* get a user's persistent keyring */
ee90389
 
ee90389
 #endif /*  _LINUX_KEYCTL_H */
ee90389
diff --git a/kernel/user.c b/kernel/user.c
ee90389
index 69b4c3d..6c9e1b9 100644
ee90389
--- a/kernel/user.c
ee90389
+++ b/kernel/user.c
ee90389
@@ -53,6 +53,10 @@ struct user_namespace init_user_ns = {
ee90389
 	.proc_inum = PROC_USER_INIT_INO,
ee90389
 	.may_mount_sysfs = true,
ee90389
 	.may_mount_proc = true,
ee90389
+#ifdef CONFIG_KEYS_KERBEROS_CACHE
ee90389
+	.krb_cache_register_sem =
ee90389
+	__RWSEM_INITIALIZER(init_user_ns.krb_cache_register_sem),
ee90389
+#endif
ee90389
 };
ee90389
 EXPORT_SYMBOL_GPL(init_user_ns);
ee90389
 
ee90389
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
ee90389
index d8c30db..ef7985e 100644
ee90389
--- a/kernel/user_namespace.c
ee90389
+++ b/kernel/user_namespace.c
ee90389
@@ -99,6 +99,9 @@ int create_user_ns(struct cred *new)
ee90389
 
ee90389
 	update_mnt_policy(ns);
ee90389
 
ee90389
+#ifdef CONFIG_PERSISTENT_KEYRINGS
ee90389
+	rwsem_init(&ns->persistent_keyring_register_sem);
ee90389
+#endif
ee90389
 	return 0;
ee90389
 }
ee90389
 
ee90389
@@ -123,6 +126,9 @@ void free_user_ns(struct user_namespace *ns)
ee90389
 
ee90389
 	do {
ee90389
 		parent = ns->parent;
ee90389
+#ifdef CONFIG_PERSISTENT_KEYRINGS
ee90389
+		key_put(ns->persistent_keyring_register);
ee90389
+#endif
ee90389
 		proc_free_inum(ns->proc_inum);
ee90389
 		kmem_cache_free(user_ns_cachep, ns);
ee90389
 		ns = parent;
ee90389
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
ee90389
index b563622..53d8748 100644
ee90389
--- a/security/keys/Kconfig
ee90389
+++ b/security/keys/Kconfig
ee90389
@@ -20,6 +20,23 @@ config KEYS
ee90389
 
ee90389
 	  If you are unsure as to whether this is required, answer N.
ee90389
 
ee90389
+config PERSISTENT_KEYRINGS
ee90389
+	bool "Enable register of persistent per-UID keyrings"
ee90389
+	depends on KEYS
ee90389
+	help
ee90389
+	  This option provides a register of persistent per-UID keyrings,
ee90389
+	  primarily aimed at Kerberos key storage.  The keyrings are persistent
ee90389
+	  in the sense that they stay around after all processes of that UID
ee90389
+	  have exited, not that they survive the machine being rebooted.
ee90389
+
ee90389
+	  A particular keyring may be accessed by either the user whose keyring
ee90389
+	  it is or by a process with administrative privileges.  The active
ee90389
+	  LSMs gets to rule on which admin-level processes get to access the
ee90389
+	  cache.
ee90389
+
ee90389
+	  Keyrings are created and added into the register upon demand and get
ee90389
+	  removed if they expire (a default timeout is set upon creation).
ee90389
+
ee90389
 config BIG_KEYS
ee90389
 	tristate "Large payload keys"
ee90389
 	depends on KEYS
ee90389
diff --git a/security/keys/Makefile b/security/keys/Makefile
ee90389
index c487c77..dfb3a7b 100644
ee90389
--- a/security/keys/Makefile
ee90389
+++ b/security/keys/Makefile
ee90389
@@ -18,6 +18,7 @@ obj-y := \
ee90389
 obj-$(CONFIG_KEYS_COMPAT) += compat.o
ee90389
 obj-$(CONFIG_PROC_FS) += proc.o
ee90389
 obj-$(CONFIG_SYSCTL) += sysctl.o
ee90389
+obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
ee90389
 
ee90389
 #
ee90389
 # Key types
ee90389
diff --git a/security/keys/compat.c b/security/keys/compat.c
ee90389
index d65fa7f..bbd32c7 100644
ee90389
--- a/security/keys/compat.c
ee90389
+++ b/security/keys/compat.c
ee90389
@@ -138,6 +138,9 @@ asmlinkage long compat_sys_keyctl(u32 option,
ee90389
 	case KEYCTL_INVALIDATE:
ee90389
 		return keyctl_invalidate_key(arg2);
ee90389
 
ee90389
+	case KEYCTL_GET_PERSISTENT:
ee90389
+		return keyctl_get_persistent(arg2, arg3);
ee90389
+
ee90389
 	default:
ee90389
 		return -EOPNOTSUPP;
ee90389
 	}
ee90389
diff --git a/security/keys/internal.h b/security/keys/internal.h
ee90389
index 581c6f6..80b2aac 100644
ee90389
--- a/security/keys/internal.h
ee90389
+++ b/security/keys/internal.h
ee90389
@@ -255,6 +255,15 @@ extern long keyctl_invalidate_key(key_serial_t);
ee90389
 extern long keyctl_instantiate_key_common(key_serial_t,
ee90389
 					  const struct iovec *,
ee90389
 					  unsigned, size_t, key_serial_t);
ee90389
+#ifdef CONFIG_PERSISTENT_KEYRINGS
ee90389
+extern long keyctl_get_persistent(uid_t, key_serial_t);
ee90389
+extern unsigned persistent_keyring_expiry;
ee90389
+#else
ee90389
+static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
ee90389
+{
ee90389
+	return -EOPNOTSUPP;
ee90389
+}
ee90389
+#endif
ee90389
 
ee90389
 /*
ee90389
  * Debugging key validation
ee90389
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
ee90389
index 33cfd27..cee72ce 100644
ee90389
--- a/security/keys/keyctl.c
ee90389
+++ b/security/keys/keyctl.c
ee90389
@@ -1667,6 +1667,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
ee90389
 	case KEYCTL_INVALIDATE:
ee90389
 		return keyctl_invalidate_key((key_serial_t) arg2);
ee90389
 
ee90389
+	case KEYCTL_GET_PERSISTENT:
ee90389
+		return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3);
ee90389
+
ee90389
 	default:
ee90389
 		return -EOPNOTSUPP;
ee90389
 	}
ee90389
diff --git a/security/keys/persistent.c b/security/keys/persistent.c
ee90389
new file mode 100644
ee90389
index 0000000..631a022
ee90389
--- /dev/null
ee90389
+++ b/security/keys/persistent.c
ee90389
@@ -0,0 +1,169 @@
ee90389
+/* General persistent per-UID keyrings register
ee90389
+ *
ee90389
+ * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
ee90389
+ * Written by David Howells (dhowells@redhat.com)
ee90389
+ *
ee90389
+ * This program is free software; you can redistribute it and/or
ee90389
+ * modify it under the terms of the GNU General Public Licence
ee90389
+ * as published by the Free Software Foundation; either version
ee90389
+ * 2 of the Licence, or (at your option) any later version.
ee90389
+ */
ee90389
+
ee90389
+#include <linux/user_namespace.h>
ee90389
+#include "internal.h"
ee90389
+
ee90389
+unsigned persistent_keyring_expiry = 3 * 24 * 3600; /* Expire after 3 days of non-use */
ee90389
+
ee90389
+/*
ee90389
+ * Create the persistent keyring register for the current user namespace.
ee90389
+ *
ee90389
+ * Called with the namespace's sem locked for writing.
ee90389
+ */
ee90389
+static int key_create_persistent_register(struct user_namespace *ns)
ee90389
+{
ee90389
+	struct key *reg = keyring_alloc(".persistent_register",
ee90389
+					KUIDT_INIT(0), KGIDT_INIT(0),
ee90389
+					current_cred(),
ee90389
+					((KEY_POS_ALL & ~KEY_POS_SETATTR) |
ee90389
+					 KEY_USR_VIEW | KEY_USR_READ),
ee90389
+					KEY_ALLOC_NOT_IN_QUOTA, NULL);
ee90389
+	if (IS_ERR(reg))
ee90389
+		return PTR_ERR(reg);
ee90389
+
ee90389
+	ns->persistent_keyring_register = reg;
ee90389
+	return 0;
ee90389
+}
ee90389
+
ee90389
+/*
ee90389
+ * Create the persistent keyring for the specified user.
ee90389
+ *
ee90389
+ * Called with the namespace's sem locked for writing.
ee90389
+ */
ee90389
+static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid,
ee90389
+				       struct keyring_index_key *index_key)
ee90389
+{
ee90389
+	struct key *persistent;
ee90389
+	key_ref_t reg_ref, persistent_ref;
ee90389
+
ee90389
+	if (!ns->persistent_keyring_register) {
ee90389
+		long err = key_create_persistent_register(ns);
ee90389
+		if (err < 0)
ee90389
+			return ERR_PTR(err);
ee90389
+	} else {
ee90389
+		reg_ref = make_key_ref(ns->persistent_keyring_register, true);
ee90389
+		persistent_ref = find_key_to_update(reg_ref, index_key);
ee90389
+		if (persistent_ref)
ee90389
+			return persistent_ref;
ee90389
+	}
ee90389
+
ee90389
+	persistent = keyring_alloc(index_key->description,
ee90389
+				   uid, INVALID_GID, current_cred(),
ee90389
+				   ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
ee90389
+				    KEY_USR_VIEW | KEY_USR_READ),
ee90389
+				   KEY_ALLOC_NOT_IN_QUOTA,
ee90389
+				   ns->persistent_keyring_register);
ee90389
+	if (IS_ERR(persistent))
ee90389
+		return ERR_CAST(persistent);
ee90389
+
ee90389
+	return make_key_ref(persistent, true);
ee90389
+}
ee90389
+
ee90389
+/*
ee90389
+ * Get the persistent keyring for a specific UID and link it to the nominated
ee90389
+ * keyring.
ee90389
+ */
ee90389
+static long key_get_persistent(struct user_namespace *ns, kuid_t uid,
ee90389
+			       key_ref_t dest_ref)
ee90389
+{
ee90389
+	struct keyring_index_key index_key;
ee90389
+	struct key *persistent;
ee90389
+	key_ref_t reg_ref, persistent_ref;
ee90389
+	char buf[32];
ee90389
+	long ret;
ee90389
+
ee90389
+	/* Look in the register if it exists */
ee90389
+	index_key.type = &key_type_keyring;
ee90389
+	index_key.description = buf;
ee90389
+	index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid));
ee90389
+
ee90389
+	if (ns->persistent_keyring_register) {
ee90389
+		reg_ref = make_key_ref(ns->persistent_keyring_register, true);
ee90389
+		down_read(&ns->persistent_keyring_register_sem);
ee90389
+		persistent_ref = find_key_to_update(reg_ref, &index_key);
ee90389
+		up_read(&ns->persistent_keyring_register_sem);
ee90389
+
ee90389
+		if (persistent_ref)
ee90389
+			goto found;
ee90389
+	}
ee90389
+
ee90389
+	/* It wasn't in the register, so we'll need to create it.  We might
ee90389
+	 * also need to create the register.
ee90389
+	 */
ee90389
+	down_write(&ns->persistent_keyring_register_sem);
ee90389
+	persistent_ref = key_create_persistent(ns, uid, &index_key);
ee90389
+	up_write(&ns->persistent_keyring_register_sem);
ee90389
+	if (!IS_ERR(persistent_ref))
ee90389
+		goto found;
ee90389
+
ee90389
+	return PTR_ERR(persistent_ref);
ee90389
+
ee90389
+found:
ee90389
+	ret = key_task_permission(persistent_ref, current_cred(), KEY_LINK);
ee90389
+	if (ret == 0) {
ee90389
+		persistent = key_ref_to_ptr(persistent_ref);
ee90389
+		ret = key_link(key_ref_to_ptr(dest_ref), persistent);
ee90389
+		if (ret == 0) {
ee90389
+			key_set_timeout(persistent, persistent_keyring_expiry);
ee90389
+			ret = persistent->serial;		
ee90389
+		}
ee90389
+	}
ee90389
+
ee90389
+	key_ref_put(persistent_ref);
ee90389
+	return ret;
ee90389
+}
ee90389
+
ee90389
+/*
ee90389
+ * Get the persistent keyring for a specific UID and link it to the nominated
ee90389
+ * keyring.
ee90389
+ */
ee90389
+long keyctl_get_persistent(uid_t _uid, key_serial_t destid)
ee90389
+{
ee90389
+	struct user_namespace *ns = current_user_ns();
ee90389
+	key_ref_t dest_ref;
ee90389
+	kuid_t uid;
ee90389
+	long ret;
ee90389
+
ee90389
+	/* -1 indicates the current user */
ee90389
+	if (_uid == (uid_t)-1) {
ee90389
+		uid = current_uid();
ee90389
+	} else {
ee90389
+		uid = make_kuid(ns, _uid);
ee90389
+		if (!uid_valid(uid))
ee90389
+			return -EINVAL;
ee90389
+
ee90389
+		/* You can only see your own persistent cache if you're not
ee90389
+		 * sufficiently privileged.
ee90389
+		 */
ee90389
+		if (uid != current_uid() &&
ee90389
+		    uid != current_suid() &&
ee90389
+		    uid != current_euid() &&
ee90389
+		    uid != current_fsuid() &&
ee90389
+		    !ns_capable(ns, CAP_SETUID))
ee90389
+			return -EPERM;
ee90389
+	}
ee90389
+
ee90389
+	/* There must be a destination keyring */
ee90389
+	dest_ref = lookup_user_key(destid, KEY_LOOKUP_CREATE, KEY_WRITE);
ee90389
+	if (IS_ERR(dest_ref))
ee90389
+		return PTR_ERR(dest_ref);
ee90389
+	if (key_ref_to_ptr(dest_ref)->type != &key_type_keyring) {
ee90389
+		ret = -ENOTDIR;
ee90389
+		goto out_put_dest;
ee90389
+	}
ee90389
+
ee90389
+	ret = key_get_persistent(ns, uid, dest_ref);
ee90389
+
ee90389
+out_put_dest:
ee90389
+	key_ref_put(dest_ref);
ee90389
+	return ret;
ee90389
+}
ee90389
diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c
ee90389
index ee32d18..8c0af08 100644
ee90389
--- a/security/keys/sysctl.c
ee90389
+++ b/security/keys/sysctl.c
ee90389
@@ -61,5 +61,16 @@ ctl_table key_sysctls[] = {
ee90389
 		.extra1 = (void *) &zero,
ee90389
 		.extra2 = (void *) &max,
ee90389
 	},
ee90389
+#ifdef CONFIG_PERSISTENT_KEYRINGS
ee90389
+	{
ee90389
+		.procname = "persistent_keyring_expiry",
ee90389
+		.data = &persistent_keyring_expiry,
ee90389
+		.maxlen = sizeof(unsigned),
ee90389
+		.mode = 0644,
ee90389
+		.proc_handler = proc_dointvec_minmax,
ee90389
+		.extra1 = (void *) &zero,
ee90389
+		.extra2 = (void *) &max,
ee90389
+	},
ee90389
+#endif
ee90389
 	{ }
ee90389
 };
ee90389
-- 
ee90389
1.8.3.1
ee90389