php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #31618 is_readable() results based on ownership of calling script, not file
Submitted: 2005-01-19 23:05 UTC Modified: 2009-05-11 09:38 UTC
Votes:5
Avg. Score:4.8 ± 0.4
Reproduced:5 of 5 (100.0%)
Same Version:0 (0.0%)
Same OS:2 (40.0%)
From: kpederson at mail dot ewu dot edu Assigned:
Status: Suspended Package: Feature/Change Request
PHP Version: 5CVS-2005-03-14 OS: redhat enterprise
Private report: No CVE-ID:
Have you experienced this issue?
Rate the importance of this bug to you:

 [2005-01-19 23:05 UTC] kpederson at mail dot ewu dot edu
Description:
------------
is_readable($myfilename) in the repro code returns true if     
the script calling it is owned by root, but false if it is     
owned by someone else.    
    
Permissions are:   
-rw-r--r--    1 root     root         5452 Jan 13  
13:02 /var/lib/php_packages/test_templ2.php   
drwxr-xr-x    4 root     root         4096 Jan 19  
08:19 /var/lib/php_packages   
drwxr-xr-x   27 root     root         4096 Jan 12  
09:27 /var/lib   
drwxr-xr-x   24 root     root         4096 Sep 22  
13:06 /var   
drwxr-xr-x   20 root     root         4096 Oct 29 09:48 /   
  
Relevant Settings:  
include_path = 
".:/var/lib/php_packages:/var/lib/php_packages/pear"  
safe_mode = On  
safe_mode_gid = On  
safe_mode_include_dir = /var/lib/php_packages 

Reproduce code:
---------------
#### test.php ###
        $myfilename = '/var/lib/php_packages/test_templ2.php';
        if (is_readable($myfilename)) {
                echo "is_readable: $myfilename (true)<br>";
        } else {
                echo "is_readable: $myfilename (false)<br>";
        }
        include($myfilename);

### test_templ2.php ###
TESTING!


Expected result:
----------------
I would expect is_readable() to return true in both  
instances.  The uid/gid check shouldn't matter despite  
safe mode, as the file is in safe_mode_include_dir, and 
even if it wasn't, the is_readable documentation says that 
it does NOT take into account safe_mode restrictions. 

Actual result:
--------------
// When test.php has the following ownership:    
$ ls -l test.php     
-rw-rw-r--    1 root     root          278 Jan 19 13:16    
test.php    
    
// I get the following output:    
is_readable: /var/lib/php_packages/test_templ2.php (true)    
TESTING!   
   
// When test.php has the following permissions:   
$ ls -l test.php    
-rw-rw-r--    1 dschlegel79 undergradadmiss      278 Jan   
19 13:16 test.php   
   
// I get the following output:   
is_readable: /var/lib/php_packages/test_templ2.php (false)    
TESTING!  

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2005-01-20 22:32 UTC] kibab at icehouse dot net
Maybe this isn't directly related, but  
fopen($myfilename,"r") also fails, even though  
include($myfilename) works.  Again, $myfilename is in the  
safe_mode_include_dir, so fopen should be able to open it.
 [2005-05-17 17:18 UTC] sniper@php.net
What are the permissions of all the directories in that path?
(/var/lib/php_packages/)
 [2005-05-19 06:14 UTC] kibab at icehouse dot net
From memory, all files were mode 664 and all directories 
had permissions of 775 being owned by root:root.  However, 
I no longer have that same structure to prove that.  If 
you like, I can setup an almost identical test case using 
the code that I included below (but using my new 
structure).
 [2005-08-10 20:24 UTC] kibab at icehouse dot net
Ok, here's a new "complete" example for you.   
   
First, we need to do some setup as this is based on   
permissions, ownership, and safe mode:   
   
cd <some directory in safe_mode_include_dir>   
# note, I used cd /usr/share/pear   
echo "TESTING" > commonfile.php   
chmod a+r commonfile.php  
   
Then: $ ls -l commonfile*   
-rw-rw-r--    1 root     root            8 Aug 10 10:54   
commonfile.php   
  
And, permissions on the source PHP file in use:  
$ ls -l bug31618.php  
-rw-rw-r--    1 kpederson financialaid      576 Aug 10  
10:50 bug31618.php  
  
I used the following relevant settings:  
  
