Files, Permissions & Ownership

Why This Matters

It is 2 AM. A deployment just went out and the application is returning 403 Forbidden errors. The developer says "it works on my machine." You SSH into the production server, check the application directory, and discover that the deployment script set the wrong file permissions -- the web server process cannot read the files it needs to serve. You run one chmod command and the site comes back to life.

Permission problems are among the most common issues in Linux administration. They are also among the quickest to fix -- if you understand how they work. This chapter gives you a thorough, hands-on understanding of Linux file types, permission bits, ownership, and the more advanced features like SUID, sticky bits, and ACLs.


Try This Right Now

# Look at permissions on some files
ls -l /etc/passwd
ls -l /etc/shadow
ls -la ~/

# Create a test file and inspect it
touch /tmp/permtest
ls -l /tmp/permtest

# What is your current umask?
umask

# Who are you?
id
whoami
groups

Look at the output of ls -l /etc/passwd:

-rw-r--r-- 1 root root 2847 Jan 15 10:30 /etc/passwd

That string -rw-r--r-- encodes the file type and permissions. By the end of this chapter, you will be able to read and set these in your sleep.


File Types in Linux

Linux has seven file types. The first character in the ls -l output tells you which type you are looking at.

SymbolTypeExample
-Regular file/etc/passwd
dDirectory/home/alice
lSymbolic link/bin -> /usr/bin
bBlock device/dev/sda
cCharacter device/dev/tty
sSocket/run/systemd/journal/socket
pNamed pipe (FIFO)Created with mkfifo

Let us see each one:

# Regular file
ls -l /etc/hostname
# -rw-r--r-- ...

# Directory
ls -ld /etc
# drwxr-xr-x ...

# Symbolic link
ls -l /bin
# lrwxrwxrwx ... /bin -> usr/bin

# Block device (disk)
ls -l /dev/sda 2>/dev/null || ls -l /dev/vda 2>/dev/null
# brw-rw---- ...

# Character device (terminal)
ls -l /dev/tty
# crw-rw-rw- ...

# Socket
ls -l /run/systemd/journal/dev-log 2>/dev/null
# srw-rw-rw- ...

# Named pipe
mkfifo /tmp/mypipe
ls -l /tmp/mypipe
# prw-r--r-- ...
rm /tmp/mypipe

Think About It: Why does Linux distinguish between block devices and character devices? Think about how you access a hard disk (in blocks of data) versus a keyboard (one character at a time).


Understanding Permission Bits

Every file has three sets of permission bits:

  Owner   Group   Others
  r w x   r w x   r w x
PermissionOn a FileOn a Directory
r (read)Read file contentsList directory contents (ls)
w (write)Modify fileCreate/delete files in directory
x (execute)Run as programEnter directory (cd)

Let us decode the string -rw-r--r--:

-    rw-    r--    r--
|    |      |      |
|    |      |      +-- Others: read only
|    |      +--------- Group: read only
|    +---------------- Owner: read + write
+--------------------- Type: regular file

Hands-On: See Permissions in Action

# Create a test directory
mkdir -p /tmp/permlab
cd /tmp/permlab

# Create a file
echo "Hello, permissions!" > testfile.txt
ls -l testfile.txt
# -rw-r--r-- 1 youruser yourgroup 20 ... testfile.txt

# Remove read permission for yourself
chmod u-r testfile.txt
cat testfile.txt
# cat: testfile.txt: Permission denied

# Restore read, remove for others
chmod u+r,o-r testfile.txt
ls -l testfile.txt
# -rw-r----- 1 youruser yourgroup 20 ... testfile.txt

# Make it executable
chmod u+x testfile.txt
ls -l testfile.txt
# -rwxr----- 1 youruser yourgroup 20 ... testfile.txt

Directory Permissions Are Different

This is a common source of confusion. For directories:

  • r (read): You can list the directory contents with ls
  • w (write): You can create, rename, or delete files inside
  • x (execute): You can enter the directory with cd and access files inside
# Create a test directory
mkdir /tmp/dirtest
echo "secret" > /tmp/dirtest/file.txt

# Remove execute permission
chmod a-x /tmp/dirtest
ls /tmp/dirtest          # Works (read still allowed)
cat /tmp/dirtest/file.txt  # FAILS -- can't traverse directory

# Restore execute, remove read
chmod a+x,a-r /tmp/dirtest
ls /tmp/dirtest          # FAILS -- can't list
cat /tmp/dirtest/file.txt  # Works (if you know the filename)

# Clean up
chmod 755 /tmp/dirtest

Think About It: A directory has r permission but not x. Can you list files in it? Can you read those files? What about the reverse -- x but no r?


Octal (Numeric) Notation

