🏠 Home / Hub

Linux 02 — File Permissions & Users

Linux uses a discretionary access control (DAC) model. Every file has an owner, a group, and three sets of permissions. Understanding this system is essential for security and system administration.

1. Understanding the Permission String

When you run ls -l, the first column shows permissions in this format:

d r w x r - x r - x
PositionValueMeaning
1dFile type: d=directory, -=file, l=symlink, c=char device, b=block device, p=pipe, s=socket
2-4rwxOwner (user) permissions: r=read, w=write, x=execute
5-7r-xGroup permissions: r=read, -=no write, x=execute
8-10r-xOthers (world) permissions: r=read, -=no write, x=execute

What permissions mean for Files vs Directories

PermissionOn a FileOn a Directory
r (read)Can read file contents (cat, less)Can list contents (ls)
w (write)Can modify file contentsCan create/delete files inside
x (execute)Can run as a programCan enter with cd, access files
# Common permission strings you'll see
-rw-r--r--   # Regular file: owner can read/write, others read-only (e.g. /etc/passwd)
-rw-------   # Only owner can read/write (e.g. ~/.bash_history, SSH keys)
-rwxr-xr-x   # Executable: owner full, others can read+execute (e.g. /bin/ls)
drwxr-xr-x   # Directory: owner full, others can ls and cd into
drwx------   # Directory only owner can access (e.g. ~/.ssh)
-rwxrwxrwx   # World-writable — DANGEROUS, avoid this
lrwxrwxrwx   # Symlink (permissions are always 777, actual target's perms apply)

2. chmod — Change File Permissions

Symbolic Mode

# Syntax: chmod [who][operator][permissions] file
# who:      u=user/owner  g=group  o=others  a=all (u+g+o)
# operator: +=add  -=remove  ==set exactly
# perms:    r=read  w=write  x=execute  X=execute if dir or already executable

$ chmod u+x script.sh           # add execute for owner
$ chmod g-w file.txt            # remove write from group
$ chmod o=r file.txt            # set others to read-only exactly
$ chmod a+r public.txt          # add read for everyone
$ chmod u+x,g-w,o= file.txt     # multiple changes at once
$ chmod a-x binary_file         # remove execute from everyone
$ chmod g+rw shared.txt         # give group read+write
$ chmod u=rwx,g=rx,o= script    # set all three at once
$ chmod -R g+rX /var/www/       # recursive: add group read + execute on dirs

Numeric (Octal) Mode

# Each permission has a numeric value:
# r = 4, w = 2, x = 1
# Add values for each set: owner, group, others

# Common combinations
$ chmod 644 file.txt            # -rw-r--r-- (standard file)
$ chmod 755 script.sh           # -rwxr-xr-x (executable)
$ chmod 700 private_dir/        # drwx------ (private)
$ chmod 777 shared/             # drwxrwxrwx (world-writable — AVOID)
$ chmod 600 ~/.ssh/id_rsa       # -rw------- (SSH private key)
$ chmod 400 ~/.ssh/id_rsa       # -r-------- (read-only private key)
$ chmod 664 group_file.txt      # -rw-rw-r-- (owner+group write)
$ chmod 440 /etc/sudoers        # -r--r----- (sudoers file)

Permission Number Reference Table

NumberBinaryrwxMeaning
0000---No permissions
1001--xExecute only
2010-w-Write only
3011-wxWrite + Execute
4100r--Read only
5101r-xRead + Execute
6110rw-Read + Write
7111rwxFull (Read + Write + Execute)
# Quick reference: chmod NNN means owner=N, group=N, others=N
# chmod 754 file → owner=7(rwx), group=5(r-x), others=4(r--)
# chmod 640 file → owner=6(rw-), group=4(r--), others=0(---)

# Recursive chmod
$ chmod -R 755 /var/www/html/   # apply to all files and dirs recursively
$ chmod -R u=rwX,go=rX /srv/   # uppercase X: only if dir or already executable

3. chown and chgrp — Change Ownership

# chown — change file owner and/or group
# Syntax: chown [owner][:group] file...

$ chown alice file.txt           # change owner to alice
$ chown alice:developers file.txt # change owner and group
$ chown :developers file.txt     # change group only (note the colon)
$ chown -R www-data:www-data /var/www/   # recursive ownership change
$ chown --from=alice bob file.txt # change only if current owner is alice

# chgrp — change group only
$ chgrp developers project/      # change group to developers
$ chgrp -R www-data /var/www/    # recursive group change
$ chgrp $(id -gn) file.txt       # set to your current primary group

# View ownership
$ ls -l file.txt
-rw-r--r-- 1 alice developers 1234 Jun 15 file.txt
#               owner  group
Note: Only root can change file ownership to another user. Regular users can change group ownership only to groups they belong to.

4. umask — Default Permission Mask

umask defines which permissions are removed from newly created files. It's subtracted from the maximum permissions (666 for files, 777 for directories).

