Access Control Lists (ACL) in Linux: Complete Guide

 

Access Control Lists (ACL) in Linux: Complete Guide

Part 1 of 8 —
Access Control Lists (ACL) in Linux: Complete Guide
🔐
Fine-Grained Permissions
🐧
Linux 2.6+
👥
Per-User / Per-Group

Traditional Unix permissions give you three buckets: owner, group, and others. Every file has exactly one owner and one group. This works for simple cases, but falls short when you need different access levels for different people. Access Control Lists (ACLs) solve this by letting you define permissions for any number of specific users and groups on a single file.

ACL support was added to the Linux kernel in version 2.6. The implementation is based on the POSIX.1e draft standard (Draft 17), which was never officially finalized but became the de-facto standard across many UNIX systems.

Key Terms in This Topic

ACL EntryTag TypeTag Qualifier Permission SetACL_USER_OBJACL_USER ACL_GROUP_OBJACL_GROUPACL_MASK ACL_OTHERMinimal ACLExtended ACL system.posix_acl_accessGroup Class

Why Traditional Permissions Are Not Enough

Consider a shared directory on a project server with this requirement:

User Role Needed Access Possible With chmod?
alice Owner Read + Write + Execute ✔ Yes (owner bits)
bob Reviewer Read only ✘ Not individually
charlie Developer Read + Write ✘ Not individually
others Public No access ✔ Yes (other bits)

With traditional chmod, bob and charlie would need to be in the same group and get the same permissions. ACLs let you give each user exactly the right access.

Structure of an ACL Entry

An ACL is a list of entries. Each entry has exactly three parts:

Part What It Stores Example Used For
Tag Type Category this entry applies to ACL_USER All entries
Tag Qualifier Specific UID or GID 1007 Only ACL_USER & ACL_GROUP
Permission Set r / w / x flags granted r-x All entries
Note: In text form, an ACL entry looks like: tag-type:[tag-qualifier]:permissions
Example: user:bob:r-x or group::r--

All Six Tag Types Explained

1. ACL_USER_OBJ — File Owner Permissions

Present in every ACL exactly once. Defines what the file owner (the user who created or owns the file) can do. Directly maps to the traditional owner permission bits (the leftmost rwx in ls -l).

# In text form — no qualifier needed, just a dash
ACL_USER_OBJ   -   rwx

# In getfacl short format:
user::rwx
2. ACL_USER — Named User Permissions

Can have zero or more of these, but at most one per user. Each entry gives specific permissions to one particular user, identified by their UID. This is the power of ACLs — giving custom rights to any user.

# User with UID 1007 gets read-only
ACL_USER   1007   r--

# User 'bob' gets read + execute
user:bob:r-x

# User 'alice' gets full access
user:alice:rwx
3. ACL_GROUP_OBJ — File Group Permissions

Present in every ACL exactly once. Defines permissions for the file’s owning group. When no ACL_MASK entry exists, this maps to the traditional group bits. When ACL_MASK is present, the mask value shows in group bits instead.

ACL_GROUP_OBJ   -   r-x

# In getfacl format:
group::r-x
4. ACL_GROUP — Named Group Permissions

Can have zero or more of these, but at most one per group. Gives permissions to a specific group by GID. Very useful for team-based access control.

# Group 'developers' gets read+write
ACL_GROUP   developers   rw-

# Group ID 103 gets write-only
ACL_GROUP   103   -w-

# In getfacl format:
group:teach:--x
5. ACL_MASK — The Permission Ceiling

At most one per ACL. Acts as an upper limit on permissions that can be granted by ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP entries. Mandatory if any ACL_USER or ACL_GROUP entries exist. This entry is the key to compatibility with traditional chmod(). Covered in depth in Part 4.

# Even if a user entry says rwx, effective = rwx AND mask
# If mask is rw-, user gets rw- at most
ACL_MASK   -   rw-

# In getfacl format:
mask::rw-
6. ACL_OTHER — Everyone Else

Present in every ACL exactly once. Applies to any process that does not match any other ACL entry. Maps directly to the traditional “other” permission bits.

ACL_OTHER   -   r--

# In getfacl format:
other::r--

Full ACL — Visual Layout

Here is what a complete extended ACL looks like for a file:

