COMP 3150Operating Systems

01 Introduction

Every file on a Linux system carries a small piece of information that determines who can do what to it. This is the permission mode — a compact encoding of nine permissions that together answer three questions for every file: can the file's owner read it, write it, execute it? Can members of the file's group do so? Can everyone else?

The model is simple enough to fit on one line of ls -l output, yet it governs an enormous range of behaviour. Whether a server can bind to a privileged port, whether a user can modify a configuration file, whether a malicious script can overwrite a system binary, whether a container can access a mounted volume — every one of these decisions is ultimately a permission check.

This lesson covers the permission model in detail: the three permission types, the three categories of user, the numeric and symbolic notations, the tools for inspecting and modifying permissions, the umask mechanism, the special permission bits (setuid, setgid, sticky), and the practical discipline of least privilege that ties them together. It builds directly on the user and group model from Lesson 2.3.

02 Learning objectives

By the end of this lesson you will be able to

  • Interpret permissions.
  • Modify permissions.
  • Explain ownership.
  • Analyse security implications.
  • Apply least privilege.

This lesson contributes to U2.4 and to course learning outcomes CLO6 and CLO7.

03 Required reading

  • Stevens & Rago — Advanced Programming in the UNIX Environment, 3rd ed., Sections 4.4–4.7 (file access permissions, ownership, access testing). The classical presentation of the model, including the access() function and the semantics the kernel applies.
  • Linux man pages: man 1 chmod, man 1 chown, man 1 umask, man 2 chmod, man 7 acl, man 1 getfacl, man 1 setfacl. The definitive reference for permission syntax and behaviour.
  • Nemeth, E., Snyder, G., Whaley, B., & Mackin, D. — UNIX and Linux System Administration Handbook, 5th ed., Chapter 8 (access control). Practical treatment of permissions, ACLs, and the special bits.

Reading strategy

Read Stevens & Rago Sections 4.4–4.7 first for the model, then consult the man pages for the specific commands. The chmod man page in particular is worth reading carefully — it documents both the numeric and symbolic notations and the special permission bits, with more precision than most tutorials.

04 Core content

4.1 The three permissions

For every file and directory, the system tracks three permissions:

Read (r)
For a file: the contents may be read. Any program that opens the file for reading will succeed. For example, cat file.txt works.
For a directory: the list of names may be read. You can see which files are in the directory, though you may not be able to access them individually. This is what ls does.
Write (w)
For a file: the contents may be modified. Whether the file's length changes or not, writing to any part of it requires write permission.
For a directory: entries may be created, renamed, or deleted within the directory. Note that changing a file's contents requires write permission on the file itself, not on its directory; changing the file's name or existence requires write permission on the directory.
Execute (x)
For a file: the file may be run as a program. This applies to binaries and scripts. A file without execute permission cannot be run directly (though it can be passed to an interpreter, e.g., bash script.sh).
For a directory: the directory may be traversed — that is, it may be used as a component in a path. Without execute permission on a directory, you cannot access files inside it even if you know their names, because the kernel cannot resolve the path.

Directory permissions are not intuitive

The r/w/x meanings on directories are different from their meanings on files, and the difference trips up many users. The most common surprises: (1) a directory with read but not execute permission lets you list the names in it but not access the files; (2) a directory with execute but not read permission lets you access files whose names you already know, but not discover new names; (3) deleting a file requires write permission on the directory, not on the file.

4.2 The three categories of user

The permissions are tracked separately for three categories of principal:

User (u)
The file's owner — the UID recorded in the file's inode. This is usually the user who created the file, but ownership can be transferred (with appropriate privileges).
Group (g)
Members of the file's group — the GID recorded in the file's inode. A user is in the group if the file's GID matches their primary GID or any of their supplementary GIDs.
Others (o)
Everyone else — any user who is neither the owner nor a member of the file's group. Sometimes called "world".

Three permissions times three categories gives nine permission bits, which is why the mode fits so neatly in the output of ls -l.

