Commit f36f8c75ae2e7d4da34f4c908cebdb4aa42c977e

Authored by David Howells
1 parent ab3c3587f8

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>

Showing 11 changed files with 230 additions and 0 deletions Side-by-side Diff

include/linux/user_namespace.h
... ... @@ -27,6 +27,12 @@
27 27 kuid_t owner;
28 28 kgid_t group;
29 29 unsigned int proc_inum;
  30 +
  31 + /* Register of per-UID persistent keyrings for this namespace */
  32 +#ifdef CONFIG_PERSISTENT_KEYRINGS
  33 + struct key *persistent_keyring_register;
  34 + struct rw_semaphore persistent_keyring_register_sem;
  35 +#endif
30 36 };
31 37  
32 38 extern struct user_namespace init_user_ns;
include/uapi/linux/keyctl.h
... ... @@ -56,6 +56,7 @@
56 56 #define KEYCTL_REJECT 19 /* reject a partially constructed key */
57 57 #define KEYCTL_INSTANTIATE_IOV 20 /* instantiate a partially constructed key */
58 58 #define KEYCTL_INVALIDATE 21 /* invalidate a key */
  59 +#define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */
59 60  
60 61 #endif /* _LINUX_KEYCTL_H */
... ... @@ -51,6 +51,10 @@
51 51 .owner = GLOBAL_ROOT_UID,
52 52 .group = GLOBAL_ROOT_GID,
53 53 .proc_inum = PROC_USER_INIT_INO,
  54 +#ifdef CONFIG_KEYS_KERBEROS_CACHE
  55 + .krb_cache_register_sem =
  56 + __RWSEM_INITIALIZER(init_user_ns.krb_cache_register_sem),
  57 +#endif
54 58 };
55 59 EXPORT_SYMBOL_GPL(init_user_ns);
56 60  
kernel/user_namespace.c
... ... @@ -101,6 +101,9 @@
101 101  
102 102 set_cred_user_ns(new, ns);
103 103  
  104 +#ifdef CONFIG_PERSISTENT_KEYRINGS
  105 + init_rwsem(&ns->persistent_keyring_register_sem);
  106 +#endif
104 107 return 0;
105 108 }
106 109  
... ... @@ -130,6 +133,9 @@
130 133  
131 134 do {
132 135 parent = ns->parent;
  136 +#ifdef CONFIG_PERSISTENT_KEYRINGS
  137 + key_put(ns->persistent_keyring_register);
  138 +#endif
133 139 proc_free_inum(ns->proc_inum);
134 140 kmem_cache_free(user_ns_cachep, ns);
135 141 ns = parent;
security/keys/Kconfig
... ... @@ -20,6 +20,23 @@
20 20  
21 21 If you are unsure as to whether this is required, answer N.
22 22  
  23 +config PERSISTENT_KEYRINGS
  24 + bool "Enable register of persistent per-UID keyrings"
  25 + depends on KEYS
  26 + help
  27 + This option provides a register of persistent per-UID keyrings,
  28 + primarily aimed at Kerberos key storage. The keyrings are persistent
  29 + in the sense that they stay around after all processes of that UID
  30 + have exited, not that they survive the machine being rebooted.
  31 +
  32 + A particular keyring may be accessed by either the user whose keyring
  33 + it is or by a process with administrative privileges. The active
  34 + LSMs gets to rule on which admin-level processes get to access the
  35 + cache.
  36 +
  37 + Keyrings are created and added into the register upon demand and get
  38 + removed if they expire (a default timeout is set upon creation).
  39 +
23 40 config BIG_KEYS
24 41 tristate "Large payload keys"
25 42 depends on KEYS
security/keys/Makefile
... ... @@ -18,6 +18,7 @@
18 18 obj-$(CONFIG_KEYS_COMPAT) += compat.o
19 19 obj-$(CONFIG_PROC_FS) += proc.o
20 20 obj-$(CONFIG_SYSCTL) += sysctl.o
  21 +obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
21 22  
22 23 #
23 24 # Key types
security/keys/compat.c
... ... @@ -138,6 +138,9 @@
138 138 case KEYCTL_INVALIDATE:
139 139 return keyctl_invalidate_key(arg2);
140 140  
  141 + case KEYCTL_GET_PERSISTENT:
  142 + return keyctl_get_persistent(arg2, arg3);
  143 +
141 144 default:
142 145 return -EOPNOTSUPP;
143 146 }
security/keys/internal.h
... ... @@ -255,6 +255,15 @@
255 255 extern long keyctl_instantiate_key_common(key_serial_t,
256 256 const struct iovec *,
257 257 unsigned, size_t, key_serial_t);
  258 +#ifdef CONFIG_PERSISTENT_KEYRINGS
  259 +extern long keyctl_get_persistent(uid_t, key_serial_t);
  260 +extern unsigned persistent_keyring_expiry;
  261 +#else
  262 +static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
  263 +{
  264 + return -EOPNOTSUPP;
  265 +}
  266 +#endif