# View current umask
$ umask
0022

# How it works:
# Files max:       666 (rw-rw-rw-)
# Dirs max:        777 (rwxrwxrwx)
# umask 022:
#   New file:  666 - 022 = 644 (rw-r--r--)
#   New dir:   777 - 022 = 755 (rwxr-xr-x)

# Common umask values
$ umask 022          # standard: files=644, dirs=755
$ umask 027          # group no write, others nothing: files=640, dirs=750
$ umask 077          # private: files=600, dirs=700 (good for ~/.ssh)
$ umask 002          # group-friendly: files=664, dirs=775

# Set umask in ~/.bashrc to persist
echo "umask 022" >> ~/.bashrc

# Show umask in symbolic form
$ umask -S
u=rwx,g=rx,o=rx

5. Special Permissions: setuid, setgid, Sticky Bit

setuid (SUID) — 4xxx

# When set on an executable, it runs as the FILE OWNER, not the running user
# Classic example: /usr/bin/passwd (must write to /etc/shadow as root)
$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 /usr/bin/passwd
#   ^ s in place of x means SUID is set

$ chmod u+s /usr/bin/myprog     # add SUID
$ chmod 4755 /usr/bin/myprog    # numeric: 4 = SUID
$ find / -perm -4000 -type f    # find all SUID files (security audit)

setgid (SGID) — 2xxx

# On executables: runs as the FILE GROUP
# On directories: new files inherit the directory's group (very useful!)
$ ls -l /usr/bin/wall
-rwxr-sr-x 1 root tty 30800 /usr/bin/wall
#        ^ s in group execute position = SGID

# SGID on directory: shared project directory
$ mkdir /srv/project
$ chown :developers /srv/project
$ chmod g+s /srv/project         # any file created inherits 'developers' group
$ chmod 2775 /srv/project        # numeric

Sticky Bit — 1xxx

# On directories: only the FILE OWNER can delete their own files
# Classic use: /tmp — anyone can write, but can't delete others' files
$ ls -ld /tmp
drwxrwxrwt 20 root root 4096 Jun 15 /tmp
#         ^ t = sticky bit set

$ chmod +t /shared/upload/       # add sticky bit
$ chmod 1777 /shared/upload/     # numeric: 1 = sticky
$ chmod -t /dir/                 # remove sticky bit

6. User Management

# useradd — create user
$ useradd alice                          # create user with defaults
$ useradd -m alice                       # create with home directory
$ useradd -m -s /bin/bash alice          # set login shell
$ useradd -m -s /bin/bash -G sudo,docker alice  # add to groups
$ useradd -m -c "Alice Smith" alice      # set comment/full name
$ useradd -u 1500 -m alice               # specify UID
$ useradd -e 2024-12-31 alice            # account expiry date

# usermod — modify existing user
$ usermod -aG docker alice               # add to group (MUST use -a flag!)
$ usermod -aG sudo,developers alice      # add to multiple groups
$ usermod -s /bin/zsh alice              # change shell
$ usermod -l newname oldname             # rename user account
$ usermod -L alice                       # lock account (disable password)
$ usermod -U alice                       # unlock account
$ usermod -e "" alice                    # remove expiry date

# userdel — remove user
$ userdel alice                          # remove user (keep home dir)
$ userdel -r alice                       # remove user AND home directory
$ userdel -f alice                       # force (even if logged in)

# passwd — set/change password
$ passwd alice                           # set password for alice (as root)
$ passwd                                 # change your own password
$ passwd -l alice                        # lock account
$ passwd -u alice                        # unlock account
$ passwd -e alice                        # force password change on next login
$ passwd -d alice                        # delete password (no password needed)

7. /etc/passwd, /etc/shadow, /etc/group

/etc/passwd — User Account Information

# Format: username:password:UID:GID:comment:home:shell
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
alice:x:1000:1000:Alice Smith,,,:/home/alice:/bin/bash
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
# 'x' in password field means hash is in /etc/shadow
# /usr/sbin/nologin prevents interactive login (system accounts)

/etc/shadow — Encrypted Password Hashes

# Format: username:hash:lastchange:min:max:warn:inactive:expire:reserved
# Only readable by root
$ sudo cat /etc/shadow
alice:$6$rounds=5000$salt$hashedpassword:19523:0:99999:7:::
# $6$ = SHA-512 hash algorithm
# $1$ = MD5 (old, insecure)
# $5$ = SHA-256

/etc/group — Group Definitions

# Format: groupname:password:GID:member1,member2,...
$ cat /etc/group
root:x:0:
sudo:x:27:alice,bob
docker:x:999:alice,charlie
developers:x:1001:alice,bob,charlie

# Add user to group without usermod
$ gpasswd -a alice developers    # add alice to developers group
$ gpasswd -d alice developers    # remove alice from developers

8. Checking Identity and Groups

