xref: /php-src/sapi/fpm/fpm/fpm_php.c (revision a8c6c616)
1 	/* (c) 2007,2008 Andrei Nigmatulin */
2 
3 #include "fpm_config.h"
4 
5 #include <stdlib.h>
6 #include <string.h>
7 #include <stdio.h>
8 
9 #include "php.h"
10 #include "php_main.h"
11 #include "php_ini.h"
12 #include "ext/standard/dl.h"
13 
14 #include "fastcgi.h"
15 
16 #include "fpm.h"
17 #include "fpm_php.h"
18 #include "fpm_cleanup.h"
19 #include "fpm_worker_pool.h"
20 #include "zlog.h"
21 
22 static char **limit_extensions = NULL;
23 
fpm_php_zend_ini_alter_master(char * name,int name_length,char * new_value,int new_value_length,int mode,int stage)24 static int fpm_php_zend_ini_alter_master(char *name, int name_length, char *new_value, int new_value_length, int mode, int stage) /* {{{ */
25 {
26 	zend_ini_entry *ini_entry;
27 	zend_string *duplicate;
28 
29 	if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length)) == NULL) {
30 		return FAILURE;
31 	}
32 
33 	duplicate = zend_string_init(new_value, new_value_length, 1);
34 
35 	if (!ini_entry->on_modify
36 			|| ini_entry->on_modify(ini_entry, duplicate,
37 				ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
38 		ini_entry->value = duplicate;
39 		/* when mode == ZEND_INI_USER keep unchanged to allow ZEND_INI_PERDIR (.user.ini) */
40 		if (mode == ZEND_INI_SYSTEM) {
41 			ini_entry->modifiable = mode;
42 		}
43 	} else {
44 		zend_string_release_ex(duplicate, 1);
45 	}
46 
47 	return SUCCESS;
48 }
49 /* }}} */
50 
fpm_php_disable(char * value,int (* zend_disable)(const char *,size_t))51 static void fpm_php_disable(char *value, int (*zend_disable)(const char *, size_t)) /* {{{ */
52 {
53 	char *s = 0, *e = value;
54 
55 	while (*e) {
56 		switch (*e) {
57 			case ' ':
58 			case ',':
59 				if (s) {
60 					*e = '\0';
61 					zend_disable(s, e - s);
62 					s = 0;
63 				}
64 				break;
65 			default:
66 				if (!s) {
67 					s = e;
68 				}
69 				break;
70 		}
71 		e++;
72 	}
73 
74 	if (s) {
75 		zend_disable(s, e - s);
76 	}
77 }
78 /* }}} */
79 
80 #define FPM_PHP_INI_ALTERING_ERROR   -1
81 #define FPM_PHP_INI_APPLIED          1
82 #define FPM_PHP_INI_EXTENSION_FAILED 0
83 #define FPM_PHP_INI_EXTENSION_LOADED 2
84 
fpm_php_apply_defines_ex(struct key_value_s * kv,int mode)85 int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode) /* {{{ */
86 {
87 
88 	char *name = kv->key;
89 	char *value = kv->value;
90 	int name_len = strlen(name);
91 	int value_len = strlen(value);
92 
93 	if (!strcmp(name, "extension") && *value) {
94 		zval zv;
95 		zend_interned_strings_switch_storage(0);
96 		php_dl(value, MODULE_PERSISTENT, &zv, 1);
97 		zend_interned_strings_switch_storage(1);
98 		return Z_TYPE(zv) == IS_TRUE ? FPM_PHP_INI_EXTENSION_LOADED : FPM_PHP_INI_EXTENSION_FAILED;
99 	}
100 
101 	if (fpm_php_zend_ini_alter_master(name, name_len, value, value_len, mode, PHP_INI_STAGE_ACTIVATE) == FAILURE) {
102 		return FPM_PHP_INI_ALTERING_ERROR;
103 	}
104 
105 	if (!strcmp(name, "disable_functions") && *value) {
106 		zend_disable_functions(value);
107 		return FPM_PHP_INI_APPLIED;
108 	}
109 
110 	if (!strcmp(name, "disable_classes") && *value) {
111 		char *v = strdup(value);
112 		PG(disable_classes) = v;
113 		fpm_php_disable(v, zend_disable_class);
114 		return FPM_PHP_INI_APPLIED;
115 	}
116 
117 	return FPM_PHP_INI_APPLIED;
118 }
119 /* }}} */
120 
fpm_php_apply_defines(struct fpm_worker_pool_s * wp)121 static int fpm_php_apply_defines(struct fpm_worker_pool_s *wp) /* {{{ */
122 {
123 	struct key_value_s *kv;
124 	int apply_result;
125 	bool extension_loaded = false;
126 
127 	for (kv = wp->config->php_values; kv; kv = kv->next) {
128 		apply_result = fpm_php_apply_defines_ex(kv, ZEND_INI_USER);
129 		if (apply_result == FPM_PHP_INI_ALTERING_ERROR) {
130 			zlog(ZLOG_ERROR, "Unable to set php_value '%s'", kv->key);
131 		} else if (apply_result == FPM_PHP_INI_EXTENSION_LOADED) {
132 			extension_loaded = true;
133 		}
134 	}
135 
136 	for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
137 		apply_result = fpm_php_apply_defines_ex(kv, ZEND_INI_SYSTEM);
138 		if (apply_result == FPM_PHP_INI_ALTERING_ERROR) {
139 			zlog(ZLOG_ERROR, "Unable to set php_admin_value '%s'", kv->key);
140 		} else if (apply_result == FPM_PHP_INI_EXTENSION_LOADED) {
141 			extension_loaded = true;
142 		}
143 	}
144 
145 	if (extension_loaded) {
146 		zend_collect_module_handlers();
147 	}
148 
149 	return 0;
150 }
151 /* }}} */
152 
fpm_php_set_allowed_clients(struct fpm_worker_pool_s * wp)153 static int fpm_php_set_allowed_clients(struct fpm_worker_pool_s *wp) /* {{{ */
154 {
155 	if (wp->listen_address_domain == FPM_AF_INET) {
156 		fcgi_set_allowed_clients(wp->config->listen_allowed_clients);
157 	}
158 	return 0;
159 }
160 /* }}} */
161 
162 #if 0 /* Comment out this non used function. It could be used later. */
163 static int fpm_php_set_fcgi_mgmt_vars(struct fpm_worker_pool_s *wp) /* {{{ */
164 {
165 	char max_workers[10 + 1]; /* 4294967295 */
166 	int len;
167 
168 	len = sprintf(max_workers, "%u", (unsigned int) wp->config->pm_max_children);
169 
170 	fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, max_workers, len);
171 	fcgi_set_mgmt_var("FCGI_MAX_REQS",  sizeof("FCGI_MAX_REQS")-1,  max_workers, len);
172 	return 0;
173 }
174 /* }}} */
175 #endif
176 
fpm_php_script_filename(void)177 char *fpm_php_script_filename(void)
178 {
179 	return SG(request_info).path_translated;
180 }
181 
fpm_php_request_uri(void)182 char *fpm_php_request_uri(void)
183 {
184 	return (char *) SG(request_info).request_uri;
185 }
186 
fpm_php_request_method(void)187 char *fpm_php_request_method(void)
188 {
189 	return (char *) SG(request_info).request_method;
190 }
191 
fpm_php_query_string(void)192 char *fpm_php_query_string(void)
193 {
194 	return SG(request_info).query_string;
195 }
196 
fpm_php_auth_user(void)197 char *fpm_php_auth_user(void)
198 {
199 	return SG(request_info).auth_user;
200 }
201 
fpm_php_content_length(void)202 size_t fpm_php_content_length(void)
203 {
204 	return SG(request_info).content_length;
205 }
206 
fpm_php_cleanup(int which,void * arg)207 static void fpm_php_cleanup(int which, void *arg) /* {{{ */
208 {
209 	php_module_shutdown();
210 	sapi_shutdown();
211 	if (limit_extensions) {
212 		fpm_worker_pool_free_limit_extensions(limit_extensions);
213 	}
214 }
215 /* }}} */
216 
fpm_php_soft_quit(void)217 void fpm_php_soft_quit(void)
218 {
219 	fcgi_terminate();
220 }
221 
fpm_php_init_main(void)222 int fpm_php_init_main(void)
223 {
224 	if (0 > fpm_cleanup_add(FPM_CLEANUP_PARENT, fpm_php_cleanup, 0)) {
225 		return -1;
226 	}
227 	return 0;
228 }
229 
fpm_php_init_child(struct fpm_worker_pool_s * wp)230 int fpm_php_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
231 {
232 	if (0 > fpm_php_apply_defines(wp) ||
233 		0 > fpm_php_set_allowed_clients(wp)) {
234 		return -1;
235 	}
236 
237 	if (wp->limit_extensions) {
238 		/* Take ownership of limit_extensions. */
239 		limit_extensions = wp->limit_extensions;
240 		wp->limit_extensions = NULL;
241 	}
242 	return 0;
243 }
244 /* }}} */
245 
fpm_php_limit_extensions(char * path)246 int fpm_php_limit_extensions(char *path) /* {{{ */
247 {
248 	char **p;
249 	size_t path_len;
250 
251 	if (!path || !limit_extensions) {
252 		return 0; /* allowed by default */
253 	}
254 
255 	p = limit_extensions;
256 	path_len = strlen(path);
257 	while (p && *p) {
258 		size_t ext_len = strlen(*p);
259 		if (path_len > ext_len) {
260 			char *path_ext = path + path_len - ext_len;
261 			if (strcmp(*p, path_ext) == 0) {
262 				return 0; /* allow as the extension has been found */
263 			}
264 		}
265 		p++;
266 	}
267 
268 
269 	zlog(ZLOG_NOTICE, "Access to the script '%s' has been denied (see security.limit_extensions)", path);
270 	return 1; /* extension not found: not allowed  */
271 }
272 /* }}} */
273 
fpm_php_is_key_in_table(zend_string * table,const char * key,size_t key_len)274 bool fpm_php_is_key_in_table(zend_string *table, const char *key, size_t key_len) /* {{{ */
275 {
276 	zval *data;
277 	zend_string *str;
278 
279 	ZEND_ASSERT(table);
280 	ZEND_ASSERT(key);
281 
282 	/* inspired from ext/standard/info.c */
283 
284 	zend_is_auto_global(table);
285 
286 	/* find the table and ensure it's an array */
287 	data = zend_hash_find(&EG(symbol_table), table);
288 	if (!data || Z_TYPE_P(data) != IS_ARRAY) {
289 		return NULL;
290 	}
291 
292 	ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(data), str) {
293 		if (str && zend_string_equals_cstr(str, key, key_len)) {
294 			return true;
295 		}
296 	} ZEND_HASH_FOREACH_END();
297 
298 	return false;
299 }
300 /* }}} */
301