Skip to content

Complete Guide to Linux File Ownership and Permission Design

Complete Guide to Linux File Ownership and Permission Design

Section titled “Complete Guide to Linux File Ownership and Permission Design”

In Linux, every file and directory has an owner, an owning group, and a set of permissions. Designing secure access rights requires more than understanding the numeric values used with chmod. You also need to understand how rwx differs between files and directories, along with umask, setuid, setgid, and the sticky bit.

In this article, we will create test users and files on a RHEL-compatible Linux system to verify ownership, standard permissions, special bits, shared directory design, and methods for troubleshooting access-denied errors. Run these procedures in a disposable test environment so that existing users and production data are not affected.

Linux has three basic access permissions: r, w, and x.

PermissionNumeric valueFileDirectory
r4Read contentsList the names of entries inside
w2Modify contentsCreate and delete files inside
x1ExecuteTraverse the directory and access entries inside

Permissions are divided into three classes.

  • u: owner
  • g: owning group
  • o: other users

For example, 640 means the owner has rw-, the owning group has r--, and other users have ---.

When a resource is accessed, owner permissions apply if the executing user is the owner. If the user belongs to the owning group or a matching supplementary group, group permissions apply. Otherwise, permissions for other users are used.

Prepare an owner, a user who belongs to the same group, and a user outside the group so that their behavior can be compared.

Terminal window
sudo groupadd -f examplegrp
sudo id exampleowner >/dev/null 2>&1 || sudo useradd -m exampleowner
sudo id examplemember >/dev/null 2>&1 || sudo useradd -m examplemember
sudo id exampleother >/dev/null 2>&1 || sudo useradd -m exampleother
sudo usermod -aG examplegrp exampleowner
sudo usermod -aG examplegrp examplemember
sudo mkdir -p /srv/example-permissions
sudo touch /srv/example-permissions/file.txt
sudo chown -R exampleowner:examplegrp /srv/example-permissions
test "$(sudo stat -c '%U:%G' /srv/example-permissions)" = "exampleowner:examplegrp"
test "$(sudo stat -c '%U:%G' /srv/example-permissions/file.txt)" = "exampleowner:examplegrp"

exampleowner and examplemember belong to examplegrp, while exampleother is not added to that group. With chown, you can set file and directory ownership in the form owner:owning group.

Step 2: Set basic permissions using symbolic and numeric notation

Section titled “Step 2: Set basic permissions using symbolic and numeric notation”

chmod supports both symbolic notation such as u=rw,g=r,o= and numeric notation such as 640.

In the following example, rw-r----- is first set using symbolic notation, and then the same permissions are applied using numeric notation.

Terminal window
sudo chown exampleowner:examplegrp /srv/example-permissions/file.txt
sudo chmod u=rw,g=r,o= /srv/example-permissions/file.txt
test "$(sudo stat -c '%a' /srv/example-permissions/file.txt)" = "640"
sudo stat -c '%U %G %A %a %n' /srv/example-permissions/file.txt
sudo chmod 640 /srv/example-permissions/file.txt
test "$(sudo stat -c '%U:%G:%a' /srv/example-permissions/file.txt)" = "exampleowner:examplegrp:640"

In numeric notation, the values r=4, w=2, and x=1 are added together.

  • 7: rwx
  • 6: rw-
  • 5: r-x
  • 4: r--

Using stat, you can check the owner, owning group, symbolic notation, and octal notation together.

For a file with permissions 640, the owner can read and write. Members of the owning group can only read. Other users have no permissions.

Terminal window
sudo -u exampleowner sh -c 'echo owner-write >> /srv/example-permissions/file.txt'
sudo -u examplemember cat /srv/example-permissions/file.txt >/dev/null
if sudo -u examplemember sh -c 'echo member-write >> /srv/example-permissions/file.txt'; then
echo 'unexpected: group write succeeded' >&2
exit 1
else
echo 'group write is correctly denied'
fi
if sudo -u exampleother cat /srv/example-permissions/file.txt >/dev/null; then
echo 'unexpected: other read succeeded' >&2
exit 1
else
echo 'other read is correctly denied'
fi

With this configuration, users in the same group can read the contents but cannot modify them. The basic principle is to allow only the operations that are actually required.

Step 4: Verify the differences between r, w, and x on directories

Section titled “Step 4: Verify the differences between r, w, and x on directories”

For directories, the same rwx permissions have different meanings than they do for files.

  • r: list the names of entries in the directory
  • w: create and delete files or directories inside it
  • x: traverse the path and access entries inside it

