Keyrings namespacing

-----BEGIN PGP SIGNATURE-----
 
 iQIVAwUAXRU89Pu3V2unywtrAQIdBBAAmMBsrfv+LUN4Vru/D6KdUO4zdYGcNK6m
 S56bcNfP6oIDEj6HrNNnzKkWIZpdZ61Odv1zle96+v4WZ/6rnLCTpcsdaFNTzaoO
 YT2jk7jplss0ImrMv1DSoykGqO3f0ThMIpGCxHKZADGSu0HMbjSEh+zLPV4BaMtT
 BVuF7P3eZtDRLdDtMtYcgvf5UlbdoBEY8w1FUjReQx8hKGxVopGmCo5vAeiY8W9S
 ybFSZhPS5ka33ynVrLJH2dqDo5A8pDhY8I4bdlcxmNtRhnPCYZnuvTqeAzyUKKdI
 YN9zJeDu1yHs9mi8dp45NPJiKy6xLzWmUwqH8AvR8MWEkrwzqbzNZCEHZ41j74hO
 YZWI0JXi72cboszFvOwqJERvITKxrQQyVQLPRQE2vVbG0bIZPl8i7oslFVhitsl+
 evWqHb4lXY91rI9cC6JIXR1OiUjp68zXPv7DAnxv08O+PGcioU1IeOvPivx8QSx4
 5aUeCkYIIAti/GISzv7xvcYh8mfO76kBjZSB35fX+R9DkeQpxsHmmpWe+UCykzWn
 EwhHQn86+VeBFP6RAXp8CgNCLbrwkEhjzXQl/70s1eYbwvK81VcpDAQ6+cjpf4Hb
 QUmrUJ9iE0wCNl7oqvJZoJvWVGlArvPmzpkTJk3N070X2R0T7x1WCsMlPDMJGhQ2
 fVHvA3QdgWs=
 =Push
 -----END PGP SIGNATURE-----

Merge tag 'keys-namespace-20190627' of git://git.kernel.org/pub/scm/linux/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
This commit is contained in:
Linus Torvalds 2019-07-08 19:36:47 -07:00
commit c84ca912b0
36 changed files with 594 additions and 315 deletions

View 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.
@ -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

View 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_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_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 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

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;
@ -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;

View 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);

View 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

View 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))

View 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);

View File

@ -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);

View File

@ -31,6 +31,7 @@ typedef int32_t key_serial_t;
typedef uint32_t key_perm_t;
struct key;
struct net;
#ifdef CONFIG_KEYS
@ -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 {
@ -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;
};
};
@ -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)
{
@ -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,
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);
@ -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);
@ -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.
@ -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__ */

View File

@ -7,8 +7,6 @@
#include <linux/refcount.h>
#include <linux/ratelimit.h>
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;

View File

@ -64,10 +64,20 @@ struct user_namespace {
struct ns_common ns;
unsigned long flags;
#ifdef CONFIG_KEYS
/* 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

View File

@ -71,6 +71,9 @@ struct net {
*/
struct llist_node cleanup_list; /* namespaces on death row */
#ifdef CONFIG_KEYS
struct key_tag *key_domain; /* Key domain of operation tag */
#endif
struct user_namespace *user_ns; /* Owning user namespace */
struct ucounts *ucounts;
spinlock_t nsid_lock;

View File

@ -128,5 +128,7 @@ 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 */
#define KEYCTL_CAPS1_NS_KEY_TAG 0x02 /* Key indexing can include a namespace tag */
#endif /* _LINUX_KEYCTL_H */

View File

@ -63,9 +63,9 @@ struct user_namespace init_user_ns = {
.ns.ops = &userns_operations,
#endif
.flags = USERNS_INIT_FLAGS,
#ifdef CONFIG_PERSISTENT_KEYRINGS
.persistent_keyring_register_sem =
__RWSEM_INITIALIZER(init_user_ns.persistent_keyring_register_sem),
#ifdef CONFIG_KEYS
.keyring_name_list = LIST_HEAD_INIT(init_user_ns.keyring_name_list),
.keyring_sem = __RWSEM_INITIALIZER(init_user_ns.keyring_sem),
#endif
};
EXPORT_SYMBOL_GPL(init_user_ns);
@ -141,8 +141,6 @@ static void free_user(struct user_struct *up, unsigned long flags)
{
uid_hash_remove(up);
spin_unlock_irqrestore(&uidhash_lock, flags);
key_put(up->uid_keyring);
key_put(up->session_keyring);
kmem_cache_free(uid_cachep, up);
}

View File

@ -128,8 +128,9 @@ int create_user_ns(struct cred *new)
ns->flags = parent_ns->flags;
mutex_unlock(&userns_state_mutex);
#ifdef CONFIG_PERSISTENT_KEYRINGS
init_rwsem(&ns->persistent_keyring_register_sem);
#ifdef CONFIG_KEYS
INIT_LIST_HEAD(&ns->keyring_name_list);
init_rwsem(&ns->keyring_sem);
#endif
ret = -ENOMEM;
if (!setup_userns_sysctls(ns))
@ -191,9 +192,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);

