Skip to content

Permissions: mode bits vs ownership vs ACLs vs capabilities

Mental model

Four separate access-control systems, often lumped together as "permissions." Mode bits and ACLs control file access. Ownership determines who matches which mode category. Capabilities control what a process is allowed to do at the kernel level.

What it looks like

"Fix the permissions." Could mean: chmod, chown, setfacl, or setcap. People use "permissions" for all four interchangeably.

What it really is

  • Mode bits (rwx): per-inode, three categories — owner / group / other. 9 permission bits + 3 special bits (setuid, setgid, sticky). Stored in the inode.
  • Ownership (uid/gid): determines which mode category applies. A process's effective uid is checked against the file's owner uid; then effective gid against file gid; then "other."
  • ACLs (Access Control Lists): extend mode bits with per-user and per-group entries. Checked after ownership matching. Stored as extended attributes (xattr).
  • Capabilities: decompose root's powers into ~60+ distinct privileges (CAP_NET_BIND_SERVICE, CAP_SYS_ADMIN, etc.). Attached to processes or executable files. Process-level, not file-level access control.

Why it seems confusing

"Permission denied" could come from any of these systems (or SELinux/AppArmor on top). Mode bits and ACLs control file access. Capabilities control kernel operations. Ownership is not a permission — it determines which permission set applies.

What actually matters

  • Kernel checks in order: is process uid 0? → owner match? → group match? → other. ACLs insert between owner and other.
  • setuid bit: when set on an executable, the process runs with the file owner's uid as effective uid. setgid: same for gid.
  • Capabilities replace the setuid-root pattern. Instead of giving a binary full root, grant only CAP_NET_BIND_SERVICE to bind port 80.
  • Sticky bit on directories: only file owner (or dir owner or root) can delete entries. Used on /tmp.
  • umask masks bits at file creation time — it doesn't change existing permissions.

Common mistakes

  • Using chmod 777 to "fix" permission errors. This removes all access control instead of diagnosing the real issue.
  • Forgetting that ACLs override group/other mode bits. If an ACL exists, the group mode bits become the ACL mask.
  • Confusing file capabilities with process capabilities. getcap reads file capabilities; /proc/PID/status shows process capabilities.
  • Setting setuid on scripts. The kernel ignores setuid on interpreted scripts (security measure).

Small examples

# Mode bits
chmod 750 app.sh           # rwxr-x--- (owner/group/other)
stat -c '%a %U:%G' app.sh  # 750 deploy:deploy

# Ownership
chown deploy:deploy app.sh # changes which uid/gid match

# ACLs
setfacl -m u:alice:rx app.sh   # alice gets r-x regardless
getfacl app.sh                  # shows ACL entries
# Note: group mode bits now act as ACL mask

# Capabilities
setcap cap_net_bind_service=+ep /usr/bin/myapp
getcap /usr/bin/myapp
# myapp can bind port 80 without running as root

# setuid
chmod u+s /usr/bin/passwd   # runs as root (file owner)
# Replaced by capabilities where possible

One-line summary

Mode bits = rwx per owner/group/other; ownership = who matches which category; ACLs = fine-grained file access; capabilities = fine-grained root powers for processes.