Skip to content

File Permissions and ACLs

Every file and directory on a Linux system carries a set of permission bits that control which users Can read, write, or execute it. The kernel enforces these permissions during every file system Operation.

Each file has three categories of permissions, each with three bits:

CategoryRead (r)Write (w)Execute (x)
Owner (u)Read file contents / List directory entriesModify file / Create/delete directory entriesRun file / Enter directory
Group (g)Same as owner for group membersSameSame
Other (o)Same for everyone elseSameSame
OctalBinaryPermission
0000---
1001—x
2010-w-
3011-wx
4100r—
5101r-x
6110rw-
7111rwx

Common permission sets:

OctalMeaningUse Case
755rwxr-xr-xExecutables, directories
644rw-r—r—Regular files
700rwx------Private scripts
600rw-------SSH keys, config files
400r--------Read-only secrets
711rwx—x—xPublic directories (listable only by owner)
Terminal window
# Long listing shows permissions, owner, group
ls -la /etc/passwd
# -rw-r--r-- 1 root root 2847 Jan 15 10:30 /etc/passwd
# Numeric view
stat -c "%a %n' /etc/passwd
# 644 /etc/passwd
stat -c '%A %U:%G %n' /etc/passwd
# -rw-r--r-- root:root /etc/passwd
# Full stat output
stat /etc/passwd

Directory permissions have different semantics than file permissions:

PermissionFileDirectory
rRead file contentsList filenames (requires x)
wModify file contentsCreate/delete/rename files (requires x)
xExecute as a programEnter directory (cd), access inode information
To list directory contents: r + x
To create/delete files: w + x
To access a file inside: x on every parent directory
Without x on a directory:
- Cannot cd into it
- Cannot stat files inside it
- Cannot read files even if they are 644
Without r on a directory:
- Cannot list files (ls fails)
- But can access files if you know their names (cat dir/file works)
Terminal window
# Example: a directory where you can access files but not list them
mkdir secret && chmod 711 secret
echo "hidden content" > secret/data.txt
chmod 644 secret/data.txt
# Other users cannot list the directory
ls secret/ # Permission denied
# But can read the file if they know the name
cat secret/data.txt # works!
Terminal window
# Format: who + action + permission
# who: u (owner), g (group), o (other), a (all)
# action: + (add), - (remove), = (set exactly)
# permission: r, w, x, X, s, t, u, g, o
# Add execute for owner
chmod u+x script.sh
# Remove write for group and other
chmod go-w file.txt
# Set exact permissions
chmod u=rwx,g=rx,o= file.txt
# Recursively set directories to 755, files to 644
chmod -R a=rX,u+w . # X sets x only if already x for any category, or if directory
find . -type d -exec chmod 755 {} +
find . -type f -exec chmod 644 {} +
# Reference mode (copy permissions from another file)
chmod --reference=reference.txt target.txt
Terminal window
chmod 755 script.sh # rwxr-xr-x
chmod 600 id_rsa # rw-------
chmod 644 config.conf # rw-r--r--
chmod 1777 /tmp # rwxrwxrwt (sticky bit)
chmod 4755 /usr/bin/sudo # rwsr-xr-x (setuid)
Terminal window
# Change owner
chown user file.txt
# Change group
chgrp group file.txt
# Change both owner and group
chown user:group file.txt
# Change owner, keep group
chown user: file.txt
# Change group only (shortcut)
chown :group file.txt
# Recursive
chown -R user:group /var/www/html/
# Reference
chown --reference=ref.txt target.txt

Only root can change the owner of a file. The owner can change the group to any group they are a Member of. This is enforced by the kernel: the chown(2) system call checks capable(CAP_CHOWN).

The umask (user file creation mask) determines the default permissions for newly created files and Directories. It is a mask that is subtracted from the maximum permissions:

CreationMaximumApplied asumask 0022 result
Regular file06660666 & ~022 = 0644rw-r—r—
Directory07770777 & ~022 = 0755rwxr-xr-x
Terminal window
# View current umask
umask # outputs octal (e.g., 0022)
umask -S # symbolic form (u=rwx,g=rx,o=rx)
# Set umask
umask 0027 # owner: rwx, group: rx, other: ---
umask 0077 # owner only (private)
# Common umask values
# 0022 — world-readable (default on most systems)
# 0027 — group-readable, no world access
# 0077 — private (SSH, PGP directories)
# Set in profile
echo 'umask 0027' >> ~/.profile

The kernel checks ACLs if they exist on the file. The check order is: owner, named users (most Specific first), owning group or named groups, mask, other. The first matching entry that grants or Denies the requested access determines the result. The mask limits the maximum effective permissions For all named users, named groups, and the owning group.

1. If process is root (UID 0):
a. If CAP_DAC_OVERRIDE is in effective set: grant
b. For directories: grant if at least one execute bit is set
c. For files: grant read/write, deny execute unless at least one execute bit is set
2. If ACLs exist:
a. Check if effective UID matches file owner -> use owner permissions
b. Check named user entries -> use matching entry (masked)
c. Check if effective GID or supplementary groups match file group -> use group (masked)
d. Check named group entries -> use matching entry (masked)
e. Use other permissions
3. If no ACLs:
a. Owner match -> use owner permissions
b. Group match -> use group permissions
c. Other -> use other permissions
Terminal window
# Find files with exactly 644 permissions
find /etc -perm 644
# Find files with at least the specified bits set
find /etc -perm -644 # 644 or higher (744, 755, 777, etc.)
# Find files with any of the specified bits set
find /etc -perm /644 # any file with r for any category
# Find world-writable files
find / -perm -o+w -type f 2>/dev/null
# Find files without any group or other permissions
find /etc -perm /600 # only owner has any permissions
# Find setuid files
find / -perm -4000 -type f 2>/dev/null
# Find setgid files
find / -perm -2000 -type f 2>/dev/null
# Find sticky bit directories
find / -perm -1000 -type d 2>/dev/null
# Find files with specific octal and group ownership
find /var -perm 640 -group www-data
# Find and fix permissions
find /var/www -type f -exec chmod 644 {} +
find /var/www -type d -exec chmod 755 {} +
Terminal window
# Find all setuid root binaries
find / -perm -4000 -user root -type f 2>/dev/null
# Check for writable setuid directories
find / -perm -2000 -type d -perm -o+w 2>/dev/null
# Find setuid binaries not owned by root
find / -perm -4000 ! -user root -type f 2>/dev/null
# Audit setuid binaries that are writable by non-root
find / -perm -4000 -type f ! -perm -u+s -writable 2>/dev/null