File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / main / suhosin_patch.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:48:05 2012 UTC (12 years, 10 months ago) by misho
Branches: php, MAIN
CVS tags: v5_3_10, HEAD
php

/*
   +----------------------------------------------------------------------+
   | Suhosin Patch for PHP                                                |
   +----------------------------------------------------------------------+
   | Copyright (c) 2004-2010 Stefan Esser                                 |
   +----------------------------------------------------------------------+
   | This source file is subject to version 2.02 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available at through the world-wide-web at                           |
   | http://www.php.net/license/2_02.txt.                                 |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Author: Stefan Esser <sesser@hardened-php.net>                       |
   +----------------------------------------------------------------------+
 */
/* $Id: suhosin_patch.c,v 1.1.1.1 2012/02/21 23:48:05 misho Exp $ */

#include "php.h"

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "SAPI.h"
#include "php_globals.h"

#if SUHOSIN_PATCH

#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif

#if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
#undef AF_UNIX
#endif

#if defined(AF_UNIX)
#include <sys/un.h>
#endif

#define SYSLOG_PATH  "/dev/log"

#ifdef PHP_WIN32
static HANDLE log_source = 0;
#endif

#include "snprintf.h"

#include "suhosin_patch.h"

#ifdef ZTS
#include "suhosin_globals.h"
int suhosin_patch_globals_id;
#else
struct _suhosin_patch_globals suhosin_patch_globals;
#endif

static char *suhosin_config = NULL;

static zend_intptr_t SUHOSIN_POINTER_GUARD = 0;

static void php_security_log(int loglevel, char *fmt, ...);

static void suhosin_patch_globals_ctor(suhosin_patch_globals_struct *suhosin_patch_globals TSRMLS_DC)
{
	memset(suhosin_patch_globals, 0, sizeof(*suhosin_patch_globals));
}

ZEND_API char suhosin_get_config(int element)
{
        return ((char *)SUHOSIN_MANGLE_PTR(suhosin_config))[element];
}

static void suhosin_set_config(int element, char value)
{
        ((char *)SUHOSIN_MANGLE_PTR(suhosin_config))[element] = value;
}

static void suhosin_read_configuration_from_environment()
{
        char *tmp;
        
        /* check if canary protection should be activated or not */
        tmp = getenv("SUHOSIN_MM_USE_CANARY_PROTECTION");
        /* default to activated */
        suhosin_set_config(SUHOSIN_MM_USE_CANARY_PROTECTION, 1);
        if (tmp) {
                int flag = zend_atoi(tmp, 0);
                suhosin_set_config(SUHOSIN_MM_USE_CANARY_PROTECTION, flag);
        }
        
        /* check if free memory should be overwritten with 0xFF or not */
        tmp = getenv("SUHOSIN_MM_DESTROY_FREE_MEMORY");
        /* default to deactivated */
        suhosin_set_config(SUHOSIN_MM_DESTROY_FREE_MEMORY, 0);
        if (tmp) {
                int flag = zend_atoi(tmp, 0);
                suhosin_set_config(SUHOSIN_MM_DESTROY_FREE_MEMORY, flag);
        }
        
        /* check if canary violations should be ignored */
        tmp = getenv("SUHOSIN_MM_IGNORE_CANARY_VIOLATION");
        /* default to NOT ignore */
        suhosin_set_config(SUHOSIN_MM_IGNORE_CANARY_VIOLATION, 0);
        if (tmp) {
                int flag = zend_atoi(tmp, 0);
                suhosin_set_config(SUHOSIN_MM_IGNORE_CANARY_VIOLATION, flag);
        }

        /* check if invalid hashtable destructors should be ignored */
        tmp = getenv("SUHOSIN_HT_IGNORE_INVALID_DESTRUCTOR");
        /* default to NOT ignore */
        suhosin_set_config(SUHOSIN_HT_IGNORE_INVALID_DESTRUCTOR, 0);
        if (tmp) {
                int flag = zend_atoi(tmp, 0);
                suhosin_set_config(SUHOSIN_HT_IGNORE_INVALID_DESTRUCTOR, flag);
        }

        /* check if invalid linkedlist destructors should be ignored */
        tmp = getenv("SUHOSIN_LL_IGNORE_INVALID_DESTRUCTOR");
        /* default to NOT ignore */
        suhosin_set_config(SUHOSIN_LL_IGNORE_INVALID_DESTRUCTOR, 0);
        if (tmp) {
                int flag = zend_atoi(tmp, 0);
                suhosin_set_config(SUHOSIN_LL_IGNORE_INVALID_DESTRUCTOR, flag);
        }
        
        suhosin_set_config(SUHOSIN_CONFIG_SET, 1);
}

