Skip to content

104.5 Manage file permissions and ownership

Weight: 3

Candidates should be able to control file access through the proper use of permissions and ownerships.

Objectives

  • Manage access permissions on regular and special files as well as directories.
  • Use access modes such as suid, sgid and the sticky bit to maintain security.
  • Know how to change the file creation mask.
  • Use the group field to grant file access to group members.

Terms

chmod, umask, chown, chgrp

Users and Groups

A linux system can have many users and many groups. You can log in with one user and use the su command to change to another. Each user belongs to one primary group and can be a member of other groups too.

To check your user and group use the whoami, groups and id commands.

- $ whoami
nagato
- $ groups
nagato adm cdrom sudo dip plugdev netdev lpadmin sambashare debian-tor
- $ id
uid=1000(nagato) gid=1000(nagato) groups=1000(nagato),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),102(netdev),108(lpadmin),124(sambashare),125(debian-tor)
- $ su root -
Password:
- # id
uid=0(root) gid=0(root) groups=0(root)
- # exit
exit
- $ whoami
nagato

As you can see id shows both user and group information.

Note the 0 UID and 0 GID belong to the root user and root group.

Look at the id output closely. gid=1000(nagato) is the primary group, the one used when this user creates a file. The list after groups= is every group they belong to, primary included. Note also that the prompt changed from $ to # after su root, which is the standard signal that you are root.

These are stored in /etc/passwd and /etc/group files.

$ cat /etc/group | grep adm
adm:x:4:syslog,nagato
lpadmin:x:108:nagato

Reading adm:x:4:syslog,nagato: the group name, an x placeholder where a password would go, the GID, then the list of members.

Two more ways to look this up. getent group lists every group on the system:

$ getent group
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:syslog,rigues
tty:x:5:rigues
disk:x:6:
lp:x:7:

groups USERNAME asks which groups one user is in, and groupmems -g GROUP -l does the reverse, listing the members of one group:

$ groups carol
carol : carol students cdrom sudo dip plugdev lpadmin sambashare
# groupmems -g cdrom -l
carol

groupmems needs root.

File ownership & permissions

Linux uses three layers of access and permissions for each file or directory: User, Group and Others.

Each file belongs to one user and one group, and this user and the members of that group will have specific read/write/execute access on it. Have a look:

$ ls -ltrh script.sh
-rwxr-xr-x 1 nagato adm 34 Mar  5  2023 script.sh

In the above example, nagato is the owner of the file. The file belongs to the adm group, and the owner (nagato here) has read, write (including deletion and edit) and execute permissions on the file, while the adm group members and others only have read and execute access.

In many distros, when you create a user, the system creates a group with the same name and assigns that user's files to that group.

Here is how to read those ten characters:

   -  rwx  r-x  r-x
   |   |    |    |
   |   |    |    +--- others   (everyone else)
   |   |    +-------- group    (members of adm)
   |   +------------- user     (nagato, the owner)
   +----------------- type     (- file, d directory, l link)

   r = 4    w = 2    x = 1    - = 0

The below table shows some more information regarding the first part of the ls -l command:

Position Meaning
1 What this entry is. Dash (-) is for ordinary files, 'l' is for links and 'd' is for directory
2,3,4 read, write and execute access for the owner
5,6,7 read, write and execute access for the group members
8,9,10 read, write and execute access for other users
11 Indicates if any other access methods (such as SELinux) apply to this file, not part of the LPIC 101 exam

Let's check another example.

$ ls -l /sbin/fdisk
-rwxr-xr-x 1 root root 267176 Oct 15 18:58 /sbin/fdisk

We can see that fdisk can be read, written and executed by its owner (root). Other users, even if they belong to the group root, can only read and execute it.

Although non-root users can execute fdisk, this program will not do much if it sees that a non-root user is running it.

Let's look at another example:

$ ls -l /home/
total 12
drwxr-xr-x 160 nagato nagato 12288 Feb  7 11:44 nagato

The first character is a d, so this is a directory. The owner (nagato) has read, write and execute access, but other members of the nagato group and others only have read and execute access on this directory.

Execute (x) means that they can see the files inside it. Have a look at what happens if a directory has no x:

(with root user)
$ tree /tmp/FOO
FOO
├── Bar1
└── Bar2

$ ls -ld /tmp/FOO/
drw-r--r--. 2 nagato nagato 80 Mar 23 19:32 FOO/

(With other user)
$ ls -l FOO/
ls: cannot access 'FOO/Bar2': Permission denied
ls: cannot access 'FOO/Bar1': Permission denied
total 0
-????????? ? ? ? ?            ? Bar1
-????????? ? ? ? ?            ? Bar2

