chmod and chown: Keys, Locks, and Who Owns the House
Unix file permissions explained through the analogy of keys and doors — who holds the key, what it opens, and why only the government can transfer the deed.
- DATE:
- APR.28.2026
- READ:
- 16 MIN
The house, the keys, and the locks
Every file on a Unix system is a house. When you create a file, you become the homeowner. You hold the master key.
But you are not the only person who needs access. Your family — a group of users with shared responsibilities — needs their own set of keys. And then there is the general public: everyone else on the system, walking past your house on the street. They might need to peek through the window, but you probably do not want them rearranging your furniture.
This is the Unix permission model. Three groups of people, three sets of keys, three types of access:
- Read (r) — peek through the window. You can see what is inside, but you cannot touch anything.
- Write (w) — rearrange the furniture. You can modify the contents, add things, remove things.
- Execute (x) — walk through the front door. For a file, this means running it as a program. For a directory, it means something different entirely (more on that later).
Every file carries exactly nine permission bits: three for the owner, three for the group, three for everyone else. That is it. Nine bits to describe who can do what. The entire security model of a file fits in less space than a single character of text.
A brief history
In 1969, Ken Thompson and Dennis Ritchie sat down at Bell Labs and started building Unix. They had already seen Multics (1965), which implemented full access control lists — any user could be granted any combination of permissions on any file. It was powerful. It was also complex, slow, and difficult to reason about.
Thompson and Ritchie made a deliberate trade. Instead of per-user ACLs, they simplified access control to three categories: the file’s owner, a single group, and everyone else. Three groups, three permission types, nine bits total. You could represent the entire permission state of a file in three octal digits.
The POSIX standard (IEEE 1003.1, 1988) later codified this model across all Unix-like systems. FreeBSD, Linux, macOS, Solaris — they all speak the same permission language.
The Unix permission model traded granularity for simplicity. Fifty years later, we are still using it.
Reading the locks — what ls -l tells you
Run ls -l on any file and the first column tells you everything about its locks:
$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 Mar 14 11:28 /usr/bin/passwdThat ten-character string at the front is the permission string. Here is how to read it:
The first character is the file type:
-regular fileddirectorylsymbolic link
The remaining nine characters break into three groups of three:
-rwsr-xr-x
^^^ owner: r=read, w=write, s=setuid+execute
^^^ group: r=read, -=no write, x=execute
^^^ other: r=read, -=no write, x=executeEach position is either the permission letter or a dash. Think of it as looking at the lock on each door: if you see the letter, the key works. If you see a dash, that lock is sealed shut.
chmod — changing the locks
chmod changes permissions. In the house analogy, it is rekeying the locks. You are not changing who owns the house or who belongs to the family — you are changing what each set of keys can do.
Octal notation
Each permission has a numeric value:
- Read = 4
- Write = 2
- Execute = 1
Add them up for each group. rwx = 4+2+1 = 7. r-x = 4+0+1 = 5. r-- = 4+0+0 = 4.
chmod 755 script.shThis means: owner gets 7 (rwx), group gets 5 (r-x), other gets 5 (r-x). In house terms: the owner gets the master key with full access, the family gets a read-only key with door access, and the public gets the same limited key.
Here are the common patterns:
+-------+------------+--------------------+ | Octal | Symbolic | Meaning | +-------+------------+--------------------+ | 644 | -rw-r--r-- | Owner | | | | reads/writes, | | | | everyone else | | | | reads only. | | | | Standard for | | | | files. | +-------+------------+--------------------+ | 755 | -rwxr-xr-x | Owner has full | | | | access, others can | | | | read and execute. | | | | Standard for dirs | | | | and scripts. | +-------+------------+--------------------+ | 600 | -rw------- | Owner | | | | reads/writes, | | | | nobody else | | | | touches it. | | | | Private files. | +-------+------------+--------------------+ | 700 | -rwx------ | Owner only, full | | | | access. Private | | | | directories. | +-------+------------+--------------------+ | 777 | -rwxrwxrwx | Everyone can do | | | | everything. Almost | | | | never correct. | +-------+------------+--------------------+
Symbolic notation
Instead of computing octal values, you can describe changes in plain English-like syntax:
chmod u+x script.sh # owner: add execute
chmod g-w shared.txt # group: remove write
chmod o=r public.html # other: set to read-only (remove everything else)
chmod a+r announcement # all: add read for everyone
chmod u+x,g=rx,o= app # combine: owner+exec, group=read+exec, other=nothingWhen to use which: octal is faster when you know the exact state you want. Symbolic is safer when you want to add or remove a single permission without disturbing the rest. chmod u+x does not touch group or other permissions. chmod 755 replaces everything.
The directory confusion
Permissions on directories do not mean what you think they mean. The three keys work differently when the “house” is actually a gated neighborhood.
- Read (r) — you can see the names on the mailboxes. You can list the files inside the directory.
- Write (w) — you can add or remove mailboxes. You can create and delete files inside the directory.
- Execute (x) — the gate is unlocked. You can walk through and actually reach the houses inside.
A directory with r but no x: you can read the names on the mailboxes from outside the fence, but you cannot walk through the gate to actually reach them. You will see filenames but get “Permission denied” when you try to read, write, or stat any of them. It is a surprisingly common misconfiguration.
A directory with x but no r: you can walk through the gate if you already know the exact address, but you cannot see the mailboxes. You can access files by name but cannot list them. This is occasionally used for security-through-obscurity setups.
chown — transferring the deed
chmod changes what the keys do. chown changes who owns the house.
chown alice:developers project/
chown alice project/ # change owner only
chown :developers project/ # change group only
chgrp developers project/ # equivalent to chown :developersHere is the critical difference: only root can run chown. A normal user cannot give away ownership of their files.
In the real world, you can hand someone your house keys. But transferring the deed? That goes through the government. Same with chown — only root can transfer ownership. There are good reasons for this:
- Quota evasion. If users could chown files to each other, you could dump gigabytes of data into someone else’s disk quota.
- Setuid exploits. If you could create a setuid program and then chown it to root, you would have instant privilege escalation.
- Non-repudiation. File ownership is part of the audit trail. Allowing arbitrary transfers would make forensics meaningless.
Historical note: old versions of Solaris did allow non-root users to chown their own files away. The POSIX standard made this implementation-defined, but modern systems universally restrict it to root for the reasons above.
The special keys
Beyond the standard nine bits, Unix has three special permission bits. These are the exotic keys — the ones that bend the normal rules.
setuid — the valet key
Imagine you need to change the locks on a room that only the building owner can access. You cannot do it yourself. But the owner leaves a valet key at the front desk: when you use it, you temporarily get the owner’s access, but only for that one specific task.
That is setuid. When set on an executable, anyone who runs it temporarily assumes the file owner’s identity for the duration of that process.
The classic example is /usr/bin/passwd. You, a normal user, need to change your password. But the password hashes live in /etc/shadow, which is owned by root and readable only by root. How do you write to a file you cannot even read?
The passwd command has the setuid bit set and is owned by root. When you run it, you temporarily act as root — but only within the confines of that program. The program itself enforces that you can only change your own password.
chmod 4755 /usr/bin/passwd
ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root ...The 4 in 4755 is the setuid bit. The s replacing the owner’s x in the ls output confirms it. If you see a capital S instead, it means setuid is set but execute is not — a broken configuration.
setgid — the family membership card
On files, setgid works like setuid but for the group: the process runs with the file’s group identity.
On directories, it does something more useful: new files created inside the directory automatically inherit the directory’s group, instead of the creator’s primary group.
chmod 2775 /opt/project/
chown :developers /opt/project/Now when any member of the developers group creates a file inside /opt/project/, the file’s group will be developers regardless of the user’s primary group. This is perfect for shared project directories where everyone needs to read and write each other’s files.
The 2 in 2775 is the setgid bit. In ls output, you will see an s in the group execute position.
sticky bit — the community bulletin board
Imagine a bulletin board in a public space. Everyone can post notices. But you can only take down your own notices — you cannot rip down someone else’s post.
That is the sticky bit on a directory. The canonical example is /tmp:
ls -ld /tmp
# drwxrwxrwt 18 root root 4096 Apr 27 10:00 /tmpThe t at the end is the sticky bit. The directory is world-writable (777), so anyone can create files in it. But the sticky bit means only a file’s owner (or root) can delete or rename that file. Without it, any user could delete anyone else’s temp files — a trivial denial-of-service attack.
chmod 1777 /tmpThe 1 in 1777 is the sticky bit.
umask — the default lock on new rooms
Every time you build a new room in your house, the locks have to start somewhere. umask decides which locks are installed by default on every new file and directory you create.
The key subtlety: umask is not subtracted from the permissions. It is a bitmask — bits set in the umask are cleared from the default permissions. The default for files is 666 (no execute) and for directories is 777.
$ umask
0022
$ touch newfile.txt
$ ls -l newfile.txt
-rw-r--r-- # 666 masked by 022 = 644
$ mkdir newdir
$ ls -ld newdir
drwxr-xr-x # 777 masked by 022 = 755Common umask values:
+-------+-----------------+-----------------+--------------------+ | umask | File result | Dir result | Use case | +-------+-----------------+-----------------+--------------------+ | 022 | 644 (rw-r--r--) | 755 (rwxr-xr-x) | Standard default. | | | | | Others can read. | +-------+-----------------+-----------------+--------------------+ | 027 | 640 (rw-r-----) | 750 (rwxr-x---) | Group can read, | | | | | others blocked. | +-------+-----------------+-----------------+--------------------+ | 077 | 600 (rw-------) | 700 (rwx------) | Owner only. | | | | | Maximum privacy. | +-------+-----------------+-----------------+--------------------+
Set your umask in your shell profile (~/.bashrc, ~/.zshrc). It applies to every file and directory you create in that session.
Real-world lock configurations
Web servers
The web server process (often running as www-data or nginx) needs to read your files but should never need to write to them.
- Standard: 755 for directories, 644 for files. The web server reads as “other.”
- Shared hosting (more secure): 750 for directories, 640 for files. Add the web server user to the file’s group. Others are completely locked out.
find /var/www/html -type d -exec chmod 755 {} ;
find /var/www/html -type f -exec chmod 644 {} ;
chown -R deployer:www-data /var/www/htmlSSH keys
SSH is fanatical about permissions. If your keys are too open, SSH will silently refuse to use them.
+--------------------+----------+--------------------+ | Path | Required | Why | +--------------------+----------+--------------------+ | ~/.ssh/ | 700 | Only you should | | | | see the directory | | | | contents. | +--------------------+----------+--------------------+ | ~/.ssh/id_rsa | 600 | Private key. | | | | Anyone who reads | | | | this IS you. | +--------------------+----------+--------------------+ | ~/.ssh/id_rsa.pub | 644 | Public key. Safe | | | | to share — that is | | | | the point. | +--------------------+----------+--------------------+ | ~/.ssh/authorized_ | 644 | Lists who can log | | keys | | in. Readable but | | | | not writable by | | | | others. | +--------------------+----------+--------------------+ | ~/.ssh/config | 600 | May contain | | | | hostnames, | | | | usernames, key | | | | paths. | +--------------------+----------+--------------------+
SSH will refuse to use a key that is too open. It is like a bank refusing to let you store cash in an unlocked box.
The chmod 777 anti-pattern
Let us be clear about chmod 777: this is leaving every door wide open, removing all locks, and putting a “come on in” sign on the lawn.
On a shared hosting server, chmod 777 means every other user on the system can read, modify, and execute your files. Your PHP scripts, your config files with database passwords, your upload directory — all of it, wide open.
It is almost never the right fix. When someone tells you to “just chmod 777 it,” what they really mean is “I do not understand the actual permission problem.” The real fix is almost always one of:
chownto the correct owner/group- Adding the web server user to the appropriate group
- Fixing the umask so new files get the right permissions
The fix for permission problems is almost always chown, not chmod 777.
Beyond the three-key model
The original nine-bit model is clean and fast, but real-world access control sometimes needs more nuance. Over the decades, several extensions have been layered on top.
POSIX ACLs — guest passes
The standard permission model has one limitation: you can only grant access to the owner, one group, and everyone else. What if you want to give one specific person read access without putting them in the group?
ACLs are like issuing individual guest passes with specific room access, instead of just “family” or “public.”
# Give alice read access to a specific file
setfacl -m u:alice:r-- secret-report.txt
# Give the 'auditors' group read+execute on a directory
setfacl -m g:auditors:r-x /opt/financials/
# View the ACLs on a file
getfacl secret-report.txt
# file: secret-report.txt
# owner: bob
# group: engineering
# user::rw-
# user:alice:r--
# group::r--
# mask::r--
# other::---A + sign at the end of the ls permission string tells you ACLs are present: -rw-r-----+.
Linux capabilities — splitting the master key
Traditionally, root holds one all-powerful key that opens every door. Linux capabilities split that master key into approximately 40 individual keys, each granting a specific privilege.
# Let a program bind to ports below 1024 without being root
setcap cap_net_bind_service=+ep /usr/local/bin/myserver
# Let a program create raw sockets (for ping, traceroute)
setcap cap_net_raw=+ep /usr/local/bin/mypingInstead of granting a program the entire root keyring, you hand it exactly the key it needs. CAP_NET_BIND_SERVICE lets it bind to privileged ports. CAP_NET_RAW lets it open raw sockets. Neither grants the ability to read other users’ files, modify system configuration, or load kernel modules.
Modern Linux is slowly replacing setuid with capabilities — giving programs exactly the key they need, nothing more.
SELinux and AppArmor — the security guard
Standard Unix permissions are like locks on doors: if you have the key, you get in. SELinux and AppArmor add a security guard who checks your ID badge even after you unlock the door.
Even if you have the right key, the guard checks your ID badge. A process might have the Unix permissions to read /etc/shadow, but if its SELinux label does not include shadow_t access, the guard turns it away.
- SELinux (Red Hat, Fedora, CentOS): label-based. Every file and process gets a security context label. Policy rules define which labels can access which other labels. Powerful but notoriously difficult to configure.
- AppArmor (Ubuntu, Debian, SUSE): path-based. Profiles define which file paths a program can access. Easier to write and debug than SELinux policies.
Both implement Mandatory Access Control (MAC) — the system enforces the policy regardless of what the file owner wants. Even root is subject to MAC policies (in enforcing mode).
How other houses work
Not every operating system uses the Unix permission model. Here is how different systems handle access control:
+---------+----------------------------------------------------+ | System | Permission Model | +---------+----------------------------------------------------+ | Linux | POSIX user/group/other + ACLs + Capabilities + | | | SELinux or AppArmor | +---------+----------------------------------------------------+ | macOS | POSIX + ACLs + SIP (System Integrity Protection) + | | | TCC (Transparency, Consent, Control) | +---------+----------------------------------------------------+ | FreeBSD | POSIX + chflags for immutable and append-only | | | attributes | +---------+----------------------------------------------------+ | Windows | NTFS ACLs — completely different model, per-user | | | and per-group entries on every object | +---------+----------------------------------------------------+ | Android | Linux UIDs (one per app) + SELinux + runtime | | | permission prompts | +---------+----------------------------------------------------+ | Docker | Linux permissions + user namespaces + rootless | | | mode for container isolation | +---------+----------------------------------------------------+
macOS layers SIP on top of POSIX — even root cannot modify system binaries. TCC controls app access to camera, microphone, and files through user consent dialogs.
Windows takes a fundamentally different approach: every file has a full ACL with explicit allow and deny entries for any user or group. There is no concept of a single “other” category. It is more flexible than POSIX permissions but significantly more complex to reason about.
Android uses Linux under the hood but assigns each app its own UID. Apps are sandboxed from each other at the kernel level. SELinux policies further restrict what each app process can do, and runtime permissions gate access to sensitive APIs like camera and location.
Docker containers share the host kernel. User namespace remapping can map the container’s root to an unprivileged host user, so even a container breakout does not grant real root access.
The key takeaways
The Unix permission model is built on a handful of principles:
chmodchanges locks (permissions).chowntransfers ownership. They solve different problems.- 644 for files, 755 for directories is the sane default. Start there.
- Never
chmod 777. Fix ownership instead. - Setuid is a powerful but dangerous tool. Prefer capabilities where possible.
- The special bits (setuid, setgid, sticky) exist because the base model needed escape hatches for specific real-world problems.
- ACLs, capabilities, and MAC systems (SELinux/AppArmor) extend the model when nine bits are not enough.
The Unix permission model is fifty years old and it works because it chose simplicity over completeness. Nine bits. Three digits. That is it.
“Unix was not designed to stop its users from doing stupid things, as that would also stop them from doing clever things.” — Doug Gwyn