The x permission is especially important. Even if the file itself has read permission, it cannot be accessed unless its parent directory can be traversed.

Terminal window
sudo mkdir -p /srv/example-dir/read-only /srv/example-dir/search-only /srv/example-dir/write-search
sudo touch /srv/example-dir/read-only/item.txt /srv/example-dir/search-only/item.txt
sudo chown -R exampleowner:examplegrp /srv/example-dir
sudo chmod 740 /srv/example-dir/read-only
sudo chmod 710 /srv/example-dir/search-only
sudo chmod 730 /srv/example-dir/write-search
sudo -u examplemember ls /srv/example-dir/read-only >/dev/null
if sudo -u examplemember cat /srv/example-dir/read-only/item.txt >/dev/null; then
echo 'unexpected: read without directory execute succeeded' >&2
exit 1
else
echo 'directory execute is required for path traversal'
fi
sudo -u examplemember cat /srv/example-dir/search-only/item.txt >/dev/null
if sudo -u examplemember ls /srv/example-dir/search-only >/dev/null 2>&1; then
echo 'unexpected: listing without directory read succeeded' >&2
exit 1
else
echo 'directory read is required for listing'
fi
sudo -u examplemember touch /srv/example-dir/write-search/new.txt
sudo -u examplemember rm /srv/example-dir/write-search/new.txt

If a directory has both w and x, files can be created and deleted inside it. Therefore, when investigating whether a file can be deleted, you need to check not only the target file but also the permissions on the parent directory.

Step 5: Control initial permissions for new files with umask

Section titled “Step 5: Control initial permissions for new files with umask”

umask specifies which permissions are not granted to newly created files and directories.

Common base values are:

  • regular file: 666
  • directory: 777

For example, with umask 027, a regular file receives 640 and a directory receives 750.

Terminal window
sudo rm -f /tmp/example-umask-file
sudo rm -rf /tmp/example-umask-dir
sudo -u exampleowner sh -c 'umask 027; touch /tmp/example-umask-file; mkdir -p /tmp/example-umask-dir'
test "$(sudo stat -c '%a' /tmp/example-umask-file)" = "640"
test "$(sudo stat -c '%a' /tmp/example-umask-dir)" = "750"
sudo stat -c '%A %a %n' /tmp/example-umask-file /tmp/example-umask-dir

The base value for regular files is 666 so that newly created regular files do not automatically receive execute permission.

For shared directories, it is not enough to consider only the directory’s chmod setting. You should also verify whether users’ umask values remove group write permission from newly created files.

In addition to standard rwx permissions, Linux provides special bits.

Special bitNumeric valueMain purpose
setuid4000Behavior related to the effective user ID of an executable file
setgid2000Effective group ID of an executable file, or group inheritance on directories
sticky bit1000Restrict deletion and renaming in shared directories

In the following example, setuid and setgid are applied to a test executable, verified with stat, and then removed.

Terminal window
sudo cp /usr/bin/true /srv/example-permissions/setid-demo
sudo chown root:examplegrp /srv/example-permissions/setid-demo
sudo chmod 4750 /srv/example-permissions/setid-demo
sudo stat -c '%A %a %n' /srv/example-permissions/setid-demo
test "$(sudo stat -c '%a' /srv/example-permissions/setid-demo)" = "4750"
sudo chmod 2750 /srv/example-permissions/setid-demo
sudo stat -c '%A %a %n' /srv/example-permissions/setid-demo
test "$(sudo stat -c '%a' /srv/example-permissions/setid-demo)" = "2750"
sudo chmod 0750 /srv/example-permissions/setid-demo
test "$(sudo stat -c '%a' /srv/example-permissions/setid-demo)" = "750"

Special bits have a greater impact than ordinary access permissions and should only be set when there is a clear need. Executables with setuid are especially relevant to privilege elevation and should not be added casually.

Step 7: Design a shared directory with setgid

Section titled “Step 7: Design a shared directory with setgid”

When multiple users share the same directory, setting setgid on the directory helps keep group ownership consistent.

When setgid is set on a directory, new files and subdirectories created inside it inherit the group of the parent directory.

In the following example, we create a shared directory that members of examplegrp can use.

Terminal window
sudo mkdir -p /srv/example-share
sudo chown root:examplegrp /srv/example-share
sudo chmod 2770 /srv/example-share
sudo -u exampleowner sh -c 'umask 007; echo shared-data > /srv/example-share/owner.txt'
sudo -u examplemember sh -c 'umask 007; echo member-data > /srv/example-share/member.txt'
test "$(sudo stat -c '%G' /srv/example-share/owner.txt)" = "examplegrp"
test "$(sudo stat -c '%G' /srv/example-share/member.txt)" = "examplegrp"
test "$(sudo stat -c '%a' /srv/example-share/owner.txt)" = "660"
test "$(sudo stat -c '%a' /srv/example-share/member.txt)" = "660"
sudo stat -c '%A %a %U %G %n' /srv/example-share /srv/example-share/owner.txt /srv/example-share/member.txt