The UNIX permission mode A diagram showing the twelve bits that make up a file mode: three special bits (setuid, setgid, sticky), followed by nine permission bits grouped into three triples — user, group, and others. Below, the octal representation and the ls -l rendering of the same bits. THE UNIX PERMISSION MODE · 12 BITS SUID bit 11 SGID bit 10 STICKY bit 9 USER bits 8 7 6 GROUP bits 5 4 3 OTHERS bits 2 1 0 — — — r w x r w x r w x OCTAL NOTATION 0 6 4 0 0 special (unused) user group others ls -l RENDERING - rw- r-- --- 1 alice developers 4096 Oct 15 11:00 notes.txt │ │ │ │ │ │ │ │ file type · owner · group · others · links · owner · group · size Reading the mode rw-r----- Owner (alice) can read and write; group (developers) can read; others have no access. Octal: 640. The first digit (0) covers the three special bits, all clear here.
Figure 2.4 — The twelve bits of a file mode. The top three are the special bits; the remaining nine are grouped into three triples for owner, group, and others.

4.3 Reading permissions with ls -l

The ls -l command prints a long-format listing that begins with the file's mode. For example:

$ ls -l notes.txt
-rw-r--r-- 1 alice developers 4096 Oct 15 11:00 notes.txt

Breaking the first field -rw-r--r-- apart:

  • First character: file type. - regular file, d directory, l symlink, c character device, b block device, p FIFO (named pipe), s socket.
  • Characters 2–4: owner permissions. rw- means the owner (alice) can read and write but not execute.
  • Characters 5–7: group permissions. r-- means members of the group (developers) can read but not write or execute.
  • Characters 8–10: others permissions. r-- means everyone else can read but not write or execute.

4.4 Numeric (octal) notation

Each permission is assigned a value: r = 4, w = 2, x = 1. These powers of two allow the three permissions for one category of user to be encoded in a single octal digit (0–7) by summing the values:

Octal values for permission combinations
Octal Binary Symbolic Meaning
0000---no permissions
1001--xexecute only
2010-w-write only
3011-wxwrite and execute
4100r--read only
5101r-xread and execute
6110rw-read and write
7111rwxread, write, and execute

A complete mode is three octal digits: owner, group, others. For example:

  • 644 — owner read/write, group read, others read. The default for most files. Verbose form: rw-r--r--.
  • 600 — owner read/write, no one else anything. Typical for private files like SSH keys. Verbose form: rw-------.
  • 755 — owner read/write/execute, group and others read/execute. Typical for executables and public directories. Verbose form: rwxr-xr-x.
  • 700 — owner full, nobody else anything. Typical for private directories. Verbose form: rwx------.
  • 777 — everyone full. Very permissive; almost never appropriate on a multi-user system.

4.5 Changing permissions with chmod

chmod changes a file's mode. It accepts two notations: symbolic and octal.

Symbolic notation

chmod [ugoa][+-=][rwxXst] file...
  • Who: u (user/owner), g (group), o (others), a (all three). If omitted, a is assumed, modified by the umask (see §4.6).
  • Operation: + (add), - (remove), = (set exactly, clearing the other bits).
  • Permissions: r, w, x. Additional letters for the special bits: s (setuid/setgid), t (sticky). The uppercase X means "execute only if the file is a directory or already has execute for someone".
# Add execute for the owner
$ chmod u+x script.sh

# Add read for the group and remove write for others
$ chmod g+r,o-w file.txt

# Set exactly rw- for the owner, r-- for group and others
$ chmod u=rw,go=r file.txt

# Grant the whole world read/execute on a directory
$ chmod a+rx public/

# Remove all permissions for the group and others
$ chmod go= secret.txt

Octal notation

# Set owner rwx, group rx, others rx
$ chmod 755 script.sh

# Set owner rw only
$ chmod 600 private.key

# Set owner rwx, group rwx, others ---
$ chmod 770 shared/

Octal notation sets the mode exactly: the new mode is precisely the three digits specified, and any bits not mentioned are cleared. Symbolic notation is relative (add or remove), except for =, which sets exactly.

Recursive changes

# Change every file and subdirectory in a tree
$ chmod -R u+rwX,go-rwx project/