That output is worth studying. The other user could read the directory, so they saw the names Bar1 and Bar2. But without x they could not enter it to look up the details, so every other column came back as ?.

Why the three permissions mean different things on a directory than on a file:

        on a FILE                  on a DIRECTORY

   r    read the contents          list the names inside
   w    edit or delete it          create or delete files inside
   x    run it as a program        enter it, and look up what is inside

The w on a directory is the surprising one. Permission to delete a file comes from the directory's w, not from the file's own permissions. And w on a directory is useless without x, since you cannot act on something you cannot enter.

The order the checks happen in catches people out:

   is the current user the OWNER?      -> use owner permissions, stop
   is the user in the owning GROUP?    -> use group permissions, stop
   otherwise                           -> use others permissions

Only one set applies. So if the owner's permissions are r-- and the group's are rwx, the owner still cannot write, even if they are also in that group. The first match wins.

Three more file types beyond -, d and l: b for block devices, c for character devices and s for sockets. Do not change the permissions on those unless you know exactly what you are doing.

It also covers ls -d, needed to see a directory itself rather than its contents, and ls -a for hidden dot files, both of which appeared in 103.3.

Changing permissions

It is possible to change the permissions on files and directories using the chmod command. There are two ways to tell this command what you want to do:

  1. using octal (base 8) codes
  2. using short codes

When using octal codes, you have to create an octal number to tell chmod what you want to do. In this method, 0 means no access, 1 means execute, 2 means write and 4 means read. So if you want to give read+execute, you have to give 4+1 which is 5. The below table shows every possible combination:

Symbolic Octal
rwx 7
rw- 6
r-x 5
r-- 4
-wx 3
-w- 2
--x 1
--- 0

So if you want to give rwx to the owner, rx to the group and only x to others, you have to use 751:

$ ls -ltrh myfile
-rw-rw-r-- 1 nagato nagato 0 Feb  8 21:01 myfile
$ chmod 751 myfile
$ ls -ltrh myfile
-rwxr-x--x 1 nagato nagato 0 Feb  8 21:01 myfile

This might look difficult, but there are some commonly used combinations like 755 for general executable files or 600 for personal files.

The reason the numbers work is that 4, 2 and 1 can only add up one way:

   r w x
   4 2 1
   -----
   7 = 4+2+1 = rwx
   6 = 4+2   = rw-
   5 = 4+  1 = r-x
   4 = 4     = r--

A quick check: if a digit is odd, the file is executable, because only the 1 bit makes a number odd.

There is also an easier method. In this method u means user, g means group and o means others. You can append +x to give execute permission, +r to give read permission and +w to give write permission. For example u+x will grant execute permission to the user. If you want to remove a permission, use a - sign, for example g-r to prevent group members from reading the file.

$ ls -ltrh myfile
-rwxr-x--x 1 nagato nagato 0 Feb  8 21:01 myfile
$ chmod u-x myfile
$ ls -ltrh myfile
-rw-r-x--x 1 nagato nagato 0 Feb  8 21:01 myfile
$ chmod +x myfile
$ chmod uo+xr myfile
$ ls -ltrh myfile
-rwxr-xr-x 1 nagato nagato 0 Feb  8 21:01 myfile

The third operator, =, sets permissions to an exact value instead of adding or removing:

$ chmod a=rw- text.txt
$ ls -l text.txt
-rw-rw-rw- 1 carol carol 765 Dec 20 21:25 text.txt

Here a means all three sets at once. Several changes can be combined with commas:

$ chmod u+rwx,g-x text.txt
$ ls -lh text.txt
-rwxrw-rw- 1 carol carol 765 Dec 20 21:25 text.txt

The symbolic form has three parts:

   chmod  u+x  file
          |||
          ||+--- which permission:  r  w  x
          |+---- what to do:        +  -  =
          +----- who:               u  g  o  a

When to use which: octal when you want to set everything to a known value like 640, symbolic when you want to flip one thing without disturbing the rest.

One very common switch on chmod is -R for recursive chmoding on files. This will give read permission of all files inside /tmp/ to any user:

# chmod -R o+r /tmp

Be careful with -R. It is easy to change permissions on files you did not mean to touch, especially in a directory with many subdirectories.

Changing owner and groups

If you need to change the ownership or group of a file or directory, use the chown command:

$ ls -ltrh newfile
-rw-rw-r-- 1 nagato nagato 0 Feb  8 21:38 newfile
$ chown root:root newfile
chown: changing ownership of 'newfile': Operation not permitted
$ sudo chown root:root newfile
[sudo] password for nagato:
$ ls -ltrh newfile
-rw-rw-r-- 1 root root 0 Feb  8 21:38 newfile

Note the failure on the first attempt. A normal user cannot give a file away to another user, only root can. That is a deliberate rule, since otherwise anyone could dump files into someone else's disk quota.

A common switch is -R to chown recursively.

If you need to only change the group you may use the chgrp command:

$ sudo chgrp postgres newfile
$ ls -ltrh newfile
-rw-rw-r-- 1 root postgres 0 Feb  8 21:38 newfile

The shorthand forms of chown, since either half can be left out:

   chown carol:students file   change both
   chown carol: file           change user only
   chown carol file            change user only
   chown :students file        change group only, same as chgrp

Also worth knowing: the owning user does not have to be a member of the owning group. They are two independent fields.

It is possible to assign more groups to a user via the usermod command:

$ sudo usermod -aG sudo nagato # will add nagato to the sudo group

The -a in -aG matters. It means append. Without it, usermod -G replaces the user's entire group list, silently removing them from every other group.

Since users can be a member of many groups, they might need to change their default group when creating files. To do so, check the groups with the groups command and set the default one with newgrp:

$ touch newfile
$ ls -ltrh newfile
-rw------- 1 nagato nagato 0 Feb  8 21:53 newfile
$ groups
nagato adm cdrom sudo dip plugdev netdev lpadmin sambashare debian-tor
$ newgrp adm
$ touch newerfile
$ ls -ltrh new*
-rw------- 1 nagato nagato 0 Feb  8 21:53 newfile
-rw------- 1 nagato adm  0 Feb  8 21:54 newerfile

The two files show the effect. Before newgrp adm, the new file got group nagato. After it, the new file got group adm.

Access modes

A question for you: if we do not have write access to /etc/passwd or /etc/shadow, how is it possible to change our password?

Normally when you run a program, it runs with your access level. But what happens if you need to run a program with a higher access level, say to make changes in the password files? For this Linux sets two special bits for each file: suid (set user id) and sgid (set group id). If these bits are set on a file, that file will be executed with the access of the owner (or group) of the file, and not the user who is running it.

$ ls -ltrh /usr/bin/passwd
-rwsr-xr-x 1 root root 50K Jul 18  2014 /usr/bin/passwd

Note the s in the executable bit for the user permissions. That means when any user runs this program, it will be run with the access level of the owner of the file, which is root, instead of that user's own.

   normal program:   you run it  ->  runs as YOU

   SUID program:     you run it  ->  runs as the file's OWNER
   (passwd)                          which is root

   that is how passwd can write /etc/shadow
   even though you cannot

It is possible to set and unset the suid and sgid using chmod with +s or -s instead of x.

While we are on this, let me introduce you to the last special bit. It is called the sticky bit, and if set, makes the file resistant to deletion. If the sticky bit is set, ONLY the owner of the file will be able to delete it, even if others do have write access to it. This is useful for places like /tmp, where everybody has write access on the directory but we do not want others to delete our files.

The sticky bit is identified by t and will be shown on the last bit of an ls -l:

$ ls -dl /tmp
drwxrwxrwt 13 root root 77824 Feb  8 21:27 /tmp

As you can see the sticky bit is set, and although all users have write access in this directory, they will not be able to delete each other's files.

This is exactly the fix for the directory w problem noted earlier. Normally w on a directory lets you delete anything in it. The sticky bit narrows that down to your own files.

Let's review how you can set these access modes:

access mode octal symbolic
suid 4000 u+s
sgid 2000 g+s
sticky 1000 +t

sgid on a directory will force any new file in that directory to have the sgid of the directory itself.

The special bits sit in a fourth digit in front of the usual three:

   chmod 6755 test.sh
         ||||
         |+++--- the normal rwx r-x r-x
         +------ special: 4 suid + 2 sgid = 6

   result:  -rwsr-sr-x
                ^  ^
                |  +-- s in group position = sgid
                +----- s in user position  = suid

The sgid directory behaviour in full, which is the most useful of the three in practice. First without sgid:

$ ls -ldh Sample_Directory/
drwxr-xr-x 2 carol users 4,0K Jan 18 17:06 Sample_Directory/
$ cd Sample_Directory/
$ touch newfile
$ ls -lh newfile
-rw-r--r-- 1 carol carol 0 Jan 18 17:11 newfile

The new file got group carol, the user's own primary group. Now with sgid set:

$ sudo chmod g+s Sample_Directory/
$ ls -ldh Sample_Directory/
drwxr-sr-x 2 carol users 4,0K Jan 18 17:17 Sample_Directory/
$ cd Sample_Directory/
$ touch emptyfile
$ ls -lh emptyfile
-rw-r--r-- 1 carol users 0 Jan 18 17:20 emptyfile

Now the file inherited group users from the directory.

Real world use: a shared project folder. Set the directory's group to the team's group and turn on sgid, and every file anyone creates in it automatically belongs to the team, so everyone can read each other's work without anyone having to remember chgrp.

SUID applies only to files and has no effect on directories, while the sticky bit applies only to directories and has no effect on files. And on a colour terminal, ls -l usually shows these with a coloured background: blue for sticky, yellow for sgid, red for suid.

umask

Another tricky question: what are the permissions of a newly created file? What happens if you touch a non-existing file? What will its permissions be? This is set by the umask. This command tells the system what permissions (in addition to execute) should not be given to new files. In other words, imagine that all new files will have 666 octal permission (write + read for user, group and others), so a umask of 0002 (yes, 4 digits) will remove the 2 (write) for others (666-0002=664):

$ umask
0002

If we need to change umask, it can be done with the same command:

$ umask
0002
$ touch newfile
$ ls -ltrh newfile
-rw-rw-r-- 1 nagato nagato 0 Feb  8 21:38 newfile
$ mkdir newdir
$ ls -ltrhd newdir
drwxrwxr-x 2 nagato nagato 4.0K Feb  8 21:38 newdir
$ umask u=rw,g=,o=
$ touch newerfile
$ ls -l newerfile
-rw------- 1 nagato nagato 0 Feb  8 21:41 newerfile
$ umask
0177

Note how I used u=rw,g=,o= to tell umask or chmod what I exactly wanted from it.

The umask is a mask, not a permission. It says what to take away:

   files start from    666   (never 777, files do not get x by default)
   directories from    777
                        |
   umask 022 removes    022
                        |
   files end up at     644   rw- r-- r--
   directories at      755   rwx r-x r-x

This is why the same umask gives files and directories different results. Directories need x to be enterable, plain files do not, so the system never hands out execute on a new file.

Use umask -S to see it as symbolic instead of octal, which is easier to read:

$ umask
0022
$ umask -S
u=rwx,g=rx,o=rx

Careful with that. umask -S prints what will be granted, while the octal form prints what will be removed. They look contradictory but describe the same setting from opposite ends.

Real world use for umask 077: on a shared server, this makes every file you create readable only by you, since it strips all group and other permissions. It is a common line to put in ~/.bashrc on a machine with several users.

Summary

I have a Linux system where every file is owned by one user and one group, and carries three sets of permissions: one for the owner, one for that group, and one for everybody else. ls -l shows them as ten characters, the first being the type and the rest three blocks of rwx. Only one block ever applies to me: the system checks whether I am the owner, then whether I am in the group, then falls back to others, and stops at the first match.

On a file, r lets me read it, w lets me change it and x lets me run it. On a directory the same letters mean different things: r lists the names inside, w creates and deletes files inside, and x lets me enter it and look anything up. That is why a directory with r but no x gives me a list of names and question marks for everything else, and why deleting a file depends on the directory's permissions rather than the file's own.

I change permissions with chmod, in two forms. Octal adds up r=4, w=2, x=1 into one digit per block, so 755 is rwxr-xr-x, and I use it when I want to set everything at once. Symbolic uses u, g, o or a, then +, - or =, then the letters, and I use it when I only want to flip one thing. -R applies either form recursively and needs care. Ownership is changed with chown user:group, or chgrp for just the group, and only root can hand a file to another user. usermod -aG adds a user to another group, where the -a is essential, and newgrp switches which of my groups new files will belong to.

On top of the nine normal bits there are three special ones, written as a fourth octal digit. SUID (4) makes a program run as its owner rather than as me, which is how passwd can edit /etc/shadow, and it shows as s in the user block. SGID (2) does the same with the group on a file, and on a directory makes every new file inherit the directory's group, which is what I use for a shared team folder. The sticky bit (1) applies to directories and stops people deleting each other's files even when the directory is world writable, which is why /tmp shows drwxrwxrwt.

Finally, the permissions a new file gets are decided by the umask, which lists the bits to remove rather than the ones to give. Files start from 666 and directories from 777, so a umask of 022 produces 644 and 755. umask -S shows the same setting the other way round, as what will be granted, and setting umask 077 is how I make everything I create private on a shared machine.