Sysadmins almost always use the octal shorthand instead of the symbolic rwx notation. Each permission bit is a power of 2:

r = 4
w = 2
x = 1

So:  rwx = 4+2+1 = 7
     rw- = 4+2+0 = 6
     r-x = 4+0+1 = 5
     r-- = 4+0+0 = 4
     --- = 0+0+0 = 0

Three digits for owner, group, others:

chmod 755 file  -->  rwxr-xr-x   (owner: full, group/others: read+execute)
chmod 644 file  -->  rw-r--r--   (owner: read+write, group/others: read only)
chmod 700 file  -->  rwx------   (owner: full, nobody else)
chmod 600 file  -->  rw-------   (owner: read+write, nobody else)
chmod 777 file  -->  rwxrwxrwx   (everyone: full access -- almost never do this!)

Common permissions you will use every day:

OctalSymbolicTypical Use
644rw-r--r--Regular files (configs, HTML, etc.)
755rwxr-xr-xDirectories, executable scripts
600rw-------Private files (SSH keys, secrets)
700rwx------Private directories
750rwxr-x---Group-accessible directories
664rw-rw-r--Shared writable files
# Practice: set permissions using octal
chmod 644 /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt

chmod 600 /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt

chmod 755 /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt

Safety Warning: Never use chmod 777 on production systems. It means "every user on this system can read, write, and execute this file." If someone tells you to run chmod -R 777 /, they are either joking or about to destroy your system's security.


Ownership: chown and chgrp

Every file has an owner (user) and a group. You change them with chown and chgrp.

# View ownership
ls -l /tmp/permlab/testfile.txt
# -rwxr-xr-x 1 youruser yourgroup 20 ... testfile.txt
#               ^^^^^^^^ ^^^^^^^^^
#               owner    group

# Change owner (requires root)
sudo chown root /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt

# Change group
sudo chgrp root /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt

# Change both at once: user:group
sudo chown youruser:yourgroup /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt

# Recursively change ownership of a directory
sudo chown -R www-data:www-data /var/www/html 2>/dev/null

Real-World Scenario: Web Server Permissions

A web server (Nginx or Apache) runs as a specific user, typically www-data (Debian/Ubuntu) or nginx (RHEL/Fedora). The web content files must be readable by that user.

