diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-31 21:47:30 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-31 21:47:30 -0400 |
| commit | fb21affa49204acd409328415b49bfe90136653c (patch) | |
| tree | 3535dbe0c0aad049a38cadfcffe78409397a1b32 /security | |
| parent | a00b6151a2ae4c52576c35d3998e144a993d50b8 (diff) | |
| parent | f23ca335462e3c84f13270b9e65f83936068ec2c (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.h | 2 | ||||
| -rw-r--r-- | security/keys/keyctl.c | 73 | ||||
| -rw-r--r-- | security/keys/process_keys.c | 20 |
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 | ||
| 150 | extern long join_session_keyring(const char *name); | 151 | extern long join_session_keyring(const char *name); |
| 152 | extern void key_change_session_keyring(struct task_work *twork); | ||
| 151 | 153 | ||
| 152 | extern struct work_struct key_gc_work; | 154 | extern struct work_struct key_gc_work; |
| 153 | extern unsigned key_gc_delay; | 155 | extern 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 | */ |
| 1455 | long keyctl_session_to_parent(void) | 1455 | long 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 | 1533 | unlock: | |
| 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 | } | |
| 1534 | already_same: | 1540 | if (newwork) { |
| 1535 | ret = 0; | 1541 | put_cred(newwork->data); |
| 1536 | not_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 | ||
| 1546 | error_newwork: | ||
| 1547 | kfree(newwork); | ||
| 1542 | error_keyring: | 1548 | error_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 | */ |
| 837 | void key_replace_session_keyring(void) | 837 | void 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; |
