2

Anyone know why rocky can't write to /tmp/afile? Giving rocky permission as an other user with chmod o+w doesn't seem to work either

I updated the acl with: setfacl -m u:rocky:rw /tmp/afile

id as rocky and owner:

rocky@jammy:~$ id
uid=1001(rocky) gid=1001(rocky) groups=1001(rocky)
seb@jammy:~$ id
uid=1000(seb) gid=1000(seb) groups=1000(seb),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),122(lpadmin),134(lxd),135(sambashare)

ls -ld output

seb@jammy:~$ ls -ld /tmp/afile
-rw-rw-rw-+ 1 seb seb 0 Jul  3 14:25 /tmp/afile

Owner is able to write without issue:

seb@jammy:~$ echo rounders > /tmp/afile
seb@jammy:~$ cat /tmp/afile 
rounders

getfacl -n output:

seb@jammy:~$ getfacl -n /tmp/afile getfacl: Removing leading '/' from
absolute path names
# file: tmp/afile
# owner: 1000
# group: 1000
user::rw- 
user:1001:rw- 
group::rw- 
mask::rw- 
other::rw-

df -T and mount output:

seb@jammy:~$ df -T /tmp
Filesystem     Type 1K-blocks     Used Available Use% Mounted on
/dev/sda2      ext4 120044288 18487840  95412296  17% /
seb@jammy:~$ mount | grep /dev/sda2
/dev/sda2 on / type ext4 (rw,relatime,errors=remount-ro)
/dev/sda2 on /var/snap/firefox/common/host-hunspell type ext4 (ro,noexec,noatime,errors=remount-ro)

Attempt to write to file as rocky:

seb@jammy:~$ getfacl /tmp/afile
getfacl: Removing leading '/' from absolute path names
# file: tmp/afile
# owner: seb
# group: seb
user::rw-
user:rocky:rw-
group::rw-
mask::rw-
other::rw-

seb@jammy:~$ su - rocky
#Password: 
rocky@jammy:~$ echo hello > /tmp/afile
-bash: /tmp/afile: Permission denied
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
Newbie
  • 23
  • 4
  • `ls -ld /tmp/afile` please. (Added to your question.). And `id` run as both `seb` and `rocky` users – roaima Jul 03 '23 at 14:18
  • Thanks @roaima, added :) – Newbie Jul 03 '23 at 14:32
  • Please also add the output of: `getfacl -n /tmp/afile`. And I assume that you have no issues writing to the file as user `seb`. – aviro Jul 03 '23 at 15:30
  • Also add the output of `df -T /tmp`. From the output of `df` take the filesystem (`/dev/...`) and show it's mount options by running `mount | grep /dev/...`. – aviro Jul 03 '23 at 15:41
  • @aviro Thank you :) I've run those and I can confirm that the owner can write without issue – Newbie Jul 03 '23 at 15:55

2 Answers2

5

Your expectation is correct: the permissions should allow rocky to write to afile, since rocky has the write permission on the file and execute permission on all the containing directories. However, you've run into an additional security hardening mechanism on Linux. When this mechanism is active, certain writes to files in directories with the sticky bit are prevented.

The sticky bit on a directory is indicated by t as the last of the 10 characters of the file mode/permissions in the ls -l listing, and can be set with chmod +t. Normally, the effect of the sticky bit on a directory is that only the owner of a file can remove or rename it. It does not affect writing to an existing file.

However, when the fs.protected_regular sysctl is enabled, the sticky bit has an additional consequence: users cannot open a file for writing in a sticky directory unless they own the file, if they open the file in a way that would create it if the file did not exist, which the program indicates by passing the O_CREAT file to the open system call. Note the subtlety here: the normal behavior is solely based on whether the file exists, whereas the hardened behavior is based on whether the file would be created if it didn't exist. This only affects opening the file in a way that might create it: other ways to write to the file are still permitted.

Note that this does not affect files in a directory that doesn't have the sticky bit. In particular, if you create a directory /tmp/foo and /tmp/foo does not have the sticky bit, then permissions of files in /tmp/foo behave normally.

The following table summarizes the behavior of the open system call when opening the file for writing in the relevant scenarios (where the directory exists and the program can access files in it, and if the file exists then the program has write permission on it):

