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') 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 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux') 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; -- 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 --- include/linux/key.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') 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); -- 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 +++++ 2 files changed, 7 insertions(+) (limited to 'include/linux') 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; -- cgit v1.2.3 From 0f44e4d976f96c6439da0d6717238efa4b91196e Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 26 Jun 2019 21:02:32 +0100 Subject: keys: Move the user and user-session keyrings to the user_namespace Move the user and user-session keyrings to the user_namespace struct rather than pinning them from the user_struct struct. This prevents these keyrings from propagating across user-namespaces boundaries with regard to the KEY_SPEC_* flags, thereby making them more useful in a containerised environment. The issue is that a single user_struct may be represent UIDs in several different namespaces. The way the patch does this is by attaching a 'register keyring' in each user_namespace and then sticking the user and user-session keyrings into that. It can then be searched to retrieve them. Signed-off-by: David Howells cc: Jann Horn --- include/linux/sched/user.h | 14 -------------- include/linux/user_namespace.h | 9 +++++++-- 2 files changed, 7 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched/user.h b/include/linux/sched/user.h index 468d2565a9fe..917d88edb7b9 100644 --- a/include/linux/sched/user.h +++ b/include/linux/sched/user.h @@ -7,8 +7,6 @@ #include #include -struct key; - /* * Some day this will be a full-fledged user tracking system.. */ @@ -30,18 +28,6 @@ struct user_struct { unsigned long unix_inflight; /* How many files in flight in unix sockets */ atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */ -#ifdef CONFIG_KEYS - /* - * These pointers can only change from NULL to a non-NULL value once. - * Writes are protected by key_user_keyring_mutex. - * Unlocked readers should use READ_ONCE() unless they know that - * install_user_keyrings() has been called successfully (which sets - * these members to non-NULL values, preventing further modifications). - */ - struct key *uid_keyring; /* UID specific keyring */ - struct key *session_keyring; /* UID's default session keyring */ -#endif - /* Hash table maintenance information */ struct hlist_node uidhash_node; kuid_t uid; diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index 90457015fa3f..fb9f4f799554 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -65,14 +65,19 @@ struct user_namespace { unsigned long flags; #ifdef CONFIG_KEYS - /* List of joinable keyrings in this namespace */ + /* List of joinable keyrings in this namespace. Modification access of + * these pointers is controlled by keyring_sem. Once + * user_keyring_register is set, it won't be changed, so it can be + * accessed directly with READ_ONCE(). + */ struct list_head keyring_name_list; + struct key *user_keyring_register; + struct rw_semaphore keyring_sem; #endif /* Register of per-UID persistent keyrings for this namespace */ #ifdef CONFIG_PERSISTENT_KEYRINGS struct key *persistent_keyring_register; - struct rw_semaphore persistent_keyring_register_sem; #endif struct work_struct work; #ifdef CONFIG_SYSCTL -- 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 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'include/linux') 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) { -- 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 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') 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__ */ -- cgit v1.2.3 From 9b242610514fe387ef957bce05e1fdd3efd60359 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 26 Jun 2019 21:02:33 +0100 Subject: keys: Network namespace domain tag Create key domain tags for network namespaces and make it possible to automatically tag keys that are used by networked services (e.g. AF_RXRPC, AFS, DNS) with the default network namespace if not set by the caller. This allows keys with the same description but in different namespaces to coexist within a 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 --- include/linux/key-type.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux') diff --git a/include/linux/key-type.h b/include/linux/key-type.h index e49d1de0614e..2148a6bf58f1 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -74,6 +74,9 @@ struct key_type { */ size_t def_datalen; + unsigned int flags; +#define KEY_TYPE_NET_DOMAIN 0x00000001 /* Keys of this type have a net namespace domain */ + /* vet a description */ int (*vet_description)(const char *description); -- 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 --- include/linux/dns_resolver.h | 3 ++- include/linux/key.h | 47 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 5 deletions(-) (limited to 'include/linux') 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); -- cgit v1.2.3