diff options
author | David Howells <dhowells@redhat.com> | 2019-06-26 16:02:32 -0400 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2019-06-26 16:02:32 -0400 |
commit | b206f281d0ee14969878469816a69db22d5838e8 (patch) | |
tree | 56828bdaec25c05d6b4126196276bf969d056929 | |
parent | dcf49dbc8077e278ddd1bc7298abc781496e8a08 (diff) |
keys: Namespace keyring names
Keyring names are held in a single global list that any process can pick
from by means of keyctl_join_session_keyring (provided the keyring grants
Search permission). This isn't very container friendly, however.
Make the following changes:
(1) Make default session, process and thread keyring names begin with a
'.' instead of '_'.
(2) Keyrings whose names begin with a '.' aren't added to the list. Such
keyrings are system specials.
(3) Replace the global list with per-user_namespace lists. A keyring adds
its name to the list for the user_namespace that it is currently in.
(4) When a user_namespace is deleted, it just removes itself from the
keyring name list.
The global keyring_name_lock is retained for accessing the name lists.
This allows (4) to work.
This can be tested by:
# keyctl newring foo @s
995906392
# unshare -U
$ keyctl show
...
995906392 --alswrv 65534 65534 \_ keyring: foo
...
$ keyctl session foo
Joined session keyring: 935622349
As can be seen, a new session keyring was created.
The capability bit KEYCTL_CAPS1_NS_KEYRING_NAME is set if the kernel is
employing this feature.
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Eric W. Biederman <ebiederm@xmission.com>
-rw-r--r-- | include/linux/key.h | 2 | ||||
-rw-r--r-- | include/linux/user_namespace.h | 5 | ||||
-rw-r--r-- | include/uapi/linux/keyctl.h | 1 | ||||
-rw-r--r-- | kernel/user.c | 3 | ||||
-rw-r--r-- | kernel/user_namespace.c | 7 | ||||
-rw-r--r-- | security/keys/keyctl.c | 3 | ||||
-rw-r--r-- | security/keys/keyring.c | 99 |
7 files changed, 60 insertions, 60 deletions
diff --git a/include/linux/key.h b/include/linux/key.h index ff102731b3db..ae1177302d70 100644 --- a/include/linux/key.h +++ b/include/linux/key.h | |||
@@ -361,6 +361,7 @@ extern void key_set_timeout(struct key *, unsigned); | |||
361 | 361 | ||
362 | extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, | 362 | extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, |
363 | key_perm_t perm); | 363 | key_perm_t perm); |
364 | extern void key_free_user_ns(struct user_namespace *); | ||
364 | 365 | ||
365 | /* | 366 | /* |
366 | * The permissions required on a key that we're looking up. | 367 | * The permissions required on a key that we're looking up. |
@@ -434,6 +435,7 @@ extern void key_init(void); | |||
434 | #define key_fsuid_changed(c) do { } while(0) | 435 | #define key_fsuid_changed(c) do { } while(0) |
435 | #define key_fsgid_changed(c) do { } while(0) | 436 | #define key_fsgid_changed(c) do { } while(0) |
436 | #define key_init() do { } while(0) | 437 | #define key_init() do { } while(0) |
438 | #define key_free_user_ns(ns) do { } while(0) | ||
437 | 439 | ||
438 | #endif /* CONFIG_KEYS */ | 440 | #endif /* CONFIG_KEYS */ |
439 | #endif /* __KERNEL__ */ | 441 | #endif /* __KERNEL__ */ |
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index d6b74b91096b..90457015fa3f 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h | |||
@@ -64,6 +64,11 @@ struct user_namespace { | |||
64 | struct ns_common ns; | 64 | struct ns_common ns; |
65 | unsigned long flags; | 65 | unsigned long flags; |
66 | 66 | ||
67 | #ifdef CONFIG_KEYS | ||
68 | /* List of joinable keyrings in this namespace */ | ||
69 | struct list_head keyring_name_list; | ||
70 | #endif | ||
71 | |||
67 | /* Register of per-UID persistent keyrings for this namespace */ | 72 | /* Register of per-UID persistent keyrings for this namespace */ |
68 | #ifdef CONFIG_PERSISTENT_KEYRINGS | 73 | #ifdef CONFIG_PERSISTENT_KEYRINGS |
69 | struct key *persistent_keyring_register; | 74 | struct key *persistent_keyring_register; |
diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h index 551b5814f53e..35b405034674 100644 --- a/include/uapi/linux/keyctl.h +++ b/include/uapi/linux/keyctl.h | |||
@@ -128,5 +128,6 @@ struct keyctl_pkey_params { | |||
128 | #define KEYCTL_CAPS0_INVALIDATE 0x20 /* KEYCTL_INVALIDATE supported */ | 128 | #define KEYCTL_CAPS0_INVALIDATE 0x20 /* KEYCTL_INVALIDATE supported */ |
129 | #define KEYCTL_CAPS0_RESTRICT_KEYRING 0x40 /* KEYCTL_RESTRICT_KEYRING supported */ | 129 | #define KEYCTL_CAPS0_RESTRICT_KEYRING 0x40 /* KEYCTL_RESTRICT_KEYRING supported */ |
130 | #define KEYCTL_CAPS0_MOVE 0x80 /* KEYCTL_MOVE supported */ | 130 | #define KEYCTL_CAPS0_MOVE 0x80 /* KEYCTL_MOVE supported */ |
131 | #define KEYCTL_CAPS1_NS_KEYRING_NAME 0x01 /* Keyring names are per-user_namespace */ | ||
131 | 132 | ||
132 | #endif /* _LINUX_KEYCTL_H */ | 133 | #endif /* _LINUX_KEYCTL_H */ |
diff --git a/kernel/user.c b/kernel/user.c index 88b834f0eebc..50979fd1b7aa 100644 --- a/kernel/user.c +++ b/kernel/user.c | |||
@@ -62,6 +62,9 @@ struct user_namespace init_user_ns = { | |||
62 | .ns.ops = &userns_operations, | 62 | .ns.ops = &userns_operations, |
63 | #endif | 63 | #endif |
64 | .flags = USERNS_INIT_FLAGS, | 64 | .flags = USERNS_INIT_FLAGS, |
65 | #ifdef CONFIG_KEYS | ||
66 | .keyring_name_list = LIST_HEAD_INIT(init_user_ns.keyring_name_list), | ||
67 | #endif | ||
65 | #ifdef CONFIG_PERSISTENT_KEYRINGS | 68 | #ifdef CONFIG_PERSISTENT_KEYRINGS |
66 | .persistent_keyring_register_sem = | 69 | .persistent_keyring_register_sem = |
67 | __RWSEM_INITIALIZER(init_user_ns.persistent_keyring_register_sem), | 70 | __RWSEM_INITIALIZER(init_user_ns.persistent_keyring_register_sem), |
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 923414a246e9..bda6e890ad88 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c | |||
@@ -133,6 +133,9 @@ int create_user_ns(struct cred *new) | |||
133 | ns->flags = parent_ns->flags; | 133 | ns->flags = parent_ns->flags; |
134 | mutex_unlock(&userns_state_mutex); | 134 | mutex_unlock(&userns_state_mutex); |
135 | 135 | ||
136 | #ifdef CONFIG_KEYS | ||
137 | INIT_LIST_HEAD(&ns->keyring_name_list); | ||
138 | #endif | ||
136 | #ifdef CONFIG_PERSISTENT_KEYRINGS | 139 | #ifdef CONFIG_PERSISTENT_KEYRINGS |
137 | init_rwsem(&ns->persistent_keyring_register_sem); | 140 | init_rwsem(&ns->persistent_keyring_register_sem); |
138 | #endif | 141 | #endif |
@@ -196,9 +199,7 @@ static void free_user_ns(struct work_struct *work) | |||
196 | kfree(ns->projid_map.reverse); | 199 | kfree(ns->projid_map.reverse); |
197 | } | 200 | } |
198 | retire_userns_sysctls(ns); | 201 | retire_userns_sysctls(ns); |
199 | #ifdef CONFIG_PERSISTENT_KEYRINGS | 202 | key_free_user_ns(ns); |
200 | key_put(ns->persistent_keyring_register); | ||
201 | #endif | ||
202 | ns_free_inum(&ns->ns); | 203 | ns_free_inum(&ns->ns); |
203 | kmem_cache_free(user_ns_cachep, ns); | 204 | kmem_cache_free(user_ns_cachep, ns); |
204 | dec_user_namespaces(ucounts); | 205 | dec_user_namespaces(ucounts); |
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 169409b611b0..8a813220f269 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c | |||
@@ -30,7 +30,7 @@ | |||
30 | 30 | ||
31 | #define KEY_MAX_DESC_SIZE 4096 | 31 | #define KEY_MAX_DESC_SIZE 4096 |
32 | 32 | ||
33 | static const unsigned char keyrings_capabilities[1] = { | 33 | static const unsigned char keyrings_capabilities[2] = { |
34 | [0] = (KEYCTL_CAPS0_CAPABILITIES | | 34 | [0] = (KEYCTL_CAPS0_CAPABILITIES | |
35 | (IS_ENABLED(CONFIG_PERSISTENT_KEYRINGS) ? KEYCTL_CAPS0_PERSISTENT_KEYRINGS : 0) | | 35 | (IS_ENABLED(CONFIG_PERSISTENT_KEYRINGS) ? KEYCTL_CAPS0_PERSISTENT_KEYRINGS : 0) | |
36 | (IS_ENABLED(CONFIG_KEY_DH_OPERATIONS) ? KEYCTL_CAPS0_DIFFIE_HELLMAN : 0) | | 36 | (IS_ENABLED(CONFIG_KEY_DH_OPERATIONS) ? KEYCTL_CAPS0_DIFFIE_HELLMAN : 0) | |
@@ -40,6 +40,7 @@ static const unsigned char keyrings_capabilities[1] = { | |||
40 | KEYCTL_CAPS0_RESTRICT_KEYRING | | 40 | KEYCTL_CAPS0_RESTRICT_KEYRING | |
41 | KEYCTL_CAPS0_MOVE | 41 | KEYCTL_CAPS0_MOVE |
42 | ), | 42 | ), |
43 | [1] = (KEYCTL_CAPS1_NS_KEYRING_NAME), | ||
43 | }; | 44 | }; |
44 | 45 | ||
45 | static int key_get_type_from_user(char *type, | 46 | static int key_get_type_from_user(char *type, |
diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 20891cd198f0..fe851292509e 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/security.h> | 16 | #include <linux/security.h> |
17 | #include <linux/seq_file.h> | 17 | #include <linux/seq_file.h> |
18 | #include <linux/err.h> | 18 | #include <linux/err.h> |
19 | #include <linux/user_namespace.h> | ||
19 | #include <keys/keyring-type.h> | 20 | #include <keys/keyring-type.h> |
20 | #include <keys/user-type.h> | 21 | #include <keys/user-type.h> |
21 | #include <linux/assoc_array_priv.h> | 22 | #include <linux/assoc_array_priv.h> |
@@ -29,11 +30,6 @@ | |||
29 | #define KEYRING_SEARCH_MAX_DEPTH 6 | 30 | #define KEYRING_SEARCH_MAX_DEPTH 6 |
30 | 31 | ||
31 | /* | 32 | /* |
32 | * We keep all named keyrings in a hash to speed looking them up. | ||
33 | */ | ||
34 | #define KEYRING_NAME_HASH_SIZE (1 << 5) | ||
35 | |||
36 | /* | ||
37 | * We mark pointers we pass to the associative array with bit 1 set if | 33 | * We mark pointers we pass to the associative array with bit 1 set if |
38 | * they're keyrings and clear otherwise. | 34 | * they're keyrings and clear otherwise. |
39 | */ | 35 | */ |
@@ -55,17 +51,20 @@ static inline void *keyring_key_to_ptr(struct key *key) | |||
55 | return key; | 51 | return key; |
56 | } | 52 | } |
57 | 53 | ||
58 | static struct list_head keyring_name_hash[KEYRING_NAME_HASH_SIZE]; | ||
59 | static DEFINE_RWLOCK(keyring_name_lock); | 54 | static DEFINE_RWLOCK(keyring_name_lock); |
60 | 55 | ||
61 | static inline unsigned keyring_hash(const char *desc) | 56 | /* |
57 | * Clean up the bits of user_namespace that belong to us. | ||
58 | */ | ||
59 | void key_free_user_ns(struct user_namespace *ns) | ||
62 | { | 60 | { |
63 | unsigned bucket = 0; | 61 | write_lock(&keyring_name_lock); |
64 | 62 | list_del_init(&ns->keyring_name_list); | |
65 | for (; *desc; desc++) | 63 | write_unlock(&keyring_name_lock); |
66 | bucket += (unsigned char)*desc; | ||
67 | 64 | ||
68 | return bucket & (KEYRING_NAME_HASH_SIZE - 1); | 65 | #ifdef CONFIG_PERSISTENT_KEYRINGS |
66 | key_put(ns->persistent_keyring_register); | ||
67 | #endif | ||
69 | } | 68 | } |
70 | 69 | ||
71 | /* | 70 | /* |
@@ -104,23 +103,17 @@ static DEFINE_MUTEX(keyring_serialise_link_lock); | |||
104 | 103 | ||
105 | /* | 104 | /* |
106 | * Publish the name of a keyring so that it can be found by name (if it has | 105 | * Publish the name of a keyring so that it can be found by name (if it has |
107 | * one). | 106 | * one and it doesn't begin with a dot). |
108 | */ | 107 | */ |
109 | static void keyring_publish_name(struct key *keyring) | 108 | static void keyring_publish_name(struct key *keyring) |
110 | { | 109 | { |
111 | int bucket; | 110 | struct user_namespace *ns = current_user_ns(); |
112 | |||
113 | if (keyring->description) { | ||
114 | bucket = keyring_hash(keyring->description); | ||
115 | 111 | ||
112 | if (keyring->description && | ||
113 | keyring->description[0] && | ||
114 | keyring->description[0] != '.') { | ||
116 | write_lock(&keyring_name_lock); | 115 | write_lock(&keyring_name_lock); |
117 | 116 | list_add_tail(&keyring->name_link, &ns->keyring_name_list); | |
118 | if (!keyring_name_hash[bucket].next) | ||
119 | INIT_LIST_HEAD(&keyring_name_hash[bucket]); | ||
120 | |||
121 | list_add_tail(&keyring->name_link, | ||
122 | &keyring_name_hash[bucket]); | ||
123 | |||
124 | write_unlock(&keyring_name_lock); | 117 | write_unlock(&keyring_name_lock); |
125 | } | 118 | } |
126 | } | 119 | } |
@@ -1097,50 +1090,44 @@ found: | |||
1097 | */ | 1090 | */ |
1098 | struct key *find_keyring_by_name(const char *name, bool uid_keyring) | 1091 | struct key *find_keyring_by_name(const char *name, bool uid_keyring) |
1099 | { | 1092 | { |
1093 | struct user_namespace *ns = current_user_ns(); | ||
1100 | struct key *keyring; | 1094 | struct key *keyring; |
1101 | int bucket; | ||
1102 | 1095 | ||
1103 | if (!name) | 1096 | if (!name) |
1104 | return ERR_PTR(-EINVAL); | 1097 | return ERR_PTR(-EINVAL); |
1105 | 1098 | ||
1106 | bucket = keyring_hash(name); | ||
1107 | |||
1108 | read_lock(&keyring_name_lock); | 1099 | read_lock(&keyring_name_lock); |
1109 | 1100 | ||
1110 | if (keyring_name_hash[bucket].next) { | 1101 | /* Search this hash bucket for a keyring with a matching name that |
1111 | /* search this hash bucket for a keyring with a matching name | 1102 | * grants Search permission and that hasn't been revoked |
1112 | * that's readable and that hasn't been revoked */ | 1103 | */ |
1113 | list_for_each_entry(keyring, | 1104 | list_for_each_entry(keyring, &ns->keyring_name_list, name_link) { |
1114 | &keyring_name_hash[bucket], | 1105 | if (!kuid_has_mapping(ns, keyring->user->uid)) |
1115 | name_link | 1106 | continue; |
1116 | ) { | ||
1117 | if (!kuid_has_mapping(current_user_ns(), keyring->user->uid)) | ||
1118 | continue; | ||
1119 | |||
1120 | if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) | ||
1121 | continue; | ||
1122 | 1107 | ||
1123 | if (strcmp(keyring->description, name) != 0) | 1108 | if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) |
1124 | continue; | 1109 | continue; |
1125 | 1110 | ||
1126 | if (uid_keyring) { | 1111 | if (strcmp(keyring->description, name) != 0) |
1127 | if (!test_bit(KEY_FLAG_UID_KEYRING, | 1112 | continue; |
1128 | &keyring->flags)) | ||
1129 | continue; | ||
1130 | } else { | ||
1131 | if (key_permission(make_key_ref(keyring, 0), | ||
1132 | KEY_NEED_SEARCH) < 0) | ||
1133 | continue; | ||
1134 | } | ||
1135 | 1113 | ||
1136 | /* we've got a match but we might end up racing with | 1114 | if (uid_keyring) { |
1137 | * key_cleanup() if the keyring is currently 'dead' | 1115 | if (!test_bit(KEY_FLAG_UID_KEYRING, |
1138 | * (ie. it has a zero usage count) */ | 1116 | &keyring->flags)) |
1139 | if (!refcount_inc_not_zero(&keyring->usage)) | 1117 | continue; |
1118 | } else { | ||
1119 | if (key_permission(make_key_ref(keyring, 0), | ||
1120 | KEY_NEED_SEARCH) < 0) | ||
1140 | continue; | 1121 | continue; |
1141 | keyring->last_used_at = ktime_get_real_seconds(); | ||
1142 | goto out; | ||
1143 | } | 1122 | } |
1123 | |||
1124 | /* we've got a match but we might end up racing with | ||
1125 | * key_cleanup() if the keyring is currently 'dead' | ||
1126 | * (ie. it has a zero usage count) */ | ||
1127 | if (!refcount_inc_not_zero(&keyring->usage)) | ||
1128 | continue; | ||
1129 | keyring->last_used_at = ktime_get_real_seconds(); | ||
1130 | goto out; | ||
1144 | } | 1131 | } |
1145 | 1132 | ||
1146 | keyring = ERR_PTR(-ENOKEY); | 1133 | keyring = ERR_PTR(-ENOKEY); |