Linux Security Fundamentals & DAC/MAC
Chapter 14: Linux Security Fundamentals & DAC/MAC
Section titled “Chapter 14: Linux Security Fundamentals & DAC/MAC”Learning Objectives
Section titled “Learning Objectives”- Understand the Linux security model from first principles
- Understand DAC (Discretionary Access Control) — the traditional Unix model
- Understand MAC (Mandatory Access Control) — SELinux and AppArmor
- Know the Linux capability model
- Understand the complete permission system
Prerequisites
Section titled “Prerequisites”What is this? (Beginner On-Ramp)
Section titled “What is this? (Beginner On-Ramp)”Linux security is about controlling who can do what. At its most basic level, it uses owners, groups, and permissions (Read, Write, Execute) to protect files. If you don’t have the right permissions, the kernel strictly blocks your access.
14.1 The Linux Security Philosophy
Section titled “14.1 The Linux Security Philosophy” Linux Security Layers ──────────────────────
┌─────────────────────────────────────────────────────────────┐ │ DEFENSE IN DEPTH │ │ │ │ Layer 1: Physical Security │ │ Layer 2: Network Security (firewall, VPN) │ │ Layer 3: Application Security (input validation, TLS) │ │ Layer 4: OS Security: │ │ ┌─────────────────────────────────────────────────┐ │ │ │ DAC: File permissions, ownership │ │ │ │ Capabilities: Fine-grained privilege control │ │ │ │ MAC: SELinux/AppArmor mandatory policies │ │ │ │ Namespaces: Process isolation │ │ │ │ Seccomp: Syscall filtering │ │ │ │ Audit: logging (auditd) │ │ │ └─────────────────────────────────────────────────┘ │ │ Layer 5: Data Encryption (LUKS, TLS) │ └─────────────────────────────────────────────────────────────┘14.2 DAC: Discretionary Access Control
Section titled “14.2 DAC: Discretionary Access Control”DAC is the traditional Unix permission model. The resource owner discretely (at their own discretion) controls access.
File Permissions
Section titled “File Permissions” Permission Breakdown ───────────────────── -rw-r--r-- 1 nginx www-data 1234 Jan 15 nginx.conf
│ │ │ │ │ │ │ └── Others: r-- = read only │ │ └───── Group (www-data): r-- = read only │ └──────── Owner (nginx): rw- = read + write └────────── File type: - = regular file d = directory l = symlink c = character device b = block device s = socket
Octal representation: r = 4, w = 2, x = 1 rw-r--r-- = 644 rwxr-xr-x = 755 rwx------ = 700 rw-rw-r-- = 664# View permissionsls -la /etc/nginx/nginx.confstat /etc/nginx/nginx.conf
# Change permissionschmod 644 /etc/nginx/nginx.conf # Octalchmod u=rw,g=r,o=r /etc/nginx/nginx.conf # Symbolic
# Change ownershipchown nginx:www-data /etc/nginx/nginx.confchown -R nginx:www-data /var/www/ # Recursive
# Umask: default permissions for new filesumask # View current umask# 0022 means: new files get 644, directories 755
# Change umask for sessionumask 0027 # New files: 640, directories: 750Special Permission Bits
Section titled “Special Permission Bits” Special Bits (High-Security Concepts) ──────────────────────────────────────
SetUID (SUID) - bit 4000: When set on an executable: → The program runs with the FILE OWNER's privileges → Not the calling user's privileges!
Example: /usr/bin/passwd ls -la /usr/bin/passwd -rwsr-xr-x 1 root root ← 's' = SUID set
When user 'alice' runs passwd: → passwd runs as root (not alice!) → This is how alice can change her password (which requires writing to /etc/shadow, owned by root)
SetGID (SGID) - bit 2000: On files: execute with file's group privileges On directories: new files inherit directory's group
Sticky bit - bit 1000: On directories: only file owner can delete their own files Example: /tmp (all can write, but only owner can delete) ls -la / | grep tmp drwxrwxrwt ← 't' = sticky bit# Find SUID files (security audit - these can be privilege escalation paths!)find / -perm -4000 -type f 2>/dev/nullfind / -perm -4000 -user root -type f 2>/dev/null | sort
# Common legitimate SUID binaries:# /usr/bin/passwd /usr/bin/sudo# /usr/bin/su /usr/bin/ping (modern Linux: uses capabilities instead)
# Any UNEXPECTED SUID binary is a red flag!
# Set SUIDchmod u+s /path/to/program # Set SUIDchmod 4755 /path/to/program # Set SUID with 755 perms
# Remove all SUID from a filesystem (emergency)find / -xdev -perm -4000 -exec chmod u-s {} \;The Root Problem: Why Root is Dangerous
Section titled “The Root Problem: Why Root is Dangerous” root (UID=0) Problems ──────────────────────
Traditional Unix: Binary model - Root can do EVERYTHING - Non-root can do NOTHING privileged
Problems: 1. Any root process can do anything → nginx running as root = nginx can read /etc/shadow! 2. All-or-nothing privilege escalation 3. SUID binaries expand attack surface
Solution: Linux Capabilities (subsection 14.3) → Split root's omnipotence into granular capabilities14.3 Linux Capabilities
Section titled “14.3 Linux Capabilities”Linux capabilities break root’s omnipotent power into 37+ granular capabilities. A process can have some capabilities without being full root.
Root Powers Split Into Capabilities ─────────────────────────────────────
Traditional root → CAP_NET_ADMIN (configure network interfaces) Traditional root → CAP_NET_BIND_SERVICE (bind to ports < 1024) Traditional root → CAP_KILL (kill any process) Traditional root → CAP_SETUID (change UID) Traditional root → CAP_SYS_ADMIN (many admin operations) Traditional root → CAP_DAC_OVERRIDE (bypass file permissions) Traditional root → CAP_CHOWN (change file ownership) Traditional root → CAP_SYS_PTRACE (trace processes) ... (37+ total)
Modern approach: Grant ONLY what's neededCapability Sets
Section titled “Capability Sets”Each process has multiple capability sets:
Process Capability Sets ─────────────────────── Permitted: Maximum capabilities the process can use Effective: Currently active capabilities (can be subset of permitted) Inheritable: Capabilities passed to exec'd programs Bounding: Maximum capabilities ANY exec can gain Ambient: Capabilities for non-privileged exec (Linux 4.3+)# ── View Process Capabilities ─────────────────────────────# Your own capabilitiescat /proc/$$/status | grep Cap# CapInh: 0000000000000000 (inheritable)# CapPrm: 0000000000000000 (permitted)# CapEff: 0000000000000000 (effective)# CapBnd: 000001ffffffffff (bounding)# CapAmb: 0000000000000000 (ambient)
# Decode capability maskcapsh --decode=000001ffffffffff
# View capabilities of a process by namegetpcaps $(pidof nginx)
# ── Grant Capabilities to an Executable ───────────────────# Allow nginx to bind to port 80 WITHOUT running as rootsetcap cap_net_bind_service=+ep /usr/sbin/nginx
# Verifygetcap /usr/sbin/nginx# /usr/sbin/nginx = cap_net_bind_service+ep
# Remove all capabilities from a binarysetcap -r /usr/sbin/nginx
# ── Important Capabilities for Security Review ─────────────# CAP_SYS_ADMIN: Most dangerous — can do almost anything root can# (mount filesystems, load modules, access raw I/O)# CAP_NET_ADMIN: Configure interfaces, routing, iptables# CAP_SETUID: Change UID (can become root!)# CAP_DAC_OVERRIDE: Bypass ALL file permission checks# CAP_SYS_PTRACE: Debug processes (can read memory of ANY process)
# Security audit: find processes with dangerous capabilitiesfor pid in /proc/[0-9]*/; do cap=$(cat $pid/status 2>/dev/null | grep CapEff | awk '{print $2}') if [ "$cap" != "0000000000000000" ] && [ -n "$cap" ]; then decoded=$(capsh --decode=$cap 2>/dev/null) comm=$(cat $pid/comm 2>/dev/null) echo "PID=$(basename $pid) COMM=$comm CAPS=$decoded" fidone | grep -v "^$"14.4 MAC: Mandatory Access Control
Section titled “14.4 MAC: Mandatory Access Control”MAC goes beyond DAC by enforcing security policies that even root cannot override (unless root explicitly modifies the policy). Linux MAC is implemented via the Linux Security Module (LSM) framework.
DAC vs MAC ───────────
DAC (file permissions): ✓ User controls their own files ✗ root can bypass all DAC ✗ Compromised root = game over
MAC (SELinux/AppArmor): ✓ Kernel enforces policy regardless of user identity ✓ Even root is constrained by MAC policy ✓ Process confined to specific operations (deny everything else) ✓ Compromised nginx = attacker can only do what nginx is allowed ✗ Complex to configure ✗ Can break applications if policy is wrongLSM Framework
Section titled “LSM Framework” Linux Security Module (LSM) Architecture ─────────────────────────────────────────
Application calls: open("/etc/shadow", O_RDONLY) │ ▼ VFS layer ┌──────────────────────────────────────────────────┐ │ DAC Check: file permissions │ │ Does process have permission? (yes → continue) │ └──────────────────────────────────────────────────┘ │ passed DAC ▼ ┌──────────────────────────────────────────────────┐ │ LSM Hook: security_inode_permission() │ │ │ │ SELinux or AppArmor checks its policy: │ │ "Is process nginx_t allowed to read shadow_t?" │ │ │ │ If YES: access granted │ │ If NO: EACCES returned even if DAC says OK! │ └──────────────────────────────────────────────────┘14.5 Understanding the Attack Surface
Section titled “14.5 Understanding the Attack Surface” Common Linux Privilege Escalation Paths ────────────────────────────────────────
1. Exploiting SUID binaries with vulnerabilities → Mitigation: Minimize SUID binaries, use capabilities instead
2. Sudo misconfigurations → Example: "user ALL=(ALL) NOPASSWD: /usr/bin/vim" → vim can run shell commands → instant root → Mitigation: Audit /etc/sudoers, use specific commands
3. Writable cron jobs owned by root → If /etc/cron.d/myjob is world-writable, attacker adds commands → Mitigation: strict permissions on cron directories
4. PATH hijacking → Vulnerable SUID binary calls "ls" without full path → Attacker creates /tmp/ls that's actually a shell → Binary calls attacker's ls as root → Mitigation: Use full paths in SUID programs
5. Exploiting services running as root → Vulnerable nginx/Apache running as root → RCE gives attacker root shell → Mitigation: Run services as non-root users
6. Kernel vulnerabilities → Dirty Cow (CVE-2016-5195), DirtyCred, etc. → Mitigation: Keep kernel patched, use MAC (SELinux/AppArmor)# Security audit checklistecho "=== SUID Binaries ==="find / -xdev -perm -4000 -type f -printf "%p %u %g %m\n" 2>/dev/null | sort
echo "=== World-Writable Files ==="find / -xdev -perm -0002 -type f 2>/dev/null | grep -v proc | head -20
echo "=== Sudo Configuration ==="cat /etc/sudoersls -la /etc/sudoers.d/
echo "=== Processes Running as Root ==="ps aux | awk '$1 == "root" && $11 != "[" {print $1, $2, $11}' | head -20
echo "=== Capabilities on Executables ==="find / -xdev -not -path "*/proc/*" -exec getcap {} \; 2>/dev/null | grep -v "^$"
echo "=== Unowned Files (orphaned) ==="find / -xdev \( -nouser -o -nogroup \) 2>/dev/null | head -2014.6 Production Scenarios
Section titled “14.6 Production Scenarios”Scenario 1: Service Needs Port 80 Without Root
Section titled “Scenario 1: Service Needs Port 80 Without Root”# Bad approach: Run nginx as root# [Service]# User=root ← DANGEROUS
# Good approach 1: Use capabilitiessetcap cap_net_bind_service=+ep /usr/sbin/nginx# nginx can bind to port 80 without root
# Good approach 2: Use systemd capabilities# In nginx.service [Service]:User=nginxAmbientCapabilities=CAP_NET_BIND_SERVICECapabilityBoundingSet=CAP_NET_BIND_SERVICE
# Good approach 3: Redirect port 80 to 8080iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080# nginx listens on 8080, no special privileges needed
# Good approach 4: Use unprivileged port binding (Linux 4.11+)sysctl net.ipv4.ip_unprivileged_port_start=80Scenario 2: Investigating “Permission Denied” Errors
Section titled “Scenario 2: Investigating “Permission Denied” Errors”# Application getting EACCES even though DAC looks correct
# Step 1: Check DAC permissionsls -laZ /path/to/resource # -Z shows SELinux contextstat /path/to/resource
# Step 2: Check if SELinux is blockingausearch -m avc -ts today # Recent SELinux denialssealert -a /var/log/audit/audit.log # Human-readable SELinux alerts
# Step 3: Check AppArmordmesg | grep "apparmor.*DENIED"cat /var/log/kern.log | grep apparmor
# Step 4: Check capabilitiesgetpcaps $(pidof myapp)
# Step 5: Run strace to see exact errorstrace -e trace=open,read,write,access -p $(pidof myapp) 2>&1 | grep "EACCES\|EPERM"
# The fix depends on the root cause:# DAC: chmod/chown the file# SELinux: semanage fcontext or audit2allow# AppArmor: update profile to allow the access# Capabilities: setcap or add AmbientCapabilities to service14.7 Interview Questions
Section titled “14.7 Interview Questions”Q1: What is the difference between DAC and MAC?
Answer: DAC (Discretionary Access Control) is the traditional Unix/Linux file permission system where the resource owner discretely controls access via permissions (rwxrwxrwx) and ownership. Root can bypass all DAC. MAC (Mandatory Access Control) enforces security policies at the kernel level regardless of user identity — even root is constrained by MAC. SELinux and AppArmor implement MAC on Linux. The key difference: with DAC, a compromised root account = total system compromise. With MAC, even a compromised root is limited to what the MAC policy permits. In practice, you run both: DAC handles normal access control, MAC provides defense-in-depth.
Q2: What are Linux capabilities and why were they introduced?
Answer: Linux capabilities break root’s omnipotent power into ~37 granular privileges. Before capabilities, privilege escalation was binary: either you had full root power or none. Capabilities allow granting specific powers:
CAP_NET_BIND_SERVICEto bind to port 80,CAP_NET_ADMINto configure networking,CAP_SYS_PTRACEto debug processes — without giving full root. This enables the principle of least privilege: nginx only getsCAP_NET_BIND_SERVICE, notCAP_SYS_ADMIN. If nginx is compromised, the attacker has limited privileges.
Q3: You discover a file with SUID bit set to an unexpected binary. What are your concerns and next steps?
Answer: SUID on an unexpected binary is a serious security red flag. When executed, the binary runs with the file owner’s (typically root’s) privileges. Concerns: (1) Could be backdoor planted by an attacker who previously had root access. (2) Could be a legitimate tool that was misconfigured with SUID. (3) Could be an exploitable binary providing privilege escalation. Steps: (1) Immediately note the path, hash, and creation/modification time. (2) Compare against known-good SUID list (from package manager:
dpkg -Vorrpm -Va). (3) Check if the binary is in the package database:dpkg -S /path/to/binary. (4) If unknown, treat as potential compromise — begin incident response. (5) Remove SUID:chmod u-s /path/to/binaryand investigate further.
14.8 Summary
Section titled “14.8 Summary”| Concept | Key Point |
|---|---|
| DAC | Traditional Unix permissions, owner controls |
| SUID | Run with file owner’s privileges (powerful + risky) |
| Capabilities | Fine-grained root privilege decomposition |
| MAC | Kernel-enforced policy, constrains even root |
| LSM | Framework enabling SELinux/AppArmor hooks |
| Defense in Depth | Multiple security layers |
Next Chapter: Chapter 15: System Hardening: CIS Benchmarks & STIG
Section titled “Next Chapter: Chapter 15: System Hardening: CIS Benchmarks & STIG”Prerequisites
Section titled “Prerequisites”Basic Linux usage. Understand file permissions (rwx).
Advantages & Disadvantages
Section titled “Advantages & Disadvantages”Advantages: Simple, proven permission model (UGO/RWX), ubiquitous across all Unix-like systems. Disadvantages: Too simplistic for complex enterprise needs (solved by ACLs/SELinux).
Common Mistakes
Section titled “Common Mistakes”- Using chmod 777 to fix permission issues — this gives everyone full access. Find the correct user/group instead.
- Running applications as root — always create a dedicated service account with minimal permissions.
- Thinking standard Linux permissions (DAC) stop root — root bypasses all DAC. Only MAC (SELinux/AppArmor) can constrain root.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Diagnosis | Fix |
|---|---|---|---|
| Permission denied reading file | Wrong owner or group, or missing read bit | ls -l filename; id | chown or chmod to grant minimal necessary access |
| Service fails to bind to port 80 | Non-root user trying to bind privileged port (<1024) | journalctl -u service | Use capabilities (setcap cap_net_bind_service=+ep) or run behind a reverse proxy |
Hands-on Lab
Section titled “Hands-on Lab”Objective: Explore basic security boundaries.
# 1. View file permissions and ownershipls -l /etc/shadow# Should be owned by root:shadow, permissions -rw-r-----
# 2. Check your effective user and group IDsid
# 3. Find files with SUID bit setfind /usr/bin -perm -4000 -ls | head -10
# 4. Try reading a file without permissionscat /etc/shadow # Permission deniedExercises
Section titled “Exercises”- Create a new group, add two users to it, and create a shared directory where both can read/write, but others cannot.
- Find all world-writable files in
/tmpusing thefindcommand. - Explain why the
passwdcommand requires the SUID bit to be set.
Revision Notes
Section titled “Revision Notes”- DAC (Discretionary Access Control) relies on the owner setting permissions (rwx).
- UID 0 (root) bypasses all DAC checks.
- SUID allows a program to run with the privileges of its owner (usually root), which is risky if the program has vulnerabilities.
Further Reading
Section titled “Further Reading”man chown,man chmod- Linux and UNIX Security by Simson Garfinkel
Related Chapters
Section titled “Related Chapters”- Chapter 16 — Mandatory Access Control (SELinux)
Last Updated: July 2026