Loading content...
In discretionary access control systems, users control access to resources they own. This creates a fundamental vulnerability: a user with access to sensitive data can, intentionally or accidentally, leak it to unauthorized parties.
Consider a defense contractor. An engineer with SECRET clearance reads a classified document, then attaches it to an email sent to an uncleared recipient. The DAC system checked: "Can Alice read the document?" (Yes) and "Can Alice send email?" (Yes)—but missed the critical question: "Should SECRET data flow to UNCLASSIFIED destinations?" (No).
Mandatory Access Control (MAC) addresses this gap. In MAC systems, a central authority assigns security labels to all subjects and objects. Access decisions are based on these labels according to system-wide rules that no user can override. The policy is mandatory—not discretionary.
MAC provides mathematically provable security properties. When correctly implemented, it guarantees that classified information cannot flow to unclassified destinations, regardless of user actions or programming errors. This guarantee makes MAC essential for high-security environments: military systems, government agencies, financial institutions, and healthcare organizations.
This page covers the theoretical foundations of MAC, including security lattices and information flow. You'll master the Bell-LaPadula model for confidentiality, the Biba model for integrity, and understand how these formal models translate into real implementations like SELinux, AppArmor, and Windows Mandatory Integrity Control.
The Core Principle
MAC's defining characteristic is that access decisions are based on security labels assigned by a central authority, not by resource owners. These labels form a mathematical structure—typically a lattice—that determines valid information flows.
Key Differences from DAC:
| Aspect | DAC | MAC |
|---|---|---|
| Who controls access? | Resource owner | Central security policy |
| Can users grant access? | Yes | No |
| Can users modify labels? | N/A (no labels) | No |
| Policy enforcement | Discretionary | Mandatory |
| Security guarantees | None (user dependent) | Mathematical (provable) |
Security Labels
Every subject (process, user session) and object (file, network socket, memory region) is assigned a security label. Labels typically consist of:
The combination creates a partial order: some labels can be compared (SECRET dominates CONFIDENTIAL), others cannot (NATO and NUCLEAR are incomparable unless you have both).
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
// MAC Security Label Structure // Security levels form a total order (fully comparable)enum SecurityLevel { UNCLASSIFIED = 0, CONFIDENTIAL = 1, SECRET = 2, TOP_SECRET = 3,} // Categories are non-hierarchical compartmentstype Category = "NATO" | "CRYPTO" | "NUCLEAR" | "HUMINT" | "SIGINT"; // A complete security labelstruct SecurityLabel { level: SecurityLevel, categories: Set<Category>,} // Exampleslet label1 = SecurityLabel { level: SECRET, categories: { NATO, CRYPTO } }; let label2 = SecurityLabel { level: SECRET, categories: { NUCLEAR } }; let label3 = SecurityLabel { level: TOP_SECRET, categories: { NATO, CRYPTO, NUCLEAR } }; // Dominance relation: L1 dominates L2 iff:// 1. L1.level >= L2.level AND// 2. L1.categories ⊇ L2.categories (L1 has all categories that L2 has)function dominates(L1: SecurityLabel, L2: SecurityLabel): boolean { return L1.level >= L2.level && L2.categories.isSubsetOf(L1.categories);} // Examples:// dominates(label3, label1) → true (TS ≥ S, and {NATO,CRYPTO,NUCLEAR} ⊇ {NATO,CRYPTO})// dominates(label1, label2) → false (S == S, but {NATO,CRYPTO} ⊉ {NUCLEAR})// dominates(label2, label1) → false (S == S, but {NUCLEAR} ⊉ {NATO,CRYPTO}) // label1 and label2 are INCOMPARABLE (neither dominates the other)The Security Lattice
Security labels with the dominance relation form a mathematical lattice. A lattice is a partially ordered set where any two elements have:
When data from multiple sources is combined, the result inherits the join of input labels, ensuring it's protected at the highest required level.
12345678910111213141516171819202122232425262728293031
// Lattice Operations on Security Labels // Join (Least Upper Bound): smallest label dominating both inputsfunction join(L1: SecurityLabel, L2: SecurityLabel): SecurityLabel { return SecurityLabel { level: max(L1.level, L2.level), categories: L1.categories ∪ L2.categories, };} // Meet (Greatest Lower Bound): largest label dominated by both inputsfunction meet(L1: SecurityLabel, L2: SecurityLabel): SecurityLabel { return SecurityLabel { level: min(L1.level, L2.level), categories: L1.categories ∩ L2.categories, };} // Example:let labelA = { level: SECRET, categories: { NATO, CRYPTO } };let labelB = { level: CONFIDENTIAL, categories: { NATO, NUCLEAR } }; join(labelA, labelB) = { SECRET, { NATO, CRYPTO, NUCLEAR } }// Result must be readable only by someone with SECRET clearance and ALL three categories meet(labelA, labelB) = { CONFIDENTIAL, { NATO } }// Result represents information that could flow to either source // Practical Application: Information Flow// If function F reads file with labelA and file with labelB,// any output from F must be labeled at least join(labelA, labelB)The lattice structure guarantees that every pair of labels has a unique join, enabling consistent label propagation. If data from SECRET/NATO and CONFIDENTIAL/NUCLEAR combines, there's exactly one correct output label: SECRET/{NATO, NUCLEAR}. Without lattice structure, combining incomparable labels would be undefined.
The Bell-LaPadula (BLP) model, developed by David Bell and Leonard LaPadula in 1973 for the US Department of Defense, is the foundational MAC model for confidentiality. It formally captures the intuition that classified information should never flow to unclassified destinations.
Core Properties
BLP defines two primary security properties:
1. Simple Security Property (No Read Up)
A subject can read an object only if the subject's clearance level dominates the object's classification level.
Formally: Subject S can read Object O iff:
clearance(S) ≥ classification(O)
Intuition: You can't read documents above your clearance. A SECRET-cleared person cannot read TOP SECRET documents.
*2. -Property (Star Property / No Write Down)
A subject can write to an object only if the object's classification level dominates the subject's current level.
Formally: Subject S can write Object O iff:
classification(O) ≥ currentLevel(S)
Intuition: You can't copy classified content to less classified destinations. A SECRET-cleared person cannot write to UNCLASSIFIED files.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
// Bell-LaPadula Model Implementation struct Subject { id: SubjectId, clearance: SecurityLabel, // Maximum allowed level currentLevel: SecurityLabel, // Current operating level (≤ clearance)} struct Object { id: ObjectId, classification: SecurityLabel, // Security label of the object} enum Operation { READ, WRITE, APPEND, EXECUTE } function bellLaPadulaCheck( subject: Subject, operation: Operation, object: Object): AccessDecision { switch (operation) { case READ: // Simple Security Property: No Read Up // Subject's clearance must dominate object's classification if (dominates(subject.clearance, object.classification)) { // Additional: subject's current level must also dominate // (subject operating at SECRET can't read TOP SECRET) if (dominates(subject.currentLevel, object.classification)) { return ALLOW; } } return DENY; case WRITE: // *-Property: No Write Down // Object's classification must dominate subject's current level if (dominates(object.classification, subject.currentLevel)) { return ALLOW; } return DENY; case APPEND: // Append is write-only (no read), so only *-property applies if (dominates(object.classification, subject.currentLevel)) { return ALLOW; } return DENY; case EXECUTE: // Execute typically requires read access return bellLaPadulaCheck(subject, READ, object); }} // Examples:let alice = Subject { clearance: { TOP_SECRET, { NATO, CRYPTO } }, currentLevel: { SECRET, { NATO } },}; let secretNatoDoc = Object { classification: { SECRET, { NATO } },}; let unclassifiedFile = Object { classification: { UNCLASSIFIED, {} },}; // alice READ secretNatoDoc:// clearance { TS, {NATO, CRYPTO} } dominates { S, {NATO} }? YES// currentLevel { S, {NATO} } dominates { S, {NATO} }? YES// Result: ALLOW // alice WRITE unclassifiedFile (while at SECRET level):// classification { U, {} } dominates currentLevel { S, {NATO} }? NO (U < S)// Result: DENY (would leak SECRET information to UNCLASSIFIED) // alice WRITE secretNatoDoc:// classification { S, {NATO} } dominates currentLevel { S, {NATO} }? YES (equal)// Result: ALLOWCurrent Level vs. Clearance
A subtle but important distinction: subjects have both a clearance (maximum authorized level) and a current operating level (actual level at this moment).
Why the distinction?
Discretionary Security Policy
BLP includes a third component: the Discretionary Security (ds) Property. Beyond mandatory label checks, BLP can incorporate a discretionary access matrix. Access requires:
This models real systems where mandatory MAC overlays discretionary permissions.
BLP guarantees no direct information flow through read/write operations. However, information can leak through covert channels: timing (how long does an operation take?), resource usage (is the disk full?), or error messages (access denied vs. file not found). BLP doesn't address covert channels, which remain an active research area.
Formal Theorems
Bell and LaPadula proved two fundamental theorems:
Basic Security Theorem (BST) If a system starts in a secure state (satisfying Simple Security and *-Property) and all state transitions preserve these properties, then the system will always remain in a secure state.
This is crucial: it means if we verify the initial state and all possible operations, we have mathematical certainty about system security.
Tranquility Properties
Most practical systems use weak tranquility, allowing administrative label changes with proper authorization.
While Bell-LaPadula addresses confidentiality (preventing unauthorized disclosure), the Biba model (Kenneth Biba, 1977) addresses integrity (preventing unauthorized modification).
Consider a military command system. We care not only about keeping orders secret but also about ensuring orders aren't tampered with. An enemy who can modify orders—even if they can't read them—can cause significant harm.
Biba's Core Insight
Integrity concerns are dual to confidentiality concerns:
Biba essentially inverts Bell-LaPadula's rules.
Biba's Primary Properties
1. Simple Integrity Property (No Read Down)
A subject can read an object only if the object's integrity level dominates the subject's integrity level.
Formally: Subject S can read Object O iff:
integrity(O) ≥ integrity(S)
Intuition: Don't read untrusted data if you're producing trusted output. A high-integrity process shouldn't read low-integrity files that might be corrupted.
*2. Integrity -Property (No Write Up)
A subject can write to an object only if the subject's integrity level dominates the object's integrity level.
Formally: Subject S can write Object O iff:
integrity(S) ≥ integrity(O)
Intuition: Untrusted sources can't modify trusted data. A low-integrity process (like a user-downloaded program) can't modify high-integrity system files.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
// Biba Model Implementation // Integrity levels (opposite interpretation from confidentiality)enum IntegrityLevel { UNTRUSTED = 0, // User-generated, network-sourced APPLICATION = 1, // Application data SYSTEM = 2, // System files, configurations CRITICAL = 3, // Boot files, security policy, kernel} struct Subject { id: SubjectId, integrityLevel: IntegrityLevel,} struct Object { id: ObjectId, integrityLevel: IntegrityLevel,} function bibaCheck( subject: Subject, operation: Operation, object: Object): AccessDecision { switch (operation) { case READ: // Simple Integrity: No Read Down // Object's integrity must dominate subject's integrity // (Can only read objects as trustworthy or more trustworthy than you) if (object.integrityLevel >= subject.integrityLevel) { return ALLOW; } return DENY; case WRITE: // Integrity *-Property: No Write Up // Subject's integrity must dominate object's integrity // (Can only write to objects as trustworthy or less trustworthy than you) if (subject.integrityLevel >= object.integrityLevel) { return ALLOW; } return DENY; }} // Examples:let userProcess = Subject { integrityLevel: UNTRUSTED };let systemProcess = Subject { integrityLevel: SYSTEM };let kernelProcess = Subject { integrityLevel: CRITICAL }; let configFile = Object { integrityLevel: SYSTEM };let userFile = Object { integrityLevel: UNTRUSTED };let bootLoader = Object { integrityLevel: CRITICAL }; // userProcess READ configFile:// configFile.integrity (SYSTEM) >= userProcess.integrity (UNTRUSTED)? YES// Result: ALLOW (user can read system config - reading doesn't corrupt) // userProcess WRITE configFile:// userProcess.integrity (UNTRUSTED) >= configFile.integrity (SYSTEM)? NO// Result: DENY (untrusted process can't corrupt system files!) // systemProcess WRITE configFile:// systemProcess.integrity (SYSTEM) >= configFile.integrity (SYSTEM)? YES// Result: ALLOW // systemProcess WRITE bootLoader:// systemProcess.integrity (SYSTEM) >= bootLoader.integrity (CRITICAL)? NO// Result: DENY (only CRITICAL-level processes can modify boot files)| Aspect | Bell-LaPadula | Biba |
|---|---|---|
| Security Goal | Confidentiality | Integrity |
| Information Flow Direction | Prevent downward flow | Prevent upward flow |
| Read Rule | No Read Up | No Read Down |
| Write Rule | No Write Down | No Write Up |
| Level Interpretation | Higher = more secret | Higher = more trusted |
| Threat Model | Prevents leaking secrets | Prevents corruption |
| Use Case | Classified documents | System file protection |
BLP and Biba are mathematical duals. Achieving both simultaneously is challenging—a subject restricted to only read-at-their-level and write-at-their-level has limited practical utility. Systems often prioritize one property or use different mechanisms for each. Windows Mandatory Integrity Control focuses on integrity; classified government systems focus on confidentiality.
Biba's Additional Policies
Biba defined several variations for flexibility:
1. Low-Water-Mark Policy (Subject) Instead of denying reads of low-integrity objects, the subject's integrity level drops to the low-water mark of objects it has read. This allows reading untrusted data but "contaminates" the subject.
2. Low-Water-Mark Policy (Object) When a subject writes an object, the object's integrity drops to the subject's level. This allows any write but tracks contamination.
3. Ring Policy Allows any reads (no read restriction) but enforces write integrity. Common in systems where reading untrusted data is necessary (e.g., processing network input) but modifying trusted data must be controlled.
These variations trade strictness for practicality in real-world systems.
BLP and Biba reason about individual read/write operations. Information Flow Analysis takes a broader view: tracking how data propagates through the entire system over time.
The Limitation of Access Control
Consider:
This is an indirect flow that BLP's per-operation checks might miss if the category lattice isn't carefully designed.
Non-Interference
The property of non-interference captures the idea that high-level actions should not be observable by low-level users.
Formally: A system has non-interference if, for any sequence of operations, the low-level observable outputs are independent of high-level inputs.
If you're a CONFIDENTIAL user, you should not be able to determine whether any SECRET operations occurred by observing your view of the system.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
// Non-Interference Property (Simplified) // A system state observed by low-level userstype LowView = Set<Observable>; // A sequence of system operationstype Trace = List<(Subject, Operation, Object)>; // Purge high-level operations from a tracefunction purge(trace: Trace, level: SecurityLevel): Trace { return trace.filter(op => op.subject.clearance <= level && op.object.classification <= level );} // Execute a trace and observe the result at low levelfunction execute(trace: Trace): SystemState;function observe(state: SystemState, level: SecurityLevel): LowView; // Non-Interference Property:// For any two traces T1 and T2 where purge(T1, LOW) == purge(T2, LOW),// observe(execute(T1), LOW) == observe(execute(T2), LOW) // In plain English:// If the only difference between two execution traces is in the high-level operations,// then low-level observers should see exactly the same thing. // Example violation:// T1: Alice(SECRET) reads file F, then Bob(UNCLASSIFIED) reads file G// T2: (no SECRET operation), then Bob(UNCLASSIFIED) reads file G// If Bob can tell the difference (e.g., different timing, different error messages),// then non-interference is violated. // Goguen-Meseguer Formulation:// The system is secure if the actions of high-privileged users// are invisible to low-privileged users. function isSecure(system: System): boolean { forall (L: SecurityLevel) { forall (T1: Trace, T2: Trace) { if (purge(T1, L) == purge(T2, L)) { assert(observe(execute(T1), L) == observe(execute(T2), L)); } } } return true; // If all assertions pass}Transitive vs. Intransitive Non-Interference
Classic non-interference is transitive: if HIGH cannot interfere with MEDIUM, and MEDIUM cannot interfere with LOW, then HIGH cannot interfere with LOW.
However, real systems often need controlled downgrading:
Intransitive non-interference allows designated "downgrader" domains. This is modeled by specifying which information flows are permitted between domains.
Denning's Lattice Model
Dorothy Denning (1976) formalized information flow using lattices. The key insight: when data flows combine, the output label is the join (least upper bound) of input labels.
This ensures that combining SECRET/NATO data with SECRET/CRYPTO data produces output labeled SECRET/{NATO, CRYPTO}—never something weaker.
A covert storage channel exists if a high-level process can modulate a shared resource (disk usage, file existence) that a low-level process can observe. A covert timing channel exists if a high-level process can modulate something that affects timing (lock contention, cache state). Complete non-interference requires eliminating all covert channels—often impractical.
Security-Enhanced Linux (SELinux) is the most prominent MAC implementation in production use. Developed by the NSA and open-sourced in 2000, SELinux adds mandatory access control to the Linux kernel through the Linux Security Modules (LSM) framework.
Type Enforcement (TE)
SELinux's primary mechanism is Type Enforcement, where every process runs in a domain and every file has a type. Policy rules specify which domains can access which types with which permissions.
Unlike classical BLP with its linear hierarchy, Type Enforcement uses an arbitrary graph of permitted domain-type interactions.
1234567891011121314151617181920212223242526272829303132333435363738394041424344
# SELinux Type Enforcement Policy (Simplified) # Declare typestype httpd_t; # Domain for Apache web servertype httpd_content_t; # Type for web content filestype httpd_log_t; # Type for web server logstype user_t; # Domain for regular user processestype shadow_t; # Type for /etc/shadow # Declare attributes (groups of types)attribute web_content;typeattribute httpd_content_t web_content; # Rules: allow source_domain target_type:object_class permission_set; # Apache can read web contentallow httpd_t httpd_content_t:file { read getattr open };allow httpd_t httpd_content_t:dir { search getattr }; # Apache can append to its logsallow httpd_t httpd_log_t:file { append create getattr open }; # Apache CANNOT read shadow file (no allow rule = deny)# (absence of rule: httpd_t -> shadow_t is forbidden) # Users can read web contentallow user_t httpd_content_t:file { read getattr open }; # Users CANNOT write web content# (no write permission in allow rule) # Transition rules: when user runs httpd, process gets httpd_t domaintype_transition user_t httpd_exec_t:process httpd_t; # File context: files in /var/www get httpd_content_t type# (This is specified in file_contexts, not policy)# /var/www(/.*)? system_u:object_r:httpd_content_t:s0 # MLS (Multi-Level Security) constraints# Even if type enforcement allows access, MLS can still denymlsconstrain file { read } ( h1 dom h2 ) or ( t1 == mlsruntrusted );# Translation: To read a file, subject's high level must dominate # object's level, unless subject is in mlsruntrusted categorySELinux Components
1. Security Contexts
Every process and file has a security context with format:
user:role:type:level
Example: system_u:system_r:httpd_t:s0
2. Policy Language SELinux policy is compiled from source files written in the m4 macro language. Production policies (like the reference policy) contain tens of thousands of rules.
3. Modes
1234567891011121314151617181920212223242526272829303132
# SELinux Practical Commands # Check current SELinux mode$ getenforceEnforcing # Get SELinux context of current process$ id -Zunconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 # Get SELinux context of files$ ls -Z /etc/passwd /etc/shadowsystem_u:object_r:passwd_file_t:s0 /etc/passwdsystem_u:object_r:shadow_t:s0 /etc/shadow # Check if access would be allowed$ sesearch --allow --source httpd_t --target httpd_content_t --class fileallow httpd_t httpd_content_t:file { read getattr ... }; # View recent SELinux denials$ ausearch -m avc -ts recenttype=AVC msg=audit(...): avc: denied { write } for pid=1234 comm="httpd" name="index.html" dev="sda1" ino=789 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:httpd_content_t:s0 tclass=file # Set file context$ chcon -t httpd_content_t /var/www/newfile.html # Restore file contexts to policy defaults$ restorecon -R /var/wwwBeyond Type Enforcement, SELinux supports Multi-Category Security (MCS), a simplified MLS. Containers and VMs often use MCS: each container gets a unique category (like c123), and processes in one container cannot access objects with a different category. This provides isolation with a lighter policy footprint than full Type Enforcement.
SELinux isn't the only MAC implementation. AppArmor and others offer alternative approaches with different tradeoffs.
AppArmor: Path-Based MAC
AppArmor (Application Armor), developed by Novell/SUSE and now maintained by Canonical, uses pathname-based access control rather than SELinux's label-based approach.
Key differences:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
# AppArmor Profile for nginx #include <tunables/global> /usr/sbin/nginx { #include <abstractions/base> #include <abstractions/nameservice> # Capabilities needed capability net_bind_service, capability setuid, capability setgid, # Network access network inet stream, network inet6 stream, # Binary and libraries /usr/sbin/nginx mr, /lib/x86_64-linux-gnu/** mr, /usr/lib/x86_64-linux-gnu/** mr, # Configuration files (read-only) /etc/nginx/** r, # Web content (read-only) /var/www/** r, /srv/www/** r, # Log files (write, append) /var/log/nginx/** w, # PID file /run/nginx.pid rw, # Deny access to sensitive files deny /etc/passwd r, deny /etc/shadow r, deny /home/** rw, # Child profiles for worker processes /usr/sbin/nginx//worker { #include <abstractions/base> /var/www/** r, /var/log/nginx/** w, }} # Profile modes:# enforce - violations blocked and logged# complain - violations logged but allowed# disabled - profile not applied| Aspect | SELinux | AppArmor |
|---|---|---|
| Access Control Basis | Security labels | File paths |
| File Labeling | Required | Not needed |
| Policy Complexity | High | Lower |
| Learning Curve | Steep | Gentler |
| Granularity | Very fine (types, attributes) | Medium (paths, capabilities) |
| Filesystem Independence | Yes (labels travel with files) | No (paths are filesystem-specific) |
| Hardlink Security | Strong | Weaker (same path = same access) |
| Adoption | Red Hat, CentOS, Fedora | Ubuntu, SUSE, Debian |
Windows Mandatory Integrity Control (MIC)
Windows Vista introduced Mandatory Integrity Control, a MAC mechanism focused on integrity (Biba-like). Each process and object has an integrity level:
By default, processes cannot write to objects with higher integrity levels (No Write Up). This prevents malware running in a browser (Low integrity) from modifying user files (Medium integrity).
12345678910111213141516171819202122232425262728
# Windows Mandatory Integrity Control Examples # View integrity level of current process> whoami /groups | findstr "Mandatory"Mandatory LabelMedium Mandatory Level S-1-16-8192 # View integrity level of a file> icacls "C:WindowsSystem32configSAM"Mandatory LabelSystem Mandatory Level:(NW) # Integrity Levels in SIDs:# S-1-16-0 = Untrusted# S-1-16-4096 = Low# S-1-16-8192 = Medium# S-1-16-12288 = High# S-1-16-16384 = System # Run a program at Low integrity level> icacls "C: empsandbox.exe" /setintegritylevel Low> start /B C: empsandbox.exe # The sandboxed process cannot:# - Write to Medium or higher files# - Read many system locations# - Inject into Medium or higher processes # UAC Prompt triggers when Medium process requests High elevation# Elevated processes run at High integrity levelmacOS uses a sandboxing mechanism based on TrustedBSD/MAC Framework. Apps from the App Store are sandboxed with entitlements specifying allowed operations (network access, file access, camera). This is MAC with a focus on app-level containment rather than system-wide multilevel security.
Deploying MAC in production requires careful planning. The theoretical models are straightforward; the practical challenges are significant.
The Usability-Security Tradeoff
MAC's strength—no user can bypass policy—is also its weakness. If policy is too restrictive:
Incremental Deployment Strategy
Assessment Phase
Policy Development
Testing Phase
Enforcement Rollout
123456789101112131415161718192021222324252627282930313233343536373839404142
# SELinux Policy Development Workflow # Step 1: Switch to permissive mode (for existing system)$ sudo setenforce 0 # Step 2: Clear audit log$ sudo systemctl restart auditd # Step 3: Exercise the application thoroughly$ ./run_all_tests.sh$ ./simulate_production_traffic.sh # Step 4: Collect all denials$ sudo ausearch -m avc -ts today > /tmp/denials.log # Step 5: Generate policy module from denials$ sudo audit2allow -M myapp < /tmp/denials.log # Review the generated policy!$ cat myapp.temodule myapp 1.0; require { type httpd_t; type myapp_data_t; class file { read write getattr open };} #============= httpd_t ==============allow httpd_t myapp_data_t:file { read write getattr open }; # Step 6: Install the policy module$ sudo semodule -i myapp.pp # Step 7: Verify denials are resolved$ sudo ausearch -m avc -ts recent # Step 8: Return to enforcing mode$ sudo setenforce 1 # Step 9: Monitor production$ sudo tail -f /var/log/audit/audit.log | grep avcCommon MAC Deployment Patterns
1. Application Sandboxing Confine individual applications to prevent lateral movement if compromised.
2. Service Isolation Separate services into distinct security domains.
3. Container Security Use MCS categories for container isolation.
MAC prevents policy violations but cannot protect against attacks within allowed policy. If httpd_t can read httpd_content_t, and an attacker compromises the web server, they can read all web content. MAC limits damage but doesn't eliminate risk. Defense in depth remains essential.
Despite its theoretical elegance, MAC faces significant practical challenges that limit adoption outside high-security environments.
1. Policy Complexity
Production SELinux policies contain tens of thousands of rules. Writing correct policy requires deep understanding of both the policy language and application internals.
2. Application Compatibility
Many applications assume they can access arbitrary files or perform operations that MAC restricts.
3. Performance Overhead
Every system call potentially requires MAC policy evaluation.
4. The Declassification Problem
Real systems need controlled information downgrading:
Strict non-interference forbids all such operations. Practical systems need trusted declassifiers—processes authorized to violate normal policy. These become high-value attack targets.
5. Covert Channels
MAC guarantees no policy-violating explicit flows but cannot prevent all covert channels:
Reducing covert channel bandwidth often requires non-interference with the system's intended function.
| Factor | MAC Favorable | DAC/RBAC Favorable |
|---|---|---|
| Security Requirements | High-assurance, regulatory | Standard enterprise |
| Application Ecosystem | Well-understood, controlled | Diverse, third-party |
| Administrative Resources | Dedicated security team | General IT staff |
| Change Velocity | Stable, planned changes | Rapid development |
| Performance Sensitivity | Security prioritized | Performance critical |
| User Population | Trusted, trained | General population |
Organizations that successfully deploy MAC share common traits: strong management commitment, dedicated security staff, clearly defined security requirements, willingness to invest in policy development, and application architectures designed with MAC in mind. Retrofitting MAC onto legacy systems is significantly harder than designing with it from the start.
Mandatory Access Control provides mathematically grounded security guarantees that discretionary and role-based models cannot offer. Let's consolidate the key concepts.
When to Use MAC
MAC is most valuable when:
For most commercial systems, layering MAC (e.g., SELinux in targeted mode) provides meaningful security improvement without full multilevel security complexity.
What's Next
This page covered MAC's theoretical foundations and practical implementations. The following pages explore:
You now understand Mandatory Access Control—from the mathematical foundations of lattice-based security through formal models like Bell-LaPadula and Biba, to practical implementations like SELinux and AppArmor. You can reason about when MAC is appropriate and how to approach its deployment.