For the complete documentation index, see llms.txt. This page is also available as Markdown.

14.2 Permissions

The file system permission mechanism is the cornerstone of the security architecture of UNIX-like systems.

In FreeBSD, each file and directory is associated with a set of permissions, and there are various tools available for viewing and modifying these permissions.

Permissions determine which files a user can access, and whether they can modify or execute files that do not belong to them.

Basic UNIX permissions are assigned using three access types: read, write, and execute. These access types are used to determine the file access permissions for the file owner, group, and other users.

This section discusses the traditional UNIX permissions used in FreeBSD and does not cover fine-grained permissions.

Discretionary Access Control (DAC) Model

The traditional UNIX permission model adopted by FreeBSD falls under the category of Discretionary Access Control (DAC). The core characteristic of DAC is that the owner of an object has the authority to independently decide whether to grant access permissions to other subjects or revoke their access permissions. In UNIX systems, the owner of a file can modify the file's permission bits to determine other users' access capabilities to the file. Since access control decisions are made by the object owner rather than centrally by the system administrator, this model is called "discretionary."

The theoretical foundation of the DAC model can be traced back to multi-user operating system security research in the 1960s-1970s. The Trusted Computer System Evaluation Criteria (TCSEC, also known as the "Orange Book") published by the U.S. Department of Defense in 1985 defines DAC as: an access control mechanism that limits access to objects based on the identity of subjects and/or the groups to which they belong, where the owner of an object can specify or modify access permissions (Source: Department of Defense. DoD 5200.28-STD: Trusted Computer System Evaluation Criteria[S]. Washington, D.C.: Department of Defense, 1985.).

The traditional UNIX DAC model has the following characteristics and limitations:

  • Coarse-grained control: Only distinguishes between three categories of subjects — owner, owning group, and other users — and cannot grant fine-grained permissions to individual users or multiple groups.

  • Permission propagation: File owners can grant permissions to any user, including granting write permissions to untrusted users, which may lead to permission leakage.

  • Superuser bypass: The root user is not constrained by DAC and can access any file. While this design provides administrative convenience, it also constitutes a potential security risk; once root privileges are illegally obtained, the entire system's security defense line collapses.

To compensate for the shortcomings of the traditional DAC model, FreeBSD provides the following enhancement mechanisms:

  • Access Control Lists (ACL): The ACL mechanism based on the POSIX.1e draft standard allows setting fine-grained access permissions for individual users or groups, breaking through the limitation of traditional DAC that only supports three categories of subjects.

  • Mandatory Access Control (MAC): The TrustedBSD MAC framework provides policy-based mandatory access control capabilities, including the Biba integrity model, MLS multi-level security model, and others. Unlike DAC, MAC access control decisions are enforced by system security policies, and object owners cannot modify them on their own.

  • File Flags: FreeBSD supports setting file flags via chflags(1), such as schg (system immutable flag) and sappnd (system append-only flag); even the root user cannot modify files with these flags (unless the flags are removed first). File flags add an additional layer of security and control; even root cannot delete or modify these files.

  • Capsicum Sandbox Framework: Limits the range of system resources accessible to a process through the capability mechanism, implementing the principle of least privilege.

Permission Expressions

Observe the following ls command output example to understand the composition of permission expressions:

Observe again:

FreeBSD file access permissions can be represented using 10 flag bits (the reader should verify whether the first column is 10 characters), and these 10 flag bits consist of 4 parts:

Separated by slashes: -/---/---/---

The first part is bit 1, representing the file type / device type:

Character
Type

d

Directory

-

Regular file

l

Symbolic link file

b

Block device file

p

Pipe file

c

Character device file

s

Socket file

The second part consists of bits 2, 3, and 4, representing the file owner's access permissions to the file, using rwx to denote read, write, and execute permissions (for directories, this means access permissions, such as ls, cd); no permission is written as -.

The third part consists of bits 5, 6, and 7, identifying the access permissions of the file's group members to the file.

