|
27 | 27 | #include <linux/lsm_hooks.h> |
28 | 28 | #include <linux/mount.h> |
29 | 29 | #include <linux/namei.h> |
| 30 | +#include <linux/net.h> |
30 | 31 | #include <linux/path.h> |
31 | 32 | #include <linux/pid.h> |
32 | 33 | #include <linux/rcupdate.h> |
|
36 | 37 | #include <linux/types.h> |
37 | 38 | #include <linux/wait_bit.h> |
38 | 39 | #include <linux/workqueue.h> |
| 40 | +#include <net/af_unix.h> |
39 | 41 | #include <uapi/linux/fiemap.h> |
40 | 42 | #include <uapi/linux/landlock.h> |
41 | 43 |
|
@@ -314,7 +316,8 @@ static struct landlock_object *get_inode_object(struct inode *const inode) |
314 | 316 | LANDLOCK_ACCESS_FS_WRITE_FILE | \ |
315 | 317 | LANDLOCK_ACCESS_FS_READ_FILE | \ |
316 | 318 | LANDLOCK_ACCESS_FS_TRUNCATE | \ |
317 | | - LANDLOCK_ACCESS_FS_IOCTL_DEV) |
| 319 | + LANDLOCK_ACCESS_FS_IOCTL_DEV | \ |
| 320 | + LANDLOCK_ACCESS_FS_RESOLVE_UNIX) |
318 | 321 | /* clang-format on */ |
319 | 322 |
|
320 | 323 | /* |
@@ -1557,6 +1560,130 @@ static int hook_path_truncate(const struct path *const path) |
1557 | 1560 | return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE); |
1558 | 1561 | } |
1559 | 1562 |
|
| 1563 | +/** |
| 1564 | + * unmask_scoped_access - Remove access right bits in @masks in all layers |
| 1565 | + * where @client and @server have the same domain |
| 1566 | + * |
| 1567 | + * This does the same as domain_is_scoped(), but unmasks bits in @masks. |
| 1568 | + * It can not return early as domain_is_scoped() does. |
| 1569 | + * |
| 1570 | + * A scoped access for a given access right bit is allowed iff, for all layer |
| 1571 | + * depths where the access bit is set, the client and server domain are the |
| 1572 | + * same. This function clears the access rights @access in @masks at all layer |
| 1573 | + * depths where the client and server domain are the same, so that, when they |
| 1574 | + * are all cleared, the access is allowed. |
| 1575 | + * |
| 1576 | + * @client: Client domain |
| 1577 | + * @server: Server domain |
| 1578 | + * @masks: Layer access masks to unmask |
| 1579 | + * @access: Access bits that control scoping |
| 1580 | + */ |
| 1581 | +static void unmask_scoped_access(const struct landlock_ruleset *const client, |
| 1582 | + const struct landlock_ruleset *const server, |
| 1583 | + struct layer_access_masks *const masks, |
| 1584 | + const access_mask_t access) |
| 1585 | +{ |
| 1586 | + int client_layer, server_layer; |
| 1587 | + const struct landlock_hierarchy *client_walker, *server_walker; |
| 1588 | + |
| 1589 | + /* This should not happen. */ |
| 1590 | + if (WARN_ON_ONCE(!client)) |
| 1591 | + return; |
| 1592 | + |
| 1593 | + /* Server has no Landlock domain; nothing to clear. */ |
| 1594 | + if (!server) |
| 1595 | + return; |
| 1596 | + |
| 1597 | + /* |
| 1598 | + * client_layer must be a signed integer with greater capacity |
| 1599 | + * than client->num_layers to ensure the following loop stops. |
| 1600 | + */ |
| 1601 | + BUILD_BUG_ON(sizeof(client_layer) > sizeof(client->num_layers)); |
| 1602 | + |
| 1603 | + client_layer = client->num_layers - 1; |
| 1604 | + client_walker = client->hierarchy; |
| 1605 | + server_layer = server->num_layers - 1; |
| 1606 | + server_walker = server->hierarchy; |
| 1607 | + |
| 1608 | + /* |
| 1609 | + * Clears the access bits at all layers where the client domain is the |
| 1610 | + * same as the server domain. We start the walk at min(client_layer, |
| 1611 | + * server_layer). The layer bits until there can not be cleared because |
| 1612 | + * either the client or the server domain is missing. |
| 1613 | + */ |
| 1614 | + for (; client_layer > server_layer; client_layer--) |
| 1615 | + client_walker = client_walker->parent; |
| 1616 | + |
| 1617 | + for (; server_layer > client_layer; server_layer--) |
| 1618 | + server_walker = server_walker->parent; |
| 1619 | + |
| 1620 | + for (; client_layer >= 0; client_layer--) { |
| 1621 | + if (masks->access[client_layer] & access && |
| 1622 | + client_walker == server_walker) |
| 1623 | + masks->access[client_layer] &= ~access; |
| 1624 | + |
| 1625 | + client_walker = client_walker->parent; |
| 1626 | + server_walker = server_walker->parent; |
| 1627 | + } |
| 1628 | +} |
| 1629 | + |
| 1630 | +static int hook_unix_find(const struct path *const path, struct sock *other, |
| 1631 | + int flags) |
| 1632 | +{ |
| 1633 | + const struct landlock_ruleset *dom_other; |
| 1634 | + const struct landlock_cred_security *subject; |
| 1635 | + struct layer_access_masks layer_masks; |
| 1636 | + struct landlock_request request = {}; |
| 1637 | + static const struct access_masks fs_resolve_unix = { |
| 1638 | + .fs = LANDLOCK_ACCESS_FS_RESOLVE_UNIX, |
| 1639 | + }; |
| 1640 | + |
| 1641 | + /* Lookup for the purpose of saving coredumps is OK. */ |
| 1642 | + if (unlikely(flags & SOCK_COREDUMP)) |
| 1643 | + return 0; |
| 1644 | + |
| 1645 | + subject = landlock_get_applicable_subject(current_cred(), |
| 1646 | + fs_resolve_unix, NULL); |
| 1647 | + |
| 1648 | + if (!subject) |
| 1649 | + return 0; |
| 1650 | + |
| 1651 | + /* |
| 1652 | + * Ignoring return value: that the domains apply was already checked in |
| 1653 | + * landlock_get_applicable_subject() above. |
| 1654 | + */ |
| 1655 | + landlock_init_layer_masks(subject->domain, fs_resolve_unix.fs, |
| 1656 | + &layer_masks, LANDLOCK_KEY_INODE); |
| 1657 | + |
| 1658 | + /* Checks the layers in which we are connecting within the same domain. */ |
| 1659 | + unix_state_lock(other); |
| 1660 | + if (unlikely(sock_flag(other, SOCK_DEAD) || !other->sk_socket || |
| 1661 | + !other->sk_socket->file)) { |
| 1662 | + unix_state_unlock(other); |
| 1663 | + /* |
| 1664 | + * We rely on the caller to catch the (non-reversible) SOCK_DEAD |
| 1665 | + * condition and retry the lookup. If we returned an error |
| 1666 | + * here, the lookup would not get retried. |
| 1667 | + */ |
| 1668 | + return 0; |
| 1669 | + } |
| 1670 | + dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain; |
| 1671 | + |
| 1672 | + /* Access to the same (or a lower) domain is always allowed. */ |
| 1673 | + unmask_scoped_access(subject->domain, dom_other, &layer_masks, |
| 1674 | + fs_resolve_unix.fs); |
| 1675 | + unix_state_unlock(other); |
| 1676 | + |
| 1677 | + /* Checks the connections to allow-listed paths. */ |
| 1678 | + if (is_access_to_paths_allowed(subject->domain, path, |
| 1679 | + fs_resolve_unix.fs, &layer_masks, |
| 1680 | + &request, NULL, 0, NULL, NULL, NULL)) |
| 1681 | + return 0; |
| 1682 | + |
| 1683 | + landlock_log_denial(subject, &request); |
| 1684 | + return -EACCES; |
| 1685 | +} |
| 1686 | + |
1560 | 1687 | /* File hooks */ |
1561 | 1688 |
|
1562 | 1689 | /** |
@@ -1834,6 +1961,7 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = { |
1834 | 1961 | LSM_HOOK_INIT(path_unlink, hook_path_unlink), |
1835 | 1962 | LSM_HOOK_INIT(path_rmdir, hook_path_rmdir), |
1836 | 1963 | LSM_HOOK_INIT(path_truncate, hook_path_truncate), |
| 1964 | + LSM_HOOK_INIT(unix_find, hook_unix_find), |
1837 | 1965 |
|
1838 | 1966 | LSM_HOOK_INIT(file_alloc_security, hook_file_alloc_security), |
1839 | 1967 | LSM_HOOK_INIT(file_open, hook_file_open), |
|
0 commit comments