Linux File Permissions: chmod, chown, ACLs, and Special Bits

File permissions are the primary access control mechanism in Linux. Every file and directory on the system has an owner, a group, and a set of permission bits that determine who can read, write, and execute it. Misunderstanding or misconfiguring permissions is one of the most frequent causes of security incidents and application failures. This guide covers everything from basic rwx permissions to advanced ACLs and special bits.

Part of the Linux System Administration guide. See also: User Management | Disk Management

Understanding the Permission Model

Linux uses a three-tier permission model: user (owner), group, and other (everyone else). Each tier can independently have read (r), write (w), and execute (x) permissions. You can view permissions with ls -l:

$ ls -l /etc/passwd
-rw-r--r-- 1 root root 2847 Mar 10 14:22 /etc/passwd

The permission string -rw-r--r-- breaks down as: file type (- for regular file), owner rw- (read and write), group r-- (read only), other r-- (read only).

For directories, the meanings shift slightly: read allows listing contents, write allows creating or deleting files within the directory, and execute allows entering the directory (using cd).

chmod: Changing Permissions

The chmod command modifies file permissions. It supports two notations: symbolic and octal.

Symbolic Notation

Symbolic notation uses letters to specify who (u user, g group, o other, a all) and what operation (+ add, - remove, = set exactly):

# Add execute permission for the owner
chmod u+x script.sh

# Remove write permission for group and other
chmod go-w config.yaml

# Set exact permissions: owner rwx, group rx, other r
chmod u=rwx,g=rx,o=r application.bin

# Add read permission for everyone
chmod a+r README.txt

Octal Notation

Octal notation represents each permission tier as a single digit (0-7), where read=4, write=2, execute=1:

# rwxr-xr-x = 755
chmod 755 script.sh

# rw-r--r-- = 644
chmod 644 document.txt

# rwx------ = 700
chmod 700 private_dir/

# rw-rw-r-- = 664
chmod 664 shared_file.txt

Common permission patterns: 755 for executables and directories, 644 for regular files, 600 for sensitive files like SSH keys, 700 for private directories.

Recursive Changes

Use -R to apply permissions recursively. Be careful with this, as files and directories typically need different permissions:

# Set directories to 755 and files to 644 (a common pattern)
find /var/www -type d -exec chmod 755 {} \;
find /var/www -type f -exec chmod 644 {} \;

chown and chgrp: Changing Ownership

The chown command changes the owner (and optionally the group) of a file. The chgrp command changes only the group.

# Change owner to www-data
chown www-data /var/www/index.html

# Change owner and group simultaneously
chown www-data:www-data /var/www/index.html

# Change only the group
chgrp developers project/

# Recursive ownership change
chown -R appuser:appgroup /opt/myapp/

Only root can change file ownership. Regular users can change the group of their own files, but only to a group they belong to.

Special Permission Bits

Beyond the standard rwx bits, Linux has three special permission bits that serve important roles.

SUID (Set User ID)

When set on an executable, SUID causes the program to run with the permissions of the file owner rather than the user who executes it. This is how commands like passwd can modify /etc/shadow even when run by a regular user:

# Set SUID bit (note the 4 prefix in octal)
chmod 4755 /usr/local/bin/special_program
chmod u+s /usr/local/bin/special_program

# Verify: 's' replaces 'x' in the owner field
$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 Mar 14 12:00 /usr/bin/passwd

Security warning: SUID binaries owned by root are a significant attack surface. Audit them regularly with find / -perm -4000 -type f.

SGID (Set Group ID)

On executables, SGID runs the program with the file's group. On directories, it is more commonly used: new files created inside an SGID directory inherit the directory's group rather than the creating user's primary group. This is invaluable for shared project directories:

# Set SGID on a shared directory
chmod 2775 /opt/project/
chmod g+s /opt/project/

# Now all new files in /opt/project/ inherit the directory's group

Sticky Bit

The sticky bit on a directory prevents users from deleting files they do not own, even if they have write permission on the directory. The classic example is /tmp:

# Set sticky bit
chmod 1777 /tmp
chmod +t /tmp

# Verify: 't' appears at the end
$ ls -ld /tmp
drwxrwxrwt 18 root root 4096 Mar 15 09:00 /tmp

umask: Default Permission Mask

The umask value determines the default permissions for newly created files and directories. It is a mask that is subtracted from the maximum permissions (666 for files, 777 for directories):

# View current umask
$ umask
0022

# With umask 0022:
# New files:       666 - 022 = 644 (rw-r--r--)
# New directories:  777 - 022 = 755 (rwxr-xr-x)

# Set a more restrictive umask
umask 0077
# New files: 600 (rw-------)
# New dirs:  700 (rwx------)

Set the umask in /etc/profile, /etc/bash.bashrc, or individual user shell profiles for persistence.

ACLs: Fine-Grained Access Control

Standard Unix permissions only support one owner and one group. Access Control Lists (ACLs) allow you to grant permissions to multiple specific users and groups. The filesystem must be mounted with ACL support (enabled by default on most modern distributions).

Setting ACLs with setfacl

# Grant read/write to a specific user
setfacl -m u:alice:rw report.csv

# Grant read to a specific group
setfacl -m g:auditors:r /var/log/app.log

# Set a default ACL on a directory (applies to new files created inside)
setfacl -d -m g:developers:rwx /opt/project/

# Remove a specific ACL entry
setfacl -x u:alice report.csv

# Remove all ACLs
setfacl -b report.csv

Viewing ACLs with getfacl

$ getfacl /opt/project/report.csv
# file: opt/project/report.csv
# owner: bob
# group: staff
user::rw-
user:alice:rw-
group::r--
group:auditors:r--
mask::rw-
other::---

The + sign at the end of ls -l output indicates that a file has ACLs set:

$ ls -l report.csv
-rw-rw----+ 1 bob staff 4096 Mar 15 10:00 report.csv

The ACL Mask

The ACL mask defines the maximum effective permissions for named users, named groups, and the owning group. When you set an ACL, the mask is automatically recalculated. You can set it explicitly:

setfacl -m m::rx /opt/project/

Practical Security Checklist

  • Audit SUID/SGID binaries monthly: find / -perm /6000 -type f -ls
  • Ensure home directories are 700 or 750
  • Use 600 for SSH private keys: chmod 600 ~/.ssh/id_rsa
  • Set sticky bit on shared writable directories
  • Use ACLs instead of making files world-readable when multiple groups need access
  • Set a restrictive umask (0027 or 0077) for service accounts
  • Review /etc/fstab for nosuid and noexec mount options on partitions that do not need them