From 2eaf6b5dcafda2b8c22930eff7f48a364fce1741 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Wed, 30 Oct 2013 11:15:23 +0000 Subject: KEYS: Make BIG_KEYS boolean Having the big_keys functionality as a module is very marginally useful. The userspace code that would use this functionality will get odd error messages from the keys layer if the module isn't loaded. The code itself is fairly small, so just have this as a boolean option and not a tristate. Signed-off-by: Josh Boyer Signed-off-by: David Howells --- security/keys/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 53d8748c9564..a4f3f8c48d6e 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -38,7 +38,7 @@ config PERSISTENT_KEYRINGS removed if they expire (a default timeout is set upon creation). config BIG_KEYS - tristate "Large payload keys" + bool "Large payload keys" depends on KEYS depends on TMPFS help -- cgit v1.2.1 From 74792b0001ee85b845dc82c1a716c6052c2db9de Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 30 Oct 2013 11:15:24 +0000 Subject: KEYS: Fix a race between negating a key and reading the error set key_reject_and_link() marking a key as negative and setting the error with which it was negated races with keyring searches and other things that read that error. The fix is to switch the order in which the assignments are done in key_reject_and_link() and to use memory barriers. Kudos to Dave Wysochanski and Scott Mayhew for tracking this down. This may be the cause of: BUG: unable to handle kernel NULL pointer dereference at 0000000000000070 IP: [] wait_for_key_construction+0x31/0x80 PGD c6b2c3067 PUD c59879067 PMD 0 Oops: 0000 [#1] SMP last sysfs file: /sys/devices/system/cpu/cpu3/cache/index2/shared_cpu_map CPU 0 Modules linked in: ... Pid: 13359, comm: amqzxma0 Not tainted 2.6.32-358.20.1.el6.x86_64 #1 IBM System x3650 M3 -[7945PSJ]-/00J6159 RIP: 0010:[] wait_for_key_construction+0x31/0x80 RSP: 0018:ffff880c6ab33758 EFLAGS: 00010246 RAX: ffffffff81219080 RBX: 0000000000000000 RCX: 0000000000000002 RDX: ffffffff81219060 RSI: 0000000000000000 RDI: 0000000000000000 RBP: ffff880c6ab33768 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000001 R11: 0000000000000000 R12: ffff880adfcbce40 R13: ffffffffa03afb84 R14: ffff880adfcbce40 R15: ffff880adfcbce43 FS: 00007f29b8042700(0000) GS:ffff880028200000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000070 CR3: 0000000c613dc000 CR4: 00000000000007f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process amqzxma0 (pid: 13359, threadinfo ffff880c6ab32000, task ffff880c610deae0) Stack: ffff880adfcbce40 0000000000000000 ffff880c6ab337b8 ffffffff81219695 0000000000000000 ffff880a000000d0 ffff880c6ab337a8 000000000000000f ffffffffa03afb93 000000000000000f ffff88186c7882c0 0000000000000014 Call Trace: [] request_key+0x65/0xa0 [] nfs_idmap_request_key+0xc5/0x170 [nfs] [] nfs_idmap_lookup_id+0x34/0x80 [nfs] [] nfs_map_group_to_gid+0x75/0xa0 [nfs] [] decode_getfattr_attrs+0xbdd/0xfb0 [nfs] [] ? __dequeue_entity+0x30/0x50 [] ? __switch_to+0x26e/0x320 [] decode_getfattr+0x83/0xe0 [nfs] [] ? nfs4_xdr_dec_getattr+0x0/0xa0 [nfs] [] nfs4_xdr_dec_getattr+0x8f/0xa0 [nfs] [] rpcauth_unwrap_resp+0x84/0xb0 [sunrpc] [] ? nfs4_xdr_dec_getattr+0x0/0xa0 [nfs] [] call_decode+0x1b3/0x800 [sunrpc] [] ? wake_bit_function+0x0/0x50 [] ? call_decode+0x0/0x800 [sunrpc] [] __rpc_execute+0x77/0x350 [sunrpc] [] ? bit_waitqueue+0x17/0xd0 [] rpc_execute+0x61/0xa0 [sunrpc] [] rpc_run_task+0x75/0x90 [sunrpc] [] rpc_call_sync+0x42/0x70 [sunrpc] [] _nfs4_call_sync+0x30/0x40 [nfs] [] _nfs4_proc_getattr+0xac/0xc0 [nfs] [] ? futex_wait+0x227/0x380 [] nfs4_proc_getattr+0x56/0x80 [nfs] [] __nfs_revalidate_inode+0xe3/0x220 [nfs] [] nfs_revalidate_mapping+0x4e/0x170 [nfs] [] nfs_file_read+0x77/0x130 [nfs] [] do_sync_read+0xfa/0x140 [] ? autoremove_wake_function+0x0/0x40 [] ? apic_timer_interrupt+0xe/0x20 [] ? common_interrupt+0xe/0x13 [] ? selinux_file_permission+0xfb/0x150 [] ? security_file_permission+0x16/0x20 [] vfs_read+0xb5/0x1a0 [] sys_read+0x51/0x90 [] ? __audit_syscall_exit+0x265/0x290 [] system_call_fastpath+0x16/0x1b Signed-off-by: David Howells cc: Dave Wysochanski cc: Scott Mayhew --- security/keys/key.c | 3 ++- security/keys/keyring.c | 1 + security/keys/request_key.c | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/keys/key.c b/security/keys/key.c index d331ea9ef380..55d110f0aced 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -557,9 +557,10 @@ int key_reject_and_link(struct key *key, if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { /* mark the key as being negatively instantiated */ atomic_inc(&key->user->nikeys); + key->type_data.reject_error = -error; + smp_wmb(); set_bit(KEY_FLAG_NEGATIVE, &key->flags); set_bit(KEY_FLAG_INSTANTIATED, &key->flags); - key->type_data.reject_error = -error; now = current_kernel_time(); key->expiry = now.tv_sec + timeout; key_schedule_gc(key->expiry + key_gc_delay); diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 9b6f6e09b50c..8c05ebd7203d 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -551,6 +551,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data) if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) { /* we set a different error code if we pass a negative key */ if (kflags & (1 << KEY_FLAG_NEGATIVE)) { + smp_rmb(); ctx->result = ERR_PTR(key->type_data.reject_error); kleave(" = %d [neg]", ctx->skipped_ret); goto skipped; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index df94827103d0..381411941cc1 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -596,8 +596,10 @@ int wait_for_key_construction(struct key *key, bool intr) intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); if (ret < 0) return ret; - if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) + if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { + smp_rmb(); return key->type_data.reject_error; + } return key_validate(key); } EXPORT_SYMBOL(wait_for_key_construction); -- cgit v1.2.1 From 034faeb9ef390d58239e1dce748143f6b35a0d9b Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 30 Oct 2013 11:15:24 +0000 Subject: KEYS: Fix keyring quota misaccounting on key replacement and unlink If a key is displaced from a keyring by a matching one, then four more bytes of quota are allocated to the keyring - despite the fact that the keyring does not change in size. Further, when a key is unlinked from a keyring, the four bytes of quota allocated the link isn't recovered and returned to the user's pool. The first can be tested by repeating: keyctl add big_key a fred @s cat /proc/key-users (Don't put it in a shell loop otherwise the garbage collector won't have time to clear the displaced keys, thus affecting the result). This was causing the kerberos keyring to run out of room fairly quickly. The second can be tested by: cat /proc/key-users a=`keyctl add user a a @s` cat /proc/key-users keyctl unlink $a sleep 1 # Give RCU a chance to delete the key cat /proc/key-users assuming no system activity that otherwise adds/removes keys, the amount of key data allocated should go up (say 40/20000 -> 47/20000) and then return to the original value at the end. Reported-by: Stephen Gallagher Signed-off-by: David Howells --- security/keys/keyring.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'security') diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 8c05ebd7203d..d80311e571c3 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -1063,12 +1063,6 @@ int __key_link_begin(struct key *keyring, if (index_key->type == &key_type_keyring) down_write(&keyring_serialise_link_sem); - /* check that we aren't going to overrun the user's quota */ - ret = key_payload_reserve(keyring, - keyring->datalen + KEYQUOTA_LINK_BYTES); - if (ret < 0) - goto error_sem; - /* Create an edit script that will insert/replace the key in the * keyring tree. */ @@ -1078,17 +1072,25 @@ int __key_link_begin(struct key *keyring, NULL); if (IS_ERR(edit)) { ret = PTR_ERR(edit); - goto error_quota; + goto error_sem; + } + + /* If we're not replacing a link in-place then we're going to need some + * extra quota. + */ + if (!edit->dead_leaf) { + ret = key_payload_reserve(keyring, + keyring->datalen + KEYQUOTA_LINK_BYTES); + if (ret < 0) + goto error_cancel; } *_edit = edit; kleave(" = 0"); return 0; -error_quota: - /* undo the quota changes */ - key_payload_reserve(keyring, - keyring->datalen - KEYQUOTA_LINK_BYTES); +error_cancel: + assoc_array_cancel_edit(edit); error_sem: if (index_key->type == &key_type_keyring) up_write(&keyring_serialise_link_sem); @@ -1146,7 +1148,7 @@ void __key_link_end(struct key *keyring, if (index_key->type == &key_type_keyring) up_write(&keyring_serialise_link_sem); - if (edit) { + if (edit && !edit->dead_leaf) { key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES); assoc_array_cancel_edit(edit); @@ -1243,6 +1245,7 @@ int key_unlink(struct key *keyring, struct key *key) goto error; assoc_array_apply_edit(edit); + key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES); ret = 0; error: -- cgit v1.2.1 From d2b86970245b64652c4d7799e707dd8bd1533b64 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 30 Oct 2013 11:23:02 +0800 Subject: KEYS: fix error return code in big_key_instantiate() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: David Howells --- security/keys/big_key.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 5f9defc4a807..2cf5e62d67af 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -71,8 +71,10 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) * TODO: Encrypt the stored data with a temporary key. */ file = shmem_file_setup("", datalen, 0); - if (IS_ERR(file)) + if (IS_ERR(file)) { + ret = PTR_ERR(file); goto err_quota; + } written = kernel_write(file, prep->data, prep->datalen, 0); if (written != datalen) { -- cgit v1.2.1