summaryrefslogtreecommitdiffstats
path: root/security/keys
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2017-10-04 11:43:25 -0400
committerDavid Howells <dhowells@redhat.com>2017-10-18 04:12:40 -0400
commit363b02dab09b3226f3bd1420dad9c72b79a42a76 (patch)
tree9162410e681bbbd73f951d5942aac5a95f568a6b /security/keys
parentb3811d36a3e7e7e8ed660bf01151496cf99cf9ed (diff)
KEYS: Fix race between updating and finding a negative key
Consolidate KEY_FLAG_INSTANTIATED, KEY_FLAG_NEGATIVE and the rejection error into one field such that: (1) The instantiation state can be modified/read atomically. (2) The error can be accessed atomically with the state. (3) The error isn't stored unioned with the payload pointers. This deals with the problem that the state is spread over three different objects (two bits and a separate variable) and reading or updating them atomically isn't practical, given that not only can uninstantiated keys change into instantiated or rejected keys, but rejected keys can also turn into instantiated keys - and someone accessing the key might not be using any locking. The main side effect of this problem is that what was held in the payload may change, depending on the state. For instance, you might observe the key to be in the rejected state. You then read the cached error, but if the key semaphore wasn't locked, the key might've become instantiated between the two reads - and you might now have something in hand that isn't actually an error code. The state is now KEY_IS_UNINSTANTIATED, KEY_IS_POSITIVE or a negative error code if the key is negatively instantiated. The key_is_instantiated() function is replaced with key_is_positive() to avoid confusion as negative keys are also 'instantiated'. Additionally, barriering is included: (1) Order payload-set before state-set during instantiation. (2) Order state-read before payload-read when using the key. Further separate barriering is necessary if RCU is being used to access the payload content after reading the payload pointers. Fixes: 146aa8b1453b ("KEYS: Merge the type-specific data with the payload data") Cc: stable@vger.kernel.org # v4.4+ Reported-by: Eric Biggers <ebiggers@google.com> Signed-off-by: David Howells <dhowells@redhat.com> Reviewed-by: Eric Biggers <ebiggers@google.com>
Diffstat (limited to 'security/keys')
-rw-r--r--security/keys/big_key.c4
-rw-r--r--security/keys/encrypted-keys/encrypted.c2
-rw-r--r--security/keys/gc.c8
-rw-r--r--security/keys/key.c31
-rw-r--r--security/keys/keyctl.c9
-rw-r--r--security/keys/keyring.c10
-rw-r--r--security/keys/proc.c7
-rw-r--r--security/keys/process_keys.c2
-rw-r--r--security/keys/request_key.c7
-rw-r--r--security/keys/request_key_auth.c2
-rw-r--r--security/keys/trusted.c2
-rw-r--r--security/keys/user_defined.c4
12 files changed, 49 insertions, 39 deletions
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
index e607830b6154..929e14978c42 100644
--- a/security/keys/big_key.c
+++ b/security/keys/big_key.c
@@ -247,7 +247,7 @@ void big_key_revoke(struct key *key)
247 247
248 /* clear the quota */ 248 /* clear the quota */
249 key_payload_reserve(key, 0); 249 key_payload_reserve(key, 0);
250 if (key_is_instantiated(key) && 250 if (key_is_positive(key) &&
251 (size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD) 251 (size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD)
252 vfs_truncate(path, 0); 252 vfs_truncate(path, 0);
253} 253}
@@ -279,7 +279,7 @@ void big_key_describe(const struct key *key, struct seq_file *m)
279 279
280 seq_puts(m, key->description); 280 seq_puts(m, key->description);
281 281
282 if (key_is_instantiated(key)) 282 if (key_is_positive(key))
283 seq_printf(m, ": %zu [%s]", 283 seq_printf(m, ": %zu [%s]",
284 datalen, 284 datalen,
285 datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff"); 285 datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 535db141f4da..d92cbf9687c3 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -854,7 +854,7 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)
854 size_t datalen = prep->datalen; 854 size_t datalen = prep->datalen;
855 int ret = 0; 855 int ret = 0;
856 856
857 if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) 857 if (key_is_negative(key))
858 return -ENOKEY; 858 return -ENOKEY;
859 if (datalen <= 0 || datalen > 32767 || !prep->data) 859 if (datalen <= 0 || datalen > 32767 || !prep->data)
860 return -EINVAL; 860 return -EINVAL;
diff --git a/security/keys/gc.c b/security/keys/gc.c
index 87cb260e4890..f01d48cb3de1 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -129,15 +129,15 @@ static noinline void key_gc_unused_keys(struct list_head *keys)
129 while (!list_empty(keys)) { 129 while (!list_empty(keys)) {
130 struct key *key = 130 struct key *key =
131 list_entry(keys->next, struct key, graveyard_link); 131 list_entry(keys->next, struct key, graveyard_link);
132 short state = key->state;
133
132 list_del(&key->graveyard_link); 134 list_del(&key->graveyard_link);
133 135
134 kdebug("- %u", key->serial); 136 kdebug("- %u", key->serial);
135 key_check(key); 137 key_check(key);
136 138
137 /* Throw away the key data if the key is instantiated */ 139 /* Throw away the key data if the key is instantiated */
138 if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags) && 140 if (state == KEY_IS_POSITIVE && key->type->destroy)
139 !test_bit(KEY_FLAG_NEGATIVE, &key->flags) &&
140 key->type->destroy)
141 key->type->destroy(key); 141 key->type->destroy(key);
142 142
143 security_key_free(key); 143 security_key_free(key);
@@ -151,7 +151,7 @@ static noinline void key_gc_unused_keys(struct list_head *keys)
151 } 151 }
152 152
153 atomic_dec(&key->user->nkeys); 153 atomic_dec(&key->user->nkeys);
154 if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) 154 if (state != KEY_IS_UNINSTANTIATED)
155 atomic_dec(&key->user->nikeys); 155 atomic_dec(&key->user->nikeys);
156 156
157 key_user_put(key->user); 157 key_user_put(key->user);
diff --git a/security/keys/key.c b/security/keys/key.c
index eb914a838840..9385e7cc710f 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -402,6 +402,18 @@ int key_payload_reserve(struct key *key, size_t datalen)
402EXPORT_SYMBOL(key_payload_reserve); 402EXPORT_SYMBOL(key_payload_reserve);
403 403
404/* 404/*
405 * Change the key state to being instantiated.
406 */
407static void mark_key_instantiated(struct key *key, int reject_error)
408{
409 /* Commit the payload before setting the state; barrier versus
410 * key_read_state().
411 */
412 smp_store_release(&key->state,
413 (reject_error < 0) ? reject_error : KEY_IS_POSITIVE);
414}
415
416/*
405 * Instantiate a key and link it into the target keyring atomically. Must be 417 * Instantiate a key and link it into the target keyring atomically. Must be
406 * called with the target keyring's semaphore writelocked. The target key's 418 * called with the target keyring's semaphore writelocked. The target key's
407 * semaphore need not be locked as instantiation is serialised by 419 * semaphore need not be locked as instantiation is serialised by
@@ -424,14 +436,14 @@ static int __key_instantiate_and_link(struct key *key,
424 mutex_lock(&key_construction_mutex); 436 mutex_lock(&key_construction_mutex);
425 437
426 /* can't instantiate twice */ 438 /* can't instantiate twice */
427 if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { 439 if (key->state == KEY_IS_UNINSTANTIATED) {
428 /* instantiate the key */ 440 /* instantiate the key */
429 ret = key->type->instantiate(key, prep); 441 ret = key->type->instantiate(key, prep);
430 442
431 if (ret == 0) { 443 if (ret == 0) {
432 /* mark the key as being instantiated */ 444 /* mark the key as being instantiated */
433 atomic_inc(&key->user->nikeys); 445 atomic_inc(&key->user->nikeys);
434 set_bit(KEY_FLAG_INSTANTIATED, &key->flags); 446 mark_key_instantiated(key, 0);
435 447
436 if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) 448 if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
437 awaken = 1; 449 awaken = 1;
@@ -577,13 +589,10 @@ int key_reject_and_link(struct key *key,
577 mutex_lock(&key_construction_mutex); 589 mutex_lock(&key_construction_mutex);
578 590
579 /* can't instantiate twice */ 591 /* can't instantiate twice */
580 if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { 592 if (key->state == KEY_IS_UNINSTANTIATED) {
581 /* mark the key as being negatively instantiated */ 593 /* mark the key as being negatively instantiated */
582 atomic_inc(&key->user->nikeys); 594 atomic_inc(&key->user->nikeys);
583 key->reject_error = -error; 595 mark_key_instantiated(key, -error);
584 smp_wmb();
585 set_bit(KEY_FLAG_NEGATIVE, &key->flags);
586 set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
587 now = current_kernel_time(); 596 now = current_kernel_time();
588 key->expiry = now.tv_sec + timeout; 597 key->expiry = now.tv_sec + timeout;
589 key_schedule_gc(key->expiry + key_gc_delay); 598 key_schedule_gc(key->expiry + key_gc_delay);
@@ -752,8 +761,8 @@ static inline key_ref_t __key_update(key_ref_t key_ref,
752 761
753 ret = key->type->update(key, prep); 762 ret = key->type->update(key, prep);
754 if (ret == 0) 763 if (ret == 0)
755 /* updating a negative key instantiates it */ 764 /* Updating a negative key positively instantiates it */
756 clear_bit(KEY_FLAG_NEGATIVE, &key->flags); 765 mark_key_instantiated(key, 0);
757 766
758 up_write(&key->sem); 767 up_write(&key->sem);
759 768
@@ -986,8 +995,8 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
986 995
987 ret = key->type->update(key, &prep); 996 ret = key->type->update(key, &prep);
988 if (ret == 0) 997 if (ret == 0)
989 /* updating a negative key instantiates it */ 998 /* Updating a negative key positively instantiates it */
990 clear_bit(KEY_FLAG_NEGATIVE, &key->flags); 999 mark_key_instantiated(key, 0);
991 1000
992 up_write(&key->sem); 1001 up_write(&key->sem);
993 1002
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 365ff85d7e27..76d22f726ae4 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -766,10 +766,9 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
766 766
767 key = key_ref_to_ptr(key_ref); 767 key = key_ref_to_ptr(key_ref);
768 768
769 if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { 769 ret = key_read_state(key);
770 ret = -ENOKEY; 770 if (ret < 0)
771 goto error2; 771 goto error2; /* Negatively instantiated */
772 }
773 772
774 /* see if we can read it directly */ 773 /* see if we can read it directly */
775 ret = key_permission(key_ref, KEY_NEED_READ); 774 ret = key_permission(key_ref, KEY_NEED_READ);
@@ -901,7 +900,7 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)
901 atomic_dec(&key->user->nkeys); 900 atomic_dec(&key->user->nkeys);
902 atomic_inc(&newowner->nkeys); 901 atomic_inc(&newowner->nkeys);
903 902
904 if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { 903 if (key->state != KEY_IS_UNINSTANTIATED) {
905 atomic_dec(&key->user->nikeys); 904 atomic_dec(&key->user->nikeys);
906 atomic_inc(&newowner->nikeys); 905 atomic_inc(&newowner->nikeys);
907 } 906 }
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 4fa82a8a9c0e..06173b091a74 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -414,7 +414,7 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m)
414 else 414 else
415 seq_puts(m, "[anon]"); 415 seq_puts(m, "[anon]");
416 416
417 if (key_is_instantiated(keyring)) { 417 if (key_is_positive(keyring)) {
418 if (keyring->keys.nr_leaves_on_tree != 0) 418 if (keyring->keys.nr_leaves_on_tree != 0)
419 seq_printf(m, ": %lu", keyring->keys.nr_leaves_on_tree); 419 seq_printf(m, ": %lu", keyring->keys.nr_leaves_on_tree);
420 else 420 else
@@ -553,7 +553,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
553{ 553{
554 struct keyring_search_context *ctx = iterator_data; 554 struct keyring_search_context *ctx = iterator_data;
555 const struct key *key = keyring_ptr_to_key(object); 555 const struct key *key = keyring_ptr_to_key(object);
556 unsigned long kflags = key->flags; 556 unsigned long kflags = READ_ONCE(key->flags);
557 short state = READ_ONCE(key->state);
557 558
558 kenter("{%d}", key->serial); 559 kenter("{%d}", key->serial);
559 560
@@ -597,9 +598,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
597 598
598 if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) { 599 if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) {
599 /* we set a different error code if we pass a negative key */ 600 /* we set a different error code if we pass a negative key */
600 if (kflags & (1 << KEY_FLAG_NEGATIVE)) { 601 if (state < 0) {
601 smp_rmb(); 602 ctx->result = ERR_PTR(state);
602 ctx->result = ERR_PTR(key->reject_error);
603 kleave(" = %d [neg]", ctx->skipped_ret); 603 kleave(" = %d [neg]", ctx->skipped_ret);
604 goto skipped; 604 goto skipped;
605 } 605 }
diff --git a/security/keys/proc.c b/security/keys/proc.c
index de834309d100..4089ce1f7757 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -182,6 +182,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
182 unsigned long timo; 182 unsigned long timo;
183 key_ref_t key_ref, skey_ref; 183 key_ref_t key_ref, skey_ref;
184 char xbuf[16]; 184 char xbuf[16];
185 short state;
185 int rc; 186 int rc;
186 187
187 struct keyring_search_context ctx = { 188 struct keyring_search_context ctx = {
@@ -236,17 +237,19 @@ static int proc_keys_show(struct seq_file *m, void *v)
236 sprintf(xbuf, "%luw", timo / (60*60*24*7)); 237 sprintf(xbuf, "%luw", timo / (60*60*24*7));
237 } 238 }
238 239
240 state = key_read_state(key);
241
239#define showflag(KEY, LETTER, FLAG) \ 242#define showflag(KEY, LETTER, FLAG) \
240 (test_bit(FLAG, &(KEY)->flags) ? LETTER : '-') 243 (test_bit(FLAG, &(KEY)->flags) ? LETTER : '-')
241 244
242 seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", 245 seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ",
243 key->serial, 246 key->serial,
244 showflag(key, 'I', KEY_FLAG_INSTANTIATED), 247 state != KEY_IS_UNINSTANTIATED ? 'I' : '-',
245 showflag(key, 'R', KEY_FLAG_REVOKED), 248 showflag(key, 'R', KEY_FLAG_REVOKED),
246 showflag(key, 'D', KEY_FLAG_DEAD), 249 showflag(key, 'D', KEY_FLAG_DEAD),
247 showflag(key, 'Q', KEY_FLAG_IN_QUOTA), 250 showflag(key, 'Q', KEY_FLAG_IN_QUOTA),
248 showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT), 251 showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
249 showflag(key, 'N', KEY_FLAG_NEGATIVE), 252 state < 0 ? 'N' : '-',
250 showflag(key, 'i', KEY_FLAG_INVALIDATED), 253 showflag(key, 'i', KEY_FLAG_INVALIDATED),
251 refcount_read(&key->usage), 254 refcount_read(&key->usage),
252 xbuf, 255 xbuf,
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 293d3598153b..740affd65ee9 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -730,7 +730,7 @@ try_again:
730 730
731 ret = -EIO; 731 ret = -EIO;
732 if (!(lflags & KEY_LOOKUP_PARTIAL) && 732 if (!(lflags & KEY_LOOKUP_PARTIAL) &&
733 !test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) 733 key_read_state(key) == KEY_IS_UNINSTANTIATED)
734 goto invalid_key; 734 goto invalid_key;
735 735
736 /* check the permissions */ 736 /* check the permissions */
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 63e63a42db3c..e8036cd0ad54 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -595,10 +595,9 @@ int wait_for_key_construction(struct key *key, bool intr)
595 intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); 595 intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
596 if (ret) 596 if (ret)
597 return -ERESTARTSYS; 597 return -ERESTARTSYS;
598 if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { 598 ret = key_read_state(key);
599 smp_rmb(); 599 if (ret < 0)
600 return key->reject_error; 600 return ret;
601 }
602 return key_validate(key); 601 return key_validate(key);
603} 602}
604EXPORT_SYMBOL(wait_for_key_construction); 603EXPORT_SYMBOL(wait_for_key_construction);
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 6ebf1af8fce9..424e1d90412e 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -73,7 +73,7 @@ static void request_key_auth_describe(const struct key *key,
73 73
74 seq_puts(m, "key:"); 74 seq_puts(m, "key:");
75 seq_puts(m, key->description); 75 seq_puts(m, key->description);
76 if (key_is_instantiated(key)) 76 if (key_is_positive(key))
77 seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len); 77 seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len);
78} 78}
79 79
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index ddfaebf60fc8..bd85315cbfeb 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -1066,7 +1066,7 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
1066 char *datablob; 1066 char *datablob;
1067 int ret = 0; 1067 int ret = 0;
1068 1068
1069 if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) 1069 if (key_is_negative(key))
1070 return -ENOKEY; 1070 return -ENOKEY;
1071 p = key->payload.data[0]; 1071 p = key->payload.data[0];
1072 if (!p->migratable) 1072 if (!p->migratable)
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index 3d8c68eba516..9f558bedba23 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -114,7 +114,7 @@ int user_update(struct key *key, struct key_preparsed_payload *prep)
114 114
115 /* attach the new data, displacing the old */ 115 /* attach the new data, displacing the old */
116 key->expiry = prep->expiry; 116 key->expiry = prep->expiry;
117 if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) 117 if (key_is_positive(key))
118 zap = dereference_key_locked(key); 118 zap = dereference_key_locked(key);
119 rcu_assign_keypointer(key, prep->payload.data[0]); 119 rcu_assign_keypointer(key, prep->payload.data[0]);
120 prep->payload.data[0] = NULL; 120 prep->payload.data[0] = NULL;
@@ -162,7 +162,7 @@ EXPORT_SYMBOL_GPL(user_destroy);
162void user_describe(const struct key *key, struct seq_file *m) 162void user_describe(const struct key *key, struct seq_file *m)
163{ 163{
164 seq_puts(m, key->description); 164 seq_puts(m, key->description);
165 if (key_is_instantiated(key)) 165 if (key_is_positive(key))
166 seq_printf(m, ": %u", key->datalen); 166 seq_printf(m, ": %u", key->datalen);
167} 167}
168 168