The -R flag applies the change recursively. The uppercase X in the permissions list is a useful refinement: it grants execute permission only to files that already have execute for someone, or to directories. This avoids accidentally making every text file executable when applying a recursive change.

4.6 The umask

When you create a new file or directory, the kernel assigns it a default mode. The exact bits depend on two things: a base mode that is compiled into the system (666 for files, 777 for directories) and the process's umask, a value that is subtracted (via bitwise AND with the complement) from the base mode.

# Show the current umask
$ umask
0022

# Show the umask in symbolic form
$ umask -S
u=rwx,g=rx,o=rx

With a umask of 022:

  • New files are created with mode 666 & ~022 = 644 (rw-r--r--).
  • New directories are created with mode 777 & ~022 = 755 (rwxr-xr-x).

A more restrictive umask of 077:

  • New files: 666 & ~077 = 600 (rw-------).
  • New directories: 777 & ~077 = 700 (rwx------).

The umask is set per process and inherited by child processes. You typically set it in your shell's startup file (~/.bashrc or ~/.profile) or system-wide in /etc/profile or /etc/login.defs.

Common umask values

022 — the default on most systems. Files are readable by everyone, writable only by the owner. 027 — files readable by the owner's group only, used on servers where coworkers may share data. 077 — files readable only by the owner, used on multi-user systems for users who want strict privacy. In hardened environments, a umask of 027 or 077 is often the standard.

4.7 Ownership

Every file has an owner (a UID) and a group (a GID). Both are independent of the permission bits: the permission bits specify what the owner and group may do, and the ownership fields say who the owner and group are.

Changing ownership

# Change the owner
$ sudo chown alice file.txt

# Change the owner and group together
$ sudo chown alice:developers file.txt

# Change only the group
$ sudo chgrp developers file.txt

# Or, using chown with a colon but no owner
$ sudo chown :developers file.txt

# Change recursively
$ sudo chown -R alice:developers project/

Changing a file's owner requires root privileges (or CAP_CHOWN). Changing the group is permitted if you own the file and are a member of the target group; otherwise it also requires root.

chown clears setuid and setgid

When you change the owner of a file that has the setuid or setgid bit set, the kernel clears those bits. This is a security measure: without it, an attacker who could change ownership could create a setuid-root binary from an ordinary one. You will need to re-set the setuid bit explicitly if you actually want it (and usually you should not).

4.8 The three special permission bits

In addition to the nine ordinary permission bits, three special bits modify a file's behaviour.

setuid (SUID)

When set on an executable, the program runs with the effective UID of the file's owner instead of the calling user. Displayed as an s in the owner's execute slot (rwsr-xr-x) or an S if execute is also clear.

$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 ... /usr/bin/passwd

The passwd program needs to write to /etc/shadow, which is writable only by root. The setuid bit lets any user run passwd and have it temporarily assume root's privileges to perform the update.

setgid (SGID)

On an executable, setgid makes the program run with the effective GID of the file's group. On a directory, setgid causes new files and subdirectories to inherit the directory's group. Displayed as an s in the group's execute slot (rwxrwsr-x).

# setgid on a directory: new files inherit the group
$ ls -ld /srv/projectx
drwxrwsr-x 2 root projectx 4096 ... /srv/projectx

The setgid bit on a directory was covered in Lesson 2.3 — it is the mechanism that makes shared project directories work. Every file created inside inherits the directory's group, so access can be governed by group membership.

sticky bit

On a directory, the sticky bit restricts deletion: a file inside the directory can only be deleted or renamed by its owner, the directory's owner, or root. Displayed as a t in the others' execute slot (rwxrwxrwt).

$ ls -ld /tmp
drwxrwxrwt 15 root root 4096 ... /tmp

The /tmp directory is the classic example. It is world-writable, so any user can create a temporary file there, but the sticky bit ensures that user A cannot delete user B's files. Without the sticky bit, world-writable directories would allow any user to destroy any other user's data.

Setting the special bits

# Symbolic
$ chmod u+s program           # setuid
$ chmod g+s directory         # setgid
$ chmod +t directory          # sticky

