From f771fde82051976a6fc0fd570f8b86de4a92124b Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 26 Jun 2019 21:02:31 +0100 Subject: keys: Simplify key description management Simplify key description management by cramming the word containing the length with the first few chars of the description also. This simplifies the code that generates the index-key used by assoc_array. It should speed up key searching a bit too. Signed-off-by: David Howells --- include/linux/key.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'include/linux/key.h') diff --git a/include/linux/key.h b/include/linux/key.h index 4cd5669184f3..86ccc2d010f6 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -86,9 +86,20 @@ struct keyring_list; struct keyring_name; struct keyring_index_key { + union { + struct { +#ifdef __LITTLE_ENDIAN /* Put desc_len at the LSB of x */ + u8 desc_len; + char desc[sizeof(long) - 1]; /* First few chars of description */ +#else + char desc[sizeof(long) - 1]; /* First few chars of description */ + u8 desc_len; +#endif + }; + unsigned long x; + }; struct key_type *type; const char *description; - size_t desc_len; }; union key_payload { @@ -202,6 +213,7 @@ struct key { union { struct keyring_index_key index_key; struct { + unsigned long len_desc; struct key_type *type; /* type of key */ char *description; }; -- cgit v1.2.3 From 355ef8e15885020da88f5ba2d85ce42b1d01f537 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 26 Jun 2019 21:02:32 +0100 Subject: keys: Cache the hash value to avoid lots of recalculation Cache the hash of the key's type and description in the index key so that we're not recalculating it every time we look at a key during a search. The hash function does a bunch of multiplications, so evading those is probably worthwhile - especially as this is done for every key examined during a search. This also allows the methods used by assoc_array to get chunks of index-key to be simplified. Signed-off-by: David Howells --- include/linux/key.h | 3 +++ security/keys/internal.h | 8 +------- security/keys/key.c | 2 +- security/keys/keyring.c | 28 ++++++++++++++++++++-------- 4 files changed, 25 insertions(+), 16 deletions(-) (limited to 'include/linux/key.h') diff --git a/include/linux/key.h b/include/linux/key.h index 86ccc2d010f6..fb2debcacea0 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -86,6 +86,8 @@ struct keyring_list; struct keyring_name; struct keyring_index_key { + /* [!] If this structure is altered, the union in struct key must change too! */ + unsigned long hash; /* Hash value */ union { struct { #ifdef __LITTLE_ENDIAN /* Put desc_len at the LSB of x */ @@ -213,6 +215,7 @@ struct key { union { struct keyring_index_key index_key; struct { + unsigned long hash; unsigned long len_desc; struct key_type *type; /* type of key */ char *description; diff --git a/security/keys/internal.h b/security/keys/internal.h index ee71c72fc5f0..4305414795ae 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -89,13 +89,7 @@ extern spinlock_t key_serial_lock; extern struct mutex key_construction_mutex; extern wait_queue_head_t request_key_conswq; - -static inline void key_set_index_key(struct keyring_index_key *index_key) -{ - size_t n = min_t(size_t, index_key->desc_len, sizeof(index_key->desc)); - memcpy(index_key->desc, index_key->description, n); -} - +extern void key_set_index_key(struct keyring_index_key *index_key); extern struct key_type *key_type_lookup(const char *type); extern void key_type_put(struct key_type *ktype); diff --git a/security/keys/key.c b/security/keys/key.c index 0a3828f15f57..9d52f2472a09 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -285,12 +285,12 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL); if (!key->index_key.description) goto no_memory_3; + key->index_key.type = type; key_set_index_key(&key->index_key); refcount_set(&key->usage, 1); init_rwsem(&key->sem); lockdep_set_class(&key->sem, &type->lock_class); - key->index_key.type = type; key->user = user; key->quotalen = quotalen; key->datalen = type->def_datalen; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index ebf52077598f..a5ee3b4d2eb8 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -168,7 +168,7 @@ static u64 mult_64x32_and_fold(u64 x, u32 y) /* * Hash a key type and description. */ -static unsigned long hash_key_type_and_desc(const struct keyring_index_key *index_key) +static void hash_key_type_and_desc(struct keyring_index_key *index_key) { const unsigned level_shift = ASSOC_ARRAY_LEVEL_STEP; const unsigned long fan_mask = ASSOC_ARRAY_FAN_MASK; @@ -206,10 +206,22 @@ static unsigned long hash_key_type_and_desc(const struct keyring_index_key *inde * zero for keyrings and non-zero otherwise. */ if (index_key->type != &key_type_keyring && (hash & fan_mask) == 0) - return hash | (hash >> (ASSOC_ARRAY_KEY_CHUNK_SIZE - level_shift)) | 1; - if (index_key->type == &key_type_keyring && (hash & fan_mask) != 0) - return (hash + (hash << level_shift)) & ~fan_mask; - return hash; + hash |= (hash >> (ASSOC_ARRAY_KEY_CHUNK_SIZE - level_shift)) | 1; + else if (index_key->type == &key_type_keyring && (hash & fan_mask) != 0) + hash = (hash + (hash << level_shift)) & ~fan_mask; + index_key->hash = hash; +} + +/* + * Finalise an index key to include a part of the description actually in the + * index key and to add in the hash too. + */ +void key_set_index_key(struct keyring_index_key *index_key) +{ + size_t n = min_t(size_t, index_key->desc_len, sizeof(index_key->desc)); + memcpy(index_key->desc, index_key->description, n); + + hash_key_type_and_desc(index_key); } /* @@ -227,7 +239,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) level /= ASSOC_ARRAY_KEY_CHUNK_SIZE; switch (level) { case 0: - return hash_key_type_and_desc(index_key); + return index_key->hash; case 1: return index_key->x; case 2: @@ -280,8 +292,8 @@ static int keyring_diff_objects(const void *object, const void *data) int level, i; level = 0; - seg_a = hash_key_type_and_desc(a); - seg_b = hash_key_type_and_desc(b); + seg_a = a->hash; + seg_b = b->hash; if ((seg_a ^ seg_b) != 0) goto differ; level += ASSOC_ARRAY_KEY_CHUNK_SIZE / 8; -- cgit v1.2.3 From dcf49dbc8077e278ddd1bc7298abc781496e8a08 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 26 Jun 2019 21:02:32 +0100 Subject: keys: Add a 'recurse' flag for keyring searches Add a 'recurse' flag for keyring searches so that the flag can be omitted and recursion disabled, thereby allowing just the nominated keyring to be searched and none of the children. Signed-off-by: David Howells --- Documentation/security/keys/core.rst | 10 ++++++---- certs/blacklist.c | 2 +- crypto/asymmetric_keys/asymmetric_type.c | 2 +- include/linux/key.h | 3 ++- lib/digsig.c | 2 +- net/rxrpc/security.c | 2 +- security/integrity/digsig_asymmetric.c | 4 ++-- security/keys/internal.h | 1 + security/keys/keyctl.c | 2 +- security/keys/keyring.c | 12 ++++++++++-- security/keys/proc.c | 3 ++- security/keys/process_keys.c | 3 ++- security/keys/request_key.c | 3 ++- security/keys/request_key_auth.c | 3 ++- 14 files changed, 34 insertions(+), 18 deletions(-) (limited to 'include/linux/key.h') diff --git a/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst index a0e245f9576f..ae930ae9d590 100644 --- a/Documentation/security/keys/core.rst +++ b/Documentation/security/keys/core.rst @@ -1162,11 +1162,13 @@ payload contents" for more information. key_ref_t keyring_search(key_ref_t keyring_ref, const struct key_type *type, - const char *description) + const char *description, + bool recurse) - This searches the keyring tree specified for a matching key. Error ENOKEY - is returned upon failure (use IS_ERR/PTR_ERR to determine). If successful, - the returned key will need to be released. + This searches the specified keyring only (recurse == false) or keyring tree + (recurse == true) specified for a matching key. Error ENOKEY is returned + upon failure (use IS_ERR/PTR_ERR to determine). If successful, the returned + key will need to be released. The possession attribute from the keyring reference is used to control access through the permissions mask and is propagated to the returned key diff --git a/certs/blacklist.c b/certs/blacklist.c index 3a507b9e2568..181cb7fa9540 100644 --- a/certs/blacklist.c +++ b/certs/blacklist.c @@ -128,7 +128,7 @@ int is_hash_blacklisted(const u8 *hash, size_t hash_len, const char *type) *p = 0; kref = keyring_search(make_key_ref(blacklist_keyring, true), - &key_type_blacklist, buffer); + &key_type_blacklist, buffer, false); if (!IS_ERR(kref)) { key_ref_put(kref); ret = -EKEYREJECTED; diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 69a0788a7de5..084027ef3121 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -87,7 +87,7 @@ struct key *find_asymmetric_key(struct key *keyring, pr_debug("Look up: \"%s\"\n", req); ref = keyring_search(make_key_ref(keyring, 1), - &key_type_asymmetric, req); + &key_type_asymmetric, req, true); if (IS_ERR(ref)) pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref)); kfree(req); diff --git a/include/linux/key.h b/include/linux/key.h index fb2debcacea0..ff102731b3db 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -341,7 +341,8 @@ extern int keyring_clear(struct key *keyring); extern key_ref_t keyring_search(key_ref_t keyring, struct key_type *type, - const char *description); + const char *description, + bool recurse); extern int keyring_add_key(struct key *keyring, struct key *key); diff --git a/lib/digsig.c b/lib/digsig.c index 3b0a579bdcdf..3782af401c68 100644 --- a/lib/digsig.c +++ b/lib/digsig.c @@ -221,7 +221,7 @@ int digsig_verify(struct key *keyring, const char *sig, int siglen, /* search in specific keyring */ key_ref_t kref; kref = keyring_search(make_key_ref(keyring, 1UL), - &key_type_user, name); + &key_type_user, name, true); if (IS_ERR(kref)) key = ERR_CAST(kref); else diff --git a/net/rxrpc/security.c b/net/rxrpc/security.c index c4479afe8ae7..2cfc7125bc41 100644 --- a/net/rxrpc/security.c +++ b/net/rxrpc/security.c @@ -148,7 +148,7 @@ found_service: /* look through the service's keyring */ kref = keyring_search(make_key_ref(rx->securities, 1UL), - &key_type_rxrpc_s, kdesc); + &key_type_rxrpc_s, kdesc, true); if (IS_ERR(kref)) { read_unlock(&local->services_lock); _leave(" = %ld [search]", PTR_ERR(kref)); diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 99080871eb9f..358f614811e8 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -39,7 +39,7 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) key_ref_t kref; kref = keyring_search(make_key_ref(key, 1), - &key_type_asymmetric, name); + &key_type_asymmetric, name, true); if (!IS_ERR(kref)) { pr_err("Key '%s' is in ima_blacklist_keyring\n", name); return ERR_PTR(-EKEYREJECTED); @@ -51,7 +51,7 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) key_ref_t kref; kref = keyring_search(make_key_ref(keyring, 1), - &key_type_asymmetric, name); + &key_type_asymmetric, name, true); if (IS_ERR(kref)) key = ERR_CAST(kref); else diff --git a/security/keys/internal.h b/security/keys/internal.h index 4305414795ae..aa361299a3ec 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -127,6 +127,7 @@ struct keyring_search_context { #define KEYRING_SEARCH_NO_CHECK_PERM 0x0008 /* Don't check permissions */ #define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0010 /* Give an error on excessive depth */ #define KEYRING_SEARCH_SKIP_EXPIRED 0x0020 /* Ignore expired keys (intention to replace) */ +#define KEYRING_SEARCH_RECURSE 0x0040 /* Search child keyrings also */ int (*iterator)(const void *object, void *iterator_data); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 9f418e66f067..169409b611b0 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -762,7 +762,7 @@ long keyctl_keyring_search(key_serial_t ringid, } /* do the search */ - key_ref = keyring_search(keyring_ref, ktype, description); + key_ref = keyring_search(keyring_ref, ktype, description, true); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); diff --git a/security/keys/keyring.c b/security/keys/keyring.c index a5ee3b4d2eb8..20891cd198f0 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -685,6 +685,9 @@ descend_to_keyring: * Non-keyrings avoid the leftmost branch of the root entirely (root * slots 1-15). */ + if (!(ctx->flags & KEYRING_SEARCH_RECURSE)) + goto not_this_keyring; + ptr = READ_ONCE(keyring->keys.root); if (!ptr) goto not_this_keyring; @@ -885,13 +888,15 @@ key_ref_t keyring_search_rcu(key_ref_t keyring_ref, * @keyring: The root of the keyring tree to be searched. * @type: The type of keyring we want to find. * @description: The name of the keyring we want to find. + * @recurse: True to search the children of @keyring also * * As keyring_search_rcu() above, but using the current task's credentials and * type's default matching function and preferred search method. */ key_ref_t keyring_search(key_ref_t keyring, struct key_type *type, - const char *description) + const char *description, + bool recurse) { struct keyring_search_context ctx = { .index_key.type = type, @@ -906,6 +911,8 @@ key_ref_t keyring_search(key_ref_t keyring, key_ref_t key; int ret; + if (recurse) + ctx.flags |= KEYRING_SEARCH_RECURSE; if (type->match_preparse) { ret = type->match_preparse(&ctx.match_data); if (ret < 0) @@ -1176,7 +1183,8 @@ static int keyring_detect_cycle(struct key *A, struct key *B) .flags = (KEYRING_SEARCH_NO_STATE_CHECK | KEYRING_SEARCH_NO_UPDATE_TIME | KEYRING_SEARCH_NO_CHECK_PERM | - KEYRING_SEARCH_DETECT_TOO_DEEP), + KEYRING_SEARCH_DETECT_TOO_DEEP | + KEYRING_SEARCH_RECURSE), }; rcu_read_lock(); diff --git a/security/keys/proc.c b/security/keys/proc.c index f081dceae3b9..b4f5ba56b9cb 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -170,7 +170,8 @@ static int proc_keys_show(struct seq_file *m, void *v) .match_data.cmp = lookup_user_key_possessed, .match_data.raw_data = key, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .flags = KEYRING_SEARCH_NO_STATE_CHECK, + .flags = (KEYRING_SEARCH_NO_STATE_CHECK | + KEYRING_SEARCH_RECURSE), }; key_ref = make_key_ref(key, 0); diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index f8ffb06d0297..b07f768d23dc 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -531,7 +531,8 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags, struct keyring_search_context ctx = { .match_data.cmp = lookup_user_key_possessed, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .flags = KEYRING_SEARCH_NO_STATE_CHECK, + .flags = (KEYRING_SEARCH_NO_STATE_CHECK | + KEYRING_SEARCH_RECURSE), }; struct request_key_auth *rka; struct key *key; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 36c55ef47b9e..1ffd3803ce29 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -569,7 +569,8 @@ struct key *request_key_and_link(struct key_type *type, .match_data.raw_data = description, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, .flags = (KEYRING_SEARCH_DO_STATE_CHECK | - KEYRING_SEARCH_SKIP_EXPIRED), + KEYRING_SEARCH_SKIP_EXPIRED | + KEYRING_SEARCH_RECURSE), }; struct key *key; key_ref_t key_ref; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 99ed7a8a273d..f613987e8a63 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -252,7 +252,8 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id) .match_data.cmp = key_default_cmp, .match_data.raw_data = description, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .flags = KEYRING_SEARCH_DO_STATE_CHECK, + .flags = (KEYRING_SEARCH_DO_STATE_CHECK | + KEYRING_SEARCH_RECURSE), }; struct key *authkey; key_ref_t authkey_ref; -- cgit v1.2.3 From b206f281d0ee14969878469816a69db22d5838e8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 26 Jun 2019 21:02:32 +0100 Subject: 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 cc: Eric W. Biederman --- include/linux/key.h | 2 + include/linux/user_namespace.h | 5 +++ include/uapi/linux/keyctl.h | 1 + kernel/user.c | 3 ++ kernel/user_namespace.c | 7 +-- security/keys/keyctl.c | 3 +- security/keys/keyring.c | 99 ++++++++++++++++++------------------------ 7 files changed, 60 insertions(+), 60 deletions(-) (limited to 'include/linux/key.h') 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); extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, key_perm_t perm); +extern void key_free_user_ns(struct user_namespace *); /* * The permissions required on a key that we're looking up. @@ -434,6 +435,7 @@ extern void key_init(void); #define key_fsuid_changed(c) do { } while(0) #define key_fsgid_changed(c) do { } while(0) #define key_init() do { } while(0) +#define key_free_user_ns(ns) do { } while(0) #endif /* CONFIG_KEYS */ #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 { struct ns_common ns; unsigned long flags; +#ifdef CONFIG_KEYS + /* List of joinable keyrings in this namespace */ + struct list_head keyring_name_list; +#endif + /* Register of per-UID persistent keyrings for this namespace */ #ifdef CONFIG_PERSISTENT_KEYRINGS 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 { #define KEYCTL_CAPS0_INVALIDATE 0x20 /* KEYCTL_INVALIDATE supported */ #define KEYCTL_CAPS0_RESTRICT_KEYRING 0x40 /* KEYCTL_RESTRICT_KEYRING supported */ #define KEYCTL_CAPS0_MOVE 0x80 /* KEYCTL_MOVE supported */ +#define KEYCTL_CAPS1_NS_KEYRING_NAME 0x01 /* Keyring names are per-user_namespace */ #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 = { .ns.ops = &userns_operations, #endif .flags = USERNS_INIT_FLAGS, +#ifdef CONFIG_KEYS + .keyring_name_list = LIST_HEAD_INIT(init_user_ns.keyring_name_list), +#endif #ifdef CONFIG_PERSISTENT_KEYRINGS .persistent_keyring_register_sem = __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) ns->flags = parent_ns->flags; mutex_unlock(&userns_state_mutex); +#ifdef CONFIG_KEYS + INIT_LIST_HEAD(&ns->keyring_name_list); +#endif #ifdef CONFIG_PERSISTENT_KEYRINGS init_rwsem(&ns->persistent_keyring_register_sem); #endif @@ -196,9 +199,7 @@ static void free_user_ns(struct work_struct *work) kfree(ns->projid_map.reverse); } retire_userns_sysctls(ns); -#ifdef CONFIG_PERSISTENT_KEYRINGS - key_put(ns->persistent_keyring_register); -#endif + key_free_user_ns(ns); ns_free_inum(&ns->ns); kmem_cache_free(user_ns_cachep, ns); 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 @@ #define KEY_MAX_DESC_SIZE 4096 -static const unsigned char keyrings_capabilities[1] = { +static const unsigned char keyrings_capabilities[2] = { [0] = (KEYCTL_CAPS0_CAPABILITIES | (IS_ENABLED(CONFIG_PERSISTENT_KEYRINGS) ? KEYCTL_CAPS0_PERSISTENT_KEYRINGS : 0) | (IS_ENABLED(CONFIG_KEY_DH_OPERATIONS) ? KEYCTL_CAPS0_DIFFIE_HELLMAN : 0) | @@ -40,6 +40,7 @@ static const unsigned char keyrings_capabilities[1] = { KEYCTL_CAPS0_RESTRICT_KEYRING | KEYCTL_CAPS0_MOVE ), + [1] = (KEYCTL_CAPS1_NS_KEYRING_NAME), }; 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 @@ #include #include #include +#include #include #include #include @@ -28,11 +29,6 @@ */ #define KEYRING_SEARCH_MAX_DEPTH 6 -/* - * We keep all named keyrings in a hash to speed looking them up. - */ -#define KEYRING_NAME_HASH_SIZE (1 << 5) - /* * We mark pointers we pass to the associative array with bit 1 set if * they're keyrings and clear otherwise. @@ -55,17 +51,20 @@ static inline void *keyring_key_to_ptr(struct key *key) return key; } -static struct list_head keyring_name_hash[KEYRING_NAME_HASH_SIZE]; static DEFINE_RWLOCK(keyring_name_lock); -static inline unsigned keyring_hash(const char *desc) +/* + * Clean up the bits of user_namespace that belong to us. + */ +void key_free_user_ns(struct user_namespace *ns) { - unsigned bucket = 0; - - for (; *desc; desc++) - bucket += (unsigned char)*desc; + write_lock(&keyring_name_lock); + list_del_init(&ns->keyring_name_list); + write_unlock(&keyring_name_lock); - return bucket & (KEYRING_NAME_HASH_SIZE - 1); +#ifdef CONFIG_PERSISTENT_KEYRINGS + key_put(ns->persistent_keyring_register); +#endif } /* @@ -104,23 +103,17 @@ static DEFINE_MUTEX(keyring_serialise_link_lock); /* * Publish the name of a keyring so that it can be found by name (if it has - * one). + * one and it doesn't begin with a dot). */ static void keyring_publish_name(struct key *keyring) { - int bucket; - - if (keyring->description) { - bucket = keyring_hash(keyring->description); + struct user_namespace *ns = current_user_ns(); + if (keyring->description && + keyring->description[0] && + keyring->description[0] != '.') { write_lock(&keyring_name_lock); - - if (!keyring_name_hash[bucket].next) - INIT_LIST_HEAD(&keyring_name_hash[bucket]); - - list_add_tail(&keyring->name_link, - &keyring_name_hash[bucket]); - + list_add_tail(&keyring->name_link, &ns->keyring_name_list); write_unlock(&keyring_name_lock); } } @@ -1097,50 +1090,44 @@ found: */ struct key *find_keyring_by_name(const char *name, bool uid_keyring) { + struct user_namespace *ns = current_user_ns(); struct key *keyring; - int bucket; if (!name) return ERR_PTR(-EINVAL); - bucket = keyring_hash(name); - read_lock(&keyring_name_lock); - if (keyring_name_hash[bucket].next) { - /* search this hash bucket for a keyring with a matching name - * that's readable and that hasn't been revoked */ - list_for_each_entry(keyring, - &keyring_name_hash[bucket], - name_link - ) { - if (!kuid_has_mapping(current_user_ns(), keyring->user->uid)) - continue; - - if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) - continue; + /* Search this hash bucket for a keyring with a matching name that + * grants Search permission and that hasn't been revoked + */ + list_for_each_entry(keyring, &ns->keyring_name_list, name_link) { + if (!kuid_has_mapping(ns, keyring->user->uid)) + continue; - if (strcmp(keyring->description, name) != 0) - continue; + if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) + continue; - if (uid_keyring) { - if (!test_bit(KEY_FLAG_UID_KEYRING, - &keyring->flags)) - continue; - } else { - if (key_permission(make_key_ref(keyring, 0), - KEY_NEED_SEARCH) < 0) - continue; - } + if (strcmp(keyring->description, name) != 0) + continue; - /* we've got a match but we might end up racing with - * key_cleanup() if the keyring is currently 'dead' - * (ie. it has a zero usage count) */ - if (!refcount_inc_not_zero(&keyring->usage)) + if (uid_keyring) { + if (!test_bit(KEY_FLAG_UID_KEYRING, + &keyring->flags)) + continue; + } else { + if (key_permission(make_key_ref(keyring, 0), + KEY_NEED_SEARCH) < 0) continue; - keyring->last_used_at = ktime_get_real_seconds(); - goto out; } + + /* we've got a match but we might end up racing with + * key_cleanup() if the keyring is currently 'dead' + * (ie. it has a zero usage count) */ + if (!refcount_inc_not_zero(&keyring->usage)) + continue; + keyring->last_used_at = ktime_get_real_seconds(); + goto out; } keyring = ERR_PTR(-ENOKEY); -- cgit v1.2.3 From 3b6e4de05e9ee2e2f94e4a3fe14d945e2418d9a8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 26 Jun 2019 21:02:32 +0100 Subject: 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 --- include/linux/key.h | 10 ++++++++++ include/uapi/linux/keyctl.h | 1 + security/keys/gc.c | 2 +- security/keys/key.c | 1 + security/keys/keyctl.c | 3 ++- security/keys/keyring.c | 36 ++++++++++++++++++++++++++++++++++-- security/keys/persistent.c | 1 + 7 files changed, 50 insertions(+), 4 deletions(-) (limited to 'include/linux/key.h') diff --git a/include/linux/key.h b/include/linux/key.h index ae1177302d70..abc68555bac3 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -82,9 +82,16 @@ struct cred; struct key_type; struct key_owner; +struct key_tag; struct keyring_list; struct keyring_name; +struct key_tag { + struct rcu_head rcu; + refcount_t usage; + bool removed; /* T when subject removed */ +}; + struct keyring_index_key { /* [!] If this structure is altered, the union in struct key must change too! */ unsigned long hash; /* Hash value */ @@ -101,6 +108,7 @@ struct keyring_index_key { unsigned long x; }; struct key_type *type; + struct key_tag *domain_tag; /* Domain of operation */ const char *description; }; @@ -218,6 +226,7 @@ struct key { unsigned long hash; unsigned long len_desc; struct key_type *type; /* type of key */ + struct key_tag *domain_tag; /* Domain of operation */ char *description; }; }; @@ -268,6 +277,7 @@ extern struct key *key_alloc(struct key_type *type, extern void key_revoke(struct key *key); extern void key_invalidate(struct key *key); extern void key_put(struct key *key); +extern bool key_put_tag(struct key_tag *tag); static inline struct key *__key_get(struct key *key) { diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h index 35b405034674..ed3d5893830d 100644 --- a/include/uapi/linux/keyctl.h +++ b/include/uapi/linux/keyctl.h @@ -129,5 +129,6 @@ struct keyctl_pkey_params { #define KEYCTL_CAPS0_RESTRICT_KEYRING 0x40 /* KEYCTL_RESTRICT_KEYRING supported */ #define KEYCTL_CAPS0_MOVE 0x80 /* KEYCTL_MOVE supported */ #define KEYCTL_CAPS1_NS_KEYRING_NAME 0x01 /* Keyring names are per-user_namespace */ +#define KEYCTL_CAPS1_NS_KEY_TAG 0x02 /* Key indexing can include a namespace tag */ #endif /* _LINUX_KEYCTL_H */ diff --git a/security/keys/gc.c b/security/keys/gc.c index 634e96b380e8..83d279fb7793 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -154,7 +154,7 @@ static noinline void key_gc_unused_keys(struct list_head *keys) atomic_dec(&key->user->nikeys); key_user_put(key->user); - + key_put_tag(key->domain_tag); kfree(key->description); memzero_explicit(key, sizeof(*key)); diff --git a/security/keys/key.c b/security/keys/key.c index 9d52f2472a09..85fdc2ea6c14 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -317,6 +317,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, goto security_error; /* publish the key by giving it a serial number */ + refcount_inc(&key->domain_tag->usage); atomic_inc(&user->nkeys); key_alloc_serial(key); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 8a813220f269..4bb5781d3ddf 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -40,7 +40,8 @@ static const unsigned char keyrings_capabilities[2] = { KEYCTL_CAPS0_RESTRICT_KEYRING | KEYCTL_CAPS0_MOVE ), - [1] = (KEYCTL_CAPS1_NS_KEYRING_NAME), + [1] = (KEYCTL_CAPS1_NS_KEYRING_NAME | + KEYCTL_CAPS1_NS_KEY_TAG), }; static int key_get_type_from_user(char *type, diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 3663e5168583..0da8fa282d56 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -175,6 +175,9 @@ static void hash_key_type_and_desc(struct keyring_index_key *index_key) type = (unsigned long)index_key->type; acc = mult_64x32_and_fold(type, desc_len + 13); acc = mult_64x32_and_fold(acc, 9207); + piece = (unsigned long)index_key->domain_tag; + acc = mult_64x32_and_fold(acc, piece); + acc = mult_64x32_and_fold(acc, 9207); for (;;) { n = desc_len; @@ -208,16 +211,36 @@ static void hash_key_type_and_desc(struct keyring_index_key *index_key) /* * Finalise an index key to include a part of the description actually in the - * index key and to add in the hash too. + * index key, to set the domain tag and to calculate the hash. */ void key_set_index_key(struct keyring_index_key *index_key) { + static struct key_tag default_domain_tag = { .usage = REFCOUNT_INIT(1), }; size_t n = min_t(size_t, index_key->desc_len, sizeof(index_key->desc)); + memcpy(index_key->desc, index_key->description, n); + index_key->domain_tag = &default_domain_tag; hash_key_type_and_desc(index_key); } +/** + * key_put_tag - Release a ref on a tag. + * @tag: The tag to release. + * + * This releases a reference the given tag and returns true if that ref was the + * last one. + */ +bool key_put_tag(struct key_tag *tag) +{ + if (refcount_dec_and_test(&tag->usage)) { + kfree_rcu(tag, rcu); + return true; + } + + return false; +} + /* * Build the next index key chunk. * @@ -238,8 +261,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) return index_key->x; case 2: return (unsigned long)index_key->type; + case 3: + return (unsigned long)index_key->domain_tag; default: - level -= 3; + level -= 4; if (desc_len <= sizeof(index_key->desc)) return 0; @@ -268,6 +293,7 @@ static bool keyring_compare_object(const void *object, const void *data) const struct key *key = keyring_ptr_to_key(object); return key->index_key.type == index_key->type && + key->index_key.domain_tag == index_key->domain_tag && key->index_key.desc_len == index_key->desc_len && memcmp(key->index_key.description, index_key->description, index_key->desc_len) == 0; @@ -309,6 +335,12 @@ static int keyring_diff_objects(const void *object, const void *data) goto differ; level += sizeof(unsigned long); + seg_a = (unsigned long)a->domain_tag; + seg_b = (unsigned long)b->domain_tag; + if ((seg_a ^ seg_b) != 0) + goto differ; + level += sizeof(unsigned long); + i = sizeof(a->desc); if (a->desc_len <= i) goto same; diff --git a/security/keys/persistent.c b/security/keys/persistent.c index 90303fe4a394..9944d855a28d 100644 --- a/security/keys/persistent.c +++ b/security/keys/persistent.c @@ -84,6 +84,7 @@ static long key_get_persistent(struct user_namespace *ns, kuid_t uid, long ret; /* Look in the register if it exists */ + memset(&index_key, 0, sizeof(index_key)); index_key.type = &key_type_keyring; index_key.description = buf; index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid)); -- cgit v1.2.3 From 218e6424e711ceee31eeba93212fed8ee92d6a11 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 26 Jun 2019 21:02:32 +0100 Subject: keys: Garbage collect keys for which the domain has been removed If a key operation domain (such as a network namespace) has been removed then attempt to garbage collect all the keys that use it. Signed-off-by: David Howells --- include/linux/key.h | 2 ++ security/keys/internal.h | 3 ++- security/keys/keyring.c | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) (limited to 'include/linux/key.h') diff --git a/include/linux/key.h b/include/linux/key.h index abc68555bac3..60c076c6e47f 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -278,6 +278,7 @@ extern void key_revoke(struct key *key); extern void key_invalidate(struct key *key); extern void key_put(struct key *key); extern bool key_put_tag(struct key_tag *tag); +extern void key_remove_domain(struct key_tag *domain_tag); static inline struct key *__key_get(struct key *key) { @@ -446,6 +447,7 @@ extern void key_init(void); #define key_fsgid_changed(c) do { } while(0) #define key_init() do { } while(0) #define key_free_user_ns(ns) do { } while(0) +#define key_remove_domain(d) do { } while(0) #endif /* CONFIG_KEYS */ #endif /* __KERNEL__ */ diff --git a/security/keys/internal.h b/security/keys/internal.h index d3a9439e2386..5a561f5f199e 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -209,7 +209,8 @@ static inline bool key_is_dead(const struct key *key, time64_t limit) return key->flags & ((1 << KEY_FLAG_DEAD) | (1 << KEY_FLAG_INVALIDATED)) || - (key->expiry > 0 && key->expiry <= limit); + (key->expiry > 0 && key->expiry <= limit) || + key->domain_tag->removed; } /* diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 0da8fa282d56..d3c86fda1510 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -241,6 +241,21 @@ bool key_put_tag(struct key_tag *tag) return false; } +/** + * key_remove_domain - Kill off a key domain and gc its keys + * @domain_tag: The domain tag to release. + * + * This marks a domain tag as being dead and releases a ref on it. If that + * wasn't the last reference, the garbage collector is poked to try and delete + * all keys that were in the domain. + */ +void key_remove_domain(struct key_tag *domain_tag) +{ + domain_tag->removed = true; + if (!key_put_tag(domain_tag)) + key_schedule_gc_links(); +} + /* * Build the next index key chunk. * -- cgit v1.2.3 From a58946c158a040068e7c94dc1d58bbd273258068 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 26 Jun 2019 21:02:33 +0100 Subject: keys: Pass the network namespace into request_key mechanism Create a request_key_net() function and use it to pass the network namespace domain tag into DNS revolver keys and rxrpc/AFS keys so that keys for different domains can coexist in the same keyring. Signed-off-by: David Howells cc: netdev@vger.kernel.org cc: linux-nfs@vger.kernel.org cc: linux-cifs@vger.kernel.org cc: linux-afs@lists.infradead.org --- Documentation/security/keys/core.rst | 28 +++++++++++++---- Documentation/security/keys/request-key.rst | 29 +++++++++++++----- fs/afs/addr_list.c | 4 +-- fs/afs/dynroot.c | 8 +++-- fs/cifs/dns_resolve.c | 3 +- fs/nfs/dns_resolve.c | 3 +- fs/nfs/nfs4idmap.c | 2 +- include/linux/dns_resolver.h | 3 +- include/linux/key.h | 47 ++++++++++++++++++++++++++--- net/ceph/messenger.c | 3 +- net/dns_resolver/dns_query.c | 7 +++-- net/rxrpc/key.c | 4 +-- security/keys/internal.h | 1 + security/keys/keyctl.c | 2 +- security/keys/keyring.c | 11 ++++--- security/keys/request_key.c | 39 ++++++++++++++++-------- 16 files changed, 145 insertions(+), 49 deletions(-) (limited to 'include/linux/key.h') diff --git a/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst index ae930ae9d590..0e74f372e58c 100644 --- a/Documentation/security/keys/core.rst +++ b/Documentation/security/keys/core.rst @@ -1102,26 +1102,42 @@ payload contents" for more information. See also Documentation/security/keys/request-key.rst. + * To search for a key in a specific domain, call: + + struct key *request_key_tag(const struct key_type *type, + const char *description, + struct key_tag *domain_tag, + const char *callout_info); + + This is identical to request_key(), except that a domain tag may be + specifies that causes search algorithm to only match keys matching that + tag. The domain_tag may be NULL, specifying a global domain that is + separate from any nominated domain. + + * To search for a key, passing auxiliary data to the upcaller, call:: struct key *request_key_with_auxdata(const struct key_type *type, const char *description, + struct key_tag *domain_tag, const void *callout_info, size_t callout_len, void *aux); - This is identical to request_key(), except that the auxiliary data is - passed to the key_type->request_key() op if it exists, and the callout_info - is a blob of length callout_len, if given (the length may be 0). + This is identical to request_key_tag(), except that the auxiliary data is + passed to the key_type->request_key() op if it exists, and the + callout_info is a blob of length callout_len, if given (the length may be + 0). * To search for a key under RCU conditions, call:: struct key *request_key_rcu(const struct key_type *type, - const char *description); + const char *description, + struct key_tag *domain_tag); - which is similar to request_key() except that it does not check for keys - that are under construction and it will not call out to userspace to + which is similar to request_key_tag() except that it does not check for + keys that are under construction and it will not call out to userspace to construct a key if it can't find a match. diff --git a/Documentation/security/keys/request-key.rst b/Documentation/security/keys/request-key.rst index 5a210baa583a..35f2296b704a 100644 --- a/Documentation/security/keys/request-key.rst +++ b/Documentation/security/keys/request-key.rst @@ -13,10 +13,18 @@ The process starts by either the kernel requesting a service by calling const char *description, const char *callout_info); +or:: + + struct key *request_key_tag(const struct key_type *type, + const char *description, + const struct key_tag *domain_tag, + const char *callout_info); + or:: struct key *request_key_with_auxdata(const struct key_type *type, const char *description, + const struct key_tag *domain_tag, const char *callout_info, size_t callout_len, void *aux); @@ -24,7 +32,8 @@ or:: or:: struct key *request_key_rcu(const struct key_type *type, - const char *description); + const char *description, + const struct key_tag *domain_tag); Or by userspace invoking the request_key system call:: @@ -38,14 +47,18 @@ does not need to link the key to a keyring to prevent it from being immediately destroyed. The kernel interface returns a pointer directly to the key, and it's up to the caller to destroy the key. -The request_key_with_auxdata() calls is like the in-kernel request_key() call, -except that they permit auxiliary data to be passed to the upcaller (the -default is NULL). This is only useful for those key types that define their -own upcall mechanism rather than using /sbin/request-key. +The request_key_tag() call is like the in-kernel request_key(), except that it +also takes a domain tag that allows keys to be separated by namespace and +killed off as a group. + +The request_key_with_auxdata() calls is like the request_key_tag() call, except +that they permit auxiliary data to be passed to the upcaller (the default is +NULL). This is only useful for those key types that define their own upcall +mechanism rather than using /sbin/request-key. -The request_key_rcu() call is like the in-kernel request_key() call, except -that it doesn't check for keys that are under construction and doesn't attempt -to construct missing keys. +The request_key_rcu() call is like the request_key_tag() call, except that it +doesn't check for keys that are under construction and doesn't attempt to +construct missing keys. The userspace interface links the key to a keyring associated with the process to prevent the key from going away, and returns the serial number of the key to diff --git a/fs/afs/addr_list.c b/fs/afs/addr_list.c index 9eaff55df7b4..6b1e8fc6c954 100644 --- a/fs/afs/addr_list.c +++ b/fs/afs/addr_list.c @@ -250,8 +250,8 @@ struct afs_vlserver_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry _enter("%s", cell->name); - ret = dns_query("afsdb", cell->name, cell->name_len, "srv=1", - &result, _expiry, true); + ret = dns_query(cell->net->net, "afsdb", cell->name, cell->name_len, + "srv=1", &result, _expiry, true); if (ret < 0) { _leave(" = %d [dns]", ret); return ERR_PTR(ret); diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c index af1689d1f32e..b075605b0c45 100644 --- a/fs/afs/dynroot.c +++ b/fs/afs/dynroot.c @@ -28,6 +28,7 @@ const struct file_operations afs_dynroot_file_operations = { static int afs_probe_cell_name(struct dentry *dentry) { struct afs_cell *cell; + struct afs_net *net = afs_d2net(dentry); const char *name = dentry->d_name.name; size_t len = dentry->d_name.len; int ret; @@ -40,13 +41,14 @@ static int afs_probe_cell_name(struct dentry *dentry) len--; } - cell = afs_lookup_cell_rcu(afs_d2net(dentry), name, len); + cell = afs_lookup_cell_rcu(net, name, len); if (!IS_ERR(cell)) { - afs_put_cell(afs_d2net(dentry), cell); + afs_put_cell(net, cell); return 0; } - ret = dns_query("afsdb", name, len, "srv=1", NULL, NULL, false); + ret = dns_query(net->net, "afsdb", name, len, "srv=1", + NULL, NULL, false); if (ret == -ENODATA) ret = -EDESTADDRREQ; return ret; diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c index 1e21b2528cfb..534cbba72789 100644 --- a/fs/cifs/dns_resolve.c +++ b/fs/cifs/dns_resolve.c @@ -77,7 +77,8 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) goto name_is_IP_address; /* Perform the upcall */ - rc = dns_query(NULL, hostname, len, NULL, ip_addr, NULL, false); + rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len, + NULL, ip_addr, NULL, false); if (rc < 0) cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n", __func__, len, len, hostname); diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c index e6a700f01452..aec769a500a1 100644 --- a/fs/nfs/dns_resolve.c +++ b/fs/nfs/dns_resolve.c @@ -22,7 +22,8 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name, size_t namelen, char *ip_addr = NULL; int ip_len; - ip_len = dns_query(NULL, name, namelen, NULL, &ip_addr, NULL, false); + ip_len = dns_query(net, NULL, name, namelen, NULL, &ip_addr, NULL, + false); if (ip_len > 0) ret = rpc_pton(net, ip_addr, ip_len, sa, salen); else diff --git a/fs/nfs/nfs4idmap.c b/fs/nfs/nfs4idmap.c index 4884fdae28fb..1e7296395d71 100644 --- a/fs/nfs/nfs4idmap.c +++ b/fs/nfs/nfs4idmap.c @@ -291,7 +291,7 @@ static struct key *nfs_idmap_request_key(const char *name, size_t namelen, if (IS_ERR(rkey)) { mutex_lock(&idmap->idmap_mutex); rkey = request_key_with_auxdata(&key_type_id_resolver_legacy, - desc, "", 0, idmap); + desc, NULL, "", 0, idmap); mutex_unlock(&idmap->idmap_mutex); } if (!IS_ERR(rkey)) diff --git a/include/linux/dns_resolver.h b/include/linux/dns_resolver.h index f2b3ae22e6b7..976cbbdb2832 100644 --- a/include/linux/dns_resolver.h +++ b/include/linux/dns_resolver.h @@ -26,7 +26,8 @@ #include -extern int dns_query(const char *type, const char *name, size_t namelen, +struct net; +extern int dns_query(struct net *net, const char *type, const char *name, size_t namelen, const char *options, char **_result, time64_t *_expiry, bool invalidate); diff --git a/include/linux/key.h b/include/linux/key.h index 60c076c6e47f..18d7f62ab6b0 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -36,6 +36,7 @@ typedef int32_t key_serial_t; typedef uint32_t key_perm_t; struct key; +struct net; #ifdef CONFIG_KEYS @@ -296,19 +297,57 @@ static inline void key_ref_put(key_ref_t key_ref) key_put(key_ref_to_ptr(key_ref)); } -extern struct key *request_key(struct key_type *type, - const char *description, - const char *callout_info); +extern struct key *request_key_tag(struct key_type *type, + const char *description, + struct key_tag *domain_tag, + const char *callout_info); extern struct key *request_key_rcu(struct key_type *type, - const char *description); + const char *description, + struct key_tag *domain_tag); extern struct key *request_key_with_auxdata(struct key_type *type, const char *description, + struct key_tag *domain_tag, const void *callout_info, size_t callout_len, void *aux); +/** + * request_key - Request a key and wait for construction + * @type: Type of key. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * + * As for request_key_tag(), but with the default global domain tag. + */ +static inline struct key *request_key(struct key_type *type, + const char *description, + const char *callout_info) +{ + return request_key_tag(type, description, NULL, callout_info); +} + +#ifdef CONFIG_NET +/* + * request_key_net - Request a key for a net namespace and wait for construction + * @type: Type of key. + * @description: The searchable description of the key. + * @net: The network namespace that is the key's domain of operation. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * + * As for request_key() except that it does not add the returned key to a + * keyring if found, new keys are always allocated in the user's quota, the + * callout_info must be a NUL-terminated string and no auxiliary data can be + * passed. Only keys that operate the specified network namespace are used. + * + * Furthermore, it then works as wait_for_key_construction() to wait for the + * completion of keys undergoing construction with a non-interruptible wait. + */ +#define request_key_net(type, description, net, callout_info) \ + request_key_tag(type, description, net->key_domain, callout_info); +#endif /* CONFIG_NET */ + extern int wait_for_key_construction(struct key *key, bool intr); extern int key_validate(const struct key *key); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index cd0b094468b6..a33402c99321 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -1887,7 +1887,8 @@ static int ceph_dns_resolve_name(const char *name, size_t namelen, return -EINVAL; /* do dns_resolve upcall */ - ip_len = dns_query(NULL, name, end - name, NULL, &ip_addr, NULL, false); + ip_len = dns_query(current->nsproxy->net_ns, + NULL, name, end - name, NULL, &ip_addr, NULL, false); if (ip_len > 0) ret = ceph_pton(ip_addr, ip_len, addr, -1, NULL); else diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c index 2d260432b3be..cab4e0df924f 100644 --- a/net/dns_resolver/dns_query.c +++ b/net/dns_resolver/dns_query.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,7 @@ /** * dns_query - Query the DNS + * @net: The network namespace to operate in. * @type: Query type (or NULL for straight host->IP lookup) * @name: Name to look up * @namelen: Length of name @@ -69,7 +71,8 @@ * * Returns the size of the result on success, -ve error code otherwise. */ -int dns_query(const char *type, const char *name, size_t namelen, +int dns_query(struct net *net, + const char *type, const char *name, size_t namelen, const char *options, char **_result, time64_t *_expiry, bool invalidate) { @@ -122,7 +125,7 @@ int dns_query(const char *type, const char *name, size_t namelen, * add_key() to preinstall malicious redirections */ saved_cred = override_creds(dns_resolver_cache); - rkey = request_key(&key_type_dns_resolver, desc, options); + rkey = request_key_net(&key_type_dns_resolver, desc, net, options); revert_creds(saved_cred); kfree(desc); if (IS_ERR(rkey)) { diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index 2722189ec273..1cc6b0c6cc42 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -914,7 +914,7 @@ int rxrpc_request_key(struct rxrpc_sock *rx, char __user *optval, int optlen) if (IS_ERR(description)) return PTR_ERR(description); - key = request_key(&key_type_rxrpc, description, NULL); + key = request_key_net(&key_type_rxrpc, description, sock_net(&rx->sk), NULL); if (IS_ERR(key)) { kfree(description); _leave(" = %ld", PTR_ERR(key)); @@ -945,7 +945,7 @@ int rxrpc_server_keyring(struct rxrpc_sock *rx, char __user *optval, if (IS_ERR(description)) return PTR_ERR(description); - key = request_key(&key_type_keyring, description, NULL); + key = request_key_net(&key_type_keyring, description, sock_net(&rx->sk), NULL); if (IS_ERR(key)) { kfree(description); _leave(" = %ld", PTR_ERR(key)); diff --git a/security/keys/internal.h b/security/keys/internal.h index 5a561f5f199e..f1f2b076f3a1 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -156,6 +156,7 @@ extern int install_session_keyring_to_cred(struct cred *, struct key *); extern struct key *request_key_and_link(struct key_type *type, const char *description, + struct key_tag *domain_tag, const void *callout_info, size_t callout_len, void *aux, diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 4bb5781d3ddf..d2f8eabcbcf4 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -224,7 +224,7 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type, } /* do the search */ - key = request_key_and_link(ktype, description, callout_info, + key = request_key_and_link(ktype, description, NULL, callout_info, callout_len, NULL, key_ref_to_ptr(dest_ref), KEY_ALLOC_IN_QUOTA); if (IS_ERR(key)) { diff --git a/security/keys/keyring.c b/security/keys/keyring.c index bca070f6ab46..29c31585ed61 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -222,10 +222,13 @@ void key_set_index_key(struct keyring_index_key *index_key) memcpy(index_key->desc, index_key->description, n); - if (index_key->type->flags & KEY_TYPE_NET_DOMAIN) - index_key->domain_tag = current->nsproxy->net_ns->key_domain; - else - index_key->domain_tag = &default_domain_tag; + if (!index_key->domain_tag) { + if (index_key->type->flags & KEY_TYPE_NET_DOMAIN) + index_key->domain_tag = current->nsproxy->net_ns->key_domain; + else + index_key->domain_tag = &default_domain_tag; + } + hash_key_type_and_desc(index_key); } diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 9201ca96c4df..aa589d3c90e2 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "internal.h" #include @@ -533,16 +534,18 @@ error: * request_key_and_link - Request a key and cache it in a keyring. * @type: The type of key we want. * @description: The searchable description of the key. + * @domain_tag: The domain in which the key operates. * @callout_info: The data to pass to the instantiation upcall (or NULL). * @callout_len: The length of callout_info. * @aux: Auxiliary data for the upcall. * @dest_keyring: Where to cache the key. * @flags: Flags to key_alloc(). * - * A key matching the specified criteria is searched for in the process's - * keyrings and returned with its usage count incremented if found. Otherwise, - * if callout_info is not NULL, a key will be allocated and some service - * (probably in userspace) will be asked to instantiate it. + * A key matching the specified criteria (type, description, domain_tag) is + * searched for in the process's keyrings and returned with its usage count + * incremented if found. Otherwise, if callout_info is not NULL, a key will be + * allocated and some service (probably in userspace) will be asked to + * instantiate it. * * If successfully found or created, the key will be linked to the destination * keyring if one is provided. @@ -558,6 +561,7 @@ error: */ struct key *request_key_and_link(struct key_type *type, const char *description, + struct key_tag *domain_tag, const void *callout_info, size_t callout_len, void *aux, @@ -566,6 +570,7 @@ struct key *request_key_and_link(struct key_type *type, { struct keyring_search_context ctx = { .index_key.type = type, + .index_key.domain_tag = domain_tag, .index_key.description = description, .index_key.desc_len = strlen(description), .cred = current_cred(), @@ -672,9 +677,10 @@ int wait_for_key_construction(struct key *key, bool intr) EXPORT_SYMBOL(wait_for_key_construction); /** - * request_key - Request a key and wait for construction + * request_key_tag - Request a key and wait for construction * @type: Type of key. * @description: The searchable description of the key. + * @domain_tag: The domain in which the key operates. * @callout_info: The data to pass to the instantiation upcall (or NULL). * * As for request_key_and_link() except that it does not add the returned key @@ -685,9 +691,10 @@ EXPORT_SYMBOL(wait_for_key_construction); * Furthermore, it then works as wait_for_key_construction() to wait for the * completion of keys undergoing construction with a non-interruptible wait. */ -struct key *request_key(struct key_type *type, - const char *description, - const char *callout_info) +struct key *request_key_tag(struct key_type *type, + const char *description, + struct key_tag *domain_tag, + const char *callout_info) { struct key *key; size_t callout_len = 0; @@ -695,7 +702,8 @@ struct key *request_key(struct key_type *type, if (callout_info) callout_len = strlen(callout_info); - key = request_key_and_link(type, description, callout_info, callout_len, + key = request_key_and_link(type, description, domain_tag, + callout_info, callout_len, NULL, NULL, KEY_ALLOC_IN_QUOTA); if (!IS_ERR(key)) { ret = wait_for_key_construction(key, false); @@ -706,12 +714,13 @@ struct key *request_key(struct key_type *type, } return key; } -EXPORT_SYMBOL(request_key); +EXPORT_SYMBOL(request_key_tag); /** * request_key_with_auxdata - Request a key with auxiliary data for the upcaller * @type: The type of key we want. * @description: The searchable description of the key. + * @domain_tag: The domain in which the key operates. * @callout_info: The data to pass to the instantiation upcall (or NULL). * @callout_len: The length of callout_info. * @aux: Auxiliary data for the upcall. @@ -724,6 +733,7 @@ EXPORT_SYMBOL(request_key); */ struct key *request_key_with_auxdata(struct key_type *type, const char *description, + struct key_tag *domain_tag, const void *callout_info, size_t callout_len, void *aux) @@ -731,7 +741,8 @@ struct key *request_key_with_auxdata(struct key_type *type, struct key *key; int ret; - key = request_key_and_link(type, description, callout_info, callout_len, + key = request_key_and_link(type, description, domain_tag, + callout_info, callout_len, aux, NULL, KEY_ALLOC_IN_QUOTA); if (!IS_ERR(key)) { ret = wait_for_key_construction(key, false); @@ -748,6 +759,7 @@ EXPORT_SYMBOL(request_key_with_auxdata); * request_key_rcu - Request key from RCU-read-locked context * @type: The type of key we want. * @description: The name of the key we want. + * @domain_tag: The domain in which the key operates. * * Request a key from a context that we may not sleep in (such as RCU-mode * pathwalk). Keys under construction are ignored. @@ -755,10 +767,13 @@ EXPORT_SYMBOL(request_key_with_auxdata); * Return a pointer to the found key if successful, -ENOKEY if we couldn't find * a key or some other error if the key found was unsuitable or inaccessible. */ -struct key *request_key_rcu(struct key_type *type, const char *description) +struct key *request_key_rcu(struct key_type *type, + const char *description, + struct key_tag *domain_tag) { struct keyring_search_context ctx = { .index_key.type = type, + .index_key.domain_tag = domain_tag, .index_key.description = description, .index_key.desc_len = strlen(description), .cred = current_cred(), -- cgit v1.2.3