Loading learning content...
Every file system stores data that ranges from trivial to existentially critical. Your browser cache? Ephemeral. Your company's financial records? Regulated by law. Your private SSH keys? Compromise means total system access. The operating system cannot know which files matter to whom—so it must provide universal protection mechanisms that users and administrators configure according to their needs.
File protection isn't just about preventing malicious access. It's about isolation, accountability, and controlled sharing. A multi-user system must ensure that one user's mistake or malice cannot destroy another user's data, while still enabling legitimate collaboration.
By the end of this page, you will understand: why access control is essential in any multi-user or networked system, the fundamental principles underlying all access control mechanisms, the distinction between authentication (who you are) and authorization (what you can do), and how access control decisions are made and enforced by the operating system kernel.
At its core, access control answers a deceptively simple question: Should this operation be allowed?
When a process attempts to open a file, read its contents, modify its data, or execute it as a program, the operating system must make a decision. This decision depends on three fundamental elements:
The access control decision:
Given a subject S, an object O, and an operation Op, the access control system must determine: Is S authorized to perform Op on O?
This binary decision (allow or deny) seems straightforward, but its implementation involves complex policy representation, efficient lookup mechanisms, and careful handling of edge cases. The answer depends on:
Operating systems implement access control through a reference monitor—a trusted component that mediates every access request. The reference monitor must be: (1) tamper-proof—attackers cannot modify it, (2) always invoked—no access bypasses it, (3) small enough to verify—its correctness can be proven. In practice, this is the kernel's access control logic, which sits between applications and protected resources.
Before diving into access control mechanisms, we must distinguish two related but fundamentally different concepts that are often confused:
Authentication answers: Who is this entity? Authorization answers: What is this entity allowed to do?
These two processes are sequential. First, the system establishes identity (authentication), then it checks what that identity permits (authorization). Access control is specifically about authorization—it assumes the subject's identity is already known.
| Aspect | Authentication | Authorization |
|---|---|---|
| Core Question | Who are you? | What can you do? |
| When It Happens | At login/session start | At each resource access |
| Frequency | Once per session (typically) | Every operation on protected resources |
| Mechanisms | Passwords, biometrics, tokens, certificates | Permission bits, ACLs, capabilities, policies |
| Failure Mode | Access denied (identity unknown) | Operation denied (identity known but unauthorized) |
| Data Required | Credentials (secrets) | Policies (rules) |
| Primary Challenge | Verifying identity claims | Efficiently evaluating permissions |
Why the distinction matters:
Strong authentication with weak authorization is dangerous. If you verify users perfectly but then grant everyone admin access, your authentication is worthless.
Conversely, sophisticated authorization rules are meaningless if attackers can impersonate legitimate users. Security requires both mechanisms working together.
The handoff point:
After authentication succeeds, the system assigns the subject a security context—a data structure containing the user's identity and security-relevant attributes. Every subsequent access control decision references this security context. In Unix, this is the process's UID and GID list. In Windows, it's the access token. In SELinux, it's the security label.
A common design mistake is conflating identity with authorization. Just because a user is a 'developer' doesn't mean they should access all development resources. Good access control separates who someone is from what they're working on, enabling fine-grained restrictions that follow the principle of least privilege.
Access control in operating systems is guided by time-tested security principles. These principles, established in the 1970s by Saltzer and Schroeder, remain the gold standard for secure system design:
Applying the principles to files:
Consider how these principles manifest in file protection:
open(), read(), write() system call checks permissions.These principles guide every access control design decision we'll explore in this module.
The access control matrix is the foundational theoretical model for understanding permissions. Introduced by Lampson in 1971, it represents all authorization relationships in a system.
Conceptually, visualize a spreadsheet:
| Subject / Object | /etc/passwd | /home/alice/secret.txt | /var/log/syslog | /bin/ls |
|---|---|---|---|---|
| root | read, write | read, write | read, write | read, execute |
| alice | read | read, write, delete | — | read, execute |
| bob | read | — | read | read, execute |
| www-data | read | — | append | read, execute |
Reading the matrix:
secret.txt), read /etc/passwd (which contains user info), and execute /bin/ls, but cannot access the system log.www-data user (web server) can only append to the log, not read previous entries—limiting what an attacker could learn from a compromised web server.The practical problem:
While the matrix elegantly represents access control, it cannot be implemented directly:
Most real systems use column-major storage (ACLs stored with files) for persistent permissions, but row-major storage (capabilities held by processes) for runtime efficiency. When a process opens a file, the kernel checks the ACL, then issues a capability (file descriptor) for subsequent operations. This combines the administrative convenience of ACLs with the runtime efficiency of capabilities.
Understanding access control requires knowing where and when enforcement happens. The operating system kernel is the ultimate enforcer—applications cannot bypass kernel-level access checks.
The enforcement boundary:
Access control is enforced at the system call boundary. When a user-space program wants to access a file, it can't manipulate disk sectors directly. It must invoke system calls (open, read, write, etc.), and the kernel interposes on each call.
12345678910111213141516171819202122232425262728293031323334353637383940
// The journey of a file access request USER SPACE:┌────────────────────────────────────────────────────────────────┐│ Process (runs as user "alice") ││ ││ fd = open("/home/bob/secret.txt", O_RDONLY); ││ │ ││ └─── System call: passes filename, mode, process context │└────────────┬───────────────────────────────────────────────────┘ │═════════════│═══════════════════════════════════════════════════ │ KERNEL BOUNDARY (privileged mode) ▼KERNEL SPACE:┌────────────────────────────────────────────────────────────────┐│ 1. sys_open() handler receives request ││ ││ 2. Path resolution: traverse "/home/bob/secret.txt" ││ - Check execute permission on "/" for alice ││ - Check execute permission on "/home" for alice ││ - Check execute permission on "/home/bob" for alice ││ ││ 3. Locate inode for "secret.txt" ││ ││ 4. ACCESS CONTROL CHECK: ││ Subject: alice (UID=1001, GIDs=[1001, 100]) ││ Object: secret.txt (owner=bob, group=users, mode=0640) ││ Operation: read (O_RDONLY) ││ ││ owner=bob (UID=1002) → alice is not owner ││ group=users (GID=100) → alice IS member ││ group permissions: r-- (read allowed) ││ ││ DECISION: ALLOW ││ ││ 5. Create file descriptor, add to process's fd table ││ ││ 6. Return fd to user space │└────────────────────────────────────────────────────────────────┘Key enforcement points:
read()/write() calls check the fd, not the original pathWhy checking happens at open(), not read():
This design is critical. Permission checks happen when you acquire access (open), not when you use access (read/write). This means:
The file descriptor becomes a capability—proof that you passed the access check. Holding a valid fd demonstrates permission.
Time-of-Check to Time-of-Use (TOCTOU) is a race condition vulnerability. If a program checks permissions on a file, then separately opens it, an attacker might swap the file between check and open. Solution: Don't separate checking and use—let the kernel check during the open() call itself, then use the returned fd. Never check permissions in user space then assume they'll hold.
Different operations on files require different permissions. Understanding what each access right permits—and what it doesn't—is essential for configuring file protection correctly.
| Right | Symbol | For Files | For Directories |
|---|---|---|---|
| Read | r | View file contents, copy file | List directory contents (ls) |
| Write | w | Modify file contents, truncate | Create/delete files in directory, rename |
| Execute | x | Run as program/script | Traverse (cd into) the directory |
| Append | a* | Add to end only, no overwrite | Typically combined with write |
| Delete | d* | Remove the file | Usually requires directory write |
| Change permissions | — | Modify protection settings | Owner or superuser only |
| Change ownership | — | Transfer to another user | Superuser only (usually) |
Note: Append and delete are explicit rights in some systems (like AFS) but implied by read/write in others (like basic Unix).
The subtleties of directory permissions:
Directory permissions are often misunderstood:
Critical insight: To delete a file, you don't need write permission on the file—you need write permission on the directory containing it. The file's permissions are irrelevant to deletion.
cd into the directorycd into directoryIn directories with write permission for multiple users (like /tmp), anyone could delete anyone's files. The sticky bit (t) restricts deletion: you can only remove files you own, even if you have directory write access. This is why /tmp has permissions drwxrwxrwt—the 't' protects users from each other while allowing everybody to create temporary files.
Let's examine how access control manifests in real operating system operations. These examples illustrate the principles we've discussed.
1234567891011121314151617181920212223242526272829303132333435
# Examining file permissions on Linux $ ls -la /etc/passwd /etc/shadow /home/alice-rw-r--r-- 1 root root 2847 Jan 15 10:23 /etc/passwd-rw-r----- 1 root shadow 1501 Jan 15 10:23 /etc/shadowdrwxr-x--- 15 alice alice 4096 Jan 16 09:45 /home/alice # /etc/passwd: world-readable (contains usernames, not passwords)# Owner: root can read/write# Group: root group can read# Others: everyone can read # /etc/shadow: contains hashed passwords, restricted access# Owner: root can read/write# Group: shadow group can read (for PAM authentication services)# Others: NO ACCESS # /home/alice: user's home directory# Owner: alice can read/write/enter# Group: alice can read/enter (but not write)# Others: CANNOT even list or enter # What happens when bob tries to access alice's files?$ whoamibob$ cat /home/alice/document.txtcat: /home/alice/document.txt: Permission denied# Bob cannot even traverse into /home/alice # But alice can grant access:# alice runs: chmod 750 /home/alice# chmod 644 /home/alice/public.txt$ ls -l /home/alice/public.txt-rw-r--r-- 1 alice alice 1024 Jan 16 09:30 public.txt# Now anyone can read public.txt (if they can traverse to it)Analyzing the examples:
Defense in depth: /etc/shadow has restricted permissions AND is encrypted—multiple layers of protection.
Path traversal matters: Even if /home/alice/public.txt is world-readable, users without execute permission on /home/alice cannot reach it.
Ownership vs. permissions: Root owns /etc/passwd but deliberately makes it world-readable. Ownership enables control; permissions define policy.
Group flexibility: The shadow group allows authentication services to verify passwords without running as root.
chmod 777: Never use this. It makes files world-writable—any user can modify or replace them.
Forgetting directory permissions: Setting a file readable but its parent directory untraversable achieves nothing.
Ignoring group permissions: Leaving group write access on files owned by a shared group can lead to accidental modification.
World-writable directories without sticky bit: Enables file deletion attacks between users.
We've established the theoretical and practical foundations of file access control. These concepts underpin every protection mechanism we'll explore in subsequent pages.
What's next:
With the fundamentals established, we'll examine specific permission models. The next page explores the classic User/Group/World model—Unix's elegant, minimalist approach that uses just 9 bits to express file permissions. This model, despite its age, remains the default protection mechanism on billions of devices worldwide.
You now understand the fundamental concepts of access control: the subject-object-operation model, the principles that guide secure design, how access control matrices are implemented, and where enforcement happens in the kernel. Next, we'll see how Unix implements these concepts with its owner/group/other permission model.