The fourth part consists of bits 8, 9, and 10, identifying the access permissions of other users to the file.

Read, write, and execute permissions, in addition to being represented by rwx, can also correspond to the numbers 4, 2, and 1 respectively; no permission is 0. The numbers corresponding to each three-bit permission are added together and combined to form a 3-digit numeric representation (mnemonic: "read 4 write 2 execute 1").

The possible numeric and alphabetic combinations are as follows.

UNIX Permissions

Value
Permission Description
Directory Listing

0

No read, no write, no execute permission

---

1

No read, no write, execute permission

--x

2

No read, write, no execute permission

-w-

3

No read, write, execute permission

-wx

4

Read, no write, no execute permission

r--

5

Read, no write, execute permission

r-x

6

Read, write, no execute permission

rw-

7

Read, write, execute permission

rwx

The code for the above numbers representing permissions can be found in main/sys/sys/stat.h, defined by the IEEE Std 1003.1 specification:

The mechanism by which the system controls device permissions is based on the traditional UNIX philosophy of "everything is a file," which treats most hardware devices as files that programs can open, read, and write. These special device files are stored in the /dev/ directory.

A typical /dev/ directory:

Thought Question

drw-------, i.e., 600, this is a directory where only the owning user can read and write.

Is this statement correct? Is it meaningful? Why? How do you understand this design paradox?

Directories are also treated as files, handled the same way, and also have read, write, and execute permissions. The execute bit for directories is slightly different from files; when a directory is set as executable, it means the cd command can be used to enter that directory.

This also means that to access files in that directory, the prerequisite is that the file's own permissions allow access:

To list the contents of a directory, the directory must have read permission. To delete a file with a known name, you must have both write and execute permissions on the directory containing the file.

There are also additional permission bits, primarily used for special scenarios, such as setuid executable files and sticky directories.

Symbolic Permissions

Symbolic permissions use characters rather than octal values to assign permissions to files or directories. The syntax is (target)(operator)(permission), where the available values are as follows:

Option
Letter
Represents

Target

u

User

Target

g

Group owner

Target

o

Other

Target

a

All (ALL)

Operator

+

Add permission

Operator

-

Remove permission

Operator

=

Explicitly set permission

Permission

r

Read

Permission

w

Write

Permission

x

Execute

Permission

t

Sticky bit

Permission

s

Set UID or GID

Note

The t (sticky bit) in chmod(1) symbolic mode is marked as an XSI extension option in the POSIX standard, not a POSIX base requirement. When operating s (set UID or GID) or t flag (sticky bit) on the "other" permission (o) alone, it will be ignored. To set the sticky bit, use +t or a+t, not o+t.

These values are used with chmod(1), but using letters instead of numbers. For example, the following command will prevent group members and all other users associated with test from accessing test:

Here, "go" does not mean "go forward," but rather "group owner" and "other." And the space after the equals sign is necessary.

When multiple changes to file permissions are needed, a comma-separated list can be provided. For example, the following command removes group and "world" write permissions from test and adds execute permission for all users:

Tip

The X permission in chmod(1) symbolic mode only sets the execute bit when the file is already executable or is a directory, commonly used in chmod =rw,+X to preserve existing execute permissions.

Operator Method

Grant execute permission to all users for the test.sh script:

In a+x:

  • a indicates that the operation target is all users. Other options include: u for the owning user, g for the owning group, o for other users; if omitted, the system default target is used;

  • + is the operator, meaning to add permission; the alternative - removes permission;

  • rwx is the permission mode. r means read, w means write, x means execute. The option s means setting the process owner or group to the file's owner or group when the file is executed.

Numeric Method

Set the permissions of test.sh to owner read/write/execute, group users read/execute, other users no permission:

Where:

  • 7: The owning user has read, write, and execute permissions

  • 5: Group users have read and execute permissions

  • 0: Other users have no permissions