Tag Type Qualifier Perms Notes
ACL_USER_OBJ rwx File owner. Maps to traditional owner bits
ACL_USER 1007 r– User 1007 — read only
ACL_USER 1010 rwx User 1010 — full access (masked by ACL_MASK)
ACL_GROUP_OBJ rwx File’s owning group (part of group class)
ACL_GROUP 102 r– Group 102 — read only
ACL_GROUP 103 -w- Group 103 — write only
ACL_MASK rw- Max permissions for group class entries
ACL_OTHER r– Everyone else. Maps to traditional other bits
Group Class: The entries ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP together form the “group class”. The ACL_MASK entry limits the effective permissions of all group class entries.

Minimal ACL vs Extended ACL

📋 Minimal ACL

Exactly 3 entries — same as traditional Unix permissions. No ACL_USER, ACL_GROUP, or ACL_MASK entries.

Storage: Stored in regular file permission bits. No extra extended attribute needed.

user::rw-
group::r--
other::r--

When you do ls -l, no + sign appears after permissions.

📋 Extended ACL

Has ACL_USER, ACL_GROUP, and ACL_MASK entries in addition to the three base entries.

Storage: Stored in system.posix_acl_access extended attribute on disk.

user::rw-
user:bob:r--
group::r--
mask::r--
other::---

When you do ls -l, a + sign appears after permissions.

How to Enable ACL Support

ACLs need kernel + filesystem support. Here is how to enable them:

# Step 1: Mount filesystem with ACL support
sudo mount -o remount,acl /dev/sda1 /home

# Or add to /etc/fstab permanently:
# /dev/sda1  /home  ext4  defaults,acl  0 2

# Step 2: Verify ACL support is enabled
tune2fs -l /dev/sda1 | grep "Default mount options"
# Should show: acl

# Step 3: Check kernel config (ACL must be built in or as module)
grep -i acl /boot/config-$(uname -r)
# CONFIG_EXT4_FS_POSIX_ACL=y
Note: On many modern distros (Ubuntu, RHEL, Fedora), ACLs are enabled by default on ext4. You don’t need to manually add the acl mount option.

Coding Examples

Example 1: Basic shell — create a file and view its default ACL
# Create a fresh file
touch project.c

# Check permissions the traditional way
ls -l project.c
# -rw-r--r-- 1 ravi users 0 Jan 1 10:00 project.c
# No '+' sign = minimal ACL only

# View ACL using getfacl
getfacl project.c
# file: project.c
# owner: ravi
# group: users
user::rw-     <-- ACL_USER_OBJ (owner)
group::r--    <-- ACL_GROUP_OBJ
other::r--    <-- ACL_OTHER
Example 2: Add named user and group — creates extended ACL
# Give bob read-only access
setfacl -m u:bob:r-- project.c

# Give devteam group read+write access
setfacl -m g:devteam:rw- project.c

# View the new ACL
getfacl project.c
# file: project.c
# owner: ravi
# group: users
user::rw-
user:bob:r--        <-- new ACL_USER entry
group::r--
group:devteam:rw-   <-- new ACL_GROUP entry
mask::rw-           <-- setfacl auto-created this!
other::r--

# Notice: ls -l now shows a '+' sign
ls -l project.c
# -rw-rw-r--+ 1 ravi users 0 Jan 1 10:00 project.c
Example 3: C program — read all ACL entries from a file
#include <stdio.h>
#include <stdlib.h>
#include <sys/acl.h>
#include <acl/libacl.h>

/* Returns human-readable tag type name */
const char *tag_to_str(acl_tag_t tag) {
    switch (tag) {
        case ACL_USER_OBJ:  return "ACL_USER_OBJ ";
        case ACL_USER:      return "ACL_USER     ";
        case ACL_GROUP_OBJ: return "ACL_GROUP_OBJ";
        case ACL_GROUP:     return "ACL_GROUP    ";
        case ACL_MASK:      return "ACL_MASK     ";
        case ACL_OTHER:     return "ACL_OTHER    ";
        default:            return "UNKNOWN      ";
    }
}

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

    /* Load ACL from disk into memory */
    acl_t acl = acl_get_file(argv[1], ACL_TYPE_ACCESS);
    if (!acl) { perror("acl_get_file"); return 1; }

    printf("File: %s\n", argv[1]);
    printf("%-14s %-10s %s\n", "Tag Type", "Qualifier", "Permissions");
    printf("%-14s %-10s %s\n", "--------", "---------", "-----------");

    acl_entry_t entry;
    acl_tag_t   tag;
    int id = ACL_FIRST_ENTRY;

    /* Loop through all ACL entries */
    while (acl_get_entry(acl, id, &entry) == 1) {
        id = ACL_NEXT_ENTRY;

        acl_get_tag_type(entry, &tag);
        printf("%-14s ", tag_to_str(tag));

        /* Print qualifier (UID/GID) if applicable */
        if (tag == ACL_USER) {
            uid_t *uid = acl_get_qualifier(entry);
            printf("UID=%-6d ", (int)*uid);
            acl_free(uid);
        } else if (tag == ACL_GROUP) {
            gid_t *gid = acl_get_qualifier(entry);
            printf("GID=%-6d ", (int)*gid);
            acl_free(gid);
        } else {
            printf("%-10s ", "-");
        }

        /* Print permissions */
        acl_permset_t perms;
        acl_get_permset(entry, &perms);
        printf("%c%c%c\n",
            acl_get_perm(perms, ACL_READ)    ? 'r' : '-',
            acl_get_perm(perms, ACL_WRITE)   ? 'w' : '-',
            acl_get_perm(perms, ACL_EXECUTE) ? 'x' : '-'
        );
    }

    acl_free(acl);
    return 0;
}

