This page builds on Users, Permissions and Access Control, which explains accounts, root and sudo, the rwx bits for user, group and others, and octal notation. Here you do the work: manage accounts and groups, set permissions precisely, and hand out administrator rights in small, controlled pieces.

Where accounts are stored

Linux keeps local accounts in three plain-text files under /etc. Tools such as useradd and passwd edit them for you; you read them to understand what those tools did. (In companies, accounts often come from a directory service such as LDAP (Lightweight Directory Access Protocol) or Active Directory as well; getent passwd ana looks a user up in every configured source, not only in the local file.)

/etc/passwd

/etc/passwd is the local account database: one line per account with seven colon-separated fields — name, password placeholder, user ID (UID), primary group ID (GID), comment, home directory and login shell. Despite the name it no longer holds passwords, and every user can read it, because programs such as ls need it to turn UIDs into names.

$ grep -E '^(root|www-data|ana):' /etc/passwd
root:x:0:0:root:/root:/bin/bash
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
ana:x:1001:1001:Ana Lima,,,:/home/ana:/bin/bash
Field Example Meaning
1 Name ana The login name
2 Password x "The hash is in /etc/shadow"
3 UID 1001 User ID: what the kernel actually uses. 0 is root; regular users start at 1000 on current distributions, lower numbers are system accounts for services
4 GID 1001 The primary group's ID
5 Comment Ana Lima,,, Full name and optional contact details (historically called GECOS)
6 Home /home/ana Home directory
7 Shell /bin/bash Program started at login; /usr/sbin/nologin refuses interactive logins

Service accounts such as www-data have a nologin shell on purpose: the web server runs as that user, but nobody can log in as it. cut pulls single fields out of such lines, as described on Pipes, Redirection and Filters:

cat <<'EOF' > accounts.txt
root:x:0:0:root:/root:/bin/bash
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
ana:x:1001:1001:Ana Lima,,,:/home/ana:/bin/bash
ben:x:1002:1002:Ben Okafor,,,:/home/ben:/bin/bash
EOF
echo "--- name and shell:"
cut -d: -f1,7 accounts.txt
echo "--- accounts that can log in with bash:"
grep ':/bin/bash$' accounts.txt | cut -d: -f1
--- name and shell:
root:/bin/bash
www-data:/usr/sbin/nologin
ana:/bin/bash
ben:/bin/bash
--- accounts that can log in with bash:
root
ana
ben

/etc/shadow

/etc/shadow holds the password hashes and password-aging settings, one line per account. It is readable only by root (on Debian and Ubuntu also by the shadow group: mode 640, owner root:shadow). On RHEL the mode is 000 and only root can read it. Splitting the hashes off from /etc/passwd keeps them away from ordinary users, who could otherwise copy them and try to crack them offline.

$ sudo grep '^ana:' /etc/shadow
ana:$y$j9T$Qm1r…$4hV0…:20362:0:99999:7:::

The fields: name, hash, date of the last password change (in days since 1 January 1970), minimum and maximum days between changes, warning days, and three fields for inactivity and expiry. The hash starts with its algorithm: $y$ is yescrypt (the default on current Debian, Ubuntu and Fedora), $6$ is SHA-512-crypt, a salted and iterated scheme built on SHA-512 (Secure Hash Algorithm; the default on Red Hat Enterprise Linux, RHEL). A hash field starting with ! means the password is locked; * means the account never had a usable password. sudo chage -l ana shows the aging settings in readable form.

/etc/group

/etc/group lists the groups: name, password placeholder, GID, and the members for whom it is a supplementary group, comma-separated:

sudo:x:27:ana
ana:x:1001:
ben:x:1002:
developers:x:1003:ana,ben

Notice that ana is not listed as a member of the group ana: a user's primary group comes from field 4 of /etc/passwd, not from this list.

Primary and supplementary groups

Every user has exactly one primary group: the group recorded in the user's /etc/passwd line, which becomes the group owner of every file the user creates. Most distributions create a personal group with the same name as the user for this (a "user private group"). On top of that a user can be in any number of supplementary groups, which grant access to files owned by those groups but do not affect new files.

id shows both, with numbers and names; groups shows only the names. Both describe your current login session:

