summaryrefslogtreecommitdiffstats
path: root/security/keys
diff options
context:
space:
mode:
Diffstat (limited to 'security/keys')
-rw-r--r--security/keys/gc.c9
-rw-r--r--security/keys/internal.h12
-rw-r--r--security/keys/key.c47
-rw-r--r--security/keys/keyctl.c67
-rw-r--r--security/keys/keyring.c312
-rw-r--r--security/keys/permission.c2
-rw-r--r--security/keys/proc.c2
-rw-r--r--security/keys/process_keys.c9
-rw-r--r--security/keys/request_key.c107
-rw-r--r--security/keys/user_defined.c3
10 files changed, 358 insertions, 212 deletions
diff --git a/security/keys/gc.c b/security/keys/gc.c
index 19902319d09..a46e825cbf0 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -77,10 +77,10 @@ static bool key_gc_keyring(struct key *keyring, time_t limit)
goto dont_gc;
/* scan the keyring looking for dead keys */
- klist = rcu_dereference_check(keyring->payload.subscriptions,
- lockdep_is_held(&key_serial_lock));
+ rcu_read_lock();
+ klist = rcu_dereference(keyring->payload.subscriptions);
if (!klist)
- goto dont_gc;
+ goto unlock_dont_gc;
for (loop = klist->nkeys - 1; loop >= 0; loop--) {
key = klist->keys[loop];
@@ -89,11 +89,14 @@ static bool key_gc_keyring(struct key *keyring, time_t limit)
goto do_gc;
}
+unlock_dont_gc:
+ rcu_read_unlock();
dont_gc:
kleave(" = false");
return false;
do_gc:
+ rcu_read_unlock();
key_gc_cursor = keyring->serial;
key_get(keyring);
spin_unlock(&key_serial_lock);
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 24ba0307b7a..38783dcf6c6 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -87,7 +87,16 @@ extern wait_queue_head_t request_key_conswq;
extern struct key_type *key_type_lookup(const char *type);
extern void key_type_put(struct key_type *ktype);
-extern int __key_link(struct key *keyring, struct key *key);
+extern int __key_link_begin(struct key *keyring,
+ const struct key_type *type,
+ const char *description,
+ struct keyring_list **_prealloc);
+extern int __key_link_check_live_key(struct key *keyring, struct key *key);
+extern void __key_link(struct key *keyring, struct key *key,
+ struct keyring_list **_prealloc);
+extern void __key_link_end(struct key *keyring,
+ struct key_type *type,
+ struct keyring_list *prealloc);
extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
const struct key_type *type,
@@ -115,6 +124,7 @@ extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
extern int install_user_keyrings(void);
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,
diff --git a/security/keys/key.c b/security/keys/key.c
index e50d264c9ad..c1eac8084ad 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -355,7 +355,7 @@ EXPORT_SYMBOL(key_alloc);
*/
int key_payload_reserve(struct key *key, size_t datalen)
{
- int delta = (int) datalen - key->datalen;
+ int delta = (int)datalen - key->datalen;
int ret = 0;
key_check(key);
@@ -398,7 +398,8 @@ static int __key_instantiate_and_link(struct key *key,
const void *data,
size_t datalen,
struct key *keyring,
- struct key *authkey)
+ struct key *authkey,
+ struct keyring_list **_prealloc)
{
int ret, awaken;
@@ -425,7 +426,7 @@ static int __key_instantiate_and_link(struct key *key,
/* and link it into the destination keyring */
if (keyring)
- ret = __key_link(keyring, key);
+ __key_link(keyring, key, _prealloc);
/* disable the authorisation key */
if (authkey)
@@ -453,15 +454,21 @@ int key_instantiate_and_link(struct key *key,
struct key *keyring,
struct key *authkey)
{
+ struct keyring_list *prealloc;
int ret;
- if (keyring)
- down_write(&keyring->sem);
+ if (keyring) {
+ ret = __key_link_begin(keyring, key->type, key->description,
+ &prealloc);
+ if (ret < 0)
+ return ret;
+ }
- ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey);
+ ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey,
+ &prealloc);
if (keyring)
- up_write(&keyring->sem);
+ __key_link_end(keyring, key->type, prealloc);
return ret;
@@ -478,8 +485,9 @@ int key_negate_and_link(struct key *key,
struct key *keyring,
struct key *authkey)
{
+ struct keyring_list *prealloc;
struct timespec now;
- int ret, awaken;
+ int ret, awaken, link_ret = 0;
key_check(key);
key_check(keyring);
@@ -488,7 +496,8 @@ int key_negate_and_link(struct key *key,
ret = -EBUSY;
if (keyring)
- down_write(&keyring->sem);
+ link_ret = __key_link_begin(keyring, key->type,
+ key->description, &prealloc);
mutex_lock(&key_construction_mutex);
@@ -508,8 +517,8 @@ int key_negate_and_link(struct key *key,
ret = 0;
/* and link it into the destination keyring */
- if (keyring)
- ret = __key_link(keyring, key);
+ if (keyring && link_ret == 0)
+ __key_link(keyring, key, &prealloc);
/* disable the authorisation key */
if (authkey)
@@ -519,13 +528,13 @@ int key_negate_and_link(struct key *key,
mutex_unlock(&key_construction_mutex);
if (keyring)
- up_write(&keyring->sem);
+ __key_link_end(keyring, key->type, prealloc);
/* wake up anyone waiting for a key to be constructed */
if (awaken)
wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT);
- return ret;
+ return ret == 0 ? link_ret : ret;
} /* end key_negate_and_link() */
@@ -749,6 +758,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_perm_t perm,
unsigned long flags)
{
+ struct keyring_list *prealloc;
const struct cred *cred = current_cred();
struct key_type *ktype;
struct key *keyring, *key = NULL;
@@ -775,7 +785,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
if (keyring->type != &key_type_keyring)
goto error_2;
- down_write(&keyring->sem);
+ ret = __key_link_begin(keyring, ktype, description, &prealloc);
+ if (ret < 0)
+ goto error_2;
/* if we're going to allocate a new key, we're going to have
* to modify the keyring */
@@ -817,7 +829,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
}
/* instantiate it and link it into the target keyring */
- ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL);
+ ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL,
+ &prealloc);
if (ret < 0) {
key_put(key);
key_ref = ERR_PTR(ret);
@@ -827,7 +840,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
error_3:
- up_write(&keyring->sem);
+ __key_link_end(keyring, ktype, prealloc);
error_2:
key_type_put(ktype);
error:
@@ -837,7 +850,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
/* we found a matching key, so we're going to try to update it
* - we can drop the locks first as we have the key pinned
*/
- up_write(&keyring->sem);
+ __key_link_end(keyring, ktype, prealloc);
key_type_put(ktype);
key_ref = __key_update(key_ref, payload, plen);
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index e9c2e7c584d..13074b45474 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -212,15 +212,15 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,
ret = key->serial;
key_put(key);
- error5:
+error5:
key_type_put(ktype);
- error4:
+error4:
key_ref_put(dest_ref);
- error3:
+error3:
kfree(callout_info);
- error2:
+error2:
kfree(description);
- error:
+error:
return ret;
} /* end sys_request_key() */
@@ -246,7 +246,7 @@ long keyctl_get_keyring_ID(key_serial_t id, int create)
ret = key_ref_to_ptr(key_ref)->serial;
key_ref_put(key_ref);
- error:
+error:
return ret;
} /* end keyctl_get_keyring_ID() */
@@ -275,7 +275,7 @@ long keyctl_join_session_keyring(const char __user *_name)
ret = join_session_keyring(name);
kfree(name);
- error:
+error:
return ret;
} /* end keyctl_join_session_keyring() */
@@ -322,9 +322,9 @@ long keyctl_update_key(key_serial_t id,
ret = key_update(key_ref, payload, plen);
key_ref_put(key_ref);
- error2:
+error2:
kfree(payload);
- error:
+error:
return ret;
} /* end keyctl_update_key() */
@@ -356,7 +356,7 @@ long keyctl_revoke_key(key_serial_t id)
ret = 0;
key_ref_put(key_ref);
- error:
+error:
return ret;
} /* end keyctl_revoke_key() */
@@ -381,7 +381,7 @@ long keyctl_keyring_clear(key_serial_t ringid)
ret = keyring_clear(key_ref_to_ptr(keyring_ref));
key_ref_put(keyring_ref);
- error:
+error:
return ret;
} /* end keyctl_keyring_clear() */
@@ -413,9 +413,9 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
ret = key_link(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref));
key_ref_put(key_ref);
- error2:
+error2:
key_ref_put(keyring_ref);
- error:
+error:
return ret;
} /* end keyctl_keyring_link() */
@@ -447,9 +447,9 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
ret = key_unlink(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref));
key_ref_put(key_ref);
- error2:
+error2:
key_ref_put(keyring_ref);
- error:
+error:
return ret;
} /* end keyctl_keyring_unlink() */
@@ -529,9 +529,9 @@ okay:
}
kfree(tmpbuf);
- error2:
+error2:
key_ref_put(key_ref);
- error:
+error:
return ret;
} /* end keyctl_describe_key() */
@@ -616,17 +616,17 @@ long keyctl_keyring_search(key_serial_t ringid,
ret = key_ref_to_ptr(key_ref)->serial;
- error6:
+error6:
key_ref_put(key_ref);
- error5:
+error5:
key_type_put(ktype);
- error4:
+error4:
key_ref_put(dest_ref);
- error3:
+error3:
key_ref_put(keyring_ref);
- error2:
+error2:
kfree(description);
- error:
+error:
return ret;
} /* end keyctl_keyring_search() */
@@ -673,7 +673,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
}
/* the key is probably readable - now try to read it */
- can_read_key:
+can_read_key:
ret = key_validate(key);
if (ret == 0) {
ret = -EOPNOTSUPP;
@@ -686,9 +686,9 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
}
}
- error2:
+error2:
key_put(key);
- error:
+error:
return ret;
} /* end keyctl_read_key() */
@@ -1269,7 +1269,7 @@ long keyctl_session_to_parent(void)
goto not_permitted;
/* the parent must be single threaded */
- if (atomic_read(&parent->signal->count) != 1)
+ if (!thread_group_empty(parent))
goto not_permitted;
/* the parent and the child must have different session keyrings or
@@ -1282,26 +1282,19 @@ long keyctl_session_to_parent(void)
/* the parent must have the same effective ownership and mustn't be
* SUID/SGID */
- if (pcred-> uid != mycred->euid ||
+ if (pcred->uid != mycred->euid ||
pcred->euid != mycred->euid ||
pcred->suid != mycred->euid ||
- pcred-> gid != mycred->egid ||
+ pcred->gid != mycred->egid ||
pcred->egid != mycred->egid ||
pcred->sgid != mycred->egid)
goto not_permitted;
/* the keyrings must have the same UID */
- if (pcred ->tgcred->session_keyring->uid != mycred->euid ||
+ if (pcred->tgcred->session_keyring->uid != mycred->euid ||
mycred->tgcred->session_keyring->uid != mycred->euid)
goto not_permitted;
- /* the LSM must permit the replacement of the parent's keyring with the
- * keyring from this process */
- ret = security_key_session_to_parent(mycred, pcred,
- key_ref_to_ptr(keyring_r));
- if (ret < 0)
- goto not_permitted;
-
/* if there's an already pending keyring replacement, then we replace
* that */
oldcred = parent->replacement_session_keyring;
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index e814d2109f8..d37f713e73c 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -17,9 +17,14 @@
#include <linux/seq_file.h>
#include <linux/err.h>
#include <keys/keyring-type.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "internal.h"
+#define rcu_dereference_locked_keyring(keyring) \
+ (rcu_dereference_protected( \
+ (keyring)->payload.subscriptions, \
+ rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
+
/*
* when plumbing the depths of the key tree, this sets a hard limit set on how
* deep we're willing to go
@@ -39,7 +44,7 @@ static inline unsigned keyring_hash(const char *desc)
unsigned bucket = 0;
for (; *desc; desc++)
- bucket += (unsigned char) *desc;
+ bucket += (unsigned char)*desc;
return bucket & (KEYRING_NAME_HASH_SIZE - 1);
}
@@ -170,12 +175,10 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m)
{
struct keyring_list *klist;
- if (keyring->description) {
+ if (keyring->description)
seq_puts(m, keyring->description);
- }
- else {
+ else
seq_puts(m, "[anon]");
- }
rcu_read_lock();
klist = rcu_dereference(keyring->payload.subscriptions);
@@ -201,8 +204,7 @@ static long keyring_read(const struct key *keyring,
int loop, ret;
ret = 0;
- klist = rcu_dereference(keyring->payload.subscriptions);
-
+ klist = rcu_dereference_locked_keyring(keyring);
if (klist) {
/* calculate how much data we could return */
qty = klist->nkeys * sizeof(key_serial_t);
@@ -237,7 +239,7 @@ static long keyring_read(const struct key *keyring,
ret = qty;
}
- error:
+error:
return ret;
} /* end keyring_read() */
@@ -306,7 +308,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
key_check(keyring);
/* top keyring must have search permission to begin the search */
- err = key_task_permission(keyring_ref, cred, KEY_SEARCH);
+ err = key_task_permission(keyring_ref, cred, KEY_SEARCH);
if (err < 0) {
key_ref = ERR_PTR(err);
goto error;
@@ -508,7 +510,7 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref,
rcu_read_unlock();
return ERR_PTR(-ENOKEY);
- found:
+found:
atomic_inc(&key->usage);
rcu_read_unlock();
return make_key_ref(key, possessed);
@@ -526,9 +528,8 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
struct key *keyring;
int bucket;
- keyring = ERR_PTR(-EINVAL);
if (!name)
- goto error;
+ return ERR_PTR(-EINVAL);
bucket = keyring_hash(name);
@@ -555,17 +556,18 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
KEY_SEARCH) < 0)
continue;
- /* we've got a match */
- atomic_inc(&keyring->usage);
- read_unlock(&keyring_name_lock);
- goto error;
+ /* we've got a match but we might end up racing with
+ * key_cleanup() if the keyring is currently 'dead'
+ * (ie. it has a zero usage count) */
+ if (!atomic_inc_not_zero(&keyring->usage))
+ continue;
+ goto out;
}
}
- read_unlock(&keyring_name_lock);
keyring = ERR_PTR(-ENOKEY);
-
- error:
+out:
+ read_unlock(&keyring_name_lock);
return keyring;
} /* end find_keyring_by_name() */
@@ -598,7 +600,7 @@ static int keyring_detect_cycle(struct key *A, struct key *B)
sp = 0;
/* start processing a new keyring */
- descend:
+descend:
if (test_bit(KEY_FLAG_REVOKED, &subtree->flags))
goto not_this_keyring;
@@ -607,7 +609,7 @@ static int keyring_detect_cycle(struct key *A, struct key *B)
goto not_this_keyring;
kix = 0;
- ascend:
+ascend:
/* iterate through the remaining keys in this keyring */
for (; kix < keylist->nkeys; kix++) {
key = keylist->keys[kix];
@@ -633,7 +635,7 @@ static int keyring_detect_cycle(struct key *A, struct key *B)
/* the keyring we're looking at was disqualified or didn't contain a
* matching key */
- not_this_keyring:
+not_this_keyring:
if (sp > 0) {
/* resume the checking of a keyring higher up in the tree */
sp--;
@@ -644,34 +646,20 @@ static int keyring_detect_cycle(struct key *A, struct key *B)
ret = 0; /* no cycles detected */
- error:
+error:
rcu_read_unlock();
return ret;
- too_deep:
+too_deep:
ret = -ELOOP;
goto error;
- cycle_detected:
+cycle_detected:
ret = -EDEADLK;
goto error;
} /* end keyring_detect_cycle() */
-/*****************************************************************************/
-/*
- * dispose of a keyring list after the RCU grace period
- */
-static void keyring_link_rcu_disposal(struct rcu_head *rcu)
-{
- struct keyring_list *klist =
- container_of(rcu, struct keyring_list, rcu);
-
- kfree(klist);
-
-} /* end keyring_link_rcu_disposal() */
-
-/*****************************************************************************/
/*
* dispose of a keyring list after the RCU grace period, freeing the unlinked
* key
@@ -681,56 +669,51 @@ static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
struct keyring_list *klist =
container_of(rcu, struct keyring_list, rcu);
- key_put(klist->keys[klist->delkey]);
+ if (klist->delkey != USHRT_MAX)
+ key_put(klist->keys[klist->delkey]);
kfree(klist);
+}
-} /* end keyring_unlink_rcu_disposal() */
-
-/*****************************************************************************/
/*
- * link a key into to a keyring
- * - must be called with the keyring's semaphore write-locked
- * - discard already extant link to matching key if there is one
+ * preallocate memory so that a key can be linked into to a keyring
*/
-int __key_link(struct key *keyring, struct key *key)
+int __key_link_begin(struct key *keyring, const struct key_type *type,
+ const char *description,
+ struct keyring_list **_prealloc)
+ __acquires(&keyring->sem)
{
struct keyring_list *klist, *nklist;
unsigned max;
size_t size;
int loop, ret;
- ret = -EKEYREVOKED;
- if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
- goto error;
+ kenter("%d,%s,%s,", key_serial(keyring), type->name, description);
- ret = -ENOTDIR;
if (keyring->type != &key_type_keyring)
- goto error;
+ return -ENOTDIR;
- /* serialise link/link calls to prevent parallel calls causing a
- * cycle when applied to two keyring in opposite orders */
- down_write(&keyring_serialise_link_sem);
+ down_write(&keyring->sem);
- /* check that we aren't going to create a cycle adding one keyring to
- * another */
- if (key->type == &key_type_keyring) {
- ret = keyring_detect_cycle(keyring, key);
- if (ret < 0)
- goto error2;
- }
+ ret = -EKEYREVOKED;
+ if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
+ goto error_krsem;
- /* see if there's a matching key we can displace */
- klist = keyring->payload.subscriptions;
+ /* serialise link/link calls to prevent parallel calls causing a cycle
+ * when linking two keyring in opposite orders */
+ if (type == &key_type_keyring)
+ down_write(&keyring_serialise_link_sem);
- if (klist && klist->nkeys > 0) {
- struct key_type *type = key->type;
+ klist = rcu_dereference_locked_keyring(keyring);
+ /* see if there's a matching key we can displace */
+ if (klist && klist->nkeys > 0) {
for (loop = klist->nkeys - 1; loop >= 0; loop--) {
if (klist->keys[loop]->type == type &&
strcmp(klist->keys[loop]->description,
- key->description) == 0
+ description) == 0
) {
- /* found a match - replace with new key */
+ /* found a match - we'll replace this one with
+ * the new key */
size = sizeof(struct key *) * klist->maxkeys;
size += sizeof(*klist);
BUG_ON(size > PAGE_SIZE);
@@ -738,22 +721,10 @@ int __key_link(struct key *keyring, struct key *key)
ret = -ENOMEM;
nklist = kmemdup(klist, size, GFP_KERNEL);
if (!nklist)
- goto error2;
-
- /* replace matched key */
- atomic_inc(&key->usage);
- nklist->keys[loop] = key;
-
- rcu_assign_pointer(
- keyring->payload.subscriptions,
- nklist);
-
- /* dispose of the old keyring list and the
- * displaced key */
- klist->delkey = loop;
- call_rcu(&klist->rcu,
- keyring_unlink_rcu_disposal);
+ goto error_sem;
+ /* note replacement slot */
+ klist->delkey = nklist->delkey = loop;
goto done;
}
}
@@ -763,90 +734,167 @@ int __key_link(struct key *keyring, struct key *key)
ret = key_payload_reserve(keyring,
keyring->datalen + KEYQUOTA_LINK_BYTES);
if (ret < 0)
- goto error2;
-
- klist = keyring->payload.subscriptions;
+ goto error_sem;
if (klist && klist->nkeys < klist->maxkeys) {
- /* there's sufficient slack space to add directly */
- atomic_inc(&key->usage);
-
- klist->keys[klist->nkeys] = key;
- smp_wmb();
- klist->nkeys++;
- smp_wmb();
- }
- else {
+ /* there's sufficient slack space to append directly */
+ nklist = NULL;
+ } else {
/* grow the key list */
max = 4;
if (klist)
max += klist->maxkeys;
ret = -ENFILE;
- if (max > 65535)
- goto error3;
+ if (max > USHRT_MAX - 1)
+ goto error_quota;
size = sizeof(*klist) + sizeof(struct key *) * max;
if (size > PAGE_SIZE)
- goto error3;
+ goto error_quota;
ret = -ENOMEM;
nklist = kmalloc(size, GFP_KERNEL);
if (!nklist)
- goto error3;
- nklist->maxkeys = max;
- nklist->nkeys = 0;
+ goto error_quota;
+ nklist->maxkeys = max;
if (klist) {
- nklist->nkeys = klist->nkeys;
- memcpy(nklist->keys,
- klist->keys,
+ memcpy(nklist->keys, klist->keys,
sizeof(struct key *) * klist->nkeys);
+ nklist->delkey = klist->nkeys;
+ nklist->nkeys = klist->nkeys + 1;
+ klist->delkey = USHRT_MAX;
+ } else {
+ nklist->nkeys = 1;
+ nklist->delkey = 0;
}
/* add the key into the new space */
- atomic_inc(&key->usage);
- nklist->keys[nklist->nkeys++] = key;
-
- rcu_assign_pointer(keyring->payload.subscriptions, nklist);
-
- /* dispose of the old keyring list */
- if (klist)
- call_rcu(&klist->rcu, keyring_link_rcu_disposal);
+ nklist->keys[nklist->delkey] = NULL;
}
done:
- ret = 0;
-error2:
- up_write(&keyring_serialise_link_sem);
-error:
- return ret;
+ *_prealloc = nklist;
+ kleave(" = 0");
+ return 0;
-error3:
+error_quota:
/* undo the quota changes */
key_payload_reserve(keyring,
keyring->datalen - KEYQUOTA_LINK_BYTES);
- goto error2;
+error_sem:
+ if (type == &key_type_keyring)
+ up_write(&keyring_serialise_link_sem);
+error_krsem:
+ up_write(&keyring->sem);
+ kleave(" = %d", ret);
+ return ret;
+}
-} /* end __key_link() */
+/*
+ * check already instantiated keys aren't going to be a problem
+ * - the caller must have called __key_link_begin()
+ * - don't need to call this for keys that were created since __key_link_begin()
+ * was called
+ */
+int __key_link_check_live_key(struct key *keyring, struct key *key)
+{
+ if (key->type == &key_type_keyring)
+ /* check that we aren't going to create a cycle by linking one
+ * keyring to another */
+ return keyring_detect_cycle(keyring, key);
+ return 0;
+}
+
+/*
+ * link a key into to a keyring
+ * - must be called with __key_link_begin() having being called
+ * - discard already extant link to matching key if there is one
+ */
+void __key_link(struct key *keyring, struct key *key,
+ struct keyring_list **_prealloc)
+{
+ struct keyring_list *klist, *nklist;
+
+ nklist = *_prealloc;
+ *_prealloc = NULL;
+
+ kenter("%d,%d,%p", keyring->serial, key->serial, nklist);
+
+ klist = rcu_dereference_protected(keyring->payload.subscriptions,
+ rwsem_is_locked(&keyring->sem));
+
+ atomic_inc(&key->usage);
+
+ /* there's a matching key we can displace or an empty slot in a newly
+ * allocated list we can fill */
+ if (nklist) {
+ kdebug("replace %hu/%hu/%hu",
+ nklist->delkey, nklist->nkeys, nklist->maxkeys);
+
+ nklist->keys[nklist->delkey] = key;
+
+ rcu_assign_pointer(keyring->payload.subscriptions, nklist);
+
+ /* dispose of the old keyring list and, if there was one, the
+ * displaced key */
+ if (klist) {
+ kdebug("dispose %hu/%hu/%hu",
+ klist->delkey, klist->nkeys, klist->maxkeys);
+ call_rcu(&klist->rcu, keyring_unlink_rcu_disposal);
+ }
+ } else {
+ /* there's sufficient slack space to append directly */
+ klist->keys[klist->nkeys] = key;
+ smp_wmb();
+ klist->nkeys++;
+ }
+}
+
+/*
+ * finish linking a key into to a keyring
+ * - must be called with __key_link_begin() having being called
+ */
+void __key_link_end(struct key *keyring, struct key_type *type,
+ struct keyring_list *prealloc)
+ __releases(&keyring->sem)
+{
+ BUG_ON(type == NULL);
+ BUG_ON(type->name == NULL);
+ kenter("%d,%s,%p", keyring->serial, type->name, prealloc);
+
+ if (type == &key_type_keyring)
+ up_write(&keyring_serialise_link_sem);
+
+ if (prealloc) {
+ kfree(prealloc);
+ key_payload_reserve(keyring,
+ keyring->datalen - KEYQUOTA_LINK_BYTES);
+ }
+ up_write(&keyring->sem);
+}
-/*****************************************************************************/
/*
* link a key to a keyring
*/
int key_link(struct key *keyring, struct key *key)
{
+ struct keyring_list *prealloc;
int ret;
key_check(keyring);
key_check(key);
- down_write(&keyring->sem);
- ret = __key_link(keyring, key);
- up_write(&keyring->sem);
+ ret = __key_link_begin(keyring, key->type, key->description, &prealloc);
+ if (ret == 0) {
+ ret = __key_link_check_live_key(keyring, key);
+ if (ret == 0)
+ __key_link(keyring, key, &prealloc);
+ __key_link_end(keyring, key->type, prealloc);
+ }
return ret;
-
-} /* end key_link() */
+}
EXPORT_SYMBOL(key_link);
@@ -868,7 +916,7 @@ int key_unlink(struct key *keyring, struct key *key)
down_write(&keyring->sem);
- klist = keyring->payload.subscriptions;
+ klist = rcu_dereference_locked_keyring(keyring);
if (klist) {
/* search the keyring for the key */
for (loop = 0; loop < klist->nkeys; loop++)
@@ -959,7 +1007,7 @@ int keyring_clear(struct key *keyring)
/* detach the pointer block with the locks held */
down_write(&keyring->sem);
- klist = keyring->payload.subscriptions;
+ klist = rcu_dereference_locked_keyring(keyring);
if (klist) {
/* adjust the quota */
key_payload_reserve(keyring,
@@ -991,7 +1039,9 @@ EXPORT_SYMBOL(keyring_clear);
*/
static void keyring_revoke(struct key *keyring)
{
- struct keyring_list *klist = keyring->payload.subscriptions;
+ struct keyring_list *klist;
+
+ klist = rcu_dereference_locked_keyring(keyring);
/* adjust the quota */
key_payload_reserve(keyring, 0);
@@ -1025,7 +1075,7 @@ void keyring_gc(struct key *keyring, time_t limit)
down_write(&keyring->sem);
- klist = keyring->payload.subscriptions;
+ klist = rcu_dereference_locked_keyring(keyring);
if (!klist)
goto no_klist;
diff --git a/security/keys/permission.c b/security/keys/permission.c
index 0ed802c9e69..28645502cd0 100644
--- a/security/keys/permission.c
+++ b/security/keys/permission.c
@@ -109,7 +109,7 @@ int key_validate(struct key *key)
}
}
- error:
+error:
return ret;
} /* end key_validate() */
diff --git a/security/keys/proc.c b/security/keys/proc.c
index 706d63f4f18..068b66ea2f1 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -306,7 +306,7 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
{
(*_pos)++;
- return key_user_next((struct rb_node *) v);
+ return key_user_next((struct rb_node *)v);
}
static void proc_key_users_stop(struct seq_file *p, void *v)
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 06c2ccf26ed..6b8e4ff4cc6 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -216,8 +216,7 @@ static int install_process_keyring(void)
/*
* install a session keyring directly to a credentials struct
*/
-static int install_session_keyring_to_cred(struct cred *cred,
- struct key *keyring)
+int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
{
unsigned long flags;
struct key *old;
@@ -508,7 +507,7 @@ try_again:
ret = install_thread_keyring();
if (ret < 0) {
- key = ERR_PTR(ret);
+ key_ref = ERR_PTR(ret);
goto error;
}
goto reget_creds;
@@ -526,7 +525,7 @@ try_again:
ret = install_process_keyring();
if (ret < 0) {
- key = ERR_PTR(ret);
+ key_ref = ERR_PTR(ret);
goto error;
}
goto reget_creds;
@@ -585,7 +584,7 @@ try_again:
case KEY_SPEC_GROUP_KEYRING:
/* group keyrings are not yet supported */
- key = ERR_PTR(-EINVAL);
+ key_ref = ERR_PTR(-EINVAL);
goto error;
case KEY_SPEC_REQKEY_AUTH_KEY:
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 03fe63ed55b..f5ec9ac5d57 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -58,6 +58,38 @@ void complete_request_key(struct key_construction *cons, int error)
}
EXPORT_SYMBOL(complete_request_key);
+static int umh_keys_init(struct subprocess_info *info)
+{
+ struct cred *cred = (struct cred*)current_cred();
+ struct key *keyring = info->data;
+ /*
+ * This is called in context of freshly forked kthread before
+ * kernel_execve(), we can just change our ->session_keyring.
+ */
+ return install_session_keyring_to_cred(cred, keyring);
+}
+
+static void umh_keys_cleanup(struct subprocess_info *info)
+{
+ struct key *keyring = info->data;
+ key_put(keyring);
+}
+
+static int call_usermodehelper_keys(char *path, char **argv, char **envp,
+ struct key *session_keyring, enum umh_wait wait)
+{
+ gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
+ struct subprocess_info *info =
+ call_usermodehelper_setup(path, argv, envp, gfp_mask);
+
+ if (!info)
+ return -ENOMEM;
+
+ call_usermodehelper_setfns(info, umh_keys_init, umh_keys_cleanup,
+ key_get(session_keyring));
+ return call_usermodehelper_exec(info, wait);
+}
+
/*
* request userspace finish the construction of a key
* - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>"
@@ -68,7 +100,8 @@ static int call_sbin_request_key(struct key_construction *cons,
{
const struct cred *cred = current_cred();
key_serial_t prkey, sskey;
- struct key *key = cons->key, *authkey = cons->authkey, *keyring;
+ struct key *key = cons->key, *authkey = cons->authkey, *keyring,
+ *session;
char *argv[9], *envp[3], uid_str[12], gid_str[12];
char key_str[12], keyring_str[3][12];
char desc[20];
@@ -93,7 +126,7 @@ static int call_sbin_request_key(struct key_construction *cons,
}
/* attach the auth key to the session keyring */
- ret = __key_link(keyring, authkey);
+ ret = key_link(keyring, authkey);
if (ret < 0)
goto error_link;
@@ -112,10 +145,12 @@ static int call_sbin_request_key(struct key_construction *cons,
if (cred->tgcred->process_keyring)
prkey = cred->tgcred->process_keyring->serial;
- if (cred->tgcred->session_keyring)
- sskey = rcu_dereference(cred->tgcred->session_keyring)->serial;
- else
- sskey = cred->user->session_keyring->serial;
+ rcu_read_lock();
+ session = rcu_dereference(cred->tgcred->session_keyring);
+ if (!session)
+ session = cred->user->session_keyring;
+ sskey = session->serial;
+ rcu_read_unlock();
sprintf(keyring_str[2], "%d", sskey);
@@ -296,12 +331,15 @@ static int construct_alloc_key(struct key_type *type,
struct key_user *user,
struct key **_key)
{
+ struct keyring_list *prealloc;
const struct cred *cred = current_cred();
struct key *key;
key_ref_t key_ref;
+ int ret;
kenter("%s,%s,,,", type->name, description);
+ *_key = NULL;
mutex_lock(&user->cons_lock);
key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred,
@@ -311,8 +349,12 @@ static int construct_alloc_key(struct key_type *type,
set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
- if (dest_keyring)
- down_write(&dest_keyring->sem);
+ if (dest_keyring) {
+ ret = __key_link_begin(dest_keyring, type, description,
+ &prealloc);
+ if (ret < 0)
+ goto link_prealloc_failed;
+ }
/* attach the key to the destination keyring under lock, but we do need
* to do another check just in case someone beat us to it whilst we
@@ -324,29 +366,49 @@ static int construct_alloc_key(struct key_type *type,
goto key_already_present;
if (dest_keyring)
- __key_link(dest_keyring, key);
+ __key_link(dest_keyring, key, &prealloc);
mutex_unlock(&key_construction_mutex);
if (dest_keyring)
- up_write(&dest_keyring->sem);
+ __key_link_end(dest_keyring, type, prealloc);
mutex_unlock(&user->cons_lock);
*_key = key;
kleave(" = 0 [%d]", key_serial(key));
return 0;
+ /* the key is now present - we tell the caller that we found it by
+ * returning -EINPROGRESS */
key_already_present:
+ key_put(key);
mutex_unlock(&key_construction_mutex);
- if (dest_keyring)
- up_write(&dest_keyring->sem);
+ key = key_ref_to_ptr(key_ref);
+ if (dest_keyring) {
+ ret = __key_link_check_live_key(dest_keyring, key);
+ if (ret == 0)
+ __key_link(dest_keyring, key, &prealloc);
+ __key_link_end(dest_keyring, type, prealloc);
+ if (ret < 0)
+ goto link_check_failed;
+ }
mutex_unlock(&user->cons_lock);
- key_put(key);
- *_key = key = key_ref_to_ptr(key_ref);
+ *_key = key;
kleave(" = -EINPROGRESS [%d]", key_serial(key));
return -EINPROGRESS;
+link_check_failed:
+ mutex_unlock(&user->cons_lock);
+ key_put(key);
+ kleave(" = %d [linkcheck]", ret);
+ return ret;
+
+link_prealloc_failed:
+ up_write(&dest_keyring->sem);
+ mutex_unlock(&user->cons_lock);
+ kleave(" = %d [prelink]", ret);
+ return ret;
+
alloc_failed:
mutex_unlock(&user->cons_lock);
- *_key = NULL;
kleave(" = %ld", PTR_ERR(key));
return PTR_ERR(key);
}
@@ -385,6 +447,10 @@ static struct key *construct_key_and_link(struct key_type *type,
kdebug("cons failed");
goto construction_failed;
}
+ } else if (ret == -EINPROGRESS) {
+ ret = 0;
+ } else {
+ key = ERR_PTR(ret);
}
key_put(dest_keyring);
@@ -417,6 +483,7 @@ struct key *request_key_and_link(struct key_type *type,
const struct cred *cred = current_cred();
struct key *key;
key_ref_t key_ref;
+ int ret;
kenter("%s,%s,%p,%zu,%p,%p,%lx",
type->name, description, callout_info, callout_len, aux,
@@ -428,6 +495,16 @@ struct key *request_key_and_link(struct key_type *type,
if (!IS_ERR(key_ref)) {
key = key_ref_to_ptr(key_ref);
+ if (dest_keyring) {
+ construct_get_dest_keyring(&dest_keyring);
+ ret = key_link(dest_keyring, key);
+ key_put(dest_keyring);
+ if (ret < 0) {
+ key_put(key);
+ key = ERR_PTR(ret);
+ goto error;
+ }
+ }
} else if (PTR_ERR(key_ref) != -EAGAIN) {
key = ERR_CAST(key_ref);
} else {
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index 7c687d56822..e9aa0792965 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -199,7 +199,8 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen)
struct user_key_payload *upayload;
long ret;
- upayload = rcu_dereference(key->payload.data);
+ upayload = rcu_dereference_protected(
+ key->payload.data, rwsem_is_locked(&((struct key *)key)->sem));
ret = upayload->datalen;
/* we can return the data as is */