aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-05-31 21:47:30 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-31 21:47:30 -0400
commitfb21affa49204acd409328415b49bfe90136653c (patch)
tree3535dbe0c0aad049a38cadfcffe78409397a1b32 /security
parenta00b6151a2ae4c52576c35d3998e144a993d50b8 (diff)
parentf23ca335462e3c84f13270b9e65f83936068ec2c (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/signal
Pull second pile of signal handling patches from Al Viro: "This one is just task_work_add() series + remaining prereqs for it. There probably will be another pull request from that tree this cycle - at least for helpers, to get them out of the way for per-arch fixes remaining in the tree." Fix trivial conflict in kernel/irq/manage.c: the merge of Andrew's pile had brought in commit 97fd75b7b8e0 ("kernel/irq/manage.c: use the pr_foo() infrastructure to prefix printks") which changed one of the pr_err() calls that this merge moves around. * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/signal: keys: kill task_struct->replacement_session_keyring keys: kill the dummy key_replace_session_keyring() keys: change keyctl_session_to_parent() to use task_work_add() genirq: reimplement exit_irq_thread() hook via task_work_add() task_work_add: generic process-context callbacks avr32: missed _TIF_NOTIFY_RESUME on one of do_notify_resume callers parisc: need to check NOTIFY_RESUME when exiting from syscall move key_repace_session_keyring() into tracehook_notify_resume() TIF_NOTIFY_RESUME is defined on all targets now
Diffstat (limited to 'security')
-rw-r--r--security/keys/internal.h2
-rw-r--r--security/keys/keyctl.c73
-rw-r--r--security/keys/process_keys.c20
3 files changed, 44 insertions, 51 deletions
diff --git a/security/keys/internal.h b/security/keys/internal.h
index f711b094ed41..3dcbf86b0d31 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -14,6 +14,7 @@
14 14
15#include <linux/sched.h> 15#include <linux/sched.h>
16#include <linux/key-type.h> 16#include <linux/key-type.h>
17#include <linux/task_work.h>
17 18
18#ifdef __KDEBUG 19#ifdef __KDEBUG
19#define kenter(FMT, ...) \ 20#define kenter(FMT, ...) \
@@ -148,6 +149,7 @@ extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
148#define KEY_LOOKUP_FOR_UNLINK 0x04 149#define KEY_LOOKUP_FOR_UNLINK 0x04
149 150
150extern long join_session_keyring(const char *name); 151extern long join_session_keyring(const char *name);
152extern void key_change_session_keyring(struct task_work *twork);
151 153
152extern struct work_struct key_gc_work; 154extern struct work_struct key_gc_work;
153extern unsigned key_gc_delay; 155extern unsigned key_gc_delay;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 21907ea35b15..0f5b3f027299 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1454,50 +1454,57 @@ long keyctl_get_security(key_serial_t keyid,
1454 */ 1454 */
1455long keyctl_session_to_parent(void) 1455long keyctl_session_to_parent(void)
1456{ 1456{
1457#ifdef TIF_NOTIFY_RESUME
1458 struct task_struct *me, *parent; 1457 struct task_struct *me, *parent;
1459 const struct cred *mycred, *pcred; 1458 const struct cred *mycred, *pcred;
1460 struct cred *cred, *oldcred; 1459 struct task_work *newwork, *oldwork;
1461 key_ref_t keyring_r; 1460 key_ref_t keyring_r;
1461 struct cred *cred;
1462 int ret; 1462 int ret;
1463 1463
1464 keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_LINK); 1464 keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_LINK);
1465 if (IS_ERR(keyring_r)) 1465 if (IS_ERR(keyring_r))
1466 return PTR_ERR(keyring_r); 1466 return PTR_ERR(keyring_r);
1467 1467
1468 ret = -ENOMEM;
1469 newwork = kmalloc(sizeof(struct task_work), GFP_KERNEL);
1470 if (!newwork)
1471 goto error_keyring;
1472
1468 /* our parent is going to need a new cred struct, a new tgcred struct 1473 /* our parent is going to need a new cred struct, a new tgcred struct
1469 * and new security data, so we allocate them here to prevent ENOMEM in 1474 * and new security data, so we allocate them here to prevent ENOMEM in
1470 * our parent */ 1475 * our parent */
1471 ret = -ENOMEM;
1472 cred = cred_alloc_blank(); 1476 cred = cred_alloc_blank();
1473 if (!cred) 1477 if (!cred)
1474 goto error_keyring; 1478 goto error_newwork;
1475 1479
1476 cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r); 1480 cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r);
1477 keyring_r = NULL; 1481 init_task_work(newwork, key_change_session_keyring, cred);
1478 1482
1479 me = current; 1483 me = current;
1480 rcu_read_lock(); 1484 rcu_read_lock();
1481 write_lock_irq(&tasklist_lock); 1485 write_lock_irq(&tasklist_lock);
1482 1486
1483 parent = me->real_parent;
1484 ret = -EPERM; 1487 ret = -EPERM;
1488 oldwork = NULL;
1489 parent = me->real_parent;
1485 1490
1486 /* the parent mustn't be init and mustn't be a kernel thread */ 1491 /* the parent mustn't be init and mustn't be a kernel thread */
1487 if (parent->pid <= 1 || !parent->mm) 1492 if (parent->pid <= 1 || !parent->mm)
1488 goto not_permitted; 1493 goto unlock;
1489 1494
1490 /* the parent must be single threaded */ 1495 /* the parent must be single threaded */
1491 if (!thread_group_empty(parent)) 1496 if (!thread_group_empty(parent))
1492 goto not_permitted; 1497 goto unlock;
1493 1498
1494 /* the parent and the child must have different session keyrings or 1499 /* the parent and the child must have different session keyrings or
1495 * there's no point */ 1500 * there's no point */
1496 mycred = current_cred(); 1501 mycred = current_cred();
1497 pcred = __task_cred(parent); 1502 pcred = __task_cred(parent);
1498 if (mycred == pcred || 1503 if (mycred == pcred ||
1499 mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) 1504 mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) {
1500 goto already_same; 1505 ret = 0;
1506 goto unlock;
1507 }
1501 1508
1502 /* the parent must have the same effective ownership and mustn't be 1509 /* the parent must have the same effective ownership and mustn't be
1503 * SUID/SGID */ 1510 * SUID/SGID */
@@ -1507,50 +1514,40 @@ long keyctl_session_to_parent(void)
1507 pcred->gid != mycred->egid || 1514 pcred->gid != mycred->egid ||
1508 pcred->egid != mycred->egid || 1515 pcred->egid != mycred->egid ||
1509 pcred->sgid != mycred->egid) 1516 pcred->sgid != mycred->egid)
1510 goto not_permitted; 1517 goto unlock;
1511 1518
1512 /* the keyrings must have the same UID */ 1519 /* the keyrings must have the same UID */
1513 if ((pcred->tgcred->session_keyring && 1520 if ((pcred->tgcred->session_keyring &&
1514 pcred->tgcred->session_keyring->uid != mycred->euid) || 1521 pcred->tgcred->session_keyring->uid != mycred->euid) ||
1515 mycred->tgcred->session_keyring->uid != mycred->euid) 1522 mycred->tgcred->session_keyring->uid != mycred->euid)
1516 goto not_permitted; 1523 goto unlock;
1517 1524
1518 /* if there's an already pending keyring replacement, then we replace 1525 /* cancel an already pending keyring replacement */
1519 * that */ 1526 oldwork = task_work_cancel(parent, key_change_session_keyring);
1520 oldcred = parent->replacement_session_keyring;
1521 1527
1522 /* the replacement session keyring is applied just prior to userspace 1528 /* the replacement session keyring is applied just prior to userspace
1523 * restarting */ 1529 * restarting */
1524 parent->replacement_session_keyring = cred; 1530 ret = task_work_add(parent, newwork, true);
1525 cred = NULL; 1531 if (!ret)
1526 set_ti_thread_flag(task_thread_info(parent), TIF_NOTIFY_RESUME); 1532 newwork = NULL;
1527 1533unlock:
1528 write_unlock_irq(&tasklist_lock); 1534 write_unlock_irq(&tasklist_lock);
1529 rcu_read_unlock(); 1535 rcu_read_unlock();
1530 if (oldcred) 1536 if (oldwork) {
1531 put_cred(oldcred); 1537 put_cred(oldwork->data);
1532 return 0; 1538 kfree(oldwork);
1533 1539 }
1534already_same: 1540 if (newwork) {
1535 ret = 0; 1541 put_cred(newwork->data);
1536not_permitted: 1542 kfree(newwork);
1537 write_unlock_irq(&tasklist_lock); 1543 }
1538 rcu_read_unlock();
1539 put_cred(cred);
1540 return ret; 1544 return ret;
1541 1545
1546error_newwork:
1547 kfree(newwork);
1542error_keyring: 1548error_keyring:
1543 key_ref_put(keyring_r); 1549 key_ref_put(keyring_r);
1544 return ret; 1550 return ret;
1545
1546#else /* !TIF_NOTIFY_RESUME */
1547 /*
1548 * To be removed when TIF_NOTIFY_RESUME has been implemented on
1549 * m68k/xtensa
1550 */
1551#warning TIF_NOTIFY_RESUME not implemented
1552 return -EOPNOTSUPP;
1553#endif /* !TIF_NOTIFY_RESUME */
1554} 1551}
1555 1552
1556/* 1553/*
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index d71056db7b67..4ad54eea1ea4 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -834,23 +834,17 @@ error:
834 * Replace a process's session keyring on behalf of one of its children when 834 * Replace a process's session keyring on behalf of one of its children when
835 * the target process is about to resume userspace execution. 835 * the target process is about to resume userspace execution.
836 */ 836 */
837void key_replace_session_keyring(void) 837void key_change_session_keyring(struct task_work *twork)
838{ 838{
839 const struct cred *old; 839 const struct cred *old = current_cred();
840 struct cred *new; 840 struct cred *new = twork->data;
841
842 if (!current->replacement_session_keyring)
843 return;
844 841
845 write_lock_irq(&tasklist_lock); 842 kfree(twork);
846 new = current->replacement_session_keyring; 843 if (unlikely(current->flags & PF_EXITING)) {
847 current->replacement_session_keyring = NULL; 844 put_cred(new);
848 write_unlock_irq(&tasklist_lock);
849
850 if (!new)
851 return; 845 return;
846 }
852 847
853 old = current_cred();
854 new-> uid = old-> uid; 848 new-> uid = old-> uid;
855 new-> euid = old-> euid; 849 new-> euid = old-> euid;
856 new-> suid = old-> suid; 850 new-> suid = old-> suid;