keys: Pass the network namespace into request_key mechanism
authorDavid Howells <dhowells@redhat.com>
Wed, 26 Jun 2019 20:02:33 +0000 (21:02 +0100)
committerDavid Howells <dhowells@redhat.com>
Thu, 27 Jun 2019 22:02:12 +0000 (23:02 +0100)
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 <dhowells@redhat.com>
cc: netdev@vger.kernel.org
cc: linux-nfs@vger.kernel.org
cc: linux-cifs@vger.kernel.org
cc: linux-afs@lists.infradead.org

16 files changed:
Documentation/security/keys/core.rst
Documentation/security/keys/request-key.rst
fs/afs/addr_list.c
fs/afs/dynroot.c
fs/cifs/dns_resolve.c
fs/nfs/dns_resolve.c
fs/nfs/nfs4idmap.c
include/linux/dns_resolver.h
include/linux/key.h
net/ceph/messenger.c
net/dns_resolver/dns_query.c
net/rxrpc/key.c
security/keys/internal.h
security/keys/keyctl.c
security/keys/keyring.c
security/keys/request_key.c

index ae930ae9d590804e8a63d88e07bfc3fb4a8b4ccf..0e74f372e58c4af59a372ce81d24e8791736d82a 100644 (file)
@@ -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.
 
 
index 5a210baa583a0900b0c0dfbf113bdce128f5e7dd..35f2296b704a3841924b3bb829d354380927ea30 100644 (file)
@@ -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
index 9eaff55df7b43ee490187b629856a4f35be6a46f..6b1e8fc6c9540a3cdbc37ef43c997a93dc7a8946 100644 (file)
@@ -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);
index af1689d1f32e7e699e1ecd13e4b7f84443a0c4a6..b075605b0c450759bcf2c10fe2ab8f1e99b4661d 100644 (file)
@@ -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;
index 1e21b2528cfb3e8397db38ab0d927ef047ab3e37..534cbba72789fca17a7b29ee9b51fea56ac25aa1 100644 (file)
@@ -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);
index e6a700f014528088ef0e15260b9a5d4d95822a64..aec769a500a15b9eac153ab5658f5d6875f98c0d 100644 (file)
@@ -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
index 4884fdae28fb18301eb376af93f499d126979a17..1e7296395d71bfc3144d5a57c6658712d7730c09 100644 (file)
@@ -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))
index f2b3ae22e6b77bf3ba37ed7b42abc94ca95222cb..976cbbdb2832f9ee223c045f132c2ec708cf39ca 100644 (file)
@@ -26,7 +26,8 @@
 
 #include <uapi/linux/dns_resolver.h>
 
-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);
 
index 60c076c6e47fc7b4f676826b5d763dbd771d7228..18d7f62ab6b02c6771264f857b0087f79d19731e 100644 (file)
@@ -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);
index cd0b094468b612b7bdb8231ddef8fa9847cf2dd5..a33402c99321cc0420a4952b547b6ba54279b597 100644 (file)
@@ -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
index 2d260432b3be0a7ddefbd6cb68c2bb2b18163525..cab4e0df924f5c5f3ae9d67b22903ae8411b53ba 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/cred.h>
 #include <linux/dns_resolver.h>
 #include <linux/err.h>
+#include <net/net_namespace.h>
 
 #include <keys/dns_resolver-type.h>
 #include <keys/user-type.h>
@@ -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)) {
index 2722189ec2739babadd37f3c25335fea40314416..1cc6b0c6cc429559b8d02c0ca5814c60f75e23af 100644 (file)
@@ -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));
index 5a561f5f199e9a764847b34ba9c510c0a88e0fbd..f1f2b076f3a1c703ffe1a4aa4f90f4fc64797837 100644 (file)
@@ -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,
index 4bb5781d3ddf8d4c24044ce15e93b8d0c642071b..d2f8eabcbcf4f5fa1864f34237826fe60f5e0a8d 100644 (file)
@@ -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)) {
index bca070f6ab460f625634d9e5c85f19a5249ebdc1..29c31585ed61acf02c3d649cf54ad44d312f5f48 100644 (file)
@@ -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);
 }
 
index 9201ca96c4df64f2e60b2bcd336dac1916141f81..aa589d3c90e2fa6d5a1d75c0636d525f3f7ec85b 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/err.h>
 #include <linux/keyctl.h>
 #include <linux/slab.h>
+#include <net/net_namespace.h>
 #include "internal.h"
 #include <keys/request_key_auth-type.h>
 
@@ -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(),