From 2e21865faf4fd7ca99eb2ace072c6d618059e342 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 22 May 2019 14:06:51 +0100 Subject: keys: sparse: Fix key_fs[ug]id_changed() Sparse warnings are incurred by key_fs[ug]id_changed() due to unprotected accesses of tsk->cred, which is marked __rcu. Fix this by passing the new cred struct to these functions from commit_creds() rather than the task pointer. Signed-off-by: David Howells Reviewed-by: James Morris --- security/keys/process_keys.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'security/keys') diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index f05f7125a7d5..ba5d3172cafe 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -293,28 +293,26 @@ static int install_session_keyring(struct key *keyring) /* * Handle the fsuid changing. */ -void key_fsuid_changed(struct task_struct *tsk) +void key_fsuid_changed(struct cred *new_cred) { /* update the ownership of the thread keyring */ - BUG_ON(!tsk->cred); - if (tsk->cred->thread_keyring) { - down_write(&tsk->cred->thread_keyring->sem); - tsk->cred->thread_keyring->uid = tsk->cred->fsuid; - up_write(&tsk->cred->thread_keyring->sem); + if (new_cred->thread_keyring) { + down_write(&new_cred->thread_keyring->sem); + new_cred->thread_keyring->uid = new_cred->fsuid; + up_write(&new_cred->thread_keyring->sem); } } /* * Handle the fsgid changing. */ -void key_fsgid_changed(struct task_struct *tsk) +void key_fsgid_changed(struct cred *new_cred) { /* update the ownership of the thread keyring */ - BUG_ON(!tsk->cred); - if (tsk->cred->thread_keyring) { - down_write(&tsk->cred->thread_keyring->sem); - tsk->cred->thread_keyring->gid = tsk->cred->fsgid; - up_write(&tsk->cred->thread_keyring->sem); + if (new_cred->thread_keyring) { + down_write(&new_cred->thread_keyring->sem); + new_cred->thread_keyring->gid = new_cred->fsgid; + up_write(&new_cred->thread_keyring->sem); } } -- cgit v1.2.3 From 7936d16df9a56c993c23385ea9c2785127f81711 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 22 May 2019 14:09:29 +0100 Subject: keys: sparse: Fix incorrect RCU accesses Fix a pair of accesses that should be using RCU protection. rcu_dereference_protected() is needed to access task_struct::real_parent. current_cred() should be used to access current->cred. Signed-off-by: David Howells Reviewed-by: James Morris --- security/keys/keyctl.c | 3 ++- security/keys/request_key_auth.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'security/keys') diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 3e4053a217c3..0f947bcbad46 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1524,7 +1524,8 @@ long keyctl_session_to_parent(void) ret = -EPERM; oldwork = NULL; - parent = me->real_parent; + parent = rcu_dereference_protected(me->real_parent, + lockdep_is_held(&tasklist_lock)); /* the parent mustn't be init and mustn't be a kernel thread */ if (parent->pid <= 1 || !parent->mm) diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index bda6201c6c45..572c7a60473a 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -152,7 +152,7 @@ struct key *request_key_auth_new(struct key *target, const char *op, struct key *dest_keyring) { struct request_key_auth *rka, *irka; - const struct cred *cred = current->cred; + const struct cred *cred = current_cred(); struct key *authkey = NULL; char desc[20]; int ret = -ENOMEM; -- cgit v1.2.3 From 9fd165379eff957ec3a9f5059c9d5ed05e02e61a Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 22 May 2019 13:30:56 +0100 Subject: keys: sparse: Fix kdoc mismatches Fix some kdoc argument description mismatches reported by sparse and give keyring_restrict() a description. Signed-off-by: David Howells Reviewed-by: James Morris cc: Mat Martineau --- security/keys/keyring.c | 10 +++++++--- security/keys/request_key.c | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'security/keys') diff --git a/security/keys/keyring.c b/security/keys/keyring.c index e14f09e3a4b0..5b218b270598 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -520,7 +520,7 @@ EXPORT_SYMBOL(keyring_alloc); * @keyring: The keyring being added to. * @type: The type of key being added. * @payload: The payload of the key intended to be added. - * @data: Additional data for evaluating restriction. + * @restriction_key: Keys providing additional data for evaluating restriction. * * Reject the addition of any links to a keyring. It can be overridden by * passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when @@ -976,9 +976,13 @@ static bool keyring_detect_restriction_cycle(const struct key *dest_keyring, /** * keyring_restrict - Look up and apply a restriction to a keyring - * - * @keyring: The keyring to be restricted + * @keyring_ref: The keyring to be restricted + * @type: The key type that will provide the restriction checker. * @restriction: The restriction options to apply to the keyring + * + * Look up a keyring and apply a restriction to it. The restriction is managed + * by the specific key type, but can be configured by the options specified in + * the restriction string. */ int keyring_restrict(key_ref_t keyring_ref, const char *type, const char *restriction) diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 75d87f9e0f49..1f234b019437 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -24,7 +24,7 @@ /** * complete_request_key - Complete the construction of a key. - * @auth_key: The authorisation key. + * @authkey: The authorisation key. * @error: The success or failute of the construction. * * Complete the attempt to construct a key. The key will be negated -- cgit v1.2.3 From 3be59f74512e37f4d4243a5d0831970e2a009206 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 30 May 2019 11:40:24 +0100 Subject: keys: Change keyring_serialise_link_sem to a mutex Change keyring_serialise_link_sem to a mutex as it's only ever write-locked. Signed-off-by: David Howells --- security/keys/keyring.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'security/keys') diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 5b218b270598..ca6694ba1773 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -100,7 +100,7 @@ EXPORT_SYMBOL(key_type_keyring); * Semaphore to serialise link/link calls to prevent two link calls in parallel * introducing a cycle. */ -static DECLARE_RWSEM(keyring_serialise_link_sem); +static DEFINE_MUTEX(keyring_serialise_link_lock); /* * Publish the name of a keyring so that it can be found by name (if it has @@ -1206,7 +1206,7 @@ int __key_link_begin(struct key *keyring, const struct keyring_index_key *index_key, struct assoc_array_edit **_edit) __acquires(&keyring->sem) - __acquires(&keyring_serialise_link_sem) + __acquires(&keyring_serialise_link_lock) { struct assoc_array_edit *edit; int ret; @@ -1228,7 +1228,7 @@ int __key_link_begin(struct key *keyring, /* serialise link/link calls to prevent parallel calls causing a cycle * when linking two keyring in opposite orders */ if (index_key->type == &key_type_keyring) - down_write(&keyring_serialise_link_sem); + mutex_lock(&keyring_serialise_link_lock); /* Create an edit script that will insert/replace the key in the * keyring tree. @@ -1260,7 +1260,7 @@ error_cancel: assoc_array_cancel_edit(edit); error_sem: if (index_key->type == &key_type_keyring) - up_write(&keyring_serialise_link_sem); + mutex_unlock(&keyring_serialise_link_lock); error_krsem: up_write(&keyring->sem); kleave(" = %d", ret); @@ -1307,13 +1307,13 @@ void __key_link_end(struct key *keyring, const struct keyring_index_key *index_key, struct assoc_array_edit *edit) __releases(&keyring->sem) - __releases(&keyring_serialise_link_sem) + __releases(&keyring_serialise_link_lock) { BUG_ON(index_key->type == NULL); kenter("%d,%s,", keyring->serial, index_key->type->name); if (index_key->type == &key_type_keyring) - up_write(&keyring_serialise_link_sem); + mutex_unlock(&keyring_serialise_link_lock); if (edit) { if (!edit->dead_leaf) { -- cgit v1.2.3 From eb0f68cb7042fd5cff0d6b57966a93049fb2495b Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 30 May 2019 14:19:20 +0100 Subject: keys: Break bits out of key_unlink() Break bits out of key_unlink() into helper functions so that they can be used in implementing key_move(). Signed-off-by: David Howells --- security/keys/keyring.c | 88 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 21 deletions(-) (limited to 'security/keys') diff --git a/security/keys/keyring.c b/security/keys/keyring.c index ca6694ba1773..6990c7761eaa 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -1382,6 +1382,65 @@ int key_link(struct key *keyring, struct key *key) } EXPORT_SYMBOL(key_link); +/* + * Lock a keyring for unlink. + */ +static int __key_unlink_lock(struct key *keyring) + __acquires(&keyring->sem) +{ + if (keyring->type != &key_type_keyring) + return -ENOTDIR; + + down_write(&keyring->sem); + return 0; +} + +/* + * Begin the process of unlinking a key from a keyring. + */ +static int __key_unlink_begin(struct key *keyring, struct key *key, + struct assoc_array_edit **_edit) +{ + struct assoc_array_edit *edit; + + BUG_ON(*_edit != NULL); + + edit = assoc_array_delete(&keyring->keys, &keyring_assoc_array_ops, + &key->index_key); + if (IS_ERR(edit)) + return PTR_ERR(edit); + + if (!edit) + return -ENOENT; + + *_edit = edit; + return 0; +} + +/* + * Apply an unlink change. + */ +static void __key_unlink(struct key *keyring, struct key *key, + struct assoc_array_edit **_edit) +{ + assoc_array_apply_edit(*_edit); + *_edit = NULL; + key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES); +} + +/* + * Finish unlinking a key from to a keyring. + */ +static void __key_unlink_end(struct key *keyring, + struct key *key, + struct assoc_array_edit *edit) + __releases(&keyring->sem) +{ + if (edit) + assoc_array_cancel_edit(edit); + up_write(&keyring->sem); +} + /** * key_unlink - Unlink the first link to a key from a keyring. * @keyring: The keyring to remove the link from. @@ -1401,33 +1460,20 @@ EXPORT_SYMBOL(key_link); */ int key_unlink(struct key *keyring, struct key *key) { - struct assoc_array_edit *edit; + struct assoc_array_edit *edit = NULL; int ret; key_check(keyring); key_check(key); - if (keyring->type != &key_type_keyring) - return -ENOTDIR; - - down_write(&keyring->sem); - - edit = assoc_array_delete(&keyring->keys, &keyring_assoc_array_ops, - &key->index_key); - if (IS_ERR(edit)) { - ret = PTR_ERR(edit); - goto error; - } - ret = -ENOENT; - if (edit == NULL) - goto error; - - assoc_array_apply_edit(edit); - key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES); - ret = 0; + ret = __key_unlink_lock(keyring); + if (ret < 0) + return ret; -error: - up_write(&keyring->sem); + ret = __key_unlink_begin(keyring, key, &edit); + if (ret == 0) + __key_unlink(keyring, key, &edit); + __key_unlink_end(keyring, key, edit); return ret; } EXPORT_SYMBOL(key_unlink); -- cgit v1.2.3 From df593ee23e05cdda16c8c995e5818779431bb29f Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 30 May 2019 11:37:39 +0100 Subject: keys: Hoist locking out of __key_link_begin() Hoist the locking of out of __key_link_begin() and into its callers. This is necessary to allow the upcoming key_move() operation to correctly order taking of the source keyring semaphore, the destination keyring semaphore and the keyring serialisation lock. Signed-off-by: David Howells --- security/keys/internal.h | 2 ++ security/keys/key.c | 27 ++++++++++++---- security/keys/keyring.c | 78 +++++++++++++++++++++++++++------------------ security/keys/request_key.c | 7 +++- 4 files changed, 76 insertions(+), 38 deletions(-) (limited to 'security/keys') diff --git a/security/keys/internal.h b/security/keys/internal.h index 8f533c81aa8d..25cdd0cbdc06 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -93,6 +93,8 @@ 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_lock(struct key *keyring, + const struct keyring_index_key *index_key); extern int __key_link_begin(struct key *keyring, const struct keyring_index_key *index_key, struct assoc_array_edit **_edit); diff --git a/security/keys/key.c b/security/keys/key.c index 696f1c092c50..bba71acec886 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -500,7 +500,7 @@ int key_instantiate_and_link(struct key *key, struct key *authkey) { struct key_preparsed_payload prep; - struct assoc_array_edit *edit; + struct assoc_array_edit *edit = NULL; int ret; memset(&prep, 0, sizeof(prep)); @@ -515,10 +515,14 @@ int key_instantiate_and_link(struct key *key, } if (keyring) { - ret = __key_link_begin(keyring, &key->index_key, &edit); + ret = __key_link_lock(keyring, &key->index_key); if (ret < 0) goto error; + ret = __key_link_begin(keyring, &key->index_key, &edit); + if (ret < 0) + goto error_link_end; + if (keyring->restrict_link && keyring->restrict_link->check) { struct key_restriction *keyres = keyring->restrict_link; @@ -570,7 +574,7 @@ int key_reject_and_link(struct key *key, struct key *keyring, struct key *authkey) { - struct assoc_array_edit *edit; + struct assoc_array_edit *edit = NULL; int ret, awaken, link_ret = 0; key_check(key); @@ -583,7 +587,12 @@ int key_reject_and_link(struct key *key, if (keyring->restrict_link) return -EPERM; - link_ret = __key_link_begin(keyring, &key->index_key, &edit); + link_ret = __key_link_lock(keyring, &key->index_key); + if (link_ret == 0) { + link_ret = __key_link_begin(keyring, &key->index_key, &edit); + if (link_ret < 0) + __key_link_end(keyring, &key->index_key, edit); + } } mutex_lock(&key_construction_mutex); @@ -810,7 +819,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, .description = description, }; struct key_preparsed_payload prep; - struct assoc_array_edit *edit; + struct assoc_array_edit *edit = NULL; const struct cred *cred = current_cred(); struct key *keyring, *key = NULL; key_ref_t key_ref; @@ -860,12 +869,18 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } index_key.desc_len = strlen(index_key.description); - ret = __key_link_begin(keyring, &index_key, &edit); + ret = __key_link_lock(keyring, &index_key); if (ret < 0) { key_ref = ERR_PTR(ret); goto error_free_prep; } + ret = __key_link_begin(keyring, &index_key, &edit); + if (ret < 0) { + key_ref = ERR_PTR(ret); + goto error_link_end; + } + if (restrict_link && restrict_link->check) { ret = restrict_link->check(keyring, index_key.type, &prep.payload, restrict_link->key); diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 6990c7761eaa..12acad3db6cf 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -1199,14 +1199,34 @@ static int keyring_detect_cycle(struct key *A, struct key *B) return PTR_ERR(ctx.result) == -EAGAIN ? 0 : PTR_ERR(ctx.result); } +/* + * Lock keyring for link. + */ +int __key_link_lock(struct key *keyring, + const struct keyring_index_key *index_key) + __acquires(&keyring->sem) + __acquires(&keyring_serialise_link_lock) +{ + if (keyring->type != &key_type_keyring) + return -ENOTDIR; + + down_write(&keyring->sem); + + /* Serialise link/link calls to prevent parallel calls causing a cycle + * when linking two keyring in opposite orders. + */ + if (index_key->type == &key_type_keyring) + mutex_lock(&keyring_serialise_link_lock); + + return 0; +} + /* * Preallocate memory so that a key can be linked into to a keyring. */ int __key_link_begin(struct key *keyring, const struct keyring_index_key *index_key, struct assoc_array_edit **_edit) - __acquires(&keyring->sem) - __acquires(&keyring_serialise_link_lock) { struct assoc_array_edit *edit; int ret; @@ -1215,20 +1235,13 @@ int __key_link_begin(struct key *keyring, keyring->serial, index_key->type->name, index_key->description); BUG_ON(index_key->desc_len == 0); + BUG_ON(*_edit != NULL); - if (keyring->type != &key_type_keyring) - return -ENOTDIR; - - down_write(&keyring->sem); + *_edit = NULL; ret = -EKEYREVOKED; if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) - goto error_krsem; - - /* serialise link/link calls to prevent parallel calls causing a cycle - * when linking two keyring in opposite orders */ - if (index_key->type == &key_type_keyring) - mutex_lock(&keyring_serialise_link_lock); + goto error; /* Create an edit script that will insert/replace the key in the * keyring tree. @@ -1239,7 +1252,7 @@ int __key_link_begin(struct key *keyring, NULL); if (IS_ERR(edit)) { ret = PTR_ERR(edit); - goto error_sem; + goto error; } /* If we're not replacing a link in-place then we're going to need some @@ -1258,11 +1271,7 @@ int __key_link_begin(struct key *keyring, error_cancel: assoc_array_cancel_edit(edit); -error_sem: - if (index_key->type == &key_type_keyring) - mutex_unlock(&keyring_serialise_link_lock); -error_krsem: - up_write(&keyring->sem); +error: kleave(" = %d", ret); return ret; } @@ -1312,9 +1321,6 @@ void __key_link_end(struct key *keyring, BUG_ON(index_key->type == NULL); kenter("%d,%s,", keyring->serial, index_key->type->name); - if (index_key->type == &key_type_keyring) - mutex_unlock(&keyring_serialise_link_lock); - if (edit) { if (!edit->dead_leaf) { key_payload_reserve(keyring, @@ -1323,6 +1329,9 @@ void __key_link_end(struct key *keyring, assoc_array_cancel_edit(edit); } up_write(&keyring->sem); + + if (index_key->type == &key_type_keyring) + mutex_unlock(&keyring_serialise_link_lock); } /* @@ -1358,7 +1367,7 @@ static int __key_link_check_restriction(struct key *keyring, struct key *key) */ int key_link(struct key *keyring, struct key *key) { - struct assoc_array_edit *edit; + struct assoc_array_edit *edit = NULL; int ret; kenter("{%d,%d}", keyring->serial, refcount_read(&keyring->usage)); @@ -1366,17 +1375,24 @@ int key_link(struct key *keyring, struct key *key) key_check(keyring); key_check(key); + ret = __key_link_lock(keyring, &key->index_key); + if (ret < 0) + goto error; + ret = __key_link_begin(keyring, &key->index_key, &edit); - if (ret == 0) { - kdebug("begun {%d,%d}", keyring->serial, refcount_read(&keyring->usage)); - ret = __key_link_check_restriction(keyring, key); - if (ret == 0) - ret = __key_link_check_live_key(keyring, key); - if (ret == 0) - __key_link(key, &edit); - __key_link_end(keyring, &key->index_key, edit); - } + if (ret < 0) + goto error_end; + + kdebug("begun {%d,%d}", keyring->serial, refcount_read(&keyring->usage)); + ret = __key_link_check_restriction(keyring, key); + if (ret == 0) + ret = __key_link_check_live_key(keyring, key); + if (ret == 0) + __key_link(key, &edit); +error_end: + __key_link_end(keyring, &key->index_key, edit); +error: kleave(" = %d {%d,%d}", ret, keyring->serial, refcount_read(&keyring->usage)); return ret; } diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 1f234b019437..857da65e1940 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -343,7 +343,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx, struct key_user *user, struct key **_key) { - struct assoc_array_edit *edit; + struct assoc_array_edit *edit = NULL; struct key *key; key_perm_t perm; key_ref_t key_ref; @@ -372,6 +372,9 @@ static int construct_alloc_key(struct keyring_search_context *ctx, set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags); if (dest_keyring) { + ret = __key_link_lock(dest_keyring, &ctx->index_key); + if (ret < 0) + goto link_lock_failed; ret = __key_link_begin(dest_keyring, &ctx->index_key, &edit); if (ret < 0) goto link_prealloc_failed; @@ -423,6 +426,8 @@ link_check_failed: return ret; link_prealloc_failed: + __key_link_end(dest_keyring, &ctx->index_key, edit); +link_lock_failed: mutex_unlock(&user->cons_lock); key_put(key); kleave(" = %d [prelink]", ret); -- cgit v1.2.3 From ed0ac5c7ec3763e3261c48e3c5d4b7528b60fd85 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 20 May 2019 21:51:50 +0100 Subject: keys: Add a keyctl to move a key between keyrings Add a keyctl to atomically move a link to a key from one keyring to another. The key must exist in "from" keyring and a flag can be given to cause the operation to fail if there's a matching key already in the "to" keyring. This can be done with: keyctl(KEYCTL_MOVE, key_serial_t key, key_serial_t from_keyring, key_serial_t to_keyring, unsigned int flags); The key being moved must grant Link permission and both keyrings must grant Write permission. flags should be 0 or KEYCTL_MOVE_EXCL, with the latter preventing displacement of a matching key from the "to" keyring. Signed-off-by: David Howells --- Documentation/security/keys/core.rst | 21 +++++++ include/linux/key.h | 5 ++ include/uapi/linux/keyctl.h | 3 + security/keys/compat.c | 3 + security/keys/internal.h | 3 + security/keys/keyctl.c | 52 +++++++++++++++++ security/keys/keyring.c | 108 +++++++++++++++++++++++++++++++++++ 7 files changed, 195 insertions(+) (limited to 'security/keys') diff --git a/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst index 9521c4207f01..823d29bf44f7 100644 --- a/Documentation/security/keys/core.rst +++ b/Documentation/security/keys/core.rst @@ -577,6 +577,27 @@ The keyctl syscall functions are: added. + * Move a key from one keyring to another:: + + long keyctl(KEYCTL_MOVE, + key_serial_t id, + key_serial_t from_ring_id, + key_serial_t to_ring_id, + unsigned int flags); + + Move the key specified by "id" from the keyring specified by + "from_ring_id" to the keyring specified by "to_ring_id". If the two + keyrings are the same, nothing is done. + + "flags" can have KEYCTL_MOVE_EXCL set in it to cause the operation to fail + with EEXIST if a matching key exists in the destination keyring, otherwise + such a key will be replaced. + + A process must have link permission on the key for this function to be + successful and write permission on both keyrings. Any errors that can + occur from KEYCTL_LINK also apply on the destination keyring here. + + * Unlink a key or keyring from another keyring:: long keyctl(KEYCTL_UNLINK, key_serial_t keyring, key_serial_t key); diff --git a/include/linux/key.h b/include/linux/key.h index 1f09aad1c98c..612e1cf84049 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -310,6 +310,11 @@ extern int key_update(key_ref_t key, extern int key_link(struct key *keyring, struct key *key); +extern int key_move(struct key *key, + struct key *from_keyring, + struct key *to_keyring, + unsigned int flags); + extern int key_unlink(struct key *keyring, struct key *key); diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h index f45ee0f69c0c..fd9fb11b312b 100644 --- a/include/uapi/linux/keyctl.h +++ b/include/uapi/linux/keyctl.h @@ -67,6 +67,7 @@ #define KEYCTL_PKEY_SIGN 27 /* Create a public key signature */ #define KEYCTL_PKEY_VERIFY 28 /* Verify a public key signature */ #define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */ +#define KEYCTL_MOVE 30 /* Move keys between keyrings */ /* keyctl structures */ struct keyctl_dh_params { @@ -112,4 +113,6 @@ struct keyctl_pkey_params { __u32 __spare[7]; }; +#define KEYCTL_MOVE_EXCL 0x00000001 /* Do not displace from the to-keyring */ + #endif /* _LINUX_KEYCTL_H */ diff --git a/security/keys/compat.c b/security/keys/compat.c index 9482df601dc3..b326bc4f84d7 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -159,6 +159,9 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, return keyctl_pkey_verify(compat_ptr(arg2), compat_ptr(arg3), compat_ptr(arg4), compat_ptr(arg5)); + case KEYCTL_MOVE: + return keyctl_keyring_move(arg2, arg3, arg4, arg5); + default: return -EOPNOTSUPP; } diff --git a/security/keys/internal.h b/security/keys/internal.h index 25cdd0cbdc06..b54a58c025ae 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -95,6 +95,8 @@ extern void key_type_put(struct key_type *ktype); extern int __key_link_lock(struct key *keyring, const struct keyring_index_key *index_key); +extern int __key_move_lock(struct key *l_keyring, struct key *u_keyring, + const struct keyring_index_key *index_key); extern int __key_link_begin(struct key *keyring, const struct keyring_index_key *index_key, struct assoc_array_edit **_edit); @@ -217,6 +219,7 @@ extern long keyctl_update_key(key_serial_t, const void __user *, size_t); extern long keyctl_revoke_key(key_serial_t); extern long keyctl_keyring_clear(key_serial_t); extern long keyctl_keyring_link(key_serial_t, key_serial_t); +extern long keyctl_keyring_move(key_serial_t, key_serial_t, key_serial_t, unsigned int); extern long keyctl_keyring_unlink(key_serial_t, key_serial_t); extern long keyctl_describe_key(key_serial_t, char __user *, size_t); extern long keyctl_keyring_search(key_serial_t, const char __user *, diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 0f947bcbad46..bbfe7d92d41c 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -572,6 +572,52 @@ error: return ret; } +/* + * Move a link to a key from one keyring to another, displacing any matching + * key from the destination keyring. + * + * The key must grant the caller Link permission and both keyrings must grant + * the caller Write permission. There must also be a link in the from keyring + * to the key. If both keyrings are the same, nothing is done. + * + * If successful, 0 will be returned. + */ +long keyctl_keyring_move(key_serial_t id, key_serial_t from_ringid, + key_serial_t to_ringid, unsigned int flags) +{ + key_ref_t key_ref, from_ref, to_ref; + long ret; + + if (flags & ~KEYCTL_MOVE_EXCL) + return -EINVAL; + + key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_LINK); + if (IS_ERR(key_ref)) + return PTR_ERR(key_ref); + + from_ref = lookup_user_key(from_ringid, 0, KEY_NEED_WRITE); + if (IS_ERR(from_ref)) { + ret = PTR_ERR(from_ref); + goto error2; + } + + to_ref = lookup_user_key(to_ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); + if (IS_ERR(to_ref)) { + ret = PTR_ERR(to_ref); + goto error3; + } + + ret = key_move(key_ref_to_ptr(key_ref), key_ref_to_ptr(from_ref), + key_ref_to_ptr(to_ref), flags); + + key_ref_put(to_ref); +error3: + key_ref_put(from_ref); +error2: + key_ref_put(key_ref); + return ret; +} + /* * Return a description of a key to userspace. * @@ -1772,6 +1818,12 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, (const void __user *)arg4, (const void __user *)arg5); + case KEYCTL_MOVE: + return keyctl_keyring_move((key_serial_t)arg2, + (key_serial_t)arg3, + (key_serial_t)arg4, + (unsigned int)arg5); + default: return -EOPNOTSUPP; } diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 12acad3db6cf..67066bb58b83 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -1221,6 +1221,40 @@ int __key_link_lock(struct key *keyring, return 0; } +/* + * Lock keyrings for move (link/unlink combination). + */ +int __key_move_lock(struct key *l_keyring, struct key *u_keyring, + const struct keyring_index_key *index_key) + __acquires(&l_keyring->sem) + __acquires(&u_keyring->sem) + __acquires(&keyring_serialise_link_lock) +{ + if (l_keyring->type != &key_type_keyring || + u_keyring->type != &key_type_keyring) + return -ENOTDIR; + + /* We have to be very careful here to take the keyring locks in the + * right order, lest we open ourselves to deadlocking against another + * move operation. + */ + if (l_keyring < u_keyring) { + down_write(&l_keyring->sem); + down_write_nested(&u_keyring->sem, 1); + } else { + down_write(&u_keyring->sem); + down_write_nested(&l_keyring->sem, 1); + } + + /* Serialise link/link calls to prevent parallel calls causing a cycle + * when linking two keyring in opposite orders. + */ + if (index_key->type == &key_type_keyring) + mutex_lock(&keyring_serialise_link_lock); + + return 0; +} + /* * Preallocate memory so that a key can be linked into to a keyring. */ @@ -1494,6 +1528,80 @@ int key_unlink(struct key *keyring, struct key *key) } EXPORT_SYMBOL(key_unlink); +/** + * key_move - Move a key from one keyring to another + * @key: The key to move + * @from_keyring: The keyring to remove the link from. + * @to_keyring: The keyring to make the link in. + * @flags: Qualifying flags, such as KEYCTL_MOVE_EXCL. + * + * Make a link in @to_keyring to a key, such that the keyring holds a reference + * on that key and the key can potentially be found by searching that keyring + * whilst simultaneously removing a link to the key from @from_keyring. + * + * This function will write-lock both keyring's semaphores and will consume + * some of the user's key data quota to hold the link on @to_keyring. + * + * Returns 0 if successful, -ENOTDIR if either keyring isn't a keyring, + * -EKEYREVOKED if either keyring has been revoked, -ENFILE if the second + * keyring is full, -EDQUOT if there is insufficient key data quota remaining + * to add another link or -ENOMEM if there's insufficient memory. If + * KEYCTL_MOVE_EXCL is set, then -EEXIST will be returned if there's already a + * matching key in @to_keyring. + * + * It is assumed that the caller has checked that it is permitted for a link to + * be made (the keyring should have Write permission and the key Link + * permission). + */ +int key_move(struct key *key, + struct key *from_keyring, + struct key *to_keyring, + unsigned int flags) +{ + struct assoc_array_edit *from_edit = NULL, *to_edit = NULL; + int ret; + + kenter("%d,%d,%d", key->serial, from_keyring->serial, to_keyring->serial); + + if (from_keyring == to_keyring) + return 0; + + key_check(key); + key_check(from_keyring); + key_check(to_keyring); + + ret = __key_move_lock(from_keyring, to_keyring, &key->index_key); + if (ret < 0) + goto out; + ret = __key_unlink_begin(from_keyring, key, &from_edit); + if (ret < 0) + goto error; + ret = __key_link_begin(to_keyring, &key->index_key, &to_edit); + if (ret < 0) + goto error; + + ret = -EEXIST; + if (to_edit->dead_leaf && (flags & KEYCTL_MOVE_EXCL)) + goto error; + + ret = __key_link_check_restriction(to_keyring, key); + if (ret < 0) + goto error; + ret = __key_link_check_live_key(to_keyring, key); + if (ret < 0) + goto error; + + __key_unlink(from_keyring, key, &from_edit); + __key_link(key, &to_edit); +error: + __key_link_end(to_keyring, &key->index_key, to_edit); + __key_unlink_end(from_keyring, key, from_edit); +out: + kleave(" = %d", ret); + return ret; +} +EXPORT_SYMBOL(key_move); + /** * keyring_clear - Clear a keyring * @keyring: The keyring to clear. -- cgit v1.2.3 From f7f1394576563b7096fe858fb178bcaedfa0b245 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 20 May 2019 21:51:43 +0100 Subject: keys: Grant Link permission to possessers of request_key auth keys Grant Link permission to the possessers of request_key authentication keys, thereby allowing a daemon that is servicing upcalls to arrange things such that only the necessary auth key is passed to the actual service program and not all the daemon's pending auth keys. Signed-off-by: David Howells Reviewed-by: James Morris --- security/keys/request_key_auth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security/keys') diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 572c7a60473a..ec5226557023 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -204,7 +204,7 @@ struct key *request_key_auth_new(struct key *target, const char *op, authkey = key_alloc(&key_type_request_key_auth, desc, cred->fsuid, cred->fsgid, cred, - KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | + KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | KEY_POS_LINK | KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(authkey)) { ret = PTR_ERR(authkey); -- cgit v1.2.3 From 47546208657f28007f41574c2d27a3fe760f987d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 May 2019 14:01:52 -0700 Subject: keys: Reuse keyring_index_key::desc_len in lookup_user_key() When lookup_user_key() checks whether the key is possessed, it should use the key's existing index_key including the 'desc_len' field, rather than recomputing the 'desc_len'. This doesn't change the behavior; this way is just simpler and faster. Signed-off-by: Eric Biggers Signed-off-by: David Howells Reviewed-by: James Morris --- security/keys/process_keys.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'security/keys') diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index ba5d3172cafe..39aaa21462bf 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -688,9 +688,7 @@ try_again: key_ref = make_key_ref(key, 0); /* check to see if we possess the key */ - ctx.index_key.type = key->type; - ctx.index_key.description = key->description; - ctx.index_key.desc_len = strlen(key->description); + ctx.index_key = key->index_key; ctx.match_data.raw_data = key; kdebug("check possessed"); skey_ref = search_process_keyrings(&ctx); -- cgit v1.2.3 From 45e0f30c30bb131663fbe1752974d6f2e39611e2 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 30 May 2019 14:53:10 +0100 Subject: keys: Add capability-checking keyctl function Add a keyctl function that requests a set of capability bits to find out what features are supported. Signed-off-by: David Howells --- include/uapi/linux/keyctl.h | 14 ++++++++++++++ security/keys/compat.c | 3 +++ security/keys/internal.h | 2 ++ security/keys/keyctl.c | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+) (limited to 'security/keys') diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h index fd9fb11b312b..551b5814f53e 100644 --- a/include/uapi/linux/keyctl.h +++ b/include/uapi/linux/keyctl.h @@ -68,6 +68,7 @@ #define KEYCTL_PKEY_VERIFY 28 /* Verify a public key signature */ #define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */ #define KEYCTL_MOVE 30 /* Move keys between keyrings */ +#define KEYCTL_CAPABILITIES 31 /* Find capabilities of keyrings subsystem */ /* keyctl structures */ struct keyctl_dh_params { @@ -115,4 +116,17 @@ struct keyctl_pkey_params { #define KEYCTL_MOVE_EXCL 0x00000001 /* Do not displace from the to-keyring */ +/* + * Capabilities flags. The capabilities list is an array of 8-bit integers; + * each integer can carry up to 8 flags. + */ +#define KEYCTL_CAPS0_CAPABILITIES 0x01 /* KEYCTL_CAPABILITIES supported */ +#define KEYCTL_CAPS0_PERSISTENT_KEYRINGS 0x02 /* Persistent keyrings enabled */ +#define KEYCTL_CAPS0_DIFFIE_HELLMAN 0x04 /* Diffie-Hellman computation enabled */ +#define KEYCTL_CAPS0_PUBLIC_KEY 0x08 /* Public key ops enabled */ +#define KEYCTL_CAPS0_BIG_KEY 0x10 /* big_key-type enabled */ +#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 */ + #endif /* _LINUX_KEYCTL_H */ diff --git a/security/keys/compat.c b/security/keys/compat.c index b326bc4f84d7..a53e30da20c5 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -162,6 +162,9 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, case KEYCTL_MOVE: return keyctl_keyring_move(arg2, arg3, arg4, arg5); + case KEYCTL_CAPABILITIES: + return keyctl_capabilities(compat_ptr(arg2), arg3); + default: return -EOPNOTSUPP; } diff --git a/security/keys/internal.h b/security/keys/internal.h index b54a58c025ae..d04bff631227 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -329,6 +329,8 @@ static inline long keyctl_pkey_e_d_s(int op, } #endif +extern long keyctl_capabilities(unsigned char __user *_buffer, size_t buflen); + /* * Debugging key validation */ diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index bbfe7d92d41c..9f418e66f067 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -30,6 +30,18 @@ #define KEY_MAX_DESC_SIZE 4096 +static const unsigned char keyrings_capabilities[1] = { + [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) | + (IS_ENABLED(CONFIG_ASYMMETRIC_KEY_TYPE) ? KEYCTL_CAPS0_PUBLIC_KEY : 0) | + (IS_ENABLED(CONFIG_BIG_KEYS) ? KEYCTL_CAPS0_BIG_KEY : 0) | + KEYCTL_CAPS0_INVALIDATE | + KEYCTL_CAPS0_RESTRICT_KEYRING | + KEYCTL_CAPS0_MOVE + ), +}; + static int key_get_type_from_user(char *type, const char __user *_type, unsigned len) @@ -1678,6 +1690,26 @@ error: return ret; } +/* + * Get keyrings subsystem capabilities. + */ +long keyctl_capabilities(unsigned char __user *_buffer, size_t buflen) +{ + size_t size = buflen; + + if (size > 0) { + if (size > sizeof(keyrings_capabilities)) + size = sizeof(keyrings_capabilities); + if (copy_to_user(_buffer, keyrings_capabilities, size) != 0) + return -EFAULT; + if (size < buflen && + clear_user(_buffer + size, buflen - size) != 0) + return -EFAULT; + } + + return sizeof(keyrings_capabilities); +} + /* * The key control system call */ @@ -1824,6 +1856,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, (key_serial_t)arg4, (unsigned int)arg5); + case KEYCTL_CAPABILITIES: + return keyctl_capabilities((unsigned char __user *)arg2, (size_t)arg3); + default: return -EOPNOTSUPP; } -- cgit v1.2.3