faqts : Computers : Programming : Languages : PHP : Installation and Setup : Web Servers : Apache

+ Search
Add Entry AlertManage Folder Edit Entry Add page to http://del.icio.us/
Did You Find This Entry Useful?

40 of 61 people (66%) answered Yes
Recently 5 of 10 people (50%) answered Yes

Entry

How to enable suexec for php scripts outside ScriptAlias directory of VirtualHost
Why are the files I create in PHP owned by "nobody"?

Dec 18th, 2003 04:23
Alexander Amelkin, Simon "janus" Dassow


Apache's mod_php executes php scripts under the UID of the apache 
process itself (usually 'nobody'), which is a potential security 
problem. There are solutions for enabling suexec on php scripts, but 
they require all your scripts are in the ScriptAlias'ed directory of a 
VirtualHost and the first line of your php scripts is a magic line 
(#!/usr/bin/php). However, if you provide virtual hosting for your 
customers, it's not too good to ask them to edit their php scripts to 
conform with those rules. That's why I developed the following small C 
program acting as a PHP wrapper. Using this program will enable UID/GID 
setting on php scripts, while keeping them look like they are processed 
by mod_php. The original version has been slightly advanced by 
Simon "janus" Dassow to fix certain problems with scripts running 
outside the cgi-bin directory. Since I keep getting questions about the 
wrapper, I decided to post Simon's version here instead of the original 
one. Later I found that both the original script and the Simon's 
version suffer from memory allocation errors sometimes. So here is the 
latest updated and advanced version of the wrapper:
/*******************************************************
 * This is a new version of the wrapper originally written
 * by me and then slightly modified by Simon 'janus' Dassow.
 * The new version fixes memory allocation problems that 
 * lead to sigfaults sometimes. It also introduces
 * debug information output to apache's error logs and the
 * output is made more "apache-like".
 *
 *                         Alexander Amelkin <spirit@reactor.ru>
 *                         23.05.2021
 *******************************************************
*/
#define VERSION "1.2"
/* Previous info:
 * 
 * This is a modified version of the PHP wrapper from
 * Alexander Amelkin.
 * 
 * I modified it to run under conditions where php is
 * running outside cgi-bin.
 * 
 * Now the wrapper fixes the environmentvariables
 * SCRIPT_NAME and SCRIPT_FILENAME.
 *
 * Wed Aug 21 16:46:46 CEST 2002
 * Simon "janus" Dassow, janus@linux-de.org
 *
 * 
 * (c) 2002, Alexander Amelkin, spirit@reactor.ru
 *
 * -----------------------------------------------------
 *                 Installation Manual
 * -----------------------------------------------------
 * To enable suexec php scripts, first of all you must
 * disable php processing by mod_php. To do so, comment
 * out all php-related strings in your httpd.conf
 *
 * Add the following strings to httpd.conf:
 *  AddType application/x-httpd-php .php .php4 .php3 .phtml
 *  Action application/x-httpd-php /cgi-bin/php_cgi        
 *
 * Do the following:
 * gcc php_cgi.c -o php_cgi
 * chmod 755 php_cgi
 *
 * Copy (do not use ln) php_cgi to the ScriptAlias /cgi-bin/ directory
 * of each VirtualHost, where php must be enabled. chown php_cgi
 * to whatever is set by User and Group directives for each
 * VirtualHost. You may also want to 'chattr +i php_cgi' in order
 * to prevent user from deleting or modifying the wrapper.
 *
 * You're done. No modification to the existing php scripts
 * is required.
 *
 * To enable debug information output, create a file called
 * 'php_debug' in the directory where php_cgi resides.
 *  
*******************************************************/
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
// PHP binary file
#define PHPPATH "/usr/bin/php"
void report(char *s)
{
 time_t t;
 char *tm;
 char ss[2048];
 time(&t);
 tm=ctime(&t);
 tm[strlen(tm)-1]=0;
 fprintf(stderr, "[%s] [debug] php_cgi %s\n", tm, s);
}
void reperror(char *s)
{
 time_t t;
 char *tm;
 char ss[2048];
 time(&t);
 tm=ctime(&t);
 tm[strlen(tm)-1]=0;
 sprintf(ss, "[%s] [error] php_cgi %s", tm, s );
 perror(ss); 
}
extern char **environ;
int main(int argc, char * argv[]) 
{
      char ss[2048];
      char *buf;
      char *script_name;
      char *script_filename;
      char *document_root;
      int i=0;
      int debug=0;
      struct stat flag;
      time_t t;
      sprintf(ss, "PHP wrapper version %s started", VERSION);
      report(ss); 
      sprintf(ss,"%s/php_debug",dirname(argv[0]));
      if( ! stat(ss,&flag) ) // File exists
	   debug=1;
      if(debug)
	 {
          report("Reporting environment variables:");
          while( environ[i] != NULL )
		report(environ[i++]); 
	  report("End of environment");
	 }
	// Check if a script name was supplied
	if(getenv("PATH_TRANSLATED")!=NULL) {
		// Allocate some memory for a temporary buffer
		if(debug) report("Allocating memory for 
PATH_TRANSLATED");
		buf=strdup(getenv("PATH_TRANSLATED"));
		if(buf==NULL) {
			printf("Content-Type: text/plain\n\n");
			reperror("step 1 memory allocation error");
			return 1;
		}
		// Get the script's working directory
		dirname(buf);
		// chdir there
		if(debug) 
		 { 
		  sprintf(ss,"Changing working directory to %s",buf);
       		  report(ss);
		 }
		if(chdir(buf)==-1) {
			printf("Content-Type: text/plain\n\n");
			reperror("can't chdir");
			return 1;
		}
		// We don't need the buffer anymore
		free(buf);
		// Reset broken SCRIPT_* vars
		// Allocate memory
		if(debug) report("Allocating memory for REQUEST_URI and 
DOCUMENT_ROOT");
		script_name=strdup(getenv("REQUEST_URI"));
		document_root=strdup(getenv("DOCUMENT_ROOT"));
		if((script_name==NULL)||(document_root==NULL)) {
			printf("Content-Type: text/plain\n\n");
			reperror("step 2 memory allocation error");
			return 1;
		}
		if(debug) report("Resetting REQUEST_URI and 
DOCUMENT_ROOT");
		setenv("REQUEST_URI",script_name,1);
		if(strchr(script_name,'?')) rindex(script_name,'?')[0]
=0;
		setenv("SCRIPT_NAME",script_name,1);
		if(debug) report("Allocating memory for 
SCRIPT_FILENAME");
		script_filename=(char*)malloc((strlen(document_root)
+strlen(script_name)+1));
		if(script_filename==NULL) {
			printf("Content-Type: text/plain\n\n");
			reperror("step 3 memory allocation error");
			return 1;
		}
		sprintf(script_filename,"%s%
s",document_root,script_name);
		if(debug) 
		  {
		   sprintf(ss,"SCRIPT_FILENAME=\"%
s\"\n",script_filename);
		   report(ss);
		  }
		// Invoke php to run the script, all CGI parameters are 
left
		// untouched in the system environment
		setenv("SCRIPT_FILENAME",script_filename,1);
		if(debug) report("Freeing script_name...");
		free(script_name);
		if(debug) report("Freeing script_filename...");
		free(script_filename);
		if(debug) report("Freeing document_root...");
		free(document_root);
		sprintf(ss,"executing [%s]...", getenv
("PATH_TRANSLATED"));
		report(ss);
		execl(PHPPATH,PHPPATH,getenv("PATH_TRANSLATED"),NULL);
		return 0;
	}
	printf("Content-Type: text/plain\n\n");
	sprintf(ss,"wrong script requested [%s]",getenv
("PATH_TRANSLATED"));
	printf("%s",ss);
	reperror(ss);
	return 1;
}