$ id
$ groups
$ id ben
uid=1001(ana) gid=1001(ana) groups=1001(ana),27(sudo),1003(developers)
ana sudo developers
uid=1002(ben) gid=1002(ben) groups=1002(ben),1003(developers)

gid= is the primary group, groups= lists all of them. Group membership is read at login: after someone adds you to a group, you only get the new rights in a new login session (log out and in again, or start a new SSH (Secure Shell) connection). newgrp developers opens a subshell with the new group as a stopgap.

Creating and managing accounts

These commands change the system, so they run as root. The session below creates a user for a new colleague, adds her to a team group, and removes a former colleague (illustrative, Ubuntu 24.04):

$ sudo useradd -m -s /bin/bash -c "Carla Rossi" carla
$ sudo passwd carla
New password:
Retype new password:
passwd: password updated successfully
$ sudo usermod -aG developers carla
$ id carla
uid=1004(carla) gid=1004(carla) groups=1004(carla),1003(developers)
$ sudo userdel -r dave
Command What it does
useradd -m -s /bin/bash -c "Name" user Create an account; -m creates the home directory (copied from /etc/skel), -s sets the shell, -c the comment
passwd user Set or change a password (passwd alone changes your own); passwd -l locks, -u unlocks. Until a password is set, a new account is locked (! in /etc/shadow)
usermod -aG group user Add the user to a supplementary group
usermod -s /usr/sbin/nologin user Change the login shell (here: disable interactive logins)
usermod -L user / -U Lock / unlock the password (SSH keys still work; also use chage -E 0 or a nologin shell)
gpasswd -d user group Remove the user from one group
userdel -r user Delete the account; -r also removes the home directory and mail spool
groupadd group / groupdel group Create / delete a group
Debian / Ubuntu RHEL / Fedora
useradd without -m Creates no home directory; default shell /bin/sh Creates the home directory; default shell /bin/bash
Friendly wrapper adduser carla asks for name and password interactively adduser is the same program as useradd
Administrators' group sudo wheel

Changing ownership: chown and chgrp

chown user:group file changes owner and group in one go; chown user file only the owner, chown :group file or chgrp group file only the group. Giving a file away requires root, otherwise users could dodge disk quotas or plant files in someone else's name; the owner may change the group to another group they belong to.

$ sudo chown carla:developers /srv/app/deploy.sh
$ sudo chgrp -R developers /srv/app
$ ls -l /srv/app/deploy.sh
-rwxr-x--- 1 carla developers 812 Oct  1 09:30 /srv/app/deploy.sh

Permission bits in depth

The mode string drwxr-x--- split into its parts: the file type d, then rwx for the user owner with value 7, r-x for the group with value 5, and three dashes for others with value 0, giving octal 750. A fourth, leading octal digit holds the special bits setuid 4, setgid 2 and sticky 1. d r w x r - x - - - type user (owner) group others 4+2+1 = 7 4+0+1 = 5 0+0+0 = 0 0750 leading digit = special bits: 4 setuid · 2 setgid · 1 sticky
A mode is a type character plus three sets of rwx. In octal, each set is one digit (r = 4, w = 2, x = 1); an optional leading digit holds the special bits.

What r, w and x mean on a directory

A directory is a list of names pointing to files. Its permission bits control that list, not the files themselves, and that explains every surprise:

  • r lets you read the list: see which names are in it.
  • x lets you pass through: enter it with cd and reach the files inside by name. Without x you cannot open a file inside, even one you own.
  • w lets you change the list: create, rename and delete entries. It only works together with x. Deleting a file is a change to the directory, so it depends on the directory's w, not on the file's permissions.

You can try each case on a directory of your own:

mkdir box
echo "hello" > box/file.txt
chmod u=r box
echo "--- r only: list, but not open"
ls box
cat box/file.txt
chmod u=x box
echo "--- x only: open by name, but not list"
ls box
cat box/file.txt
chmod u=rx box
echo "--- r and x, no w: cannot create or delete"
touch box/new.txt
rm box/file.txt
chmod u=rwx box
--- r only: list, but not open
file.txt
cat: box/file.txt: Permission denied
--- x only: open by name, but not list
ls: cannot open directory 'box': Permission denied
hello
--- r and x, no w: cannot create or delete
touch: cannot touch 'box/new.txt': Permission denied
rm: cannot remove 'box/file.txt': Permission denied