The leading 2 in 2770 represents setgid.

In this configuration, examplegrp is fixed as the owning group of the directory, and umask 007 causes new files to receive permissions equivalent to 660. Users outside the group receive no access rights.

When collaborative editing is required, combining a dedicated group, setgid, and an appropriate umask is safer than simply broadening permissions with chmod 777.

Step 8: Restrict deletion in shared directories with the sticky bit

Section titled “Step 8: Restrict deletion in shared directories with the sticky bit”

If a directory writable by all users is simply set to 777, users may be able to delete files created by other users.

Setting the sticky bit restricts users from deleting or renaming files owned by other users, even in a writable shared directory.

Terminal window
sudo mkdir -p /srv/example-sticky
sudo chmod 1777 /srv/example-sticky
sudo -u exampleowner sh -c 'echo owner-data > /srv/example-sticky/owner.txt'
if sudo -u exampleother rm /srv/example-sticky/owner.txt; then
echo 'unexpected: non-owner removed another user file' >&2
exit 1
else
echo 'sticky bit prevented non-owner deletion'
fi
sudo -u exampleowner rm /srv/example-sticky/owner.txt
test ! -e /srv/example-sticky/owner.txt

The leading 1 in 1777 represents the sticky bit. A typical example is /tmp.

The sticky bit does not control reading or writing file contents. It mainly controls deletion and renaming inside shared directories.

When access is denied, changing only the target file’s chmod is risky. First, check the following in order:

  1. the file owner and owning group
  2. the file’s rwx permissions
  3. x permissions along the entire path, including parent directories
  4. umask if the issue involves a newly created file
  5. special bits such as setuid, setgid, and sticky bit

The following example reproduces a situation where the file’s group read permission is correct, but the group does not have execute permission on the parent directory. It then adds only the permission that is actually required.

Terminal window
sudo mkdir -p /srv/example-troubleshoot
sudo sh -c 'echo diagnostic-data > /srv/example-troubleshoot/data.txt'
sudo chown root:examplegrp /srv/example-troubleshoot /srv/example-troubleshoot/data.txt
sudo chmod 740 /srv/example-troubleshoot
sudo chmod 640 /srv/example-troubleshoot/data.txt
if sudo -u examplemember cat /srv/example-troubleshoot/data.txt >/dev/null; then
echo 'unexpected: access succeeded before repair' >&2
exit 1
else
echo 'access denial reproduced'
fi
sudo stat -c '%A %a %U %G %n' /srv /srv/example-troubleshoot /srv/example-troubleshoot/data.txt
sudo -u examplemember sh -c 'umask'
sudo stat -c '%A %a %n' /srv/example-troubleshoot /srv/example-troubleshoot/data.txt
sudo chmod 750 /srv/example-troubleshoot
sudo -u examplemember cat /srv/example-troubleshoot/data.txt >/dev/null
echo 'access restored with group execute on the parent directory'

In this example, the file with permissions 640 is readable by the group, but the parent directory with permissions 740 does not grant x to the group. Changing it to 750 adds only the group traversal permission that is needed. This restores access without expanding permissions for other users.

If standard ownership and permissions are correct, other access-control mechanisms such as POSIX ACLs or SELinux may also be involved. When diagnosing the cause, it is important not to mix these control layers, but to check them separately and in order.

Do not broaden access rights until something works. Instead, allow only the operations required by the users or groups that actually need them.

  • Grant personal files only the permissions required by the owner
  • Use dedicated groups for collaboration
  • Use setgid on shared directories to keep group inheritance consistent
  • Include umask when designing permissions for newly created files
  • Consider the sticky bit for shared writable areas used by unrestricted users
  • Do not casually use 777 or 666 as a quick fix for permission problems
  • Limit executables with setuid or setgid to the minimum necessary

When designing Linux access rights, you need to consider the owner, owning group, the different meanings of rwx for files and directories, umask, and special bits together.

For shared directories in particular, combining a dedicated group, setgid, and an appropriate umask makes collaboration easier without granting excessive permissions.

When troubleshooting permission problems, do not look only at the affected file. Check the entire path, including parent directories, and keep changes to the minimum required.

Category: Linux