static void suhosin_write_protect_configuration()
{
        /* check return value of mprotect() to ensure memory is read only now */
        if (mprotect(SUHOSIN_MANGLE_PTR(suhosin_config), sysconf(_SC_PAGESIZE), PROT_READ) != 0) {
                perror("suhosin");
                _exit(1);
        }
}

PHPAPI void suhosin_startup()
{
#ifdef ZTS
	ts_allocate_id(&suhosin_patch_globals_id, sizeof(suhosin_patch_globals_struct), (ts_allocate_ctor) suhosin_patch_globals_ctor, NULL);
#else
	suhosin_patch_globals_ctor(&suhosin_patch_globals TSRMLS_CC);
#endif
	zend_suhosin_log = php_security_log;
	
	/* get the pointer guardian and ensure low 3 bits are 1 */
        if (SUHOSIN_POINTER_GUARD == 0) {
                zend_canary(&SUHOSIN_POINTER_GUARD, sizeof(SUHOSIN_POINTER_GUARD));
                SUHOSIN_POINTER_GUARD |= 7;
        }
	
	if (!suhosin_config) {
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
		suhosin_config = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
		if (suhosin_config == MAP_FAILED) {
			perror("suhosin");
			_exit(1);
		}
                suhosin_config = SUHOSIN_MANGLE_PTR(suhosin_config);
	}
	if (!SUHOSIN_CONFIG(SUHOSIN_CONFIG_SET)) {
        suhosin_read_configuration_from_environment();
        suhosin_write_protect_configuration();
    }
}

static char *loglevel2string(int loglevel)
{
	switch (loglevel) {
	    case S_FILES:
		return "FILES";
	    case S_INCLUDE:
		return "INCLUDE";
	    case S_MEMORY:
		return "MEMORY";
	    case S_MISC:
		return "MISC";
		case S_SESSION:
		return "SESSION";
	    case S_SQL:
		return "SQL";
	    case S_EXECUTOR:
		return "EXECUTOR";
	    case S_VARS:
		return "VARS";
	    default:
		return "UNKNOWN";    
	}
}

