Loading learning content...
Every file, directory, and system object begins its existence with a set of permissions. But where do these initial permissions come from? The answer involves a fascinating interplay between application requests, system masks, inherited ACLs, and administrative policies.
Misconfigured default permissions are among the most common sources of security vulnerabilities. Files created with overly permissive defaults can expose sensitive data. Files created with overly restrictive defaults can break applications. Understanding exactly how default permissions are determined—and how to configure them correctly—is essential knowledge for any systems administrator or security engineer.
This page dissects the complete mechanism of default permission assignment, from the moment an application calls create() to the final ACL written to disk.
By the end of this page, you will understand how umask works, how POSIX default ACLs override umask, how Windows permission inheritance operates, and how to configure secure defaults for different use cases. You'll be able to predict exactly what permissions a newly created file will receive in any scenario.
When an application creates a new file or directory, the operating system determines its initial permissions through a multi-stage process. Understanding this pipeline is fundamental to predicting and controlling default permissions.
POSIX Permission Creation Pipeline:
┌──────────────────────────────────────────────────────────────────────┐
│ APPLICATION CREATES FILE │
│ │
│ open("/path/file", O_CREAT, 0666) │
│ mkdir("/path/dir", 0777) │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ Requested Mode (mode_t) │ │
│ │ File: typically 0666 │ │
│ │ Dir: typically 0777 │ │
│ └────────────────┬───────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ Does parent have default ACL? │ │
│ └────────────────┬───────────────────┘ │
│ YES │ NO │
│ ┌─────────────┴─────────────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Use Default ACL │ │ Apply umask │ │
│ │ (mask step still │ │ │ │
│ │ applies) │ │ final = requested │ │
│ └──────────┬──────────┘ │ & ~umask │ │
│ │ └──────────┬──────────┘ │
│ │ │ │
│ └──────────┬───────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ Final Permission Mode │ │
│ │ Written to inode │ │
│ └────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
Windows Permission Creation Pipeline:
┌──────────────────────────────────────────────────────────────────────┐
│ APPLICATION CREATES FILE │
│ │
│ CreateFile(..., lpSecurityAttributes, ...) │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ Security Attributes Specified? │ │
│ └────────────────┬───────────────────┘ │
│ YES │ NO/NULL │
│ ┌─────────────┴─────────────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ Use Provided │ │ Inherit from │ │
│ │ Security Desc. │ │ Parent Container │ │
│ └─────────┬──────────┘ └─────────┬──────────┘ │
│ │ │ │
│ └──────────┬──────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ Merge inherited ACEs with │ │
│ │ explicit ACEs, apply CreatorOwner │ │
│ │ substitution │ │
│ └────────────────┬───────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ Apply container/object inherit │ │
│ │ flags, set INHERITED_ACE flags │ │
│ └────────────────┬───────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ Final Security Descriptor │ │
│ │ Written to object │ │
│ └────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
In POSIX, the mode parameter to open() or mkdir() is a REQUEST that gets modified by umask or default ACL. In Windows, if you provide a security descriptor, it's used DIRECTLY with only inheritance processing applied. This means POSIX applications don't control their own permissions, while Windows applications can.
The umask (user file-creation mode mask) is the traditional Unix mechanism for controlling default permissions. It's a bitmask that specifies which permission bits should be removed from the requested mode when creating files or directories.
The umask Formula:
final_mode = requested_mode & ~umask
Where ~umask is the bitwise complement of the umask. The umask clears bits; it can never add permissions beyond what the application requested.
12345678910111213141516171819202122232425262728293031323334353637383940414243
# View current umask$ umask0022 # umask is shown in octal:# 0 = special bits (setuid, setgid, sticky)# 0 = owner bits to remove (none)# 2 = group bits to remove (write = 2)# 2 = other bits to remove (write = 2) # Binary representation:# rwx rwx rwx (permission bits)# 000 010 010 (umask 022)# ───────────# A umask bit of 1 means "remove this permission" # Example: Creating a regular file# Application calls: open("file", O_CREAT, 0666)# Requested: rw- rw- rw- (0666)# umask: --- -w- -w- (0022)# ─────────────────# Result: rw- r-- r-- (0644) $ touch newfile.txt$ ls -l newfile.txt-rw-r--r-- 1 user group 0 Jan 15 10:00 newfile.txt # Example: Creating a directory# Application calls: mkdir("dir", 0777)# Requested: rwx rwx rwx (0777)# umask: --- -w- -w- (0022)# ─────────────────# Result: rwx r-x r-x (0755) $ mkdir newdir$ ls -ld newdirdrwxr-xr-x 2 user group 4096 Jan 15 10:00 newdir # Common umask values:# 022 - Standard (owner: rw/rwx, group/others: r/rx)# 002 - Group-collaborative (owner/group: rw/rwx, others: r/rx) # 027 - Restrictive (owner: rw/rwx, group: r/rx, others: none)# 077 - Private (owner only: rw/rwx, group/others: none)| umask | File Result | Directory Result | Use Case |
|---|---|---|---|
| 000 | rw-rw-rw- (666) | rwxrwxrwx (777) | Maximum permissive (dangerous) |
| 002 | rw-rw-r-- (664) | rwxrwxr-x (775) | Group collaboration |
| 022 | rw-r--r-- (644) | rwxr-xr-x (755) | Standard default |
| 027 | rw-r----- (640) | rwxr-x--- (750) | Restrictive |
| 077 | rw------- (600) | rwx------ (700) | Private/sensitive |
| 177 | rw------- (600) | rw------- (600) | Maximum private (no execute) |
1234567891011121314151617181920212223242526272829303132
# Set umask for current shell session$ umask 027 # Symbolic mode (shows what's allowed, not removed)$ umask -Su=rwx,g=rx,o= # Setting umask symbolically $ umask u=rwx,g=rx,o= # Equivalent to 027 # Per-process umask# umask is inherited by child processes# Each process can modify its own umask // C code to set umask programmatically#include <sys/stat.h>mode_t old_umask = umask(0077); // Returns previous umask// ... create sensitive files ...umask(old_umask); // Restore original # System-wide default umask locations:# /etc/profile - All login shells# /etc/bashrc - All bash interactive shells# /etc/login.defs - UMASK setting for useradd# ~/.bashrc, ~/.profile - Per-user settings# PAM (pam_umask.so) - Session-based umask control # Example /etc/profile setting:umask 022 # Example /etc/login.defs:UMASK 077 # Default umask for new user home directoriesumask is a blunt instrument—it applies the SAME mask to ALL files and directories created by a process. It cannot distinguish between sensitive and non-sensitive files. For fine-grained control, default ACLs are required. Also, umask can only REMOVE permissions; if an application requests mode 0600, umask cannot grant group or others any access.
POSIX default ACLs provide directory-based inheritance that overrides the umask mechanism. When a default ACL exists on a directory, new files and subdirectories created within it derive their ACL from the default ACL rather than umask.
Key Characteristics of Default ACLs:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
# Create a shared project directory with default ACL$ mkdir /projects/shared$ chown alice:developers /projects/shared # Set access ACL for the directory itself$ setfacl -m u::rwx,g::rwx,g:testers:rx,o::---,m::rwx /projects/shared # Set default ACL (what new children will get)$ setfacl -d -m u::rwx,g::rwx,g:testers:rx,o::---,m::rwx /projects/shared # View the complete ACL$ getfacl /projects/shared# file: projects/shared# owner: alice# group: developersuser::rwx # Access ACL entriesgroup::rwx group:testers:r-x mask::rwx other::--- default:user::rwx # Default ACL entries (for children)default:group::rwx default:group:testers:r-x default:mask::rwx default:other::--- # Now create a file - ignores umask because default ACL exists$ umask 077 # Very restrictive umask$ touch /projects/shared/code.py $ getfacl /projects/shared/code.py# file: projects/shared/code.py# owner: alice# group: developersuser::rw- # Derived from default:user (x removed for file)group::rwx #effective:rw-group:testers:r-x #effective:r--mask::rw- # Mask computed from requested mode & defaultother::--- # Note: Even though umask was 077, the file got group permissions# because the default ACL, not umask, determined permissions! # Create a subdirectory$ mkdir /projects/shared/modules $ getfacl /projects/shared/modules# file: projects/shared/modules# owner: alice# group: developersuser::rwxgroup::rwxgroup:testers:r-xmask::rwxother::---default:user::rwx # Default ACL is COPIED to subdirectory!default:group::rwx default:group:testers:r-x default:mask::rwx default:other::---The Mask Calculation with Default ACLs:
Even with default ACLs, the mask entry is computed based on the intersection of the requested mode and the default ACL permissions. This preserves the application's ability to create restricted files:
123456789101112131415161718192021222324252627
# Parent has permissive default ACL$ getfacl /shareddefault:user::rwxdefault:group::rwxdefault:mask::rwxdefault:other::--- # Application creates file with mode 0600 (owner read/write only)# This is common for files containing secrets # Python example:with open('/shared/secrets.conf', 'w', opener=lambda p, f: os.open(p, f, 0o600)) as f: f.write("password=secret") # Result ACL:$ getfacl /shared/secrets.confuser::rw- # From default, intersected with 0600group::rwx #effective:--- (mask limits to nothing!)mask::--- # Mask = (requested_mode & default_mask) = 0600 & rwxother::--- # ... for group/other bits = --- # The mask of --- means named groups/users effectively have no access# even though the default ACL would grant them rwx! # This preserves the application's intent:# - Requested mode 0600 = owner only# - Result: owner only (named entries get masked to nothing)The mask intersection behavior is a critical security feature. It means applications that intentionally create restricted files (mode 0600 or 0640) maintain their security intent even in directories with permissive default ACLs. This is why security-sensitive applications like SSH and GPG still work correctly in shared directories.
Windows NTFS uses ACE inheritance flags to control how permissions flow from parent containers to child objects. Unlike POSIX default ACLs which are a separate structure, Windows inheritance is controlled by flags on each ACE.
Inheritance Flow:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
# ACE Inheritance Flags:# # OBJECT_INHERIT_ACE (OI) - Inherit to files# CONTAINER_INHERIT_ACE (CI) - Inherit to subdirectories # INHERIT_ONLY_ACE (IO) - Don't apply here, only inherit# NO_PROPAGATE_INHERIT_ACE (NP)- Stop at immediate children## INHERITED_ACE (I) - Was inherited (read-only flag) # Common inheritance patterns:## (OI)(CI) - Inherit to everything (files and subdirs), all levels# (CI) - Inherit to subdirectories only, all levels# (OI) - Inherit to files only, all levels# (OI)(CI)(IO) - Don't apply here, but inherit to everything# (OI)(CI)(NP) - Inherit to immediate children only# (NP)(IO) - Apply only to immediate children, not this# None - No inheritance (this object only) # Example: Set up inheritance on a folderC:\> mkdir C:\Data\Projects # Grant Developers modify with full inheritanceC:\> icacls C:\Data\Projects /grant "Developers:(OI)(CI)M" # Grant Managers read-only to files, not subdirectoriesC:\> icacls C:\Data\Projects /grant "Managers:(OI)R" # Grant Auditors read to subdirectories only, not filesC:\> icacls C:\Data\Projects /grant "Auditors:(CI)R" # Create a file - it inherits applicable ACEsC:\> echo test > C:\Data\Projects\test.txtC:\> icacls C:\Data\Projects\test.txtC:\Data\Projects\test.txt BUILTIN\Developers:(M) # Inherited from (OI)(CI) BUILTIN\Managers:(R) # Inherited from (OI) # No Auditors entry - (CI) doesn't apply to files # Create a subdirectory - it inherits applicable ACEsC:\> mkdir C:\Data\Projects\SubDirC:\> icacls C:\Data\Projects\SubDirC:\Data\Projects\SubDir BUILTIN\Developers:(OI)(CI)(M) # Inherited, keeps inheritance flags BUILTIN\Auditors:(CI)(R) # Only (CI), will pass to subdirs # No Managers entry - (OI) doesn't apply to directoriesSpecial Inheritance Identities:
Windows provides special security principals that are replaced during inheritance:
| Special Principal | SID | Replaced With |
|---|---|---|
| CREATOR OWNER | S-1-3-0 | SID of the object's creator (for inherited ACEs) |
| CREATOR GROUP | S-1-3-1 | Primary group of the object's creator |
| OWNER RIGHTS | S-1-3-4 | The current owner (dynamic evaluation) |
| PRINCIPAL SELF | S-1-5-10 | The identity of the object itself (AD objects) |
123456789101112131415161718192021222324252627282930
# CREATOR OWNER allows granting permissions to whoever creates objects# This is useful for home directories, drop boxes, etc. # Set up a drop box where each user owns their own filesC:\> mkdir C:\DropBox # Grant Everyone write access to create filesC:\> icacls C:\DropBox /grant "Everyone:(CI)(WDAC)"# WDAC = Write DAC + Write Data + Add Subdirectory + Create File # Grant CREATOR OWNER full control on items they createC:\> icacls C:\DropBox /grant "CREATOR OWNER:(OI)(CI)(IO)F"# (OI)(CI) = Inherit to all children# (IO) = Inherit only (don't apply to DropBox itself)# F = Full control # When Alice creates a file:C:\> echo secret > C:\DropBox\alice_file.txt # Check permissions on alice_file.txt:C:\> icacls C:\DropBox\alice_file.txt CONTOSO\alice:(F) # CREATOR OWNER was replaced with alice! Everyone:(R) # Inherited read (if set) # When Bob creates a file in the same directory:C:\> echo data > C:\DropBox\bob_file.txt C:\> icacls C:\DropBox\bob_file.txt CONTOSO\bob:(F) # CREATOR OWNER replaced with bob # Alice cannot access bob's file (unless Everyone has access)Windows allows objects to PROTECT their ACL from inheritance using the SE_DACL_PROTECTED flag. When set, the object ignores inherited ACEs and uses only explicit ACEs. This is visible in GUI as 'Disable inheritance' and in icacls as /inheritance:r (remove). Protected ACLs are critical for objects that need different permissions than their parent.
When multiple inheritance sources exist or when inherited permissions conflict with explicit permissions, the operating system must resolve these conflicts. The resolution rules differ significantly between POSIX and Windows.
123456789101112131415161718192021222324252627282930313233343536373839
# Windows canonical ACE order (SE_DACL_AUTO_INHERIT_REQ):## 1. Explicit Deny ACEs# 2. Explicit Allow ACEs # 3. Inherited Deny ACEs (from nearest ancestor first)# 4. Inherited Allow ACEs (from nearest ancestor first) # Example conflict scenario:# Parent grants Users read, but we want to deny a specific user # Parent folder ACL:C:\> icacls C:\Data BUILTIN\Users:(OI)(CI)(R) # Child file inherits:C:\> icacls C:\Data\file.txt BUILTIN\Users:(I)(R) # Inherited read # Now add explicit deny for EvilUser:C:\> icacls C:\Data\file.txt /deny "EvilUser:(R)" # Result - properly ordered:C:\> icacls C:\Data\file.txt CONTOSO\EvilUser:(DENY)(R) # Explicit deny - checked FIRST BUILTIN\Users:(I)(R) # Inherited allow - checked AFTER # EvilUser is denied even though they're in Users group# because explicit deny is evaluated before inherited allow! # Another scenario: Granting MORE than parent allowsC:\> icacls C:\Data\file.txt /grant "PowerUser:(M)" # Result:C:\> icacls C:\Data\file.txt CONTOSO\PowerUser:(M) # Explicit allow BUILTIN\Users:(I)(R) # Inherited allow # PowerUser gets Modify even though Users only gets Read# because explicit ACEs are independent of inherited ACEsIn Windows, deep directory structures can accumulate many inherited ACEs. A file 10 levels deep might have 10+ inherited ACEs, one from each ancestor. This can bloat security descriptors and slow access checks. Use inheritance judiciously and consider protection points to limit accumulation. The $SECURE deduplication helps, but doesn't eliminate evaluation overhead.
Configure default permissions according to the principle of least privilege: start restrictive and explicitly grant additional access as needed. Here are battle-tested configurations for common scenarios:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
# ============================================# SCENARIO 1: User Home Directories# ============================================# Goal: Private by default, user controls sharing # System-wide umask for new sessionsecho "umask 077" >> /etc/profile # For /home skeleton (new user template)chmod 700 /etc/skel# No default ACL - let users decide # Result: ~/newfile = -rw------- (600)# ~/newdir = drwx------ (700) # ============================================# SCENARIO 2: Team Project Directory# ============================================# Goal: Team members have full access, others have none mkdir /projects/team-alphachown projectlead:team-alpha /projects/team-alphachmod 2770 /projects/team-alpha # setgid for group inheritance # Set default ACL for consistent permissionssetfacl -d -m u::rwx /projects/team-alphasetfacl -d -m g::rwx /projects/team-alphasetfacl -d -m o::--- /projects/team-alphasetfacl -d -m m::rwx /projects/team-alpha # Result: All new files get team-alpha group ownership# All team members can read/write # ============================================# SCENARIO 3: Web Server Document Root# ============================================# Goal: Apache can read, webmasters can write, others none mkdir /var/www/sitechown root:webmasters /var/www/sitechmod 2750 /var/www/site # Default ACL for new contentsetfacl -d -m u::rwx /var/www/sitesetfacl -d -m g::rwx /var/www/sitesetfacl -d -m g:www-data:rx /var/www/site # Apache groupsetfacl -d -m o::--- /var/www/sitesetfacl -d -m m::rwx /var/www/site # Result: New files readable by Apache, writable by webmasters # ============================================# SCENARIO 4: Sensitive Configuration Directory# ============================================# Goal: Root only, with specific service account read mkdir /etc/secretschmod 700 /etc/secrets # Only specific service can readsetfacl -m u:myservice:rx /etc/secretssetfacl -d -m u::rwx /etc/secretssetfacl -d -m u:myservice:r /etc/secretssetfacl -d -m g::--- /etc/secretssetfacl -d -m o::--- /etc/secrets # Result: Only root and myservice can accessWhen newly created files don't have the expected permissions, systematic debugging is required. Here's a comprehensive troubleshooting guide:
123456789101112131415161718192021222324252627282930313233343536373839404142434445
# Problem: New files have wrong permissions # Step 1: Check the current umask$ umask0022 # Step 2: Check if parent has default ACL$ getfacl /path/to/parent/# Look for "default:" entries # If default ACL exists, umask is ignored!# The default ACL determines permissions instead. # Step 3: Trace what's happening# Use strace to see the actual mode requested$ strace -e trace=open,openat,mkdir touch /path/to/newfile 2>&1 | grep -E 'open|mkdir'openat(AT_FDCWD, "/path/to/newfile", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666) = 3 # Application requested 0666, not 0644# Something between 0666 and final is modifying it # Step 4: Check for ACL inheritance issues$ getfacl /path/to/newfile# Compare with parent's default ACL # Step 5: Verify the mask computation# mask = (requested_mode & default_mask) for group/other bits# If file has:# user::rw-# group::rwx #effective:r--# mask::r--# The mask of r-- came from requested mode 0644 (not 0666) # Step 6: Check for filesystem mount options$ mount | grep /path# Look for: noacl, acl options# Some filesystems disable ACL by default # Step 7: Check if copying preserved ACLs$ cp --preserve=all source dest # Preserves ACLs$ cp source dest # May not preserve ACLs!$ rsync -A source dest # -A preserves ACLs # Fix: Set correct default ACL on parent$ setfacl -d -m u::rw-,g::r--,o::r--,m::r-- /path/to/parent/1234567891011121314151617181920212223242526272829303132333435363738394041
# Problem: New files have unexpected permissions # Step 1: Check current permissions including inheritance sourceC:\> icacls "C:\Path\To\NewFile" /t# Look for (I) flag indicating inherited ACE# Follow ancestor chain to find source # Step 2: Check parent folder inheritance settingsC:\> icacls "C:\Path\To\"# Look for (OI)(CI) on parent ACEs # Step 3: Determine if application provided security descriptor# Many apps use NULL security attributes, meaning inheritance applies# Check application documentation or use Process Monitor # Step 4: Use Process Monitor to trace permission setting# Download from Sysinternals# Filter: Path contains "NewFile", Operation is "SetSecurityFile"# See what security descriptor is being set # Step 5: Check for inheritance blockingC:\> icacls "C:\Path\To\NewFile"# If no (I) entries, inheritance might be disabled # Step 6: Verify CREATOR OWNER substitution# If expecting creator ownership, check if CREATOR OWNER# ACE exists on parent with (OI)(CI)(IO) # Step 7: Check effective permissionsC:\> icacls "C:\Path\To\NewFile" /grant "TestUser:(R)"C:\> icacls "C:\Path\To\NewFile" # Or use PowerShell:(Get-Acl "C:\Path\To\NewFile").Access | Format-Table # Step 8: Reset inheritanceC:\> icacls "C:\Path\To\NewFile" /reset# This re-inherits from parent and removes explicit entries # Fix: Repair parent folder ACL if neededC:\> icacls "C:\Path\To" /grant "Users:(OI)(CI)M"Default permissions are determined through a systematic pipeline that balances application intent with system policy. Understanding this pipeline is essential for configuring secure systems and debugging permission issues. Let's consolidate the key concepts:
What's Next:
Now that you understand how permissions are assigned, we'll explore ACL inheritance in greater depth in the next page. You'll learn advanced inheritance patterns, multi-level inheritance chains, and how to design inheritance hierarchies for complex organizational structures.
You now understand the complete mechanism of default permission assignment in both POSIX and Windows systems. You can configure umask, create default ACLs, set up Windows inheritance, and debug permission problems. Next, we'll explore advanced inheritance patterns and multi-level permission hierarchies.