# Octal (the leading digit)
$ chmod 4755 program          # setuid + rwxr-xr-x
$ chmod 2755 directory        # setgid + rwxr-xr-x
$ chmod 1777 directory        # sticky + rwxrwxrwx

4.9 Access checking by the kernel

When a process attempts to open a file for reading, the kernel performs the following checks, in order:

  1. Is the process's effective UID 0 (root)? If so, most checks are bypassed. (Root can read and write any file. Execute permission is still required for a file to be run as a program, however.)
  2. Does the process's effective UID match the file's owner? If so, the owner's permission bits apply.
  3. Does the process's effective GID, or any of its supplementary GIDs, match the file's group? If so, the group's permission bits apply.
  4. Otherwise, the others' permission bits apply.

Only the first matching category is considered. If the owner's bits deny access, the group and others' bits are not consulted — even if they would permit it. This is why a file with mode 000 owned by user A is inaccessible even to a member of the file's group, if that member happens to be the owner.

The check is enforced on every open, every read, every write, and every execute. On directories, it is enforced on every path component that is traversed.

4.10 Security implications

The permission model is small but has a range of security implications that are worth understanding.

Over-permissive files

A file with mode 777 is writable by anyone. If it is a script that gets run by a privileged user, an attacker can modify it and gain that user's privileges the next time it runs. World-writable files are a common source of privilege escalation.

Wrong ownership

A file that should be owned by root but is owned by an ordinary user can be modified by that user. If it is referenced in a privileged context, that is a vulnerability. System files should be owned by root and not writable by non-root users.

Directory execute bits

Execute permission on a directory is required to traverse it. A directory with mode rw- lets you list its contents but not access files inside. A directory with mode --x lets you access files if you know their names, but not list them. Neither is a strong barrier against an attacker who can guess filenames.

Setuid surprises

Every setuid-root binary on a system is a potential privilege-escalation target. Modern practice minimises the number of setuid binaries and replaces them with capabilities (Lesson 12.5) where possible.

Umask defaults

A permissive umask creates files that may leak information to other users. On a shared system, use 027 or 077. On a personal laptop, 022 is often acceptable.

Recursive chmod

chmod -R 777 directory is a common mistake. It makes everything in the tree world-writable, including files that should not be. This one command has caused countless security incidents.

4.11 Applying least privilege

The principle of least privilege — every program and user should operate with the fewest privileges necessary — translates directly into permission choices:

  1. Start restrictive, widen as needed. When creating a file, use the most restrictive mode that still lets the intended users do their work. Modes 600, 640, and 660 are usually sufficient.
  2. Avoid 777 and 666. World-writable files are rarely necessary on a modern system. When a shared resource needs to be writable by multiple users, use a dedicated group and mode 775 or 770 instead of 777.
  3. Set a sensible umask. On a multi-user system, use 027 at minimum. Users who handle sensitive data can set 077.
  4. Own system files correctly. Configuration and binaries should be owned by root. Log files should be owned by the service that writes them. Data files should be owned by the user who produces them.
  5. Audit periodically. Use find to locate files with unexpected permissions or ownership:
    # World-writable files
    find / -perm -0002 -type f 2>/dev/null
    
    # Files owned by non-root in /etc
    find /etc -not -user root 2>/dev/null
    
    # setuid and setgid binaries
    find / -perm -6000 -type f 2>/dev/null
  6. Use capabilities instead of setuid where possible. A program that needs to bind to a low-numbered port should have CAP_NET_BIND_SERVICE, not the setuid bit. Detailed in Lesson 12.5.
  7. Prefer groups to world access. If a resource needs to be shared by a team, create a group and give the resource group ownership. Avoid opening access to "others" unless the resource is genuinely public.

4.12 Beyond the classical model: ACLs

The nine-bit model has a limitation: it only expresses permissions for one owner, one group, and everyone else. For a file that needs to be readable by user A, writable by user B, and inaccessible to everyone else, there is no clean way to express this with the classical bits.

