Why Traditional Permissions Fall Short and How ACLs Help in Linux

 

Why Traditional Permissions Fall Short and How ACLs Help in Linux
Part 4 — Why Traditional Permissions Fall Short & How ACLs Help
Linux 2.6+
Kernel Support
Per-user & per-group
Fine-grained control
POSIX.1e
Based on draft standard
system.posix_acl_*
Stored as EAs

Key Terms

ACL Access Control List DAC (Discretionary Access Control) setfacl getfacl POSIX.1e ACL entry ACE (Access Control Entry) mask entry default ACL acl_t acl_get_file

The Problem with Traditional UNIX Permissions

Traditional UNIX file permissions give you exactly three buckets: owner (user), group, and everyone else (other). You set read/write/execute for each bucket — and that’s it.

This model works for many situations, but it breaks down when you need more precise control. Consider this scenario:

Scenario: You have a file project_notes.txt owned by user alice. You want:

  • alice — full read/write
  • bob — read only
  • carol — read + write
  • Everyone else — no access

With traditional permissions, you cannot express “bob = read-only, carol = read-write” at the same time. You can only set one permission for the group, and bob and carol would need to be in different groups or you’d have to use workarounds.

Traditional Permissions vs ACLs
Feature Traditional UNIX Permissions ACLs
Specify permission for individual users? ✗ Only owner ✓ Any number of users
Specify permission for individual groups? ✗ Only one group ✓ Any number of groups
Backward compatible with stat() and ls? ✓ Native ✓ Maintained via mask
Visible with ls -l? ✓ Always ⚠ Partial (+ symbol in ls -l)
Requires special filesystem mount? ✗ Always available ⚠ Needs -o acl on ext*/Reiserfs
Standardized by POSIX? ✓ Yes (POSIX.1) ⚠ Draft only (POSIX.1e withdrawn)
Stored as Fixed i-node fields Extended Attributes (system.*)

How ACLs Work — The Concept

An ACL is a list of ACL entries. Each entry specifies a principal (a specific user or group) and a set of permissions (read, write, execute).

ACL Entry Type Meaning Example Mandatory?
user:: File owner user::rw- ✓ Always
user:alice: Specific named user user:alice:r-- Optional
group:: File’s owning group group::r-- ✓ Always
group:devs: Specific named group group:devs:rw- Optional
mask:: Max permissions for named entries mask::rw- ✓ When named entries exist
other:: Everyone else other::--- ✓ Always

Enabling ACL Support

ACLs require both kernel support (present by default in modern kernels) and the file system must be mounted with the acl option on ext2/ext3/ext4/Reiserfs:

# Mount with ACL support
mount -o acl /dev/sdb1 /mnt/data

# Or add to /etc/fstab
# /dev/sdb1  /mnt/data  ext4  defaults,acl  0 2

# XFS and Btrfs have ACL support enabled by default (no mount option needed)

# Check if a mounted filesystem has ACL support
tune2fs -l /dev/sdb1 | grep "Default mount options"
# Look for: acl
# Install ACL tools
sudo apt install acl      # Debian/Ubuntu
sudo dnf install acl      # Fedora/RHEL

ACL Shell Commands — setfacl & getfacl
# Create a test file
touch project.txt

# View current ACL (same as traditional perms initially)
getfacl project.txt
# Output:
# file: project.txt
# owner: ravi
# group: ravi
# user::rw-
# group::r--
# other::r--

# Grant specific user 'alice' read-only access
setfacl -m u:alice:r-- project.txt

# Grant specific user 'carol' read+write access
setfacl -m u:carol:rw- project.txt

# Grant a group 'devteam' read access
setfacl -m g:devteam:r-- project.txt

# View the updated ACL
getfacl project.txt
# Output:
# file: project.txt
# owner: ravi
# group: ravi
# user::rw-
# user:alice:r--
# user:carol:rw-
# group::r--
# group:devteam:r--
# mask::rw-        <-- auto-created when named entries added
# other::r--

# Notice the '+' in ls -l output indicating ACL is present
ls -l project.txt
# -rw-rw-r--+ 1 ravi ravi 0 Jan 10 project.txt

# Remove a specific ACL entry
setfacl -x u:alice project.txt

# Remove ALL ACL entries (revert to traditional permissions)
setfacl -b project.txt

Default ACLs — Inherited Permissions for New Files

A default ACL can be set on a directory. When new files or subdirectories are created inside, they inherit the default ACL as their initial ACL. This is very useful for shared project directories.

# Create a shared project directory
mkdir /home/projects/webapp

# Set default ACL: new files will give devteam read+write
setfacl -d -m g:devteam:rw- /home/projects/webapp

# View both access ACL and default ACL
getfacl /home/projects/webapp
# Output:
# file: projects/webapp
# owner: ravi
# group: ravi
# user::rwx
# group::r-x
# other::r-x
# default:user::rwx
# default:group::r-x
# default:group:devteam:rw-    <-- inherited by new files
# default:mask::rwx
# default:other::r-x

# Now create a file inside — it inherits the default ACL
touch /home/projects/webapp/index.html
getfacl /home/projects/webapp/index.html
# The devteam group will have rw- on this new file automatically

ACLs and Extended Attributes — The Connection

ACLs are implemented on top of the extended attribute system. The kernel stores ACL data in the system namespace EAs:

EA Name What it Stores
system.posix_acl_access The access ACL for the file (controls who can access it)
system.posix_acl_default The default ACL on a directory (inherited by new children)
# You can see the raw EA (binary) that stores the ACL
getfattr -m system -d /home/projects/webapp/index.html
# Output (binary, shown as hex):
# system.posix_acl_access=0s...