# Typical web directory setup
sudo mkdir -p /var/www/mysite
sudo chown -R www-data:www-data /var/www/mysite
sudo chmod -R 755 /var/www/mysite
sudo chmod 644 /var/www/mysite/*.html 2>/dev/null

Distro Note: The web server user is www-data on Debian/Ubuntu, nginx on RHEL/Fedora (when using Nginx), and apache on RHEL/Fedora (when using Apache httpd).


The umask: Default Permission Control

When you create a new file or directory, the permissions are not set to 777. They are filtered through your umask (user file-creation mask).

# Check your current umask
umask
# Typical output: 0022

# What does 0022 mean?
# Default for files:   666 - 022 = 644 (rw-r--r--)
# Default for dirs:    777 - 022 = 755 (rwxr-xr-x)

# Verify
touch /tmp/umask-test-file
mkdir /tmp/umask-test-dir
ls -l /tmp/umask-test-file
ls -ld /tmp/umask-test-dir

The umask subtracts permissions. Common umask values:

umaskFile resultDirectory resultUse case
022644755Default (shared readable)
027640750Group-readable, others blocked
077600700Private (only owner)
002664775Group-collaborative
# Temporarily change umask
umask 077
touch /tmp/private-file
ls -l /tmp/private-file
# -rw------- 1 youruser yourgroup 0 ... /tmp/private-file

# Reset to default
umask 022

To make umask changes permanent, add umask 027 to your ~/.bashrc or set it system-wide in /etc/login.defs or /etc/profile.


Special Permission Bits: SUID, SGID, and Sticky

Beyond the basic rwx bits, there are three special bits that you will encounter in production systems.

SUID (Set User ID) -- The 4 Bit

When set on an executable, the program runs as the file's owner, not the user who invoked it. This is how passwd can modify /etc/shadow even when run by a regular user.

# Notice the 's' in the owner's execute position
ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root ... /usr/bin/passwd
#    ^
#    SUID bit

# Find all SUID binaries on the system
sudo find / -perm -4000 -type f 2>/dev/null

Setting SUID:

# Using octal (4 prepended)
chmod 4755 somefile

# Using symbolic
chmod u+s somefile

Safety Warning: SUID binaries are a security-sensitive feature. A SUID root program with a vulnerability can give attackers root access. Regular security audits should check for unexpected SUID binaries.

SGID (Set Group ID) -- The 2 Bit

On an executable: the program runs with the file's group privileges. On a directory: new files created inside inherit the directory's group (instead of the creator's primary group). This is extremely useful for shared project directories.

# Create a shared project directory
sudo mkdir /tmp/project
sudo chgrp developers /tmp/project 2>/dev/null || sudo chgrp staff /tmp/project
sudo chmod 2775 /tmp/project
ls -ld /tmp/project
# drwxrwsr-x ... /tmp/project
#         ^
#         SGID bit (s in group execute position)

# New files inherit the group
touch /tmp/project/newfile
ls -l /tmp/project/newfile
# The group will be 'developers' (or 'staff'), not your primary group

Sticky Bit -- The 1 Bit

When set on a directory, only the file's owner (or root) can delete or rename files inside, even if others have write permission. The classic example is /tmp.

ls -ld /tmp
# drwxrwxrwt ... /tmp
#          ^
#          Sticky bit (t in others execute position)

# Without the sticky bit, any user could delete any other user's files in /tmp
# With it, you can only delete your own files

Setting the sticky bit:

chmod 1777 /tmp/shared-dir    # Octal
chmod +t /tmp/shared-dir      # Symbolic

Summary of Special Bits

Octal    Symbolic    Position              Effect
4xxx     u+s         Owner execute: s/S    SUID -- run as file owner
2xxx     g+s         Group execute: s/S    SGID -- run as file group / inherit group
1xxx     +t          Other execute: t/T    Sticky -- only owner can delete

When you see an uppercase S or T instead of lowercase, it means the special bit is set but the underlying execute bit is NOT set (an unusual and often incorrect configuration).

# Uppercase S means SUID without execute -- usually a mistake
chmod 4644 /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt
# -rwSr--r-- ... (capital S = SUID set but no execute)

# Fix it
chmod 4755 /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt
# -rwsr-xr-x ... (lowercase s = SUID with execute)

Access Control Lists (ACLs)

Traditional Unix permissions are limited: one owner, one group, and everyone else. What if you need to give user alice read access and user bob read+write access to the same file, without creating a special group? ACLs solve this.

Checking ACL Support

# Check if the filesystem supports ACLs
mount | grep -E 'ext4|xfs|btrfs'
# Most modern filesystems support ACLs by default

# Install ACL tools if needed
# Debian/Ubuntu: sudo apt install acl
# Fedora/RHEL: sudo dnf install acl
# (usually pre-installed)

Using getfacl and setfacl

# Create a test file
echo "ACL test" > /tmp/acltest.txt

# View current ACL
getfacl /tmp/acltest.txt
# file: tmp/acltest.txt
# owner: youruser
# group: yourgroup
# user::rw-
# group::r--
# other::r--

# Grant specific user read+write access
sudo setfacl -m u:nobody:rw /tmp/acltest.txt

# View updated ACL
getfacl /tmp/acltest.txt
# Notice the new line: user:nobody:rw-

# ls -l now shows a '+' indicating ACLs are present
ls -l /tmp/acltest.txt
# -rw-rw-r--+ 1 youruser yourgroup ... /tmp/acltest.txt
#           ^
#           The + means ACLs are set

# Grant a group specific permissions
sudo setfacl -m g:adm:r /tmp/acltest.txt

# Set a default ACL on a directory (affects new files created inside)
mkdir /tmp/acl-dir
setfacl -d -m u:nobody:rwx /tmp/acl-dir
getfacl /tmp/acl-dir

# Remove a specific ACL entry
setfacl -x u:nobody /tmp/acltest.txt

# Remove all ACLs
setfacl -b /tmp/acltest.txt

ACL Quick Reference

CommandEffect
getfacl fileShow ACLs on a file
setfacl -m u:alice:rwx fileGive user alice rwx access
setfacl -m g:devs:rx fileGive group devs r-x access
setfacl -x u:alice fileRemove alice's ACL entry
setfacl -b fileRemove all ACLs
setfacl -d -m u:alice:rwx dirSet default ACL for new files in dir
setfacl -R -m u:alice:rx dirApply recursively

Think About It: When would you use ACLs instead of traditional Unix permissions? Think about a web development team where the designer needs read-only access to CSS files, the developer needs full access, and the deployment bot needs read+execute.


Real-World Permission Scenarios

Scenario 1: SSH Key Permissions

SSH is very strict about permissions. If your key files are too permissive, SSH will refuse to use them.

# These are the required permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa            # Private key: owner read/write ONLY
chmod 644 ~/.ssh/id_rsa.pub        # Public key: readable
chmod 600 ~/.ssh/authorized_keys   # authorized_keys: owner read/write ONLY
chmod 644 ~/.ssh/config            # SSH config: readable

# If you get "Permissions too open" errors, this is why

Scenario 2: Shared Team Directory

# Create a shared directory for the 'devteam' group
sudo groupadd devteam
sudo usermod -aG devteam alice
sudo usermod -aG devteam bob

sudo mkdir /opt/project
sudo chown root:devteam /opt/project
sudo chmod 2775 /opt/project
# 2 = SGID (new files inherit the group)
# 775 = rwxrwxr-x (owner and group can write, others can read)

Scenario 3: Fixing the Web Server 403 Error

# The problem: web server can't read files
# Step 1: Check what user the web server runs as
ps aux | grep -E 'nginx|apache|httpd' | head -3

# Step 2: Check current permissions
ls -la /var/www/html/

# Step 3: Fix ownership
sudo chown -R www-data:www-data /var/www/html/

# Step 4: Fix permissions
sudo find /var/www/html -type d -exec chmod 755 {} \;
sudo find /var/www/html -type f -exec chmod 644 {} \;

Debug This

You have a script called deploy.sh with the following permissions:

-rw-r--r-- 1 deploy deploy 1542 Jan 20 14:30 deploy.sh

When you try to run it, you get "Permission denied." You run chmod 777 deploy.sh and it works, but your security team flags this as a violation. What is the minimum permission change needed?

Solution

The script is missing the execute bit. You do not need 777. The minimum fix is:

chmod u+x deploy.sh
# Result: -rwxr--r-- (owner can execute, no extra access for others)

# Or if the whole team needs to run it:
chmod 755 deploy.sh
# Result: -rwxr-xr-x (everyone can execute, only owner can modify)

Using 777 was wrong because it gives write access to everyone -- any user on the system could modify your deploy script and inject malicious commands.


What Just Happened?

+------------------------------------------------------------------+
|                    CHAPTER 6 RECAP                                |
+------------------------------------------------------------------+
|                                                                    |
|  File Types: regular(-) dir(d) link(l) block(b) char(c)          |
|              socket(s) pipe(p)                                    |
|                                                                    |
|  Permission Bits:    r=4   w=2   x=1                              |
|  Three sets:         Owner | Group | Others                       |
|  Common octals:      644 (files), 755 (dirs), 600 (private)      |
|                                                                    |
|  Commands:                                                         |
|    chmod   -- Change permissions (symbolic or octal)              |
|    chown   -- Change owner (and optionally group)                 |
|    chgrp   -- Change group                                        |
|    umask   -- Set default permission mask                         |
|                                                                    |
|  Special bits:                                                     |
|    SUID (4xxx)  -- Run as file owner                              |
|    SGID (2xxx)  -- Run as file group / inherit group in dirs      |
|    Sticky (1xxx) -- Only owner can delete in shared dirs          |
|                                                                    |
|  ACLs: getfacl / setfacl for fine-grained access control         |
|                                                                    |
+------------------------------------------------------------------+

Try This

Exercises

  1. Decode these permissions. Without using a computer, write out what each octal means in rwx notation:

    • 750
    • 4711
    • 1777
    • 2755
    • 0600
  2. Permission detective. Run ls -l /usr/bin/passwd, ls -l /etc/shadow, and ls -l /etc/passwd. Explain why passwd has the SUID bit and how this allows regular users to change their password.

  3. Directory permissions lab. Create a directory where:

    • The owner can do everything
    • The group can read and enter but not create files
    • Others have no access at all What octal value is this?
  4. ACL challenge. Create a file. Using ACLs, give three different users three different levels of access (one gets r, another rw, another rwx). Verify with getfacl.

  5. Security audit. Find all SUID and SGID binaries on your system. How many are there? Research what three of them do and explain why they need the special bit.

Bonus Challenge

Write a script that takes a directory as an argument and produces a permission report:

  • Lists all files with their octal permissions
  • Flags any files with 777 permissions
  • Flags any SUID/SGID files
  • Reports the total number of files for each permission level
#!/bin/bash
# permission-audit.sh - Audit file permissions in a directory
DIR="${1:-.}"
echo "=== Permission Audit: $DIR ==="
echo ""

echo "--- Files with 777 permissions (DANGER!) ---"
find "$DIR" -perm 777 -type f 2>/dev/null

echo ""
echo "--- SUID files ---"
find "$DIR" -perm -4000 -type f 2>/dev/null

echo ""
echo "--- SGID files ---"
find "$DIR" -perm -2000 -type f 2>/dev/null

echo ""
echo "--- Permission distribution ---"
find "$DIR" -type f -printf '%m\n' 2>/dev/null | sort | uniq -c | sort -rn | head -20

echo ""
echo "--- World-writable files ---"
find "$DIR" -perm -o+w -type f 2>/dev/null

Next up: Chapter 7 -- Disks, Partitions & Filesystems. Now that you know about files and permissions, let us look at the physical storage underneath it all.