When a backend service opens a configuration file, the kernel does not ask which developer wrote the program or which username appears in a deployment manifest. It checks the credentials attached to the running process against the ownership and permission metadata attached to the file.
That distinction is the foundation of the Unix permission model:
Users and groups provide identities. Processes carry those identities. Files and directories describe which operations those identities may perform.
A correct permission setup lets several services share a machine without giving each service access to everything. An incorrect setup can expose credentials, prevent a deployment from starting, or let one compromised process modify another service's data.
This chapter develops the model from the kernel's point of view and then applies it with ordinary Linux tools.
In everyday language, a user usually means a person. In an operating system, a user is an identity under which processes run and resources can be owned.
Some identities correspond to people who log in interactively. Others are service accounts created for programs such as a database, web server, or monitoring agent. A service account may have no usable password and no interactive shell. It is still a user as far as the kernel's protection checks are concerned.
Linux represents each user with a numeric user ID, or UID. Human-readable names such as ashish, postgres, and payments are convenient labels resolved by user-space software.
For example:
The kernel normally stores and compares the numbers. If a file is owned by UID 992, tools may display payments after looking up the name associated with that UID. If no name can be resolved, the tools can display 992 directly.
This separation explains why copying a storage device between systems can produce surprising ownership. UID 992 may name payments on one system and a different account, or no known account, on another. The file still contains the numeric owner ID.
On a traditional local Linux installation, /etc/passwd contains user account information such as the name, UID, primary group, home directory, and login shell. Despite its name, it does not normally contain ordinary password hashes on modern systems. Account information can also come from network or directory services, so reading /etc/passwd is not always a complete view.
The getent command asks the system's configured identity sources:
A result might look like:
The relevant fields here are the username payments, UID 992, primary GID 992, home directory, and login shell. The x is a placeholder; it is not the user's password.
A group is a named collection represented by a numeric group ID, or GID. Groups make it possible to grant the same class of access to several users without configuring each user separately on every file.
A user has one primary group recorded in its account entry. It may also belong to several supplementary groups.
Suppose a host has these relationships:
Both identities are members of payment-logs, so that group can be used to share selected log data. They do not need to share the same primary group or become the same user.
As with users, local group records traditionally appear in /etc/group, but the configured identity source may be elsewhere. Query it with:
A local result might be:
The final field lists accounts explicitly assigned as supplementary members. An account whose primary GID is 993 does not have to appear in that list, because its primary group relationship comes from the account record instead.
Use id to inspect the credentials associated with a shell:
Typical output is:
The groups list includes the primary group and the supplementary group set available to processes in that session.
Changing the group database does not normally rewrite the credentials of processes that are already running. A new login session or a restarted service receives a newly initialized group list. This is why adding an account to a group may appear to have no effect until the relevant session or service is restarted.
An executable file is a program stored on disk. When that program runs, the resulting process has a set of credentials.
The same executable can run with different credentials at the same time:
The program bytes are the same. The processes' authority can be different because the kernel makes access decisions using process credentials.
Processes normally inherit credentials from their parent when they are created. A shell started for a logged-in user has that user's credentials, and ordinary commands launched by the shell inherit them. A service manager can instead start a service using a dedicated service identity.
On Linux, /proc/<pid>/status exposes a process's numeric credentials. For the current shell, inspect:
The output has a form such as:
The four UID and GID columns exist because a process can carry more than one version of each identity.
A Linux process has a real UID, an effective UID, and a saved set-user-ID. It has corresponding forms of its group ID.
The real UID normally identifies the user that started the process. It helps preserve the process's origin even if the process temporarily operates with another effective identity.
The effective UID is the identity used for many authorization decisions. When reasoning about whether an ordinary process can open a file, the effective identity is usually the most important one.
The saved set-user-ID lets a carefully written program temporarily give up an effective identity and later regain it without obtaining a new identity from elsewhere.
For an ordinary process, all three IDs are usually equal:
Linux also maintains a filesystem UID used specifically for filesystem access checks. In normal applications it follows the effective UID, which is why explanations commonly describe file permissions in terms of the effective UID. Specialized privileged software can make the distinction relevant.
Groups follow the same broad pattern. Filesystem checks can use the process's effective group identity and its supplementary groups.
These IDs are kernel-maintained credentials. Changing an environment variable such as USER, changing a prompt, or printing another username does not alter them.
Loading simulation...
On Unix-like systems, UID 0 is the traditional privileged identity, conventionally named root.
The number is what matters to the kernel. Renaming an ordinary UID to root does not give it UID 0 authority, while a second account assigned UID 0 would carry the same dangerous level of authority despite having a different name.
A process operating with UID 0 can traditionally bypass many ordinary owner, group, and other restrictions, change ownership, and perform system-wide administration. Modern Linux evaluates privileged operations more precisely internally, so “root can always do absolutely anything” is not a reliable description of every kernel operation. UID 0 nevertheless represents far broader authority than an ordinary service normally needs.
This is why running every daemon as root defeats much of the value of file ownership and permission separation. If a network-facing service runs as a dedicated nonzero UID, its compromise can be contained by ordinary access checks. If it runs with broad administrative authority, the same compromise may reach files and processes across the machine.
Root is not a person standing outside the process model. A root shell and a root service are processes whose credentials carry privileged authority.
Every ordinary Unix filesystem object has an owning UID, an owning GID, and a set of mode bits.
Run ls -l to see a human-readable form:
The important fields are:
| Field | Value | Meaning |
|---|---|---|
-rw-r----- | Mode and object type | A regular file with the permissions shown |
root | Owning user | |
payments | Owning group | |
db.conf | Name |
The first character is the object type. A hyphen indicates a regular file, while d indicates a directory.
The next nine characters are three permission classes:
Owner permissions apply when the process's filesystem UID matches the file's owning UID.
Group permissions apply when the process did not match the owner and one of its relevant group IDs matches the file's owning GID.
Other permissions apply when neither the owner nor group class matched.
The three permission characters in each class are read (r), write (w), and execute (x). A hyphen means that permission is absent.
For db.conf:
Ownership names are display conveniences. Use ls -ln or numeric stat output when the exact UID and GID matter.
The owner, group, and other permissions are not combined. The kernel selects one applicable class and tests the requested operation against that class.
A simplified file-access decision is:
This order leads to an important result. Suppose a file has:
The owner has no permissions, the group has read permission, and other has none. If the owner is also a member of the file's group, the owner is still denied read access. The owner class matched first, so the kernel does not fall through to the more permissive group class.
Similarly, a matching group member uses the group bits even if the other bits are more permissive.
Processes with system-wide administrative authority can override many ordinary mode-bit checks. That authority is not a reason to ignore file modes: services should run with narrowly scoped identities so ordinary checks meaningfully contain them.
Loading simulation...
The letters r, w, and x have different meanings for regular files and directories.
| Permission | On a regular file | On a directory |
|---|---|---|
Read (r) | Read file contents | List entry names |
Write (w) | Modify or truncate file contents | Create, remove, or rename directory entries |
Execute (x) | Execute the file as a program | Search or traverse the directory |
For a regular file, read permission controls access to its bytes. Write permission allows changing those bytes, including truncating the file. Execute permission allows asking the operating system to execute the file as a program, subject to the file format and other system rules.
Directory permissions operate on the namespace.
Read permission on a directory allows listing the names it contains. It does not by itself grant access to the objects named there.
Execute permission on a directory is often called search permission. It allows pathname lookup to pass through the directory and reach a named entry. A process needs search permission on every directory component in a pathname.
Write permission on a directory controls changes to its entries. In practice, useful creation, removal, and rename operations generally require both write and execute permission on the directory.
This distinction is one of the most important parts of the Unix model.
To open:
the process must search each directory in sequence:
Every directory on this path needs search permission. A correct mode on db.conf is not enough if one directory above it denies traversal.
Having read permission on db.conf is not sufficient. The process also needs execute permission on /, /srv, /srv/payments, and /srv/payments/config.
This explains a common failure:
The file appears readable by the payments group, but the service receives Permission denied. One parent directory may be missing group execute permission.
The namei command is useful for inspecting every component:
It displays the ownership and mode of each directory and the final object, making a broken traversal path easier to find than repeated ls commands.
Directory read and execute can be granted separately. With read but not execute, a process may be able to list entry names but cannot inspect or open those entries normally. With execute but not read, a process may access a known name if the final object's permissions allow it, but cannot list the directory to discover names.
Writing a file changes the file's contents. Deleting a name changes the containing directory.
Suppose a process owns report.txt and can write it, but cannot write the containing directory. The process can modify the file but cannot necessarily remove its name.
The reverse can also occur. A process with write and execute permission on a directory may be able to remove a file in that directory even if it cannot write the file's contents.
The reason is structural:
Rename is also primarily a directory operation. Moving a name can require write and execute permissions on the relevant source and destination directories.
This behavior is especially important for log, upload, and temporary directories. Protecting a file from content modification does not automatically protect its name from removal or replacement by a process that can modify the directory.
A symbolic link contains a pathname that is resolved when the link is followed. On Linux, the displayed read, write, and execute bits of the symbolic link itself do not normally control access to the target.
The kernel checks search permission on directories traversed while resolving the path and then checks the permissions of the target object for the requested operation.
For example:
Opening /srv/current-config does not bypass the permissions on /etc/payments/db.conf. The process still needs a searchable path to the target and appropriate access to the target file.
Operations that manipulate the link as a directory entry depend on the containing directory. This follows the same principle as deletion and rename: changing a name is different from changing the object reached through that name.
Each permission has a numeric value:
Add the values within each class. For example:
The complete mode is 640, often written 0640 when code needs to make the octal base explicit.
Common examples include:
chmod changes mode bits. It accepts numeric notation:
It also accepts symbolic notation:
In symbolic notation, u means owner, g means group, o means other, and a means all three classes.
Use stat when the exact mode and ownership matter:
Example output:
chmod changes permission bits; it does not change the owning UID or GID.
chown changes ownership:
chgrp changes only the group:
Ordinary file owners can generally change the mode of their files. Changing a file's owning user is more restricted because it could transfer ownership, accounting, and control in unsafe ways. The exact ownership changes allowed to an unprivileged process depend on the operating system and filesystem rules.
When a process creates a file, the file's owner is normally derived from the process's effective filesystem identity. The new file's group is commonly derived from the process or inherited from the containing directory, depending on that directory's configuration.
Ownership and permissions must be interpreted together. The string rw-r----- does not identify who receives those rights until the owning UID and GID are known.
umaskPrograms specify a requested mode when they create files and directories. The process's file mode creation mask, commonly called the umask, removes selected permission bits from that request.
The basic calculation is:
Ordinary programs commonly request 0666 for new regular files:
They omit execute permission because creating a data file should not make it executable automatically.
Programs commonly request 0777 for new directories because directories need execute permission to be traversable.
With a umask of 0027:
The results are:
Inspect the current shell's umask with:
Set it for the shell and subsequently launched processes with:
The umask is inherited by child processes. Service managers and application runtimes may set it explicitly, so a service does not necessarily use the same mask as an interactive shell.
A umask only removes bits from the mode requested during creation. It does not add permissions the program did not request, and changing it does not retroactively modify existing objects.
For example, a umask of 0000 does not make a newly created regular data file executable if the program requested 0666. There were no execute bits for the mask to preserve.
Three additional mode bits modify execution or directory behavior: set-user-ID, set-group-ID, and the sticky bit.
When an executable has the set-user-ID, or setuid, bit, executing it can set the new process's effective UID to the executable file's owner rather than leaving it equal to the caller's UID.
Conceptually:
| UID | Before execve | After executing the setuid file |
|---|---|---|
| Real | 1000 | 1000 |
| Effective | 1000 | 0 |
| Saved | 1000 | 0 |
The real UID is unchanged, so the kernel still knows who started the program. Only the effective and saved UIDs carry the elevated identity.
The real UID preserves the caller's identity. The effective and saved IDs let the program perform carefully limited work using the file owner's authority and, when designed correctly, drop and regain that effective authority as needed.
The setuid bit appears as s in the owner's execute position:
Its numeric value is placed before the ordinary three digits:
A setuid executable is a sensitive trust boundary. Every input and operation reachable while it has elevated authority must be handled carefully. Many systems also restrict where setuid behavior is honored; it should not be assumed to work for arbitrary scripts or on every mounted filesystem.
On an executable, the set-group-ID, or setgid, bit can set the process's effective group identity from the file's owning group.
On a directory, setgid has a different and very practical role: newly created objects inherit the directory's group. New subdirectories commonly inherit the setgid behavior as well.
This helps a shared service directory keep consistent group ownership:
The leading 2 sets the setgid bit. ls -ld displays the directory with an s in the group execute position:
Files created there can inherit the payment-logs group instead of whichever primary group the creating process would otherwise use.
A world-writable directory creates a deletion problem. If directory write permission were the only rule, every user could remove every other user's entries.
The sticky bit restricts removal and rename inside such a directory. In the common Linux case, an entry can be removed or renamed by the file owner, the directory owner, or a sufficiently privileged process.
The standard example is /tmp:
The t in the other execute position shows the sticky bit. Its numeric form is:
All users can create entries in /tmp, but they cannot ordinarily delete one another's files merely because the directory is writable.
An uppercase S or T in ls output means the special bit is set while the corresponding execute bit is absent. This is often a sign that the mode deserves closer inspection.
open()When a process opens a file, the kernel resolves the pathname, checks the relevant permissions, and if allowed returns a file descriptor.
A pathname and a requested operation go through the ownership and mode check, and if allowed the caller receives an open file descriptor.
The descriptor represents an already established relationship with the open file. Changing the pathname's mode later does not normally revoke read or write access through descriptors that are already open.
For example:
Removing the file's name also does not invalidate an existing open descriptor. The process can continue using the open file object even though new pathname lookup can no longer find that directory entry.
This behavior is useful for stable I/O, but it matters operationally. Tightening file permissions is not necessarily an immediate revocation mechanism for running processes. Restarting or otherwise causing a service to close existing descriptors may be necessary when access must actually end.
Consider a payment service running as user payments with primary group payments. A log collector runs as log-agent and belongs to the supplementary group payment-logs.
The database configuration could be owned by an administrator and readable by the service group:
Mode 0640 means:
The parent directory must also be searchable by the service:
For logs, the service must create and modify files while the collector should only read them. A directory owned by the service and grouped for readers can use setgid:
If the service uses umask 0027 and requests mode 0666 for new logs, those logs are created as:
The pieces work together:
| Mechanism | Effect on the new log file |
|---|---|
| The service UID owns the log | Owner rw- lets the service append |
| The directory is setgid | The new file inherits the payment-logs group |
umask 0027 | Group gets r--, and other gets --- |
The log collector's membership in payment-logs gives it read access without making it the file owner or giving it write permission. An unrelated process matches neither owner nor group and receives the other permissions, which are empty.
This design still depends on every parent directory being traversable, the service actually running with the intended credentials, and the log collector having refreshed group membership. Inspect all three when a deployment behaves differently from the mode string you expected.
Permission deniedWhen a Linux operation returns EACCES or reports Permission denied, inspect the complete decision instead of changing the mode blindly.
Start with the process identity:
Then inspect the object:
For pathname problems, inspect every component:
Finally, identify the requested operation. Reading file contents, creating a new file, replacing a name, and traversing a directory require different permissions.
A disciplined diagnosis asks:
Avoid fixing the symptom with chmod 777. That grants every local identity read, write, and execute/search permission and often creates a larger security problem. The goal is to find the missing relationship and grant only the access the workload requires.
Linux protection checks use numeric identities carried by processes. Usernames and group names are human-readable mappings for UIDs and GIDs.
A process has real, effective, and saved identities plus supplementary groups. Its effective filesystem identity and group set determine which file permission class applies.
Every filesystem object has an owner, group, and mode. The kernel selects exactly one of the owner, group, or other classes rather than combining them.
Read, write, and execute have different meanings for files and directories. Path traversal requires execute permission on every directory, while deletion and rename depend mainly on permissions on containing directories.
chmod, chown, and chgrp manage existing metadata. A process's umask removes permission bits when new objects are created. Setuid, setgid, and sticky bits provide specialized execution and directory behavior.
Correct permission debugging starts with the requesting process's credentials, the complete pathname, and the exact operation, not with making the object accessible to everyone.
5 quizzes