php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login

Patch fpm-extensions.v1.patch for FPM related Bug #55181

Patch version 2011-07-11 12:36 UTC

Return to Bug #55181 | Download this patch
This patch is obsolete

Obsoleted by patches:

Patch Revisions:

Developer: fat@php.net

Index: sapi/fpm/php-fpm.conf.in
===================================================================
--- sapi/fpm/php-fpm.conf.in	(revision 313131)
+++ sapi/fpm/php-fpm.conf.in	(working copy)
@@ -421,6 +421,12 @@
 ; process time (several ms).
 ; Default Value: no
 ;catch_workers_output = yes
+
+; Limits the extensions FPM will allow to parse. This can prevent configuration
+; mistakes on the web server side. You should only limit FPM to .php extensions
+; to prevent malicious users to use other extensions to exectute php code.
+; Default Value: no
+;security.limit_extensions = .php
  
 ; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
 ; the current environment.
Index: sapi/fpm/fpm/fpm_conf.c
===================================================================
--- sapi/fpm/fpm/fpm_conf.c	(revision 313131)
+++ sapi/fpm/fpm/fpm_conf.c	(working copy)
@@ -121,6 +121,7 @@
 	{ "ping.response",             &fpm_conf_set_string,      WPO(ping_response) },
 	{ "access.log",                &fpm_conf_set_string,      WPO(access_log) },
 	{ "access.format",             &fpm_conf_set_string,      WPO(access_format) },
+	{ "security.limit_extensions", &fpm_conf_set_string,      WPO(security_limit_extensions) },
 	{ 0, 0, 0 }
 };
 
@@ -599,6 +600,15 @@
 	free(wpc->prefix);
 	free(wpc->access_log);
 	free(wpc->access_format);
+	free(wpc->security_limit_extensions);
+	if (wpc->security_limit_extensions_array) {
+		char **p = wpc->security_limit_extensions_array;
+		while (p && *p) {
+			free(*p);
+			p++;
+		}
+		free(wpc->security_limit_extensions_array);
+	}
 
 	return 0;
 }
@@ -845,6 +855,53 @@
 			}
 		}
 
