Lars Lars - 1 month ago 18
PHP Question

sys_get_temp_dir in shared hosting environment

Note: This could also fit in superuser.

I am setting up PHP 5.3.10 on a shared host with apache2 mpm itk and open_basedir in a way, that each user may not see or change the files of another user. In the apache2 vhost settings, I add the appropriate entries to restrict the user:

AssignUserId userA userA
php_admin_value open_basedir /home/userA/www/
php_admin_value upload_tmp_dir /home/userA/www/tmp/
php_admin_value session.save_path /home/userA/www/tmp/
SetEnv TMPDIR /home/userA/www/tmp/


Now, the first line sets the linux user to use for apache2, the next three lines define the basedir, upload directory and session savepath to be in the user directory. I'll get back to the last line in a sec.

Now for the problem:
sys_get_temp_dir()
should give back the temporary directory for php, which is /tmp be default on a linux system. For security reasons, this directory should reside in the open_basedir of userA. According to the php-source of 5.3.10, the
sys_get_temp_dir()
-function uses the environment variable TMPDIR to get this directory:

// php-src/main/php_open_temporary_file.c:217-219
/* On Unix use the (usual) TMPDIR environment variable. */
{
char* s = getenv("TMPDIR");


This is what the fifth line in the configuration above should do. However,
sys_get_temp_dir()
simply returns the global system directory, ignoring the environmental variable (which is perfectly set in $_SERVER, also viewable via
phpinfo()
).

This results in some nasty bugs with various software relying on
sys_get_temp_dir()
, as that directory is outside of the
open_basedir
setting. I've tried to set the variable directly into $_ENV and $_SERVER without a change in behaviour. I've tried a
putenv('TMPDIR=/home/userA/www/tmp')
without change.

However, I am able to change the output by defining the variable into /etc/apache2/envvars - which is useless for me, as I want each VHOST to have its own temporary folder.

The only solution I have found so far is overwriting the internal
sys_get_temp_dir()
through an extension like runkit and enforcing its inclusion via auto_prepend_file. But that solution is so dirty, I simply can't believe, that there is no better solution around.

So, my question: Is there any way to change the result of
sys_get_temp_dir()
to be set in an apache2 vhost setting, without reimplementing the function with runkit?


Edit: The apache version is 2.2.22, and I currently use mod_php. As I will have to add all users manually, an fcgi or similar setup would also be possible.

Answer

Running a putenv('TMPDIR=/foo/bar') inside PHP seems to be able to affect the result of sys_get_temp_dir(). You could have an auto_prepend_file directive arranged to run a piece of PHP to set up the TMPDIR and avoid messing with a redefinition of sys_get_temp_dir().

Edit: Also, you could easily use putenv('TMPDIR='.ini_get('open_basedir').'/tmp') to set the temporary directory to the directory structure you laid out in the question.

Funny enough, this turns out to also work (given that you keep the SetEnv TMPDIR /foo/bar in your Apache configuration):

putenv('TMPDIR='.getenv('TMPDIR'));

Seems like a no-op, but actually does have effect on sys_get_temp_dir(). I'm starting to suspect this has to be some environment-handling bug in PHP.