Skip to Content
All memories

Operating System Protection - Guarding the Gates of Your Digital Kingdom!

 — #OS#CS Basics

Hey OS architects and security enthusiasts! We know the OS manages resources, but just as crucial is controlling access to those resources. In a world of multiple users, concurrent processes, and network connections, how does the OS ensure that Process A can't read Process B's private data, or that a user can't accidentally (or maliciously) delete critical system files? This vital function is Protection. Let's explore the guiding principles, formal models like the Access Matrix, and various implementation techniques used to build secure and reliable systems.

Guiding Principles: Need-to-Know & Least Privilege

Two fundamental principles guide robust protection systems:

  1. Principle of Least Privilege: This is perhaps the most important guiding rule. An entity (a user, a process, a program) should operate using the minimum set of privileges necessary to complete its current task. Why? It drastically limits the damage that can result from accidents, errors, or security breaches. If a program only needs to read a file, it shouldn't be given write permission. If a user only needs to run standard applications, they shouldn't have administrator rights by default. This can be enforced statically (permissions set for the life of a process/user session) or dynamically (privileges acquired temporarily when needed and dropped afterwards).
  2. Need-to-Know: Closely related to least privilege, this principle states that a process should only be allowed access to the resources (objects, data) it currently requires to complete its task. Access should be granted based on functional necessity, not just convenience.

The "grain" of privilege management matters. Rough-grained control (e.g., traditional Unix user vs. root) is simpler but less precise. Fine-grained control (e.g., detailed Access Control Lists, Role-Based Access Control) offers better adherence to least privilege but adds complexity and overhead.

Domains of Protection: Defining Boundaries

To implement protection, we need to define protection boundaries. A Domain represents a set of access rights. An access right is the permission to perform a specific operation (like read, write, execute, delete) on a specific object (like a file, device, memory segment, or even another domain).

So, a Domain essentially defines the collection of objects a process running within that domain can access, and which operations it's allowed to perform on them. A process operates within a protection domain, and mechanisms must exist to allow processes to switch domains when necessary (and authorized).

Real-World Domains: Examples

  • UNIX/Linux: Domains are typically associated with user IDs (UIDs). Each process runs with the UID of the user who started it. Domain switching can occur via:
    • The setuid bit: A special permission bit on executable files. If set, when that file is executed, the process runs with the UID of the file's owner (often root for system utilities) instead of the user running it. This grants temporary privilege escalation for specific tasks. The original UID is restored upon completion. (Caution: setuid programs must be written extremely carefully to avoid security holes!).
    • su / sudo commands: Allow users to temporarily execute commands as another user (usually root), typically requiring a password or specific configuration (sudoers file).
  • Multics Rings: An influential early system that used hierarchical protection rings (numbered 0 to N, with 0 being most privileged). Each segment (file) was assigned a ring number, and each process ran in a specific ring. A process in ring i could freely access segments in ring j if j >= i. Accessing segments in more privileged rings (j < i) required a controlled transfer (a "gate") through specific entry points. This provided more levels of privilege than the simple user/kernel model but was still somewhat rigid (if Domain Dj was more privileged than Di (j < i), then all objects accessible in Di were also accessible in Dj, violating strict need-to-know).

The Access Matrix: A Formal Model of Protection

Conceptually, the relationship between domains, objects, and rights can be represented by an Access Matrix.

  • Rows: Represent Domains (D1,D2,...D_1, D_2, ...).
  • Columns: Represent Objects (O1,O2,...O_1, O_2, ...). Objects can be files, devices, memory segments, or even other domains.
  • Entry Access[i, j]: Contains the set of operations {op1, op2, ...} that a process executing in Domain DiD_i is permitted to invoke on Object OjO_j.

Dynamic Protection: The matrix isn't always static. Operations might exist to add or remove rights. Special rights can also be included in the matrix entries:

  • owner: The process/domain that owns the object can typically modify its access rights.
  • copy (or *): Allows a domain to copy a specific right for an object into another domain's entry for that object (propagation of rights). Often involves owner checks.
  • control: If OjO_j is a domain object, Access[i, j] containing control means a process in DiD_i can modify the access rights in Domain DjD_j.
  • transfer: Allows a process to switch its execution from Domain DiD_i to Domain DjD_j.

