sys_get_temp_dir in shared hosting environment - php

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.

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.

You have tagged your question cgi, however you are making use of
php_admin_value open_basedir /home/userA/www/
^^^^^^^^^^^^^^^
which is a setting for the apache module version of PHP, Mod_PHP. In that case PHP is loaded once the webserver starts.
Then you make use of SetEnv:
SetEnv TMPDIR /home/userA/www/tmp/
this is setting an internal environment variable. It is passed to other apache modules, however I think you need it with the request, not with the virtual server. I don't know it specifically, but I would assume according to your description that this environment variable is getting reset before the PHP script is invoked.
So more a comment than a real answer, but hopefully it helps you clarify some things.
I normally use FCGI for multi-user environments so that I can better separate the users. I never had problems with setting environment variables per each user. But that's just another comment, I don't want to say you have to use it, too. Just to highlight that you need to find out the right place within apache to set the environment variable so it is (still) set when the script is executed.
Also you might not be setting the right environment variable. According to Apache Documentation about environment variables:
Although these variables are referred to as environment variables, they are not the same as the environment variables controlled by the underlying operating system. Instead, these variables are stored and manipulated in an internal Apache structure. They only become actual operating system environment variables when they are provided to CGI scripts and Server Side Include scripts. If you wish to manipulate the operating system environment under which the server itself runs, you must use the standard environment manipulation mechanisms provided by your operating system shell.
You want to set the operating system environment variable for PHP. But you are setting the internal environment variable only.
Mod_PHP might import them to the script, so if you use getenv('TMPDIR') the PHP SAPI specific implementation is used - which does allow you to see those internal environment variables - however the php_get_temporary_directory function is not using it - it looks like.
Please add your Apache and PHP version to your question.

