diff options
Diffstat (limited to 'security/keys/request_key.c')
-rw-r--r-- | security/keys/request_key.c | 47 |
1 files changed, 31 insertions, 16 deletions
diff --git a/security/keys/request_key.c b/security/keys/request_key.c index ac49c8aacbf0..f656e9c069e3 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -299,6 +299,7 @@ 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; @@ -306,6 +307,7 @@ static int construct_alloc_key(struct key_type *type, kenter("%s,%s,,,", type->name, description); + *_key = NULL; mutex_lock(&user->cons_lock); key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred, @@ -315,8 +317,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 @@ -328,11 +334,11 @@ 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)); @@ -341,27 +347,36 @@ static int construct_alloc_key(struct key_type *type, /* 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); - ret = 0; + key = key_ref_to_ptr(key_ref); if (dest_keyring) { - ret = __key_link(dest_keyring, key_ref_to_ptr(key_ref)); - up_write(&dest_keyring->sem); + 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); - if (ret < 0) { - key_ref_put(key_ref); - *_key = NULL; - kleave(" = %d [link]", ret); - return ret; - } - *_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); } |