Skip to content

Commit 3b6e4de

Browse files
committed
keys: Include target namespace in match criteria
Currently a key has a standard matching criteria of { type, description } and this is used to only allow keys with unique criteria in a keyring. This means, however, that you cannot have keys with the same type and description but a different target namespace in the same keyring. This is a potential problem for a containerised environment where, say, a container is made up of some parts of its mount space involving netfs superblocks from two different network namespaces. This is also a problem for shared system management keyrings such as the DNS records keyring or the NFS idmapper keyring that might contain keys from different network namespaces. Fix this by including a namespace component in a key's matching criteria. Keyring types are marked to indicate which, if any, namespace is relevant to keys of that type, and that namespace is set when the key is created from the current task's namespace set. The capability bit KEYCTL_CAPS1_NS_KEY_TAG is set if the kernel is employing this feature. Signed-off-by: David Howells <[email protected]>
1 parent 0f44e4d commit 3b6e4de

File tree

7 files changed

+50
-4
lines changed

7 files changed

+50
-4
lines changed

include/linux/key.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,16 @@ struct cred;
8282

8383
struct key_type;
8484
struct key_owner;
85+
struct key_tag;
8586
struct keyring_list;
8687
struct keyring_name;
8788

89+
struct key_tag {
90+
struct rcu_head rcu;
91+
refcount_t usage;
92+
bool removed; /* T when subject removed */
93+
};
94+
8895
struct keyring_index_key {
8996
/* [!] If this structure is altered, the union in struct key must change too! */
9097
unsigned long hash; /* Hash value */
@@ -101,6 +108,7 @@ struct keyring_index_key {
101108
unsigned long x;
102109
};
103110
struct key_type *type;
111+
struct key_tag *domain_tag; /* Domain of operation */
104112
const char *description;
105113
};
106114