With only r, ls -l shows the names with question marks in place of the details, because reading a file's details also needs x. The x-only case is useful: a web server can reach /home/ana/public_html/index.html when each directory on the way grants others x, without being able to list anything.

chmod in practice

chmod accepts octal modes, which set all bits at once, and symbolic changes, which adjust only the bits you name: who (u user, g group, o others, a all), an operator (+ add, - remove, = set exactly) and the bits. stat -c '%a %A %n' prints the mode in octal and symbolic form next to the name:

touch deploy.sh notes.txt
chmod 644 deploy.sh;       stat -c '%a %A %n' deploy.sh
chmod u+x deploy.sh;       stat -c '%a %A %n' deploy.sh
chmod go-r deploy.sh;      stat -c '%a %A %n' deploy.sh
chmod 750 deploy.sh;       stat -c '%a %A %n' deploy.sh
chmod a=r,u+w notes.txt;   stat -c '%a %A %n' notes.txt
644 -rw-r--r-- deploy.sh
744 -rwxr--r-- deploy.sh
700 -rwx------ deploy.sh
750 -rwxr-x--- deploy.sh
644 -rw-r--r-- notes.txt

Symbolic mode is safer when you only want to add or remove one thing, because it leaves the other bits alone; octal is clearer when you want to know exactly what the result is. For recursive changes and the capital X trick, see the warning on chmod and chown, and never solve an access problem with chmod 777.

Default permissions: umask

The umask (user file-creation mask) is a per-process setting that lists the permission bits to remove from every new file and directory. Programs create files with mode 666 (rw-rw-rw-) and directories with 777, and the umask takes bits away: with 022, write permission is removed for group and others, giving 644 files and 755 directories.

umask 022
touch report.txt; mkdir shared-docs
stat -c '%a %A %n' report.txt shared-docs
umask 027
touch payroll.txt; mkdir hr
stat -c '%a %A %n' payroll.txt hr
umask 077
touch id_backup; mkdir private
stat -c '%a %A %n' id_backup private
umask -S
644 -rw-r--r-- report.txt
755 drwxr-xr-x shared-docs
640 -rw-r----- payroll.txt
750 drwxr-x--- hr
600 -rw------- id_backup
700 drwx------ private
u=rwx,g=,o=

Two details. The umask only ever removes bits, so it never makes a new file executable; that is why scripts need an explicit chmod +x. And it is not simple subtraction: 666 with umask 027 is 640, not 639 — each bit in the mask is switched off. umask without arguments prints the current mask, umask -S the permissions that remain. It is set per shell, so a change lasts until you close the terminal; the system-wide default comes from /etc/login.defs or the shell startup files, and services can have their own (UMask= in a systemd unit). Debian and root typically use 022; Ubuntu, Fedora and RHEL typically give ordinary users 002 (each user has a private group, so group-write gives nobody else access).

Special permissions: setuid, setgid and sticky

Three extra bits sit in front of the normal nine. In octal they form a fourth, leading digit; in ls -l they replace the x of the user, group or others position.

Bit Octal Shown as On a program On a directory
setuid 4000 rws------ Runs with the file owner's rights (ignored on Linux)
setgid 2000 ---rws--- Runs with the file's group rights New files inherit the directory's group
sticky 1000 --------t (no effect on Linux) Only a file's owner (or the directory's owner, or root) may delete or rename it

A capital S or T means the special bit is set but the underlying x is not, which is almost always a mistake on a program.

setuid

Setuid (set user ID) is a permission bit on an executable file that makes it run with the privileges of the file's owner instead of the user who started it. The classic example is passwd: an ordinary user must be able to change their own password, which means writing /etc/shadow, which only root may do. /usr/bin/passwd is owned by root and setuid, so it runs as root — and is written carefully to change only the caller's own entry (illustrative):

