You need a Linux machine where you can use sudo, and
you should not practise on a server anyone depends on. The commands
here change real system state. A spare machine, a virtual machine, or a
cloud instance you can rebuild is the right place; a laptop you own is
fine too. If the terminal itself is new to you, do
Introduction to the
Linux Terminal first — it takes about ten minutes and everything
below assumes it. Every step tells you how to undo it.
What This Builds On
This tutorial assumes you already understand ls -l output, what
r, w and x mean, and basic chmod. If any
of that is unfamiliar, read the introductory permissions tutorial in the Terminal section
first — everything here builds directly on it.
What follows is the part that causes real incidents: directory semantics, the special bits, the mask that silently decides every new file's permissions, and access control lists.
Directories Do Not Mean What You Think
The same three letters mean something completely different on a directory, and this is the single most common source of confusion.
ron a directory — you may list the names inside it. That is allxon a directory — you may traverse it and access things inside if you already know their names. Withoutx, nothing below is reachable regardless of the permissions on those fileswon a directory — you may create and delete entries in it. Deleting a file is a write to its directory, not to the file. This is why someone can delete a read-only file they do not own
r-x lets someone browse. --x lets them pass through to a
known path without being able to list what else is there — genuinely useful for a home
directory that must be traversable but not enumerable.
Ownership
sudo chown alice file.txt # change owner
sudo chown alice:developers file.txt # owner and group
sudo chgrp developers file.txt # group only
sudo chown -R alice:alice /srv/app # recurse
Use -R deliberately. Recursive ownership changes on a system directory are a
well-worn way to break a machine — and there is no undo.
The Special Bits
Three extra bits sit above the usual nine, written as a fourth leading digit.
- setuid (4) — an executable runs as its owner, not as the user
who ran it. This is how
passwdlets you edit a root-owned file. It appears assin the owner's execute position - setgid (2) — on an executable, runs with the group's privileges. On a directory, far more usefully, new files inside inherit the directory's group instead of the creator's
- sticky bit (1) — on a directory, only the owner of a file may delete it,
even if others can write to the directory. This is what makes
/tmpsafe
chmod g+s /srv/shared # setgid: shared group ownership
chmod +t /srv/dropbox # sticky: users can only delete their own
ls -ld /tmp # drwxrwxrwt — note the trailing t
A shared team directory that actually works:
sudo mkdir /srv/shared
sudo chgrp developers /srv/shared
sudo chmod 2770 /srv/shared # rwx for owner+group, setgid, nothing for others
A setuid-root binary with a bug hands an attacker root. Never set it on scripts or on anything you compiled casually. Audit what already has it:
sudo find / -perm -4000 -type f 2>/dev/null
Expect a short, familiar list — sudo, passwd,
su, mount. Anything unexpected, especially in
/tmp or a home directory, deserves immediate investigation.
umask: What Decides New Files
You never choose the permissions of a file you create — the umask does, by subtracting from
a base of 666 for files and 777 for directories.
umask # typically 0022
umask 0077 # new files 600, new dirs 700 — private by default
- 022 (common default) → files
644, directories755— everyone on the system can read your new files - 027 → files
640, directories750— group reads, others get nothing. A sensible default for a shared server - 077 → files
600, directories700— fully private
Setting umask in a shell affects that session only. For a persistent change, set
it in the user's shell profile, or system-wide via /etc/login.defs and PAM.
ACLs: When Owner/Group/Other Is Not Enough
The classic model has exactly three subjects. When you need "this one extra person gets read access" without creating a group, use an access control list.
getfacl report.txt # view
setfacl -m u:alice:rw report.txt # grant alice read+write
setfacl -m g:auditors:r report.txt # grant a group read
setfacl -x u:alice report.txt # remove alice's entry
setfacl -b report.txt # strip all ACLs
A file with ACLs shows a + at the end of its ls -l mode string.
That marker is easy to miss, and it means ls -l alone no longer tells you who
has access — always check getfacl when auditing.
Default ACLs make new files in a directory inherit a rule:
setfacl -d -m g:developers:rwx /srv/project
getfacl shows a mask:: line that caps every named user and
group entry. If an ACL "is not working", the mask is usually why — a
chmod on the file can silently tighten it.
Auditing Permissions
find /srv -perm -o+w -type f 2>/dev/null # world-writable files
find /srv -nouser -o -nogroup 2>/dev/null # orphaned by a deleted account
find /home -name '.ssh' -perm /go+rwx # SSH dirs others can reach
find / -perm -2000 -type f 2>/dev/null # setgid binaries
World-writable files are the classic finding: anyone on the system can modify them, which for a script or config file means anyone can change what runs.
Common Mistakes
chmod 777— never the right answer. It is what people reach for when a permission problem is not understood, and it grants every user on the machine write access- Recursive chmod on mixed content —
chmod -R 644stripsxfrom directories and makes the whole tree unreachable. Usefind … -type d -exec chmod 755and-type f -exec chmod 644separately - Forgetting the parent directory — a perfectly permissioned file inside a directory you cannot traverse is still unreachable
- Loose SSH permissions —
~/.sshmust be700and private keys600, or OpenSSH refuses to use them. That refusal is a feature, not a bug
Now Do It Yourself: Five Steps
Beyond read, write and execute there are three bits that decide who a program runs as and who may delete what. They are behind a large share of real Linux privilege problems. You will see each one, on files you create. Every output below is exactly what the system printed.
Go: open a terminal, run cd, then mkdir -p practice and cd practice.
Do: run touch report.txt, then chown root report.txt with no sudo.
You should see: chown: changing ownership of 'report.txt': Operation not permitted. You own the file, yet you cannot hand it to someone else. Only root can, and that is deliberate: if you could give a file away, you could plant a file in another user's name and make them accountable for it.
If not: if it succeeds, you are running as root already — check with whoami. Note the asymmetry worth remembering: chmod needs only ownership, while chown needs root. That is why you can lock your own files down without asking anyone.
Go: same terminal. This step only looks; it changes nothing.
Do: run ls -l /usr/bin/passwd, then find /usr/bin -maxdepth 1 -perm -4000.
You should see: a mode of -rwsr-xr-x on passwd — note the s where the owner's x would be — and then a short list of similar programs, typically including newgrp and passwd. That s is setuid: the program runs as its owner, root, no matter who launches it. It is how you can change your own password even though the password file is root-only.
If not: an empty list is fine on a hardened or containerised system — some deliberately remove every setuid binary. The reason this matters: a setuid-root program with a bug hands root to whoever exploits it, which is why that find command is one of the first things a security audit runs.
Go: same folder.
Do: run these four commands.
touch tool
chmod 4755 tool
stat -c '%A %a' tool
chmod 755 tool
You should see: -rwsr-xr-x 4755, then the bit removed again. The fourth digit is the special one: 4 setuid, 2 setgid, 1 sticky. Everything you already know is still there — 755 is unchanged underneath.
If not: if you see -rwSr-xr-x with a capital S, the execute bit is missing underneath. A capital letter means the special bit is set but the underlying execute bit is not, so it does nothing — a lowercase s is active, an uppercase S is inert. That distinction is easy to miss and is worth reading carefully every time.
Go: same terminal.
Do: run stat -c '%A %a %n' /tmp, then make your own: mkdir -p shared && chmod 1777 shared && stat -c '%A %a' shared.
You should see: drwxrwxrwt 1777 /tmp, and then the identical mode on your own folder. The final t is the sticky bit. 777 would normally mean anyone can delete anything in there; sticky restricts deletion to the owner of each file. That is the only reason a world-writable /tmp is not chaos.
If not: if /tmp shows something else on your system, it may be a private per-service temporary directory — systemd does that deliberately. The lesson stands: whenever you find yourself typing 777 on a directory, the answer you actually want is almost always 1777, or better, narrower permissions and a group.
Go: same folder.
Do: run chmod 2775 shared, then stat -c '%A %a' shared.
You should see: drwxrwsr-x 2775 — an s in the group position this time. On a directory, setgid means every file created inside inherits the folder's group rather than the creator's. That is the standard fix for a team share where one person's files keep coming out unreadable to everyone else.
If not: if new files still get the wrong group, remember setgid only affects files created after it was set. Fix the existing ones separately with chgrp -R teamname shared. And check your umask — umask 077 produces -rw-------, which no group setting can override, because the group has no permission to inherit in the first place.
Without scrolling up: you see -rwSr--r-- on a file. Is the setuid bit
doing anything? Answer: no. The capital S means setuid is set but the
owner's execute bit is not, so there is nothing to run and the bit is inert. Lowercase
s is active; uppercase is a warning sign that something was set
incompletely.
Now do it without the page: survey your own machine for setuid
programs outside /usr/bin — search /usr/sbin and
/usr/local/bin the same way as step 2 — and for each one you do not
recognise, look up what it is before assuming it belongs there. That survey is a real
audit task, not an exercise.
Summary
- Directory permissions differ from file permissions —
won a directory means delete - setgid on a directory is the clean way to run shared team folders
- The sticky bit stops users deleting each other's files in shared spaces
- setuid is dangerous — audit it with
find / -perm -4000 - umask decides every new file —
027or077beats the usual022on a shared machine - ACLs add precision, and a
+inls -lmeans you must checkgetfacl
sudo find / -perm -4000 -type f 2>/dev/null to see every setuid binary
on your system, and umask to see what your next file will be created as.
Both answers are usually more interesting than expected.