Loading learning content...
In 1969, when Ken Thompson and Dennis Ritchie created Unix at Bell Labs, they faced a fundamental design challenge: how should a multi-user operating system protect files? Their answer was breathtakingly simple—nine bits.
With just nine binary digits, Unix encodes a permission model that has protected files on servers, workstations, mobile devices, embedded systems, and cloud infrastructure for over half a century. The User/Group/World model (also called owner/group/other) is perhaps the most successful security mechanism ever designed, not because it's the most powerful, but because it achieves the right balance of simplicity, expressiveness, and efficiency.
This page explores every facet of this elegant system—how it works, why it was designed this way, and how to use it effectively.
By the end of this page, you will understand: how permission bits are encoded and interpreted, the semantics of owner/group/other classifications, how umask controls default permissions, special permission bits (setuid, setgid, sticky), and practical permission settings for common scenarios.
The Unix permission model divides all potential accessors of a file into exactly three classes. This is a key simplification—rather than maintaining individual user permissions, Unix categorizes everyone into one of three buckets:
The classification algorithm:
When a process (running as some user) attempts to access a file, the kernel determines which permission class applies:
Critical detail: Only ONE class applies. If you're the owner, group permissions are ignored—even if you're in the group and group permissions are more permissive. The classifications are mutually exclusive and evaluated in strict order.
1234567891011121314151617181920212223242526272829303132
// Simplified permission check logic (conceptual)int check_permission(struct process *proc, struct inode *file, int mode) { int permission_bits; // Superuser bypasses all checks if (proc->euid == 0) { // For execute, at least one execute bit must be set if (mode == EXECUTE && !(file->mode & 0111)) return PERMISSION_DENIED; return PERMISSION_GRANTED; } // Determine which class applies if (proc->euid == file->owner_uid) { // Process owner == file owner: use owner bits permission_bits = (file->mode >> 6) & 0x7; // bits 6-8 } else if (is_member_of_group(proc, file->owner_gid)) { // Process is in file's group: use group bits permission_bits = (file->mode >> 3) & 0x7; // bits 3-5 } else { // Everyone else: use other bits permission_bits = file->mode & 0x7; // bits 0-2 } // Check if requested mode is permitted if ((permission_bits & mode) == mode) return PERMISSION_GRANTED; else return PERMISSION_DENIED;}A surprising consequence of class exclusivity: an owner can have fewer permissions than the group. If a file has mode ---rwx---, the owner cannot read it, but group members can! This is occasionally used intentionally—for example, preventing accidental modification by the owner while allowing team access.
Each permission class receives three bits, representing three distinct access rights. Nine bits total (3 classes × 3 rights), stored in the file's inode along with other metadata.
| Position | Octal Value | Symbol | Meaning |
|---|---|---|---|
| Bit 8 (Owner Read) | 400 | r | Owner can read file / list directory |
| Bit 7 (Owner Write) | 200 | w | Owner can modify file / add/remove directory entries |
| Bit 6 (Owner Execute) | 100 | x | Owner can execute file / traverse directory |
| Bit 5 (Group Read) | 040 | r | Group can read file / list directory |
| Bit 4 (Group Write) | 020 | w | Group can modify file / add/remove directory entries |
| Bit 3 (Group Execute) | 010 | x | Group can execute file / traverse directory |
| Bit 2 (Other Read) | 004 | r | Others can read file / list directory |
| Bit 1 (Other Write) | 002 | w | Others can modify file / add/remove directory entries |
| Bit 0 (Other Execute) | 001 | x | Others can execute file / traverse directory |
Reading octal notation:
Permissions are commonly expressed as a three-digit octal number, where each digit represents one permission class (owner, group, other). Each digit ranges from 0-7, with bits for read (4), write (2), and execute (1).
1234567891011121314151617181920212223242526272829303132333435
Permission Octal Values:┌─────────────────────────────────────────────────────────┐│ Read (r) = 4 (binary: 100) ││ Write (w) = 2 (binary: 010) ││ Execute (x) = 1 (binary: 001) │└─────────────────────────────────────────────────────────┘ Example: Mode 0754┌───────────┬───────────┬───────────┐│ Owner │ Group │ Other │├───────────┼───────────┼───────────┤│ 7 │ 5 │ 4 ││ 4+2+1 │ 4+0+1 │ 4+0+0 ││ rwx │ r-x │ r-- │└───────────┴───────────┴───────────┘ Meaning:- Owner: read, write, execute (full control)- Group: read and execute (can run, can't modify)- Other: read only (can view, can't modify or run) Common Permission Modes:┌──────────┬────────────┬───────────────────────────────────────┐│ Mode │ Symbolic │ Purpose │├──────────┼────────────┼───────────────────────────────────────┤│ 0755 │ rwxr-xr-x │ Executable, world-runnable ││ 0644 │ rw-r--r-- │ Regular file, world-readable ││ 0700 │ rwx------ │ Private executable ││ 0600 │ rw------- │ Private file (config, keys) ││ 0775 │ rwxrwxr-x │ Shared group directory/executable ││ 0664 │ rw-rw-r-- │ Shared group file ││ 0750 │ rwxr-x--- │ Group-accessible directory ││ 0640 │ rw-r----- │ Group-readable file ││ 0000 │ --------- │ No access (root can still access) │└──────────┴────────────┴───────────────────────────────────────┘When ls -l shows -rwxr-xr--, read it as: first character is file type (- for regular, d for directory, l for link), then three groups of three characters for owner/group/other. Each character is r, w, x, or - (for absent). So rwxr-xr-- means owner=rwx, group=r-x, other=r--.
The same permission bits have different meanings depending on whether they apply to a regular file or a directory. This distinction is crucial and frequently misunderstood.
| Permission | On Regular Files | On Directories |
|---|---|---|
| Read (r) | Open file and read its contents | List directory entries (file names) |
| Write (w) | Modify file contents, truncate file | Create, delete, or rename entries within the directory |
| Execute (x) | Load and execute as program | Enter (cd into) the directory; access files by name |
Directory permissions—the counterintuitive parts:
Execute without Read (--x): You can access files inside if you know their exact names, but you cannot list the directory. This creates 'hidden' files accessible only to those who know the path. Commonly used for drop-boxes or URL path patterns.
Read without Execute (r--): You can list file names but cannot access any file inside or cd into the directory. The names are visible, but the contents are inaccessible.
Write on Directory (not file) controls deletion:
To delete a file, you need write permission on the directory, not on the file itself. A file with mode 000 can still be deleted if you have write on its parent directory.
123456789101112
# Directory with read only$ ls -ld test_dirdr--r--r-- 2 alice alice 4096 Jan 16 10:00 test_dir $ ls test_dirfile1.txt file2.txt # ✓ Can list $ cat test_dir/file1.txtcat: test_dir/file1.txt: Permission denied # ✗ $ cd test_dirbash: cd: test_dir: Permission denied # ✗123456789101112
# Directory with execute only$ ls -ld test_dird--x--x--x 2 alice alice 4096 Jan 16 10:00 test_dir $ ls test_dirls: cannot open directory 'test_dir': Permission denied # ✗ $ cat test_dir/file1.txtHello, World! # ✓ If you know the name $ cd test_diralice@host:~/test_dir$ # ✓ Can enterThis is a common source of confusion: to delete mydir/myfile, you need write permission on mydir, NOT on myfile. Even if myfile is mode 000 (no permissions), you can delete it if you can write to its parent directory. This is because deletion modifies the directory, not the file.
The chmod (change mode) command modifies file and directory permissions. Only the file's owner or the superuser (root) can change permissions. Two notation styles are supported:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
# =====================================================# OCTAL NOTATION - Set exact permission value# ===================================================== # Set standard executable permissions (rwxr-xr-x)chmod 755 myscript.sh # Set standard file permissions (rw-r--r--)chmod 644 document.txt # Private file - owner read/write onlychmod 600 ~/.ssh/id_rsa # Private directory - owner full access onlychmod 700 ~/.ssh # Group shared file (rw-rw-r--)chmod 664 team_report.txt # =====================================================# SYMBOLIC NOTATION - Relative permission changes# ===================================================== # Syntax: chmod [who][operator][permissions] file# who: u (user/owner), g (group), o (other), a (all)# operator: + (add), - (remove), = (set exactly)# permissions: r, w, x, X, s, t # Add execute permission for ownerchmod u+x script.sh # Remove write permission from group and otherschmod go-w sensitive.txt # Set group permissions to read-only (exactly)chmod g=r config.yml # Add execute for everyone (all)chmod a+x startup.sh # Remove all permissions from otherschmod o= private_file.txt # or chmod o-rwx # Combination: owner full, group read-execute, others nonechmod u=rwx,g=rx,o= myapp # =====================================================# RECURSIVE CHANGES# ===================================================== # Change directory and all contentschmod -R 755 project_dir/ # Add group write recursivelychmod -R g+w shared_folder/ # =====================================================# SPECIAL: Capital X# ===================================================== # X means: add execute only if it's a directory OR # already has execute for some class.# Useful for recursive: makes directories traversable# without making all files executable. chmod -R u=rwX,g=rX,o=rX project/# Directories get rwx/rx/rx, files get rw-/r--/r--Octal notation is best when you know exactly what permissions you want—it's concise and unambiguous. Use it for setting security-conscious configurations.
Symbolic notation is best for modifications—adding or removing specific permissions without changing others. It's also self-documenting: chmod g+w clearly means 'add group write.'
When you create a new file or directory, what permissions does it get? The answer involves two factors: the requested permissions (from the creating program) and the umask (a mask that removes permissions).
The formula:
Actual Permissions = Requested Permissions AND (NOT umask)
Or, more intuitively: the umask specifies which permission bits to remove from newly created files.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
# Understanding umask # View current umask$ umask0022 # Typical default: remove group-write and other-write # What does this mean?# New files: 0666 (requested) minus 0022 (umask) = 0644 (rw-r--r--)# New directories: 0777 (requested) minus 0022 (umask) = 0755 (rwxr-xr-x) # Why 0666 for files, 0777 for dirs?# Programs request 0666 for files (no execute by default - security)# Programs request 0777 for directories (need execute to traverse) # Umask is a MASK (bits to remove), not a permission value# umask 0022 = remove bits 022 = remove group-write, other-write # =====================================================# Common umask values# ===================================================== umask 0022 # Standard: files 644, dirs 755 (world readable)umask 0027 # Tighter: files 640, dirs 750 (no world access) umask 0077 # Private: files 600, dirs 700 (owner only)umask 0002 # Group sharing: files 664, dirs 775 (group can write) # =====================================================# Setting umask# ===================================================== # Temporary (current shell only)umask 0027 # Permanent (add to ~/.bashrc or ~/.profile)echo "umask 0027" >> ~/.bashrc # =====================================================# Demonstration# ===================================================== $ umask 0022$ touch newfile.txt$ mkdir newdir$ ls -la-rw-r--r-- 1 alice alice 0 Jan 16 10:30 newfile.txt # 644drwxr-xr-x 2 alice alice 4096 Jan 16 10:30 newdir # 755 $ umask 0077$ touch private.txt$ mkdir privatedir$ ls -la-rw------- 1 alice alice 0 Jan 16 10:31 private.txt # 600drwx------ 2 alice alice 4096 Jan 16 10:31 privatedir # 700The umask doesn't set permissions; it removes them. A umask of 0077 removes all group and world permissions. A umask of 0000 removes nothing—you get whatever the creating program requested (usually 0666 for files, 0777 for directories).
Every file has an owner (UID) and a group (GID). Changing these determines which permission class applies to which users.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
# =====================================================# chown - Change file owner (and optionally group)# ===================================================== # Change owner to bobchown bob myfile.txt # Change owner to bob and group to developerschown bob:developers myfile.txt # Change group only (owner unchanged) - note the colonchown :developers myfile.txt # Recursive ownership changechown -R bob:developers project/ # =====================================================# chgrp - Change file group# ===================================================== # Change group to developerschgrp developers myfile.txt # Recursive group changechgrp -R developers project/ # =====================================================# Restrictions on ownership changes# ===================================================== # Only root can change owner (on most Unix systems)# This prevents users from "giving away" files to escape quotas # Regular users can change group, but only to a group they're in$ groupsalice developers staff # alice can change files she owns to any of: alice, developers, staff$ chgrp developers myfile.txt # ✓ Works$ chgrp marketing myfile.txt # ✗ Fails (not a member) # =====================================================# Practical example: Setting up shared directory# ===================================================== # Create shared project directorysudo mkdir /shared/project_alphasudo chown root:developers /shared/project_alphasudo chmod 2775 /shared/project_alpha # Result:# - Root owns the directory (admin control)# - developers group can write# - setgid (the 2) makes new files inherit group# - World can read/traverse but not modifyMost Unix systems restrict chown to root only. Why? If users could give files away, they could: (1) exceed disk quotas by creating large files then giving them to others, (2) plant malicious files appearing to come from trusted users, (3) circumvent backup and audit policies. BSD systems allowed user chown historically, but modern Linux restricts it.
Beyond the basic 9 bits, Unix has three additional permission bits that modify execution and file creation behavior. These are stored in bits 9-11 of the mode and displayed as modifications to the execute bits.
| Bit | Octal | On Files | On Directories |
|---|---|---|---|
| setuid | 4000 | Execute with file owner's privileges | Usually ignored |
| setgid | 2000 | Execute with file group's privileges | New files inherit directory's group |
| sticky | 1000 | Historically: keep in swap; now: rare | Only owner can delete files they own |
setuid — Controlled privilege escalation:
When an executable has setuid set, it runs with the privileges of the file's owner, not the user who executed it. This is how regular users can change their passwords:
123456789101112131415161718192021222324
# Examining setuid programs $ ls -l /usr/bin/passwd-rwsr-xr-x 1 root root 63736 Feb 7 2023 /usr/bin/passwd ^ └── 's' instead of 'x' means setuid is set # When alice runs passwd:# - Process runs as root (file owner), not as alice# - Process can modify /etc/shadow (owned by root)# - Regular users can change their own passwords # Other common setuid programs:$ ls -l /bin/su /usr/bin/sudo /usr/bin/crontab-rwsr-xr-x 1 root root 63568 ... /bin/su-rwsr-xr-x 1 root root 182600 ... /usr/bin/sudo-rwsr-xr-x 1 root root 43352 ... /usr/bin/crontab # Setting setuid (requires root for root-owned files)chmod 4755 my_privileged_prog # setuid + rwxr-xr-xchmod u+s my_privileged_prog # symbolic form # Capital 'S' indicates setuid without execute (broken):-rwSr-xr-x # setuid set but owner execute missing - won't worksetgid — Group collaboration and privilege:
For executables, setgid runs the program with the file's group privileges. For directories, it causes new files to inherit the directory's group instead of the creator's primary group—essential for shared workspaces.
123456789101112131415161718192021222324
# setgid on directories - group inheritance # Without setgid: files get creator's primary group$ umask 0002$ mkdir /shared/normal_dir$ touch /shared/normal_dir/myfile$ ls -l /shared/normal_dir/myfile-rw-rw-r-- 1 alice alice 0 ... # group is alice's primary group # With setgid: files inherit directory's group$ chmod g+s /shared/project # or chmod 2775 /shared/project$ ls -ld /shared/projectdrwxrwsr-x 2 root developers 4096 ... # note the 's' in group-execute ^ └── 's' means setgid on directory $ touch /shared/project/newfile$ ls -l /shared/project/newfile-rw-rw-r-- 1 alice developers 0 ... # group is "developers"! # Subdirectories also inherit setgid$ mkdir /shared/project/subdir$ ls -ld /shared/project/subdirdrwxrwsr-x 2 alice developers 4096 ... # setgid propagatessticky bit — Deletion restriction:
On directories (its only modern use), the sticky bit restricts deletion. In a sticky directory, you can only delete files you own, regardless of directory write permissions. This protects shared directories like /tmp.
123456789101112131415161718192021222324
# The sticky bit on /tmp $ ls -ld /tmpdrwxrwxrwt 15 root root 12288 Jan 16 10:00 /tmp ^ └── 't' means sticky bit is set # Everyone can create files in /tmp (mode 777)# But the sticky bit prevents deletion of others' files $ touch /tmp/alice_file$ ls -l /tmp/alice_file-rw-r--r-- 1 alice alice 0 Jan 16 10:01 /tmp/alice_file # Bob cannot delete alice's file despite dir being world-writable$ sudo -u bob rm /tmp/alice_filerm: cannot remove '/tmp/alice_file': Operation not permitted # But alice can delete her own file$ rm /tmp/alice_file # ✓ Works # Setting the sticky bitchmod +t shared_directory # symbolicchmod 1777 shared_directory # octal (1000 + 777)setuid programs are one of the largest attack surfaces in Unix. A vulnerability in a setuid-root program gives attackers root access. Modern systems: (1) minimize setuid programs, (2) drop privileges as soon as possible within setuid programs, (3) use capabilities instead of full root for specific privileges, (4) use filesystem features to prevent setuid in untrusted directories (nosuid mount option).
Despite its success, the User/Group/World model has fundamental limitations that drove the development of more advanced permission systems:
The multi-group problem illustrated:
Suppose you have three development teams: Frontend, Backend, and DevOps. You want:
deploy.sh: Backend and DevOps can execute (Frontend cannot)config.yaml: All three can read, only DevOps can writeWith User/Group/World, this is impossible without:
This is why Access Control Lists (ACLs), covered in the next page, were developed as an extension.
Despite limitations, User/Group/World remains dominant because: (1) Simplicity — 9 bits are fast to check and easy to understand; (2) Sufficient for most cases — The majority of files need only owner-private or world-readable semantics; (3) Ubiquity — Every tool, from ls to rsync, understands these permissions; (4) Efficiency — Storing and checking permissions is trivial. More complex systems layer on top when needed.
What's next:
The next page explores Access Control Lists (ACLs)—extending the Unix model to support arbitrary user- and group-specific permissions on individual files, overcoming the one-group limitation while maintaining compatibility with traditional Unix permissions.
You now have a thorough understanding of Unix's User/Group/World permission model—its structure, semantics, commands, and limitations. This foundation enables you to configure file protection correctly and understand when more advanced mechanisms are needed.