| Age | Commit message (Collapse) | Author |
|
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>
|
|
label replacement can result in the need for locking on two separate
trees. Currently this is done by locking the tree to remove and then
the tree to add to.
For compound labels the race can result in the old label proxy
pointing to the the new label that lost the race and that was not
inserted in to the new tree. This does not break mediation, but it does
result in a task that will not update its profile correctly on future
mediation, and that will leak its refcount due to a circular reference
in its proxy, resulting in a memory leak.
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
The full bprm does not need to be passed into xattrs_match, so only
pass in the path, and propagate the change backup the call stack until
bprm is actually needed.
Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
If a task is already confined by a stack the unprivileged transition
restriction on unconfined is not correctly, applied. This results in
an escape if two transitions through an unconfined profile can be
executed.
Fix this by pushing the check into the per profile label build. The
check will always be done against unconfined and result in a stack of
just the unconfined component when necessary.
Fixes: 2d9da9b188b8 ("apparmor: allow restricting unprivileged change_profile")
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
Packet mediation is going to be added in the future, reserve a class
for it.
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
both of these fns are using ifdef CONFIG_NETWORK_SECMARK and related
to AppArmor's secmark based mediation, so move them together.
Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
Make sure all the network mediation hooks are next to each other in
a logical block. This just makes it easier to read/understand the
network mediation code.
Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
Make it easier for distros to support the network backwards compat
patch by refactoring the code to minimize the changes needed.
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
Continue preparing for fine grained inet mediation by setting up the
stacked mediation callback. This lifts address mapping and audit
context setup out of the stacking loop and pushing the mediation down
into the callback fn.
While this patch sets up the structure for fine grained mediation it
does not change mediation and the callback fns only call the default
mediation that will be used when fine grained inet mediation is not
available.
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
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>
|
|
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>
|
|
copy_from_user does not return an error code and the check should be
setting the error code.
Fixes: 8b236f99edf8 ("apparmor: Initial support for compressed policies")
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
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>
|
|
verify_tags() validates the tagset table unpacked from a policy blob.
For each set it reads a count and checks that advancing the index by
that count stays inside sets.table[]:
u32 cnt = tags->sets.table[i];
if (i+cnt >= tags->sets.size) {
i, cnt and sets.size are all u32, so i+cnt is evaluated modulo 2^32.
sets.table[] is filled by unpack_tagsets() with aa_unpack_u32(), so
every entry is a raw unbounded 32-bit word taken from the policy blob,
and verify_tags() is the function that is supposed to validate it. A
count close to U32_MAX makes the sum wrap to a small value, the guard
passes, and the inner loop then walks sets.table[++i] past the end of
the kcalloc(size, sizeof(u32)) allocation.
Note that sets.size is bounded by 65535, because unpack_tagsets() reads
it with aa_unpack_array() as a u16, so the wrap cannot be reached by
growing the table; it is reached purely through the attacker-supplied
count.
With sets.size = 2 and sets.table = { 0, 0xffffffff }:
i = 0: cnt = 0, guard 0 + 0 >= 2 is false, inner loop does not run
i = 1: cnt = 0xffffffff, guard (1 + 0xffffffff) mod 2^32 == 0 >= 2 is
false, so the guard is bypassed and the inner loop reads
sets.table[2] -- one element past a two element allocation
The walk continues until an out-of-bounds value happens to be >=
hdrs.size or the access faults, so a crafted policy yields an
out-of-bounds read on the policy load path
(aa_replace_profiles -> aa_unpack -> unpack_policydb -> unpack_tags ->
verify_tags). unpack_tags() runs before the perms and DFA tables are
unpacked, so no other table needs to be well formed to reach it.
Policy load is gated by aa_may_manage_policy(), which checks
CAP_MAC_ADMIN relative to the subject's own user namespace rather than
the init user namespace, so with the default
unprivileged_userns_apparmor_policy=1 the path is reachable from an
unprivileged task in a matched-level nested namespace, not only by a
globally privileged one.
Perform the addition in u64 so that it cannot wrap, restoring the
intended i + cnt < sets.size guarantee.
Fixes: 3d28e2397af7 ("apparmor: add support loading per permission tagging")
Signed-off-by: Fabrice Derepas <fabrice.derepas@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
When inspecting the credentials of another task, objective credentials
(->real_cred, accessed with __task_cred()) must always be used.
Accessing ->cred on a non-current task is forbidden unless that task is
being created or destroyed; a task is allowed to change its own ->cred
pointer with no synchronization, and changing ->cred should only affect the
current syscall.
smack_file_send_sigiotask() was accessing both sets of credentials: First
tsk->cred, then __task_cred(tsk).
Fix it, always access the objective credentials here.
I have tested that this bug can lead to a KASAN-reported UAF of struct cred
in smack_file_send_sigiotask(), and that this fix prevents the race.
Cc: stable@vger.kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
|
|
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>
|
|
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>
|
|
SEND_SIG_NOINFO is defined as ((struct kernel_siginfo *) 0), so passing
NULL works, but:
- this works "by accident" and looks as if the caller doesn't understand
the signal sending API.
- more importantly, this hides the usage of SEND_SIG_NOINFO from grep,
and this is really bad.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Georgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux
Pull selinux fixes from Paul Moore:
- Continue to improve the validation of SELinux policies during load
- Fix a SELinux regression caused by bpffs changes in v7.2-rc1
- Fix a SELinux preformance regression caused by SELinux changes in
v7.2-rc1
* tag 'selinux-pr-20260805' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
selinux: check level category sets once at load time
selinux: require every boolean value to be defined
selinux: reject an unclaimed class value in security_get_classes()
selinux: require a class's permission values to cover its permission count
selinux: do not cancel a policy conversion that never started
selinux: bpf: check SBLABEL_MNT before isec init
selinux: reject a class permission count below its inherited common
selinux: reject a permission value exceeding the class permission count
|
|
When a signed policy is not mandatory, userspace can write IMA policy rules
directly to the securityfs policy file:
echo -e "measure func=BPRM_CHECK mask=MAY_EXEC\n" \
"audit func=BPRM_CHECK mask=MAY_EXEC\n" \
> /sys/kernel/security/ima/policy
or by cat'ing the entire IMA custom policy file:
cat ima-policy-file > /sys/kernel/security/ima/policy
Because these rules originate from userspace and cross the userspace/kernel
trust boundary, measure the raw write buffer before parsing, regardless of
whether the new policy will be accepted or not. This can be caught when
'measure func=POLICY_CHECK' is enabled (e.g., ima_policy=tcb). The
measurement template is forced to ima-buf.
This follows the "measure & load" paradigm, exposing potential bugs in
the policy code and detecting attempts to corrupt IMA. It also completes
the POLICY_CHECK hook, which already measures partial policy load by file.
To verify the template data hash value, convert the buffer policy data
to binary:
grep "ima_policy_written" \
/sys/kernel/security/integrity/ima/ascii_runtime_measurements | \
tail -1 | cut -d' ' -f 6 | xxd -r -p | sha256sum
Suggested-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Enrico Bravi <enrico.bravi@polito.it>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
|
|
IMA policy can be written multiple times in the securityfs policy file
at runtime if CONFIG_IMA_WRITE_POLICY=y. When IMA_APPRAISE_POLICY is
required, the policy needs to be signed to be loaded, writing the absolute
path of the file containing the new policy:
echo /path/of/custom_ima_policy > /sys/kernel/security/ima/policy
When this is not required, policy can be written directly, rule by rule:
echo -e "measure func=BPRM_CHECK mask=MAY_EXEC\n" \
"audit func=BPRM_CHECK mask=MAY_EXEC\n" \
> /sys/kernel/security/ima/policy
In this case, a new policy can be loaded without being measured or
appraised.
Add a new critical data record to measure the textual policy
representation when it becomes effective. Include in the
architecture-specific policy the new critical data record only when it
is not mandatory to load a signed policy. Additionally, enable the
policy serialization code even when CONFIG_IMA_READ_POLICY=n.
To verify the template data hash value, convert the buffer policy data
to binary:
grep "ima_policy_loaded" \
/sys/kernel/security/integrity/ima/ascii_runtime_measurements | \
tail -1 | cut -d' ' -f 6 | xxd -r -p | sha256sum
Signed-off-by: Enrico Bravi <enrico.bravi@polito.it>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
|
|
Instantiate the file_truncate and path_truncate LSM hooks to reset the
action cache flags (IMA_DONE_MASK) as soon as truncation is requested,
so the file, based on policy, is re-collected, re-measured, re-audited,
and re-appraised on next access.
Tested-by: Frederick Lawler <fred@cloudflare.com>
Cc: stable@vger.kernel.org
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
|
|
The Linux IMA (Integrity Measurement Architecture) subsystem used for
secure boot, file integrity, or remote attestation cannot be a loadable
module for few reasons listed below:
o Boot-Time Integrity: IMA’s main role is to measure and appraise files
before they are used. This includes measuring critical system files
during early boot (e.g., init, init scripts, login binaries). If IMA
were a module, it would be loaded too late to cover those.
o TPM Dependency: IMA integrates tightly with the TPM to record
measurements into PCRs. The TPM must be initialized early (ideally
before init_ima()), which aligns with IMA being built-in.
o Security Model: IMA is part of a Trusted Computing Base (TCB). Making
it a module would weaken the security model, as a potentially
compromised system could delay or tamper with its initialization.
IMA must be built-in to ensure it starts measuring from the earliest
possible point in boot which inturn implies TPM must be initialised and
ready to use before IMA.
Unfortunately some TPM drivers (such as Arm FF-A, or SPI attached TPM
devices) are not reliably available during the initcall_late stage,
resulting in a log error:
ima: No TPM chip found, activating TPM-bypass!
To address this issue, IMA_INIT_LATE_SYNC is introduced.
However, a remote attestation service cannot determine when IMA has been
initialized because the boot_aggregate measurement name remains unchanged,
even though IMA is initialized later at late_initcall_sync when
IMA_INIT_LATE_SYNC is enabled.
Therefore, use a distinct boot_aggregate name when IMA_INIT_LATE_SYNC
is enabled, allowing the remote attestation service to identify
when IMA has been initialized.
Signed-off-by: Jonathan McDowell <noodles@meta.com>
[yeoreum.yun@arm.com: modified to align with the IMA_INIT_LATE_SYNC change]
Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
|
|
The digest-length check in xattr_verify() mixes int and size_t:
if (xattr_len - sizeof(xattr_value->type) - hash_start >=
iint->ima_hash->length)
sizeof() yields size_t, so the usual arithmetic conversions promote
the whole left-hand side to unsigned 64-bit before the subtraction
runs. For a truncated xattr this underflows instead of going negative:
a 1-byte IMA_XATTR_DIGEST_NG xattr (xattr_len == 1, hash_start == 1)
turns "1 - 1 - 1" into SIZE_MAX, which is trivially >= ima_hash->length.
The check then passes and the following memcmp() reads
iint->ima_hash->length bytes starting past the end of the buffer
vfs_getxattr_alloc() allocated for it.
Nothing upstream clamps xattr_len back into a safe range first:
ima_get_hash_algo() only special-cases xattr_len < 2 to pick a default
algorithm, and evm_verifyxattr() returns INTEGRITY_UNKNOWN rather than
failing when no HMAC key is loaded, so a truncated security.ima value
reaches the length check as-is.
Rewrite the comparison so every operand stays a signed int and no
implicit conversion to size_t can occur.
Fixes: 3ea7a56067e6 ("ima: provide hash algo info in the xattr")
Cc: stable@vger.kernel.org
Signed-off-by: Lincoln Wallace <locnnil0@gmail.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
|
|
To generate the boot_aggregate log in the IMA subsystem with
TPM PCR values, the TPM driver must be built as built-in and
must be probed before the IMA subsystem is initialized.
However, when the TPM device operates over the FF-A protocol using
the CRB interface, probing fails and returns -EPROBE_DEFER if
the tpm_crb_ffa device — an FF-A device that provides the communication
interface to the tpm_crb driver — has not yet been probed.
To ensure the TPM device operating over the FF-A protocol with
the CRB interface is probed before IMA initialization,
the following conditions must be met:
1. The corresponding ffa_device must be registered,
which is done via ffa_init().
2. The tpm_crb_driver must successfully probe this device via
tpm_crb_ffa_init().
3. The tpm_crb driver using CRB over FF-A can then
be probed successfully. (See crb_acpi_add() and
tpm_crb_ffa_init() for reference.)
Unfortunately, ffa_init(), tpm_crb_ffa_init(), and crb_acpi_driver_init()
are all registered with device_initcall, which means
crb_acpi_driver_init() may be invoked before ffa_init() and
tpm_crb_ffa_init() are completed.
When this occurs, probing the TPM device is deferred.
However, the deferred probe can happen after the IMA subsystem
has already been initialized, since IMA initialization is performed
during late_initcall, and deferred_probe_initcall() is performed
at the same level.
And the similar situation is reported on TPM devices attached on SPI
bus[0].
To resolve this, introduce IMA_INIT_LATE_SYNC option to initialise
IMA at late_inicall_sync so that IMA is initialized with the TPM
device probed deferred.
When this option is enabled, modules that access files in the
initramfs through usermode helper calls such as request_module()
during initcall must not be built-in. Otherwise, IMA may miss
measuring those files [1].
Link: https://lore.kernel.org/all/aYXEepLhUouN5f99@earth.li/ [0]
Link: https://lore.kernel.org/all/2b3782398cc17ce9d355490a0c42ebce9120a9ae.camel@linux.ibm.com/ [1]
Suggested-by: Mimi Zohar <zohar@linux.ibm.com>
Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
[zohar@linux.ibm.com: Fixed Kconfig merge conflict]
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
|
|
There are situations where LSMs have dependencies that might mean they
want to be initialised later in the boot process, to ensure those
dependencies are available. In particular there are some TPM setups (Arm
FF-A devices, SPI attached TPMs) required by IMA which are not
guaranteed to be initialised for regular initcall_late.
Add an initcall_late_sync option that can be used in these situations.
Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
Cc: Paul Moore <paul@paul-moore.com>
Acked-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
|
|
As reported by Jiri Vozar, commit 7edea6e8c8e8 ("selinux: beef up
isvalid checks") introduces a new loop in mls_level_isvalid() that
causes ~89-94% throughput regression in System V IPC message queue
operations (msgsnd/msgrcv).
Move the expensive part of the ebitmap checking to policy load time
instead as the reporter suggested.
Link: https://lore.kernel.org/selinux/CAMgFczCi2Z011dNf84Amc0Q-qnTt0+VUjWY+Y7zPyXdaH35Jvw@mail.gmail.com/
Fixes: 7edea6e8c8e8 ("selinux: beef up isvalid checks")
Reported-by: Jiri Vozar <jvozar@redhat.com>
Suggested-by: Jiri Vozar <jvozar@redhat.com>
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
|
|
p_bools.nprim comes from the policy image independently of how many
booleans follow it, and cond_index_bool() fills bool_val_to_struct[] at
value - 1, so a count larger than the values present leaves NULL entries.
Every user of that array then walks it by index and dereferences each
entry: cond_evaluate_expr() on the access-vector path,
security_get_bools() and security_get_bool_value() behind selinuxfs, and
security_set_bools(). A sparse class value is absorbed by
policydb_class_isvalid() and its siblings; booleans have no such
predicate, and no consumer that could use one.
Reject a boolean value that no boolean defines, once, where the array is
built. Conforming policies define every boolean they declare and are
unaffected.
Cc: stable@vger.kernel.org
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
|
|
security_get_classes() sizes an array by p_classes.nprim and fills it at
value - 1, so a class value the policy never defines leaves a NULL.
sel_make_classes() passes every entry to sel_make_dir(), reaching the same
d_alloc_name() dereference as the permission array. The class symbol table
is allowed to be sparse (policydb_class_isvalid() exists to absorb that),
but this getter builds its own array straight from the hash table and has
no such predicate.
Fail the lookup when a value went unclaimed instead of handing out the
NULL. Conforming policies define every class they declare and are
unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
|
|
security_get_permissions() sizes an array by the class's permissions.nprim
and fills it at value - 1, from the inherited common's permission table and
then the class's own. A value no permission defines leaves a NULL that
sel_make_perm_files() passes to d_alloc_name(), an oops inside
sel_write_load() that strands selinux_state.policy_mutex and leaves every
later load in uninterruptible sleep; two permissions sharing a value
overwrite the first kstrdup(). Bounding each value by nprim catches
neither, and neither would a count: the symbol table is keyed on the
permission name, so duplicates pass.
Track the values each permission table claims and require them to cover
exactly what its count declares, rejecting a count no value can reach.
Conforming policies are unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
|
|
sel_write_load() calls selinux_policy_cancel() when sel_make_policy_nodes()
fails, and that helper dereferences the outgoing policy to cancel its
sidtab conversion. On the first policy load there is no outgoing policy:
security_load_policy() returns early for that case, before it converts
anything, and state->policy is still NULL. A first load that fails while
building the selinuxfs tree therefore takes a NULL dereference in
selinux_policy_cancel(), reached from a write(2) to /sys/fs/selinux/load.
Skip the cancel when there is no old policy, mirroring the check
security_load_policy() already makes before it converts.
Cc: stable@vger.kernel.org
Fixes: 02a52c5c8c3b ("selinux: move policy commit after updating selinuxfs")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
|
|
decompress_zstd() is used in two cases:
- CONFIG SECURITY_APPARMOR_COMPRESSED_POLICY: which allows for
compressed policy to be loaded
- CONFIG_SECURITY_APPARMOR_EXPORT_BINARY: which allows introspecting
loaded policy, that is stored in compressed form until it is needed.
When neither of these are selected there is no need for decpress_zstd(),
which results in the decompressed_zstd defined but not used message.
Only define decmpress_zstd() if either of those config options are
enabled. The stub routine is not needed because all calling code is
gated by one of those config options.
Fixes: 1c5f27e845e84 ("apparmor: Fix build failure when ZSTD_DECOMPRESS is not enabled")
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
When CONFIG_ZSTD_DECOMPRESS is not enabled, and neither
CONFIG_SECURITY_APPARMOR_EXPORT_BINARY nor
CONFIG_SECURITY_APPARMOR_COMPRESSED_POLICY are enabled.
The build will fail with implicit declaration of function
'decompress_zstd' because there is not an appropriate stub function,
for when the zstd decompression isn't enabled.
In addition fix compress_min, and compress_max to be conditional on
CONFIG_SECURITY_APPARMOR_EXPORT_BINARY, as they are used with the
exported policy.
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202608010834.9yIVzhG2-lkp@intel.com/
Fixes: 1c5f27e845e84 ("apparmor: Fix build failure when ZSTD_DECOMPRESS is not enabled")
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
selinux_inode_init_security() marks the isec as initialized before
checking if mount labeling is supported (SBLABEL_MNT). This was fine
until commit 9722955b5430 ("bpf: Add simple xattr support to bpffs"),
where genfscon bpffs mounts fail the SBLABEL_MNT check as expected (no
xattrs) and yet leave the isec->initialized. This breaks subsequent
calls to inode_doinit_with_dentry().
Do the SBLABEL_MNT check before the inode security is initialized.
Cc: stable@vger.kernel.org
Closes: https://lore.kernel.org/all/akWdcp6P0FkNDzBk@google.com/
Fixes: 9722955b5430 ("bpf: Add simple xattr support to bpffs")
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Carlos Llamas <cmllamas@google.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
|
|
security_get_permissions() maps an inherited common's permissions into
an array sized by the class's own permissions.nprim, but class_read()
takes that nprim verbatim from the policy image and never checks that it
covers the common. A class that inherits a common of N permissions while
declaring a smaller nprim is accepted, and on load the common's
permissions are written past the class-sized array -- an out-of-bounds
heap write.
Reject a class whose permission count is below its inherited common's.
Well-formed policies, where the class count already includes the
inherited permissions, are unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
|
|
perm_read() bounds a permission value by SEL_VEC_MAX but never by the
nprim of the owning class or common, which is taken verbatim from the
policy image. security_get_permissions() then writes perms[value - 1]
into an nprim-sized kcalloc() array, so a class declaring fewer
permissions than its largest permission value drives an out-of-bounds
heap write. The top-level symbol tables are validated this way; the
nested per-class permission table is not.
Reject a permission whose value exceeds nprim, which is already set when
perm_read() runs. Well-formed policies are unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
[PM: tweak comment for line length]
Signed-off-by: Paul Moore <paul@paul-moore.com>
|
|
commit
17b5758bf35c ("apparmor: Initial support for compressed policies")
added the ability for apparmor to load compressed policy, unfortunately
it did not add a config option or select CONFIG_ZSTD_DECOMPRESS
which it depends on, leading to the following build failure
apparmorfs.c makes calls into zstd_*() even when
CONFIG_SECURITY_APPARMOR_EXPORT_BINARY is not set, causing
build errors:
/usr/bin/ld.bfd: security/apparmor/apparmorfs.o: in function `policy_update':
apparmorfs.c:(.text+0x1307): undefined reference to `zstd_get_frame_header'
/usr/bin/ld.bfd: apparmorfs.c:(.text+0x1359): undefined reference to `zstd_dctx_workspace_bound'
/usr/bin/ld.bfd: apparmorfs.c:(.text+0x13f7): undefined reference to `zstd_init_dctx'
/usr/bin/ld.bfd: apparmorfs.c:(.text+0x140c): undefined reference to `zstd_decompress_dctx'
/usr/bin/ld.bfd: apparmorfs.c:(.text+0x1411): undefined reference to `zstd_is_error'
Add a new config option to enable compress policy loading as using
the existing CONFIG_SECURITY_APPARMOR_EXPORT_BINARY is in appropriate
as that is about retaining loaded policy so that it can be introspected
at a later date.
Fixes: 17b5758bf35c ("apparmor: Initial support for compressed policies")
Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
Warning: security/apparmor/apparmorfs.c:501 function parameter 'compressed_data' not described in 'aa_get_data_from_compressed'
Warning: security/apparmor/apparmorfs.c:501 function parameter 'compressed_data' not described in 'aa_get_data_from_compressed'
Warning: security/apparmor/apparmorfs.c:501 function parameter 'compressed_data' not described in 'aa_get_data_from_compressed'
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202607240144.4iqulDF1-lkp@intel.com/
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
read_cons_helper() validates the expression type and stack depth
of each constraint node but leaves e->attr and e->op unchecked,
so a policy with an invalid operator or attribute value is
accepted at load and only detected when the constraint is evaluated.
constraint_expr_eval() handles such unrecognized cases with BUG()
so the first permission check that reaches such a node oopses in
the context of the checking process or panics with panic_on_oops.
Reject these expresssions when the policy is loaded, matching what
the libsepol validator already does.
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
|
|
Should make harder for MITM to redirect to somewhere else.
Signed-off-by: Baruch Siach <baruch@tkos.co.il>
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
keyring_get_key_chunk() loads description bytes into the index chunk low
address first, while keyring_diff_objects() numbers the first differing
bit from the low end and folds the absolute byte index into the level
without removing the inline-prefix offset the level already carries.
The two disagree on byte order and bit position, so the array can be
told two keys first differ at a bit that does not differ in the chunk
the walker uses, letting crafted descriptions collide into one node.
Load the chunk in the order keyring_diff_objects() assumes and drop the
inline-prefix length when folding the byte index into the level. This
only changes the in-memory ordering used to place keys within a keyring;
add, search and read of non-colliding keys are unaffected.
Fixes: f771fde82051 ("keys: Simplify key description management")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
|
|
For description-level chunks keyring_get_key_chunk() advances the read
pointer by level * sizeof(long) past the inline prefix but only
bounds-checks the prefix, so a long enough key description is read past
its kmemdup(desc, desc_len + 1) allocation. Compute the full byte
offset and bounds-check the description against it before reading.
The walk only reaches a description-level chunk when two keys collide
through the hash, x, type and domain_tag chunks, so this is reached from
an unprivileged add_key(2) with a crafted pair of same-type keys whose
index hashes collide; KASAN reports a slab-out-of-bounds read.
Fixes: f771fde82051 ("keys: Simplify key description management")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
|
|
Two correctness and type-hygiene issues exist in the DCP trusted keys
implementation.
First, trusted_dcp_unseal() reads p->key_len from a user-supplied blob
without checking if it exceeds MAX_KEY_SIZE. If a crafted blob provides a
payload_len larger than 128, the subsequent do_aead_crypto() call writes
past the end of the p->key array into the adjacent p->blob buffer within
the same struct trusted_key_payload -- the caller's own input, not
unrelated kernel memory. While not exploitable, this violates strict array
bounds and triggers static analyzers. Fix this by adding a validation
check against MIN_KEY_SIZE and MAX_KEY_SIZE immediately after reading the
length, matching the checks already done in trusted_core.c.
Second, calc_blob_len() calculates a sum in size_t that truncates to
unsigned int on 64-bit platforms. Because the DCP hardware is only present
on 32-bit i.MX SoC platforms, size_t and unsigned int are functionally
equivalent in production, making this truncation harmless in practice.
Nevertheless, updating the return type to size_t (and subsequently updating
'blen' in the seal/unseal paths) resolves type-narrowing warnings and
improves overall code hygiene.
Fixes: 2e8a0f40a39c ("KEYS: trusted: Introduce NXP DCP-backed trusted keys")
Signed-off-by: Fabrice Derepas <fabrice.derepas@canonical.com>
Reviewed-by: David Gstir <david@sigma-star.at>
Reviewed-by: Richard Weinberger <richard@nod.at>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
Link: https://lore.kernel.org/r/20260719163939.3624767-1-fabrice.derepas@canonical.com
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
|
|
Make use of the audit_log_n_untrustedstring() function to simplify the
code in aa_label_xaudit().
Signed-off-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux
Pull selinux fix from Paul Moore:
"A single SELinux patch to correct a problem with the overlayfs mmap()
and mprotect() fixes from earlier this year where we inadvertenly
included an additional SELinux execmem permission check on some
operations"
* tag 'selinux-pr-20260717' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
selinux: fix incorrect execmem checks on overlayfs
|
|
git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux
Pull landlock fix from Mickaël Salaün:
"This fixes TCP Fast Open support, specific test environments, and doc
warnings"
* tag 'landlock-7.2-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux:
selftests/landlock: Skip scoped_signal subtest with MSG_OOB if not available
selftests/landlock: Fix screwed up pointers in the scoped_signal_test
landlock: Update formatting
landlock: Fix kernel-doc for the nested quiet layer flag
selftests/landlock: Add test for TCP fast open
landlock: Fix TCP Fast Open connection bypass
|
|
selinux_ima_collect_state() builds a string of the current SELinux
configuration settings. The string lists each setting as a name and
one digit. The length of the string therefore never changes, but is
still recomputed on every call.
Add selinux_ima_config_len_init() to compute the length once during
selinux_init(). Update selinux_ima_collect_state() to use the stored
length.
Suggested-by: Paul Moore <paul@paul-moore.com>
Link: https://lore.kernel.org/r/df755e0282dab3b932d19aceab71b7d7@paul-moore.com
Signed-off-by: Ian Bridges <icb@fastmail.org>
Reviewed-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
|
|
Fix "undefined symbol: decompress_zstd" error caused by decompress_zstd()
being guarded by CONFIG_SECURITY_APPARMOR_EXPORT_BINARY=y.
Reported-by: syzbot+1f14a35d0c73d31555e4@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=1f14a35d0c73d31555e4
Fixes: 17b5758bf35c ("apparmor: Initial support for compressed policies")
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Acked-by: Georgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
|
|
The commit fixing the overlayfs mmap() and mprotect() access checks
failed to skip the execmem check in __file_map_prot_check() for the case
where the "mounter check" is being performed. This check should be
performed only against the credentials of the task that is calling
mmap()/mprotect(), since it doesn't pertain to the file itself, but
rather just gates the ability of the calling task to get an executable
memory mapping in general.
The purpose of the "mounter check" is to guard against using an
overlayfs mount to gain file access that would otherwise be denied to
the mounter. For execmem this is not relevant, as there is no further
file access granted based on it (notice that the file's context is not
used as the target in the check), so checking it also against the
mounter credentials would be incorrect.
Fix this by passing a boolean to [__]file_map_prot_check() and
selinux_mmap_file_common() that indicates if we are doing the "mounter
check" and skiping the execmem check in that case. Since this boolean
also indicates if we use current_cred() or the mounter cred as the
subject, also remove the "cred" argument from these functions and
determine it based on the boolean and the file struct.
Cc: stable@vger.kernel.org
Fixes: 82544d36b172 ("selinux: fix overlayfs mmap() and mprotect() access checks")
Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
Reviewed-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
|
|
In preparation for removing the deprecated strlcat() API[1], replace the
strscpy()/strlcat() chain in selinux_ima_collect_state() with a struct
seq_buf, which tracks the write position and remaining space internally.
Each field is written with seq_buf_printf() using a "=%d;" format, which
removes the open-coded "=1;"/"=0;" constants. The seven per-append
WARN_ON(rc >= buf_len) truncation checks are replaced by a single
seq_buf_has_overflowed() check after the string is built.
Link: https://github.com/KSPP/linux/issues/370 [1]
Signed-off-by: Ian Bridges <icb@fastmail.org>
Reviewed-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
|