php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #49049 file_exists() under unprivileged user with set user ID on execution fails
Submitted: 2009-07-24 14:57 UTC Modified: 2009-07-30 19:32 UTC
Votes:2
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:1 (100.0%)
From: rusxakep at gmail dot com Assigned:
Status: Not a bug Package: Filesystem function related
PHP Version: 5.2.10 OS: Linux 2.6.29
Private report: No CVE-ID: None
 [2009-07-24 14:57 UTC] rusxakep at gmail dot com
Description:
------------
1. Create and save to /home/user this next short program:
#define REAL_PATH "/home/user/test.php"
main(ac, av)
     char **av;
{
  execv(REAL_PATH, av);
}

2. Compile cc -o test test.c
3. chown root.root test and chmod 6755 test
5. Create test.php
#!/usr/bin/php-cgi -q
<?php
if (!file_exists("/home/user/2/3")) mkdir ("/home/user/2/3",0700,true);
?>
6. chown root.root test.php and chmod 0755 test.php
7. chmod 1777 /home/user
7. run with any unprivileged user:
su - user -c "/home/user/test"

First run ok, creating directory with root access
Second run failed with next error message:

Warning: mkdir(): File exists in /home/user/test.php on line 3

Reproduce code:
---------------
no safe mode, default php.ini

Expected result:
----------------
Empty exit w/o warning

Actual result:
--------------
Warning: mkdir(): File exists in /home/user/test.php on line 3

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-07-24 15:00 UTC] rusxakep at gmail dot com
If you reduce path to "/home/user/2", then working fine.)
 [2009-07-24 15:37 UTC] jani@php.net
Isn't this just same bug as bug #45040 is? Try compile using the LFS 
flags.
 [2009-07-25 20:20 UTC] rusxakep at gmail dot com
No, isn't LFS bug.

I'm run test php from LFS bug notes. mkdir working fine. Directory has been created successfully.)
 [2009-07-26 12:02 UTC] jani@php.net
Did you or did you not compile using the LFS flags?
 [2009-07-27 09:19 UTC] rusxakep at gmail dot com
Added -D__USE_FILE_OFFSET64 to CFLAGS and re-compile all php stuff.

Example of compilation (correct?):

"/bin/sh /var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/libtool --silent --preserve-dup-deps --mode=compile i686-pc-linux-gnu-gcc  -IZend/ -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/Zend/ -DPHP_ATOM_INC -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/include -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/main -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430 -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/ext/date/lib -I/usr/include/libxml2 -I/usr/include/freetype2 -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/ext/mbstring/oniguruma -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/ext/mbstring/libmbfl -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/ext/mbstring/libmbfl/mbfl -I/usr/include/mysql -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/TSRM -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/Zend    -I/usr/include -march=pentium4 -O2 -fomit-frame-pointer -pipe -D_GNU_SOURCE -D__USE_FILE_OFFSET64  -prefer-non-pic -c /var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/Zend/zend_objects.c -o Zend/zend_objects.lo
/bin/sh /var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/libtool --silent --preserve-dup-deps --mode=compile i686-pc-linux-gnu-gcc  -IZend/ -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/Zend/ -DPHP_ATOM_INC -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/include -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/main -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430 -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/ext/date/lib -I/usr/include/libxml2 -I/usr/include/freetype2 -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/ext/mbstring/oniguruma -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/ext/mbstring/libmbfl -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/ext/mbstring/libmbfl/mbfl -I/usr/include/mysql -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/TSRM -I/var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/Zend    -I/usr/include -march=pentium4 -O2 -fomit-frame-pointer -pipe -D_GNU_SOURCE -D__USE_FILE_OFFSET64  -prefer-non-pic -c /var/tmp/portage/dev-lang/php-5.2.99/work/php5.2-200907241430/Zend/zend_object_handlers.c -o Zend/zend_object_handlers.lo"