POSIX Access Control Lists (ACLs) extend the model to allow per-user and per-group entries beyond the owner/group/others triad. They are supported by ext4, XFS, Btrfs, and other modern file systems, and they are enabled by default on most distributions.

# Show the ACL on a file
$ getfacl report.txt
# file: report.txt
# owner: alice
# group: developers
user::rw-
user:bob:r--          # extra entry: bob can read
group::r--
mask::r--
other::---

# Add a read permission for bob
$ setfacl -m u:bob:r report.txt

# Add read+write for a group
$ setfacl -m g:auditors:rw report.txt

# Remove the ACL entry for bob
$ setfacl -x u:bob report.txt

# Remove all extended ACLs (leave only the base permissions)
$ setfacl -b report.txt

When a file has an ACL, ls -l displays a + at the end of the mode field to signal this. The ACL "mask" entry is a subtlety: it acts as an upper bound on the permissions granted by named user, named group, and group entries, and it is what chmod modifies when applied to a file with an ACL.

ACLs are useful but they add complexity: the file's true access policy is no longer visible in a single ls -l, and chmod no longer sets the whole policy. For most files, the classical bits suffice; for files that need finer-grained access, ACLs are the standard tool.

4.13 What follows from this

The permission model established in this lesson is what makes multi-user Linux possible: it is the mechanism by which isolation between users is enforced, the means by which services are confined to their own data, and the foundation on which the more elaborate security mechanisms of Module 5 are built. Understanding it in detail is a prerequisite for everything that follows — from setting up the development environment in the next lesson to reasoning about security boundaries in Unit 12.

05 Worked example

To make the permission model concrete, let's walk through a realistic scenario: setting up the file permissions for a small web application deployment.

Setting permissions for a web application

A web application has the following files:

/srv/webapp/
├── config/
│   ├── database.conf        # contains credentials
│   └── app.conf             # application settings
├── logs/
│   └── app.log              # application log
├── public/
│   └── index.html           # served to the public
└── bin/
    └── start.sh             # startup script

The application runs as the webapp user, and it is administered by the webteam group. We want:

  • database.conf readable only by webapp, because it contains secrets.
  • app.conf readable by webapp and the webteam administrators, writable only by webapp.
  • logs/ writable by webapp and readable by webteam for troubleshooting.
  • public/ readable by everyone, so the web server can serve it, and writable only by administrators.
  • start.sh executable by the webapp user.

Applying the permissions:

# Set the base ownership: everything to webapp, group webteam
$ sudo chown -R webapp:webteam /srv/webapp

# Database config: owner (webapp) read only
$ sudo chmod 400 /srv/webapp/config/database.conf
$ ls -l /srv/webapp/config/database.conf
-r-------- 1 webapp webteam 128 Oct 15 12:00 database.conf

# App config: owner read/write, group read
$ sudo chmod 640 /srv/webapp/config/app.conf
$ ls -l /srv/webapp/config/app.conf
-rw-r----- 1 webapp webteam 512 Oct 15 12:00 app.conf

# Logs directory: owner read/write/execute, group read/execute
$ sudo chmod 750 /srv/webapp/logs
$ ls -ld /srv/webapp/logs
drwxr-x--- 2 webapp webteam 4096 Oct 15 12:00 logs

# Public directory: world read/execute, owner (webapp) full
$ sudo chmod 755 /srv/webapp/public
$ ls -ld /srv/webapp/public
drwxr-xr-x 2 webapp webteam 4096 Oct 15 12:00 public

# Public HTML file: world readable
$ sudo chmod 644 /srv/webapp/public/index.html
$ ls -l /srv/webapp/public/index.html
-rw-r--r-- 1 webapp webteam 1024 Oct 15 12:00 index.html

# Startup script: owner read/write/execute
$ sudo chmod 700 /srv/webapp/bin/start.sh
$ ls -l /srv/webapp/bin/start.sh
-rwx------ 1 webapp webteam 256 Oct 15 12:00 start.sh

Let's verify the effect from three different perspectives:

As a member of webteam (an administrator):

$ id alice
uid=1002(alice) gid=1002(alice) groups=1002(alice),2000(webteam)

