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>