diff options
Diffstat (limited to 'security/keys/keyctl.c')
-rw-r--r-- | security/keys/keyctl.c | 77 |
1 files changed, 37 insertions, 40 deletions
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index b61c063888b9..3bdc419b272f 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c | |||
@@ -84,7 +84,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, | |||
84 | vm = false; | 84 | vm = false; |
85 | if (_payload) { | 85 | if (_payload) { |
86 | ret = -ENOMEM; | 86 | ret = -ENOMEM; |
87 | payload = kmalloc(plen, GFP_KERNEL); | 87 | payload = kmalloc(plen, GFP_KERNEL | __GFP_NOWARN); |
88 | if (!payload) { | 88 | if (!payload) { |
89 | if (plen <= PAGE_SIZE) | 89 | if (plen <= PAGE_SIZE) |
90 | goto error2; | 90 | goto error2; |
@@ -1110,7 +1110,7 @@ long keyctl_instantiate_key_iov(key_serial_t id, | |||
1110 | goto no_payload; | 1110 | goto no_payload; |
1111 | 1111 | ||
1112 | ret = rw_copy_check_uvector(WRITE, _payload_iov, ioc, | 1112 | ret = rw_copy_check_uvector(WRITE, _payload_iov, ioc, |
1113 | ARRAY_SIZE(iovstack), iovstack, &iov, 1); | 1113 | ARRAY_SIZE(iovstack), iovstack, &iov); |
1114 | if (ret < 0) | 1114 | if (ret < 0) |
1115 | return ret; | 1115 | return ret; |
1116 | if (ret == 0) | 1116 | if (ret == 0) |
@@ -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); | ||
1529 | rcu_read_unlock(); | ||
1530 | if (oldcred) | ||
1531 | put_cred(oldcred); | ||
1532 | return 0; | ||
1533 | |||
1534 | already_same: | ||
1535 | ret = 0; | ||
1536 | not_permitted: | ||
1537 | write_unlock_irq(&tasklist_lock); | 1534 | write_unlock_irq(&tasklist_lock); |
1538 | rcu_read_unlock(); | 1535 | rcu_read_unlock(); |
1539 | put_cred(cred); | 1536 | if (oldwork) { |
1537 | put_cred(oldwork->data); | ||
1538 | kfree(oldwork); | ||
1539 | } | ||
1540 | if (newwork) { | ||
1541 | put_cred(newwork->data); | ||
1542 | kfree(newwork); | ||
1543 | } | ||
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 | /* |