]> granicus.if.org Git - php/commitdiff
- Implemented FR #55181 (Enhance security by limiting access to user defined extensions)
authorJérôme Loyet <fat@php.net>
Tue, 12 Jul 2011 23:00:42 +0000 (23:00 +0000)
committerJérôme Loyet <fat@php.net>
Tue, 12 Jul 2011 23:00:42 +0000 (23:00 +0000)
NEWS
sapi/fpm/fpm/fpm_conf.c
sapi/fpm/fpm/fpm_conf.h
sapi/fpm/fpm/fpm_main.c
sapi/fpm/fpm/fpm_php.c
sapi/fpm/fpm/fpm_php.h
sapi/fpm/fpm/fpm_worker_pool.h
sapi/fpm/php-fpm.conf.in

diff --git a/NEWS b/NEWS
index 4cdc72560346494c2237a25d38faed9b44136a27..2fd5ee16345fce5de028e6f5ed83e17ed739ffd3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,8 @@
 - Improved PHP-FPM SAPI:
   . Added partial syslog support (on error_log only). FR #52052. (fat)
   . Lowered default value for Process Manager. FR #54098. (fat)
+  . Enhance security by limiting access to user defined extensions.
+    FR #55181. (fat)
 
 - Improved core functions:
   . Changed http_response_code() to be able to set a response code. (Kalle)
index a7ad4d255116906ac7030ce27dd67e49e3fc892c..2f28fb946566f1868b304a34439470394afad366 100644 (file)
@@ -121,6 +121,7 @@ static struct ini_value_parser_s ini_fpm_pool_options[] = {
        { "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,7 @@ int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc) /* {{{ */
        free(wpc->prefix);
        free(wpc->access_log);
        free(wpc->access_format);
+       free(wpc->security_limit_extensions);
 
        return 0;
 }
@@ -845,6 +847,56 @@ static int fpm_conf_process_all_pools() /* {{{ */
                        }
                }
 
+               if (!wp->config->security_limit_extensions) {
+                       wp->config->security_limit_extensions = strdup(".php");
+               }
+
+               if (*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->limit_extensions = malloc(sizeof(char *) * (nb_ext + 1));
+                               if (!wp->limit_extensions) {
+                                       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->limit_extensions[nb_ext++] = strdup(ext);
+                               }
+
+                               /* end the array with NULL in order to parse it */
+                               wp->limit_extensions[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 +1432,7 @@ static void fpm_conf_dump() /* {{{ */
                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 87a5cd332b6f3b0de24f1f8b26cbf2efcbbf31a8..d19c2b61f5dca6d4fcc3eefc5330077fea2866d5 100644 (file)
@@ -66,6 +66,7 @@ struct fpm_worker_pool_config_s {
        char *listen_group;
        char *listen_mode;
        char *listen_allowed_clients;
+       char *security_limit_extensions;
        struct key_value_s *env;
        struct key_value_s *php_admin_values;
        struct key_value_s *php_values;
index 7dbd6c00c9a510ec4ec2dc69a59003edf6347bf2..06370d855ce0470768807e10d24f6aa26443e2c4 100644 (file)
@@ -1879,6 +1879,12 @@ consult the installation file that came with this distribution, or visit \n\
                                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 {
index 33b9e410e1fff71d1b374e7b593a6e69f7d893c9..f320f5148e6be631226e6bfbce935d8df9496d32 100644 (file)
@@ -19,6 +19,9 @@
 #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) /* {{{ */
 {
@@ -219,7 +222,38 @@ int fpm_php_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
                0 > fpm_php_set_allowed_clients(wp)) {
                return -1;
        }
+
+       if (wp->limit_extensions) {
+               limit_extensions = wp->limit_extensions;
+       }
        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++;
+       }
+
+
+       zlog(ZLOG_NOTICE, "Access to the file '%s' has been denied (see security.limit_extensions)", path);
+       return 1; /* extension not found: not allowed  */
+}
+/* }}} */
index 62a47e7fd37b3698eb520c2239062fcf6c86efe0..a2c7ed318a092bf208068ec23c9474a33627103e 100644 (file)
@@ -43,6 +43,7 @@ size_t fpm_php_content_length(TSRMLS_D);
 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 098def5b7e2764e2b1bc636359560e70cc95010d..100c3689cb6ed86b9dcfa26bbd6e850350be7a29 100644 (file)
@@ -37,6 +37,7 @@ struct fpm_worker_pool_s {
 #endif
        struct fpm_scoreboard_s *scoreboard;
        int log_fd;
+       char **limit_extensions;
 };
 
 struct fpm_worker_pool_s *fpm_worker_pool_alloc();
index b3151c0a9995063f7b1f133ff7282d3b1cc38735..8c9d4bb406635e9bd6b032bb987989612e6ff899 100644 (file)
@@ -421,6 +421,14 @@ pm.max_spare_servers = 3
 ; process time (several ms).
 ; Default Value: no
 ;catch_workers_output = yes
+
+; Limits the extensions of the main script 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.
+; Note: set an empty value to allow all extensions.
+; Default Value: .php
+;security.limit_extensions = .php .php3 .php4 .php5
  
 ; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
 ; the current environment.