summaryrefslogtreecommitdiff
path: root/security/apparmor/include
AgeCommit message (Collapse)Author
14 daysapparmor: fix deadlock in complain-mode change_hatJohn Johansen
The use of change_hat when in complain mode can cause a deadlock when the hat doesn't exist and a new learning profile is created for the missing profile. This is because change_hat() has taken the lock to search the hat list and creating the new learning profile needs to take the lock to add it to the list. From the bug report: Originally found in 7.0.0 in LTS ubuntu 26.04 with pam_apparmor + su in complain mode set to change hats. Then verified in newest available vanilla kernel I've compiled to see if still present: 7.2-rc7 vanilla -> affected checked also some other kernels: 6.18.44 vanilla -> affected 6.12.95 with debian patches -> unaffected On systems without bug (for example 6.12.95 debian) it just prints: aa_change_hat rc=0 On systems with bug, the executable always hangs, prints nothing and becomes unkillable. (And once stuck this way, it will cause any further hat changes to also cause the changing process to get stuck) Then in syslog you can find hint about cause: kernel: INFO: task hat:3409 blocked for more than 483 seconds. kernel: Not tainted 7.2.0-rc7 #1 kernel: "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. kernel: task:hat state:D stack:0 pid:3409 tgid:3409 ppid:2605 task_flags:0x400000 flags:0x00080800 kernel: Call Trace: kernel: <TASK> kernel: __schedule+0x48f/0xfe0 kernel: schedule+0x27/0xa0 kernel: schedule_preempt_disabled+0x15/0x30 kernel: __mutex_lock.constprop.0+0x569/0xa10 kernel: aa_new_learning_profile+0x15f/0x210 kernel: build_change_hat+0x19f/0x3b0 kernel: change_hat.isra.0+0x5dd/0xd60 kernel: aa_change_hat+0x2f3/0x710 kernel: aa_setprocattr_changehat+0x121/0x1f0 kernel: do_setattr+0x28c/0x340 kernel: apparmor_setselfattr+0x20/0x50 kernel: security_setselfattr+0xf6/0x110 kernel: __x64_sys_lsm_set_self_attr+0x53/0x90 kernel: do_syscall_64+0xdd/0x5e0 kernel: ? __mod_memcg_lruvec_state+0xfd/0x260 kernel: ? lruvec_stat_mod_folio+0x8d/0xd0 kernel: ? __folio_mod_stat+0x2d/0x90 kernel: ? map_anon_folio_pte_nopf+0xd1/0x1f0 kernel: ? do_anonymous_page+0x184/0xa10 kernel: ? __handle_mm_fault+0x805/0x870 kernel: ? count_memcg_events+0xef/0x230 kernel: ? handle_mm_fault+0x1f0/0x2f0 kernel: ? do_user_addr_fault+0x2bb/0x7b0 kernel: ? do_syscall_64+0x94/0x5e0 kernel: ? exc_page_fault+0x75/0x160 kernel: entry_SYSCALL_64_after_hwframe+0x76/0x7e kernel: RIP: 0033:0x7f815e134c8d kernel: RSP: 002b:00007fff6df94ea8 EFLAGS: 00000246 ORIG_RAX: 00000000000001cc kernel: RAX: ffffffffffffffda RBX: 0000556d8c81d040 RCX: 00007f815e134c8d kernel: RDX: 0000000000000046 RSI: 0000556d8c81d040 RDI: 0000000000000064 kernel: RBP: 00007fff6df94ef0 R08: 00007f815e212ac8 R09: 000000000000000c kernel: R10: 0000000000000000 R11: 0000000000000246 R12: 0000556d8c81d010 kernel: R13: 0000000000000026 R14: 0000000000000046 R15: 0000000000000064 kernel: </TASK> kernel: INFO: task hat:3409 is blocked on a mutex likely owned by task hat:3409. To fix the issue, lift the locking out of the core of aa_new_learning_profile(), introduce a wrapper function that takes the lock where needed, and have build_change_hat() call the core function that no longer takes the lock. In addition fix 4 other issues introduced by commit 32e92764d6f8d ("apparmor: grab ns lock and refresh when looking up changehat child profiles") - aa_get_profile_rcu() was replaced-by: aa_get_profile without the accompanying rcu_dereference_protected() - an extra aa_get_label(label) was introduced at the start of change_hat() without an accompanying aa_put_label() causing a reference count leak. - a reference count leak was introduced in the label_is_stale(label) case, where the newest profile would be leaked instead of the label passed to the function. - a potential UAF when the lookup walks up the tree with new_ns != ns the new label reference is put, and then used for the next lookup. The mutex_lock, will block replacement, and removal in the locked ns. However there are two cases where putting the reference can result in the label being freed even with the lock held. 1. the label does not have a list reference (possible for temporary or special profiles) in which case the put can trigger the cleanup. 2. the new label reference is in a different namespace, which does not have a lock held on it. This extends case 1 to also include replacement, and removal that could be occurring in the namespace new is in. Reported-by: Martin Petricek <mp@petricek.net> Link: https://lists.ubuntu.com/archives/apparmor/2026-August/014907.html Fixes: 32e92764d6f8d ("apparmor: grab ns lock and refresh when looking up changehat child profiles") Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-08-10apparmor: constify aa_label parameters on read-only query helpersJohn Johansen
Several label helpers only read from their struct aa_label * arguments: they compare labels, test subset relationships, or check the mediation bitmask, all via direct field/index access. Mark those parameters const struct aa_label * to document intent and let the compiler enforce that the label is not modified. The converted functions are: - label_mediates(), label_mediates_safe() - aa_label_cmp() (and its vec_cmp() helper) - __aa_label_next_not_in_set(), aa_label_is_subset(), aa_label_is_unconfined_subset() - __aa_subj_label_is_cached() - aa_label_next_confined(), aa_label_next_in_merge() These all access the label through direct indexing or manual iterators rather than the label_for_each()/fn_for_each() macros, which are not const-correct and so gate the majority of the remaining label consumers (the print, match, and permission-check paths) from being constified. No functional change. Signed-off-by: John Johansen <john.johansen@canonical.com> Assisted-by: Claude:claude-opus-4.8
2026-08-10apparmor: constify aa_dfa parameters on read-only compute pathsJohn Johansen
Most uses of aa_dfa a read-only walking of the dfa. Have the compiler enforce this. Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-08-10apparmor: constify aa_profile parameters on read-only compute pathsJohn Johansen
A number of functions take a struct aa_profile * argument that is only ever read from: they compute DFA matches or apply the profile's mode flags without modifying the profile, taking a reference on it, or touching its embedded label. Mark those parameters const struct aa_profile * to document intent and let the compiler enforce it. The converted functions are the permission "compute" path plus a few pure readers: - aa_apply_modes_to_perms(), aa_profile_match_label() - AUDIT_MODE() - aa_label_match() and its match_component()/label_compound_match()/ label_components_match() helpers (label.c) - match_component()/label_compound_match()/label_components_match()/ label_match()/change_profile_perms()/aa_xattrs_match() (domain.c) - match_iface()/match_addr_iface()/match_addr_iface_label()/ skb_match_to_sk()/skb_match_to_cmd() (af_inet.c) - aa_profile_capget(), path_flags(), profile_query_cb() The remaining aa_profile * parameters cannot be made const: the audit path stores &profile->label into the owned, refcounted apparmor_audit_data.subj_label/peer fields, and the domain/lifecycle paths take references on the profile's embedded label (aa_get_label()/aa_get_newest_label()/aa_get_profile()) or write profile fields. No functional change. Signed-off-by: John Johansen <john.johansen@canonical.com> Assisted-by: Claude:claude-opus-4.8
2026-08-10apparmor: constify aa_perms parameters that are read-onlyJohn Johansen
Several functions take a struct aa_perms * argument that is only ever read from and never modified through the pointer. Mark those parameters const struct aa_perms * to document intent and let the compiler enforce that the permission set is not mutated. The converted functions are: - aa_check_perms() - aa_do_perms() - do_perms() (af_inet) - match_label() (af_unix) - verify_perm() - aa_perms_accum() / aa_perms_accum_raw() (@addend only) No functional change. Signed-off-by: John Johansen <john.johansen@canonical.com> Assisted-by: Claude:claude-opus-4.8
2026-08-10apparmor: refactory mount to use check_permsJohn Johansen
Move the mount permissions check to use the common backend aa_check_perms() to check permissions. This will make it so caching, audit, complain, logic can be handled consistently in a single place. Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-08-10apparmor: add audit mode to provide a mechanism to silence complain messagesJohn Johansen
Complain messages can be very noisy and fill the logs quickly. Allow complain (allow) messages to be silenced separate from denied messages. Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-08-10apparmor: mark static tables and structs as read onlyJohn Johansen
static tables, and structs that are initialized as part of their data section or during init should be read only to protect against accidental or malicous changes. Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-08-10apparmor: fix error debug output in fn_label_buildJohn Johansen
checking PTR_ERROR() is not correct to just determine if any error occured, instead use the IS_ERR macro and also output the PTR_ERR as part of the debug message. Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-08-10apparmor: make table entry count last enum for static tablesJohn Johansen
Instead of keeping an external define for the various tables indexed by an enum, make the size the last entry of the enum so the table size will get updated correctly with changes to the enum. Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-08-10apparmor: reserve mediation class for packet mediationJohn Johansen
Packet mediation is going to be added in the future, reserve a class for it. Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-08-10apparmor: refactor network sock mediation in preparation for inet mediationJohn Johansen
Refactor network mediation, introducing the stub code for the fine grained inet mediation. This is a preparatory step and does not change mediation. Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-08-10apparmor: make MEDIATES_AF_UNIX its own fnJohn Johansen
Hide the functionality of determinig unix mediation behind its own fn so it is easier to adjust the test in the future as it has different requirements than the other socket mediation. Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-08-10apparmor: fix out-of-bounds write when null terminating a label vecHyunwoo Kim
aa_vec_unique() null terminates at vec[n - dups] when VEC_FLAG_TERMINATE is passed. If the components are all distinct no duplicates are dropped, dups is 0 and the terminator goes to vec[n], so the caller has to provide room for n + 1 entries. aa_label_strn_parse() sets up its vector with vec_setup(profile, vec, len, gfp) and then calls aa_vec_unique(vec, len, VEC_FLAG_TERMINATE), but vec_setup() does not reserve the terminator entry. Up to LOCAL_VEC_ENTRIES it uses the local array of LOCAL_VEC_ENTRIES pointers, above that it allocates exactly len pointers. The terminator therefore lands one entry past the end of the local array when len is LOCAL_VEC_ENTRIES, and one entry past the end of the allocation when len is larger. len comes from the number of "//&" separated components in the label name and label_count_strn_entries() does not bound it. An unprivileged task reaches the parse by writing to /proc/self/attr/apparmor/current or through lsm_set_self_attr(2), both of which go through do_setattr(), and the name is parsed before the change_profile permission is checked. The query_label() path behind the securityfs .access file, which is mode 0666, performs no permission check at all. Every component has to resolve to a loaded profile, so a system with policy loaded is required. The other two VEC_FLAG_TERMINATE users work on a label vec that aa_label_alloc() has already sized with "+ 1 for null terminator entry on vec". Reserve the same entry in vec_setup() and DEFINE_VEC(). Passing len + 1 from the caller instead would move len == LOCAL_VEC_ENTRIES out of the local array and into kzalloc(). Fixes: f1bd904175e8 ("apparmor: add the base fns() for domain labels") Cc: stable@vger.kernel.org Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-08-07apparmor: optimize current_label_crit_section() with needputJohn Johansen
The {begin,end}_current_label_crit_section() has the same issue as the {__begin,__end} version. That is the check to see if the label has been updated in the end check forces an unnecessary memory barrier. We can optimize this the same way we do with the {__begin,__end} variant by passing in a local variable that carries the state information from the begin check into the end check. No functional change. Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-08-06apparmor: fix cred UAF caused by begin_current_label_crit_section()Jann Horn
AppArmor's begin_current_label_crit_section() is a scary function called from lots of LSM hooks (in particular VFS/socket-related ones) that checks if the label referenced by the current creds is marked FLAG_STALE, and if so, attempts to use aa_replace_current_label() to replace the creds with an updated version that uses a new label. The first problem with this is that it would directly lead to UAF of `struct cred` if anything in the kernel takes a pointer to the current creds and accesses these past a security hook invocation that replaces creds, like so: ``` const struct cred *cred = current_cred(); alloc_file_pseudo(...); uid_t uid = cred->euid; ``` I don't know if anything in the kernel actually does this, but I think it is very surprising that this pattern could lead to UAF. The second problem is that things go wrong when aa_replace_current_label() runs with overridden credentials. aa_replace_current_label() bails out if `current_cred() != current_real_cred()` (mirroring the check in proc_pid_attr_write()), but this check can't actually reliably detect overridden credentials because the overridden creds can be the same as the objective creds. So in approximately the following scenario, things go wrong: 1. task begins with <creds A> (as both objective and subjective creds), with refcount=2 2. task grabs an extra reference on <creds A> for overriding 3. task calls override_creds(<creds A>), which returns a pointer to the old subjective creds (<creds A>) 4. task enters AppArmor LSM hook 5. AppArmor checks that objective/subjective creds are equal 6. AppArmor replaces both cred pointers with <creds B> and drops 2 refs on <creds A> 7. task leaves AppArmor LSM hook 8. task calls revert_creds(<creds A>) 9. now task->cred is <creds A> while task->real_cred is <creds B>, but the task_struct logically holds two references to <creds B> 10. another task drops the extra reference on <creds A> that was used for overriding, refcount drops to 0 11. now task->real_cred points to freed creds At this point, any access to current_cred() will be UAF. I have a test case where I run aa-disable on a profile while a process using that profile is blocked on splice() from a FUSE passthrough file into a full pipe; after the profile update, the pipe becomes empty, splice() resumes, the credentials go out of sync, and a subsequent getuid() syscall results in a KASAN UAF splat. To fix this, instead of directly replacing creds, do it via task_work that will run at the end of the current syscall. (The point in time at which the cred replacement happens should have no correctness impact; it is just a performance optimization to avoid unnecessarily touching the refcount of the new label.) Note that AppArmor still performs direct cred replacements in the sb_pivotroot LSM hook after this change, and that direct cred replacements can still happen in VFS ->write() callbacks via proc_pid_attr_write(). There are two options for what to do with aa_dup_task_ctx(): Either explicitly reset new->label_replacement_pending after the entire aa_task_ctx has been copied, or switch to manually copying members over. I am switching to manually copying members over because that should make bugs more obvious. Cc: stable@vger.kernel.org Fixes: c75afcd153f6 ("AppArmor: contexts used in attaching policy to system objects") Signed-off-by: Jann Horn <jannh@google.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-06-29apparmor: Initial support for compressed policiesMaxime Bélair
This patch allows policies to be compressed in userspace and be sent to the kernel through the existing ".load" and ".replace" kernel interfaces. The benefits of this approach are: - Save kernel time when loading policies - Allow userspace to provide a higher level of compression than the one provided by the kernel (ZSTD_CLEVEL_DEFAULT), thus saving space. - Allow small embedded systems to only store the compressed version of policies in userspace, saving memory. Userspace-compressed policies improve system time by up to ~30% for big profiles. Signed-off-by: Maxime Bélair <maxime.belair@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-06-29apparmor: make include headers self-containedRyan Lee
Besides of resolving clangd IDE warnings, self-contained headers will be less likely to break if the surrounding includes in .c files using them change. Signed-off-by: Ryan Lee <ryan.lee@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-06-29apparmor: fix net.h and policy.h circular include patternRyan Lee
While the #ifdef guards prevent the circular include from blowing up, policy.h does not actually need anything from net.h. Remove, that include and instead include net.h in the other files that need it. Signed-off-by: Ryan Lee <ryan.lee@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-06-13apparmor: fix kernel-doc warningsRodrigo Zaiden
Fix two kernel-doc warnings: - non-kernel-doc comment marked with '/**' in af_unix.c - documented symbol name mismatch for aa_get_i_loaddata() in policy_unpack.h No functional changes. Signed-off-by: Rodrigo Zaiden <rodrigoffzz@gmail.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-06-13apparmor: fix use-after-free in rawdata dedup loopRuslan Valiyev
aa_replace_profiles() walks ns->rawdata_list to dedup the incoming policy blob against entries already attached to existing profiles. Per the kernel-doc on struct aa_loaddata, list membership does not hold a reference: profiles hold pcount, and when the last pcount drops, do_ploaddata_rmfs() is queued on a workqueue that takes ns->lock and removes the entry. Between dropping the last pcount and the workqueue running, an entry remains on the list with pcount == 0. aa_get_profile_loaddata() is an unconditional kref_get() on pcount, so when the dedup loop hits such an entry, refcount hardening reports refcount_t: addition on 0; use-after-free. inside aa_replace_profiles(), and the poisoned counter then trips "saturated" and "underflow" warnings on the subsequent uses of the same loaddata. Before commit a0b7091c4de4 ("apparmor: fix race on rawdata dereference") the dedup path used a get_unless_zero-style helper on a single counter, so the existing "if (tmp)" guard was meaningful. The split-refcount refactor introduced aa_get_profile_loaddata(), which has plain kref_get() semantics, and the guard quietly became a no-op. Introduce aa_get_profile_loaddata_not0(), matching the existing _not0 convention used by aa_get_profile_not0(), and use it for the rawdata_list dedup lookup so dying entries are skipped. Reproduced on x86_64 with v7.1-rc5 in QEMU+KVM running Ubuntu 24.04 + stress-ng 0.17.06: stress-ng --apparmor 1 --klog-check --timeout 60s Without this patch the three refcount_t warnings fire within a few seconds. With it the same 60 s run is clean. Coverage is a smoke-test only; a longer soak with CONFIG_KASAN, CONFIG_KCSAN and CONFIG_PROVE_LOCKING would be welcome from anyone with the cycles. Fixes: a0b7091c4de4 ("apparmor: fix race on rawdata dereference") Reported-by: Colin Ian King <colin.i.king@gmail.com> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221513 Cc: stable@vger.kernel.org Signed-off-by: Ruslan Valiyev <linuxoid@gmail.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-06-13apparmor: remove or add symlinks to rawdata according to export_binaryGeorgia Garcia
When the export_binary parameter is set, then rawdata is available and there should be a symbolic link for the rawdata in the profile directory in apparmorfs. If the parameter is unset, then the symlinks should not exist. The issue arises when changing the value of export_binary on runtime and replacing profiles. If export_binary was set when the profile was originally loaded, then changed to 0 and the profile was reloaded, then the symbolic links would still exist but would return ENOENT because the rawdata no longer exists. On the opposite side, if export_binary was unset when the profile was originally loaded, then changed to 1 and the profile was reloaded, then the symbolic links would not exist, even though the rawdata does. Fixes: d61c57fde8191 ("apparmor: make export of raw binary profile to userspace optional") Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-06-13apparmor: make fn_label_build() capable of handling not supportedJohn Johansen
Currently fn_label_build() callback fns must provide a transition or failure. Change this so that a callback can indicate it should be skipped/not be involved in the label being built. This will be useful when building object labels based on mediation flags, as to whether the label should be set. Existing callers can keep treating NULL return as an error because none of those callback fns support skipping, but instead of the old error handling replace with AA_BUG. Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-06-13apparmor: change fn_label_build() call to not return NULLJohn Johansen
Previously fn_label_build() was accepting a NULL which represented ENOMEM return and ERR_PTR for errors. Clean this up by requiring the cb fn to return an ERR_PTR or valid value. Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-06-13apparmor: add a conditional version of get_newest_labelJohn Johansen
get_newest_label() will always return a refcount, on the profile it returns. However there are cases where we only need the refcount if the label is stale and get_newest_label() will return a different label. Optimize this by making the get/put happen conditionally, by keeping a flag indicating if the get was performed and a put is needed. Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-03-09apparmor: fix race between freeing data and fs accessing itJohn Johansen
AppArmor was putting the reference to i_private data on its end after removing the original entry from the file system. However the inode can aand does live beyond that point and it is possible that some of the fs call back functions will be invoked after the reference has been put, which results in a race between freeing the data and accessing it through the fs. While the rawdata/loaddata is the most likely candidate to fail the race, as it has the fewest references. If properly crafted it might be possible to trigger a race for the other types stored in i_private. Fix this by moving the put of i_private referenced data to the correct place which is during inode eviction. Fixes: c961ee5f21b20 ("apparmor: convert from securityfs to apparmorfs for policy ns files") Reported-by: Qualys Security Advisory <qsa@qualys.com> Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com> Reviewed-by: Maxime Bélair <maxime.belair@canonical.com> Reviewed-by: Cengiz Can <cengiz.can@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-03-09apparmor: fix race on rawdata dereferenceJohn Johansen
There is a race condition that leads to a use-after-free situation: because the rawdata inodes are not refcounted, an attacker can start open()ing one of the rawdata files, and at the same time remove the last reference to this rawdata (by removing the corresponding profile, for example), which frees its struct aa_loaddata; as a result, when seq_rawdata_open() is reached, i_private is a dangling pointer and freed memory is accessed. The rawdata inodes weren't refcounted to avoid a circular refcount and were supposed to be held by the profile rawdata reference. However during profile removal there is a window where the vfs and profile destruction race, resulting in the use after free. Fix this by moving to a double refcount scheme. Where the profile refcount on rawdata is used to break the circular dependency. Allowing for freeing of the rawdata once all inode references to the rawdata are put. Fixes: 5d5182cae401 ("apparmor: move to per loaddata files, instead of replicating in profiles") Reported-by: Qualys Security Advisory <qsa@qualys.com> Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com> Reviewed-by: Maxime Bélair <maxime.belair@canonical.com> Reviewed-by: Cengiz Can <cengiz.can@canonical.com> Tested-by: Salvatore Bonaccorso <carnil@debian.org> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-03-09apparmor: fix differential encoding verificationJohn Johansen
Differential encoding allows loops to be created if it is abused. To prevent this the unpack should verify that a diff-encode chain terminates. Unfortunately the differential encode verification had two bugs. 1. it conflated states that had gone through check and already been marked, with states that were currently being checked and marked. This means that loops in the current chain being verified are treated as a chain that has already been verified. 2. the order bailout on already checked states compared current chain check iterators j,k instead of using the outer loop iterator i. Meaning a step backwards in states in the current chain verification was being mistaken for moving to an already verified state. Move to a double mark scheme where already verified states get a different mark, than the current chain being kept. This enables us to also drop the backwards verification check that was the cause of the second error as any already verified state is already marked. Fixes: 031dcc8f4e84 ("apparmor: dfa add support for state differential encoding") Reported-by: Qualys Security Advisory <qsa@qualys.com> Tested-by: Salvatore Bonaccorso <carnil@debian.org> Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com> Reviewed-by: Cengiz Can <cengiz.can@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-03-09apparmor: fix unprivileged local user can do privileged policy managementJohn Johansen
An unprivileged local user can load, replace, and remove profiles by opening the apparmorfs interfaces, via a confused deputy attack, by passing the opened fd to a privileged process, and getting the privileged process to write to the interface. This does require a privileged target that can be manipulated to do the write for the unprivileged process, but once such access is achieved full policy management is possible and all the possible implications that implies: removing confinement, DoS of system or target applications by denying all execution, by-passing the unprivileged user namespace restriction, to exploiting kernel bugs for a local privilege escalation. The policy management interface can not have its permissions simply changed from 0666 to 0600 because non-root processes need to be able to load policy to different policy namespaces. Instead ensure the task writing the interface has privileges that are a subset of the task that opened the interface. This is already done via policy for confined processes, but unconfined can delegate access to the opened fd, by-passing the usual policy check. Fixes: b7fd2c0340eac ("apparmor: add per policy ns .load, .replace, .remove interface files") Reported-by: Qualys Security Advisory <qsa@qualys.com> Tested-by: Salvatore Bonaccorso <carnil@debian.org> Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com> Reviewed-by: Cengiz Can <cengiz.can@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-03-09apparmor: fix: limit the number of levels of policy namespacesJohn Johansen
Currently the number of policy namespaces is not bounded relying on the user namespace limit. However policy namespaces aren't strictly tied to user namespaces and it is possible to create them and nest them arbitrarily deep which can be used to exhaust system resource. Hard cap policy namespaces to the same depth as user namespaces. Fixes: c88d4c7b049e8 ("AppArmor: core policy routines") Reported-by: Qualys Security Advisory <qsa@qualys.com> Reviewed-by: Ryan Lee <ryan.lee@canonical.com> Reviewed-by: Cengiz Can <cengiz.can@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-01-29apparmor: split xxx_in_ns into its two separate semantic use casesJohn Johansen
This patch doesn't change current functionality, it switches the two uses of the in_ns fns and macros into the two semantically different cases they are used for. xxx_in_scope for checking mediation interaction between profiles xxx_in_view to determine which profiles are visible.The scope will always be a subset of the view as profiles that can not see each other can not interact. The split can not be completely done for label_match because it has to distinct uses matching permission against label in scope, and checking if a transition to a profile is allowed. The transition to a profile can include profiles that are in view but not in scope, so retain this distinction as a parameter. While at the moment the two uses are very similar, in the future there will be additional differences. So make sure the semantics differences are present in the code. Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-01-29apparmor: refactor/cleanup cred helper fns.John Johansen
aa_cred_raw_label() and cred_label() now do the same things so consolidate to cred_label() Document the crit section use and constraints better and refactor __begin_current_label_crit_section() into a base fn __begin_cred_crit_section() and a wrapper that calls the base with current cred. Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-01-29apparmor: fix label and profile debug macrosJohn Johansen
The label and profile debug macros were not correctly pasting their var args. Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-01-29apparmor: add support loading per permission taggingJohn Johansen
Add support for the per permission tag index for a given permission set. This will be used by both meta-data tagging, to allow annotating accept states with context and debug information. As well as by rule tainting and triggers to specify the taint or trigger to be applied. Since these are low frequency ancillary data items they are stored in a tighter packed format to that allows for sharing and reuse of the strings between permissions and accept states. Reducing the amount of kernel memory use at the cost of having to go through a couple if index based indirections. The tags are just strings that has no meaning with out context. When used as meta-data for auditing and debugging its entirely information for userspace, but triggers, and tainting can be used to affect the domain. However they all exist in the same packed data set and can be shared between different uses. Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-01-22apparmor: make str table more generic and be able to have multiple entriesJohn Johansen
The strtable is currently limited to a single entry string on unpack even though domain has the concept of multiple entries within it. Make this a reality as it will be used for tags and more advanced domain transitions. Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2026-01-22apparmor: Fix & Optimize table creation from possibly unaligned memoryHelge Deller
Source blob may come from userspace and might be unaligned. Try to optize the copying process by avoiding unaligned memory accesses. - Added Fixes tag - Added "Fix &" to description as this doesn't just optimize but fixes a potential unaligned memory access Fixes: e6e8bf418850d ("apparmor: fix restricted endian type warnings for dfa unpack") Signed-off-by: Helge Deller <deller@gmx.de> [jj: remove duplicate word "convert" in comment trigger checkpatch warning] Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-10-22apparmor: move initcalls to the LSM frameworkPaul Moore
Reviewed-by: Kees Cook <kees@kernel.org> Acked-by: John Johansen <john.johansen@canonical.com> Signed-off-by: Paul Moore <paul@paul-moore.com>
2025-08-04Merge tag 'apparmor-pr-2025-08-04' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor Pull apparmor updates from John Johansen: "This has one major feature, it pulls in a cleaned up version of af_unix mediation that Ubuntu has been carrying for years. It is placed behind a new abi to ensure that it does cause policy regressions. With pulling in the af_unix mediation there have been cleanups and some refactoring of network socket mediation. This accounts for the majority of the changes in the diff. In addition there are a few improvements providing minor code optimizations. several code cleanups, and bug fixes. Features: - improve debug printing - carry mediation check on label (optimization) - improve ability for compiler to optimize __begin_current_label_crit_section - transition for a linked list of rulesets to a vector of rulesets - don't hardcode profile signal, allow it to be set by policy - ability to mediate caps via the state machine instead of lut - Add Ubuntu af_unix mediation, put it behind new v9 abi Cleanups: - fix typos and spelling errors - cleanup kernel doc and code inconsistencies - remove redundant checks/code - remove unused variables - Use str_yes_no() helper function - mark tables static where appropriate - make all generated string array headers const char *const - refactor to doc semantics of file_perm checks - replace macro calls to network/socket fns with explicit calls - refactor/cleanup socket mediation code preparing for finer grained mediation of different network families - several updates to kernel doc comments Bug fixes: - fix incorrect profile->signal range check - idmap mount fixes - policy unpack unaligned access fixes - kfree_sensitive() where appropriate - fix oops when freeing policy - fix conflicting attachment resolution - fix exec table look-ups when stacking isn't first - fix exec auditing - mitigate userspace generating overly large xtables" * tag 'apparmor-pr-2025-08-04' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor: (60 commits) apparmor: fix: oops when trying to free null ruleset apparmor: fix Regression on linux-next (next-20250721) apparmor: fix test error: WARNING in apparmor_unix_stream_connect apparmor: Remove the unused variable rules apparmor: fix: accept2 being specifie even when permission table is presnt apparmor: transition from a list of rules to a vector of rules apparmor: fix documentation mismatches in val_mask_to_str and socket functions apparmor: remove redundant perms.allow MAY_EXEC bitflag set apparmor: fix kernel doc warnings for kernel test robot apparmor: Fix unaligned memory accesses in KUnit test apparmor: Fix 8-byte alignment for initial dfa blob streams apparmor: shift uid when mediating af_unix in userns apparmor: shift ouid when mediating hard links in userns apparmor: make sure unix socket labeling is correctly updated. apparmor: fix regression in fs based unix sockets when using old abi apparmor: fix AA_DEBUG_LABEL() apparmor: fix af_unix auditing to include all address information apparmor: Remove use of the double lock apparmor: update kernel doc comments for xxx_label_crit_section apparmor: make __begin_current_label_crit_section() indicate whether put is needed ...
2025-07-20apparmor: transition from a list of rules to a vector of rulesJohn Johansen
The set of rules on a profile is not dynamically extended, instead if a new ruleset is needed a new version of the profile is created. This allows us to use a vector of rules instead of a list, slightly reducing memory usage and simplifying the code. Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-07-20apparmor: make sure unix socket labeling is correctly updated.John Johansen
When a unix socket is passed into a different confinement domain make sure its cached mediation labeling is updated to correctly reflect which domains are using the socket. Fixes: c05e705812d1 ("apparmor: add fine grained af_unix mediation") Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-07-15apparmor: fix regression in fs based unix sockets when using old abiJohn Johansen
Policy loaded using abi 7 socket mediation was not being applied correctly in all cases. In some cases with fs based unix sockets a subset of permissions where allowed when they should have been denied. This was happening because the check for if the socket was an fs based unix socket came before the abi check. But the abi check is where the correct path is selected, so having the fs unix socket check occur early would cause the wrong code path to be used. Fix this by pushing the fs unix to be done after the abi check. Fixes: dcd7a559411e ("apparmor: gate make fine grained unix mediation behind v9 abi") Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-07-15apparmor: fix AA_DEBUG_LABEL()John Johansen
AA_DEBUG_LABEL() was not specifying it vargs, which is needed so it can output debug parameters. Fixes: 71e6cff3e0dd ("apparmor: Improve debug print infrastructure") Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-07-15apparmor: fix af_unix auditing to include all address informationJohn Johansen
The auditing of addresses currently doesn't include the source address and mixes source and foreign/peer under the same audit name. Fix this so source is always addr, and the foreign/peer is peer_addr. Fixes: c05e705812d1 ("apparmor: add fine grained af_unix mediation") Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-07-15apparmor: Remove use of the double lockJohn Johansen
The use of the double lock is not necessary and problematic. Instead pull the bits that need locks into their own sections and grab the needed references. Fixes: c05e705812d1 ("apparmor: add fine grained af_unix mediation") Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-07-15apparmor: update kernel doc comments for xxx_label_crit_sectionJohn Johansen
Add a kernel doc header for __end_current_label_crit_section(), and update the header for __begin_current_label_crit_section(). Fixes: b42ecc5f58ef ("apparmor: make __begin_current_label_crit_section() indicate whether put is needed") Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-07-15apparmor: make __begin_current_label_crit_section() indicate whether put is ↵Mateusz Guzik
needed Same as aa_get_newest_cred_label_condref(). This avoids a bunch of work overall and allows the compiler to note when no clean up is necessary, allowing for tail calls. This in particular happens in apparmor_file_permission(), which manages to tail call aa_file_perm() 105 bytes in (vs a regular call 112 bytes in followed by branches to figure out if clean up is needed). Signed-off-by: Mateusz Guzik <mjguzik@gmail.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-07-15apparmor: mitigate parser generating large xtablesJohn Johansen
Some versions of the parser are generating an xtable transition per state in the state machine, even when the state machine isn't using the transition table. The parser bug is triggered by commit 2e12c5f06017 ("apparmor: add additional flags to extended permission.") In addition to fixing this in userspace, mitigate this in the kernel as part of the policy verification checks by detecting this situation and adjusting to what is actually used, or if not used at all freeing it, so we are not wasting unneeded memory on policy. Fixes: 2e12c5f06017 ("apparmor: add additional flags to extended permission.") Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-05-25apparmor: Document that label must be last member in struct aa_profileJohn Johansen
The label struct is variable length. While its use in struct aa_profile is fixed length at 2 entries the variable length member needs to be the last member in the structure. The code already does this but the comment has it in the wrong location. Also add a comment to ensure it stays at the end of the structure. While we are at it, update the documentation for other profile members as well. Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-05-25apparmor: fix loop detection used in conflicting attachment resolutionRyan Lee
Conflicting attachment resolution is based on the number of states traversed to reach an accepting state in the attachment DFA, accounting for DFA loops traversed during the matching process. However, the loop counting logic had multiple bugs: - The inc_wb_pos macro increments both position and length, but length is supposed to saturate upon hitting buffer capacity, instead of wrapping around. - If no revisited state is found when traversing the history, is_loop would still return true, as if there was a loop found the length of the history buffer, instead of returning false and signalling that no loop was found. As a result, the adjustment step of aa_dfa_leftmatch would sometimes produce negative counts with loop- free DFAs that traversed enough states. - The iteration in the is_loop for loop is supposed to stop before i = wb->len, so the conditional should be < instead of <=. This patch fixes the above bugs as well as the following nits: - The count and size fields in struct match_workbuf were not used, so they can be removed. - The history buffer in match_workbuf semantically stores aa_state_t and not unsigned ints, even if aa_state_t is currently unsigned int. - The local variables in is_loop are counters, and thus should be unsigned ints instead of aa_state_t's. Fixes: 21f606610502 ("apparmor: improve overlapping domain attachment resolution") Signed-off-by: Ryan Lee <ryan.lee@canonical.com> Co-developed-by: John Johansen <john.johansen@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-05-17apparmor: ensure WB_HISTORY_SIZE value is a power of 2Ryan Lee
WB_HISTORY_SIZE was defined to be a value not a power of 2, despite a comment in the declaration of struct match_workbuf stating it is and a modular arithmetic usage in the inc_wb_pos macro assuming that it is. Bump WB_HISTORY_SIZE's value up to 32 and add a BUILD_BUG_ON_NOT_POWER_OF_2 line to ensure that any future changes to the value of WB_HISTORY_SIZE respect this requirement. Fixes: 136db994852a ("apparmor: increase left match history buffer size") Signed-off-by: Ryan Lee <ryan.lee@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>