Here, the -R option can recursively modify permissions.

Example:

umask: File Creation Mask

In UNIX systems, when a process calls system calls such as open(2) or mkdir(2) to create a file or directory, the kernel uses the file creation mask (umask) to determine the final permissions. umask is a process-level attribute that specifies which permission bits should be masked (removed) during file creation. The umask mechanism ensures that newly created files do not accidentally have overly permissive permissions.

In fact, the permission values are octal, not decimal. Converting octal 755 to binary yields: 111 101 101. The specific calculation process:

Tip

Generally, any non-zero number raised to the power of 0 equals 1.

Specifically, the initial permissions of a new file are determined by performing a bitwise AND operation between the mode parameter in the creation request and the negated umask:

Tip

Bitwise AND &:

Bitwise NOT:

Bit 1
Bit 2
Result

1

1

1

1

0

0

0

1

0

0

0

0

For example, when a process requests to create a file with mode 0666 (read/write for all users) and the current umask is 0022:

The final permissions are 0666 & ~0022 = 0644 (owner read/write, group and other users read-only).

For directories, the typical request mode is 0777, which with a umask of 0022 results in 0755.

FreeBSD's default umask is 0022, which can be viewed and set via the umask(2) system call or the shell built-in umask command:

umask Value
New File Permissions (request 0666)
New Directory Permissions (request 0777)
Notes

0022

0644 (rw-r--r--)

0755 (rwxr-xr-x)

Default value

0027

0640 (rw-r-----)

0750 (rwxr-x---)

More restrictive, prevents other users from accessing

0077

0600 (rw-------)

0700 (rwx------)

Most restrictive, only owner can access

umask is set in shell startup files (such as ~/.profile or ~/.cshrc). In csh/tcsh, use umask 022; in sh/bash, also use umask 022.

Note

  • umask only affects the initial permissions at file creation time and does not modify the permissions of existing files.

  • In Jail containers, umask is inherited from the host system's process environment and is not directly affected by Jail configuration.

The chown Command

The chown(8) command is used to change the owner of a file, including the owning user and owning group. Only root can change the owner of a file.

Note

Symbolic notation: user:group modifies both owner and group simultaneously; :group modifies only the group; user: modifies the owner and sets the group to the specified user's login group.

Example: Change the owner of t.sh to test1.

Example: Change the owner of t.sh to user test1 and group test.

Example: Change the owner of all files under the /tmp directory to user test1 and group test.

Here, the -R option can recursively modify the owner.

Special Permission Bits: setuid, setgid, and sticky bit

In addition to the basic read, write, and execute permissions, UNIX systems also define three special permission bits that provide functionality not normally granted to regular users. To understand these permission bits, it is necessary to note the distinction between the real user ID and the effective user ID: the real user ID is the UID of the user who owns or started the process, while the effective UID is the UID under which the process runs.

setuid (Set User ID)

When an executable file has the setuid bit set, regardless of which user executes the file, the process's effective user ID (EUID) will be set to the UID of the file owner, rather than the executor's UID. This mechanism allows regular users to temporarily obtain the file owner's permissions to perform specific operations.

The setuid bit is represented in the permission expression by an s in the owner execute position (e.g., -r-sr-xr-x).

The most typical application scenario for setuid is the passwd(1) command.

When a regular user changes their password, they need to update the /etc/master.passwd file, which only root has write permission to. Through the setuid mechanism, the passwd command runs with root's EUID, thereby gaining permission to modify the password file. passwd(1) runs with the regular user's real user ID, but to update the password database, the command runs with root's effective ID, allowing the user to change their password without encountering a "Permission Denied" error.

In numeric notation, the setuid bit is indicated by prefixing the three-digit permission code with 4 (e.g., 4555). Use the following command to display full permissions (including type and special bits, in octal):

Warning

