On Fri, Sep 13, 2024 at 11:19 AM Stephen Smalley <stephen.smalley.work@xxxxxxxxx> wrote: > > On Wed, Sep 11, 2024 at 4:13 PM Stephen Smalley > <stephen.smalley.work@xxxxxxxxx> wrote: > > > > On Tue, Sep 10, 2024 at 3:13 PM Stephen Smalley > > <stephen.smalley.work@xxxxxxxxx> wrote: > > Next up is tackling #2 below. > > > > > 2. Updating the SELinux hook functions to check permissions against > > > all ancestor namespaces rather than just the current one, and consider > > > introducing a top-level global AVC to avoid the need to check against > > > each per-namespace AVC on every check. > > I've now pushed another commit that introduces new permission checking > functions that are namespace-aware and started converting some of the > hook functions to use them, beginning with checks between a cred and a > target task, e.g. selinux_task_kill(). With this change, I verified > that a process in a child namespace could NOT violate the policy of > the parent when sending signals. There remains a lot of work to > convert the rest of the permission checks to do the same. Reproducing > this particular case can be done as follows: > > # Temporarily switch to permissive to allow the following runcon to work > sudo setenforce 0 > # Switch to a confined user > runcon staff_u:staff_r:staff_t:s0-s0:c0.c1023 /bin/bash > # Switch back to enforcing mode > sudo setenforce 1 > # Become root, still as a staff user/role/domain > sudo bash > # Unshare SELinux namespace > echo 1 > /sys/fs/selinux/unshare > # Unshare mount and network namespaces for selinuxfs and > NETLINK_SELINUX isolation > unshare -m -n > # Unmount the parent's selinuxfs and mount our own private instance > umount /sys/fs/selinux > mount -t selinuxfs none /sys/fs/selinux > # Load a policy into our namespace, prior to this we don't have one > load_policy > # Switch to unconfined in our namespace > runcon unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 /bin/bash > # Check enforcing mode in our namespace, should be permissive still > getenforce > # Try to kill a process running unconfined in the parent namespace > # Get a PID from a ps -eZ from a shell in the parent namespace not here. > kill <pid-of-unconfined-process-in-parent> > # Should get Permission denied due to parent denying access. > > Next step is to introduce additional namespace-aware permission > checking functions that take things other than just cred-to-task and > convert additional permission checks in the hook functions to use > them. As I've been converting the permission checks to use namespace-aware helpers, I've noted policy capabilities (as in network_peer_controls, open_perms, extended_socket_class, always_check_network, etc) as a case that isn't cleanly handled by my current approach. At present, the hook functions are just checking the value of the policy capability in the current SELinux namespace to decide which branch of the hook function to follow, and then within whichever branch is selected, each permission check that is converted to using namespace-aware helpers is checking permissions against the current and all ancestor namespaces. So for example, if the current namespace enables open permission, then it will check open permission against the current namespace and all ancestors, irrespective of whether the ancestor enabled that capability or not. Similarly, if the current namespace disables open permission, then it will NOT check open permission against any namespaces, even if the ancestors enabled it. Lifting the logic to iterate through all of the namespaces up into each hook function is possible but rather painful. That said, it may be unavoidable if we want to support different policy capability settings between child namespaces and their ancestors, or prevent child namespaces from effectively disabling certain checks by disabling those capabilities. If I were to go down that route, then I would also need to resolve how to handle the non-permission checking logic in each hook across multiple namespaces, e.g. labeling logic. Currently it is just assigning SIDs/contexts to objects based on the current namespace, then checking permissions against all namespaces with that SID/context. But if each hook function has its body wrapped with the same loop that currently exists inside the namespace-aware permission checking functions to iterate through the namespaces, then we would actually end up with multiple SIDs/contexts computed and need to decide which one to use (or special case that part of the body to skip doing so for anything but the first/current namespace). Thoughts?