aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2012-10-02 14:24:29 -0400
committerDavid Howells <dhowells@redhat.com>2012-10-02 14:24:29 -0400
commit3a50597de8635cd05133bd12c95681c82fe7b878 (patch)
treed81c3e46dcef80fbaf84fdf1e8f43676625bab8e /security
parenta84a921978b7d56e0e4b87ffaca6367429b4d8ff (diff)
KEYS: Make the session and process keyrings per-thread
Make the session keyring per-thread rather than per-process, but still inherited from the parent thread to solve a problem with PAM and gdm. The problem is that join_session_keyring() will reject attempts to change the session keyring of a multithreaded program but gdm is now multithreaded before it gets to the point of starting PAM and running pam_keyinit to create the session keyring. See: https://bugs.freedesktop.org/show_bug.cgi?id=49211 The reason that join_session_keyring() will only change the session keyring under a single-threaded environment is that it's hard to alter the other thread's credentials to effect the change in a multi-threaded program. The problems are such as: (1) How to prevent two threads both running join_session_keyring() from racing. (2) Another thread's credentials may not be modified directly by this process. (3) The number of threads is uncertain whilst we're not holding the appropriate spinlock, making preallocation slightly tricky. (4) We could use TIF_NOTIFY_RESUME and key_replace_session_keyring() to get another thread to replace its keyring, but that means preallocating for each thread. A reasonable way around this is to make the session keyring per-thread rather than per-process and just document that if you want a common session keyring, you must get it before you spawn any threads - which is the current situation anyway. Whilst we're at it, we can the process keyring behave in the same way. This means we can clean up some of the ickyness in the creds code. Basically, after this patch, the session, process and thread keyrings are about inheritance rules only and not about sharing changes of keyring. Reported-by: Mantas M. <grawity@gmail.com> Signed-off-by: David Howells <dhowells@redhat.com> Tested-by: Ray Strode <rstrode@redhat.com>
Diffstat (limited to 'security')
-rw-r--r--security/keys/keyctl.c11
-rw-r--r--security/keys/process_keys.c66
-rw-r--r--security/keys/request_key.c10
3 files changed, 33 insertions, 54 deletions
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index a0d373f76815..65b38417c211 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1475,7 +1475,8 @@ long keyctl_session_to_parent(void)
1475 goto error_keyring; 1475 goto error_keyring;
1476 newwork = &cred->rcu; 1476 newwork = &cred->rcu;
1477 1477
1478 cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r); 1478 cred->session_keyring = key_ref_to_ptr(keyring_r);
1479 keyring_r = NULL;
1479 init_task_work(newwork, key_change_session_keyring); 1480 init_task_work(newwork, key_change_session_keyring);
1480 1481
1481 me = current; 1482 me = current;
@@ -1500,7 +1501,7 @@ long keyctl_session_to_parent(void)
1500 mycred = current_cred(); 1501 mycred = current_cred();
1501 pcred = __task_cred(parent); 1502 pcred = __task_cred(parent);
1502 if (mycred == pcred || 1503 if (mycred == pcred ||
1503 mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) { 1504 mycred->session_keyring == pcred->session_keyring) {
1504 ret = 0; 1505 ret = 0;
1505 goto unlock; 1506 goto unlock;
1506 } 1507 }
@@ -1516,9 +1517,9 @@ long keyctl_session_to_parent(void)
1516 goto unlock; 1517 goto unlock;
1517 1518
1518 /* the keyrings must have the same UID */ 1519 /* the keyrings must have the same UID */
1519 if ((pcred->tgcred->session_keyring && 1520 if ((pcred->session_keyring &&
1520 pcred->tgcred->session_keyring->uid != mycred->euid) || 1521 pcred->session_keyring->uid != mycred->euid) ||
1521 mycred->tgcred->session_keyring->uid != mycred->euid) 1522 mycred->session_keyring->uid != mycred->euid)
1522 goto unlock; 1523 goto unlock;
1523 1524
1524 /* cancel an already pending keyring replacement */ 1525 /* cancel an already pending keyring replacement */
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 178b8c3b130a..9de5dc598276 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -169,9 +169,8 @@ static int install_thread_keyring(void)
169int install_process_keyring_to_cred(struct cred *new) 169int install_process_keyring_to_cred(struct cred *new)
170{ 170{
171 struct key *keyring; 171 struct key *keyring;
172 int ret;
173 172
174 if (new->tgcred->process_keyring) 173 if (new->process_keyring)
175 return -EEXIST; 174 return -EEXIST;
176 175
177 keyring = keyring_alloc("_pid", new->uid, new->gid, 176 keyring = keyring_alloc("_pid", new->uid, new->gid,
@@ -179,17 +178,8 @@ int install_process_keyring_to_cred(struct cred *new)
179 if (IS_ERR(keyring)) 178 if (IS_ERR(keyring))
180 return PTR_ERR(keyring); 179 return PTR_ERR(keyring);
181 180
182 spin_lock_irq(&new->tgcred->lock); 181 new->process_keyring = keyring;
183 if (!new->tgcred->process_keyring) { 182 return 0;
184 new->tgcred->process_keyring = keyring;
185 keyring = NULL;
186 ret = 0;
187 } else {
188 ret = -EEXIST;
189 }
190 spin_unlock_irq(&new->tgcred->lock);
191 key_put(keyring);
192 return ret;
193} 183}
194 184
195/* 185/*
@@ -230,7 +220,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
230 /* create an empty session keyring */ 220 /* create an empty session keyring */
231 if (!keyring) { 221 if (!keyring) {
232 flags = KEY_ALLOC_QUOTA_OVERRUN; 222 flags = KEY_ALLOC_QUOTA_OVERRUN;
233 if (cred->tgcred->session_keyring) 223 if (cred->session_keyring)
234 flags = KEY_ALLOC_IN_QUOTA; 224 flags = KEY_ALLOC_IN_QUOTA;
235 225
236 keyring = keyring_alloc("_ses", cred->uid, cred->gid, 226 keyring = keyring_alloc("_ses", cred->uid, cred->gid,
@@ -242,17 +232,11 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
242 } 232 }
243 233
244 /* install the keyring */ 234 /* install the keyring */
245 spin_lock_irq(&cred->tgcred->lock); 235 old = cred->session_keyring;
246 old = cred->tgcred->session_keyring; 236 rcu_assign_pointer(cred->session_keyring, keyring);
247 rcu_assign_pointer(cred->tgcred->session_keyring, keyring); 237
248 spin_unlock_irq(&cred->tgcred->lock); 238 if (old)
249
250 /* we're using RCU on the pointer, but there's no point synchronising
251 * on it if it didn't previously point to anything */
252 if (old) {
253 synchronize_rcu();
254 key_put(old); 239 key_put(old);
255 }
256 240
257 return 0; 241 return 0;
258} 242}
@@ -367,9 +351,9 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
367 } 351 }
368 352
369 /* search the process keyring second */ 353 /* search the process keyring second */
370 if (cred->tgcred->process_keyring) { 354 if (cred->process_keyring) {
371 key_ref = keyring_search_aux( 355 key_ref = keyring_search_aux(
372 make_key_ref(cred->tgcred->process_keyring, 1), 356 make_key_ref(cred->process_keyring, 1),
373 cred, type, description, match, no_state_check); 357 cred, type, description, match, no_state_check);
374 if (!IS_ERR(key_ref)) 358 if (!IS_ERR(key_ref))
375 goto found; 359 goto found;
@@ -388,12 +372,10 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
388 } 372 }
389 373
390 /* search the session keyring */ 374 /* search the session keyring */
391 if (cred->tgcred->session_keyring) { 375 if (cred->session_keyring) {
392 rcu_read_lock(); 376 rcu_read_lock();
393 key_ref = keyring_search_aux( 377 key_ref = keyring_search_aux(
394 make_key_ref(rcu_dereference( 378 make_key_ref(rcu_dereference(cred->session_keyring), 1),
395 cred->tgcred->session_keyring),
396 1),
397 cred, type, description, match, no_state_check); 379 cred, type, description, match, no_state_check);
398 rcu_read_unlock(); 380 rcu_read_unlock();
399 381
@@ -563,7 +545,7 @@ try_again:
563 break; 545 break;
564 546
565 case KEY_SPEC_PROCESS_KEYRING: 547 case KEY_SPEC_PROCESS_KEYRING:
566 if (!cred->tgcred->process_keyring) { 548 if (!cred->process_keyring) {
567 if (!(lflags & KEY_LOOKUP_CREATE)) 549 if (!(lflags & KEY_LOOKUP_CREATE))
568 goto error; 550 goto error;
569 551
@@ -575,13 +557,13 @@ try_again:
575 goto reget_creds; 557 goto reget_creds;
576 } 558 }
577 559
578 key = cred->tgcred->process_keyring; 560 key = cred->process_keyring;
579 atomic_inc(&key->usage); 561 atomic_inc(&key->usage);
580 key_ref = make_key_ref(key, 1); 562 key_ref = make_key_ref(key, 1);
581 break; 563 break;
582 564
583 case KEY_SPEC_SESSION_KEYRING: 565 case KEY_SPEC_SESSION_KEYRING:
584 if (!cred->tgcred->session_keyring) { 566 if (!cred->session_keyring) {
585 /* always install a session keyring upon access if one 567 /* always install a session keyring upon access if one
586 * doesn't exist yet */ 568 * doesn't exist yet */
587 ret = install_user_keyrings(); 569 ret = install_user_keyrings();
@@ -596,7 +578,7 @@ try_again:
596 if (ret < 0) 578 if (ret < 0)
597 goto error; 579 goto error;
598 goto reget_creds; 580 goto reget_creds;
599 } else if (cred->tgcred->session_keyring == 581 } else if (cred->session_keyring ==
600 cred->user->session_keyring && 582 cred->user->session_keyring &&
601 lflags & KEY_LOOKUP_CREATE) { 583 lflags & KEY_LOOKUP_CREATE) {
602 ret = join_session_keyring(NULL); 584 ret = join_session_keyring(NULL);
@@ -606,7 +588,7 @@ try_again:
606 } 588 }
607 589
608 rcu_read_lock(); 590 rcu_read_lock();
609 key = rcu_dereference(cred->tgcred->session_keyring); 591 key = rcu_dereference(cred->session_keyring);
610 atomic_inc(&key->usage); 592 atomic_inc(&key->usage);
611 rcu_read_unlock(); 593 rcu_read_unlock();
612 key_ref = make_key_ref(key, 1); 594 key_ref = make_key_ref(key, 1);
@@ -766,12 +748,6 @@ long join_session_keyring(const char *name)
766 struct key *keyring; 748 struct key *keyring;
767 long ret, serial; 749 long ret, serial;
768 750
769 /* only permit this if there's a single thread in the thread group -
770 * this avoids us having to adjust the creds on all threads and risking
771 * ENOMEM */
772 if (!current_is_single_threaded())
773 return -EMLINK;
774
775 new = prepare_creds(); 751 new = prepare_creds();
776 if (!new) 752 if (!new)
777 return -ENOMEM; 753 return -ENOMEM;
@@ -783,7 +759,7 @@ long join_session_keyring(const char *name)
783 if (ret < 0) 759 if (ret < 0)
784 goto error; 760 goto error;
785 761
786 serial = new->tgcred->session_keyring->serial; 762 serial = new->session_keyring->serial;
787 ret = commit_creds(new); 763 ret = commit_creds(new);
788 if (ret == 0) 764 if (ret == 0)
789 ret = serial; 765 ret = serial;
@@ -806,6 +782,9 @@ long join_session_keyring(const char *name)
806 } else if (IS_ERR(keyring)) { 782 } else if (IS_ERR(keyring)) {
807 ret = PTR_ERR(keyring); 783 ret = PTR_ERR(keyring);
808 goto error2; 784 goto error2;
785 } else if (keyring == new->session_keyring) {
786 ret = 0;
787 goto error2;
809 } 788 }
810 789
811 /* we've got a keyring - now to install it */ 790 /* we've got a keyring - now to install it */
@@ -862,8 +841,7 @@ void key_change_session_keyring(struct callback_head *twork)
862 841
863 new->jit_keyring = old->jit_keyring; 842 new->jit_keyring = old->jit_keyring;
864 new->thread_keyring = key_get(old->thread_keyring); 843 new->thread_keyring = key_get(old->thread_keyring);
865 new->tgcred->tgid = old->tgcred->tgid; 844 new->process_keyring = key_get(old->process_keyring);
866 new->tgcred->process_keyring = key_get(old->tgcred->process_keyring);
867 845
868 security_transfer_creds(new, old); 846 security_transfer_creds(new, old);
869 847
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 000e75017520..275c4f9e4b8c 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -150,12 +150,12 @@ static int call_sbin_request_key(struct key_construction *cons,
150 cred->thread_keyring ? cred->thread_keyring->serial : 0); 150 cred->thread_keyring ? cred->thread_keyring->serial : 0);
151 151
152 prkey = 0; 152 prkey = 0;
153 if (cred->tgcred->process_keyring) 153 if (cred->process_keyring)
154 prkey = cred->tgcred->process_keyring->serial; 154 prkey = cred->process_keyring->serial;
155 sprintf(keyring_str[1], "%d", prkey); 155 sprintf(keyring_str[1], "%d", prkey);
156 156
157 rcu_read_lock(); 157 rcu_read_lock();
158 session = rcu_dereference(cred->tgcred->session_keyring); 158 session = rcu_dereference(cred->session_keyring);
159 if (!session) 159 if (!session)
160 session = cred->user->session_keyring; 160 session = cred->user->session_keyring;
161 sskey = session->serial; 161 sskey = session->serial;
@@ -297,14 +297,14 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)
297 break; 297 break;
298 298
299 case KEY_REQKEY_DEFL_PROCESS_KEYRING: 299 case KEY_REQKEY_DEFL_PROCESS_KEYRING:
300 dest_keyring = key_get(cred->tgcred->process_keyring); 300 dest_keyring = key_get(cred->process_keyring);
301 if (dest_keyring) 301 if (dest_keyring)
302 break; 302 break;
303 303
304 case KEY_REQKEY_DEFL_SESSION_KEYRING: 304 case KEY_REQKEY_DEFL_SESSION_KEYRING:
305 rcu_read_lock(); 305 rcu_read_lock();
306 dest_keyring = key_get( 306 dest_keyring = key_get(
307 rcu_dereference(cred->tgcred->session_keyring)); 307 rcu_dereference(cred->session_keyring));
308 rcu_read_unlock(); 308 rcu_read_unlock();
309 309
310 if (dest_keyring) 310 if (dest_keyring)