$ grep -iE "safe|include" /etc/php.ini | grep -v "^;"  
safe_mode = On  
safe_mode_gid = On  
safe_mode_include_dir = /usr/share/pear  
safe_mode_exec_dir = "/usr/local/php_exe/bin"  
safe_mode_allowed_env_vars = PHP_  
safe_mode_protected_env_vars = LD_LIBRARY_PATH  
include_path =  
".:/usr/share/pear/:/usr/share/pear/ewu_lib:/var/lib/php_secure"  
sql.safe_mode = Off  
  
Now, grab my PHP script from the following URL:  
  
http://www.ewu.edu/web/tools/bug31618.php.txt  
  
It's output looks like the following (as can be seen from  
http://www.ewu.edu/web/tools/bug31618.php):  
  
is_readable: /usr/share/pear/commonfile.php (false)  
TESTING   
  
Now, if I change the ownership to root:root (as I did for  
bug31618_2.php, eg. as seen by  
http://www.ewu.edu/web/tools/bug31618_2.php):  
  
is_readable: /usr/share/pear/commonfile.php (true)  
TESTING   
  
Thus, the results are based on ownership of the calling 
php script, not the file attempting to be read, despite 
being in safe_mode_include_dir.
 [2005-08-10 20:36 UTC] tony2001@php.net
Could you plz also try this:
<?php
fopen($myfilename, 'r');
?>
And post the error message here.
Thanks.
 [2005-08-10 20:44 UTC] tony2001@php.net
Of course, I meant this:
<?php
$myfilename = '/usr/share/pear/commonfile.php';
fopen($myfilename, 'r');
?>

 [2005-08-11 01:59 UTC] kibab at icehouse dot net
Ok.  It says (see 
http://www.ewu.edu/web/tools/bug31618_3.php): 
 
Warning: fopen() [function.fopen]: SAFE MODE Restriction 
in effect. The script whose uid/gid is 687/694 is not 
allowed to access /usr/share/pear/commonfile.php owned by 
uid/gid 0/0 in /var/www/sites/web/tools/bug31618_3.php on 
line 3 
  
 Warning: fopen(/usr/share/pear/commonfile.php) 
[function.fopen]: failed to open stream: Resource 
temporarily unavailable 
in /var/www/sites/web/tools/bug31618_3.php on line 3
 [2005-08-11 11:10 UTC] tony2001@php.net
Did you read something about safe_mode before turning it On?

"By default, Safe Mode does a UID compare check when opening files. If you want to relax this to a GID compare, then turn on safe_mode_gid. Whether to use UID (FALSE) or GID (TRUE) checking upon file access." (c) http://www.php.net/manual/en/features.safe-mode.php

So, it's perfectly fine to have these errors and to have FALSE in is_readable() because you turned safe_mode yourself.
This is expected behaviour.
 [2005-08-11 15:57 UTC] kibab at icehouse dot net
Yes.  I read docs, although I sometimes misunderstand   
them:   
   
Note in my previous post:   
safe_mode_include_dir = /usr/share/pear     
  
Also note that per the documentation  
(http://www.php.net/manual/en/features.safe-mode.php):  
  
"safe_mode_include_dir string  
UID/GID checks are bypassed when including files from this  
directory and its subdirectories (directory must also be  
in include_path or full path must including)." 
  
Certainly  $myfilename = '/usr/share/pear/commonfile.php'; 
is in safe_mode_include dir. 
 
Thus, the error message is incorrect and *is* a PHP bug.
 [2005-08-12 01:00 UTC] tony2001@php.net
Please try this patch:
http://tony2001.phpclub.net/dev/tmp/bugs_29840_31618.diff
(with the latest snapshot/CVS).
 [2005-08-12 19:11 UTC] kibab at icehouse dot net
Ok, I tried it out on my dev server and it works!  
is_readable() now returns the correct values based on the 
ownership of the file. 
 
From my initial test: 
 
is_readable: /var/lib/php/test_templ2.php (true) 
TEST 
 
The fopen($myfilename) call, however, still fails with the 
following error message (perhaps I should file this as a 
separate bug report...): 
 
Warning: fopen() [function.fopen]: SAFE MODE Restriction 
in effect. The script whose uid/gid is 49/49 is not 
allowed to access /var/lib/php/test_templ2.php owned by 
uid/gid 0/0 in /var/www/sites/devel/test.php on line 10 
  
 Warning: fopen(/var/lib/php/test_templ2.php) 
[function.fopen]: failed to open stream: Success 
in /var/www/sites/devel/test.php on line 10 
 
And... safe_mode_include_dir on that server is set 
to /var/lib/php.
 [2005-08-12 19:17 UTC] tony2001@php.net
Yes, I've made this intentionally.
The file in safe_mode_include_dir can be included, but cannot be opened using fopen() and friends.
IMO that's why this directive is called safe_mode_*INCLUDE*_dir.
 [2005-08-12 20:04 UTC] kpederson at mail dot ewu dot edu
Hmm... I can understand that logic.  The docs say:    
    
"UID/GID checks are bypassed when including files from    
this directory and its subdirectories"    
    
which also seems to fit.    
  
It's going to make it really hard for me (and I would    
guess many others) as a developer to support a given use   
case.  
  
The use case that doesn't seem to be satisfied by this is    
when the files in the safe_mode_include_dir (smarty for   
example) needs to fopen() other files in that directory.    
Smarty tries is_readable, which now succeeds, but there is  
no way for smarty, when running under safe mode, to  
actually read common templates... <sigh>.  I don't know if  
this use case can be satisfied without that  
functionality.... I have hundreds of users, but no  
apparent way, (other than includes which now work) to do  
any type of complex templating.  In most commercial  
environments, this wouldn't seem like a problem, but in  
the university setting, they all need access to a common  
template.  
  
Ok.  I'm ranting and a bit frustrated -- although I do  
admit that you're right per all the docs.  Feel free to  
delete this message and close the bug report.  I  
appreciate you leaving it open a bit longer. If you're 
open for any discussion, you can e-mail me. 
  
Thanks.
 [2005-08-12 20:29 UTC] tony2001@php.net
This is the reason why safe_mode should have been nuked long time ago.

 [2005-08-12 22:15 UTC] kpederson at mail dot ewu dot edu
Hmm... wouldn't something like safe_mode_read_dir make it  
possible to have shared libraries while using safe mode,  
assuming it allowed fopen(), include/require access?  
  
I don't see how else it's possible to make common modules,  
like the pear library, available globally, unless they  
never need to do more than include other files in their  
own hierarchy, while using safe mode.  
  
To turn off safe mode, would be a huge security risk  
unless I were running it using suExec and CGI or  
something.  
  
I'm going to ask on #PHP for other thoughts as there has  
to be a way to get the best of both worlds (common  
accessible libraries vs. security).  Thanks for the help.
 [2006-05-29 06:45 UTC] parktrip at gmail dot com
Could someone tell me what will happened to this report ? is this supposed to be solved in a future version of PHP ? I have the same problem with smarty in a commercial application. Is there another way to make it work with safe_mode on ?

Thanks a lot.
 [2006-06-19 20:02 UTC] yanstiac at yahoo dot com
Guys... that is what open_basedir is actually for. 
Cheers,
Nstiac
 [2006-06-19 20:07 UTC] yanstiac at yahoo dot com
Just need to read a bit =) 
Nstiac

http://www.php.net/manual/en/features.safe-mode.php#ini.sect.safe-mode
 [2006-06-19 21:03 UTC] kpederson at mail dot ewu dot edu
open_basedir does not do what I need it to do.  The 
functionality and setup that I need:

1) I have many users per host, each with their own group 
hierarchy.
2) Each user cannot access any other users data, unless 
they are in the same group.  Thus, I have user and group 
permissions that need to be managed.
3) I have common scripts that everyone needs to access 
(smarty templates and wrappers).

Because of #1 and #2, I need safe mode with GID checking. 
Because of #3, I need to have a directory that *everyone* 
can include and read from -- safe_mode_include_dir is not 
sufficient because it doesn't allow the users to read the 
templates, only include them and smarty (smarty.php.net) 
needs the ability to read them in order for them to work.

open_basedir is great for restricting reads between hosts. 
I could set it to /path/to/host/;/path/to/templates/ and 
then users would only be able to access files within their 
host and the templates, but it still doesn't solve the 
problem at hand.
 [2007-02-25 19:10 UTC] nobody at bugs dot php dot net
Until an is_includible() is added, it's possible to check a file exists using realpath() even with safe mode enabled which allows Smarty to at least see and include() its own plugins.
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Thu Apr 17 09:02:29 2014 UTC