# Who am I?
$ whoami
alice

# Full identity info
$ id
uid=1000(alice) gid=1000(alice) groups=1000(alice),27(sudo),999(docker)

$ id alice              # check another user's identity
$ id -u                 # print only UID
$ id -g                 # print only primary GID
$ id -G                 # print all GIDs
$ id -Gn                # print all group names

# Show groups
$ groups                # list groups current user belongs to
$ groups alice          # list groups for specific user

# Currently logged-in users
$ who                   # show who is logged in
$ w                     # show who + what they're doing
$ last                  # login history
$ last alice            # login history for specific user
$ lastb                 # failed login attempts (bad logins)

9. sudo — Execute as Another User

# sudo — superuser do
$ sudo apt update               # run as root
$ sudo -u alice command         # run as specific user
$ sudo -i                       # start root interactive shell (with root env)
$ sudo -s                       # start root shell (with current env)
$ sudo !!                       # re-run last command with sudo
$ sudo -l                       # list what sudo commands you can run
$ sudo -l -U alice              # list alice's sudo permissions (as root)

# /etc/sudoers — sudo configuration (ALWAYS edit with visudo)
$ sudo visudo                   # safely edit sudoers (validates syntax)

# Common sudoers entries:
# alice   ALL=(ALL:ALL) ALL       # alice can run any command as any user
# %sudo   ALL=(ALL:ALL) ALL       # sudo group members can run anything
# alice   ALL=(ALL) NOPASSWD: ALL # alice needs no password for sudo
# alice   ALL=/usr/bin/apt        # alice can only run apt with sudo
# @includedir /etc/sudoers.d      # include files from this directory

# Drop-in sudoers files (preferred method)
$ echo "alice ALL=(ALL) NOPASSWD: /usr/bin/systemctl" | sudo tee /etc/sudoers.d/alice
$ sudo chmod 440 /etc/sudoers.d/alice

10. su — Switch User

# su — substitute user
$ su alice                   # switch to alice (uses alice's password)
$ su - alice                 # switch with full login environment
$ su -                       # switch to root (full login shell)
$ su -c "command" alice      # run single command as alice
$ su -c "apt update" root    # run single command as root

# Difference between su and su -:
# su alice:   switches user but keeps current environment variables
# su - alice: full login — gets alice's PATH, HOME, env variables
Best practice: Use sudo instead of su on modern systems. sudo provides better auditing (commands are logged), doesn't require sharing the root password, and allows fine-grained permission control.

11. ACLs — Access Control Lists

ACLs extend standard permissions. They let you set permissions for specific users or groups beyond the standard owner/group/others model.

# Install acl tools
$ sudo apt install acl          # Debian/Ubuntu
$ sudo yum install acl          # CentOS/RHEL

# getfacl — view ACL
$ getfacl /var/www/html/
# file: var/www/html/
# owner: root
# group: www-data
user::rwx
group::r-x
other::r-x

# setfacl — set ACL
$ setfacl -m u:alice:rwx /var/www/html/        # give alice full access
$ setfacl -m g:developers:rx /srv/app/         # give developers group r-x
$ setfacl -m o::--- /private/                  # remove all other access
$ setfacl -x u:alice /var/www/html/            # remove alice's ACL entry
$ setfacl -b /var/www/html/                    # remove all ACL entries
$ setfacl -R -m u:alice:rw /srv/data/          # recursive ACL set
$ setfacl -d -m g:developers:rwx /shared/     # default ACL (for new files)

# Check if ACL is set — ls shows '+' after permissions
$ ls -l /var/www/html/
drwxr-xr-x+ 2 root www-data 4096 Jun 15 /var/www/html/
#          ^ '+' indicates ACL is present

12. Practical Permission Scenarios

# Scenario 1: Web server files
$ chown -R www-data:www-data /var/www/html/
$ chmod -R 644 /var/www/html/          # files: owner rw, others r
$ find /var/www/html -type d -exec chmod 755 {} \;  # dirs need execute

# Scenario 2: Shared development directory
$ mkdir /srv/devteam
$ chown root:developers /srv/devteam
$ chmod 2775 /srv/devteam              # SGID + rwxrwxr-x

# Scenario 3: SSH key security (CRITICAL)
$ chmod 700 ~/.ssh/                    # only owner can access dir
$ chmod 600 ~/.ssh/id_rsa              # private key: only owner read/write
$ chmod 644 ~/.ssh/id_rsa.pub          # public key: others can read
$ chmod 600 ~/.ssh/authorized_keys     # authorized keys: only owner
$ chmod 644 ~/.ssh/known_hosts         # known hosts: others can read

# Scenario 4: Log files that app needs to write
$ touch /var/log/myapp.log
$ chown myappuser:myappgroup /var/log/myapp.log
$ chmod 640 /var/log/myapp.log         # app writes, admins read, others nothing

📌 Study Checklist