| @@ -1456,47 +1456,55 @@ long keyctl_session_to_parent(void) · 1455 unmodified lines |
| 1456 | 1456 | { |
| 1457 | 1457 | struct task_struct *me, *parent; |
| 1458 | 1458 | const struct cred *mycred, *pcred; |
| 1459 | | − struct cred *cred, *oldcred; |
| 1459 | + struct task_work *newwork, *oldwork; |
| 1460 | 1460 | key_ref_t keyring_r; |
| 1461 | + struct cred *cred; |
| 1461 | 1462 | int ret; |
| 1462 | 1463 | |
| 1463 | 1464 | keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_LINK); |
| 1464 | 1465 | if (IS_ERR(keyring_r)) |
| 1465 | 1466 | return PTR_ERR(keyring_r); |
| 1466 | 1467 | |
| 1468 | + ret = -ENOMEM; |
| 1469 | + newwork = kmalloc(sizeof(struct task_work), GFP_KERNEL); |
| 1470 | + if (!newwork) |
| 1471 | + goto error_keyring; |
| 1472 | + |
| 1467 | 1473 | /* our parent is going to need a new cred struct, a new tgcred struct |
| 1468 | 1474 | * and new security data, so we allocate them here to prevent ENOMEM in |
| 1469 | 1475 | * our parent */ |
| 1470 | | − ret = -ENOMEM; |
| 1471 | 1476 | cred = cred_alloc_blank(); |
| 1472 | 1477 | if (!cred) |
| 1473 | | − goto error_keyring; |
| 1478 | + goto error_newwork; |
| 1474 | 1479 | |
| 1475 | 1480 | cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r); |
| 1476 | | − keyring_r = NULL; |
| 1481 | + init_task_work(newwork, key_change_session_keyring, cred); |
| 1477 | 1482 | |
| 1478 | 1483 | me = current; |
| 1479 | 1484 | rcu_read_lock(); |
| 1480 | 1485 | write_lock_irq(&tasklist_lock); |
| 1481 | 1486 | |
| 1482 | | − parent = me->real_parent; |
| 1483 | 1487 | ret = -EPERM; |
| 1488 | + oldwork = NULL; |
| 1489 | + parent = me->real_parent; |
| 1484 | 1490 | |
| 1485 | 1491 | /* the parent mustn't be init and mustn't be a kernel thread */ |
| 1486 | 1492 | if (parent->pid <= 1 || !parent->mm) |
| 1487 | | − goto not_permitted; |
| 1493 | + goto unlock; |
| 1488 | 1494 | |
| 1489 | 1495 | /* the parent must be single threaded */ |
| 1490 | 1496 | if (!thread_group_empty(parent)) |
| 1491 | | − goto not_permitted; |
| 1497 | + goto unlock; |
| 1492 | 1498 | |
| 1493 | 1499 | /* the parent and the child must have different session keyrings or |
| 1494 | 1500 | * there's no point */ |
| 1495 | 1501 | mycred = current_cred(); |
| 1496 | 1502 | pcred = __task_cred(parent); |
| 1497 | 1503 | if (mycred == pcred || |
| 1498 | | − mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) |
| 1499 | | − goto already_same; |
| 1504 | + mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) { |
| 1505 | + ret = 0; |
| 1506 | + goto unlock; |
| 1507 | + } |
| 1500 | 1508 | |
| 1501 | 1509 | /* the parent must have the same effective ownership and mustn't be |
| 1502 | 1510 | * SUID/SGID */ |
| @@ -1506,38 +1514,37 @@ long keyctl_session_to_parent(void) · 3 unmodified lines |
| 1506 | 1514 | pcred->gid != mycred->egid || |
| 1507 | 1515 | pcred->egid != mycred->egid || |
| 1508 | 1516 | pcred->sgid != mycred->egid) |
| 1509 | | − goto not_permitted; |
| 1517 | + goto unlock; |
| 1510 | 1518 | |
| 1511 | 1519 | /* the keyrings must have the same UID */ |
| 1512 | 1520 | if ((pcred->tgcred->session_keyring && |
| 1513 | 1521 | pcred->tgcred->session_keyring->uid != mycred->euid) || |
| 1514 | 1522 | mycred->tgcred->session_keyring->uid != mycred->euid) |
| 1515 | | − goto not_permitted; |
| 1523 | + goto unlock; |
| 1516 | 1524 | |
| 1517 | | − /* if there's an already pending keyring replacement, then we replace |
| 1518 | | − * that */ |
| 1519 | | − oldcred = parent->replacement_session_keyring; |
| 1525 | + /* cancel an already pending keyring replacement */ |
| 1526 | + oldwork = task_work_cancel(parent, key_change_session_keyring); |
| 1520 | 1527 | |
| 1521 | 1528 | /* the replacement session keyring is applied just prior to userspace |
| 1522 | 1529 | * restarting */ |
| 1523 | | − parent->replacement_session_keyring = cred; |
| 1524 | | − cred = NULL; |
| 1525 | | − set_ti_thread_flag(task_thread_info(parent), TIF_NOTIFY_RESUME); |
| 1526 | | − |
| 1527 | | − write_unlock_irq(&tasklist_lock); |
| 1528 | | − rcu_read_unlock(); |
| 1529 | | − if (oldcred) |
| 1530 | | − put_cred(oldcred); |
| 1531 | | − return 0; |
| 1532 | | − |
| 1533 | | −already_same: |
| 1534 | | − ret = 0; |
| 1535 | | −not_permitted: |
| 1530 | + ret = task_work_add(parent, newwork, true); |
| 1531 | + if (!ret) |
| 1532 | + newwork = NULL; |
| 1533 | +unlock: |
| 1536 | 1534 | write_unlock_irq(&tasklist_lock); |
| 1537 | 1535 | rcu_read_unlock(); |
| 1538 | | − 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 | + } |
| 1539 | 1544 | return ret; |
| 1540 | 1545 | |
| 1546 | +error_newwork: |
| 1547 | + kfree(newwork); |
| 1541 | 1548 | error_keyring: |
| 1542 | 1549 | key_ref_put(keyring_r); |
| 1543 | 1550 | return ret; |