The setuid mechanism is a double-edged sword: if a setuid program has security vulnerabilities, attackers may be able to exploit the program to execute arbitrary code with the file owner's (typically root) privileges. Therefore, system administrators should minimize the number of setuid programs and regularly audit setuid files on the system. The nosuid mount option causes the kernel to ignore the setuid/setgid bits of executable files on the file system; programs can still run but will not gain privilege escalation.

setgid (Set Group ID)

setgid works similarly to setuid, but applies to groups rather than users. When an executable file has the setgid bit set, the process's effective group ID (EGID) will be set to the GID of the file's owning group. When a directory has the setgid bit set, new files created in that directory will inherit the directory's owning group rather than the creator's primary group.

The setgid bit is represented in the permission expression by an s in the group execute position (e.g., -r-xr-sr-x):

In numeric notation, the setgid bit is indicated by prefixing the three-digit permission code with 2 (e.g., 2555):

sticky bit

The sticky bit was originally used to "stick" the code segment of executable files in swap space to improve repeated launch speed, but this use is no longer applicable in modern systems. Currently, the sticky bit is primarily used for directories: when a directory has the sticky bit set, only the file owner (or root) can delete or rename files in that directory, even if other users have write permission to the directory.

The most common application scenario for the sticky bit is the /tmp directory. This directory has write permissions open to all users, but the sticky bit prevents users from deleting other people's temporary files. When a directory has the sticky bit set, only the file owner can delete files in that directory, which is very useful for preventing non-owners from deleting files in public directories.

The sticky bit is represented in the permission expression by a t in the other users execute position (e.g., drwxrwxrwt):

In numeric notation, the sticky bit is indicated by prefixing the three-digit permission code with 1 (e.g., 1777):

FreeBSD File Flags

In addition to file permissions, FreeBSD also supports the use of "file flags." These flags add an additional layer of security and control, applicable to both files and directories; through file flags, even root can be prevented from deleting or modifying files.

Commonly used flags are as follows:

Flag
Description
Notes

schg / schange / simmutable

System immutable flag

Only superuser can set; cannot be cleared when securelevel is greater than 0

sappnd / sappend

System append-only flag

Only superuser can set; also subject to securelevel restrictions

uchg / uchange / uimmutable

User immutable flag

File owner or superuser can set; not subject to securelevel restrictions

uappnd / uappend

User append-only flag

File owner or superuser can set

nodump

No dump flag

File owner or superuser can set; dump(8) will skip such files

sunlnk / sunlink

System undeletable flag

Only superuser can set

uunlnk / uunlink

User undeletable flag

File owner or superuser can set

Note

  • Prefixing a flag with no clears that flag, e.g., nouchg clears the user immutable flag.

  • The ability to modify certain flags depends on the current kernel securelevel; see security(7).

  • Only a limited number of tools support chflags, including ls(1), cp(1), find(1), install(1), dump(8), restore(8); currently pax(1) does not support chflags.

Use chflags(1) to modify file flags. chflags(1) first appeared in 4.4BSD and is a BSD-specific feature; Linux does not natively support it (similar functionality is achieved through the chattr and lsattr commands). To view a file's flags, use the -lo option of ls(1):

Note

If using the ZFS file system, all files may have the archive flag uarch, which is expected behavior.

For example, to enable the system undeletable flag on the file test, execute the following command:

After execution, view the file:

To disable the system undeletable flag, add "no" before sunlink:

File flags can typically only be added or removed by the root user:

Otherwise, the system will return an operation not permitted error.

References

Exercises

  1. Review the core source code for permission checking in the FreeBSD kernel and try to make it more fine-grained.

  2. Modify the default permission configuration of a system directory (such as /var/tmp) and record the impact of the modification on file creation and process access behavior under that directory.

  3. The Unix rwx three-level permission model has been in use since the 1970s. Compare the expressiveness of this model with SELinux's Type Enforcement and AppArmor's pathname restrictions, and analyze whether mixing three mechanisms on a single system would introduce policy conflicts.

Last updated