static void php_security_log(int loglevel, char *fmt, ...)
{
	int s, r, i=0;
#if defined(AF_UNIX)
	struct sockaddr_un saun;
#endif
#ifdef PHP_WIN32
	LPTSTR strs[2];
	unsigned short etype;
	DWORD evid;
#endif
	char buf[4096+64];
	char error[4096+100];
	char *ip_address;
	char *fname;
	char *alertstring;
	int lineno;
	va_list ap;
	TSRMLS_FETCH();

	/*SDEBUG("(suhosin_log) loglevel: %d log_syslog: %u - log_sapi: %u - log_script: %u", loglevel, SPG(log_syslog), SPG(log_sapi), SPG(log_script));*/
	
	if (SPG(log_use_x_forwarded_for)) {
		ip_address = sapi_getenv("HTTP_X_FORWARDED_FOR", 20 TSRMLS_CC);
		if (ip_address == NULL) {
			ip_address = "X-FORWARDED-FOR not set";
		}
	} else {
		ip_address = sapi_getenv("REMOTE_ADDR", 11 TSRMLS_CC);
		if (ip_address == NULL) {
			ip_address = "REMOTE_ADDR not set";
		}
	}
	
	
	va_start(ap, fmt);
	ap_php_vsnprintf(error, sizeof(error), fmt, ap);
	va_end(ap);
	while (error[i]) {
		if (error[i] < 32) error[i] = '.';
		i++;
	}
	
/*	if (SPG(simulation)) {
		alertstring = "ALERT-SIMULATION";
	} else { */
		alertstring = "ALERT";
/*	}*/
	
	if (zend_is_executing(TSRMLS_C)) {
		if (EG(current_execute_data)) {
			lineno = EG(current_execute_data)->opline->lineno;
			fname = EG(current_execute_data)->op_array->filename;
		} else {
			lineno = zend_get_executed_lineno(TSRMLS_C);
			fname = zend_get_executed_filename(TSRMLS_C);
		}
		ap_php_snprintf(buf, sizeof(buf), "%s - %s (attacker '%s', file '%s', line %u)", alertstring, error, ip_address, fname, lineno);
	} else {
		fname = sapi_getenv("SCRIPT_FILENAME", 15 TSRMLS_CC);
		if (fname==NULL) {
			fname = "unknown";
		}
		ap_php_snprintf(buf, sizeof(buf), "%s - %s (attacker '%s', file '%s')", alertstring, error, ip_address, fname);
	}
			
	/* Syslog-Logging disabled? */
	if (((SPG(log_syslog)|S_INTERNAL) & loglevel)==0) {
		goto log_sapi;
	}	
	
#if defined(AF_UNIX)
	ap_php_snprintf(error, sizeof(error), "<%u>suhosin[%u]: %s\n", (unsigned int)(SPG(log_syslog_facility)|SPG(log_syslog_priority)),getpid(),buf);

	s = socket(AF_UNIX, SOCK_DGRAM, 0);
	if (s == -1) {
		goto log_sapi;
	}
	
	memset(&saun, 0, sizeof(saun));
	saun.sun_family = AF_UNIX;
	strcpy(saun.sun_path, SYSLOG_PATH);
	/*saun.sun_len = sizeof(saun);*/
	
	r = connect(s, (struct sockaddr *)&saun, sizeof(saun));
	if (r) {
		close(s);
    		s = socket(AF_UNIX, SOCK_STREAM, 0);
		if (s == -1) {
			goto log_sapi;
		}
	
		memset(&saun, 0, sizeof(saun));
		saun.sun_family = AF_UNIX;
		strcpy(saun.sun_path, SYSLOG_PATH);
		/*saun.sun_len = sizeof(saun);*/

		r = connect(s, (struct sockaddr *)&saun, sizeof(saun));
		if (r) { 
			close(s);
			goto log_sapi;
		}
	}
	send(s, error, strlen(error), 0);
	
	close(s);
#endif
#ifdef PHP_WIN32
	ap_php_snprintf(error, sizeof(error), "suhosin[%u]: %s", getpid(),buf);

	switch (SPG(log_syslog_priority)) {			/* translate UNIX type into NT type */
		case 1: /*LOG_ALERT:*/
			etype = EVENTLOG_ERROR_TYPE;
			break;
		case 6: /*LOG_INFO:*/
			etype = EVENTLOG_INFORMATION_TYPE;
			break;
		default:
			etype = EVENTLOG_WARNING_TYPE;
	}
	evid = loglevel;
	strs[0] = error;
	/* report the event */
	if (log_source == NULL) {
		log_source = RegisterEventSource(NULL, "Suhosin-Patch-" SUHOSIN_PATCH_VERSION);
	}
	ReportEvent(log_source, etype, (unsigned short) SPG(log_syslog_priority), evid, NULL, 1, 0, strs, NULL);
	
#endif
log_sapi:
	/* SAPI Logging activated? */
	/*SDEBUG("(suhosin_log) log_syslog: %u - log_sapi: %u - log_script: %u - log_phpscript: %u", SPG(log_syslog), SPG(log_sapi), SPG(log_script), SPG(log_phpscript));*/
	if (((SPG(log_sapi)|S_INTERNAL) & loglevel)!=0) {
		sapi_module.log_message(buf);
	}

/*log_script:*/
	/* script logging activaed? */
	if (((SPG(log_script) & loglevel)!=0) && SPG(log_scriptname)!=NULL) {
		char cmd[8192], *cmdpos, *bufpos;
		FILE *in;
		int space;
		
		ap_php_snprintf(cmd, sizeof(cmd), "%s %s \'", SPG(log_scriptname), loglevel2string(loglevel));
		space = sizeof(cmd) - strlen(cmd);
		cmdpos = cmd + strlen(cmd);
		bufpos = buf;
		if (space <= 1) return;
		while (space > 2 && *bufpos) {
			if (*bufpos == '\'') {
				if (space<=5) break;
				*cmdpos++ = '\'';
				*cmdpos++ = '\\';
				*cmdpos++ = '\'';
				*cmdpos++ = '\'';
				bufpos++;
				space-=4;
			} else {
				*cmdpos++ = *bufpos++;
				space--;
			}
		}
		*cmdpos++ = '\'';
		*cmdpos = 0;
		
		if ((in=VCWD_POPEN(cmd, "r"))==NULL) {
			php_security_log(S_INTERNAL, "Unable to execute logging shell script: %s", SPG(log_scriptname));
			return;
		}
		/* read and forget the result */
		while (1) {
			int readbytes = fread(cmd, 1, sizeof(cmd), in);
			if (readbytes<=0) {
				break;
			}
		}
		pclose(in);
	}
/*log_phpscript:*/
	if ((SPG(log_phpscript) & loglevel)!=0 && EG(in_execution) && SPG(log_phpscriptname) && SPG(log_phpscriptname)[0]) {
		zend_file_handle file_handle;
		zend_op_array *new_op_array;
		zval *result = NULL;
		
		/*long orig_execution_depth = SPG(execution_depth);*/
		zend_bool orig_safe_mode = PG(safe_mode);
		char *orig_basedir = PG(open_basedir);
		
		char *phpscript = SPG(log_phpscriptname);
/*SDEBUG("scriptname %s", SPG(log_phpscriptname));`*/
#ifdef ZEND_ENGINE_2
		if (zend_stream_open(phpscript, &file_handle TSRMLS_CC) == SUCCESS) {
#else
		if (zend_open(phpscript, &file_handle) == SUCCESS && ZEND_IS_VALID_FILE_HANDLE(&file_handle)) {
			file_handle.filename = phpscript;
			file_handle.free_filename = 0;
#endif		
			if (!file_handle.opened_path) {
				file_handle.opened_path = estrndup(phpscript, strlen(phpscript));
			}
			new_op_array = zend_compile_file(&file_handle, ZEND_REQUIRE TSRMLS_CC);
			zend_destroy_file_handle(&file_handle TSRMLS_CC);
			if (new_op_array) {
				HashTable *active_symbol_table = EG(active_symbol_table);
				zval *zerror, *zerror_class;
				
				if (active_symbol_table == NULL) {
					active_symbol_table = &EG(symbol_table);
				}
				EG(return_value_ptr_ptr) = &result;
				EG(active_op_array) = new_op_array;
				
				MAKE_STD_ZVAL(zerror);
				MAKE_STD_ZVAL(zerror_class);
				ZVAL_STRING(zerror, buf, 1);
				ZVAL_LONG(zerror_class, loglevel);

				zend_hash_update(active_symbol_table, "SUHOSIN_ERROR", sizeof("SUHOSIN_ERROR"), (void **)&zerror, sizeof(zval *), NULL);
				zend_hash_update(active_symbol_table, "SUHOSIN_ERRORCLASS", sizeof("SUHOSIN_ERRORCLASS"), (void **)&zerror_class, sizeof(zval *), NULL);
				
				/*SPG(execution_depth) = 0;*/
				if (SPG(log_phpscript_is_safe)) {
					PG(safe_mode) = 0;
					PG(open_basedir) = NULL;
				}
				
				zend_execute(new_op_array TSRMLS_CC);
				
				/*SPG(execution_depth) = orig_execution_depth;*/
				PG(safe_mode) = orig_safe_mode;
				PG(open_basedir) = orig_basedir;
				
#ifdef ZEND_ENGINE_2
				destroy_op_array(new_op_array TSRMLS_CC);
#else
				destroy_op_array(new_op_array);
#endif
				efree(new_op_array);
#ifdef ZEND_ENGINE_2
				if (!EG(exception))
#endif			
				{
					if (EG(return_value_ptr_ptr)) {
						zval_ptr_dtor(EG(return_value_ptr_ptr));
						EG(return_value_ptr_ptr) = NULL;
					}
				}
			} else {
				php_security_log(S_INTERNAL, "Unable to execute logging PHP script: %s", SPG(log_phpscriptname));
				return;
			}
		} else {
			php_security_log(S_INTERNAL, "Unable to execute logging PHP script: %s", SPG(log_phpscriptname));
			return;
		}
	}

}


#endif

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: sw=4 ts=4 fdm=marker
 * vim<600: sw=4 ts=4
 */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>