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