Add keyring patches to support krb5 (rhbz 1003043)
This commit is contained in:
parent
49ba08f01f
commit
9cb73b95df
@ -4203,6 +4203,8 @@ CONFIG_ZLIB_DEFLATE=m
|
||||
|
||||
CONFIG_INITRAMFS_SOURCE=""
|
||||
CONFIG_KEYS=y
|
||||
CONFIG_PERSISTENT_KEYRINGS=y
|
||||
CONFIG_BIG_KEYS=m
|
||||
CONFIG_TRUSTED_KEYS=m
|
||||
CONFIG_ENCRYPTED_KEYS=m
|
||||
CONFIG_KEYS_DEBUG_PROC_KEYS=y
|
||||
|
11
kernel.spec
11
kernel.spec
@ -646,6 +646,10 @@ Patch800: crash-driver.patch
|
||||
|
||||
# crypto/
|
||||
|
||||
# keys
|
||||
Patch900: keys-expand-keyring.patch
|
||||
Patch901: keys-krb-support.patch
|
||||
|
||||
# secure boot
|
||||
Patch1000: secure-modules.patch
|
||||
Patch1001: modsign-uefi.patch
|
||||
@ -1379,6 +1383,10 @@ ApplyPatch crash-driver.patch
|
||||
|
||||
# crypto/
|
||||
|
||||
# keys
|
||||
ApplyPatch keys-expand-keyring.patch
|
||||
ApplyPatch keys-krb-support.patch
|
||||
|
||||
# secure boot
|
||||
ApplyPatch secure-modules.patch
|
||||
ApplyPatch modsign-uefi.patch
|
||||
@ -2258,6 +2266,9 @@ fi
|
||||
# ||----w |
|
||||
# || ||
|
||||
%changelog
|
||||
* Tue Sep 03 2013 Josh Boyer <jwboyer@fedoraproject.org>
|
||||
- Add keyring patches to support krb5 (rhbz 1003043)
|
||||
|
||||
* Tue Sep 03 2013 Kyle McMartin <kyle@redhat.com>
|
||||
- [arm64] disable VGA_CONSOLE and PARPORT_PC
|
||||
- [arm64] install dtb as on %{arm}
|
||||
|
6834
keys-expand-keyring.patch
Normal file
6834
keys-expand-keyring.patch
Normal file
File diff suppressed because it is too large
Load Diff
747
keys-krb-support.patch
Normal file
747
keys-krb-support.patch
Normal file
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user