@@ -218,6 +226,7 @@ struct key {
218226
unsigned long hash;
219227
unsigned long len_desc;
220228
struct key_type *type; /* type of key */
229+
struct key_tag *domain_tag; /* Domain of operation */
221230
char *description;
222231
};
223232
};
@@ -268,6 +277,7 @@ extern struct key *key_alloc(struct key_type *type,
268277
extern void key_revoke(struct key *key);
269278
extern void key_invalidate(struct key *key);
270279
extern void key_put(struct key *key);
280+
extern bool key_put_tag(struct key_tag *tag);
271281

272282
static inline struct key *__key_get(struct key *key)
273283
{

include/uapi/linux/keyctl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,5 +129,6 @@ struct keyctl_pkey_params {
129129
#define KEYCTL_CAPS0_RESTRICT_KEYRING 0x40 /* KEYCTL_RESTRICT_KEYRING supported */
130130
#define KEYCTL_CAPS0_MOVE 0x80 /* KEYCTL_MOVE supported */
131131
#define KEYCTL_CAPS1_NS_KEYRING_NAME 0x01 /* Keyring names are per-user_namespace */
132+
#define KEYCTL_CAPS1_NS_KEY_TAG 0x02 /* Key indexing can include a namespace tag */
132133

133134
#endif /* _LINUX_KEYCTL_H */

security/keys/gc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ static noinline void key_gc_unused_keys(struct list_head *keys)
154154
atomic_dec(&key->user->nikeys);
155155

156156
key_user_put(key->user);
157-
157+
key_put_tag(key->domain_tag);
158158
kfree(key->description);
159159

160160
memzero_explicit(key, sizeof(*key));

security/keys/key.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
317317
goto security_error;
318318

319319
/* publish the key by giving it a serial number */
320+
refcount_inc(&key->domain_tag->usage);
320321
atomic_inc(&user->nkeys);
321322
key_alloc_serial(key);
322323

security/keys/keyctl.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ static const unsigned char keyrings_capabilities[2] = {
4040
KEYCTL_CAPS0_RESTRICT_KEYRING |
4141
KEYCTL_CAPS0_MOVE
4242
),
43-
[1] = (KEYCTL_CAPS1_NS_KEYRING_NAME),
43+
[1] = (KEYCTL_CAPS1_NS_KEYRING_NAME |
44+
KEYCTL_CAPS1_NS_KEY_TAG),
4445
};
4546

4647
static int key_get_type_from_user(char *type,

security/keys/keyring.c

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ static void hash_key_type_and_desc(struct keyring_index_key *index_key)
175175
type = (unsigned long)index_key->type;
176176
acc = mult_64x32_and_fold(type, desc_len + 13);
177177
acc = mult_64x32_and_fold(acc, 9207);
178+
piece = (unsigned long)index_key->domain_tag;
179+
acc = mult_64x32_and_fold(acc, piece);
180+
acc = mult_64x32_and_fold(acc, 9207);
178181

179182
for (;;) {
180183
n = desc_len;
@@ -208,16 +211,36 @@ static void hash_key_type_and_desc(struct keyring_index_key *index_key)
208211

209212
/*
210213
* Finalise an index key to include a part of the description actually in the
211-
* index key and to add in the hash too.
214+
* index key, to set the domain tag and to calculate the hash.
212215
*/
213216
void key_set_index_key(struct keyring_index_key *index_key)
214217
{
218+
static struct key_tag default_domain_tag = { .usage = REFCOUNT_INIT(1), };
215219
size_t n = min_t(size_t, index_key->desc_len, sizeof(index_key->desc));
220+
216221
memcpy(index_key->desc, index_key->description, n);
217222

223+
index_key->domain_tag = &default_domain_tag;
218224
hash_key_type_and_desc(index_key);
219225
}
220226

227+
/**
228+
* key_put_tag - Release a ref on a tag.
229+
* @tag: The tag to release.
230+
*
231+
* This releases a reference the given tag and returns true if that ref was the
232+
* last one.
233+
*/
234+
bool key_put_tag(struct key_tag *tag)
235+
{
236+
if (refcount_dec_and_test(&tag->usage)) {
237+
kfree_rcu(tag, rcu);
238+
return true;
239+
}
240+
241+
return false;
242+
}
243+
221244
/*
222245
* Build the next index key chunk.
223246
*
@@ -238,8 +261,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
238261
return index_key->x;
239262
case 2:
240263
return (unsigned long)index_key->type;
264+
case 3:
265+
return (unsigned long)index_key->domain_tag;
241266
default:
242-
level -= 3;
267+
level -= 4;
243268
if (desc_len <= sizeof(index_key->desc))
244269
return 0;
245270

@@ -268,6 +293,7 @@ static bool keyring_compare_object(const void *object, const void *data)
268293
const struct key *key = keyring_ptr_to_key(object);
269294

270295
return key->index_key.type == index_key->type &&
296+
key->index_key.domain_tag == index_key->domain_tag &&
271297
key->index_key.desc_len == index_key->desc_len &&
272298
memcmp(key->index_key.description, index_key->description,
273299
index_key->desc_len) == 0;
@@ -309,6 +335,12 @@ static int keyring_diff_objects(const void *object, const void *data)
309335
goto differ;
310336
level += sizeof(unsigned long);
311337

338+
seg_a = (unsigned long)a->domain_tag;
339+
seg_b = (unsigned long)b->domain_tag;
340+
if ((seg_a ^ seg_b) != 0)
341+
goto differ;
342+
level += sizeof(unsigned long);
343+
312344
i = sizeof(a->desc);
313345
if (a->desc_len <= i)
314346
goto same;

security/keys/persistent.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ static long key_get_persistent(struct user_namespace *ns, kuid_t uid,
8484
long ret;
8585

8686
/* Look in the register if it exists */
87+
memset(&index_key, 0, sizeof(index_key));
8788
index_key.type = &key_type_keyring;
8889
index_key.description = buf;
8990
index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid));

0 commit comments

Comments
 (0)