$ cat /srv/webapp/config/database.conf
cat: /srv/webapp/config/database.conf: Permission denied
# denied — the file is 400 and alice is not the owner

$ cat /srv/webapp/config/app.conf
# succeeds — alice is in webteam, group bit is r--

$ tail /srv/webapp/logs/app.log
# succeeds — logs directory is 750, group has r-x

$ ./srv/webapp/bin/start.sh
bash: /srv/webapp/bin/start.sh: Permission denied
# denied — the file is 700 and alice is not the owner

As the webapp user (the application):

$ sudo -u webapp cat /srv/webapp/config/database.conf
# succeeds — webapp is the owner, owner bit is r--

$ sudo -u webapp cat /srv/webapp/config/app.conf
# succeeds — webapp is the owner, owner bits are rw-

$ sudo -u webapp ./srv/webapp/bin/start.sh
# succeeds — webapp is the owner, owner bit is rwx

As a random user (not in webteam):

$ id carol
uid=1003(carol) gid=1003(carol) groups=1003(carol)

$ cat /srv/webapp/config/app.conf
cat: /srv/webapp/config/app.conf: Permission denied
# denied — carol is not the owner and not in webteam

$ ls /srv/webapp/public/
index.html
# succeeds — public is world-readable

$ cat /srv/webapp/public/index.html
# succeeds — world-readable

$ cat /srv/webapp/logs/app.log
cat: /srv/webapp/logs/app.log: Permission denied
# denied — carol is not the owner and not in webteam

Notice that the "public" directory is world-readable and world-executable, but its parent directories are not: /srv/webapp itself is 750, meaning that a user like carol cannot traverse into it. If we wanted the public directory to actually be publicly accessible, we would need to make the parent directory world-executable too — or restructure the layout so that the public content lives outside the private tree.

The lesson: permissions are not just about individual files. The whole path matters. A restrictive parent directory overrides permissive permissions on a child. When setting up a directory tree, it is worth walking through the path from / to each file and checking every component — exactly as the kernel does when it resolves a path.

06 Interactive activity

Activity · experiment with permissions on your VM

Open a terminal and run the following. Record what you observe for each step. Some of the exercises require a second user account; if you do not have one, use su - or sudo -u to switch to a system account (for example, nobody).

  1. Create a test file and inspect its mode.
    touch test.txt
    ls -l test.txt
    What is the mode? What does it correspond to in octal? What is the umask that produced it?
  2. Change the mode with symbolic notation.
    chmod u+x test.txt
    ls -l test.txt
    
    chmod g-w test.txt
    ls -l test.txt
    
    chmod o-r test.txt
    ls -l test.txt
    What is the mode after each step? Confirm your predictions by translating the symbolic change to the resulting bits.
  3. Set an exact mode with octal notation.
    chmod 640 test.txt
    ls -l test.txt
    chmod 755 test.txt
    ls -l test.txt
    chmod 000 test.txt
    ls -l test.txt
    Try cat test.txt after the final command. What happens? Can you restore access?
  4. Test the umask.
    umask 077
    touch private.txt
    mkdir private-dir
    ls -l private.txt private-dir
    
    umask 022
    touch default.txt
    mkdir default-dir
    ls -l default.txt default-dir
    What mode does each file and directory have? Confirm that the mode matches what the formula base & ~umask predicts.
  5. Explore the special bits. Create a script, then observe what happens when you run it with and without the execute bit:
    echo 'echo hello' > script.sh
    ./script.sh
    chmod +x script.sh
    ./script.sh
    Then examine the sticky bit on /tmp:
    ls -ld /tmp
    touch /tmp/myfile-$$
    ls -l /tmp/myfile-*
    Can you see the effect of the sticky bit by checking whether another user could delete your file?
  6. Test ownership with a second user. If you have a second account, log in as that user and try to read a file you own. Which permissions mattered?
    # As user1
    echo "secret" > /tmp/shared.txt
    chmod 600 /tmp/shared.txt
    
    # As user2 (switch with su or sudo -u)
    cat /tmp/shared.txt
    # denied
    
    # Back as user1: allow read for others
    chmod o+r /tmp/shared.txt
    
    # As user2 again
    cat /tmp/shared.txt
    # succeeds
  7. Audit your system. Run the three audit commands from section 4.11:
    # World-writable files
    sudo find / -perm -0002 -type f 2>/dev/null | head -10
    
    # setuid and setgid binaries
    sudo find / -perm -6000 -type f 2>/dev/null | head -20
    
    # Files in /etc not owned by root
    sudo find /etc -not -user root 2>/dev/null
    What did you find? Are any of the results surprising?