View File

@ -218,7 +218,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

View 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

View File

@ -39,9 +39,16 @@ EXPORT_SYMBOL_GPL(net_namespace_list);
DECLARE_RWSEM(net_rwsem);
EXPORT_SYMBOL_GPL(net_rwsem);
#ifdef CONFIG_KEYS
static struct key_tag init_net_key_domain = { .usage = REFCOUNT_INIT(1) };
#endif
struct net init_net = {
.count = REFCOUNT_INIT(1),
.dev_base_head = LIST_HEAD_INIT(init_net.dev_base_head),
#ifdef CONFIG_KEYS
.key_domain = &init_net_key_domain,
#endif
};
EXPORT_SYMBOL(init_net);
@ -387,10 +394,22 @@ static struct net *net_alloc(void)
if (!net)
goto out_free;
#ifdef CONFIG_KEYS
net->key_domain = kzalloc(sizeof(struct key_tag), GFP_KERNEL);
if (!net->key_domain)
goto out_free_2;
refcount_set(&net->key_domain->usage, 1);
#endif
rcu_assign_pointer(net->gen, ng);
out:
return net;
#ifdef CONFIG_KEYS
out_free_2:
kmem_cache_free(net_cachep, net);
net = NULL;
#endif
out_free:
kfree(ng);
goto out;
@ -567,6 +586,7 @@ static void cleanup_net(struct work_struct *work)
list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) {
list_del_init(&net->exit_list);
dec_net_namespaces(net->ucounts);
key_remove_domain(net->key_domain);
put_user_ns(net->user_ns);
net_drop_ns(net);
}

View File

@ -314,6 +314,7 @@ static long dns_resolver_read(const struct key *key,
struct key_type key_type_dns_resolver = {
.name = "dns_resolver",
.flags = KEY_TYPE_NET_DOMAIN,
.preparse = dns_resolver_preparse,
.free_preparse = dns_resolver_free_preparse,
.instantiate = generic_key_instantiate,

View 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)) {

View File

@ -39,6 +39,7 @@ static long rxrpc_read(const struct key *, char __user *, size_t);
*/
struct key_type key_type_rxrpc = {
.name = "rxrpc",
.flags = KEY_TYPE_NET_DOMAIN,
.preparse = rxrpc_preparse,
.free_preparse = rxrpc_free_preparse,
.instantiate = generic_key_instantiate,
@ -54,6 +55,7 @@ EXPORT_SYMBOL(key_type_rxrpc);
*/
struct key_type key_type_rxrpc_s = {
.name = "rxrpc_s",
.flags = KEY_TYPE_NET_DOMAIN,
.vet_description = rxrpc_vet_description_s,
.preparse = rxrpc_preparse_s,
.free_preparse = rxrpc_free_preparse_s,
@ -908,7 +910,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));
@ -939,7 +941,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));

View File

