I'm attempting to use realpath() to avoid a directory traversal attack, and I can't quite figure out why it's returning false on a file that definitely exists.
The files are being hosted on an NFS share that the Apache user has access to. The entire hierarchy is 755, except for the file itself which is 777.
Here's a code snippet:
$path = "/mnt/share/path/to/20160111-133552-msg0000.wav";
$path = realpath($path);
var_dump($path);
The result is:
bool(false)
The file is definitely there. I su-ed into the Apache user and ran:
bash-4.2$ ls -lah /mnt/share/path/to/20160111-133552-msg0000.wav
-rwxrwxrwx. 1 1001 1001 77K Jan 11 13:35 /mnt/share/path/to/3000-20160111-133552-msg0000.wav
One other observation: When I remove the filename from the $path variable:
$path = "/mnt/share/path/to/
it works just fine:
string(39) "/mnt/share/path/to"
Anyone ran into this behavior before?
I think I figured this one out on my own! Good ol' selinux was preventing httpd from accessing the file:
type=AVC msg=audit(1452550767.071:18495): avc: denied { read } for pid=10397 comm="httpd" name="3000-20160111-133552-msg0000.wav" dev="0:41" ino=12483 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:nfs_t:s0 tclass=file
Was caused by:
One of the following booleans was set incorrectly.
Description:
Allow httpd to use nfs
Allow access by executing:
# setsebool -P httpd_use_nfs 1
I only discovered this when I tried to do a readfile() on the .wav file in question and got a permission denied error, which didn't make sense on a 777 file. Checked /var/log/audit/audit.log and it was obvious.
Running setsebool -P httpd_use_nfs 1 fixed it right up for me.
Don't take the following for true: this answer was written based on a misremember of the detailed combination of the access rights: 755 already allows execution for all!
You said:
The entire hierarchy is 755, except for the file itself which is 777.
While the PHP manual page says:
The running script must have executable permissions on all directories in the hierarchy, otherwise realpath() will return FALSE.
So you'd set the entire hierarchy to 777 to this realpath()to work.
Related
I changed my server; the old one ran Centos 8, the new runs Ubuntu 20.4. Now my php scripts have a problem with permissions -- why?
Example:
current user: root
script was executed under user: nobody
Message: fopen(/tmp/RebuildCat_sequence.cnt): failed to open stream: Permission denied
Actually this file is owned by nobody, so I should not get this warning at all:
CS-1 01:47:22 :/tmp# ls -latr /tmp/RebuildCat_sequence.cnt
--wxrwxrwT+ 1 nobody nogroup 480 Oct 23 00:02 /tmp/RebuildCat_sequence.cnt
This php file is called by cron (root), as a rule, and as I noticed that some similar cron triggered files are owned by systemd-timesync, I issued
setfacl -m u:systemd-timesync:rwX /tmp
setfacl -m u:nobody:rwX /tmp
and even
setfacl -R -m u:nobody:rwX /tmp
setfacl -R -m u:systemd-timesync:rwX /tmp
to no avail. How do I understand this?
In my understanding both users systemd-timesync and nobody should be able to read and write files in /tmp without problem due to acl. I think someone has to educate me here.
/tmp resides on /dev/md2
CS-1 01:32:34 :/tmp# tune2fs -l /dev/md2 | grep "Default mount options:"
Default mount options: user_xattr acl
No problem here, I guess.
CS-1 01:46:09 :/tmp# getfacl /tmp
getfacl: Removing leading '/' from absolute path names
# file: tmp
# owner: root
# group: root
# flags: --t
user::rwx
user:systemd-timesync:rwx
user:nobody:rwx
group::rwx
mask::rwx
other::rwx
Looks OK to me, as well. Clueless.
Addendum
OK, I set chmod 777 /tmp, but still I get a PHP error fopen(/tmp/RebuildCat_sequence.cnt): failed to open stream: Permission denied, but file_put_contents works with 777 -- how do I understand this? Why does fopen throw an error, but file_put_contents does not?
Alas, chmod 777 /tmp in the console is not the answer. It is much more complicated than that. So I wasn't silly at all.
How to reproduce?
In my case, I have 2 docker PHP containers writing to a number of case specific log files, one of them using apache, the other nginx. Both have a mapping /tmp:/tmp, so we can look at a file from the host or from inside a container.
From inside the containers, the owner is apache:apache if created in the apache container or nginx:nginx in the nginx container, but from the host's perspective it is systemd-timesync:systemd-journal for the same file in both cases.
Of course, the apache container does not have a user nginx and vice versa (neither does the host). So if my PHP script wants to write to a file created by the other container, I have said permission problem.
Fix
The remedy is easy, if at creation time the owner is changed to nobody and permissions to 666.
If not, we cannot change the owner from the other container and get failed to open stream: Permission denied. See https://serverfault.com/questions/772227/chmod-not-working-correctly-in-docker (also see the discussion about changing permissions from host via system call below).
So I wrote a wrapper function str_to_file to add this manipulation:
function str_to_file($str, $filename= "/tmp/tmp.txt", $mode="a+") {
if (!file_exists($filename)){
touch($filename); # create file
chmod($filename, 0666); # the owner can chmod
chown($filename, 'nobody'); # the owner can chown
} # if (!file_exists($filename))
$mode = $mode == 'w'
? LOCK_EX
: FILE_APPEND | LOCK_EX;
file_put_contents($filename, $str . PHP_EOL, $mode);
} # str_to_file
This works for both the apache and nginx containers.
Explanation
How did I get into this mess anyway?
It looks like either I didn't mess it up when on CentOS, so in this case most probably it has nothing to do with my switch to Ubuntu. I cannot remember what I did but I don't remember I had this kind of permission problem on CentOS.
Or CentOS handles files created in a container differently than Ubuntu. I am sure I never saw a user systemd-timesync:systemd-journal on CentOS. Unfortunately I cannot investigate without too much effort what user CentOS would substitute from the host point of view and which consequences this has.
Oh wait, I remember I do have another server running PHP and nginx on CentOS. Creating a file in /tmp from within the container reveals that the owner from both inside the container and from the host is nobody. So there is a significant difference between both Linux versions.
What's more, this insight explains why I experienced this permission problem only after the switch from CentOS to Ubuntu, to which I was forced due to the fact that my provider does not offer CentOS anymore for obvious reasons.
By the way, I first tried the CentOS substitutes AlmaLinux and RockyLinux with bad results of different nature which finally forced me to use Ubuntu. This switch in turn revealed lots of problems, this one being the last, which cost me several weeks so far. I hope that this nightmare ends now.
Acting from cron, the apache version is and was used exclusively, so no problem here.
Testing from the browser lately, I switched by chance and for no particular reason from the apache version to the nginx version and vice versa, creating those described problems.
Actually I don't know why the containers write on behalf of those users. The user of the container is root in both cases, as is standard with docker. I guess it is the browser engine which introduces these users.
Interestingly, when trying to change permissions and ownership via system call after creation, I failed, if I remember correctly, although the user then should be root and root should be capable of doing that.
It turns out that on apache the system user [system('whoami')] is not root but apache, but the file owner is apache where as on nginx the system user is nobody and the file owner is nginx. So changing permissions with system should work on apache [system("chmod 0666 $filename"); system("chown nobody:nobody $filename");], but not on nginx. Alas, it does not work on apache either.
Quick check in apache container:
/tmp # whoami
root
/tmp # ls -la *6_0.ins
-rw-r--r-- 1 apache apache 494 Nov 14 18:25 tmp_test_t6_0.ins
/tmp # su apache chmod 0666 tmp_test_t6_0.ins
This account is not available
Sorry, I can't understand this. And no idea about ACL.
apache vs. nginx
Why do I use both web server versions in the first place?
Well, the process I invoke processes random data which may take a long time, so chances are that nginx times out. This is a well-known nginx feature.
Despite all my studies and obvious instructions, I could not manage nginx to behave. Geez!
Finally, as an appropriate workaround, I introduced apache, which does not have this problem. For decent runtimes it makes no difference, of course.
Well, it looks like I was kind of silly. I'm sure I double checked permissions on /tmp in WinSCP (1777), but I am unsure if I did it in the console.
Now I issued chmod 777 /tmp in the console and there it is! This problem is gone. chmod 666 /tmp reintroduces the problem. chmod 676 /tmp is OK as well.
I googled quite a bit to educate myself about this topic, but frankly I don't understand it. In particular why did I have the problem in the first place and why did ACL not solve the problem? I'd appreciate some enlightenment.
After reading lots of questions regarding php scandir() I haven't found one that answered my question. If this is a duplicate, please let me know where the answer is before marking me down.
Problem:
If I do
var_dump(scandir("/"));
Then I get the contents of the system root folder: bin, installs, nvme, var, etc, etc... 😊
If I do
var_dump(scandir("/nvme"));
Then I get false and an error that var/www/public_html/nvme doesn't exist.
So then if I do
var_dump(scandir("../../../../../../../"));
I can see the system root folder
but if I do
var_dump(scandir("../../../../../../../nvme"));
then I get a permission denied error.
I tested that I can scan each directory between the public_html and the root directory, but the moment I try to scan forward a directory then I run into errors. All the directories on the way back are owned by the same user as the nvme directory I'm trying to scan. Using file_get_contents throws the same error.
In SElinux I gave apache permission to read and write to the nvme folder. I'm running on Centos 7 with SElinux enabled.
Why can't I can a up from root on a different directory path?
EDIT:
My specific error was that I hadn't completed setting the SElinux context.
Previously I had run
semanage fcontext -a -t httpd_sys_content_t "/nvme(/.*)
but after that I still needed to run
restorecon -v "/nvme"
so that the new context was actually implemented. Now var_dump(scandir("/./nvme")); and var_dump(scandir("../../../../../../../nvme")); both work.
I am using Php Smarty on Linux.
I have a line in my Php file:
$phpsmart->display("pagetemplate.tpl");
This line is supposed to display pagetemplate.tpl. It doesn't.
Enable errors, or check your logs. The most likely thing is that you haven't set up writeable directories needed by smarty.
I just installed smarty with composer:
$ composer.phar require smarty/smarty
And tried the demo:
( ! ) Fatal error: Uncaught --> Smarty: unable to write file ./templates_c/wrt56681191371d80_85949214 <-- thrown in /var/www/smarty/vendor/smarty/smarty/libs/sysplugins/smarty_internal_write_file.php on line 46
I then created that 'template_c' folder, and made it writeable by the web server.
( ! ) Fatal error: Uncaught --> Smarty: unable to write file ./cache/wrt566812bd6f7b08_17223124 <-- thrown in /var/www/smarty/vendor/smarty/smarty/libs/sysplugins/smarty_internal_write_file.php on line 46
I then created the 'cache' folder, and made it writeable by the web server.
The demo then worked.
See the quick install.
It doesn't display a tpl file even after changing the file permissions and ownership of a file.
for people who are having problem like this.
1) chmod -R 755 /var/www -> This will give read write permission to the owner and other for the group( the owner belongs to), and others the permission in read and execute. and this is assigned recursively so all your files and directories inside www will also be having the same permissions
2) chown -R apache:apache /var/www ->This will give make apache owner of www and including files. This is also applied recursively.
3)your website owner needs write permission to template_c file so check using ls -altr to see whether it has given the write permission, if write is like 755(rwxr-xr-x) and still not working change it to 777 (remember chmod). also check the cache folder..
4) If still it's not working may be selinux is protecting write access to your template_c file. so for this you need the following command
setsebool -P httpd_unified=1 -> This will disable selilnux for apache httpd.
Enjoy!
This is driving me insane. httpd runs as the user apache. I have two directories within /var/www/html -- uploads and photos. Both have group:owner of apache:apache. Both are 755. uploads is writable from php -- photos is not.
Some test code:
var_dump(touch('/var/www/html/photos/_test.log'));
var_dump(touch('/var/www/html/uploads/_test.log'));
var_dump(touch('/var/www/html/uploadsasdf/_test.log'));
And results:
Warning: touch(): Unable to create file /var/www/html/photos/_test.log because Permission denied in /var/www/html/test.php on line 2
bool(false)
bool(true)
Warning: touch(): Unable to create file /var/www/html/uploadsasdf/_test.log because Permission denied in /var/www/html/test.php on line 4
bool(false)
I've confirmed permissions through a shell and GUI interface. I've chowned and chmoded everything again just to be sure. I've renamed the uploads directory to something else and renamed photos to uploads to see if the name of the directory was the key here, but it wasn't. It's the directory itself. The renamed uploads still works with a new name, and the photos directory that is now called "uploads" does not.
Of note, _test.log does not exist in the folders before testing, so it's not like that file has bad permissions or anything.
Interestingly, if I create a new directory, chown it to apache:apache, chmod it to 777, I can't write to it, so something larger may be wrong here; but the question remains: why then does the uploads directory work?
Has anyone seen this behavior before? Am I missing something obvious?
Thanks in advance for any help!
Edited to add more info:
exec('whoami')
"apache"
var_dump(posix_getpwuid(fileowner('/var/www/html/')));
var_dump(posix_getpwuid(fileowner('/var/www/html/uploads/')));
var_dump(posix_getpwuid(fileowner('/var/www/html/photos/')));
all "apache"
All have the same fileperms() value. However, is_writable() is false on all but "uploads".
mkdir('var/www/html/test');
Warning: mkdir(): Permission denied
ls-alF
drwxr-xr-x. 2 apache apache 286720 Nov 22 15:17 photos/
drwxr-xr-x. 2 apache apache 81920 Nov 22 12:06 uploads/
drwxr-xr-x. 2 apache apache 6 Nov 22 10:31 uploadsasdf/
I have called clearstatcache(); I have rebooted the server. What ... on ... Earth?
Since you are using CentOS and and you've tried everything else, my guess would be something related to SELinux. One of the answers from this question may be helpful Forbidden You don't have permission to access on this server. Centos 6 / Laravel 4
Specifically try this to analyze SELinux permissions (ls -lZ) and temporarily disable SELinux:
If you're using CentOS it could be an issue with selinux. Check to see if selinux is enabled with 'sestatus'. If it is enabled, you can check to see if that is the issue (temporarily) using 'sudo setenforce 0'. If apache can serve the site, then you just need to change the context of the files recursively using 'sudo chcon -R -t httpd_sys_content_t' (you can check the existing context using 'ls -Z'.
If selinux is enabled (sestatus will tell you), try sudo restorecon -Rv /var/www/ first. Sounds much like SELinux is getting in the way and you somehow have got a file/directory there which is not labelled correctly. Restorecon will revert labels to default, and -v will show you which labels have been corrected, if any.
Failing that, extended attributes come to mind. do lsattr <filename> and if the output looks anything like ------i-----, the immutable flag is set. Change it with chattr -i <filename> and you're good to go.
When I use chmod() to change permissions at run time, it gives me the below message:
Warning: chmod() [function.chmod]: Operation not permitted in /home/loud/public_html/readalbum.php
How can I remove this error and make the chmod function work?
$ sudo chmod ...
You need to either be the owner of the file or be the superuser, i.e., user root. If you own the directory but not the file, you can copy the file, rm the original, then mv it back, and then you will be able to chown it.
The easy way to temporarily be root is to run the command via sudo. ($ man 8 sudo)
In order to perform chmod, you need to be owner of the file you are trying to modify, or the root user.
This is a tricky question.
There a set of problems about file permissions. If you can do this at the command line
$ sudo chown myaccount /path/to/file
then you have a standard permissions problem. Make sure you own the file and have permission to modify the directory.
If you cannnot get permissions, then you have probably mounted a FAT-32 filesystem. If you ls -l the file, and you find it is owned by root and a member of the "plugdev" group, then you are certain its the issue. FAT-32 permissions are set at the time of mounting, using the line of /etc/fstab file. You can set the uid/gid of all the files like this:
UUID=C14C-CE25 /big vfat utf8,umask=007,uid=1000,gid=1000 0 1
Also, note that the FAT-32 won't take symbolic links.
Wrote the whole thing up at http://www.charlesmerriam.com/blog/2009/12/operation-not-permitted-and-the-fat-32-system/
You, or most likely your sysadmin, will need to login as root and run the chown command:
http://www.computerhope.com/unix/uchown.htm
Through this command you will become the owner of the file.
Or, you can be a member of a group that owns this file and then you can use chmod.
But, talk with your sysadmin.