My problem not resolved yet :(

access("/home/1/2/3", F_OK)             = -1 EACCES (Permission denied)
stat64("/home/1/2", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0
mkdir("/home/1/2/3", 0700)              = -1 EEXIST (File exists)
write(1, "\nWarning: mkdir(): File exists in"..., 61
Warning: mkdir(): File exists in /home/1/test.php on line 3
) = 61)
 [2009-07-28 19:52 UTC] jani@php.net
Your report is quite confusing. You talk about creating /home/user/2/3 
directories (note the missing 1?) but your straces show quite different 
outputs. Please, come up with _simple_ way to reproduce this. Simple 
meaning ONE php (and PHP ONLY!) file.
 [2009-07-28 20:12 UTC] rusxakep at gmail dot com
Jani,

Is bug reproducing ONLY with using "set execution bit"!

Simple way test with strace output:

1. Temprorarly set "chmod 6755" to /usr/bin/strace binary for correct test.
2. Create simple CONSOLE test.php file and place to /home/1/ directory (owner should be root.root):

#!/usr/bin/php -q
<?php
if (!file_exists("/home/1/2/3")) mkdir("/home/1/2/3",0700,true);
?>

3. Run this script under any unprivileged user with next cmd: "su - someuser -c "/usr/bin/strace /home/1/test.php"

First run test.php create directory /home/1/2 and /home/1/2/3 successfully with "someuser" owner and 0700.

Second run test.php script must be finish w/o any messages, because directory already exists, but function file_exists() incorrectly fulfils and produces that the directory does not exist, though it is. 

Try it!

If something else is not clear, I will explain more in detail.)
 [2009-07-28 20:49 UTC] jani@php.net
My /home dir isn't writable by anyone. So how could it work? And what 
does setting those permissions on strace help to debug this?
 [2009-07-29 16:34 UTC] rusxakep at gmail dot com
You can use any other directory where you are able to do it. You can use the virtual machine (like VirtalBox or VMware) and so forth.

Installation "execution bit" on/usr/bin/strace is necessary for situation creation where the error in function file_exist() appears. It is necessary for more exact understanding of an error.

In a real situation which is described in my first post, the role strace is fulfilled by the program written on C with "execution bit enable".

If still something is not clear, write, I will explain.)
 [2009-07-29 23:26 UTC] jani@php.net
PLEASE give proper steps to reproduce this. Preferrably something we can 
simply cut'n'paste..
 [2009-07-30 12:52 UTC] rusxakep at gmail dot com
bash script ok? Real cut'n'paste :-)

Copy this script in any place and run under root:


#!/bin/sh
# Create test user
useradd -d /home/user1 -g users -m -N -p 12345 -s /bin/sh user1
chmod 0755 /home/user1

# Create wrapper with "execution bit" enabled
echo "#define REAL_PATH \"/home/user1/test.php\"
main(ac, av)
    char **av;
{
    execv(REAL_PATH, av);
}" > /home/user1/test.c

# Compile and set "execution bit"
cc -o /home/user1/test /home/user1/test.c
chown user1.users /home/user1/test
chmod 6755 /home/user1/test

# Create php script, who'll work under wrapper (see before)
echo "#!/usr/bin/php -q
<?php
if (!file_exists(\"/home/user1/1/2/3/4/5\")) {
    if (mkdir (\"/home/user1/1/2/3/4/5\",0700,true)) echo \"MKDIR OK, directory has been created\n\"; else echo \"MKDIR FAIL\n\";
} else {
    echo \"TEST PASSED!\n\";
}
?>
" > /home/user1/test.php

chown user1.users /home/user1/test.php
chmod 755 /home/user1/test.php

# Create second test user
useradd -d /home/user2 -g users -m -N -p 12345 -s /bin/sh user2

# Test!
echo "First run, when directory doesn't exist ...";
su - user2 -c "/home/user1/test"

echo "Second (bug) run, when directory already exist and must be return TEST PASSED! message";
su - user2 -c "/home/user1/test"

# Cleaning
userdel -f -r user2
userdel -f -r user1)
 [2009-07-30 19:32 UTC] rusxakep at gmail dot com
Quote from http://ru.php.net/manual/en/function.file-exists.php:

"Note: The check is done using the real UID/GID instead of the effective one."

Quote from man 2 access:

"The check is done using the calling process's real UID and GID, rather than the effective IDs as is done when actually attempting an operation (e.g., open(2)) on the  file.This allows set-user-ID programs to easily determine the invoking user's authority."

Workaround -> use stat() or is_dir().)
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sun Jun 02 00:01:30 2024 UTC