@ -144,7 +144,7 @@ int rxrpc_init_server_conn_security(struct rxrpc_connection *conn)
/* 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));

View File

@ -35,7 +35,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);
@ -47,7 +47,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

View File

@ -150,7 +150,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));

View File

@ -85,7 +85,7 @@ extern spinlock_t key_serial_lock;
extern struct mutex key_construction_mutex;
extern wait_queue_head_t request_key_conswq;
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);
@ -123,6 +123,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);
@ -143,13 +144,15 @@ extern key_ref_t search_process_keyrings_rcu(struct keyring_search_context *ctx)
extern struct key *find_keyring_by_name(const char *name, bool uid_keyring);
extern int install_user_keyrings(void);
extern int look_up_user_keyrings(struct key **, struct key **);
extern struct key *get_user_session_keyring_rcu(const struct cred *);
extern int install_thread_keyring_to_cred(struct cred *);
extern int install_process_keyring_to_cred(struct cred *);
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,
@ -203,7 +206,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;
}
/*

View File

@ -281,11 +281,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;
@ -312,6 +313,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);
@ -864,6 +866,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
goto error_free_prep;
}
index_key.desc_len = strlen(index_key.description);
key_set_index_key(&index_key);
ret = __key_link_lock(keyring, &index_key);
if (ret < 0) {

View File

@ -26,7 +26,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) |
@ -36,6 +36,8 @@ static const unsigned char keyrings_capabilities[1] = {
KEYCTL_CAPS0_RESTRICT_KEYRING |
KEYCTL_CAPS0_MOVE
),
[1] = (KEYCTL_CAPS1_NS_KEYRING_NAME |
KEYCTL_CAPS1_NS_KEY_TAG),
};
static int key_get_type_from_user(char *type,
@ -218,7 +220,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)) {
@ -758,7 +760,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);

View File

@ -12,10 +12,13 @@
#include <linux/security.h>
#include <linux/seq_file.h>
#include <linux/err.h>
#include <linux/user_namespace.h>
#include <linux/nsproxy.h>
#include <keys/keyring-type.h>
#include <keys/user-type.h>
#include <linux/assoc_array_priv.h>
#include <linux/uaccess.h>
#include <net/net_namespace.h>
#include "internal.h"
/*
@ -24,11 +27,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.
@ -51,17 +49,21 @@ 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;
write_lock(&keyring_name_lock);
list_del_init(&ns->keyring_name_list);
write_unlock(&keyring_name_lock);
for (; *desc; desc++)
bucket += (unsigned char)*desc;
return bucket & (KEYRING_NAME_HASH_SIZE - 1);
key_put(ns->user_keyring_register);
#ifdef CONFIG_PERSISTENT_KEYRINGS
key_put(ns->persistent_keyring_register);
#endif
}
/*
@ -100,23 +102,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);
}
}
@ -164,7 +160,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;
@ -175,9 +171,12 @@ static unsigned long hash_key_type_and_desc(const struct keyring_index_key *inde
int n, desc_len = index_key->desc_len;
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;
if (n <= 0)
@ -202,66 +201,101 @@ 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, 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);
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);
}
/**
* 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;
}
/**
* 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.
*
* On 32-bit systems the index key is laid out as:
*
* 0 4 5 9...
* hash desclen typeptr desc[]
*
* On 64-bit systems:
*
* 0 8 9 17...
* hash desclen typeptr desc[]
*
* We return it one word-sized chunk at a time.
*/
static unsigned long keyring_get_key_chunk(const void *data, int level)
{
const struct keyring_index_key *index_key = data;
unsigned long chunk = 0;
long offset = 0;
const u8 *d;
int desc_len = index_key->desc_len, n = sizeof(chunk);
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 ((unsigned long)index_key->type << 8) | desc_len;
return index_key->x;
case 2:
if (desc_len == 0)
return (u8)((unsigned long)index_key->type >>
(ASSOC_ARRAY_KEY_CHUNK_SIZE - 8));
n--;
offset = 1;
/* fall through */
return (unsigned long)index_key->type;
case 3:
return (unsigned long)index_key->domain_tag;
default:
offset += sizeof(chunk) - 1;
offset += (level - 3) * sizeof(chunk);
if (offset >= desc_len)
level -= 4;
if (desc_len <= sizeof(index_key->desc))
return 0;
desc_len -= offset;
d = index_key->description + sizeof(index_key->desc);
d += level * sizeof(long);
desc_len -= sizeof(index_key->desc);
if (desc_len > n)
desc_len = n;
offset += desc_len;
do {
chunk <<= 8;
chunk |= ((u8*)index_key->description)[--offset];
chunk |= *d++;
} while (--desc_len > 0);
if (level == 2) {
chunk <<= 8;
chunk |= (u8)((unsigned long)index_key->type >>
(ASSOC_ARRAY_KEY_CHUNK_SIZE - 8));
}
return chunk;
}
}
@ -278,6 +312,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;
@ -296,43 +331,38 @@ 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;
/* The number of bits contributed by the hash is controlled by a
* constant in the assoc_array headers. Everything else thereafter we
* can deal with as being machine word-size dependent.
*/
level += ASSOC_ARRAY_KEY_CHUNK_SIZE / 8;
seg_a = a->desc_len;
seg_b = b->desc_len;
seg_a = a->x;
seg_b = b->x;
if ((seg_a ^ seg_b) != 0)
goto differ;
level += sizeof(unsigned long);
/* The next bit may not work on big endian */
level++;
seg_a = (unsigned long)a->type;
seg_b = (unsigned long)b->type;
if ((seg_a ^ seg_b) != 0)
goto differ;
level += sizeof(unsigned long);
if (a->desc_len == 0)
goto same;
i = 0;
if (((unsigned long)a->description | (unsigned long)b->description) &
(sizeof(unsigned long) - 1)) {
do {
seg_a = *(unsigned long *)(a->description + i);
seg_b = *(unsigned long *)(b->description + i);
seg_a = (unsigned long)a->domain_tag;
seg_b = (unsigned long)b->domain_tag;
if ((seg_a ^ seg_b) != 0)
goto differ_plus_i;
i += sizeof(unsigned long);
} while (i < (a->desc_len & (sizeof(unsigned long) - 1)));
}
goto differ;
level += sizeof(unsigned long);
i = sizeof(a->desc);
if (a->desc_len <= i)
goto same;
for (; i < a->desc_len; i++) {
seg_a = *(unsigned char *)(a->description + i);
@ -658,6 +688,9 @@ static bool search_nested_keyrings(struct key *keyring,
BUG_ON((ctx->flags & STATE_CHECKS) == 0 ||
(ctx->flags & STATE_CHECKS) == STATE_CHECKS);
if (ctx->index_key.description)
key_set_index_key(&ctx->index_key);
/* Check to see if this top-level keyring is what we are looking for
* and whether it is valid or not.
*/
@ -697,6 +730,9 @@ static bool search_nested_keyrings(struct key *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;
@ -897,13 +933,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,
@ -918,6 +956,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)
@ -1102,24 +1142,19 @@ key_ref_t find_key_to_update(key_ref_t keyring_ref,
*/
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))
/* 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 (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
@ -1146,7 +1181,6 @@ struct key *find_keyring_by_name(const char *name, bool uid_keyring)
keyring->last_used_at = ktime_get_real_seconds();
goto out;
}
}
keyring = ERR_PTR(-ENOKEY);
out:
@ -1188,7 +1222,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();

View File

@ -80,15 +80,17 @@ 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));
key_set_index_key(&index_key);
if (ns->persistent_keyring_register) {
reg_ref = make_key_ref(ns->persistent_keyring_register, true);
down_read(&ns->persistent_keyring_register_sem);
down_read(&ns->keyring_sem);
persistent_ref = find_key_to_update(reg_ref, &index_key);
up_read(&ns->persistent_keyring_register_sem);
up_read(&ns->keyring_sem);
if (persistent_ref)
goto found;
@ -97,9 +99,9 @@ static long key_get_persistent(struct user_namespace *ns, kuid_t uid,
/* It wasn't in the register, so we'll need to create it. We might
* also need to create the register.
*/
down_write(&ns->persistent_keyring_register_sem);
down_write(&ns->keyring_sem);
persistent_ref = key_create_persistent(ns, uid, &index_key);
up_write(&ns->persistent_keyring_register_sem);
up_write(&ns->keyring_sem);
if (!IS_ERR(persistent_ref))
goto found;

View File

@ -166,7 +166,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);

