Learning Record learning from practice

· security

chmod vs. setfacl: Where Linux File Permissions Live

chmod vs. setfacl: Where Linux File Permissions Live

chmod and setfacl both change a file’s permission metadata. The difference is where that metadata is stored and how expressive the rules can be.

In short:

chmod edits three fixed permission classes in the inode’s mode; setfacl adds POSIX ACL entries in extended attributes, so you can grant access to specific extra users and groups.

Throughout this post, alice is the file owner, appuser is an additional service account, devs is an additional group, and shared/ is a placeholder directory. Substitute your own names.

chmod: Permission Bits in the Inode

Every file on Linux has an inode. Among other metadata, it records the owner (UID), the group (GID), and a mode field. chmod changes the permission bits in that mode:

-rw-r-----
 │  │  └── other: everyone else
 │  └───── group: members of the file's group
 └──────── owner: the file's owner

Each class has three bits: r=4, w=2, and x=1. So chmod 640 file means the owner can read and write, the group can read, and everyone else has no access.

The kernel does not combine these classes. It selects exactly one class and checks only those bits:

  1. If the process’s effective (filesystem) UID equals the file owner, use the owner bits.
  2. Otherwise, if the file’s GID equals the process’s effective GID or one of its supplementary GIDs, use the group bits.
  3. Otherwise, use the other bits.

A privileged process can bypass these checks. On Linux, root’s power is split into capabilities: CAP_DAC_OVERRIDE overrides the permission checks, but it grants execute permission only if at least one of the file’s execute bits is set. CAP_DAC_READ_SEARCH grants read access to files and read/search access to directories.

The three classes are fixed. The mode alone cannot express a rule such as “also let appuser read this file” without changing the file’s owner or group, or opening access to other.

setfacl: ACLs in Extended Attributes

POSIX ACLs extend the mode. On Linux they are stored in an extended attribute (xattr) named system.posix_acl_access. The filesystem must support ACLs:

  • ext4 supports POSIX ACLs through the acl / noacl mount options. Many defaults come from the filesystem superblock (set with tune2fs).
  • XFS supports them when the kernel is built with XFS_POSIX_ACL.
  • Btrfs enables the acl mount option by default, if ACL support was compiled in.

Install the tools first. On Debian, the acl package provides getfacl and setfacl:

sudo apt install acl

Grant one extra user read access:

setfacl -m u:appuser:r shared/model.bin
getfacl shared/model.bin

A typical getfacl result with both a named user and a named group looks like this:

# file: shared/model.bin
# owner: alice
# group: alice
user::rw-          # owner
user:appuser:r--   # named user
group::r--         # owning group
group:devs:r-x     # named group
mask::r-x          # upper limit for the group class
other::---         # everyone else

ACL Entry Types

Entry Meaning
user:: The file owner
user:NAME: A named user
group:: The file’s owning group
group:NAME: A named group
mask:: Maximum permissions for named users, the owning group, and named groups
other:: Everyone who matched no other entry

An ACL with any named user or named group entry must contain exactly one mask entry.

The ACL Access Check

With an ACL, the kernel still picks one path and stops there:

  1. If the process is the owner, use user::. The mask does not apply.
  2. Otherwise, if the process matches a named user, use that user:NAME: entry intersected with mask.
  3. Otherwise, if the process’s effective GID or any supplementary GID matches the owning group or a named group, access is granted if any matching group entry, intersected with mask, contains the requested permissions. If none does, access is denied.
  4. Otherwise, use other::.

Two details are easy to miss:

  • A match ends the search. If appuser has a named-user entry with ---, a more permissive group entry will not rescue it.
  • The mask is an upper limit, not a grant. For example:
user:appuser:rwx
mask::r--

appuser effectively gets only r--. getfacl shows this with a comment such as #effective:r--.

How ACLs and Mode Bits Stay in Sync

ACLs are a superset of the mode bits, and the kernel keeps the two synchronized:

ACL entry Corresponding mode bits
user:: owner bits
mask:: if present; otherwise group:: group bits
other:: other bits

Changing the mode bits changes the matching ACL entries, and changing those ACL entries changes the mode bits.

That is why, on a file with an extended ACL, the group bits shown by ls -l are the mask, not the group:: entry. GNU ls also prints + after the mode bits when an alternate access method, such as an ACL, applies:

-rw-r-x---+ 1 alice alice 4096 Sep 28 10:00 model.bin

This leads to a common trap:

chmod g-r shared/model.bin

You may expect this to affect only the owning group. On an ACL file, however, it removes r from the mask, so user:appuser:r-- and every group-class entry lose read access too. Likewise, chmod 600 sets the mask to ---.

The reverse also matters: by default, setfacl -m recalculates the mask as the union of the owning group, named users, and named groups, unless you give an explicit mask or use -n. A later setfacl -m can therefore restore permissions that an earlier chmod narrowed.

Default ACLs: Inheritance for New Files

An access ACL applies only to files that already exist. A directory can also have a default ACL, stored in the system.posix_acl_default xattr. The default ACL applies only to objects created later inside that directory:

# Existing tree: directories get x; files get x only if they are already executable
setfacl -R -m u:appuser:rX shared/

# Future files and subdirectories
setfacl -R -d -m u:appuser:rX shared/

Without -d, the first command updates only files and directories that exist now. Files added later will not get the appuser entry.

When a file is created in a directory that has a default ACL:

  • The new object’s access ACL is copied from the directory’s default ACL.
  • A new subdirectory also receives that default ACL, so inheritance continues down the tree.
  • The mode requested by the creating program still limits the result. The process umask is not applied in this case. If the program asks for mode 0600, the inherited mask becomes ---, so named entries become ineffective.

When the parent has no default ACL, the requested mode and the process umask determine the new file’s permissions, as usual.

To inspect or remove a default ACL:

getfacl -d shared/
setfacl -k shared/   # remove the default ACL

Who Can Change ACLs?

As with the mode, the file owner or a process with CAP_FOWNER can modify a file’s ACL. Extra ACL entries do not let a named user change permissions.

Summary

Aspect chmod setfacl
Storage Mode bits in the inode xattrs system.posix_acl_access and system.posix_acl_default
Who can be targeted Owner, group, other Owner, group, other, plus any named users and groups
Upper limit None mask limits named users and all group entries
New-file permissions Requested mode and umask Requested mode and the directory’s default ACL
ls -l group column The group bits The mask, with a trailing +
Best fit Simple owner/group/other layouts Granting access to a few specific users or groups

Use chmod when the owner, group, and other classes are enough. Use setfacl when one extra account needs access and you do not want to change ownership or open the file to everyone. Add a default ACL with -d if new files must inherit that access. After using ACLs, remember that chmod’s group bits now control the mask.

References