CoursesLinux essentialsReading & setting permissions

Reading & setting permissions

rwx, octal, and chmod at a glance.

Beginner14 min · lesson 14 of 25

Every file and folder on a Linux machine comes with a little rule card clipped to it. The card answers one question: who is allowed to do what to this thing? Learn to read that card at a glance and a big chunk of how Linux keeps itself safe stops feeling like a mystery.

The card sorts everyone into three circles. First there is you, the owner, usually the account that created the file. Then there is the group, a named team of accounts that share access, like a team called devs where everyone carries the same key. Last there is others: every account on the machine that is neither you nor in that group. Security folks call that last circle the world. Each circle gets three powers: read (look at the contents), write (change the contents), and execute (run it as a program). Three circles, three powers, nine switches. That is the entire model.

Reading the nine switches

The command that shows you the card is ls -l (list, in long format). Point it at a file and the very first thing on the line is a block of ten characters.

~/secopslog — bash
$ ls -l script.sh
-rwxr-x--- 1 deploy devs 512 Jul 3 10:00 script.sh

That first character tells you the file type. A dash means an ordinary file. A d means a directory, the technical word for a folder. An l means a symlink (a shortcut that points at another file, like a signpost). After the type come the nine switches, in three groups of three, always in the same order: owner first, then group, then others, and inside each group always read, then write, then execute. A letter means the switch is on. A dash means off. So rwxr-x--- reads like this: the owner deploy can read, write, and run it; the group devs can read and run it but not change it; everyone else gets nothing at all. Those two words in the middle of the line, deploy and devs, name the owner and the group the card is talking about.

The nine switches of -rwxr-x---
Owner (deploy) = 7
read (4)
see the contents
write (2)
change the contents
execute (1)
run it
Group (devs) = 5
read (4)
see the contents
off (0)
cannot change it
execute (1)
run it
Others (world) = 0
off (0)
no read
off (0)
no write
off (0)
no run
Each group of three becomes one number: read is 4, write is 2, execute is 1, added up.

The same permissions as numbers

Typing rwxr-x--- over and over gets tiring, so Linux lets you write the same thing as a short number. This is octal (base 8, where each digit runs from 0 to 7). Here is the trick, and it is the kind of thing you can do in your head after a day of practice. Give each switch a point value: read is worth 4, write is worth 2, execute is worth 1. Add up the switches that are on in a group and you get one digit for that group. rwx is 4+2+1 = 7. rw- is 4+2 = 6. r-x is 4+1 = 5. r-- on its own is 4. So rwxr-x--- turns into 750, and rw-r----- turns into 640.

The command that sets those switches is chmod (change mode). Hand it three digits and a filename and watch the card flip:

~/secopslog — bash
$ ls -l notes.txt chmod 600 notes.txt ls -l notes.txt
-rw-rw-r-- 1 sam sam 1200 Jul 17 09:14 notes.txt -rw------- 1 sam sam 1200 Jul 17 09:14 notes.txt

Four number combinations cover most of what you do day to day. Here they are, with a plain note on each:

~/secopslog — bash
$ chmod 644 index.html # owner reads/writes; group + others read only chmod 755 deploy.sh # owner does everything; others read + run chmod 600 secrets.env # owner reads/writes; nobody else sees it chmod 700 ~/private # a folder only the owner can enter

Once the shape clicks, the rest follows: 7 is everything, 6 is read and write, 5 is read and run, 4 is read-only, 0 is locked shut. Now and then you will see a fourth digit tacked on the front, like 0644. That leading slot holds a set of special bits we will get to at the end. A plain 0 there means none of them are set, so 0644 and 644 describe the exact same file.

Changing one switch at a time

The number form rewrites all nine switches in one go. Sometimes that is too blunt. You want to flip a single switch and leave the other eight exactly as they were. That is what the symbolic form is for. You name a circle (u for the user, meaning the owner, g for group, o for others, or a for all of them), then say whether you are adding with +, removing with -, or setting exactly with =, and then the letters of the switches you mean.

~/secopslog — bash
$ ls -l backup.sh chmod u+x,go-w backup.sh ls -l backup.sh
-rw-rw-r-- 1 sam sam 340 Jul 17 10:00 backup.sh -rwxr--r-- 1 sam sam 340 Jul 17 10:00 backup.sh

Here u+x switched on the run bit for the owner, and go-w stripped the write bit off both the group and others. You can stack changes with a comma, the way this line did, and any switch you did not name is left untouched.

On folders, execute means something else

This is the spot where people trip. On a file, execute means run this as a program. On a folder it means something completely different: it is the key to the door. Execute on a directory is what lets you step through it, cd into it (cd is short for change directory), and reach the files inside by their names. Read on a directory is a separate power with its own job: it lets you see the list of names inside, like reading the labels on a row of drawers. Those two powers come apart. You can hold one without the other, and that catches people out the first time they hit it.

~/secopslog — bash
$ chmod 600 project # read on, execute off ls project cd project
data.csv report.txt bash: cd: project: Permission denied

You can read the labels, but the door will not open. Now flip it the other way, execute on and read off:

~/secopslog — bash
$ chmod 100 project # execute on, read off ls project cd project && cat report.txt
ls: cannot open directory 'project': Permission denied quarterly numbers are in row 40

Now the opposite happens. You can walk in and open a file whose name you already know, but you cannot pull a list of what is in there. Public web servers lean on exactly this split: execute on the folder so the server can walk to /var/www/html/index.html and serve it, no read on the folder so a stranger cannot ask for the full list of every file you host.

