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