Skip to content

Commit

Permalink
Merge tag 'keys-namespace-20190627' of git://git.kernel.org/pub/scm/l…
Browse files Browse the repository at this point in the history
…inux/kernel/git/dhowells/linux-fs

Pull keyring namespacing from David Howells:
 "These patches help make keys and keyrings more namespace aware.

  Firstly some miscellaneous patches to make the process easier:

   - Simplify key index_key handling so that the word-sized chunks
     assoc_array requires don't have to be shifted about, making it
     easier to add more bits into the key.

   - Cache the hash value in the key so that we don't have to calculate
     on every key we examine during a search (it involves a bunch of
     multiplications).

   - Allow keying_search() to search non-recursively.

  Then the main patches:

   - Make it so that keyring names are per-user_namespace from the point
     of view of KEYCTL_JOIN_SESSION_KEYRING so that they're not
     accessible cross-user_namespace.

     keyctl_capabilities() shows KEYCTL_CAPS1_NS_KEYRING_NAME for this.

   - Move the user and user-session keyrings to the user_namespace
     rather than the user_struct. This prevents them propagating
     directly across user_namespaces boundaries (ie. the KEY_SPEC_*
     flags will only pick from the current user_namespace).

   - Make it possible to include the target namespace in which the key
     shall operate in the index_key. This will allow the possibility of
     multiple keys with the same description, but different target
     domains to be held in the same keyring.

     keyctl_capabilities() shows KEYCTL_CAPS1_NS_KEY_TAG for this.

   - Make it so that keys are implicitly invalidated by removal of a
     domain tag, causing them to be garbage collected.

   - Institute a network namespace domain tag that allows keys to be
     differentiated by the network namespace in which they operate. New
     keys that are of a type marked 'KEY_TYPE_NET_DOMAIN' are assigned
     the network domain in force when they are created.

   - Make it so that the desired network namespace can be handed down
     into the request_key() mechanism. This allows AFS, NFS, etc. to
     request keys specific to the network namespace of the superblock.

     This also means that the keys in the DNS record cache are
     thenceforth namespaced, provided network filesystems pass the
     appropriate network namespace down into dns_query().

     For DNS, AFS and NFS are good, whilst CIFS and Ceph are not. Other
     cache keyrings, such as idmapper keyrings, also need to set the
     domain tag - for which they need access to the network namespace of
     the superblock"

* tag 'keys-namespace-20190627' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  keys: Pass the network namespace into request_key mechanism
  keys: Network namespace domain tag
  keys: Garbage collect keys for which the domain has been removed
  keys: Include target namespace in match criteria
  keys: Move the user and user-session keyrings to the user_namespace
  keys: Namespace keyring names
  keys: Add a 'recurse' flag for keyring searches
  keys: Cache the hash value to avoid lots of recalculation
  keys: Simplify key description management
  • Loading branch information
torvalds committed Jul 9, 2019
2 parents c236b6d + a58946c commit c84ca91
Show file tree
Hide file tree
Showing 36 changed files with 589 additions and 310 deletions.
38 changes: 28 additions & 10 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 Expand Up @@ -1162,11 +1178,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
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
2 changes: 1 addition & 1 deletion certs/blacklist.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,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;
Expand Down
2 changes: 1 addition & 1 deletion crypto/asymmetric_keys/asymmetric_type.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,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);
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 @@ -246,8 +246,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 @@ -24,6 +24,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 @@ -36,13 +37,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
3 changes: 3 additions & 0 deletions include/linux/key-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,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);

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

struct key;
struct net;

#ifdef CONFIG_KEYS

Expand Down Expand Up @@ -77,13 +78,34 @@ 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 */
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;
struct key_tag *domain_tag; /* Domain of operation */
const char *description;
size_t desc_len;
};

union key_payload {
Expand Down Expand Up @@ -197,7 +219,10 @@ struct key {
union {
struct keyring_index_key index_key;
struct {
unsigned long hash;
unsigned long len_desc;
struct key_type *type; /* type of key */
struct key_tag *domain_tag; /* Domain of operation */
char *description;
};
};
Expand Down Expand Up @@ -248,6 +273,8 @@ 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);
extern void key_remove_domain(struct key_tag *domain_tag);

static inline struct key *__key_get(struct key *key)
{
Expand All @@ -265,19 +292,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 Expand Up @@ -321,7 +386,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);
Expand All @@ -340,6 +406,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.
Expand Down Expand Up @@ -413,6 +480,8 @@ 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)
#define key_remove_domain(d) do { } while(0)

#endif /* CONFIG_KEYS */
#endif /* __KERNEL__ */
Expand Down
Loading

0 comments on commit c84ca91

Please sign in to comment.