What File Permissions Are and Why They Matter
Linux is a multi-user operating system: many accounts, plus background services, can share one machine. Every file and directory carries a set of permissions that decide who is allowed to read it, change it, or run it. These permissions are the first and most fundamental layer of access control on a Linux host. In cybersecurity this matters enormously. A huge share of real Linux incidents — data exposure, tampering, and privilege escalation — trace back to a permission that was set too loosely: a config file the whole system could read, a private SSH key readable by everyone, or a script directory anyone could write to and replace. Learning to read and set permissions correctly is a core defensive skill, not a formality.
The core idea: least privilege
Grant the narrowest access that still lets the work happen, and nothing more. Most permission-based vulnerabilities are the result of someone opening access wider than the task actually required.
The rwx Model: Three Permissions, Three Classes
Every file and directory has three permission bits — r (read), w (write), and x (execute) — and those three bits are defined separately for three classes of user: the owner, the file's group, and others (everyone else). That gives nine bits in total: rwx for owner, rwx for group, rwx for other. The catch students always trip on: r, w, and x mean different things on a directory than on a file.
The three classes (who the permission applies to):
| Symbol | Who it is | Position in ls -l |
|---|---|---|
| u | User — the single account that owns the file | chars 2-4 |
| g | Group — members of the file's assigned group | chars 5-7 |
| o | Other — every other account on the system | chars 8-10 |
| a | All — u, g, and o at once (a shortcut) | — |
The three permissions, and how each behaves on a file vs. a directory:
| Symbol | Meaning on a file | Meaning on a directory |
|---|---|---|
| r | Read the contents (e.g. cat, less) | List the names inside the directory |
| w | Modify or overwrite the contents | Create, rename, or delete entries inside |
| x | Run the file as a program/script | Change into it and reach the items inside |
Why a directory needs x
To open a file inside a folder you must have execute (x) on the folder itself — x is the permission to "pass through" a directory. That is why private directories use 700 (rwx), not 600: without x, even the owner cannot enter it.
Reading ls -l Output
The ls -l command prints a long listing. Its first field is a 10-character permission string. The first character is the file type; the next nine are the three rwx triplets for owner, group, and other. A dash (-) in any slot means that permission is absent.
| Characters | Example | Meaning |
|---|---|---|
| 1 | `-` | File type: `-` regular file, `d` directory, `l` symlink |
| 2-4 | `rwx` | Owner may read, write, and execute |
| 5-7 | `r-x` | Group may read and execute, but not write |
| 8-10 | `r--` | Other may only read |
After the permission string, the remaining fields are: link count, owner, group, size in bytes, last-modified date, and finally the name. Owner and group are exactly the two names that the owner and group triplets apply to.
chmod — Symbolic Form (u/g/o with + - =)
The chmod (change mode) command sets permissions. Its symbolic form is built from three parts: who (u owner, g group, o other, a all), an operator (+ add, - remove, = set exactly), and the permissions (r, w, x).
+ and - are relative — they add or remove a bit and leave the rest alone. = is absolute — it sets that class to exactly what you list and clears anything else.
Symbolic chmod, with concrete examples:
| Command | What it does | Example |
|---|---|---|
| chmod MODE FILE | CHange MODe - set a file's permission bits; the symbolic form names WHO (u owner, g group, o other, a all), an operator (+ add, - remove, = set exactly), and the bits (r, w, x) | chmod u+x deploy.sh |
| chmod u+x FILE | u+x = user, add execute - add execute permission for the owner | chmod u+x deploy.sh |
| chmod g-w FILE | g-w = group, remove write - remove write permission from the group | chmod g-w report.csv |
| chmod o=r FILE | o=r = other, set exactly read - set other to read-only, clearing its w and x | chmod o=r index.html |
| chmod a+r FILE | a+r = all, add read - add read for every class at once (a = u, g, and o) | chmod a+r README.md |
| chmod go-rwx FILE | go-rwx = group + other, remove read/write/execute - strip all access from everyone but the owner (fast lock-down for a secret) | chmod go-rwx id_rsa |
| chmod u+x,g+x FILE | comma = combine changes - apply several class changes in one call | chmod u+x,g+x build.sh |
| chmod -R g+rX DIR | -R = recursive, capital X = execute for directories and already-executable files only - recurse a whole tree without making every plain file runnable | chmod -R g+rX project/ |
Capital X vs. lowercase x
Lowercase x sets execute on everything, including plain text files (rarely what you want). Capital X adds execute only to directories and files that already have an execute bit — perfect for fixing a whole project tree without making every .txt "runnable."
chmod — Octal (Numeric) Form
The octal form describes all nine bits with three digits — one per class, in the order owner / group / other. Each digit (0-7) is the sum of the permissions you want, where read = 4, write = 2, execute = 1. Add them up per class and you get the digit.
Every octal digit and the rwx bits it represents:
| Octal digit | What it allows | rwx |
|---|---|---|
| 0 | No permissions | --- |
| 1 | Execute only | --x |
| 2 | Write only | -w- |
| 3 | Write + execute | -wx |
| 4 | Read only | r-- |
| 5 | Read + execute | r-x |
| 6 | Read + write | rw- |
| 7 | Read + write + execute | rwx |
Doing octal in your head
read = 4, write = 2, execute = 1. Add the ones you want for each class. So rw- is 4+2 = 6, r-x is 4+1 = 5, and rwx is 4+2+1 = 7. That makes rw-r--r-- into 6-4-4 = 644.
Common modes you will use constantly:
| Mode | Typical use | Permissions |
|---|---|---|
| 644 | Normal files: owner edits, everyone reads | rw-r--r-- |
| 755 | Scripts, programs, and directories anyone may run or enter | rwxr-xr-x |
| 700 | A private directory only the owner may use | rwx------ |
| 600 | Secrets: SSH keys, credential and config files | rw------- |
| 640 | Owner writes, group reads, other locked out | rw-r----- |
| 777 | Everyone can do anything — almost always a mistake | rwxrwxrwx |
chown and chgrp — Changing Ownership
Permissions decide what each class may do; ownership decides who those classes are. chown changes a file's owner (and optionally its group), and chgrp changes just the group. Changing the owner normally requires root (via sudo), because otherwise anyone could hand off files to escape accountability. A regular user may change a file's group to any group they belong to.
Changing owner and group:
| Command | What it does | Example |
|---|---|---|
| chown alice FILE | CHange OWNer - change the file's owner to alice (needs root) | sudo chown alice app.log |
| chown alice:devs FILE | CHange OWNer, owner:group form - set the owner and the group in one step | sudo chown alice:devs app.log |
| chown :devs FILE | CHange OWNer, leading colon = group only - change just the group; no root needed for a group you belong to | chown :devs app.log |
| chgrp devs FILE | CHange GRouP - change the group to one you belong to | chgrp devs shared.txt |
| chown -R alice DIR | CHange OWNer, -R = recursive - descend into a directory and change ownership of everything inside it | sudo chown -R alice /srv/site |
| id | id (identity) - print your user, groups, and numeric UIDs/GIDs so you know what you are allowed to set | id |
Recursive chown can lock you out
A mistyped sudo chown -R on the wrong directory (or on /) can change ownership of system files and break the machine. Always double-check the path before running a recursive ownership change.
Why Permissions Matter for Security
Permissions are the frontline access control on a Linux host, and a single over-permissive file can quietly undo every other protection. A few lessons that come up again and again in the field:
Secrets stay at 600. OpenSSH will refuse to use a private key such as ~/.ssh/id_rsa if group or other can read it, printing a "permissions are too open" error and failing the connection. The same 600 rule applies to API keys and credential files.
World-writable is dangerous. If a script or its directory is writable by other, any account on the box can replace that script with malicious code that later runs with the owner's privileges. This is a classic path to privilege escalation.
setuid programs are high-value targets. A setuid-root binary runs with root's power no matter who launches it, so a bug in one can hand an attacker full control. Defenders inventory them with find / -perm -4000 -type f 2>/dev/null and keep the list short.
Special permission bits you should recognize:
| Bit | Effect | Octal |
|---|---|---|
| u+s | setuid: the program runs with the file owner's privileges instead of the caller's — a classic privilege-escalation target when the owner is root | 4000 |
| g+s | setgid: runs with the file's group; on a directory, newly created files inherit that directory's group | 2000 |
| +t | sticky bit: in a shared, writable directory only a file's own owner may delete it (this is how /tmp is protected) | 1000 |
Never chmod -R 777
Recursively making a whole tree world-writable and world-executable is one of the most common self-inflicted Linux vulnerabilities. If something "won't work," fix the specific owner or bit it actually needs — do not open everything to everyone as a shortcut.
Where default permissions come from
New files don't come out wide-open. Programs create ordinary files from a base of 666 and directories from 777, and your shell's umask then clears bits from that base before the file is made — which is also why new files never get an execute bit by default. The common default umask 022 strips write from group and other, producing 644 for new files and 755 for new directories. Run umask to see the value in effect for your session.
Try it yourself
Work through these on a Linux shell (a VM or a class sandbox) to make the rwx model stick:
1. Run touch practice.txt (touch makes an empty file) then ls -l practice.txt. Read off the rwx string and convert it to octal. What did your umask give the file, and does it match the 644 default?
2. Run chmod 640 practice.txt. Before you run ls -l again, write down the exact rwx string you expect (owner, group, other), then check yourself.
3. Run mkdir vault (mkdir makes a new folder) and chmod 700 vault. In one sentence, explain why a private directory needs 700 and not 600 — which permission lets the owner actually enter it, and what breaks without it?
ls-l-listing.txtDownload ls-l-listing.txt: https://restedteacher.com/practice-files/cybersecurity/ls-l-listing.txt
A captured ls -l long listing from a home directory, so you can practice reading permission strings without needing your own Linux box.
Get the file into a labs folder
Move the download into a labs folder and go there, using commands from the [Terminal Basics](/reference/cybersecurity/terminal-basics) sheet:
mkdir -p ~/labs/perms && mv ~/Downloads/ls-l-listing.txt ~/labs/perms/ && cd ~/labs/perms
Then run cat ls-l-listing.txt to see the whole listing at once.
Using ls-l-listing.txt, work out each answer by reading the permission string yourself (or check it with grep):
1. Exactly one file in the listing has the setuid bit set (an s in the owner's execute slot, position 4 of the permission string). Which file is it?
2. Exactly one file has the setgid bit set instead (an s in the group's execute slot, position 7). Which file is it?
3. Convert shared-notes.txt's permission string to a three-digit octal mode.
4. Of id_rsa, config.yaml, and shared-notes.txt, only one is at mode 600 or stricter — the only one OpenSSH would actually accept as a private key without refusing it as "too open." Which file is it?