e3f67fd712
- Fixup keys-krb-support.patch to build with USER_NS enabled
749 lines
22 KiB
Diff
749 lines
22 KiB
Diff
From d7ccdaa17aab12a49f5e9e327b55167c4af26bf8 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 862e98313b10123fa4352117b0b0c0f5a530cefb 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 | 7 ++
|
|
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, 231 insertions(+)
|
|
create mode 100644 security/keys/persistent.c
|
|
|
|
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
|
|
index 4db2985..bb0639d 100644
|
|
--- a/include/linux/user_namespace.h
|
|
+++ b/include/linux/user_namespace.h
|
|
@@ -27,6 +27,13 @@ struct user_namespace {
|
|
kuid_t owner;
|
|
kgid_t group;
|
|
unsigned int proc_inum;
|
|
+
|
|
+ /* 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 5bbb919..a3a0dbf 100644
|
|
--- a/kernel/user.c
|
|
+++ b/kernel/user.c
|
|
@@ -51,6 +51,10 @@ struct user_namespace init_user_ns = {
|
|
.owner = GLOBAL_ROOT_UID,
|
|
.group = GLOBAL_ROOT_GID,
|
|
.proc_inum = PROC_USER_INIT_INO,
|
|
+#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 13fb113..2dbc299 100644
|
|
--- a/kernel/user_namespace.c
|
|
+++ b/kernel/user_namespace.c
|
|
@@ -101,6 +101,9 @@ int create_user_ns(struct cred *new)
|
|
|
|
set_cred_user_ns(new, ns);
|
|
|
|
+#ifdef CONFIG_PERSISTENT_KEYRINGS
|
|
+ init_rwsem(&ns->persistent_keyring_register_sem);
|
|
+#endif
|
|
return 0;
|
|
}
|
|
|
|
@@ -130,6 +133,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_eq(uid, current_uid()) &&
|
|
+ /* uid_eq(uid, current_suid()) && */
|
|
+ !uid_eq(uid, current_euid()) &&
|
|
+ /* uid_eq(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
|
|
|