/* Compile: gcc -o read_acl read_acl.c -lacl
   Usage:   ./read_acl project.c */
Example 4: C program — check if ACL is minimal or extended
#include <stdio.h>
#include <sys/stat.h>
#include <sys/acl.h>
#include <acl/libacl.h>

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

    acl_t acl = acl_get_file(argv[1], ACL_TYPE_ACCESS);
    if (!acl) { perror("acl_get_file"); return 1; }

    mode_t mode;
    /* acl_equiv_mode returns 0 if ACL == traditional perms (minimal) */
    int is_minimal = (acl_equiv_mode(acl, &mode) == 0);

    printf("File:    %s\n", argv[1]);
    printf("ACL type: %s\n", is_minimal ? "MINIMAL (3 entries)" : "EXTENDED");

    if (is_minimal) {
        printf("Equiv perms: %04o\n", (unsigned)mode & 0777);
        printf("No extended attribute needed on disk.\n");
    } else {
        printf("Stored in: system.posix_acl_access extended attribute\n");
        printf("ls -l will show a '+' after permissions.\n");
    }

    acl_free(acl);
    return 0;
}
/* Compile: gcc -o acl_type acl_type.c -lacl */

Interview Questions

Q1. What is an ACL in Linux? How does it differ from traditional file permissions?

An ACL (Access Control List) is a list of entries that define file permissions for individual users and groups, beyond the traditional owner/group/others model. Traditional permissions give one set of rights to owner, one to group, one to others. ACLs allow any number of named users or groups to have their own specific permission set on a file.

Q2. What are the six ACL tag types and what does each represent?

  • ACL_USER_OBJ — permissions for the file owner (exactly 1 per ACL)
  • ACL_USER — permissions for a specific named user (0 or more; max 1 per user)
  • ACL_GROUP_OBJ — permissions for the file’s owning group (exactly 1)
  • ACL_GROUP — permissions for a specific named group (0 or more; max 1 per group)
  • ACL_MASK — upper bound on group class entry permissions (0 or 1)
  • ACL_OTHER — permissions for all other users (exactly 1)

Q3. What is the difference between a minimal ACL and an extended ACL? How does Linux store each?

A minimal ACL has exactly three entries (ACL_USER_OBJ, ACL_GROUP_OBJ, ACL_OTHER) — same as standard Unix permissions. It is stored in the regular file permission bits, no extra disk space needed. An extended ACL has additional ACL_USER, ACL_GROUP, and ACL_MASK entries. It is stored as the system.posix_acl_access extended attribute on the filesystem.

Q4. What is the “group class” in ACL terminology?

The group class is the set of ACL entries consisting of ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP entries. The ACL_MASK entry acts as an upper bound (ceiling) for effective permissions in this group class. When a process’s access is determined through any group class entry, the final permissions are the entry’s permissions AND-masked with the ACL_MASK value.

Q5. Which filesystem versions support ACLs in Linux and what is required to enable them?

ext2, ext3, ext4, Reiserfs (2.6.7+), XFS, and JFS support ACLs. For ext2/ext3/ext4/Reiserfs, the filesystem must be mounted with the -o acl mount option (though many modern distros enable this by default). ACL support is also a kernel configuration option under the File Systems menu. The kernel version must be 2.6 or later.

Q6. If you run ls -l on a file and see a + after the permissions string (e.g., -rwxr-x--x+), what does that mean?

The + sign indicates that the file has an extended ACL — it has ACL_USER or ACL_GROUP entries beyond the standard three. Use getfacl filename to view the full ACL.

Next: ACL Permission-Checking Algorithm →

Learn exactly how Linux decides if a process can access a file that has an ACL

Continue → All Interview Q&A

Leave a Reply

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