# Or view as hex explicitly
getfattr -m system --absolute-names -d project.txt
This is why EA support must be enabled on the file system before ACLs work — ACLs use the system namespace EAs under the hood. If the file system doesn’t support EAs, it cannot support ACLs.

ACL C Programming API (POSIX.1e)

The ACL C API is defined in <sys/acl.h>. Link with -lacl.

/* Compile: gcc -o acl_demo acl_demo.c -lacl */

#include <sys/types.h>
#include <sys/acl.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <file>\n", argv[0]);
        return 1;
    }

    /* Get the access ACL for the file */
    acl_t acl = acl_get_file(argv[1], ACL_TYPE_ACCESS);
    if (acl == NULL) {
        perror("acl_get_file");
        return 1;
    }

    /* Convert ACL to a human-readable string */
    char *text = acl_to_text(acl, NULL);
    if (text) {
        printf("ACL for %s:\n%s\n", argv[1], text);
        acl_free(text);
    }

    acl_free(acl);
    return 0;
}
/* Check if a file has an extended ACL (beyond basic user/group/other) */
#include <sys/types.h>
#include <sys/acl.h>
#include <stdio.h>
#include <acl/libacl.h>   /* Linux-specific extension */

int has_extended_acl(const char *path) {
    acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS);
    if (!acl) return -1;

    /* acl_equiv_mode() returns 0 if ACL is equivalent to basic permissions */
    mode_t mode;
    int is_extended = (acl_equiv_mode(acl, &mode) == 1);
    acl_free(acl);
    return is_extended;
}

int main() {
    const char *files[] = { "/etc/passwd", "/tmp/project.txt", NULL };
    for (int i = 0; files[i]; i++) {
        int ext = has_extended_acl(files[i]);
        printf("%s: %s\n", files[i],
               ext == 1 ? "Has extended ACL" :
               ext == 0 ? "Basic permissions only" : "Error");
    }
    return 0;
}

POSIX.1e / POSIX.2c — The Abandoned Standard

An important historical note: ACLs on Linux (and most UNIX systems) are based on a draft standard — POSIX.1e and POSIX.2c — which was ultimately withdrawn and never finalized.

1990s
IEEE begins POSIX.1e/2c work to standardize ACLs, capabilities, and audit
Draft 17
Final published draft. Many UNIX vendors implement based on this draft
Withdrawn
IEEE withdraws the draft — standardization abandoned
Today
Linux, BSDs, Solaris all implement Draft 17 APIs — de facto standard
Because POSIX.1e was never finalized, there are small differences in ACL implementations across Linux, macOS (NFSv4 ACLs), and Solaris. Linux follows Draft 17 closely.

Quick Reference — setfacl Syntax Summary
# Syntax: setfacl [options] acl_spec file...

# -m = modify (add or update entries)
# -x = remove specific entry
# -b = remove all ACL entries (back to basic perms)
# -d = operate on the default ACL
# -R = recursive (apply to all files in directory)
# -k = remove default ACL

# Grant user permissions
setfacl -m u:username:rwx  file     # read+write+execute
setfacl -m u:username:r--  file     # read only
setfacl -m u:username:---  file     # no permissions

# Grant group permissions
setfacl -m g:groupname:rw- file

# Remove a specific user entry
setfacl -x u:username      file

# Set default ACL on a directory
setfacl -d -m u:username:rw- /some/dir

# Copy ACL from one file to another
getfacl file1 | setfacl --set-file=- file2

# Recursive ACL on a directory tree
setfacl -R -m g:devteam:rw- /home/projects/webapp/

Interview Questions
Q1. What problem do ACLs solve that traditional UNIX permissions cannot?
Traditional UNIX permissions only allow you to set permissions for three entities: the file owner, one group, and everyone else. ACLs let you set different permissions for any number of individual users and groups. For example, you can give user alice read-only access and user bob read-write access to the same file independently.
Q2. How are ACLs stored on a Linux file system?
ACLs are stored as Extended Attributes in the system namespace. Specifically, system.posix_acl_access stores the access ACL and system.posix_acl_default stores the default ACL for directories. This is why EA support must be available on the file system before ACLs can be used.
Q3. What is the mask entry in an ACL?
The mask entry defines the maximum permissions that can be granted to any named user or named group entry. It acts as an upper bound — even if a named user has rwx, if the mask is r--, the effective permission is only r--. The mask is automatically created and updated when you add named entries with setfacl.
Q4. What is a default ACL and where does it apply?
A default ACL can only be set on a directory. It defines the ACL that newly created files and subdirectories inside that directory will inherit at creation time. This is useful for ensuring that all new files in a shared directory automatically have the right group or user permissions without manual configuration.
Q5. What does the ‘+’ sign mean in the output of ls -l?
The + at the end of the permission string (e.g., -rw-rw-r--+) indicates that the file has an extended ACL beyond the basic user/group/other permissions. To see the full ACL, use getfacl filename.
Q6. Is there a formal POSIX standard for Linux ACLs?
No. Linux ACLs are based on POSIX.1e Draft 17, which was a draft standard that was ultimately withdrawn and never finalized. Despite this, most UNIX-like systems (Linux, BSDs, Solaris) implement ACLs based on this same draft, making it a de facto standard in practice.
Q7. How do you programmatically check if a file has an extended ACL in C?
Use acl_get_file() to retrieve the ACL, then acl_equiv_mode() (Linux-specific, from <acl/libacl.h>) which returns 1 if the ACL contains entries beyond the minimal user/group/other set. Alternatively, call getxattr() directly and check if system.posix_acl_access exists and has more than the minimum 3 entries.

Series Complete

You have covered all topics from the PDF chapter

← Part 1: EA Overview ← Part 3: EA System Calls

Leave a Reply

Your email address will not be published. Required fields are marked *