diff options
Diffstat (limited to 'security/keys')
-rw-r--r-- | security/keys/gc.c | 9 | ||||
-rw-r--r-- | security/keys/internal.h | 12 | ||||
-rw-r--r-- | security/keys/key.c | 47 | ||||
-rw-r--r-- | security/keys/keyctl.c | 67 | ||||
-rw-r--r-- | security/keys/keyring.c | 312 | ||||
-rw-r--r-- | security/keys/permission.c | 2 | ||||
-rw-r--r-- | security/keys/proc.c | 2 | ||||
-rw-r--r-- | security/keys/process_keys.c | 9 | ||||
-rw-r--r-- | security/keys/request_key.c | 107 | ||||
-rw-r--r-- | security/keys/user_defined.c | 3 |
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 */ |