View File

@ -15,15 +15,13 @@
#include <linux/security.h>
#include <linux/user_namespace.h>
#include <linux/uaccess.h>
#include <linux/init_task.h>
#include <keys/request_key_auth-type.h>
#include "internal.h"
/* Session keyring create vs join semaphore */
static DEFINE_MUTEX(key_session_mutex);
/* User keyring creation semaphore */
static DEFINE_MUTEX(key_user_keyring_mutex);
/* The root user's tracking struct */
struct key_user root_key_user = {
.usage = REFCOUNT_INIT(3),
@ -35,61 +33,99 @@ struct key_user root_key_user = {
};
/*
* Install the user and user session keyrings for the current process's UID.
* Get or create a user register keyring.
*/
int install_user_keyrings(void)
static struct key *get_user_register(struct user_namespace *user_ns)
{
struct user_struct *user;
const struct cred *cred;
struct key *uid_keyring, *session_keyring;
key_perm_t user_keyring_perm;
char buf[20];
int ret;
uid_t uid;
struct key *reg_keyring = READ_ONCE(user_ns->user_keyring_register);
user_keyring_perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL;
cred = current_cred();
user = cred->user;
uid = from_kuid(cred->user_ns, user->uid);
if (reg_keyring)
return reg_keyring;
kenter("%p{%u}", user, uid);
down_write(&user_ns->keyring_sem);
if (READ_ONCE(user->uid_keyring) && READ_ONCE(user->session_keyring)) {
kleave(" = 0 [exist]");
return 0;
/* Make sure there's a register keyring. It gets owned by the
* user_namespace's owner.
*/
reg_keyring = user_ns->user_keyring_register;
if (!reg_keyring) {
reg_keyring = keyring_alloc(".user_reg",
user_ns->owner, INVALID_GID,
&init_cred,
KEY_POS_WRITE | KEY_POS_SEARCH |
KEY_USR_VIEW | KEY_USR_READ,
0,
NULL, NULL);
if (!IS_ERR(reg_keyring))
smp_store_release(&user_ns->user_keyring_register,
reg_keyring);
}
mutex_lock(&key_user_keyring_mutex);
up_write(&user_ns->keyring_sem);
/* We don't return a ref since the keyring is pinned by the user_ns */
return reg_keyring;
}
/*
* Look up the user and user session keyrings for the current process's UID,
* creating them if they don't exist.
*/
int look_up_user_keyrings(struct key **_user_keyring,
struct key **_user_session_keyring)
{
const struct cred *cred = current_cred();
struct user_namespace *user_ns = current_user_ns();
struct key *reg_keyring, *uid_keyring, *session_keyring;
key_perm_t user_keyring_perm;
key_ref_t uid_keyring_r, session_keyring_r;
uid_t uid = from_kuid(user_ns, cred->user->uid);
char buf[20];
int ret;
user_keyring_perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL;
kenter("%u", uid);
reg_keyring = get_user_register(user_ns);
if (IS_ERR(reg_keyring))
return PTR_ERR(reg_keyring);
down_write(&user_ns->keyring_sem);
ret = 0;
if (!user->uid_keyring) {
/* get the UID-specific keyring
* - there may be one in existence already as it may have been
* pinned by a session, but the user_struct pointing to it
* may have been destroyed by setuid */
sprintf(buf, "_uid.%u", uid);
uid_keyring = find_keyring_by_name(buf, true);
if (IS_ERR(uid_keyring)) {
uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID,
/* Get the user keyring. Note that there may be one in existence
* already as it may have been pinned by a session, but the user_struct
* pointing to it may have been destroyed by setuid.
*/
snprintf(buf, sizeof(buf), "_uid.%u", uid);
uid_keyring_r = keyring_search(make_key_ref(reg_keyring, true),
&key_type_keyring, buf, false);
kdebug("_uid %p", uid_keyring_r);
if (uid_keyring_r == ERR_PTR(-EAGAIN)) {
uid_keyring = keyring_alloc(buf, cred->user->uid, INVALID_GID,
cred, user_keyring_perm,
KEY_ALLOC_UID_KEYRING |
KEY_ALLOC_IN_QUOTA,
NULL, NULL);
NULL, reg_keyring);
if (IS_ERR(uid_keyring)) {
ret = PTR_ERR(uid_keyring);
goto error;
}
} else if (IS_ERR(uid_keyring_r)) {
ret = PTR_ERR(uid_keyring_r);
goto error;
} else {
uid_keyring = key_ref_to_ptr(uid_keyring_r);
}
/* get a default session keyring (which might also exist
* already) */
sprintf(buf, "_uid_ses.%u", uid);
session_keyring = find_keyring_by_name(buf, true);
if (IS_ERR(session_keyring)) {
session_keyring =
keyring_alloc(buf, user->uid, INVALID_GID,
/* Get a default session keyring (which might also exist already) */
snprintf(buf, sizeof(buf), "_uid_ses.%u", uid);
session_keyring_r = keyring_search(make_key_ref(reg_keyring, true),
&key_type_keyring, buf, false);
kdebug("_uid_ses %p", session_keyring_r);
if (session_keyring_r == ERR_PTR(-EAGAIN)) {
session_keyring = keyring_alloc(buf, cred->user->uid, INVALID_GID,
cred, user_keyring_perm,
KEY_ALLOC_UID_KEYRING |
KEY_ALLOC_IN_QUOTA,
@ -99,34 +135,83 @@ int install_user_keyrings(void)
goto error_release;
}
/* we install a link from the user session keyring to
* the user keyring */
/* We install a link from the user session keyring to
* the user keyring.
*/
ret = key_link(session_keyring, uid_keyring);
if (ret < 0)
goto error_release_both;
goto error_release_session;
/* And only then link the user-session keyring to the
* register.
*/
ret = key_link(reg_keyring, session_keyring);
if (ret < 0)
goto error_release_session;
} else if (IS_ERR(session_keyring_r)) {
ret = PTR_ERR(session_keyring_r);
goto error_release;
} else {
session_keyring = key_ref_to_ptr(session_keyring_r);
}
/* install the keyrings */
/* paired with READ_ONCE() */
smp_store_release(&user->uid_keyring, uid_keyring);
/* paired with READ_ONCE() */
smp_store_release(&user->session_keyring, session_keyring);
}
up_write(&user_ns->keyring_sem);
mutex_unlock(&key_user_keyring_mutex);
if (_user_session_keyring)
*_user_session_keyring = session_keyring;
else
key_put(session_keyring);
if (_user_keyring)
*_user_keyring = uid_keyring;
else
key_put(uid_keyring);
kleave(" = 0");
return 0;
error_release_both:
error_release_session:
key_put(session_keyring);
error_release:
key_put(uid_keyring);
error:
mutex_unlock(&key_user_keyring_mutex);
up_write(&user_ns->keyring_sem);
kleave(" = %d", ret);
return ret;
}
/*
* Get the user session keyring if it exists, but don't create it if it
* doesn't.
*/
struct key *get_user_session_keyring_rcu(const struct cred *cred)
{
struct key *reg_keyring = READ_ONCE(cred->user_ns->user_keyring_register);
key_ref_t session_keyring_r;
char buf[20];
struct keyring_search_context ctx = {
.index_key.type = &key_type_keyring,
.index_key.description = buf,
.cred = cred,
.match_data.cmp = key_default_cmp,
.match_data.raw_data = buf,
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.flags = KEYRING_SEARCH_DO_STATE_CHECK,
};
if (!reg_keyring)
return NULL;
ctx.index_key.desc_len = snprintf(buf, sizeof(buf), "_uid_ses.%u",
from_kuid(cred->user_ns,
cred->user->uid));
session_keyring_r = keyring_search_rcu(make_key_ref(reg_keyring, true),
&ctx);
if (IS_ERR(session_keyring_r))
return NULL;
return key_ref_to_ptr(session_keyring_r);
}
/*
* Install a thread keyring to the given credentials struct if it didn't have
* one already. This is allowed to overrun the quota.
@ -336,6 +421,7 @@ void key_fsgid_changed(struct cred *new_cred)
*/
key_ref_t search_cred_keyrings_rcu(struct keyring_search_context *ctx)
{
struct key *user_session;
key_ref_t key_ref, ret, err;
const struct cred *cred = ctx->cred;
@ -411,10 +497,11 @@ key_ref_t search_cred_keyrings_rcu(struct keyring_search_context *ctx)
}
}
/* or search the user-session keyring */
else if (READ_ONCE(cred->user->session_keyring)) {
key_ref = keyring_search_rcu(
make_key_ref(READ_ONCE(cred->user->session_keyring), 1),
else if ((user_session = get_user_session_keyring_rcu(cred))) {
key_ref = keyring_search_rcu(make_key_ref(user_session, 1),
ctx);
key_put(user_session);
if (!IS_ERR(key_ref))
goto found;
@ -527,10 +614,11 @@ 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;
struct key *key, *user_session;
key_ref_t key_ref, skey_ref;
int ret;
@ -579,20 +667,20 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
if (!ctx.cred->session_keyring) {
/* always install a session keyring upon access if one
* doesn't exist yet */
ret = install_user_keyrings();
ret = look_up_user_keyrings(NULL, &user_session);
if (ret < 0)
goto error;
if (lflags & KEY_LOOKUP_CREATE)
ret = join_session_keyring(NULL);
else
ret = install_session_keyring(
ctx.cred->user->session_keyring);
ret = install_session_keyring(user_session);
key_put(user_session);
if (ret < 0)
goto error;
goto reget_creds;
} else if (ctx.cred->session_keyring ==
READ_ONCE(ctx.cred->user->session_keyring) &&
} else if (test_bit(KEY_FLAG_UID_KEYRING,
&ctx.cred->session_keyring->flags) &&
lflags & KEY_LOOKUP_CREATE) {
ret = join_session_keyring(NULL);
if (ret < 0)
@ -606,26 +694,16 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
break;
case KEY_SPEC_USER_KEYRING:
if (!READ_ONCE(ctx.cred->user->uid_keyring)) {
ret = install_user_keyrings();
ret = look_up_user_keyrings(&key, NULL);
if (ret < 0)
goto error;
}
key = ctx.cred->user->uid_keyring;
__key_get(key);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_USER_SESSION_KEYRING:
if (!READ_ONCE(ctx.cred->user->session_keyring)) {
ret = install_user_keyrings();
ret = look_up_user_keyrings(NULL, &key);
if (ret < 0)
goto error;
}
key = ctx.cred->user->session_keyring;
__key_get(key);
key_ref = make_key_ref(key, 1);
break;
@ -874,7 +952,7 @@ void key_change_session_keyring(struct callback_head *twork)
*/
static int __init init_root_keyring(void)
{
return install_user_keyrings();
return look_up_user_keyrings(NULL, NULL);
}
late_initcall(init_root_keyring);

View File

@ -13,6 +13,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>
@ -117,7 +118,7 @@ static int call_sbin_request_key(struct key *authkey, void *aux)
struct request_key_auth *rka = get_request_key_auth(authkey);
const struct cred *cred = current_cred();
key_serial_t prkey, sskey;
struct key *key = rka->target_key, *keyring, *session;
struct key *key = rka->target_key, *keyring, *session, *user_session;
char *argv[9], *envp[3], uid_str[12], gid_str[12];
char key_str[12], keyring_str[3][12];
char desc[20];
@ -125,9 +126,9 @@ static int call_sbin_request_key(struct key *authkey, void *aux)
kenter("{%d},{%d},%s", key->serial, authkey->serial, rka->op);
ret = install_user_keyrings();
ret = look_up_user_keyrings(NULL, &user_session);
if (ret < 0)
goto error_alloc;
goto error_us;
/* allocate a new session keyring */
sprintf(desc, "_req.%u", key->serial);
@ -165,7 +166,7 @@ static int call_sbin_request_key(struct key *authkey, void *aux)
session = cred->session_keyring;
if (!session)
session = cred->user->session_keyring;
session = user_session;
sskey = session->serial;
sprintf(keyring_str[2], "%d", sskey);
@ -207,6 +208,8 @@ static int call_sbin_request_key(struct key *authkey, void *aux)
key_put(keyring);
error_alloc:
key_put(user_session);
error_us:
complete_request_key(authkey, ret);
kleave(" = %d", ret);
return ret;
@ -313,13 +316,15 @@ static int construct_get_dest_keyring(struct key **_dest_keyring)
/* fall through */
case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
dest_keyring =
key_get(READ_ONCE(cred->user->session_keyring));
ret = look_up_user_keyrings(NULL, &dest_keyring);
if (ret < 0)
return ret;
break;
case KEY_REQKEY_DEFL_USER_KEYRING:
dest_keyring =
key_get(READ_ONCE(cred->user->uid_keyring));
ret = look_up_user_keyrings(&dest_keyring, NULL);
if (ret < 0)
return ret;
break;
case KEY_REQKEY_DEFL_GROUP_KEYRING:
@ -525,16 +530,18 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx,
* 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.
@ -550,6 +557,7 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx,
*/
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,
@ -558,6 +566,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(),
@ -565,7 +574,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;
@ -663,9 +673,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
@ -676,8 +687,9 @@ 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,
struct key *request_key_tag(struct key_type *type,
const char *description,
struct key_tag *domain_tag,
const char *callout_info)
{
struct key *key;
@ -686,7 +698,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);
@ -697,12 +710,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.
@ -715,6 +729,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)
@ -722,7 +737,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);
@ -739,6 +755,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.
@ -746,10 +763,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(),

View File

@ -248,7 +248,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;