The Access Matrix provides a clear model separating the mechanism (the OS providing and enforcing the matrix) from the policy (the users/administrators deciding what rights go where).

Implementing the Access Matrix: Making it Real

The conceptual matrix is often sparse (most entries are empty) and potentially huge. Direct implementation is rare. Common practical approaches include:

  1. Global Table: Store all non-empty access rights as triples <Domain, Object, Rights-set> in one large system table. Checking access involves searching this table. Simple concept but inefficient search, difficult management (grouping rights), and the table itself can become enormous.
  2. Access Control Lists (ACLs): Stores the matrix column-wise. Each object has an associated list (ACL) of <Domain, Rights-set> pairs, specifying which domains have what access. When a process in DiD_i attempts operation M on OjO_j, the system checks OjO_j's ACL for an entry for DiD_i containing M. This is the most common approach for file systems (e.g., Windows NTFS ACLs, POSIX ACLs). Easy to manage per-object permissions. Can include default/group entries. Checking rights for a specific domain across many objects requires examining many ACLs.
  3. Capability Lists: Stores the matrix row-wise. Each domain has an associated list of capabilities. A capability is like an unforgeable ticket or token <Object, Rights-set> specifying an object and the operations the domain can perform on it. When a process wants to access an object, it presents the relevant capability to the OS. Possession of the capability itself implies permission. Capabilities must be protected by the OS (e.g., stored in kernel space or using special hardware tags) to prevent user processes from creating or modifying them arbitrarily. Makes it easy to see what a domain can do, but revocation (taking away a capability) is difficult – how do you find and invalidate all copies of a capability that might have been passed around?
  4. Lock-Key Mechanism: A hybrid approach. Each object has a list of unique bit patterns called locks. Each domain has a list of unique bit patterns called keys. A process in a domain can access an object only if the domain holds a key that matches one of the object's locks. Flexible, keys can be passed between domains, and revocation involves removing locks from the object or keys from the domain.

Most systems use a combination, often ACLs for persistent storage and temporary capabilities created upon first access (e.g., file descriptors/handles) for efficiency during a session.

The Challenge of Revocation

Taking away access rights after they've been granted (Revocation) is a surprisingly tricky problem, especially in dynamic systems.

  • ACLs: Revocation is relatively easy – just remove the relevant entry from the object's ACL. The change takes effect immediately on the next access attempt.
  • Capabilities: Revocation is hard. Since capabilities can be copied and passed around, how does the system find and invalidate all existing copies? Solutions involve indirections (capabilities point to an entry in a global table, which can be invalidated), back-pointers (tracking where capabilities went, complex), or periodic invalidation.

Beyond the Matrix: Other Approaches

  1. Capability-Based Systems: Systems designed around capabilities as the primary means of protection, rather than just an implementation technique for an access matrix. Often require specialized hardware or language support. Focuses on possessing the authority (capability) rather than checking identity against a list.
  2. Role-Based Access Control (RBAC): Manages permissions indirectly. Instead of assigning permissions directly to users, permissions (often defined as the right to execute certain system calls or operations) are granted to Roles. Users are then assigned to Roles. To perform a privileged task, a user activates the necessary role (often requiring authentication). This simplifies administration (manage roles, not individual user permissions) and adheres well to least privilege. Solaris uses RBAC extensively.
  3. Language-Based Protection: Protection mechanisms enforced by the programming language compiler and runtime system, rather than the OS. Examples include type safety in languages like Java or C#, preventing direct memory manipulation or forging pointers. Can provide fine-grained protection within a single process address space but relies on the language implementation's correctness and typically doesn't manage OS-level resources directly.

Conclusion: The Ever-Vigilant Gatekeeper

OS Protection is a multifaceted challenge involving defining boundaries (domains), specifying permissions (access matrix/rights), and implementing enforcement mechanisms (ACLs, capabilities, locks). Guiding principles like least privilege help design more secure systems. From the classic setuid mechanism in UNIX and the hierarchical rings of Multics to modern RBAC systems and language-level safety, operating systems employ a diverse range of strategies to act as vigilant gatekeepers, controlling access and safeguarding the integrity of the digital kingdom.