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

    1: /*
    2:    +----------------------------------------------------------------------+
    3:    | Suhosin Patch for PHP                                                |
    4:    +----------------------------------------------------------------------+
    5:    | Copyright (c) 2004-2010 Stefan Esser                                 |
    6:    +----------------------------------------------------------------------+
    7:    | This source file is subject to version 2.02 of the PHP license,      |
    8:    | that is bundled with this package in the file LICENSE, and is        |
    9:    | available at through the world-wide-web at                           |
   10:    | http://www.php.net/license/2_02.txt.                                 |
   11:    | If you did not receive a copy of the PHP license and are unable to   |
   12:    | obtain it through the world-wide-web, please send a note to          |
   13:    | license@php.net so we can mail you a copy immediately.               |
   14:    +----------------------------------------------------------------------+
   15:    | Author: Stefan Esser <sesser@hardened-php.net>                       |
   16:    +----------------------------------------------------------------------+
   17:  */
   18: /* $Id: suhosin_patch.c,v 1.1.1.1 2012/02/21 23:48:05 misho Exp $ */
   19: 
   20: #include "php.h"
   21: 
   22: #include <stdio.h>
   23: #include <stdlib.h>
   24: #include <sys/mman.h>
   25: 
   26: #if HAVE_UNISTD_H
   27: #include <unistd.h>
   28: #endif
   29: #include "SAPI.h"
   30: #include "php_globals.h"
   31: 
   32: #if SUHOSIN_PATCH
   33: 
   34: #ifdef HAVE_SYS_SOCKET_H
   35: #include <sys/socket.h>
   36: #endif
   37: 
   38: #if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
   39: #undef AF_UNIX
   40: #endif
   41: 
   42: #if defined(AF_UNIX)
   43: #include <sys/un.h>
   44: #endif
   45: 
   46: #define SYSLOG_PATH  "/dev/log"
   47: 
   48: #ifdef PHP_WIN32
   49: static HANDLE log_source = 0;
   50: #endif
   51: 
   52: #include "snprintf.h"
   53: 
   54: #include "suhosin_patch.h"
   55: 
   56: #ifdef ZTS
   57: #include "suhosin_globals.h"
   58: int suhosin_patch_globals_id;
   59: #else
   60: struct _suhosin_patch_globals suhosin_patch_globals;
   61: #endif
   62: 
   63: static char *suhosin_config = NULL;
   64: 
   65: static zend_intptr_t SUHOSIN_POINTER_GUARD = 0;
   66: 
   67: static void php_security_log(int loglevel, char *fmt, ...);
   68: 
   69: static void suhosin_patch_globals_ctor(suhosin_patch_globals_struct *suhosin_patch_globals TSRMLS_DC)
   70: {
   71: 	memset(suhosin_patch_globals, 0, sizeof(*suhosin_patch_globals));
   72: }
   73: 
   74: ZEND_API char suhosin_get_config(int element)
   75: {
   76:         return ((char *)SUHOSIN_MANGLE_PTR(suhosin_config))[element];
   77: }
   78: 
   79: static void suhosin_set_config(int element, char value)
   80: {
   81:         ((char *)SUHOSIN_MANGLE_PTR(suhosin_config))[element] = value;
   82: }
   83: 
   84: static void suhosin_read_configuration_from_environment()
   85: {
   86:         char *tmp;
   87:         
   88:         /* check if canary protection should be activated or not */
   89:         tmp = getenv("SUHOSIN_MM_USE_CANARY_PROTECTION");
   90:         /* default to activated */
   91:         suhosin_set_config(SUHOSIN_MM_USE_CANARY_PROTECTION, 1);
   92:         if (tmp) {
   93:                 int flag = zend_atoi(tmp, 0);
   94:                 suhosin_set_config(SUHOSIN_MM_USE_CANARY_PROTECTION, flag);
   95:         }
   96:         
   97:         /* check if free memory should be overwritten with 0xFF or not */
   98:         tmp = getenv("SUHOSIN_MM_DESTROY_FREE_MEMORY");
   99:         /* default to deactivated */
  100:         suhosin_set_config(SUHOSIN_MM_DESTROY_FREE_MEMORY, 0);
  101:         if (tmp) {
  102:                 int flag = zend_atoi(tmp, 0);
  103:                 suhosin_set_config(SUHOSIN_MM_DESTROY_FREE_MEMORY, flag);
  104:         }
  105:         
  106:         /* check if canary violations should be ignored */
  107:         tmp = getenv("SUHOSIN_MM_IGNORE_CANARY_VIOLATION");
  108:         /* default to NOT ignore */
  109:         suhosin_set_config(SUHOSIN_MM_IGNORE_CANARY_VIOLATION, 0);
  110:         if (tmp) {
  111:                 int flag = zend_atoi(tmp, 0);
  112:                 suhosin_set_config(SUHOSIN_MM_IGNORE_CANARY_VIOLATION, flag);
  113:         }
  114: 
  115:         /* check if invalid hashtable destructors should be ignored */
  116:         tmp = getenv("SUHOSIN_HT_IGNORE_INVALID_DESTRUCTOR");
  117:         /* default to NOT ignore */
  118:         suhosin_set_config(SUHOSIN_HT_IGNORE_INVALID_DESTRUCTOR, 0);
  119:         if (tmp) {
  120:                 int flag = zend_atoi(tmp, 0);
  121:                 suhosin_set_config(SUHOSIN_HT_IGNORE_INVALID_DESTRUCTOR, flag);
  122:         }
  123: 
  124:         /* check if invalid linkedlist destructors should be ignored */
  125:         tmp = getenv("SUHOSIN_LL_IGNORE_INVALID_DESTRUCTOR");
  126:         /* default to NOT ignore */
  127:         suhosin_set_config(SUHOSIN_LL_IGNORE_INVALID_DESTRUCTOR, 0);
  128:         if (tmp) {
  129:                 int flag = zend_atoi(tmp, 0);
  130:                 suhosin_set_config(SUHOSIN_LL_IGNORE_INVALID_DESTRUCTOR, flag);
  131:         }
  132:         
  133:         suhosin_set_config(SUHOSIN_CONFIG_SET, 1);
  134: }
  135: 
  136: static void suhosin_write_protect_configuration()
  137: {
  138:         /* check return value of mprotect() to ensure memory is read only now */
  139:         if (mprotect(SUHOSIN_MANGLE_PTR(suhosin_config), sysconf(_SC_PAGESIZE), PROT_READ) != 0) {
  140:                 perror("suhosin");
  141:                 _exit(1);
  142:         }
  143: }
  144: 
  145: PHPAPI void suhosin_startup()
  146: {
  147: #ifdef ZTS
  148: 	ts_allocate_id(&suhosin_patch_globals_id, sizeof(suhosin_patch_globals_struct), (ts_allocate_ctor) suhosin_patch_globals_ctor, NULL);
  149: #else
  150: 	suhosin_patch_globals_ctor(&suhosin_patch_globals TSRMLS_CC);
  151: #endif
  152: 	zend_suhosin_log = php_security_log;
  153: 	
  154: 	/* get the pointer guardian and ensure low 3 bits are 1 */
  155:         if (SUHOSIN_POINTER_GUARD == 0) {
  156:                 zend_canary(&SUHOSIN_POINTER_GUARD, sizeof(SUHOSIN_POINTER_GUARD));
  157:                 SUHOSIN_POINTER_GUARD |= 7;
  158:         }
  159: 	
  160: 	if (!suhosin_config) {
  161: #ifndef MAP_ANONYMOUS
  162: #define MAP_ANONYMOUS MAP_ANON
  163: #endif
  164: 		suhosin_config = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
  165: 		if (suhosin_config == MAP_FAILED) {
  166: 			perror("suhosin");
  167: 			_exit(1);
  168: 		}
  169:                 suhosin_config = SUHOSIN_MANGLE_PTR(suhosin_config);
  170: 	}
  171: 	if (!SUHOSIN_CONFIG(SUHOSIN_CONFIG_SET)) {
  172:         suhosin_read_configuration_from_environment();
  173:         suhosin_write_protect_configuration();
  174:     }
  175: }
  176: 
  177: static char *loglevel2string(int loglevel)
  178: {
  179: 	switch (loglevel) {
  180: 	    case S_FILES:
  181: 		return "FILES";
  182: 	    case S_INCLUDE:
  183: 		return "INCLUDE";
  184: 	    case S_MEMORY:
  185: 		return "MEMORY";
  186: 	    case S_MISC:
  187: 		return "MISC";
  188: 		case S_SESSION:
  189: 		return "SESSION";
  190: 	    case S_SQL:
  191: 		return "SQL";
  192: 	    case S_EXECUTOR:
  193: 		return "EXECUTOR";
  194: 	    case S_VARS:
  195: 		return "VARS";
  196: 	    default:
  197: 		return "UNKNOWN";    
  198: 	}
  199: }
  200: 
  201: static void php_security_log(int loglevel, char *fmt, ...)
  202: {
  203: 	int s, r, i=0;
  204: #if defined(AF_UNIX)
  205: 	struct sockaddr_un saun;
  206: #endif
  207: #ifdef PHP_WIN32
  208: 	LPTSTR strs[2];
  209: 	unsigned short etype;
  210: 	DWORD evid;
  211: #endif
  212: 	char buf[4096+64];
  213: 	char error[4096+100];
  214: 	char *ip_address;
  215: 	char *fname;
  216: 	char *alertstring;
  217: 	int lineno;
  218: 	va_list ap;
  219: 	TSRMLS_FETCH();
  220: 
  221: 	/*SDEBUG("(suhosin_log) loglevel: %d log_syslog: %u - log_sapi: %u - log_script: %u", loglevel, SPG(log_syslog), SPG(log_sapi), SPG(log_script));*/
  222: 	
  223: 	if (SPG(log_use_x_forwarded_for)) {
  224: 		ip_address = sapi_getenv("HTTP_X_FORWARDED_FOR", 20 TSRMLS_CC);
  225: 		if (ip_address == NULL) {
  226: 			ip_address = "X-FORWARDED-FOR not set";
  227: 		}
  228: 	} else {
  229: 		ip_address = sapi_getenv("REMOTE_ADDR", 11 TSRMLS_CC);
  230: 		if (ip_address == NULL) {
  231: 			ip_address = "REMOTE_ADDR not set";
  232: 		}
  233: 	}
  234: 	
  235: 	
  236: 	va_start(ap, fmt);
  237: 	ap_php_vsnprintf(error, sizeof(error), fmt, ap);
  238: 	va_end(ap);
  239: 	while (error[i]) {
  240: 		if (error[i] < 32) error[i] = '.';
  241: 		i++;
  242: 	}
  243: 	
  244: /*	if (SPG(simulation)) {
  245: 		alertstring = "ALERT-SIMULATION";
  246: 	} else { */
  247: 		alertstring = "ALERT";
  248: /*	}*/
  249: 	
  250: 	if (zend_is_executing(TSRMLS_C)) {
  251: 		if (EG(current_execute_data)) {
  252: 			lineno = EG(current_execute_data)->opline->lineno;
  253: 			fname = EG(current_execute_data)->op_array->filename;
  254: 		} else {
  255: 			lineno = zend_get_executed_lineno(TSRMLS_C);
  256: 			fname = zend_get_executed_filename(TSRMLS_C);
  257: 		}
  258: 		ap_php_snprintf(buf, sizeof(buf), "%s - %s (attacker '%s', file '%s', line %u)", alertstring, error, ip_address, fname, lineno);
  259: 	} else {
  260: 		fname = sapi_getenv("SCRIPT_FILENAME", 15 TSRMLS_CC);
  261: 		if (fname==NULL) {
  262: 			fname = "unknown";
  263: 		}
  264: 		ap_php_snprintf(buf, sizeof(buf), "%s - %s (attacker '%s', file '%s')", alertstring, error, ip_address, fname);
  265: 	}
  266: 			
  267: 	/* Syslog-Logging disabled? */
  268: 	if (((SPG(log_syslog)|S_INTERNAL) & loglevel)==0) {
  269: 		goto log_sapi;
  270: 	}	
  271: 	
  272: #if defined(AF_UNIX)
  273: 	ap_php_snprintf(error, sizeof(error), "<%u>suhosin[%u]: %s\n", (unsigned int)(SPG(log_syslog_facility)|SPG(log_syslog_priority)),getpid(),buf);
  274: 
  275: 	s = socket(AF_UNIX, SOCK_DGRAM, 0);
  276: 	if (s == -1) {
  277: 		goto log_sapi;
  278: 	}
  279: 	
  280: 	memset(&saun, 0, sizeof(saun));
  281: 	saun.sun_family = AF_UNIX;
  282: 	strcpy(saun.sun_path, SYSLOG_PATH);
  283: 	/*saun.sun_len = sizeof(saun);*/
  284: 	
  285: 	r = connect(s, (struct sockaddr *)&saun, sizeof(saun));
  286: 	if (r) {
  287: 		close(s);
  288:     		s = socket(AF_UNIX, SOCK_STREAM, 0);
  289: 		if (s == -1) {
  290: 			goto log_sapi;
  291: 		}
  292: 	
  293: 		memset(&saun, 0, sizeof(saun));
  294: 		saun.sun_family = AF_UNIX;
  295: 		strcpy(saun.sun_path, SYSLOG_PATH);
  296: 		/*saun.sun_len = sizeof(saun);*/
  297: 
  298: 		r = connect(s, (struct sockaddr *)&saun, sizeof(saun));
  299: 		if (r) { 
  300: 			close(s);
  301: 			goto log_sapi;
  302: 		}
  303: 	}
  304: 	send(s, error, strlen(error), 0);
  305: 	
  306: 	close(s);
  307: #endif
  308: #ifdef PHP_WIN32
  309: 	ap_php_snprintf(error, sizeof(error), "suhosin[%u]: %s", getpid(),buf);
  310: 
  311: 	switch (SPG(log_syslog_priority)) {			/* translate UNIX type into NT type */
  312: 		case 1: /*LOG_ALERT:*/
  313: 			etype = EVENTLOG_ERROR_TYPE;
  314: 			break;
  315: 		case 6: /*LOG_INFO:*/
  316: 			etype = EVENTLOG_INFORMATION_TYPE;
  317: 			break;
  318: 		default:
  319: 			etype = EVENTLOG_WARNING_TYPE;
  320: 	}
  321: 	evid = loglevel;
  322: 	strs[0] = error;
  323: 	/* report the event */
  324: 	if (log_source == NULL) {
  325: 		log_source = RegisterEventSource(NULL, "Suhosin-Patch-" SUHOSIN_PATCH_VERSION);
  326: 	}
  327: 	ReportEvent(log_source, etype, (unsigned short) SPG(log_syslog_priority), evid, NULL, 1, 0, strs, NULL);
  328: 	
  329: #endif
  330: log_sapi:
  331: 	/* SAPI Logging activated? */
  332: 	/*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));*/
  333: 	if (((SPG(log_sapi)|S_INTERNAL) & loglevel)!=0) {
  334: 		sapi_module.log_message(buf);
  335: 	}
  336: 
  337: /*log_script:*/
  338: 	/* script logging activaed? */
  339: 	if (((SPG(log_script) & loglevel)!=0) && SPG(log_scriptname)!=NULL) {
  340: 		char cmd[8192], *cmdpos, *bufpos;
  341: 		FILE *in;
  342: 		int space;
  343: 		
  344: 		ap_php_snprintf(cmd, sizeof(cmd), "%s %s \'", SPG(log_scriptname), loglevel2string(loglevel));
  345: 		space = sizeof(cmd) - strlen(cmd);
  346: 		cmdpos = cmd + strlen(cmd);
  347: 		bufpos = buf;
  348: 		if (space <= 1) return;
  349: 		while (space > 2 && *bufpos) {
  350: 			if (*bufpos == '\'') {
  351: 				if (space<=5) break;
  352: 				*cmdpos++ = '\'';
  353: 				*cmdpos++ = '\\';
  354: 				*cmdpos++ = '\'';
  355: 				*cmdpos++ = '\'';
  356: 				bufpos++;
  357: 				space-=4;
  358: 			} else {
  359: 				*cmdpos++ = *bufpos++;
  360: 				space--;
  361: 			}
  362: 		}
  363: 		*cmdpos++ = '\'';
  364: 		*cmdpos = 0;
  365: 		
  366: 		if ((in=VCWD_POPEN(cmd, "r"))==NULL) {
  367: 			php_security_log(S_INTERNAL, "Unable to execute logging shell script: %s", SPG(log_scriptname));
  368: 			return;
  369: 		}
  370: 		/* read and forget the result */
  371: 		while (1) {
  372: 			int readbytes = fread(cmd, 1, sizeof(cmd), in);
  373: 			if (readbytes<=0) {
  374: 				break;
  375: 			}
  376: 		}
  377: 		pclose(in);
  378: 	}
  379: /*log_phpscript:*/
  380: 	if ((SPG(log_phpscript) & loglevel)!=0 && EG(in_execution) && SPG(log_phpscriptname) && SPG(log_phpscriptname)[0]) {
  381: 		zend_file_handle file_handle;
  382: 		zend_op_array *new_op_array;
  383: 		zval *result = NULL;
  384: 		
  385: 		/*long orig_execution_depth = SPG(execution_depth);*/
  386: 		zend_bool orig_safe_mode = PG(safe_mode);
  387: 		char *orig_basedir = PG(open_basedir);
  388: 		
  389: 		char *phpscript = SPG(log_phpscriptname);
  390: /*SDEBUG("scriptname %s", SPG(log_phpscriptname));`*/
  391: #ifdef ZEND_ENGINE_2
  392: 		if (zend_stream_open(phpscript, &file_handle TSRMLS_CC) == SUCCESS) {
  393: #else
  394: 		if (zend_open(phpscript, &file_handle) == SUCCESS && ZEND_IS_VALID_FILE_HANDLE(&file_handle)) {
  395: 			file_handle.filename = phpscript;
  396: 			file_handle.free_filename = 0;
  397: #endif		
  398: 			if (!file_handle.opened_path) {
  399: 				file_handle.opened_path = estrndup(phpscript, strlen(phpscript));
  400: 			}
  401: 			new_op_array = zend_compile_file(&file_handle, ZEND_REQUIRE TSRMLS_CC);
  402: 			zend_destroy_file_handle(&file_handle TSRMLS_CC);
  403: 			if (new_op_array) {
  404: 				HashTable *active_symbol_table = EG(active_symbol_table);
  405: 				zval *zerror, *zerror_class;
  406: 				
  407: 				if (active_symbol_table == NULL) {
  408: 					active_symbol_table = &EG(symbol_table);
  409: 				}
  410: 				EG(return_value_ptr_ptr) = &result;
  411: 				EG(active_op_array) = new_op_array;
  412: 				
  413: 				MAKE_STD_ZVAL(zerror);
  414: 				MAKE_STD_ZVAL(zerror_class);
  415: 				ZVAL_STRING(zerror, buf, 1);
  416: 				ZVAL_LONG(zerror_class, loglevel);
  417: 
  418: 				zend_hash_update(active_symbol_table, "SUHOSIN_ERROR", sizeof("SUHOSIN_ERROR"), (void **)&zerror, sizeof(zval *), NULL);
  419: 				zend_hash_update(active_symbol_table, "SUHOSIN_ERRORCLASS", sizeof("SUHOSIN_ERRORCLASS"), (void **)&zerror_class, sizeof(zval *), NULL);
  420: 				
  421: 				/*SPG(execution_depth) = 0;*/
  422: 				if (SPG(log_phpscript_is_safe)) {
  423: 					PG(safe_mode) = 0;
  424: 					PG(open_basedir) = NULL;
  425: 				}
  426: 				
  427: 				zend_execute(new_op_array TSRMLS_CC);
  428: 				
  429: 				/*SPG(execution_depth) = orig_execution_depth;*/
  430: 				PG(safe_mode) = orig_safe_mode;
  431: 				PG(open_basedir) = orig_basedir;
  432: 				
  433: #ifdef ZEND_ENGINE_2
  434: 				destroy_op_array(new_op_array TSRMLS_CC);
  435: #else
  436: 				destroy_op_array(new_op_array);
  437: #endif
  438: 				efree(new_op_array);
  439: #ifdef ZEND_ENGINE_2
  440: 				if (!EG(exception))
  441: #endif			
  442: 				{
  443: 					if (EG(return_value_ptr_ptr)) {
  444: 						zval_ptr_dtor(EG(return_value_ptr_ptr));
  445: 						EG(return_value_ptr_ptr) = NULL;
  446: 					}
  447: 				}
  448: 			} else {
  449: 				php_security_log(S_INTERNAL, "Unable to execute logging PHP script: %s", SPG(log_phpscriptname));
  450: 				return;
  451: 			}
  452: 		} else {
  453: 			php_security_log(S_INTERNAL, "Unable to execute logging PHP script: %s", SPG(log_phpscriptname));
  454: 			return;
  455: 		}
  456: 	}
  457: 
  458: }
  459: 
  460: 
  461: #endif
  462: 
  463: /*
  464:  * Local variables:
  465:  * tab-width: 4
  466:  * c-basic-offset: 4
  467:  * End:
  468:  * vim600: sw=4 ts=4 fdm=marker
  469:  * vim<600: sw=4 ts=4
  470:  */

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