| From 7007a83026676d323ed412554d4d13e981c6785c Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 16 Apr 2025 18:42:08 -0400 |
| Subject: apparmor: shift ouid when mediating hard links in userns |
| |
| From: Gabriel Totev <gabriel.totev@zetier.com> |
| |
| [ Upstream commit c5bf96d20fd787e4909b755de4705d52f3458836 ] |
| |
| 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> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| security/apparmor/file.c | 6 ++++-- |
| 1 file changed, 4 insertions(+), 2 deletions(-) |
| |
| diff --git a/security/apparmor/file.c b/security/apparmor/file.c |
| index 6fd21324a097..a51b83cf6968 100644 |
| --- a/security/apparmor/file.c |
| +++ b/security/apparmor/file.c |
| @@ -436,9 +436,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; |
| -- |
| 2.39.5 |
| |