Deleting a file is the folder's call, not the file's
Whether you can delete a file is decided by the write bit on the folder it lives in, not by the file's own permissions. Anyone who can write to a directory can remove files inside it, including files they do not own and cannot even open. That is why a shared directory that is world-writable (one any account on the machine can write into) is dangerous, and why /tmp carries a special guard called the sticky bit, which changes the rule so that inside that folder you can only delete files you own.

Where new files get their permissions

You never sit and type out the permissions for every single file you make, so where do the defaults come from? From a setting called the umask (short for user file-creation mask). Think of it as a stencil laid over every new file: it blocks certain switches from ever being on to start with. The everyday umask is 022, which blocks the write bit for group and others. That is the reason a brand-new file shows up at 644 and a brand-new folder at 755.

~/secopslog — bash
$ umask touch fresh.txt && ls -l fresh.txt
0022 -rw-r--r-- 1 sam sam 0 Jul 17 11:02 fresh.txt

If your work touches secrets, a tighter umask of 077 makes every new file owner-only from the second it is created. Then you are not leaning on your own memory to lock each one down after the fact, which is the kind of step people forget on a busy day.

What goes wrong, and what to check

Two permission mistakes cause real incidents, and the good news is that both are easy to scan for. The first is a world-writable file, meaning the write bit is on for others, so any account on the box can rewrite it. Picture a world-writable script that runs as root on a schedule. An attacker edits the file, waits for the clock, and their code runs with root's power. You can hunt for these across the whole disk:

~/secopslog — bash
$ sudo find / -xdev -type f -perm -0002 -not -path '/proc/*' 2>/dev/null
/opt/app/run.sh /srv/uploads/tmp.dat

The second mistake is the mirror image: a world-readable secret. A database password sitting in a config file that every account on the machine can read is a finding, even when nobody can write to it. Reading it is enough. Set anything sensitive to 600 (owner only) or 640 (owner plus one trusted group), and never leave a credentials file at 644.

SSH (secure shell, the standard tool for logging into another machine over the network) agrees with you so firmly that it refuses to run when you get this wrong. Leave a private key readable by others and SSH will not even attempt to use it:

~/secopslog — bash
$ ls -l ~/.ssh/id_ed25519 ssh deploy@build-01
-rw-r--r-- 1 sam sam 411 Jul 10 08:00 /home/sam/.ssh/id_ed25519 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: UNPROTECTED PRIVATE KEY FILE! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Permissions 0644 for '/home/sam/.ssh/id_ed25519' are too open. It is required that your private key files are NOT accessible by others. This private key will be ignored. Load key "/home/sam/.ssh/id_ed25519": bad permissions deploy@build-01: Permission denied (publickey).

The fix is a single command, chmod 600 ~/.ssh/id_ed25519, and the login goes through again.

Remember that fourth octal digit from earlier? One of the bits it holds is setuid (set user ID on run). A setuid program runs with the identity of its owner instead of yours, a bit like a staff keycard that borrows the manager's access for one specific door. That is how an ordinary user is able to change their own password: the passwd program is owned by root and carries the setuid bit, so for that one narrow job it runs with root's power. Useful, and also a favorite target for attackers. A setuid-root program that you can write to, or one with a bug in it, can hand over the whole machine. So it pays to know what is sitting on your box:

~/secopslog — bash
$ find / -xdev -perm -4000 -type f 2>/dev/null
/usr/bin/su /usr/bin/sudo /usr/bin/passwd /usr/bin/chsh /usr/bin/chfn /usr/bin/mount /usr/bin/umount

That list should be short and boring, full of names you recognize from the system's own toolbox. A setuid binary that turned up in someone's home directory, or in /tmp, is exactly the kind of thing worth stopping and asking hard questions about.

Quick check
01You can see a folder in ls and even run ls on it to read the names inside, but cd into it fails with Permission denied. Which switch is missing for you?
Incorrect — No. Read is exactly what lets you list the names, and that is working fine.
Correct — Execute is the key that lets you step into a directory; without it, cd is denied even when you can read the names.
Incorrect — No. Write controls creating and deleting entries inside the folder, not walking into it.
Incorrect — No. A file's own bits do not decide whether you can enter the folder that holds it.
02On Linux, what actually decides whether you are allowed to delete a file?
Incorrect — a file's own write bit controls changing its contents, not removing the file.
Incorrect — ownership is not required; anyone who can write the directory can delete files in it, even ones they do not own or cannot open.
Correct — deletion is a change to the directory's list of entries, so it is governed by write permission on the folder, not the file.
Incorrect — execute controls running the file as a program and has nothing to do with deletion.
03You run find / -xdev -perm -4000 -type f and, alongside the usual /usr/bin/sudo and /usr/bin/passwd, you spot /tmp/.x owned by root. Why should that stop you?
Incorrect — the setuid bit works on any executable regardless of location; where it sits does not disable it.
Incorrect — setuid is the 4000 bit, a different thing from the world-write bit (0002); the find command was searching for setuid, not world-write.
Incorrect — the concern is not who made it but that the setuid bit hands root's power to anyone who runs it.
Correct — setuid (set user ID) makes a program run as its owner, here root; an unfamiliar setuid-root binary in /tmp is exactly the kind of thing to investigate.

Try this

Work through “What goes wrong, and what to check” yourself on a sandbox you can throw away, following the commands above in order. Then break one step deliberately and re-run, so you have seen the failure before it finds you.

Takeaway

The trap worth remembering here: deleting a file is the folder's call, not the file's. Check that on your own systems before you need to, because it is cheaper to find on a quiet afternoon than during an incident.

Related