Reflect: which of these experiments best illustrated the difference between the classical permission model and your mental model before the lesson? Which commands are you most likely to actually use in practice?

07 Knowledge check

Check your understanding

Question 1

A file has mode 754. What can the owner do? The group? Others?

Show answer
754 in octal is rwxr-xr-- in symbolic form. The owner can read, write, and execute (7 = rwx). The group can read and execute (5 = r-x) but not write. Others can read only (4 = r--) and cannot write or execute. This is a typical mode for an executable that should be readable by everyone but writable only by its owner.

Question 2

What is the difference between the r, w, and x permissions on a file versus on a directory?

Show answer
On a file: r allows reading the file's contents, w allows modifying its contents, and x allows executing it as a program. On a directory: r allows listing the names of entries in the directory, w allows creating, renaming, and deleting entries within the directory, and x allows traversing the directory — that is, using it as a component in a path to access files inside. The distinction is important because the permissions on a file's directory determine whether the file can be found at all, while the permissions on the file itself determine what can be done with it once found.

Question 3

What does the umask 027 produce for a new file and a new directory? Show your work.

Show answer
The base modes are 666 for a file and 777 for a directory. The umask is subtracted by bitwise AND with its complement: ~027 = 750 in octal.
For a file: 666 & 750 = 640, which is rw-r----- — the owner can read and write, the group can read, and others have no access.
For a directory: 777 & 750 = 750, which is rwxr-x--- — the owner has full access, the group can read and traverse, and others have no access.
A umask of 027 is common on servers where files should be readable within a group but not to the wider world.

Question 4

Explain the difference between the setuid and setgid bits, and describe one classic use of each.

Show answer
The setuid bit on an executable causes the program to run with the effective UID of the file's owner rather than the caller. The classic example is /usr/bin/passwd, which must write to /etc/shadow — a file writable only by root — and so is setuid root. The setgid bit on an executable causes the program to run with the effective GID of the file's group; on a directory, it causes new files and subdirectories to inherit the directory's group. The classic use of setgid on a directory is a shared project folder: making the directory setgid ensures all files created inside belong to the project's group, so group permissions can be used to grant access to all project members.

Question 5

Why is chmod -R 777 directory considered a security mistake?

Show answer
chmod -R 777 makes every file and subdirectory in the tree world-readable, world-writable, and world-executable. Any user on the system can modify any file, replace any file, or add executable content. If the directory contains scripts that will be run by a privileged user, an attacker can modify those scripts and gain the privileged user's access the next time they run. Even in the absence of a privileged context, 777 on the whole tree allows any user to destroy any other user's work. The correct approach is to use the minimum permissions that the intended users need — typically 755 for directories and 644 for files, or a group-based scheme with 750/640 for shared resources that should not be world-accessible.

08 Unit discussion

Discussion prompt

The classical UNIX permission model has three categories (owner, group, others) and three permissions (read, write, execute). This is compact and easy to reason about, but it is also coarse: it cannot express "user A may read, user B may write, user C may execute" without resorting to ACLs or more elaborate mechanisms. Some systems have responded by moving away from the UNIX model entirely — Windows uses a rich ACL model by default, and modern cloud platforms use IAM policies that are even more expressive.

Is the classical UNIX permission model still the right foundation for a modern operating system, or has it been outgrown? Argue for whichever view you find more persuasive. Consider the trade-offs: expressiveness versus simplicity, learnability versus precision, and the historical fact that most UNIX-like systems have managed with the nine-bit model for fifty years.