According to this - 4 year old - bug, sys_get_temp_dir() won't work with virtual-hosts; so
you can try to use only libraries that fixed this issue (& open a bug for those who didn't)
or append /tmp (or whatever your OS uses) in your open_basedir, as it can hold multiple directories (like include_path - separate it with ; on Windows, : otherwise)

Looking at the PHP source, sys_get_temp_dir() works with the following priority:
If its value has been calculated before, the cached value is used.
sys_temp_dir is checked in the ini configuration.
On Windows, the GetTempPathW Win32 API method is used, and according to its documentation the following are used (in this order):
The path specified by the TMP environment variable.
The path specified by the TEMP environment variable.
The path specified by the USERPROFILE environment variable.
The Windows directory.
In *nix, the following are used (in this order):
The TMPDIR environment variable.
The P_tmpdir macro
/tmp (according to the source, this is a last-ditch effort that should never happen).
That should give you enough options for controlling the result of sys_get_temp_dir (e.g. ini_set('sys_temp_dir', $tmpPath) or putenv('TMPDIR=/foo/bar') as others mentioned) Unless it was previously calculated, in which case you're SOL as far as I know and the cached value will be used (but I have zero knowledge in PHP so would love to hear otherwise).

This is a bug in php 5.2 - specify temp dir by php.ini
It's fixed in 5.5
Use this as a temporary solution:
<?php
putenv('TMPDIR=/path/to/your/tmp');
...your code here ...

In case people end up here whos Problem is not solved with putenv...
... for me, it worked to set the sys_temp_dir using php's ini_set like this:
$tmpPath = realpath(__DIR__.'/../app/tmp');
ini_set('sys_temp_dir', $tmpPath);
I am running PHP 5.5.9 (cli) on a windows8 machine.

It looks like you can change the value returned by sys_get_temp_dir(), I have just tried on apache 2.4 and php 5.6.27.
Add a sys_temp_dir in the virtualhost:
php_admin_value sys_temp_dir "/var/www/alternc/f/fser/tmp"
Restart apache, and print the value in a web page using sys_get_temp_dir():
<?php
echo sys_get_temp_dir () ;
Produces the expected output: /var/www/alternc/f/fser/tmp.

Related

How to change php.ini on ibm bluemix

I want to use phpmyadmin by this git.
https://github.com/dmikusa-pivotal/cf-ex-phpmyadmin
but I have to change max_file_uploads in php.ini.
Any ideas how to solve this?
Sorry for my bad English.
PHP is quite flexible in how you configure it. There are several ways to change php.ini settings. Here's what I recommend for the PHP build pack.
The easiest way to change php.ini settings for a PHP application deployed to CF is to use PHP's per-directory settings.
To do this, you can just drop a file named .user.ini into the root of your PHP files. In the case of the PHPMyAdmin app you referenced, you'd put it in the htdocs directory, since this is where all the application files are installed. Set whatever options you need to adjust in that file.
Push your application to Cloud Foundry and it should pick up the new configuration options.
Side note, you can put the .user.ini file in other directories too, exactly where you put it depends on the scope of where you want the settings contained in that file to be applied. See the PHP docs for more details.
This option will work for any php.ini setting that's not marked as PHP_INI_SYSTEM.
If the setting you want to change is marked as PHP_INI_SYSTEM or if you want to change php.ini settings during staging (for example, to affect the options used by Composer) then you need to use this option.
First create the folder .bp-config/php/conf.d in the root of your project. Then add a file with the .ini extension into that directory, ex: my-settings.ini (side note, you can add multiple .ini files to this directory). Set whatever options you need to adjust in it.
Second set the environment variable PHP_INI_SCAN_DIR for for your application to .bp-config/php/conf.d. This will instruct PHP to look for additional INI configuration in the directory that we just created.
You can set the environment variable by running cf set-env app-name 'PHP_INI_SCAN_DIR' '.bp-config/php/conf.d' or by adding it to the env block of your manifest.yml file.
Push your application to Cloud Foundry and it should pick up the new configuration options.
The benefit of both of these approaches is that your .user.ini file only needs to set the options that you care about. The option that Alex mentioned in his answer will technically work, but if you do that you will override all of the default php.ini settings that are provided by the build pack.
There's two reasons I don't recommend overriding everything.
You now have the complete configuration file in your app and if we change anything in the build pack, your configuration will get out-of-sync and your app could fail to stage the next time you push it.
If you're not very careful you can break things. This is one of the most common things I see people doing with PHP apps that causes an application to fail, more specifically it causes the PHP extensions to not load which in turn causes the app fail.
Here is what you have to do:
1) First follow the instructions in the README to install PHPMyAdmin:
https://github.com/dmikusa-pivotal/cf-ex-phpmyadmin
2) In your application folder under .bp-config directory create a php sub-directory
3) Copy default php.ini to the php sub-directory. The default file is located here
4) Edit local php.ini and change max_file_uploads to 180
5) Push your application using cf command line
6) Check if max_file_uploads was properly updated:
# cf files <your app name> app/php/etc/php.ini | grep max_file_uploads
You can get more details here.

php doc_root in php.ini and DocumentRoot in httpd.conf

I wanted to learn more about PHP and Apache so I decided to install them manually. I don't exactly understand how the two files work together (or if they even go hand in hand in this situation). Whenever I load the localhost webpage, the location of the php files are directed from what I specify in the httpd.conf file. I've made two root folders just for the sake of testing, C:/Users/Alex/test and C:\Users\Alex\My Websites. Apache does not actually use the location that I specified in php.ini (doc_root = "C:\Users\Alex\My Websites"), but instead uses the location that I specified in httpd.conf (DocumentRoot "C:/Users/Alex/test" ). Can anyone please explain when is the root useful in php.ini?
Most likely you're running mod_php inside of Apache (this is the most common way to run PHP under Apache). That means that the PHP environment is controlled entirely by Apache (in Unix environments Apache has its own user as well). You can reconfigure it to use Fast CGI (which is the only way to run PHP under other web servers like nginx) and the setting will matter under that type of setup.
Here's the manual entry for the setting
PHP's "root directory" on the server. Only used if non-empty. If PHP
is configured with safe mode, no files outside this directory are
served. If PHP was not compiled with FORCE_REDIRECT, you should set
doc_root if you are running PHP as a CGI under any web server (other
than IIS). The alternative is to use the cgi.force_redirect
configuration below.

How to configure htaccess-like files for configuring PHP when on shared hosting situations or FCGI? [duplicate]

