File Permissions Advanced — chmod, chown, ACLs, setuid, Sticky Bit, umask
Linux file permissions control who can read, write, and execute files and directories. Beyond the basic rwx model, Linux supports Access Control Lists (ACLs), special permission bits (setuid, setgid, sticky), and default permission masks (umask). This guide covers everything you need for production multi-user systems.
What You’ll Learn
You’ll understand the full Linux permission model — symbolic and numeric modes, recursive ownership changes, ACL-based fine-grained access control, the security implications of setuid/setgid binaries, how the sticky bit protects shared directories, and how umask prevents accidentally exposed files. You’ll also learn how Durga Antivirus Pro uses restricted permissions and ACLs to isolate threat analysis sandboxes.
Why File Permissions Matter
Every security breach investigation starts with file permissions. A world-readable private SSH key, a setuid shell script, or a directory with 777 permissions are common attack vectors. In multi-user systems — like DodaTech’s shared build servers — misconfigured permissions let users read each other’s data. Mastering permissions is the foundation of Linux security.
Learning Path
flowchart LR
A[Essential Commands] --> B[Networking Commands]
B --> C[File Permissions<br/>You are here]
C --> D[Process Management]
D --> E[Security Hardening]
style C fill:#f90,color:#fff
Understanding File Permissions
Every file and directory in Linux has three permission sets:
| Set | Applies to | Example |
|---|---|---|
| User (u) | The file’s owner | rwx |
| Group (g) | Members of the file’s group | r-x |
| Others (o) | Everyone else | r-- |
Each set has three permissions: r (read), w (write), x (execute).
Numeric (Octal) Mode
Permissions are represented as octal numbers:
r = 4, w = 2, x = 1
rwx = 4+2+1 = 7
r-x = 4+0+1 = 5
r-- = 4+0+0 = 4
--- = 0Common modes:
755— rwxr-xr-x (owner full, group/others read+execute)644— rw-r–r– (owner read/write, others read-only)700— rwx—— (owner only)600— rw——- (owner read/write)777— rwxrwxrwx (everyone full access — avoid this)
chmod — Changing Permissions
# Symbolic mode
chmod u+x script.sh # Add execute for owner
chmod g-w file.txt # Remove write for group
chmod o+r file.txt # Add read for others
chmod a+rx script.sh # Add read+execute for all
chmod u=rwx,g=rx,o= file # Set precisely
# Numeric mode (most common)
chmod 755 script.sh
chmod 644 config.json
chmod 600 .ssh/id_rsa # Private key — must be 600!
chmod 700 ~/.ssh # SSH directory
# Recursive (use with caution)
chmod -R 755 /path/to/dir # All files get execute — wrong!
find /path -type f -exec chmod 644 {} \; # Files
find /path -type d -exec chmod 755 {} \; # DirectoriesExpected output for ls -l after chmod 600 private.key:
-rw------- 1 alice devops 1675 Jun 20 10:00 private.keychown and chgrp — Ownership
# Change owner
chown alice file.txt
# Change owner and group
chown alice:devops file.txt
# Change group only
chgrp devops file.txt
# Recursive with different users and groups
chown -R alice:devops /projects/myapp
# Use reference file
chown --reference=template.txt target.txtumask — Default Permission Mask
Umask defines the default permissions for newly created files and directories. It subtracts from the maximum permissions (666 for files, 777 for directories).
# Show current umask
umask
# The math: default_perms = max_perms - umask
# umask 022: files get 644 (666-022), dirs get 755 (777-022)
# umask 077: files get 600, dirs get 700
# umask 002: files get 664, dirs get 775
# Set umask (temporary, for current shell)
umask 027
# Set permanently (add to ~/.bashrc or /etc/profile)
echo "umask 027" >> ~/.bashrcUmask comparison:
| umask | File Perms | Directory Perms | Use Case |
|---|---|---|---|
| 022 | 644 (rw-r–r–) | 755 (rwxr-xr-x) | Default — public readable |
| 027 | 640 (rw-r—–) | 750 (rwxr-x—) | Team — group readable |
| 077 | 600 (rw——-) | 700 (rwx——) | Private — owner only |
| 002 | 664 (rw-rw-r–) | 775 (rwxrwxr-x) | Collaborative — group writable |
Special Permission Bits
setuid (SUID) — chmod u+s
When set on an executable, the process runs with the file owner’s privileges, not the user who ran it. This is how passwd lets regular users edit /etc/shadow.
# Set SUID
chmod u+s /usr/bin/myapp
chmod 4755 /usr/bin/myapp # Numeric: 4 = SUID
# Find SUID binaries (potential security risk)
find / -perm -4000 -type f 2>/dev/null
# Remove SUID
chmod u-s /usr/bin/myappSecurity warning: Never set SUID on shell scripts — they can be exploited via shell injection. SUID binaries must be carefully audited.
setgid (SGID) — chmod g+s
On executables: runs with the group of the file. On directories: new files inherit the directory’s group, not the creator’s primary group.
# Set SGID on directory — new files inherit group
chmod g+s /shared/project
chmod 2755 /shared/project # Numeric: 2 = SGID
# Verify — directory shows 's' in group execute position
ls -ld /shared/project
# drwxr-sr-x 2 alice devops 4096 Jun 20 10:00 /shared/projectSticky Bit — chmod o+t
Restricts deletion: only the owner of a file (or root) can delete or rename it, even if the directory is world-writable. Essential for /tmp.
# Set sticky bit
chmod o+t /shared/temp
chmod 1777 /shared/temp # Numeric: 1 = sticky
# Verify — shows 't' in others execute position
ls -ld /shared/temp
# drwxrwxrwt 2 root root 4096 Jun 20 10:00 /shared/temp
# Common sticky bit directories
ls -ld /tmp /var/tmp
# drwxrwxrwt 10 root root 4096 Jun 20 10:00 /tmpAccess Control Lists (ACLs)
ACLs provide fine-grained permissions beyond the traditional owner/group/others model. You can grant permissions to specific users or groups.
# Install ACL tools
sudo apt install acl # Debian/Ubuntu
sudo yum install acl # RHEL/CentOS
# Check if a filesystem supports ACLs
mount | grep aclManaging ACLs
# View ACLs on a file
getfacl file.txt
# Grant read/write to a specific user
setfacl -m u:bob:rw file.txt
# Grant read/execute to a specific group
setfacl -m g:qa:rx file.txt
# Remove a specific user entry
setfacl -x u:bob file.txt
# Remove all ACL entries (revert to basic)
setfacl -b file.txt
# Recursive with default ACLs (new files inherit)
setfacl -R -m g:devops:rx /shared/project
setfacl -R -d -m g:devops:rx /shared/project # Default ACLExpected output for getfacl /shared/project/data.txt:
# file: data.txt
# owner: alice
# group: devops
user::rw-
user:bob:rw- # Bob has explicit read/write
group::r-x # devops group has read/execute
mask::rwx # Maximum permissions ACL can grant
other::--- # No access for others
default:user::rwx # Default ACLs inherited by new files
default:group::r-xACL Best Practices
- Use ACLs sparingly — Too many ACL entries make auditing difficult
- Prefer groups over user ACLs — Adding users to a group is cleaner than per-user ACLs
- Understand the mask — The ACL mask limits the maximum permissions ACL entries can grant
- Document ACLs — ACLs aren’t visible in
ls -l(shows+), so document why they exist
Permission Inheritance and Best Practices
Directory vs File Permissions
| Permission | File Effect | Directory Effect |
|---|---|---|
| r (read) | Read content | List entries |
| w (write) | Modify content | Create/delete entries |
| x (execute) | Run as program | Access (cd into) |
Security Checklist
- SSH keys must be 600 —
chmod 600 ~/.ssh/id_rsa - Directories should not be world-writable — Avoid 777
- Remove SUID from unused binaries —
find / -perm -4000 -type f - Set restrictive umask (027) on servers
- Use groups, not individual user ACLs
- Regularly audit permission changes —
find /etc -mtime -7 -ls
Common Permission Errors
1. “Permission Denied” on a Readable File
The directory containing the file must have execute (x) permission. Without it, you can’t traverse the directory to reach the file.
2. chmod -R 755 on All Files
Applying chmod -R 755 sets execute on every file, including text files. This is unnecessary and a security risk. Use find to separate files and directories.
3. SSH Key with Wrong Permissions
Permissions 0644 for '/home/alice/.ssh/id_rsa' are too open.SSH requires private keys to be readable only by the owner (600 or 400).
4. SUID on Shell Scripts
Modern Linux kernels ignore SUID on interpreted scripts (starting with #!) for security reasons. Use a compiled wrapper or capabilities instead.
5. umask Forgotten in Scripts
Scripts that create temporary files inherit the umask from the calling shell. Always set umask 077 at the start of scripts that handle sensitive data.
6. ACL Mask Limiting Permissions
If ACLs aren’t working, check the mask. setfacl -m m::rwx file updates the mask to allow full ACL permissions.
7. Sticky Bit on Non-Shared Directories
Applying the sticky bit on a private directory provides no benefit and can confuse users who expect group collaboration.
Practice Questions
1. What do permissions 4755 mean? Setuid (4) + owner rwx (7) + group r-x (5) + others r-x (5). The binary runs with the file owner’s privileges.
2. How do you allow two specific users to write to a directory without giving all group members write access?
Use ACLs: setfacl -m u:alice:rwx,d:u:alice:rwx /shared/dir and similarly for bob. The d: prefix sets default ACLs for new files.
3. What does the sticky bit do on /tmp?
It prevents users from deleting each other’s files. Without it, anyone could rm /tmp/alice-temp-file since /tmp is world-writable.
4. What is the result of umask 027 on a new file? 666 - 027 = 640 (rw-r—–). New files are readable by owner and group, writable by owner, with no access for others.
5. Challenge: A shared project directory has users from two teams who need different access levels — Team A needs read/write, Team B needs read-only. New files should automatically get the right permissions. Design the solution.
Answer: Create two groups (team-a, team-b). Set SGID on the directory. Use default ACLs: setfacl -d -m g:team-a:rwx /project and setfacl -d -m g:team-b:rx /project. Set umask to 007. New files inherit group and ACLs automatically.
Mini Project: Permission Audit Script
Create a script that scans a directory for permission security issues:
#!/bin/bash
# perm_audit.sh — Scan for permission security issues
# Usage: ./perm_audit.sh /path/to/scan
SCAN_DIR="${1:-.}"
REPORT_FILE="perm_audit_$(date +%Y%m%d_%H%M%S).log"
echo "=== Permission Security Audit ===" | tee "$REPORT_FILE"
echo "Scan target: $SCAN_DIR" | tee -a "$REPORT_FILE"
echo "Report: $REPORT_FILE"
echo ""
# World-writable files
echo "--- World-Writable Files ---" | tee -a "$REPORT_FILE"
find "$SCAN_DIR" -type f -perm -o+w -ls 2>/dev/null >> "$REPORT_FILE"
# World-writable directories
echo "--- World-Writable Directories ---" | tee -a "$REPORT_FILE"
find "$SCAN_DIR" -type d -perm -o+w -ls 2>/dev/null >> "$REPORT_FILE"
# SUID binaries
echo "--- SUID Binaries ---" | tee -a "$REPORT_FILE"
find "$SCAN_DIR" -type f -perm -4000 -ls 2>/dev/null >> "$REPORT_FILE"
# Files with no permissions for owner
echo "--- Owner Without Access ---" | tee -a "$REPORT_FILE"
find "$SCAN_DIR" ! -perm -u+r -ls 2>/dev/null >> "$REPORT_FILE"
# Summary
echo "" | tee -a "$REPORT_FILE"
echo "=== Summary ===" | tee -a "$REPORT_FILE"
echo "World-writable files: $(find "$SCAN_DIR" -type f -perm -o+w 2>/dev/null | wc -l)" | tee -a "$REPORT_FILE"
echo "World-writable dirs: $(find "$SCAN_DIR" -type d -perm -o+w 2>/dev/null | wc -l)" | tee -a "$REPORT_FILE"
echo "SUID binaries: $(find "$SCAN_DIR" -type f -perm -4000 2>/dev/null | wc -l)" | tee -a "$REPORT_FILE"Expected output:
=== Permission Security Audit ===
Scan target: /home/alice
Report: perm_audit_20260620_100000.log
--- World-Writable Files ---
--- World-Writable Directories ---
--- SUID Binaries ---
--- Owner Without Access ---
=== Summary ===
World-writable files: 3
World-writable dirs: 0
SUID binaries: 1This script is modeled after the permission checks Durga Antivirus Pro runs during its security scan to identify files with insecure permissions that malware might exploit.
FAQ
What’s Next
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-20.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro