]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
apparmor: shift ouid when mediating hard links in userns
authorGabriel Totev <gabriel.totev@zetier.com>
Wed, 16 Apr 2025 22:42:08 +0000 (18:42 -0400)
committerJohn Johansen <john.johansen@canonical.com>
Sun, 20 Jul 2025 09:19:27 +0000 (02:19 -0700)
When using AppArmor profiles inside an unprivileged container,
the link operation observes an unshifted ouid.
(tested with LXD and Incus)

For example, root inside container and uid 1000000 outside, with
`owner /root/link l,` profile entry for ln:

/root$ touch chain && ln chain link
==> dmesg
apparmor="DENIED" operation="link" class="file"
namespace="root//lxd-feet_<var-snap-lxd-common-lxd>" profile="linkit"
name="/root/link" pid=1655 comm="ln" requested_mask="l" denied_mask="l"
fsuid=1000000 ouid=0 [<== should be 1000000] target="/root/chain"

Fix by mapping inode uid of old_dentry in aa_path_link() rather than
using it directly, similarly to how it's mapped in __file_path_perm()
later in the file.

Signed-off-by: Gabriel Totev <gabriel.totev@zetier.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
security/apparmor/file.c

index 65e1d29af792f61120d2e57004ead5bdf9675a99..5504059d6101ebad7f9139c4dc6ab93f4dca7d90 100644 (file)
@@ -430,9 +430,11 @@ int aa_path_link(const struct cred *subj_cred,
 {
        struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry };
        struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry };
+       struct inode *inode = d_backing_inode(old_dentry);
+       vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_idmap(target.mnt), inode);
        struct path_cond cond = {
-               d_backing_inode(old_dentry)->i_uid,
-               d_backing_inode(old_dentry)->i_mode
+               .uid = vfsuid_into_kuid(vfsuid),
+               .mode = inode->i_mode,
        };
        char *buffer = NULL, *buffer2 = NULL;
        struct aa_profile *profile;