258 267  
259 268 /*
260 269 * Debugging key validation
security/keys/keyctl.c
... ... @@ -1667,6 +1667,9 @@
1667 1667 case KEYCTL_INVALIDATE:
1668 1668 return keyctl_invalidate_key((key_serial_t) arg2);
1669 1669  
  1670 + case KEYCTL_GET_PERSISTENT:
  1671 + return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3);
  1672 +
1670 1673 default:
1671 1674 return -EOPNOTSUPP;
1672 1675 }
security/keys/persistent.c
  1 +/* General persistent per-UID keyrings register
  2 + *
  3 + * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
  4 + * Written by David Howells (dhowells@redhat.com)
  5 + *
  6 + * This program is free software; you can redistribute it and/or
  7 + * modify it under the terms of the GNU General Public Licence
  8 + * as published by the Free Software Foundation; either version
  9 + * 2 of the Licence, or (at your option) any later version.
  10 + */
  11 +
  12 +#include <linux/user_namespace.h>
  13 +#include "internal.h"
  14 +
  15 +unsigned persistent_keyring_expiry = 3 * 24 * 3600; /* Expire after 3 days of non-use */
  16 +
  17 +/*
  18 + * Create the persistent keyring register for the current user namespace.
  19 + *
  20 + * Called with the namespace's sem locked for writing.
  21 + */
  22 +static int key_create_persistent_register(struct user_namespace *ns)
  23 +{
  24 + struct key *reg = keyring_alloc(".persistent_register",
  25 + KUIDT_INIT(0), KGIDT_INIT(0),
  26 + current_cred(),
  27 + ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
  28 + KEY_USR_VIEW | KEY_USR_READ),
  29 + KEY_ALLOC_NOT_IN_QUOTA, NULL);
  30 + if (IS_ERR(reg))
  31 + return PTR_ERR(reg);
  32 +
  33 + ns->persistent_keyring_register = reg;
  34 + return 0;
  35 +}
  36 +
  37 +/*
  38 + * Create the persistent keyring for the specified user.
  39 + *
  40 + * Called with the namespace's sem locked for writing.
  41 + */
  42 +static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid,
  43 + struct keyring_index_key *index_key)
  44 +{
  45 + struct key *persistent;
  46 + key_ref_t reg_ref, persistent_ref;
  47 +
  48 + if (!ns->persistent_keyring_register) {
  49 + long err = key_create_persistent_register(ns);
  50 + if (err < 0)
  51 + return ERR_PTR(err);
  52 + } else {
  53 + reg_ref = make_key_ref(ns->persistent_keyring_register, true);
  54 + persistent_ref = find_key_to_update(reg_ref, index_key);
  55 + if (persistent_ref)
  56 + return persistent_ref;
  57 + }
  58 +
  59 + persistent = keyring_alloc(index_key->description,
  60 + uid, INVALID_GID, current_cred(),
  61 + ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
  62 + KEY_USR_VIEW | KEY_USR_READ),
  63 + KEY_ALLOC_NOT_IN_QUOTA,
  64 + ns->persistent_keyring_register);
  65 + if (IS_ERR(persistent))
  66 + return ERR_CAST(persistent);
  67 +
  68 + return make_key_ref(persistent, true);
  69 +}
  70 +
  71 +/*
  72 + * Get the persistent keyring for a specific UID and link it to the nominated
  73 + * keyring.
  74 + */
  75 +static long key_get_persistent(struct user_namespace *ns, kuid_t uid,
  76 + key_ref_t dest_ref)
  77 +{
  78 + struct keyring_index_key index_key;
  79 + struct key *persistent;
  80 + key_ref_t reg_ref, persistent_ref;
  81 + char buf[32];
  82 + long ret;
  83 +
  84 + /* Look in the register if it exists */
  85 + index_key.type = &key_type_keyring;
  86 + index_key.description = buf;
  87 + index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid));
  88 +
  89 + if (ns->persistent_keyring_register) {
  90 + reg_ref = make_key_ref(ns->persistent_keyring_register, true);
  91 + down_read(&ns->persistent_keyring_register_sem);
  92 + persistent_ref = find_key_to_update(reg_ref, &index_key);
  93 + up_read(&ns->persistent_keyring_register_sem);
  94 +
  95 + if (persistent_ref)
  96 + goto found;
  97 + }
  98 +
  99 + /* It wasn't in the register, so we'll need to create it. We might
  100 + * also need to create the register.
  101 + */
  102 + down_write(&ns->persistent_keyring_register_sem);
  103 + persistent_ref = key_create_persistent(ns, uid, &index_key);
  104 + up_write(&ns->persistent_keyring_register_sem);
  105 + if (!IS_ERR(persistent_ref))
  106 + goto found;
  107 +
  108 + return PTR_ERR(persistent_ref);
  109 +
  110 +found:
  111 + ret = key_task_permission(persistent_ref, current_cred(), KEY_LINK);
  112 + if (ret == 0) {
  113 + persistent = key_ref_to_ptr(persistent_ref);
  114 + ret = key_link(key_ref_to_ptr(dest_ref), persistent);
  115 + if (ret == 0) {
  116 + key_set_timeout(persistent, persistent_keyring_expiry);
  117 + ret = persistent->serial;
  118 + }
  119 + }
  120 +
  121 + key_ref_put(persistent_ref);
  122 + return ret;
  123 +}
  124 +
  125 +/*
  126 + * Get the persistent keyring for a specific UID and link it to the nominated
  127 + * keyring.
  128 + */
  129 +long keyctl_get_persistent(uid_t _uid, key_serial_t destid)
  130 +{
  131 + struct user_namespace *ns = current_user_ns();
  132 + key_ref_t dest_ref;
  133 + kuid_t uid;
  134 + long ret;
  135 +
  136 + /* -1 indicates the current user */
  137 + if (_uid == (uid_t)-1) {
  138 + uid = current_uid();
  139 + } else {
  140 + uid = make_kuid(ns, _uid);
  141 + if (!uid_valid(uid))
  142 + return -EINVAL;
  143 +
  144 + /* You can only see your own persistent cache if you're not
  145 + * sufficiently privileged.
  146 + */
  147 + if (uid_eq(uid, current_uid()) &&
  148 + uid_eq(uid, current_suid()) &&
  149 + uid_eq(uid, current_euid()) &&
  150 + uid_eq(uid, current_fsuid()) &&
  151 + !ns_capable(ns, CAP_SETUID))
  152 + return -EPERM;
  153 + }
  154 +
  155 + /* There must be a destination keyring */
  156 + dest_ref = lookup_user_key(destid, KEY_LOOKUP_CREATE, KEY_WRITE);
  157 + if (IS_ERR(dest_ref))
  158 + return PTR_ERR(dest_ref);
  159 + if (key_ref_to_ptr(dest_ref)->type != &key_type_keyring) {
  160 + ret = -ENOTDIR;
  161 + goto out_put_dest;
  162 + }
  163 +
  164 + ret = key_get_persistent(ns, uid, dest_ref);
  165 +
  166 +out_put_dest:
  167 + key_ref_put(dest_ref);
  168 + return ret;
  169 +}
security/keys/sysctl.c
... ... @@ -61,6 +61,17 @@
61 61 .extra1 = (void *) &zero,
62 62 .extra2 = (void *) &max,
63 63 },
  64 +#ifdef CONFIG_PERSISTENT_KEYRINGS
  65 + {
  66 + .procname = "persistent_keyring_expiry",
  67 + .data = &persistent_keyring_expiry,
  68 + .maxlen = sizeof(unsigned),
  69 + .mode = 0644,
  70 + .proc_handler = proc_dointvec_minmax,
  71 + .extra1 = (void *) &zero,
  72 + .extra2 = (void *) &max,
  73 + },
  74 +#endif
64 75 { }
65 76 };