27

I create a file as regular user testuser:

$ cat > /tmp/zz

the file is owned by that user (as expected):

$ ls -lA /tmp/zz 
-rw------- 1 testuser testuser 0 Feb 20 15:32 zz

Now when I try to truncate it as root, I get permission denied:

# truncate --size=0 /tmp/zz
truncate: cannot open '/tmp/zz' for writing: Permission denied

When I try with strace, I see the following:

openat(AT_FDCWD, "/tmp/zz", O_WRONLY|O_CREAT|O_NONBLOCK, 0666) = -1 EACCES (Permission denied)
write(2, "truncate: ", 10truncate: )              = 10
write(2, "cannot open '/tmp/zz' for writin"..., 33cannot open '/tmp/zz' for writing) = 33
...
write(2, ": Permission denied", 19: Permission denied)     = 19
write(2, "\n", 1

Why does root not have permissions to write to that file? Root can delete the file, but not write.

Martin Vegter
  • 358
  • 75
  • 236
  • 411

1 Answers1

52

This is a new behavior available on Linux kernels since version 4.19 to prevent attacks using /tmp/ tricks. The default value of the option might have been enabled later or be different depending on the distribution.

(FEATURED) Avoid unintentional writes to an attacker-controlled FIFO or regular file: disallow open of FIFOs or regular files not owned by the user in world writable sticky directories, unless the owner is the same as that of the directory or the file is opened without the O_CREAT flag. The purpose is to make data spoofing attacks harder. This protection can be turned on and off separately for FIFOs (protected_fifos) and regular files (protected_regular) via sysctl, just like the symlinks/hardlinks protection commit

This is intended to protect an user (including root which normally has always enough privileges) to write to a preexisting file in a directory like /tmp or /var/tmp while it would have intended to create it itself.

It's enabled with this sysctl toggle: fs.protected_regular. One can revert to former behavior with:

sysctl -w fs.protected_regular=0

but this will likely lower overall security, while making some strange "bugs" like OP's case disappear.

As for why root could still delete the file, that is because the additional security feature is triggered only for opening a file for writing, not for unlink-ing it: truncate -s ... does open the file for writing, rm doesn't (it uses unlink or unlinkat).

A.B
  • 36,364
  • 2
  • 73
  • 118
  • 5
    This one is going to make a mess. There's going to be bugs back and forth forever. – Joshua Feb 21 '22 at 04:01
  • I'm surprised this rule treats root the same as everyone else. – trlkly Feb 21 '22 at 10:10
  • 18
    @Joshua Kernel 4.19 has been out for over 3 years, and the world hasn't ended. I think we'll survive this change. – marcelm Feb 21 '22 at 10:28
  • 5
    @trlkly this feature is especially intended to affect the root user: the target of a privilege escalation. – A.B Feb 21 '22 at 11:18
  • @A.B - thank you, but I don't understand how this increases security. After all, you have to be root to actually be affected by this. So it's not like a security feature preventing regular users to become root. – Martin Vegter Feb 21 '22 at 17:09
  • 1
    @marcelm: I just checked my system and my distro has it turned off. I think we're not having many problems because it's still mostly unused. – Joshua Feb 21 '22 at 17:10
  • 3
    @400theCat If an user can guess the file name in advance, it can preseed it with data that an other account (root) thought it would have written on its own. If this data participates in any way to authentication... last CVE in the list (https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-7489) has an example: http://www.vapidlabs.com/advisory.php?v=173 . It's a feature to prevent a category of bugs in applications. Of course the application should be fixed but... – A.B Feb 21 '22 at 17:13
  • 4
    @Joshua Fair enough, it appears it was turned off by default initially. It does seem this was enabled in Ubuntu starting in 20.04, almost two years ago. More conservative distributions may be slower to enable this by default, but with ~2 years exposure in a popular distribution, I still think the fallout of this change isn't all that dramatic. – marcelm Feb 21 '22 at 19:40
  • "This affects only files in a directory not already owned by the user attempting to write" -- I think you're misinterpreting the feature description here, or else this answer does not explain the observed behavior. root owns /tmp (or should do), yet it is prevented from opening some files within. What the description seems actually to say in this area is that the protection does not apply if the owner of the target file is the same as the owner of the directory in which it resides or if the file is owned by the user opening it (among other exceptions). – John Bollinger Feb 21 '22 at 21:18
  • 3
    @JohnBollinger I made a correction, but yes even then it's difficult to parse. The text in quotes should be read "unless the owner [of the file] is the same as that of the directory". If user1 creates a a+wt directory, if the file foo with mode 666 within has for owner user1, then any user can write to it. If it has an other owner than user1, eg user2, then only user2 can write to it. In particular not user1 nor root (nor user3). – A.B Feb 21 '22 at 21:55
  • This is something where I would've really appreciated if the kernel developers had added a warning to dmesg whenever it happens. It's really hard to figure out what's going wrong when the kernel suddenly doesn't do what you'd expect, and until I realized that the file I was trying to overwrite was in a folder with the sticky bit set, also practically impossible to google :| – Martin von Wittich Feb 14 '24 at 18:35