$ ls -l /usr/bin/passwd
$ ls -ld /tmp
-rwsr-xr-x 1 root root 64152 May 30  2024 /usr/bin/passwd
drwxrwxrwt 18 root root 4096 Oct  1 09:41 /tmp

Every setuid-root program is a potential way to become root if it has a bug, so the list should be short and known. sudo find / -xdev -perm -4000 -type f lists them; a setuid file in a home directory or under /tmp is a red flag. Linux ignores setuid on scripts (only compiled programs honor it), and a file system mounted with nosuid ignores it entirely.

setgid on shared directories

Setgid on a directory is the everyday use. Normally a new file gets the creator's primary group, so in a team folder every file ends up in a different personal group and colleagues cannot edit each other's work. With setgid set on the folder, new files and subdirectories inherit the folder's group instead (and new subdirectories inherit the setgid bit too):

$ sudo mkdir /srv/projects
$ sudo chgrp developers /srv/projects
$ sudo chmod 2770 /srv/projects     # rwxrws---: group members only, setgid
$ touch /srv/projects/plan.md
$ ls -l /srv/projects
-rw-rw-r-- 1 ana developers 0 Oct  1 10:02 plan.md

The file belongs to group developers, not to ana. Combine it with a umask of 002 for team members so the group also gets write permission on new files.

The sticky bit

The sticky bit is a permission bit on a directory that allows only a file's owner, the directory's owner or root to delete or rename files in it, even when others have write permission on the directory. That is what makes a world-writable directory such as /tmp (mode 1777) safe to share: everyone may create files there, nobody may remove someone else's.

All three special bits can be set, in octal or symbolically (u+s, g+s, +t), on files and directories you own. Setting setuid on an empty file of your own is harmless and shows what the bit looks like:

mkdir team-folder dropbox
touch program
chmod 2770 team-folder
chmod 1777 dropbox
chmod 4755 program
stat -c '%a %A %n' team-folder dropbox program
chmod u-s program
echo "--- setuid removed, then added back symbolically:"
stat -c '%a %A %n' program
chmod u+s program
stat -c '%a %A %n' program
2770 drwxrws--- team-folder
1777 drwxrwxrwt dropbox
4755 -rwsr-xr-x program
--- setuid removed, then added back symbolically:
755 -rwxr-xr-x program
4755 -rwsr-xr-x program

sudo and the sudoers file

sudo runs one command as root (or as another user) after checking a rule, asks for your own password, and logs the command; its basics are on root and sudo. The rules live in the sudoers policy: the file /etc/sudoers plus drop-in files in /etc/sudoers.d/, which define who may run which commands as which user on which hosts. Each rule reads who where=(as whom) what:

# Members of the admin group: anything, as anyone (Debian/Ubuntu)
%sudo   ALL=(ALL:ALL) ALL

# The same on RHEL/Fedora
%wheel  ALL=(ALL)       ALL

# Ben may only restart the web server, as root
ben     ALL=(root) /usr/bin/systemctl restart nginx

A % marks a group. The narrow rule for Ben is least privilege in practice: he can do the one task his job needs, and nothing else. Being in the sudo or wheel group means full root access, so membership should be as rare as the root password.

Always edit these files with visudo. It opens a temporary copy, checks the syntax when you save and refuses to install a broken file; a syntax error saved directly into /etc/sudoers can stop sudo from working at all, and on a server without a root password that locks every administrator out. If visudo reports an error it asks What now?: press e to fix it, or x to discard your changes. Never press Q, which saves the broken file anyway. Put your own rules in a drop-in file, which survives package upgrades:

$ sudo visudo -f /etc/sudoers.d/webteam     # edit a drop-in file
$ sudo visudo -c                            # check all sudoers files
$ sudo -l -U ben                             # what may ben run?
/etc/sudoers: parsed OK
/etc/sudoers.d/README: parsed OK
/etc/sudoers.d/webteam: parsed OK
Matching Defaults entries for ben on web01:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User ben may run the following commands on web01:
    (root) /usr/bin/systemctl restart nginx

An administrator checks another user with sudo -l -U ben; Ben himself would type sudo -l and see the same list. Files in /etc/sudoers.d/ whose names contain a dot or end in ~ are skipped, so name them webteam, not webteam.conf.