Skip to content

Commit

Permalink
keys: Pass the network namespace into request_key mechanism
Browse files Browse the repository at this point in the history
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
  • Loading branch information
dhowells committed Jun 27, 2019
1 parent 9b24261 commit a58946c
Show file tree
Hide file tree
Showing 16 changed files with 145 additions and 49 deletions.
28 changes: 22 additions & 6 deletions Documentation/security/keys/core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.


Expand Down
29 changes: 21 additions & 8 deletions Documentation/security/keys/request-key.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,27 @@ 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);

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::

Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions fs/afs/addr_list.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 5 additions & 3 deletions fs/afs/dynroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion fs/cifs/dns_resolve.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion fs/nfs/dns_resolve.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/nfs4idmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
3 changes: 2 additions & 1 deletion include/linux/dns_resolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
47 changes: 43 additions & 4 deletions include/linux/key.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ typedef int32_t key_serial_t;
typedef uint32_t key_perm_t;

struct key;
struct net;

#ifdef CONFIG_KEYS

Expand Down Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion net/ceph/messenger.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 5 additions & 2 deletions net/dns_resolver/dns_query.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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
Expand All @@ -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)
{
Expand Down Expand Up @@ -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)) {
Expand Down
4 changes: 2 additions & 2 deletions net/rxrpc/key.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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));
Expand Down
1 change: 1 addition & 0 deletions security/keys/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion security/keys/keyctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
11 changes: 7 additions & 4 deletions security/keys/keyring.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Loading

0 comments on commit a58946c

Please sign in to comment.