Unix permissions allow, yet services may still be denied by policy; possessing a capability does not imply that all related operations will pass. Access control requires interpreting the subject, object, operation, and policy combination together.
DAC includes owner/group/other and ACLs, typically allowing the object owner to arrange access; mandatory policies can further constrain the service domain.
Target to consult
Location to read
LSM and labels/profiles
Sections 1–3
Capabilities and BPF policies
Sections 4–5
Keyrings, integrity, and runtime state
Sections 6–7
1. LSM provides check points; modules provide policies
LSM hooks are located in the operation paths of kernel objects such as files, credentials, processes, and networks; a single system call may trigger multiple checks, and internal kernel operations may also pass through these points. Hooks include functions that return decisions, as well as notifications for allocating, releasing, or updating security state. It is incorrect to uniformly state that "all hooks returning an error result in denial."
For example, security_file_open() uses a file_open hook with a return value; it is invalid to write it as call_void_hook() and then claim to check for errors. Reading the target version's security/security.c and include/linux/lsm_hook_defs.h is more reliable than copying old hook list structures.
For common denial-type permission hooks, enabled modules participate in checks according to defined combination rules; a denial by one module cannot be overridden by an allowance from another. It is incorrect to draw SELinux and AppArmor as running in fixed parallel on all systems: the actual combination and order of enabling depend on the kernel version, configuration, and boot parameters. When securityfs is visible, /sys/kernel/security/lsm lists the currently active modules.
The name "LSM" does not imply they are ordinary modules that can be freely insmod/rmmod. Framework concepts are described in the LSM documentation, where historical structure explanations should be read in conjunction with current source code.
2. SELinux: Subject domains and object types
SELinux security contexts typically contain user, role, type, and level/range. For example, httpd_sys_content_t in a file label is an object type; service processes run in a specific domain.
When reading a type enforcement denial record, align the following four items:
Field
Question to confirm
source context
Which subject domain initiated the access?
target context
What type is the object actually?
object class
Is it a file, directory, or other object?
permission
Which operation was denied: read, search, etc.?
Looking only at the path can lead to missing labels, domain transitions, or directory search permissions. Rules express the permission for a source domain to perform operations on a specific type of target object.
The AVC cache stores access decisions to reduce query costs. Label sources include filesystem extended attributes and labels generated by the policy for specific objects; the labeling methods for procfs, tmpfs, etc., cannot be uniformly stated as all generated by genfscon.
Enforcing mode denies access according to policy; permissive mode typically logs accesses that would have been denied without enforcing the SELinux denial. Other DAC, LSM, and system errors can still cause operations to fail, so permissive does not mean all operations will succeed. Auditing is also affected by policy and log configuration, so it is incorrect to assume a new log entry is generated for every decision.
3. AppArmor and Smack: Different policy expressions
AppArmor centers on program profiles; file access rules are often expressed by path and can restrict capabilities, network operations, etc. Different programs under the same Unix user can thus be subject to different constraints.
Path-based policies require understanding the process's mount view, aliases, renaming, and linking rules, but one cannot directly conclude that "creating a hardlink or bind mount will bypass AppArmor." Links and mounts are also subject to checks; the profile, policy namespace, and specific rules determine the outcome. The following are merely fragments of rule syntax, not a complete, deployable service profile:
It lacks program loading, dynamic libraries, directory access, and complete runtime dependencies. Theoretically understanding a rule is a different task from having an available and sufficiently convergent policy. See the AppArmor documentation.
Smack also uses subject/object labels and access rules, but has its own rules and special label semantics. It is not a version of "SELinux with a fixed percentage of features removed"; the choice mechanism should compare actual expressive power and deployment environments. See the Smack documentation.
4. Capabilities: Splitting privileges, not generating a complete sandbox
Kernel capability semantics are based on thread credentials, including permitted, effective, and inheritable sets, as well as bounding and ambient sets. Conventional permission checks care about the relevant capabilities in the effective set; how they are recalculated during exec depends on conditions such as file capabilities, set-ID bits, and securebits.
CAP_NET_RAW involves permissions for raw/packet sockets, etc.
CAP_SYS_PTRACE can participate in bypassing some ptrace credential restrictions, but other checks still apply.
CAP_NET_BIND_SERVICE involves protected low ports; port threshold settings in network namespaces also affect whether it is needed.
CAP_SYS_ADMIN covers a wide range; it is inappropriate to interpret the name as a narrow operation.
Capabilities within a user namespace are only valid within the applicable namespace and object relationships; root inside a container does not automatically possess all the capabilities of the host root. Conversely, removing capabilities does not eliminate all interfaces that a regular user could otherwise use. See capabilities(7).
Separate the scopes of DAC and capabilities, LSM, and read-only filesystem conditions. The following presents the combination of these constraints, rather than disguising them as a single function call order that all systems fixedly follow.
Preparing the visual
Can one allowance override another denial?
An applicable capability can satisfy specific DAC bypass conditions, but it cannot change an LSM denial or a read-only filesystem into an allowance. Root within a user namespace is also not a pass for all host objects.
{"id":"access-control-gates","title":"Can one allowance override another denial?","summary":"An applicable capability can satisfy specific DAC bypass conditions, but it cannot change an LSM denial or a read-only filesystem into an allowance. Root within a user namespace is also not a pass for all host objects.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><label class=\"check\"><input type=\"checkbox\" id=\"dac\"><span data-i18n=\"dac\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"cap\" checked><span data-i18n=\"cap\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"scopeOK\"><span data-i18n=\"scopeOK\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"lsm\" checked><span data-i18n=\"lsm\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"rw\" checked><span data-i18n=\"rw\"></span></label><div class=\"track-row\"><span data-i18n=\"dacGate\"></span><div id=\"dacGate\" class=\"track\"></div></div><div class=\"track-row\"><span data-i18n=\"lsmGate\"></span><div id=\"lsmGate\" class=\"track\"></div></div><div class=\"track-row\"><span data-i18n=\"fsGate\"></span><div id=\"fsGate\" class=\"track\"></div></div><div class=\"formula\" id=\"formula\"></div><div class=\"card\"><header data-i18n=\"result\"></header><div id=\"result\" class=\"readout\"></div></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"note\"></p></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n.formula{min-height:4.8em}.readout{min-height:3.2em}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}function draw(){const dac=$('#dac').checked,cap=$('#cap').checked,scope=$('#scopeOK').checked,l=$('#lsm').checked,r=$('#rw').checked,d=dac||(cap&&scope);for(const[k,v]of [['dacGate',d],['lsmGate',l],['fsGate',r]])cells(k,[{text:t(v?'satisfied':'fail'),cls:v?'on':'gold'}]);set('formula','('+Number(dac)+' ∨ ('+Number(cap)+' ∧ '+Number(scope)+')) ∧ '+Number(l)+' ∧ '+Number(r)+' = '+Number(d&&l&&r));set('result',t(d&&l&&r?'allow':'deny'));set('explanation',t('note'));}document.querySelectorAll('input').forEach(e=>e.onchange=draw);draw();","audio":false,"strings":{"allow":"These conditions allow","cap":"Holds effective CAP_DAC_OVERRIDE","dac":"DAC itself allows writing","dacGate":"DAC or applicable bypass condition","deny":"At least one denies","fail":"Not satisfied","fsGate":"Filesystem write condition","heading":"Can one allowance override another denial?","intro":"Set up DAC, capability scope, LSM, and read-only mount conditions for writing to a regular file. Observe the final decision with all constraints retained.","lsm":"LSM policy allows","lsmGate":"LSM constraint","note":"Cancel the capability scope to simulate having rights only within a sub-user namespace. Then individually disable LSM or the writable condition to observe why adding a capability still cannot satisfy all conditions.","reset":"Reset","result":"Conjunction of the above necessary conditions","rw":"Mount is writable","satisfied":"Satisfied","scope":"Analyze only regular file writes, assuming valid inode identity mapping, no ACL exceptions, or other errors. The capability is effective CAP_DAC_OVERRIDE, and the scope condition represents applicability to that inode; this does not claim that all syscalls follow a fixed check order using a three-box diagram. It is not a complete authorization simulator.","scopeLabel":"Model scope and conventions","scopeOK":"The capability applies to the target inode"}}
5. BPF LSM: Verified programs may still write incorrect policies
BPF LSM allows extending supported hooks via permission-controlled BPF programs. libbpf can handle loading and linking, while CO-RE/BTF helps adapt to kernel types.
For system-wide MAC hooks, when multiple BPF programs are chained, programs should preserve previous denial results according to the interface specification; it is incorrect to unconditionally change a non-zero ret passed in to 0. Different attach types have different return semantics, so one cannot directly apply the return value rules from one example to all BPF LSM programs.
The verifier checks acceptable memory access, control flow, and other conditions, reducing the risk of the program damaging the kernel; it does not prove the correctness of business authorization logic, nor does it promise that the kernel/verifier is forever bug-free. Deploying incorrect policies can still block normal work. See the BPF LSM documentation.
6. Keyrings, IMA, and EVM
The kernel keyring manages key objects with types, permissions, and lifetimes, and supports linking keys to keyrings. It is not a global dictionary allowing all users to read all keys, nor does it guarantee that every key payload is protected by hardware; trusted, encrypted, and other key types have their own encapsulation and root trust mechanisms.
Measurement records and denial upon use are different goals
IMA measurement measures objects such as files according to policy, retains measurement logs, and can extend TPM PCRs. It does not recompute the entire file for every byte read, nor does it store the hash of every file separately in the TPM. Remote verification requires understanding the log, PCRs, trusted attestation, and expected state simultaneously.
IMA appraisal, on the other hand, verifies file integrity/authenticity according to policy and decides whether to deny use. EVM protects the integrity of selected security extended attributes and related metadata, using HMAC or signatures. Measurement, denial of execution, and disk content encryption are three different goals; having only measurement logs does not automatically prevent tampered programs from running.
7. Read-only observation of the current environment
File invisibility may stem from securityfs not being mounted, configuration, permissions, or container views; one cannot assert that the system has no access control solely based on read failures. Actual authorization issues should be analyzed in conjunction with the service process's credentials, namespaces, labels/profiles, and denial logs, rather than only checking the current shell.