Post your response to the Unit 2 discussion forum and reply to at least two classmates. This activity supports CLO6 and CLO7.

09 Practice exercise

This ungraded exercise consolidates the lesson. Aim to spend about 45–60 minutes on it.

  1. Complete the octal table. Without looking at the table in section 4.4, write out the octal value for each of these symbolic modes: rwx, rw-, r-x, r--, -wx, -w-, --x, ---. Then check your answers.
  2. Build a directory with specific permissions. Create the following structure and set the permissions as shown:
    sandbox/
    ├── private/       (700)
    │   └── secret.txt (600)
    ├── team/          (770, setgid)
    │   └── shared.txt (660)
    └── public/        (755)
        └── readme.txt (644)
    Verify the mode of each entry with ls -ld and ls -l. Confirm that the setgid directory makes new files inherit the directory's group.
  3. Test the umask. Set the umask to 077, create a file and a directory, and note the resulting modes. Then set the umask to 000 and repeat. Then 022 and repeat. Confirm that each mode matches the formula in section 4.6.
  4. Write a script to audit permissions. Write a shell script that takes a directory as an argument and lists every file inside it whose mode is more permissive than 644 for regular files or more permissive than 755 for directories. Print the file path and its current mode.
  5. Investigate the special bits. Find three setuid binaries on your system (use find / -perm -4000). For each, look up what the binary does and explain in one sentence why it needs setuid. What would happen if the setuid bit were removed?
  6. Explore ACLs. Create a file owned by you, then use setfacl to grant read access to another user on the system. Verify with getfacl and by trying to read the file as that user. Then remove the ACL entry with setfacl -x and verify that access has been revoked.
  7. Reflect. In your own words, explain why "delete a file" requires write permission on the directory rather than on the file. Give a concrete example of a scenario where this distinction matters.

10 Summary

  • Every file has a nine-bit permission mode: three permissions (read, write, execute) for three categories (owner, group, others).
  • For a file, r, w, and x control reading, modifying, and executing the file. For a directory, they control listing the names, creating or removing entries, and traversing the directory.
  • Permissions can be expressed in symbolic form (rwxr-xr-x) or octal form (755). The mapping is r=4, w=2, x=1.
  • chmod changes permissions; chown and chgrp change ownership and group. Ownership changes require root privileges (or appropriate capabilities).
  • The umask determines the default mode of newly created files and directories. Common values are 022, 027, and 077.
  • Three special bits modify file behaviour: setuid makes a binary run with the owner's privileges, setgid makes it run with the group's privileges (or, on a directory, causes new files to inherit the directory's group), and the sticky bit on a directory restricts deletion to the file's owner.
  • The kernel checks permissions on every access, using the first matching category (owner, then group, then others).
  • Least privilege means using the most restrictive permissions that still let the intended users do their work. Avoid 777, audit for unexpected permissions, and use capabilities instead of setuid where possible.
  • POSIX ACLs extend the classical model with per-user and per-group entries, at the cost of additional complexity.

Looking ahead

Lesson 2.5 closes Unit 2 by turning to the development environment: the compilers, build tools, version control, and debuggers that every subsequent laboratory will assume. The shell, user, and permission skills from this unit are all prerequisites for that material.

11 References

  • Stevens, W. R., & Rago, S. A. Advanced Programming in the UNIX Environment, 3rd ed., Sections 4.4–4.7. Addison-Wesley, 2013.
  • Nemeth, E., Snyder, G., Whaley, B., & Mackin, D. UNIX and Linux System Administration Handbook, 5th ed., Chapter 8. Addison-Wesley, 2017.
  • Shotts, W. The Linux Command Line, 5th ed., Chapter 9 (permissions). No Starch Press, 2019.
  • Linux man pages: man 1 chmod, man 1 chown, man 1 chgrp, man 1 umask, man 2 chmod, man 2 access, man 5 acl, man 1 getfacl, man 1 setfacl.
  • POSIX.1-2017 / IEEE Std 1003.1, Standard for Information Technology — Portable Operating System Interface (POSIX), Section on file access permissions.
👤