directory file with O_CREAT without O_CREAT
rwxr-xr-x (755) exists, writable (666) ok ok
rwxr-xr-x (755) does not exist ok ENOENT
rwxrwxrwx (777) exists, writable (666) ok ok
rwxrwxrwx (777) does not exist ok ENOENT
rwxr-xr-t (1755) exists, writable (666) ok ok
rwxr-xr-t (1755) does not exist ok ENOENT
rwxrwxrwt (1777) normally exists, writable (666) ok ok
rwxrwxrwt (1777) normally does not exist ok ENOENT
rwxrwxrwt (1777) on hardened Linux exists, writable (666) EACCES ok
rwxrwxrwt (1777) on hardened Linux does not exist ok ENOENT

(ENOENT is the error “No such file or directory”. EACCES is the error “Permission denied”.)

You can control whether this hardening is enabled with sysctl fs.protected_regular=0 (disable), sysctl fs.protected_regular=1 (enable for world-writable directories) or sysctl fs.protected_regular=2 (enable for world-writable or group-writable directories). At boot time, this may be set from /etc/sysctl.conf or /etc/sysctl.d/*. Some distributions enable it by default, others don't. The protected_regular setting applies to regular files; there is a similar setting for named pipes.

To see this for yourself (if you're running on Linux and have root access), run the following commands as root:

mkdir /tmp/experiment
mkdir -m 1777 /tmp/experiment/sticky-world
mkdir -m 777 /tmp/experiment/ordinary-world
mkdir -m 1755 /tmp/experiment/sticky-user
mkdir -m 755 /tmp/experiment/ordinary-user
for d in /tmp/experiment/*; do touch $d/file; chown nobody:nogroup $d/file; chmod 666 $d/file; done

Checking the permissions:

$ ls -l /tmp/experiment 
total 16
drwxr-xr-x 2 root root 4096 Jul  3 21:10 ordinary-user
drwxrwxrwx 2 root root 4096 Jul  3 21:10 ordinary-world
drwxr-xr-t 2 root root 4096 Jul  3 21:10 sticky-user
drwxrwxrwt 2 root root 4096 Jul  3 21:10 sticky-world
$ ls -l /tmp/experiment/*/file
-rw-rw-rw- 1 nobody nogroup 0 Jul  3 21:10 /tmp/experiment/ordinary-user/file
-rw-rw-rw- 1 nobody nogroup 0 Jul  3 21:10 /tmp/experiment/ordinary-world/file
-rw-rw-rw- 1 nobody nogroup 0 Jul  3 21:10 /tmp/experiment/sticky-user/file
-rw-rw-rw- 1 nobody nogroup 0 Jul  3 21:10 /tmp/experiment/sticky-world/file

Now, as a non-root user who isn't nobody, let's run some code that tries opening the files for writing, once with the O_CREAT flag and once without. I use Perl because the shell doesn't give direct access to O_CREAT (shell write redirections always have O_CREAT enabled except for appending in some shells when noclobber is enabled). On a hardened Linux:

$ perl -w -MFcntl -e 'foreach (@ARGV) { sysopen(F, $_, O_WRONLY | O_CREAT) or warn "$_: $!\n"; close F; }' /tmp/experiment/*/file
/tmp/experiment/sticky-world/file: Permission denied
$ perl -w -MFcntl -e 'foreach (@ARGV) { sysopen(F, $_, O_WRONLY) or warn "$_: $!\n"; close F; }' /tmp/experiment/*/file

Without the Linux hardening, all the file openings would succeed since the program has permission to write to the file.

