Linux File Permissions & Access Control
Overview
Linux file permissions are a fundamental security mechanism that controls who can read, write, and execute files and directories. This lesson covers standard Unix permissions, special permissions, Access Control Lists (ACLs), and practical security implementations.
Learning Objectives
- Master the Linux permission model (rwx)
- Understand user, group, and other permissions
- Configure special permissions (SUID, SGID, Sticky Bit)
- Implement Access Control Lists (ACLs)
- Apply security best practices
- Troubleshoot permission-related issues
Prerequisites
- Basic Linux command line knowledge
- Understanding of users and groups
- Access to a Linux system or WSL
- Basic file system navigation skills
Core Concepts
The Linux Security Model
Linux uses a discretionary access control (DAC) model where file owners control access permissions for their files.
Permission Components
1. **Owner (User)**: The user who owns the file 2. **Group**: The group associated with the file 3. **Others**: Everyone else on the system 4. **Root**: Superuser with unlimited access
Permission Types
- **Read (r)**: View file contents or list directory
- **Write (w)**: Modify file or add/remove files in directory
- **Execute (x)**: Run file as program or access directory
Understanding Permission Notation
Symbolic Notation (rwxrwxrwx)
ls -l /home/user/file.txt
-rw-r--r-- 1 user group 1024 Dec 1 10:00 file.txt
# Breaking down: -rw-r--r--
# - : File type (- = regular file, d = directory, l = link)
# rw- : Owner permissions (read, write, no execute)
# r-- : Group permissions (read only)
# r-- : Others permissions (read only)Numeric (Octal) Notation
# Each permission has a numeric value:
# Read (r) = 4
# Write (w) = 2
# Execute (x) = 1
# Examples:
# 755 = rwxr-xr-x (common for executables)
# 644 = rw-r--r-- (common for files)
# 600 = rw------- (private files)
# 777 = rwxrwxrwx (avoid - security risk!)Permission Calculation Table
| Permission | Binary | Octal | Meaning | |------------|--------|-------|---------| | --- | 000 | 0 | No permissions | | --x | 001 | 1 | Execute only | | -w- | 010 | 2 | Write only | | -wx | 011 | 3 | Write and execute | | r-- | 100 | 4 | Read only | | r-x | 101 | 5 | Read and execute | | rw- | 110 | 6 | Read and write | | rwx | 111 | 7 | Full permissions |
Basic Permission Management
Viewing Permissions
# Long format listing
ls -l filename
# Show hidden files too
ls -la
# Directory permissions
ls -ld /directory
# Detailed file information
stat filename
# Check your user and groups
id
whoami
groupsChanging Permissions with chmod
Symbolic Method
# Add permission
chmod u+x file.sh # Add execute for owner
chmod g+w file.txt # Add write for group
chmod o-r file.conf # Remove read for others
chmod a+r file.txt # Add read for all (a = all)
# Set exact permissions
chmod u=rwx,g=rx,o=r file # Owner: rwx, Group: r-x, Others: r--
# Multiple changes
chmod u+x,g+w,o-r file # Multiple operations
# Recursive changes
chmod -R u+w directory/ # Apply to all files in directoryNumeric Method
# Set exact permissions
chmod 755 script.sh # rwxr-xr-x
chmod 644 document.txt # rw-r--r--
chmod 600 private.key # rw-------
chmod 700 private_dir/ # rwx------
# Recursive
chmod -R 755 /var/www/html # Web server files
chmod -R 600 ~/.ssh/ # SSH keys (except public keys)Changing Ownership with chown
# Change owner
chown newuser file.txt
# Change owner and group
chown newuser:newgroup file.txt
# Change group only
chown :newgroup file.txt
# or use chgrp
chgrp newgroup file.txt
# Recursive ownership change
chown -R webuser:www-data /var/www/html
# Copy ownership from another file
chown --reference=template.txt target.txtDirectory Permissions
Directory Permission Meanings
- **Read (r)**: List directory contents (ls)
- **Write (w)**: Create, delete, rename files in directory
- **Execute (x)**: Access directory and its contents (cd)
Common Directory Permissions
# Public directory (everyone can access)
chmod 755 public_dir/ # drwxr-xr-x
# Shared directory (group collaboration)
chmod 770 shared_dir/ # drwxrwx---
chmod g+s shared_dir/ # Set SGID for group inheritance
# Private directory
chmod 700 private_dir/ # drwx------
# Drop box (users can add but not see files)
chmod 733 dropbox/ # drwx-wx-wxSpecial Permissions
SUID (Set User ID) - 4000
When set on executable, runs with owner's privileges
# Set SUID
chmod u+s program # Symbolic
chmod 4755 program # Numeric (4xxx)
# Example: passwd command
ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 Feb 6 2024 /usr/bin/passwd
# Find all SUID files (security audit)
find / -perm -4000 -type f 2>/dev/nullSGID (Set Group ID) - 2000
- On executable: Runs with group's privileges
- On directory: New files inherit directory's group
# Set SGID
chmod g+s directory/ # Symbolic
chmod 2755 directory/ # Numeric (2xxx)
# Example: Shared project directory
mkdir /projects/team1
chgrp developers /projects/team1
chmod 2770 /projects/team1
# Now all files created inherit 'developers' group
# Find all SGID files/directories
find / -perm -2000 2>/dev/nullSticky Bit - 1000
Prevents users from deleting files they don't own in shared directories
# Set sticky bit
chmod +t directory/ # Symbolic
chmod 1777 directory/ # Numeric (1xxx)
# Example: /tmp directory
ls -ld /tmp
drwxrwxrwt 23 root root 4096 Dec 1 10:00 /tmp
# Find directories with sticky bit
find / -perm -1000 -type d 2>/dev/nullCombining Special Permissions
# SUID + SGID
chmod 6755 program # rwsr-sr-x
# SGID + Sticky
chmod 3770 shared_dir/ # rwxrws--T
# All special permissions
chmod 7777 special_file # rwsrwsrwt (never do this!)Default Permissions and umask
Understanding umask
umask sets default permissions for new files and directories
# View current umask
umask # Octal format
umask -S # Symbolic format
# How umask works:
# File default: 666 (rw-rw-rw-)
# Directory default: 777 (rwxrwxrwx)
# Subtract umask value
# Example with umask 022:
# File: 666 - 022 = 644 (rw-r--r--)
# Directory: 777 - 022 = 755 (rwxr-xr-x)Setting umask
# Temporary change (current session)
umask 077 # Very restrictive (files: 600, dirs: 700)
umask 022 # Common default (files: 644, dirs: 755)
umask 002 # Group-friendly (files: 664, dirs: 775)
# Permanent change
echo "umask 022" >> ~/.bashrc
# System-wide
echo "umask 022" >> /etc/profileCommon umask Values
| umask | File Result | Directory Result | Use Case | |-------|-------------|------------------|----------| | 000 | 666 (rw-rw-rw-) | 777 (rwxrwxrwx) | Never use! | | 002 | 664 (rw-rw-r--) | 775 (rwxrwxr-x) | Group collaboration | | 022 | 644 (rw-r--r--) | 755 (rwxr-xr-x) | Default for most systems | | 027 | 640 (rw-r-----) | 750 (rwxr-x---) | Secure, group readable | | 077 | 600 (rw-------) | 700 (rwx------) | Private files only |
Access Control Lists (ACLs)
Extended Permissions Beyond Traditional Unix
ACLs provide fine-grained access control for specific users and groups
Viewing ACLs
# Check if file has ACL (+ sign in permissions)
ls -l file.txt
-rw-r--r--+ 1 user group 1024 Dec 1 10:00 file.txt
# View ACLs
getfacl file.txt
# View ACLs for multiple files
getfacl file1.txt file2.txt
# Recursive view
getfacl -R directory/Setting ACLs
# Grant specific user permission
setfacl -m u:john:rw file.txt # User john gets read/write
# Grant specific group permission
setfacl -m g:developers:rwx dir/ # Group developers gets full access
# Set default ACL for directory (inherited by new files)
setfacl -d -m u:alice:rw dir/ # Default for user alice
setfacl -d -m g:staff:r dir/ # Default for group staff
# Multiple ACL entries
setfacl -m u:bob:rwx,g:admin:rx file.txt
# Copy ACLs from one file to another
getfacl file1.txt | setfacl --set-file=- file2.txtModifying and Removing ACLs
# Modify existing ACL
setfacl -m u:john:r file.txt # Change john's permission to read-only
# Remove specific ACL entry
setfacl -x u:john file.txt # Remove john's ACL
setfacl -x g:developers dir/ # Remove developers group ACL
# Remove all ACLs
setfacl -b file.txt # Remove all ACL entries
# Remove default ACLs
setfacl -k dir/ # Remove default ACLs from directoryACL Mask
# The mask limits the maximum permissions for users and groups
setfacl -m m::r file.txt # Set mask to read-only
# Recalculate mask
setfacl --recalculate-mask file.txtPractical Security Scenarios
Scenario 1: Web Server Setup
# Web content directory
mkdir -p /var/www/mysite
chown -R www-data:www-data /var/www/mysite
chmod -R 755 /var/www/mysite
# Uploads directory (writable by web server)
mkdir /var/www/mysite/uploads
chmod 775 /var/www/mysite/uploads
# Configuration files (readable only by web server)
chmod 640 /var/www/mysite/config.php
chown root:www-data /var/www/mysite/config.phpScenario 2: Shared Project Directory
# Create shared directory for team collaboration
mkdir /projects/alpha
groupadd alpha-team
chgrp alpha-team /projects/alpha
chmod 2770 /projects/alpha # SGID for group inheritance
# Add users to team
usermod -a -G alpha-team alice
usermod -a -G alpha-team bob
# Set default ACLs for new files
setfacl -d -m g:alpha-team:rwx /projects/alpha
setfacl -d -m o::--- /projects/alphaScenario 3: Secure Backup Directory
# Create backup directory
mkdir /backups
chmod 700 /backups
chown backup-user:backup-group /backups
# Set immutable attribute (even root cannot delete without removing attribute)
chattr +i /backups/critical-backup.tar.gz
# View attributes
lsattr /backups/
# Remove immutable attribute when needed
chattr -i /backups/critical-backup.tar.gzScenario 4: Multi-User System
# User home directories
chmod 700 /home/* # Private home directories
# or
chmod 755 /home/* # Allow others to access public files
# Shared resources
mkdir /shared
chmod 1777 /shared # Like /tmp - anyone can use, sticky bit set
# Department directories
mkdir /dept/finance
chgrp finance /dept/finance
chmod 2770 /dept/finance # SGID for group collaborationSecurity Best Practices
File Permission Guidelines
# Critical system files
chmod 644 /etc/passwd # World readable (needed for user lookups)
chmod 640 /etc/shadow # Root and shadow group only
chmod 600 /etc/ssh/ssh_host_*key # Private SSH keys
# User files
chmod 700 ~/.ssh # SSH directory
chmod 600 ~/.ssh/id_rsa # Private keys
chmod 644 ~/.ssh/id_rsa.pub # Public keys
chmod 600 ~/.ssh/authorized_keys # Authorized keys
# Web files
chmod 755 /var/www/html # Web root
chmod 644 /var/www/html/*.html # Static content
chmod 640 /var/www/html/*.php # Scripts (readable by web server group)
# Log files
chmod 640 /var/log/*.log # Readable by root and adm group
chmod 755 /var/log # Log directoryFinding and Fixing Insecure Permissions
# Find world-writable files
find / -perm -002 -type f 2>/dev/null
# Find world-writable directories
find / -perm -002 -type d 2>/dev/null
# Find unowned files
find / -nouser -o -nogroup 2>/dev/null
# Find SUID/SGID files
find / -perm -4000 -o -perm -2000 2>/dev/null
# Fix overly permissive files
find /home -type f -perm 777 -exec chmod 644 {} \;
find /home -type d -perm 777 -exec chmod 755 {} \;
# Audit home directory permissions
for dir in /home/*; do
echo "Checking $dir"
ls -ld "$dir"
find "$dir" -type f -perm -o+w 2>/dev/null
donePermission Hardening Script
#!/bin/bash
# Security audit and fix script
echo "=== Permission Security Audit ==="
# Check for world-writable files
echo "Checking for world-writable files..."
WORLD_WRITABLE=$(find / -path /proc -prune -o -path /sys -prune -o -perm -002 -type f -print 2>/dev/null)
if [ ! -z "$WORLD_WRITABLE" ]; then
echo "WARNING: World-writable files found:"
echo "$WORLD_WRITABLE"
fi
# Check SUID files
echo "Checking SUID files..."
SUID_FILES=$(find / -path /proc -prune -o -perm -4000 -type f -print 2>/dev/null)
echo "SUID files found (review for necessity):"
echo "$SUID_FILES" | head -20
# Check SSH permissions
echo "Checking SSH configurations..."
for user_home in /home/*; do
if [ -d "$user_home/.ssh" ]; then
ssh_perms=$(stat -c %a "$user_home/.ssh")
if [ "$ssh_perms" != "700" ]; then
echo "WARNING: $user_home/.ssh has incorrect permissions: $ssh_perms (should be 700)"
fi
fi
done
# Check for files without owners
echo "Checking for unowned files..."
find / -path /proc -prune -o -nouser -print 2>/dev/null | head -20Troubleshooting Common Permission Issues
Issue 1: Permission Denied
# Diagnosis steps
ls -l problematic_file
whoami
groups
id
# Check parent directory permissions
ls -ld $(dirname problematic_file)
# Check ACLs
getfacl problematic_file
# Trace system calls to see exact failure
strace -e open,access command_that_fails 2>&1 | grep -i deniedIssue 2: Cannot Execute Script
# Check if file has execute permission
ls -l script.sh
# Add execute permission
chmod +x script.sh
# or
chmod u+x script.sh
# Check shebang line
head -1 script.sh
# Check if filesystem mounted with noexec
mount | grep noexecIssue 3: Cannot Access Directory
# Need execute permission to traverse directory
chmod +x directory/
# Check all parent directories
namei -l /path/to/deep/directory
# Fix directory path permissions
find /path -type d -exec chmod +x {} \;Issue 4: Group Permissions Not Working
# Verify group membership
groups username
# Check effective group
id username
# User may need to log out/in after group change
# Or use newgrp for immediate effect
newgrp groupnameAdvanced Permission Concepts
Capabilities (Fine-grained Privileges)
# View file capabilities
getcap /usr/bin/ping
# Set capability
setcap cap_net_raw+ep /usr/bin/ping
# Remove capability
setcap -r /usr/bin/ping
# List all files with capabilities
getcap -r / 2>/dev/nullSELinux Context (Red Hat/CentOS/Fedora)
# View SELinux context
ls -Z file.txt
# Change context
chcon -t httpd_sys_content_t /var/www/html/index.html
# Restore default context
restorecon -v /var/www/html/index.html
# Check SELinux status
getenforce
sestatusAppArmor Profiles (Ubuntu/Debian)
# Check AppArmor status
aa-status
# List enforced profiles
aa-enforced
# Put profile in complain mode
aa-complain /usr/sbin/nginx
# Enforce profile
aa-enforce /usr/sbin/nginxPractical Exercises
Exercise 1: Create a Secure Shared Directory
# Create a shared directory for a project team
sudo mkdir /projects/secret-project
sudo groupadd project-team
sudo chgrp project-team /projects/secret-project
sudo chmod 2770 /projects/secret-project
# Add users to the team
sudo usermod -a -G project-team alice
sudo usermod -a -G project-team bob
# Set default ACLs
sudo setfacl -d -m g:project-team:rwx /projects/secret-project
sudo setfacl -d -m o::--- /projects/secret-project
# Verify
ls -ld /projects/secret-project
getfacl /projects/secret-projectExercise 2: Secure Script Deployment
# Create a script that needs elevated privileges
sudo nano /usr/local/bin/backup-system.sh
# Set ownership and permissions
sudo chown root:admin /usr/local/bin/backup-system.sh
sudo chmod 750 /usr/local/bin/backup-system.sh
# If script needs to run as root, use sudo instead of SUID
# Add to sudoers
echo "admin ALL=(root) NOPASSWD: /usr/local/bin/backup-system.sh" | sudo tee /etc/sudoers.d/backupExercise 3: Fix Permission Problems
# Simulate permission issues
mkdir test-perms
cd test-perms
# Create files with various permissions
touch file1.txt file2.txt file3.txt
chmod 000 file1.txt
chmod 200 file2.txt
chmod 400 file3.txt
# Try operations and fix
cat file1.txt # Fails
chmod +r file1.txt # Fix
echo "test" > file2.txt # Fails initially
chmod u+w file2.txt # Already has write, needs read
chmod u+r file2.txt # Fix
# Clean up
cd ..
rm -rf test-permsExercise 4: Audit System Permissions
#!/bin/bash
# System permission audit script
# Function to check directory permissions
audit_directory() {
local dir=$1
local expected_perm=$2
local actual_perm=$(stat -c %a "$dir")
if [ "$actual_perm" != "$expected_perm" ]; then
echo "WARNING: $dir has permissions $actual_perm (expected $expected_perm)"
else
echo "OK: $dir has correct permissions ($expected_perm)"
fi
}
# Audit critical directories
audit_directory "/etc" "755"
audit_directory "/var/log" "755"
audit_directory "/tmp" "1777"
audit_directory "/root" "700"
# Check for dangerous permissions
echo -e "\nChecking for dangerous permissions..."
find /home -type f -perm -o+w 2>/dev/null | head -10
find /etc -type f -perm -o+w 2>/dev/nullQuick Reference Commands
Essential Permission Commands
# View
ls -l # List with permissions
ls -la # Include hidden files
stat file # Detailed file info
getfacl file # View ACLs
# Modify Permissions
chmod 755 file # Numeric method
chmod u+x file # Symbolic method
chmod -R 644 dir/ # Recursive
# Change Ownership
chown user file # Change owner
chgrp group file # Change group
chown user:group # Change both
# Special Permissions
chmod u+s file # Set SUID
chmod g+s dir/ # Set SGID
chmod +t dir/ # Set sticky bit
# ACLs
setfacl -m u:user:rwx file # Set ACL
setfacl -x u:user file # Remove ACL
setfacl -b file # Remove all ACLs
# Find Files by Permission
find / -perm 777 # Exact permission
find / -perm -777 # At least these permissions
find / -perm /777 # Any of these permissionsCommon Permission Patterns
Standard File Permissions
644(rw-r--r--): Regular files600(rw-------): Private files664(rw-rw-r--): Group-editable files400(r--------): Read-only private files
Standard Directory Permissions
755(rwxr-xr-x): Regular directories700(rwx------): Private directories775(rwxrwxr-x): Group-accessible directories1777(rwxrwxrwt): Temporary directories2775(rwxrwsr-x): Shared project directories
Security Checklist
- [ ] No world-writable files in system directories
- [ ] SUID/SGID bits only where necessary
- [ ] Home directories are 700 or 750
- [ ] SSH keys are 600
- [ ] Web files not writable by web server
- [ ] Log files readable only by authorized users
- [ ] Regular permission audits scheduled
- [ ] ACLs documented and reviewed
- [ ] umask set appropriately (022 or 027)
- [ ] Sticky bit set on shared writable directories
Key Takeaways
1. Permissions are the first line of defense in Linux 2. Follow the principle of least privilege 3. Regular audits prevent permission creep 4. Understand the difference between file and directory permissions 5. Special permissions can be security risks if misused 6. ACLs provide fine-grained control when needed 7. Different applications require different permission strategies 8. Always test permission changes in non-production first