I'm having permissions problems when running the following PHP script as root:
#!/usr/bin/php
<?php
$ph = proc_open('whoami', [['pipe','r'],['pipe','w'],['file','/tmp/foo.bar', 'w']], $fds);
if ($ph) {
echo 'command output: ' . stream_get_contents($fds[1]);
proc_close($ph);
} else {
echo 'proc_open failed' . PHP_EOL;
}
The script itself runs fine if /tmp/foo.bar
doesn't exist, or is owned by root. But if ownership is changed to another user, proc_open
will fail regardless of permissions on the file.
SELinux is disabled, and we are not using ACLs. I'm using PHP 7.4.33 (I know it's old and unsupported, but it's a requirement for FreePBX) on Alma Linux 9.1.
Output:
$ ./test.php
command output: root
$ ls -lah /tmp/
total 12K
drwxrwxrwt. 18 root root 4.0K Dec 14 16:57 .
dr-xr-xr-x. 18 root root 4.0K Dec 14 16:48 ..
-rw-r--r-- 1 root root 0 Dec 14 16:57 foo.bar
$ chown admin /tmp/foo.bar
$ ./test.php
proc_open failed
$ chmod 777 /tmp/foo.bar
$ ./test.php
proc_open failed
$ ls -lah /tmp/
total 12K
drwxrwxrwt. 18 root root 4.0K Dec 14 16:57 .
dr-xr-xr-x. 18 root root 4.0K Dec 14 16:48 ..
-rwxrwxrwx 1 admin root 0 Dec 14 16:57 foo.bar
$ tail -2 /var/log/php.log
[14-Dec-2022 16:57:17 America/Toronto] PHP Warning: proc_open(/tmp/foo.bar): failed to open stream: Permission denied in /test.php on line 3
[14-Dec-2022 16:57:28 America/Toronto] PHP Warning: proc_open(/tmp/foo.bar): failed to open stream: Permission denied in /test.php on line 3
Even disregarding the fact that I'm root, group permissions should allow me full access to the file. So what's going on?
CodePudding user response:
This is due to the permissions on the /tmp
directory. When PHP tries to open the file for writing, it gets the EACCES
error. From the documentation of open(2)
:
EACCES
Where O_CREAT is specified, the protected_fifos or protected_regular sysctl is enabled, the file already exists and is a FIFO or regular file, the owner of the file is neither the current user nor the owner of the containing directory, and the containing directory is both world- or group-writable and sticky. For details, see the descriptions of /proc/sys/fs/protected_fifos
and /proc/sys/fs/protected_regular
in proc(5)
.
/tmp
has the sticky bit set so that anyone can create files there, but users can only delete their own files. Although root
can bypass this deletion restriction, it can't bypass the above check in open()
.
CodePudding user response:
Ok I tried this in a different directory than /tmp
, as suggested in comments, and it worked as expected. Using that to hone my search terms I was able pretty quickly to find this U&L answer. Beginning with kernel 4.19 the fs.protected_regular
kernel parameter was made available. This parameter:
Disallows 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.
Apparently it's enabled by default. So because /tmp
is world-writable and sticky, I can't touch files that aren't mine – even if I'm root. For the record, if I have to disable this feature:
sysctl fs.protected_regular=0
echo 'fs.protected_regular=0' > /etc/sysctl.d/90-writabletemp.conf
But I'll be better off trying to work around it in the code somehow.