If you have audit enabled, the attempt to write to /tmp/experiment/sticky-world/file triggers an ANOM_CREAT audit event, which is logged in /var/log/audit/audit.log (or wherever those logs go on your distribution.) (This is how I discovered this Linux mechanism, which didn't know about before.)

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
1

Because of the sticky bit (+t) on /tmp.

man 1 chmod

RESTRICTED DELETION FLAG OR STICKY BIT

   The restricted deletion flag or sticky bit is a single bit,  whose  in‐
   terpretation  depends  on  the file type.  For directories, it prevents
   unprivileged users from removing or renaming a file  in  the  directory
   unless  they  own  the  file  or  the directory; this is called the re‐
   stricted deletion flag for the directory,  and  is  commonly  found  on
   world-writable  directories like /tmp.  
Ipor Sircer
  • 14,376
  • 1
  • 27
  • 34
  • 2
    This is not true for **modifying** a file. Your answer might have been true if he was trying to remove or delete the file, but that's not the case here. The sticky bit of the directory doesn't affect modifying the contents of the file in the directory, only the actual file permissions do. – aviro Jul 03 '23 at 14:50
  • @aviro: In Linux world the renaming/removing operation is the same write permission as modifying. If you don't believe me, try it yourself. – Ipor Sircer Jul 03 '23 at 15:31
  • Nope. renaming/removing depend on the directory permissions. writing/reading/executing a file depend on the file permissions. And yes, I've tried it. Maybe "modifying" wasn't the correct term (since it might imply modifying the meta data of the file and not its content), and the better term for what I mean is **writing** to a file. – aviro Jul 03 '23 at 15:36
  • @aviro: No, you haven't tried it. It's not too much time to create a file in a folder with `+t` then `-t` with 2 different users. – Ipor Sircer Jul 03 '23 at 15:49
  • I have tried it, but naturally I cannot paste my results into the comment. Since **you** submitted this answer, maybe you would prove it by showing a test that proves your point. – aviro Jul 03 '23 at 16:11
  • @aviro I've also tried it and on my (Debian) system with kernel 5.10.0-22-amd64 it behaves exactly as IporSircer explains. `ls -l /tmp/file` → `-rw-rw-rw- 1 roaima roaima 4 Jul 3 18:01 /tmp/file`, and `ls -ld /tmp` → `drwxrwxrwt 10 root root 4096 Jul 3 18:01 /tmp`, and `id -nu` → `auser`, and finally `echo bar >/tmp/file` → `-bash: /tmp/file: Permission denied` – roaima Jul 03 '23 at 17:03
  • @IporSircer it might be worth expanding on the quoted documentation to explain that it also applies for writing to files in such a directory too. I've learnt something new today :-) – roaima Jul 03 '23 at 17:07
  • @roima that's not what I see on my SLES system with 5.14.21-150400.22 kernel. `ls -l /tmp/file` → `-rw-rw-rw- 1 root root 0 Jul 3 20:15 /tmp/file`, `ls -ld /tmp` → `drwxrwxrwt 853 root root 917504 Jul 3 20:15 /tmp` and `id -nu` → `aviro` and `echo bar >/tmp/file` works. – aviro Jul 03 '23 at 17:21
  • @aviro ah, I see that too. Create the `/tmp/file` as a non-root user – roaima Jul 03 '23 at 17:40
  • @roima I ran `chown user1:group1 /tmp/file` and it still worked. Haven't tried to **create** it as another user, but how would that have any effect? Can you also try creating as root and `chown`ing it? – aviro Jul 03 '23 at 17:44
  • 1
    @aviro, roima, Ivor: You're both partly right. Normally, the sticky bit only controls file creation and removal. Writing to an existing file should only depend on the file's permissions. However [Linux has an additional hardening mechanism](https://elixir.bootlin.com/linux/v6.4/source/fs/namei.c#L1208) that prevents writing to an existing file in a `+t` directory if the call _might_ create a file, even if the file happens to already exist. Non-Linux systems, ancient Linux systems, or Linux systems configured without this hardening will show the expected behavior. – Gilles 'SO- stop being evil' Jul 03 '23 at 18:43
  • 1
    Thanks everyone for chipping in! :) It took me a little while to understand (and some more googling :D ). Removing the sticky bit from `/tmp` resolved the issue for me (I've now replaced `+t` to keep permissions as intended). I'm super new to Linux, when reading `man chmod` and searching google, the pages did state that `+t` prevented renaming or removing the file, but no mention of writing/appending to the file. Removing `+t` certainly resolved the issue, but was preventing `rocky` from writing to the file its intended purpose? – Newbie Jul 03 '23 at 18:50
  • 2
    @Newbie *Do not remove the sticky bit from `/tmp`! This is likely to break stuff. Never change the permission on system directories unless you thoroughly understand what you're doing. – Gilles 'SO- stop being evil' Jul 03 '23 at 19:44
  • @Gilles'SO-stopbeingevil', thank you for the advice! :) The sticky bit has been restored. And thank you for the link above too! – Newbie Jul 03 '23 at 19:55