xref: /PHP-8.0/sapi/fpm/fpm/fpm_php.c (revision e2a5428c)
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 
fpm_php_apply_defines_ex(struct key_value_s * kv,int mode)80 int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode) /* {{{ */
81 {
82 
83 	char *name = kv->key;
84 	char *value = kv->value;
85 	int name_len = strlen(name);
86 	int value_len = strlen(value);
87 
88 	if (!strcmp(name, "extension") && *value) {
89 		zval zv;
90 		php_dl(value, MODULE_PERSISTENT, &zv, 1);
91 		return Z_TYPE(zv) == IS_TRUE;
92 	}
93 
94 	if (fpm_php_zend_ini_alter_master(name, name_len, value, value_len, mode, PHP_INI_STAGE_ACTIVATE) == FAILURE) {
95 		return -1;
96 	}
97 
98 	if (!strcmp(name, "disable_functions") && *value) {
99 		zend_disable_functions(value);
100 		return 1;
101 	}
102 
103 	if (!strcmp(name, "disable_classes") && *value) {
104 		char *v = strdup(value);
105 		PG(disable_classes) = v;
106 		fpm_php_disable(v, zend_disable_class);
107 		return 1;
108 	}
109 
110 	return 1;
111 }
112 /* }}} */
113 
fpm_php_apply_defines(struct fpm_worker_pool_s * wp)114 static int fpm_php_apply_defines(struct fpm_worker_pool_s *wp) /* {{{ */
115 {
116 	struct key_value_s *kv;
117 
118 	for (kv = wp->config->php_values; kv; kv = kv->next) {
119 		if (fpm_php_apply_defines_ex(kv, ZEND_INI_USER) == -1) {
120 			zlog(ZLOG_ERROR, "Unable to set php_value '%s'", kv->key);
121 		}
122 	}
123 
124 	for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
125 		if (fpm_php_apply_defines_ex(kv, ZEND_INI_SYSTEM) == -1) {
126 			zlog(ZLOG_ERROR, "Unable to set php_admin_value '%s'", kv->key);
127 		}
128 	}
129 
130 	return 0;
131 }
132 /* }}} */
133 
fpm_php_set_allowed_clients(struct fpm_worker_pool_s * wp)134 static int fpm_php_set_allowed_clients(struct fpm_worker_pool_s *wp) /* {{{ */
135 {
136 	if (wp->listen_address_domain == FPM_AF_INET) {
137 		fcgi_set_allowed_clients(wp->config->listen_allowed_clients);
138 	}
139 	return 0;
140 }
141 /* }}} */
142 
143 #if 0 /* Comment out this non used function. It could be used later. */
144 static int fpm_php_set_fcgi_mgmt_vars(struct fpm_worker_pool_s *wp) /* {{{ */
145 {
146 	char max_workers[10 + 1]; /* 4294967295 */
147 	int len;
148 
149 	len = sprintf(max_workers, "%u", (unsigned int) wp->config->pm_max_children);
150 
151 	fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, max_workers, len);
152 	fcgi_set_mgmt_var("FCGI_MAX_REQS",  sizeof("FCGI_MAX_REQS")-1,  max_workers, len);
153 	return 0;
154 }
155 /* }}} */
156 #endif
157 
fpm_php_script_filename(void)158 char *fpm_php_script_filename(void)
159 {
160 	return SG(request_info).path_translated;
161 }
162 
fpm_php_request_uri(void)163 char *fpm_php_request_uri(void)
164 {
165 	return (char *) SG(request_info).request_uri;
166 }
167 
fpm_php_request_method(void)168 char *fpm_php_request_method(void)
169 {
170 	return (char *) SG(request_info).request_method;
171 }
172 
fpm_php_query_string(void)173 char *fpm_php_query_string(void)
174 {
175 	return SG(request_info).query_string;
176 }
177 
fpm_php_auth_user(void)178 char *fpm_php_auth_user(void)
179 {
180 	return SG(request_info).auth_user;
181 }
182 
fpm_php_content_length(void)183 size_t fpm_php_content_length(void)
184 {
185 	return SG(request_info).content_length;
186 }
187 
fpm_php_cleanup(int which,void * arg)188 static void fpm_php_cleanup(int which, void *arg) /* {{{ */
189 {
190 	php_module_shutdown();
191 	sapi_shutdown();
192 	if (limit_extensions) {
193 		fpm_worker_pool_free_limit_extensions(limit_extensions);
194 	}
195 }
196 /* }}} */
197 
fpm_php_soft_quit(void)198 void fpm_php_soft_quit(void)
199 {
200 	fcgi_terminate();
201 }
202 
fpm_php_init_main(void)203 int fpm_php_init_main(void)
204 {
205 	if (0 > fpm_cleanup_add(FPM_CLEANUP_PARENT, fpm_php_cleanup, 0)) {
206 		return -1;
207 	}
208 	return 0;
209 }
210 
fpm_php_init_child(struct fpm_worker_pool_s * wp)211 int fpm_php_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
212 {
213 	if (0 > fpm_php_apply_defines(wp) ||
214 		0 > fpm_php_set_allowed_clients(wp)) {
215 		return -1;
216 	}
217 
218 	if (wp->limit_extensions) {
219 		/* Take ownership of limit_extensions. */
220 		limit_extensions = wp->limit_extensions;
221 		wp->limit_extensions = NULL;
222 	}
223 	return 0;
224 }
225 /* }}} */
226 
fpm_php_limit_extensions(char * path)227 int fpm_php_limit_extensions(char *path) /* {{{ */
228 {
229 	char **p;
230 	size_t path_len;
231 
232 	if (!path || !limit_extensions) {
233 		return 0; /* allowed by default */
234 	}
235 
236 	p = limit_extensions;
237 	path_len = strlen(path);
238 	while (p && *p) {
239 		size_t ext_len = strlen(*p);
240 		if (path_len > ext_len) {
241 			char *path_ext = path + path_len - ext_len;
242 			if (strcmp(*p, path_ext) == 0) {
243 				return 0; /* allow as the extension has been found */
244 			}
245 		}
246 		p++;
247 	}
248 
249 
250 	zlog(ZLOG_NOTICE, "Access to the script '%s' has been denied (see security.limit_extensions)", path);
251 	return 1; /* extension not found: not allowed  */
252 }
253 /* }}} */
254 
fpm_php_get_string_from_table(zend_string * table,char * key)255 char* fpm_php_get_string_from_table(zend_string *table, char *key) /* {{{ */
256 {
257 	zval *data, *tmp;
258 	zend_string *str;
259 	if (!table || !key) {
260 		return NULL;
261 	}
262 
263 	/* inspired from ext/standard/info.c */
264 
265 	zend_is_auto_global(table);
266 
267 	/* find the table and ensure it's an array */
268 	data = zend_hash_find(&EG(symbol_table), table);
269 	if (!data || Z_TYPE_P(data) != IS_ARRAY) {
270 		return NULL;
271 	}
272 
273 	ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(data), str, tmp) {
274 		if (str && !strncmp(ZSTR_VAL(str), key, ZSTR_LEN(str))) {
275 			return Z_STRVAL_P(tmp);
276 		}
277 	} ZEND_HASH_FOREACH_END();
278 
279 	return NULL;
280 }
281 /* }}} */
282