On this page

LSM and Access Control

Coverage: Linux Security Modules Architecture → SELinux/AppArmor/SMACK → LSM hooks mechanism → BPF LSM → capabilities → keyring → IMA/EVM Kernel version: 2.6 ~ 6.x

Overview

Traditional Unix DAC (Discretionary Access Control) only has three levels of permissions: owner, group, and other. It cannot express fine-grained policies such as "this web server can only read /var/www and cannot touch /home/user/.ssh." LSM inserts hooks into critical system call paths, allowing security modules to provide additional access control decisions.

The two largest LSMs currently are SELinux (a mandatory access control based on type enforcement) and AppArmor (a profile-based policy based on path matching).

LSM Hook Architecture

// include/linux/lsm_hooks.h
// 200+ hooks covering all sensitive operations:
struct security_hook_heads {
    struct hlist_head file_open;          // Last step of open() → security_file_open()
    struct hlist_head inode_create;       // Create inode
    struct hlist_head inode_unlink;       // unlink()
    struct hlist_head socket_create;      // socket()
    struct hlist_head socket_bind;        // bind()
    struct hlist_head task_kill;          // kill()
    struct hlist_head sb_mount;           // mount()
    // ... 200+ more
};

// Call chain:
// security_file_open(file) → call_void_hook(file_open, file)
// → Iterate through hooks of all registered modules → If any returns an error → Deny operation

Hook Insertion Points

LSM Hook Insertion Points: open() System Call Path open("/etc/shadow", O_RDONLY) vfs_open() → do_dentry_open() security_file_open(file) LSM hook insertion point SELinux: file_has_perm() Check type enforcement rules AppArmor: aa_file_perm() Check path profile Both return OK → Operation allowed At the same hook point, SELinux and AppArmor independently check the same open call, and the operation is only allowed if both modules permit it.

SELinux: Type Enforcement

Core Concepts:
  - Everything has a label (label): files, processes, sockets, ...
    → ls -Z: system_u:object_r:httpd_sys_content_t:s0
  - Policies define "which source type can access which class of which target type"
    → Whitelist model: Deny by default, only explicitly allowed actions are permitted
  - AVC (Access Vector Cache): Cache for policy query results (critical for performance)

SELinux Modes:
  /sys/fs/selinux/enforce
    Enforcing (1): Deny + Log
    Permissive (0): Allow + Log (for debugging)
    Disabled: Do not load at all

Label Storage:
  ext4/xfs: xattr security.selinux
  tmpfs/procfs: Generated at runtime (genfscon)

AppArmor: Path Matching

Path Profile Model:
  Security policies are not based on type, but on path matching:
    /usr/bin/nginx {
      /var/www/** r,
      /var/log/nginx/* w,
      /run/nginx.pid w,
      /tmp/** rw,
      capability net_bind_service,
    }

  Advantages:
    - More intuitive than SELinux (see the path to know the policy)
    - Policy files are independent (one per program)
  Disadvantages:
    - Paths can be bypassed (hardlinks, bind mounts)
    - Difficult to express complex global constraints

SMACK: Simplified Type Enforcement

Similar to SELinux but simplified by 90%:
  - Only labels, no role/user/MLS
  - Rules: "subject label access object label"
  - Used in: Tizen (Samsung), Automotive Grade Linux

Kernel Compile: CONFIG_SECURITY_SMACK

BPF LSM (5.7+)

// Implement LSM hooks using BPF programs:
// bpf(BPF_PROG_LOAD, BPF_PROG_TYPE_LSM, ...)
// attach: bpf(BPF_RAW_TRACEPOINT_OPEN, lsm/file_mprotect)

// Advantages over kernel modules:
//   - No need to compile kernel modules
//   - Verifier ensures safety (no panic)
//   - Easier to distribute and update

Capabilities: Splitting Root Privileges

// include/uapi/linux/capability.h
// Split all root capabilities into fine-grained bits:
CAP_SYS_ADMIN      // System administration
CAP_NET_RAW        // Raw sockets
CAP_NET_BIND_SERVICE  // Bind to ports <1024
CAP_SYS_PTRACE     // ptrace other processes
CAP_KILL           // Send signals to processes of other users
// ... 40+ capabilities

// Key for container security:
//   docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE
//   → Root inside the container can only bind ports, cannot load modules

IMA/EVM: File Integrity

IMA (Integrity Measurement Architecture):
  Measurement: Calculate hash every time a file is read → Record to TPM
  Appraisal: Verify file hash → Reject tampered files

EVM (Extended Verification Module):
  Protect file metadata (xattr, mode, owner) from tampering
  Based on HMAC (using TPM key) or digital signatures

Debugging

# SELinux
getenforce
ausearch -m avc --start recent  # Recent denials
sealert -a /var/log/audit/audit.log  # Analyze denial reasons

# AppArmor
aa-status
cat /proc/<pid>/attr/apparmor/current

# Capabilities
capsh --print  # Capabilities of the current shell
getpcaps <pid>

References

  • Source Code: security/ (lsm, selinux, apparmor, smack, integrity), include/linux/lsm_hooks.h
  • Kernel Documentation: Documentation/admin-guide/LSM/
  • LWN: "The LSM hook interface", "BPF LSM"

Keywords: LSM, SELinux, AppArmor, BPF LSM, capabilities, IMA, EVM, type enforcement, AVC