+		if (wp->config->security_limit_extensions && *wp->config->security_limit_extensions) {
+			int nb_ext;
+			char *ext;
+			char *security_limit_extensions;
+			char *limit_extensions;
+
+
+			/* strdup because strtok(3) alters the string it parses */
+			security_limit_extensions = strdup(wp->config->security_limit_extensions);
+			limit_extensions = security_limit_extensions;
+			nb_ext = 0;
+
+			/* find the number of extensions */
+			while ((ext = strtok(limit_extensions, " \t"))) {
+				limit_extensions = NULL;
+				nb_ext++;
+			}
+			free(security_limit_extensions);
+
+			/* if something found */
+			if (nb_ext > 0) {
+
+				/* malloc the extension array */
+				wp->config->security_limit_extensions_array = malloc(sizeof(char *) * (nb_ext + 1));
+				if (!wp->config->security_limit_extensions_array) {
+					zlog(ZLOG_ERROR, "[pool %s] unable to malloc extensions array", wp->config->name);
+					return -1;
+				}
+
+				/* strdup because strtok(3) alters the string it parses */
+				security_limit_extensions = strdup(wp->config->security_limit_extensions);
+				limit_extensions = security_limit_extensions;
+				nb_ext = 0;
+
+				/* parse the string and save the extension in the array */
+				while ((ext = strtok(security_limit_extensions, " \t"))) {
+					security_limit_extensions = NULL;
+					wp->config->security_limit_extensions_array[nb_ext] = strdup(ext);
+					nb_ext++;
+				}
+
+				/* end the array with NULL in order to parse it */
+				wp->config->security_limit_extensions_array[nb_ext] = NULL;
+				free(security_limit_extensions);
+			}
+		}
+
 		if (wp->config->chroot && *wp->config->chroot) {
 
 			fpm_evaluate_full_path(&wp->config->chroot, wp, NULL, 1);
@@ -1380,6 +1437,7 @@
 		zlog(ZLOG_NOTICE, "\tslowlog = %s",                    STR2STR(wp->config->slowlog));
 		zlog(ZLOG_NOTICE, "\trlimit_files = %d",               wp->config->rlimit_files);
 		zlog(ZLOG_NOTICE, "\trlimit_core = %d",                wp->config->rlimit_core);
+		zlog(ZLOG_NOTICE, "\tsecurity.limit_extensions = %s",  wp->config->security_limit_extensions);
 
 		for (kv = wp->config->env; kv; kv = kv->next) {
 			zlog(ZLOG_NOTICE, "\tenv[%s] = %s", kv->key, kv->value);
Index: sapi/fpm/fpm/fpm_php.c
===================================================================
--- sapi/fpm/fpm/fpm_php.c	(revision 313131)
+++ sapi/fpm/fpm/fpm_php.c	(working copy)
@@ -19,7 +19,10 @@
 #include "fpm_php.h"
 #include "fpm_cleanup.h"
 #include "fpm_worker_pool.h"
+#include "zlog.h"
 
+static char **limit_extensions = NULL;
+
 static int fpm_php_zend_ini_alter_master(char *name, int name_length, char *new_value, int new_value_length, int mode, int stage TSRMLS_DC) /* {{{ */
 {
 	zend_ini_entry *ini_entry;
@@ -219,7 +222,69 @@
 		0 > fpm_php_set_allowed_clients(wp)) {
 		return -1;
 	}
+
+	if (wp->config->security_limit_extensions_array) {
+		int nb;
+		char **p;
+		
+		/* count array eelement */
+		p = wp->config->security_limit_extensions_array;
+		nb = 0;
+		while (p && *p) {
+			nb++;
+			p++;
+		}
+
+		/* if found */
+		if (nb > 0) {
+
+			/* malloc the array */
+			limit_extensions = malloc(sizeof(char *) * (nb + 1));
+			if (!limit_extensions) {
+				zlog(ZLOG_ERROR, "Unable to malloc limit_extensions");
+				return -1;
+			}
+
+			/* fill the array */
+			p = wp->config->security_limit_extensions_array;
+			nb = 0;
+			while (p && *p) {
+				limit_extensions[nb] = strdup(*p);
+				nb++;
+				p++;
+			}
+
+			/* NULL terminated array */
+			limit_extensions[nb] = NULL;
+		}
+	}
 	return 0;
 }
 /* }}} */
 
+int fpm_php_limit_extensions(char *path) /* {{{ */
+{
+	char **p;
+	size_t path_len;
+
+	if (!path || !limit_extensions) {
+		return 0; /* allowed by default */
+	}
+
+	p = limit_extensions;
+	path_len = strlen(path);
+	while (p && *p) {
+		size_t ext_len = strlen(*p);
+		if (path_len > ext_len) {
+			char *path_ext = path + path_len - ext_len;
+			if (strcmp(*p, path_ext) == 0) {
+				return 0; /* allow as the extension has been found */
+			}
+		}
+		p++;
+	}
+
+
+	return 1; /* extension not found: not allowed  */
+}
+/* }}} */
Index: sapi/fpm/fpm/fpm_conf.h
===================================================================
--- sapi/fpm/fpm/fpm_conf.h	(revision 313131)
+++ sapi/fpm/fpm/fpm_conf.h	(working copy)
@@ -66,6 +66,8 @@
 	char *listen_group;
 	char *listen_mode;
 	char *listen_allowed_clients;
+	char *security_limit_extensions;
+	char **security_limit_extensions_array;
 	struct key_value_s *env;
 	struct key_value_s *php_admin_values;
 	struct key_value_s *php_values;
Index: sapi/fpm/fpm/fpm_php.h
===================================================================
--- sapi/fpm/fpm/fpm_php.h	(revision 313131)
+++ sapi/fpm/fpm/fpm_php.h	(working copy)
@@ -43,6 +43,7 @@
 void fpm_php_soft_quit();
 int fpm_php_init_main();
 int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode);
+int fpm_php_limit_extensions(char *path);
 
 #endif
 
Index: sapi/fpm/fpm/fpm_main.c
===================================================================
--- sapi/fpm/fpm/fpm_main.c	(revision 313131)
+++ sapi/fpm/fpm/fpm_main.c	(working copy)
@@ -1879,6 +1879,12 @@
 				goto fastcgi_request_done;
 			}
 
+			if (fpm_php_limit_extensions(SG(request_info).path_translated)) {
+				SG(sapi_headers).http_response_code = 403;
+				PUTS("Access denied.\n");
+				goto fastcgi_request_done;
+			}
+
 			/* path_translated exists, we can continue ! */
 			if (php_fopen_primary_script(&file_handle TSRMLS_CC) == FAILURE) {
 				zend_try {
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Wed Apr 23 09:02:23 2014 UTC