My development environment is shared with other developers of my startup and is setup on Rackspace. The php.ini file is located in /etc/ folder, and I believe this is a centralized location from where every other developer's dev environment setting is being configured from. I want to customize this php.ini file specifically for myself rather than having to do it in the /etc/ location.
Specifically I am setting up XDEBUG in my environment, some other developers don't want it, so I don't want to bug em :)
To do so, I scanned the Internet on how to override the php.ini file specifically for a directory, and found this page on stackoverflow
And following that, I simply copy pasted the php.ini file within my htdocs folder and then simply echoed out phpinfo() (I echoed this in one of my Controllers, (using Zend)). The index.php file is within the htdocs folder.
When I look # "Loaded Configuration File", it still reads
/etc/ instead of ../htdocs/
Anybody know what's up?
In general, it isn't possible to load php.ini on a per directory basis, but in some special cases (CGI/FastCGI), it is: see documentation for Per-user configuration
Since PHP 5.3.0, PHP includes support for .htaccess-style INI files on a per-directory basis. These files are processed only by the CGI/FastCGI SAPI. This functionality obsoletes the PECL htscanner extension. If you are using Apache, use .htaccess files for the same effect.
In addition to the main php.ini file, PHP scans for INI files in each directory, starting with the directory of the requested PHP file, and working its way up to the current document root (as set in $_SERVER['DOCUMENT_ROOT']). In case the PHP file is outside the document root, only its directory is scanned.
If you are hosting several independent sites on one server, you should consider FastCGI anyway, to keep them separated. With php5-fpm it's very easy to setup many pools of workers.
Note that only set a limited subset of the ini-options in the user-ini-file.
As you said you don't have control on the server, the possible work-arounds would be to:
Use ini_set() to override the changes inside your script. Not all of the configuration directives can be changed using ini_set() though.
Use an .htaccess file in your directory to override the configurations in php.ini file.
(certain parts adapted from #1438393)
Hope this helps!
I'm not sure you understood the post. The post means if you run the server and want a per domain php.ini you can run the module as a per domain so each user controls there domain php.ini however it looks like your server does not offer this so you will need to us htaccess file to overwrite the php.ini settings.
By over write this doesn't mean you can change the directory this means maybe add a module or add error reporting ect...
You can do it by using this post: How can I use xdebug to debug only one virtual host?

Overriding PHP.ini in a shared development environment

My development environment is shared with other developers of my startup and is setup on Rackspace. The php.ini file is located in /etc/ folder, and I believe this is a centralized location from where every other developer's dev environment setting is being configured from. I want to customize this php.ini file specifically for myself rather than having to do it in the /etc/ location.
Specifically I am setting up XDEBUG in my environment, some other developers don't want it, so I don't want to bug em :)
To do so, I scanned the Internet on how to override the php.ini file specifically for a directory, and found this page on stackoverflow
And following that, I simply copy pasted the php.ini file within my htdocs folder and then simply echoed out phpinfo() (I echoed this in one of my Controllers, (using Zend)). The index.php file is within the htdocs folder.
When I look # "Loaded Configuration File", it still reads
/etc/ instead of ../htdocs/
Anybody know what's up?
In general, it isn't possible to load php.ini on a per directory basis, but in some special cases (CGI/FastCGI), it is: see documentation for Per-user configuration
Since PHP 5.3.0, PHP includes support for .htaccess-style INI files on a per-directory basis. These files are processed only by the CGI/FastCGI SAPI. This functionality obsoletes the PECL htscanner extension. If you are using Apache, use .htaccess files for the same effect.
In addition to the main php.ini file, PHP scans for INI files in each directory, starting with the directory of the requested PHP file, and working its way up to the current document root (as set in $_SERVER['DOCUMENT_ROOT']). In case the PHP file is outside the document root, only its directory is scanned.
If you are hosting several independent sites on one server, you should consider FastCGI anyway, to keep them separated. With php5-fpm it's very easy to setup many pools of workers.
Note that only set a limited subset of the ini-options in the user-ini-file.
As you said you don't have control on the server, the possible work-arounds would be to:
Use ini_set() to override the changes inside your script. Not all of the configuration directives can be changed using ini_set() though.
Use an .htaccess file in your directory to override the configurations in php.ini file.
(certain parts adapted from #1438393)
Hope this helps!
I'm not sure you understood the post. The post means if you run the server and want a per domain php.ini you can run the module as a per domain so each user controls there domain php.ini however it looks like your server does not offer this so you will need to us htaccess file to overwrite the php.ini settings.
By over write this doesn't mean you can change the directory this means maybe add a module or add error reporting ect...
You can do it by using this post: How can I use xdebug to debug only one virtual host?

PHP file_exists() function returns false on /usr/bin/mysql

I've read numerous posts on this problem and none of them matches my problem exactly. I have a WordPress site (currently 3.5) on a GoDaddy virtual host. In November I opted to upgrade the O/S from CentOS 5 to CentOS 6.3, which involved a full O/S reinstall over which I had no control and about which I had no information. Following the O/S reinstall I rebuilt the site from a backup I had taken just before starting.
After the rebuild, a WordPress plugin we've been using for years, WP-DBManager, suddenly stopped backing up our mysql database. The backup fails because the backup panel claims "MYSQL path does NOT exist." Annoyingly, when you go to the DB Options page and tell it to auto-detect the mysql path, the options page produces /usr/bin/mysql, which is correct. I can log into the site with SSH and there it is. The permissions are:
-rwxr-xr-x 1 root root 338184 Jun 22 05:58 /usr/bin/mysql
This SHOULD work. SOMETHING in my site permissions changed with this rebuild and I don't know what; so far I've only documented WordPress configurations. The research I've done suggests it may be something to do with PHP safe mode. We run PHP 5.3.3, and the configuration list from phpinfo() does not show
--enable-safe-mode
which means safe mode should be OFF. The safe mode settings in php.ini when this started were:
safe_mode_allowed_env_vars = PHP_
safe_mode_protected_env_vars = LD_LIBRARY_PATH
safe_mode_exec_dir =
safe_mode_include_dir =
safe_mode = off
safe_mode_gid = off
I have since changed safe_mode_gid to ON, with no effect. I have a test site built from the production site, where safe_mode_include_dir = ~ so I tried that, with no effect. The test site runs PHP 5.3.14 and the safe mode settings above were identical except for safe_mode_include_dir. I checked the ENV variable and /usr/bin is included in the PATH:
PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/lrservice/bin
I don't know if this is an environment variable problem, here are the safe mode entries for that:
safe_mode_allowed_env_vars = PHP_
safe_mode_protected_env_vars = LD_LIBRARY_PATH
These settings are not all the same on the working test site, one reads:
safe_mode_allowed_env_vars = PHP_ LANG LANG_
Since the site is fully functional except for this, I know that mysql's permissions are generally correct. Does this ring any bell for anyone?? Why am I getting all this if safe mode is officially turned off? I have a feeling there's something obvious and stupid that I'm missing.
You have access to the mysql binary from an ssh session in the /usr/bin directory, but php is unable to find it at that same location.
I am assuming that your system is using the apache2 webserver.
Is the ChrootDir directive present in the apache configuration file (usually located at /etc/httpd/conf/httpd.conf)?
If that's the case, you can check in the directory pointed by this directive if there is a link to the mysql binary. If not, simply add it by executing the following command (assuming you have the priviledges to do so) in your ssh session:
$ ln /usr/bin/mysql /chroot/path/usr/bin/mysql
with /chroot/path replaced by the ChrootDir directive path.
One of the comments mentions the open_basedir PHP setting, which can be configured either in php.ini, httpd.conf, or .htaccess files.
This setting limits access to certain directory of the filesystem available to PHP. A possible fix is to remove this restriction for the scripts executed by the plugin you are using, if that setting is not protected:
locate the scripts installed by the plugin in your wordpress directory,
create a .htaccess file lifting the restriction in the directory containing the scripts with the following commands:
$ echo 'php_value open_basedir none' >> .htaccess
The above will add the text between simple quote at the end of the .htaccess file, creating it if necessary.
This solution is probably the safest, as it reduces the security relaxing to only these scripts. You should be wary that you are going to let these scripts potentially have access to much more than they really need to operate.
If the above does not work, it means that the setting is protected, and must be changed in either httpd.conf or php.ini which should be both located within the /etc directory. See this SO question for details.

Categories