summaryrefslogtreecommitdiff
path: root/src/snuffleupagus.c
diff options
context:
space:
mode:
authorjvoisin2022-03-20 18:20:45 +0100
committerjvoisin2022-03-20 18:20:45 +0100
commit81dd7f2ef07af306fe83d7755cbac4529aa9fc8d (patch)
tree32cc44c6231b30db5ac7b15699297863460784aa /src/snuffleupagus.c
parent83b01942dfc80474cc05e09aeef4b44307a7120b (diff)
parentc38df1077a6c1dfbca1baca049214d053e2e7684 (diff)
Merge remote-tracking branch 'sektioneins/master'
Diffstat (limited to 'src/snuffleupagus.c')
-rw-r--r--src/snuffleupagus.c539
1 files changed, 400 insertions, 139 deletions
diff --git a/src/snuffleupagus.c b/src/snuffleupagus.c
index 1ac04d0..ebb7f9c 100644
--- a/src/snuffleupagus.c
+++ b/src/snuffleupagus.c
@@ -4,6 +4,8 @@
4#include <glob.h> 4#include <glob.h>
5#endif 5#endif
6 6
7#include "zend_smart_str.h"
8
7#include "php_snuffleupagus.h" 9#include "php_snuffleupagus.h"
8 10
9#ifndef ZEND_EXT_API 11#ifndef ZEND_EXT_API
@@ -30,7 +32,7 @@ static inline void sp_op_array_handler(zend_op_array *const op) {
30 if (NULL == op->filename || op->fn_flags & ZEND_ACC_STRICT_TYPES) { 32 if (NULL == op->filename || op->fn_flags & ZEND_ACC_STRICT_TYPES) {
31 return; 33 return;
32 } else { 34 } else {
33 if (true == SNUFFLEUPAGUS_G(config).config_global_strict->enable) { 35 if (SPCFG(global_strict).enable) {
34 op->fn_flags |= ZEND_ACC_STRICT_TYPES; 36 op->fn_flags |= ZEND_ACC_STRICT_TYPES;
35 } 37 }
36 } 38 }
@@ -38,20 +40,10 @@ static inline void sp_op_array_handler(zend_op_array *const op) {
38 40
39ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) 41ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
40 42
41static PHP_INI_MH(StrictMode) {
42 TSRMLS_FETCH();
43
44 SNUFFLEUPAGUS_G(allow_broken_configuration) = false;
45 if (new_value && zend_string_equals_literal(new_value, "1")) {
46 SNUFFLEUPAGUS_G(allow_broken_configuration) = true;
47 }
48 return SUCCESS;
49}
50
51PHP_INI_BEGIN() 43PHP_INI_BEGIN()
52PHP_INI_ENTRY("sp.configuration_file", "", PHP_INI_SYSTEM, 44PHP_INI_ENTRY("sp.configuration_file", "", PHP_INI_SYSTEM, OnUpdateConfiguration)
53 OnUpdateConfiguration) 45STD_PHP_INI_BOOLEAN("sp.allow_broken_configuration", "0", PHP_INI_SYSTEM, OnUpdateBool, allow_broken_configuration, zend_snuffleupagus_globals, snuffleupagus_globals)
54PHP_INI_ENTRY("sp.allow_broken_configuration", "0", PHP_INI_SYSTEM, StrictMode) 46
55PHP_INI_END() 47PHP_INI_END()
56 48
57ZEND_DLEXPORT zend_extension zend_extension_entry = { 49ZEND_DLEXPORT zend_extension zend_extension_entry = {
@@ -73,155 +65,170 @@ ZEND_DLEXPORT zend_extension zend_extension_entry = {
73 NULL, /* op_array_dtor_func_t */ 65 NULL, /* op_array_dtor_func_t */
74 STANDARD_ZEND_EXTENSION_PROPERTIES}; 66 STANDARD_ZEND_EXTENSION_PROPERTIES};
75 67
76PHP_GINIT_FUNCTION(snuffleupagus) { 68static void sp_load_other_modules() {
69 // try to load other modules before initializing Snuffleupagus
70 zend_module_entry *module;
71 bool should_start = false;
72 ZEND_HASH_FOREACH_PTR(&module_registry, module) {
73 if (should_start) {
74 sp_log_debug("attempting to start module '%s' early", module->name);
75 if (zend_startup_module_ex(module) != SUCCESS) {
76 // startup failed. let's try again later.
77 module->module_started = 0;
78 }
79 }
80 if (strcmp(module->name, PHP_SNUFFLEUPAGUS_EXTNAME) == 0) {
81 should_start = true;
82 }
83 } ZEND_HASH_FOREACH_END();
84
85
86}
87
88static PHP_GINIT_FUNCTION(snuffleupagus) {
77#ifdef SP_DEBUG_STDERR 89#ifdef SP_DEBUG_STDERR
78 sp_debug_stderr = dup(STDERR_FILENO); 90 if (getenv("SP_NODEBUG")) {
91 sp_debug_stderr = -1;
92 } else {
93 sp_debug_stderr = dup(STDERR_FILENO);
94 }
79#endif 95#endif
80 sp_log_debug("(GINIT)"); 96 sp_log_debug("(GINIT)");
97 sp_load_other_modules();
81 snuffleupagus_globals->is_config_valid = SP_CONFIG_NONE; 98 snuffleupagus_globals->is_config_valid = SP_CONFIG_NONE;
82 snuffleupagus_globals->in_eval = 0; 99 snuffleupagus_globals->in_eval = 0;
83 100
84#define SP_INIT_HT(F) \ 101#define SP_INIT_HT(F) \
85 snuffleupagus_globals->F = pemalloc(sizeof(*(snuffleupagus_globals->F)), 1); \ 102 snuffleupagus_globals->F = pemalloc(sizeof(*(snuffleupagus_globals->F)), 1); \
86 zend_hash_init(snuffleupagus_globals->F, 10, NULL, NULL, 1); 103 zend_hash_init(snuffleupagus_globals->F, 10, NULL, NULL, 1);
87 SP_INIT_HT(disabled_functions_hook); 104 SP_INIT_HT(disabled_functions_hook);
88 SP_INIT_HT(sp_internal_functions_hook); 105 SP_INIT_HT(sp_internal_functions_hook);
89 SP_INIT_HT(sp_eval_blacklist_functions_hook); 106 SP_INIT_HT(sp_eval_blacklist_functions_hook);
90 SP_INIT_HT(config.config_disabled_functions); 107 SP_INIT_HT(config_disabled_functions);
91 SP_INIT_HT(config.config_disabled_functions_hooked); 108 SP_INIT_HT(config_disabled_functions_hooked);
92 SP_INIT_HT(config.config_disabled_functions_ret); 109 SP_INIT_HT(config_disabled_functions_ret);
93 SP_INIT_HT(config.config_disabled_functions_ret_hooked); 110 SP_INIT_HT(config_disabled_functions_ret_hooked);
111 SP_INIT_HT(config_ini.entries);
94#undef SP_INIT_HT 112#undef SP_INIT_HT
95 113
96#define SP_INIT(F) \ 114#define SP_INIT_NULL(F) snuffleupagus_globals->F = NULL;
97 snuffleupagus_globals->config.F = \ 115 SP_INIT_NULL(config_encryption_key);
98 pecalloc(sizeof(*(snuffleupagus_globals->config.F)), 1, 1); 116 SP_INIT_NULL(config_cookies_env_var);
99 SP_INIT(config_unserialize); 117 SP_INIT_NULL(config_disabled_functions_reg.disabled_functions);
100 SP_INIT(config_random); 118 SP_INIT_NULL(config_disabled_functions_reg_ret.disabled_functions);
101 SP_INIT(config_sloppy); 119 SP_INIT_NULL(config_cookie.cookies);
102 SP_INIT(config_readonly_exec); 120 SP_INIT_NULL(config_eval.blacklist);
103 SP_INIT(config_global_strict); 121 SP_INIT_NULL(config_eval.whitelist);
104 SP_INIT(config_auto_cookie_secure); 122 SP_INIT_NULL(config_wrapper.whitelist);
105 SP_INIT(config_snuffleupagus);
106 SP_INIT(config_disable_xxe);
107 SP_INIT(config_upload_validation);
108 SP_INIT(config_disabled_functions_reg);
109 SP_INIT(config_disabled_functions_reg_ret);
110 SP_INIT(config_cookie);
111 SP_INIT(config_session);
112 SP_INIT(config_eval);
113 SP_INIT(config_wrapper);
114#undef SP_INIT
115
116#define SP_INIT_NULL(F) snuffleupagus_globals->config.F = NULL;
117 SP_INIT_NULL(config_disabled_functions_reg->disabled_functions);
118 SP_INIT_NULL(config_disabled_functions_reg_ret->disabled_functions);
119 SP_INIT_NULL(config_cookie->cookies);
120 SP_INIT_NULL(config_eval->blacklist);
121 SP_INIT_NULL(config_eval->whitelist);
122 SP_INIT_NULL(config_wrapper->whitelist);
123#undef SP_INIT_NULL 123#undef SP_INIT_NULL
124} 124}
125 125
126PHP_MINIT_FUNCTION(snuffleupagus) { 126PHP_MINIT_FUNCTION(snuffleupagus) {
127 sp_log_debug("(MINIT)"); 127 sp_log_debug("(MINIT)");
128 REGISTER_INI_ENTRIES(); 128 REGISTER_INI_ENTRIES();
129 129
130 return SUCCESS; 130 return SUCCESS;
131} 131}
132 132
133static void free_disabled_functions_hashtable(HashTable *const ht) { 133PHP_MSHUTDOWN_FUNCTION(snuffleupagus) {
134 sp_log_debug("(MSHUTDOWN)");
135 unhook_functions(SPG(sp_internal_functions_hook));
136 unhook_functions(SPG(disabled_functions_hook));
137 unhook_functions(SPG(sp_eval_blacklist_functions_hook));
138 if (SPCFG(ini).enable) { sp_unhook_ini(); }
139 UNREGISTER_INI_ENTRIES();
140
141 return SUCCESS;
142}
143
144static inline void free_disabled_functions_hashtable(HashTable *const ht) {
134 void *ptr = NULL; 145 void *ptr = NULL;
135 ZEND_HASH_FOREACH_PTR(ht, ptr) { sp_list_free(ptr); } 146 ZEND_HASH_FOREACH_PTR(ht, ptr) { sp_list_free(ptr, sp_free_disabled_function); }
136 ZEND_HASH_FOREACH_END(); 147 ZEND_HASH_FOREACH_END();
137} 148}
138 149
139PHP_MSHUTDOWN_FUNCTION(snuffleupagus) { 150static inline void free_config_ini_entries(HashTable *const ht) {
151 void *ptr = NULL;
152 ZEND_HASH_FOREACH_PTR(ht, ptr) { sp_free_ini_entry(ptr); pefree(ptr, 1); }
153 ZEND_HASH_FOREACH_END();
154}
155
156static PHP_GSHUTDOWN_FUNCTION(snuffleupagus) {
157 sp_log_debug("(GSHUTDOWN)");
140#define FREE_HT(F) \ 158#define FREE_HT(F) \
141 zend_hash_destroy(SNUFFLEUPAGUS_G(F)); \ 159 zend_hash_destroy(snuffleupagus_globals->F); \
142 pefree(SNUFFLEUPAGUS_G(F), 1); 160 pefree(snuffleupagus_globals->F, 1);
143 FREE_HT(disabled_functions_hook); 161 FREE_HT(disabled_functions_hook);
144 FREE_HT(sp_eval_blacklist_functions_hook); 162 FREE_HT(sp_eval_blacklist_functions_hook);
145 163
146#define FREE_HT_LIST(F) \ 164#define FREE_HT_LIST(F) \
147 free_disabled_functions_hashtable(SNUFFLEUPAGUS_G(config).F); \ 165 free_disabled_functions_hashtable(snuffleupagus_globals->F); \
148 FREE_HT(config.F); 166 FREE_HT(F);
149 FREE_HT_LIST(config_disabled_functions); 167 FREE_HT_LIST(config_disabled_functions);
150 FREE_HT_LIST(config_disabled_functions_hooked); 168 FREE_HT_LIST(config_disabled_functions_hooked);
151 FREE_HT_LIST(config_disabled_functions_ret); 169 FREE_HT_LIST(config_disabled_functions_ret);
152 FREE_HT_LIST(config_disabled_functions_ret_hooked); 170 FREE_HT_LIST(config_disabled_functions_ret_hooked);
153#undef FREE_HT_LIST 171#undef FREE_HT_LIST
154#undef FREE_HT
155 172
156#define FREE_LST_DISABLE(L) \ 173 free_config_ini_entries(snuffleupagus_globals->config_ini.entries);
157 do { \ 174 FREE_HT(config_ini.entries);
158 sp_list_node *_n = SNUFFLEUPAGUS_G(config).L; \ 175#undef FREE_HT
159 sp_disabled_function_list_free(_n); \
160 sp_list_free(_n); \
161 } while (0)
162 FREE_LST_DISABLE(config_disabled_functions_reg->disabled_functions);
163 FREE_LST_DISABLE(config_disabled_functions_reg_ret->disabled_functions);
164#undef FREE_LST_DISABLE
165 176
166 sp_list_node *_n = SNUFFLEUPAGUS_G(config).config_cookie->cookies; 177 sp_list_free(snuffleupagus_globals->config_disabled_functions_reg.disabled_functions, sp_free_disabled_function);
167 sp_cookie_list_free(_n); 178 sp_list_free(snuffleupagus_globals->config_disabled_functions_reg_ret.disabled_functions, sp_free_disabled_function);
168 sp_list_free(_n); 179 sp_list_free(snuffleupagus_globals->config_cookie.cookies, sp_free_cookie);
169 180
170#define FREE_LST(L) sp_list_free(SNUFFLEUPAGUS_G(config).L); 181#define FREE_LST(L) sp_list_free(snuffleupagus_globals->L, sp_free_zstr);
171 FREE_LST(config_eval->blacklist); 182 FREE_LST(config_eval.blacklist);
172 FREE_LST(config_eval->whitelist); 183 FREE_LST(config_eval.whitelist);
173 FREE_LST(config_wrapper->whitelist); 184 FREE_LST(config_wrapper.whitelist);
174#undef FREE_LST 185#undef FREE_LST
175 186
176#define FREE_CFG(C) pefree(SNUFFLEUPAGUS_G(config).C, 1);
177 FREE_CFG(config_unserialize);
178 FREE_CFG(config_random);
179 FREE_CFG(config_readonly_exec);
180 FREE_CFG(config_global_strict);
181 FREE_CFG(config_auto_cookie_secure);
182 FREE_CFG(config_snuffleupagus);
183 FREE_CFG(config_disable_xxe);
184 FREE_CFG(config_upload_validation);
185 FREE_CFG(config_session);
186 FREE_CFG(config_disabled_functions_reg);
187 FREE_CFG(config_disabled_functions_reg_ret);
188 FREE_CFG(config_cookie);
189 FREE_CFG(config_wrapper);
190#undef FREE_CFG
191 187
192 UNREGISTER_INI_ENTRIES(); 188// #define FREE_CFG(C) pefree(snuffleupagus_globals->config.C, 1);
189#define FREE_CFG_ZSTR(C) sp_free_zstr(snuffleupagus_globals->C);
190 FREE_CFG_ZSTR(config_unserialize.dump);
191 FREE_CFG_ZSTR(config_unserialize.textual_representation);
192 FREE_CFG_ZSTR(config_upload_validation.script);
193 FREE_CFG_ZSTR(config_eval.dump);
194 FREE_CFG_ZSTR(config_eval.textual_representation);
195// #undef FREE_CFG
196#undef FREE_CFG_ZSTR
193 197
194 return SUCCESS; 198#ifdef SP_DEBUG_STDERR
199 if (sp_debug_stderr >= 0) {
200 close(sp_debug_stderr);
201 sp_debug_stderr = STDERR_FILENO;
202 }
203#endif
195} 204}
196 205
197PHP_RINIT_FUNCTION(snuffleupagus) { 206PHP_RINIT_FUNCTION(snuffleupagus) {
198 const sp_config_wrapper *const config_wrapper = 207 SPG(execution_depth) = 0;
199 SNUFFLEUPAGUS_G(config).config_wrapper; 208 SPG(in_eval) = 0;
209
210 const sp_config_wrapper *const config_wrapper = &(SPCFG(wrapper));
200#if defined(COMPILE_DL_SNUFFLEUPAGUS) && defined(ZTS) 211#if defined(COMPILE_DL_SNUFFLEUPAGUS) && defined(ZTS)
201 ZEND_TSRMLS_CACHE_UPDATE(); 212 ZEND_TSRMLS_CACHE_UPDATE();
202#endif 213#endif
203 214
204 if (!SNUFFLEUPAGUS_G(allow_broken_configuration)) { 215 if (!SPG(allow_broken_configuration)) {
205 if (SNUFFLEUPAGUS_G(is_config_valid) == SP_CONFIG_INVALID) { 216 if (SPG(is_config_valid) == SP_CONFIG_INVALID) {
206 sp_log_err("config", "Invalid configuration file"); 217 sp_log_err("config", "Invalid configuration file");
207 } else if (SNUFFLEUPAGUS_G(is_config_valid) == SP_CONFIG_NONE) { 218 } else if (SPG(is_config_valid) == SP_CONFIG_NONE) {
208 sp_log_warn("config", 219 sp_log_warn("config", "No configuration specificed via sp.configuration_file");
209 "No configuration specificed via sp.configuration_file");
210 } 220 }
211 } 221 }
212 222
213 // We need to disable wrappers loaded by extensions loaded after 223 // We need to disable wrappers loaded by extensions loaded after SNUFFLEUPAGUS.
214 // SNUFFLEUPAGUS.
215 if (config_wrapper->enabled && 224 if (config_wrapper->enabled &&
216 zend_hash_num_elements(php_stream_get_url_stream_wrappers_hash()) != 225 zend_hash_num_elements(php_stream_get_url_stream_wrappers_hash()) != config_wrapper->num_wrapper) {
217 config_wrapper->num_wrapper) {
218 sp_disable_wrapper(); 226 sp_disable_wrapper();
219 } 227 }
220 228
221 if (NULL != SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key) { 229 if (NULL != SPCFG(encryption_key)) {
222 if (NULL != SNUFFLEUPAGUS_G(config).config_cookie->cookies) { 230 if (NULL != SPCFG(cookie).cookies) {
223 zend_hash_apply_with_arguments( 231 zend_hash_apply_with_arguments(Z_ARRVAL(PG(http_globals)[TRACK_VARS_COOKIE]), decrypt_cookie, 0);
224 Z_ARRVAL(PG(http_globals)[TRACK_VARS_COOKIE]), decrypt_cookie, 0);
225 } 232 }
226 } 233 }
227 return SUCCESS; 234 return SUCCESS;
@@ -231,7 +238,7 @@ PHP_RSHUTDOWN_FUNCTION(snuffleupagus) { return SUCCESS; }
231 238
232PHP_MINFO_FUNCTION(snuffleupagus) { 239PHP_MINFO_FUNCTION(snuffleupagus) {
233 const char *valid_config; 240 const char *valid_config;
234 switch (SNUFFLEUPAGUS_G(is_config_valid)) { 241 switch (SPG(is_config_valid)) {
235 case SP_CONFIG_VALID: 242 case SP_CONFIG_VALID:
236 valid_config = "yes"; 243 valid_config = "yes";
237 break; 244 break;
@@ -245,22 +252,254 @@ PHP_MINFO_FUNCTION(snuffleupagus) {
245 php_info_print_table_start(); 252 php_info_print_table_start();
246 php_info_print_table_row( 253 php_info_print_table_row(
247 2, "snuffleupagus support", 254 2, "snuffleupagus support",
248 SNUFFLEUPAGUS_G(is_config_valid) ? "enabled" : "disabled"); 255 SPG(is_config_valid) ? "enabled" : "disabled");
249 php_info_print_table_row(2, "Version", PHP_SNUFFLEUPAGUS_VERSION); 256 php_info_print_table_row(2, "Version", PHP_SNUFFLEUPAGUS_VERSION "-sng (with Suhosin-NG patches)");
250 php_info_print_table_row(2, "Valid config", valid_config); 257 php_info_print_table_row(2, "Valid config", valid_config);
251 php_info_print_table_end(); 258 php_info_print_table_end();
252 DISPLAY_INI_ENTRIES(); 259 DISPLAY_INI_ENTRIES();
253} 260}
254 261
255static PHP_INI_MH(OnUpdateConfiguration) { 262#define ADD_ASSOC_ZSTR(arr, key, zstr) if (zstr) { add_assoc_str(arr, key, zstr); } else { add_assoc_null(arr, key); }
256 sp_log_debug("(OnUpdateConfiguration)"); 263#define ADD_ASSOC_REGEXP(arr, key, regexp) if (regexp && regexp->pattern) { add_assoc_str(arr, key, regexp->pattern); } else { add_assoc_null(arr, key); }
264
265static void add_df_to_arr(zval *arr, sp_disabled_function *df) {
266 zval arr_df;
267 array_init(&arr_df);
268
269 ADD_ASSOC_ZSTR(&arr_df, SP_TOKEN_FILENAME, df->filename);
270 ADD_ASSOC_REGEXP(&arr_df, SP_TOKEN_FILENAME_REGEXP, df->r_filename);
271 ADD_ASSOC_ZSTR(&arr_df, SP_TOKEN_FUNCTION, df->function);
272 ADD_ASSOC_REGEXP(&arr_df, SP_TOKEN_FUNCTION_REGEXP, df->r_function);
273 if (df->functions_list && df->functions_list->data) {
274 zval arr_fl;
275 array_init(&arr_fl);
276 for (sp_list_node *p = df->functions_list; p; p = p->next) { add_next_index_str(&arr_fl, p->data); }
277 add_assoc_zval(&arr_df, "function_list", &arr_fl);
278 } else {
279 add_assoc_null(&arr_df, "function_list");
280 }
281 ADD_ASSOC_ZSTR(&arr_df, SP_TOKEN_HASH, df->hash);
282 add_assoc_bool(&arr_df, SP_TOKEN_SIM, df->simulation);
283 if (df->param && df->param->value) {
284 add_assoc_string(&arr_df, SP_TOKEN_PARAM, df->param->value);
285 } else {
286 add_assoc_null(&arr_df, SP_TOKEN_PARAM);
287 }
288 ADD_ASSOC_REGEXP(&arr_df, SP_TOKEN_PARAM_REGEXP, df->r_param);
289 add_assoc_long(&arr_df, SP_TOKEN_PARAM_TYPE, df->param_type);
290 add_assoc_long(&arr_df, SP_TOKEN_VALUE_ARG_POS, df->pos);
291 add_assoc_long(&arr_df, SP_TOKEN_LINE_NUMBER, df->line);
292 ADD_ASSOC_ZSTR(&arr_df, SP_TOKEN_RET, df->ret);
293 ADD_ASSOC_REGEXP(&arr_df, SP_TOKEN_RET_REGEXP, df->r_ret);
294 ADD_ASSOC_ZSTR(&arr_df, SP_TOKEN_VALUE, df->value);
295 ADD_ASSOC_REGEXP(&arr_df, SP_TOKEN_VALUE_REGEXP, df->r_value);
296 ADD_ASSOC_ZSTR(&arr_df, SP_TOKEN_KEY, df->key);
297 ADD_ASSOC_REGEXP(&arr_df, SP_TOKEN_KEY_REGEXP, df->r_key);
298 ADD_ASSOC_ZSTR(&arr_df, SP_TOKEN_DUMP, df->dump);
299 ADD_ASSOC_ZSTR(&arr_df, SP_TOKEN_ALIAS, df->alias);
300 add_assoc_bool(&arr_df, "param_is_array", df->param_is_array);
301 add_assoc_bool(&arr_df, "var_is_array", df->var_is_array);
302 add_assoc_bool(&arr_df, "allow", df->allow);
303 // todo: properly traverse tree for .var() and .param()
304 // sp_tree *tr = df->var;
305 // for (; tr; tr = tr->next) {
306 // sp_log_debug("tree: %s", tr->value);
307 // }
308
309 if (df->var && df->var->value) {
310 add_assoc_string(&arr_df, SP_TOKEN_LOCAL_VAR, df->var->value);
311 } else {
312 add_assoc_null(&arr_df, SP_TOKEN_LOCAL_VAR);
313 }
314 if (df->param && df->param->value) {
315 add_assoc_string(&arr_df, SP_TOKEN_PARAM, df->param->value);
316 } else {
317 add_assoc_null(&arr_df, SP_TOKEN_PARAM);
318 }
319
320 if (df->cidr) {
321 char cidrstr[INET6_ADDRSTRLEN+5];
322 if (!get_ip_str(cidrstr, sizeof(cidrstr), df->cidr)) {
323 add_assoc_null(&arr_df, SP_TOKEN_CIDR);
324 } else {
325 add_assoc_string(&arr_df, SP_TOKEN_CIDR, cidrstr);
326 }
327 } else {
328 add_assoc_null(&arr_df, SP_TOKEN_CIDR);
329 }
330
331 add_next_index_zval(arr, &arr_df);
332}
333
334static void dump_config() {
335 zval arr;
336 php_serialize_data_t var_hash;
337 smart_str buf = {0};
338
339 array_init(&arr);
340 add_assoc_string(&arr, "version", PHP_SNUFFLEUPAGUS_VERSION);
257 341
342 add_assoc_bool(&arr, SP_TOKEN_UNSERIALIZE_HMAC "." SP_TOKEN_ENABLE, SPCFG(unserialize).enable);
343 add_assoc_bool(&arr, SP_TOKEN_UNSERIALIZE_HMAC "." SP_TOKEN_SIM, SPCFG(unserialize).simulation);
344 ADD_ASSOC_ZSTR(&arr, SP_TOKEN_UNSERIALIZE_HMAC "." SP_TOKEN_DUMP, SPCFG(unserialize).dump);
345
346 add_assoc_bool(&arr, SP_TOKEN_HARDEN_RANDOM "." SP_TOKEN_ENABLE, SPCFG(random).enable);
347
348 add_assoc_bool(&arr, "readonly_exec.enable", SPCFG(readonly_exec).enable);
349 add_assoc_bool(&arr, "readonly_exec.sim", SPCFG(readonly_exec).simulation);
350 ADD_ASSOC_ZSTR(&arr, SP_TOKEN_READONLY_EXEC "." SP_TOKEN_DUMP, SPCFG(readonly_exec).dump);
351
352 add_assoc_bool(&arr, "global_strict.enable", SPCFG(global_strict).enable);
353
354 add_assoc_bool(&arr, "upload_validation.enable", SPCFG(upload_validation).enable);
355 add_assoc_bool(&arr, "upload_validation.sim", SPCFG(upload_validation).simulation);
356 ADD_ASSOC_ZSTR(&arr, SP_TOKEN_UPLOAD_VALIDATION "." SP_TOKEN_UPLOAD_SCRIPT, SPCFG(upload_validation).script);
357
358 // global
359 add_assoc_bool(&arr, SP_TOKEN_GLOBAL "." SP_TOKEN_ENCRYPTION_KEY, SPCFG(encryption_key) && ZSTR_LEN(SPCFG(encryption_key)));
360 ADD_ASSOC_ZSTR(&arr, SP_TOKEN_GLOBAL "." SP_TOKEN_ENV_VAR, SPCFG(cookies_env_var));
361 add_assoc_long(&arr, SP_TOKEN_GLOBAL "." SP_TOKEN_LOG_MEDIA, SPCFG(log_media));
362 add_assoc_long(&arr, SP_TOKEN_GLOBAL "." SP_TOKEN_MAX_EXECUTION_DEPTH, SPCFG(max_execution_depth));
363 add_assoc_bool(&arr, SP_TOKEN_GLOBAL "." SP_TOKEN_SERVER_ENCODE, SPCFG(server_encode));
364 add_assoc_bool(&arr, SP_TOKEN_GLOBAL "." SP_TOKEN_SERVER_STRIP, SPCFG(server_strip));
365 add_assoc_bool(&arr, SP_TOKEN_GLOBAL "." SP_TOKEN_SHOW_OLD_PHP_WARNING, SPCFG(show_old_php_warning));
366
367 add_assoc_bool(&arr, SP_TOKEN_AUTO_COOKIE_SECURE, SPCFG(auto_cookie_secure).enable);
368 add_assoc_bool(&arr, SP_TOKEN_XXE_PROTECTION, SPCFG(xxe_protection).enable);
369
370 add_assoc_bool(&arr, SP_TOKEN_EVAL_BLACKLIST "." SP_TOKEN_SIM, SPCFG(eval).simulation);
371 ADD_ASSOC_ZSTR(&arr, SP_TOKEN_EVAL_BLACKLIST "." SP_TOKEN_DUMP, SPCFG(eval).dump);
372#define ADD_ASSOC_SPLIST(arr, key, splist) \
373 if (splist) { \
374 zval arr_sp; \
375 array_init(&arr_sp); \
376 for (sp_list_node *p = splist; p; p = p->next) { add_next_index_str(&arr_sp, p->data); } \
377 add_assoc_zval(arr, key, &arr_sp); \
378 } else { add_assoc_null(arr, key); }
379
380 ADD_ASSOC_SPLIST(&arr, SP_TOKEN_EVAL_BLACKLIST "." SP_TOKEN_LIST, SPCFG(eval).blacklist);
381 ADD_ASSOC_SPLIST(&arr, SP_TOKEN_EVAL_WHITELIST "." SP_TOKEN_LIST, SPCFG(eval).whitelist)
382
383 add_assoc_bool(&arr, SP_TOKEN_SESSION_ENCRYPTION "." SP_TOKEN_ENCRYPT, SPCFG(session).encrypt);
384 add_assoc_bool(&arr, SP_TOKEN_SESSION_ENCRYPTION "." SP_TOKEN_SIM, SPCFG(session).simulation);
385
386 add_assoc_long(&arr, SP_TOKEN_SESSION_ENCRYPTION "." SP_TOKEN_SID_MIN_LENGTH, SPCFG(session).sid_min_length);
387 add_assoc_long(&arr, SP_TOKEN_SESSION_ENCRYPTION "." SP_TOKEN_SID_MAX_LENGTH, SPCFG(session).sid_max_length);
388 add_assoc_bool(&arr, SP_TOKEN_SLOPPY_COMPARISON "." SP_TOKEN_ENABLE, SPCFG(sloppy).enable);
389
390 ADD_ASSOC_SPLIST(&arr, SP_TOKEN_ALLOW_WRAPPERS, SPCFG(wrapper).whitelist);
391
392 add_assoc_bool(&arr, SP_TOKEN_INI_PROTECTION "." SP_TOKEN_ENABLE, SPCFG(ini).enable);
393 add_assoc_bool(&arr, SP_TOKEN_INI_PROTECTION "." SP_TOKEN_SIM, SPCFG(ini).simulation);
394 add_assoc_bool(&arr, SP_TOKEN_INI_PROTECTION "." "policy_ro", SPCFG(ini).policy_readonly);
395 add_assoc_bool(&arr, SP_TOKEN_INI_PROTECTION "." "policy_silent_ro", SPCFG(ini).policy_silent_ro);
396 add_assoc_bool(&arr, SP_TOKEN_INI_PROTECTION "." "policy_silent_fail", SPCFG(ini).policy_silent_fail);
397 add_assoc_bool(&arr, SP_TOKEN_INI_PROTECTION "." "policy_drop", SPCFG(ini).policy_drop);
398
399 if (SPCFG(ini).entries && zend_hash_num_elements(SPCFG(ini).entries) > 0) {
400 zval arr_ini;
401 array_init(&arr_ini);
402
403 sp_ini_entry *sp_entry;
404 ZEND_HASH_FOREACH_PTR(SPCFG(ini).entries, sp_entry)
405 zval arr_ini_entry;
406 array_init(&arr_ini_entry);
407 add_assoc_bool(&arr_ini_entry, SP_TOKEN_SIM, sp_entry->simulation);
408 ADD_ASSOC_ZSTR(&arr_ini_entry, SP_TOKEN_KEY, sp_entry->key);
409 ADD_ASSOC_ZSTR(&arr_ini_entry, "msg", sp_entry->msg);
410 ADD_ASSOC_ZSTR(&arr_ini_entry, "set", sp_entry->set);
411 ADD_ASSOC_ZSTR(&arr_ini_entry, "min", sp_entry->min);
412 ADD_ASSOC_ZSTR(&arr_ini_entry, "max", sp_entry->max);
413 add_assoc_long(&arr_ini_entry, "access", sp_entry->access);
414 add_assoc_bool(&arr_ini_entry, "drop", sp_entry->drop);
415 add_assoc_bool(&arr_ini_entry, "allow_null", sp_entry->allow_null);
416 ADD_ASSOC_REGEXP(&arr_ini_entry, "regexp", sp_entry->regexp);
417 add_next_index_zval(&arr_ini, &arr_ini_entry);
418 ZEND_HASH_FOREACH_END();
419 add_assoc_zval(&arr, SP_TOKEN_INI, &arr_ini);
420 } else {
421 add_assoc_null(&arr, SP_TOKEN_INI);
422 }
423
424 if (SPCFG(cookie).cookies && SPCFG(cookie).cookies->data) {
425 zval arr_cookies;
426 array_init(&arr_cookies);
427
428 sp_cookie *cookie;
429 sp_list_node *p = SPCFG(cookie).cookies;
430 for (; p; p = p->next) {
431 zval arr_cookie;
432 array_init(&arr_cookie);
433 cookie = (sp_cookie*)p->data;
434
435 add_assoc_long(&arr_cookie, SP_TOKEN_SAMESITE, cookie->samesite);
436 add_assoc_bool(&arr_cookie, SP_TOKEN_ENCRYPT, cookie->encrypt);
437 ADD_ASSOC_ZSTR(&arr_cookie, SP_TOKEN_NAME, cookie->name);
438 ADD_ASSOC_REGEXP(&arr_cookie, SP_TOKEN_NAME_REGEXP, cookie->name_r);
439 add_assoc_bool(&arr_cookie, SP_TOKEN_SIM, cookie->simulation);
440
441 add_next_index_zval(&arr_cookies, &arr_cookie);
442 }
443
444 add_assoc_zval(&arr, SP_TOKEN_COOKIE_ENCRYPTION, &arr_cookies);
445 } else {
446 add_assoc_null(&arr, SP_TOKEN_COOKIE_ENCRYPTION);
447 }
448
449 // disabled_functions
450 zval arr_dfs;
451 array_init(&arr_dfs);
452 size_t num_df = 0;
453 sp_list_node *dflist, *dfp;
454 ZEND_HASH_FOREACH_PTR(SPCFG(disabled_functions), dflist)
455 for (dfp = dflist; dfp; dfp = dfp->next) {
456 add_df_to_arr(&arr_dfs, dfp->data);
457 num_df++;
458 }
459 ZEND_HASH_FOREACH_END();
460 ZEND_HASH_FOREACH_PTR(SPCFG(disabled_functions_ret), dflist)
461 for (dfp = dflist; dfp; dfp = dfp->next) {
462 add_df_to_arr(&arr_dfs, dfp->data);
463 num_df++;
464 }
465 ZEND_HASH_FOREACH_END();
466 for (dfp = SPCFG(disabled_functions_reg).disabled_functions; dfp; dfp = dfp->next) {
467 add_df_to_arr(&arr_dfs, dfp->data);
468 num_df++;
469 }
470 for (dfp = SPCFG(disabled_functions_reg_ret).disabled_functions; dfp; dfp = dfp->next) {
471 add_df_to_arr(&arr_dfs, dfp->data);
472 num_df++;
473 }
474
475 if (num_df) {
476 add_assoc_zval(&arr, SP_TOKEN_DISABLE_FUNC, &arr_dfs);
477 } else {
478 add_assoc_null(&arr, SP_TOKEN_DISABLE_FUNC);
479 }
480
481 // serialize and print array
482 PHP_VAR_SERIALIZE_INIT(var_hash);
483 php_var_serialize(&buf, &arr, &var_hash);
484 PHP_VAR_SERIALIZE_DESTROY(var_hash);
485
486 printf("%s", ZSTR_VAL(buf.s));
487 sp_log_debug("--");
488
489 smart_str_free(&buf);
490
491}
492
493static PHP_INI_MH(OnUpdateConfiguration) {
258 TSRMLS_FETCH(); 494 TSRMLS_FETCH();
259 495
260 if (!new_value || !new_value->len) { 496 if (!new_value || !new_value->len) {
261 return FAILURE; 497 return FAILURE;
262 } 498 }
263 499
500 // set some defaults
501 SPCFG(show_old_php_warning) = true;
502
264 char *str = new_value->val; 503 char *str = new_value->val;
265 504
266 while (1) { 505 while (1) {
@@ -270,14 +509,14 @@ static PHP_INI_MH(OnUpdateConfiguration) {
270 509
271 glob_t globbuf; 510 glob_t globbuf;
272 if (0 != glob(config_file, GLOB_NOCHECK, NULL, &globbuf)) { 511 if (0 != glob(config_file, GLOB_NOCHECK, NULL, &globbuf)) {
273 SNUFFLEUPAGUS_G(is_config_valid) = SP_CONFIG_INVALID; 512 SPG(is_config_valid) = SP_CONFIG_INVALID;
274 globfree(&globbuf); 513 globfree(&globbuf);
275 return FAILURE; 514 return FAILURE;
276 } 515 }
277 516
278 for (size_t i = 0; globbuf.gl_pathv[i]; i++) { 517 for (size_t i = 0; globbuf.gl_pathv[i]; i++) {
279 if (sp_parse_config(globbuf.gl_pathv[i]) != SUCCESS) { 518 if (sp_parse_config(globbuf.gl_pathv[i]) != SUCCESS) {
280 SNUFFLEUPAGUS_G(is_config_valid) = SP_CONFIG_INVALID; 519 SPG(is_config_valid) = SP_CONFIG_INVALID;
281 globfree(&globbuf); 520 globfree(&globbuf);
282 return FAILURE; 521 return FAILURE;
283 } 522 }
@@ -285,43 +524,62 @@ static PHP_INI_MH(OnUpdateConfiguration) {
285 globfree(&globbuf); 524 globfree(&globbuf);
286 } 525 }
287 526
288 SNUFFLEUPAGUS_G(is_config_valid) = SP_CONFIG_VALID; 527 SPG(is_config_valid) = SP_CONFIG_VALID;
528
529 // dump config
530 sp_log_debug("module name? %s", sapi_module.name);
531 if (getenv("SP_DUMP_CONFIG")) {
532 sp_log_debug("env? %s", getenv("SP_DUMP_CONFIG"));
533 }
534
535 if (strcmp(sapi_module.name, "cli") == 0 && getenv("SP_DUMP_CONFIG")) {
536 dump_config();
537 return SUCCESS;
538 }
539
289 540
290 if ((SNUFFLEUPAGUS_G(config).config_sloppy->enable)) { 541 // start hooks
542
543 if (SPCFG(sloppy).enable) {
291 hook_sloppy(); 544 hook_sloppy();
292 } 545 }
293 546
294 if (SNUFFLEUPAGUS_G(config).config_random->enable) { 547 if (SPCFG(random).enable) {
295 hook_rand(); 548 hook_rand();
296 } 549 }
297 550
298 if (SNUFFLEUPAGUS_G(config).config_upload_validation->enable) { 551 if (SPCFG(upload_validation).enable) {
299 hook_upload(); 552 hook_upload();
300 } 553 }
301 554
302 if (SNUFFLEUPAGUS_G(config).config_disable_xxe->enable == 0) { 555 if (SPCFG(xxe_protection).enable) {
303 hook_libxml_disable_entity_loader(); 556 hook_libxml_disable_entity_loader();
304 } 557 }
305 558
306 if (SNUFFLEUPAGUS_G(config).config_wrapper->enabled) { 559 if (SPCFG(wrapper).enabled) {
307 hook_stream_wrappers(); 560 hook_stream_wrappers();
308 } 561 }
309 562
310 if (SNUFFLEUPAGUS_G(config).config_session->encrypt) { 563 if (SPCFG(session).encrypt || SPCFG(session).sid_min_length || SPCFG(session).sid_max_length) {
311 hook_session(); 564 hook_session();
312 } 565 }
313 566
314 if (NULL != SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key) { 567 if (NULL != SPCFG(encryption_key)) {
315 if (SNUFFLEUPAGUS_G(config).config_unserialize->enable) { 568 if (SPCFG(unserialize).enable) {
316 hook_serialize(); 569 hook_serialize();
317 } 570 }
318 } 571 }
319 572
320 hook_disabled_functions();
321 hook_execute(); 573 hook_execute();
322 hook_cookies(); 574 hook_cookies();
323 575
324 if (true == SNUFFLEUPAGUS_G(config).config_global_strict->enable) { 576 if (SPCFG(ini).enable) {
577 sp_hook_ini();
578 }
579
580 sp_hook_register_server_variables();
581
582 if (SPCFG(global_strict).enable) {
325 if (!zend_get_extension(PHP_SNUFFLEUPAGUS_EXTNAME)) { 583 if (!zend_get_extension(PHP_SNUFFLEUPAGUS_EXTNAME)) {
326 zend_extension_entry.startup = NULL; 584 zend_extension_entry.startup = NULL;
327 zend_register_extension(&zend_extension_entry, NULL); 585 zend_register_extension(&zend_extension_entry, NULL);
@@ -330,28 +588,31 @@ static PHP_INI_MH(OnUpdateConfiguration) {
330 CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY; 588 CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
331 } 589 }
332 590
591 hook_disabled_functions();
592
333 // If `zend_write_default` is not NULL it is already hooked. 593 // If `zend_write_default` is not NULL it is already hooked.
334 if ((zend_hash_str_find( 594 if ((zend_hash_str_find(SPCFG(disabled_functions_hooked), ZEND_STRL("echo")) ||
335 SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked, "echo", 595 zend_hash_str_find(SPCFG(disabled_functions_ret_hooked), ZEND_STRL("echo"))) &&
336 sizeof("echo") - 1) ||
337 zend_hash_str_find(
338 SNUFFLEUPAGUS_G(config).config_disabled_functions_ret_hooked, "echo",
339 sizeof("echo") - 1)) &&
340 NULL == zend_write_default && zend_write != hook_echo) { 596 NULL == zend_write_default && zend_write != hook_echo) {
341 zend_write_default = zend_write; 597 zend_write_default = zend_write;
342 zend_write = hook_echo; 598 zend_write = hook_echo;
343 } 599 }
344 600
345 SNUFFLEUPAGUS_G(config).hook_execute = 601 SPG(hook_execute) = SPCFG(max_execution_depth) > 0 ||
346 SNUFFLEUPAGUS_G(config) 602 SPCFG(disabled_functions_reg).disabled_functions ||
347 .config_disabled_functions_reg->disabled_functions || 603 SPCFG(disabled_functions_reg_ret).disabled_functions ||
348 SNUFFLEUPAGUS_G(config) 604 (SPCFG(disabled_functions) && zend_hash_num_elements(SPCFG(disabled_functions))) ||
349 .config_disabled_functions_reg_ret->disabled_functions || 605 (SPCFG(disabled_functions_ret) && zend_hash_num_elements(SPCFG(disabled_functions_ret)));
350 zend_hash_num_elements(
351 SNUFFLEUPAGUS_G(config).config_disabled_functions) ||
352 zend_hash_num_elements(
353 SNUFFLEUPAGUS_G(config).config_disabled_functions_ret);
354 606
607 if (SPCFG(show_old_php_warning) && getenv("SP_SKIP_OLD_PHP_CHECK") == NULL) {
608 time_t ts = time(NULL);
609 if ((PHP_VERSION_ID < 70300) ||
610 (PHP_VERSION_ID < 70400 && ts >= (time_t)1638745200L) ||
611 (PHP_VERSION_ID < 80000 && ts >= (time_t)1669590000L) ||
612 (PHP_VERSION_ID < 80100 && ts >= (time_t)1700953200L)) {
613 sp_log_warn("End-of-Life Check", "Your PHP version '" PHP_VERSION "' is not officially mainained anymore. Please upgrade as soon as possible. - Note: This message can be switched off by setting 'sp.global.show_old_php_warning.disable();' in your rules file or by setting the environment variable SP_SKIP_OLD_PHP_CHECK=1.");
614 }
615 }
355 return SUCCESS; 616 return SUCCESS;
356} 617}
357 618
@@ -369,7 +630,7 @@ zend_module_entry snuffleupagus_module_entry = {
369 PHP_SNUFFLEUPAGUS_VERSION, 630 PHP_SNUFFLEUPAGUS_VERSION,
370 PHP_MODULE_GLOBALS(snuffleupagus), 631 PHP_MODULE_GLOBALS(snuffleupagus),
371 PHP_GINIT(snuffleupagus), 632 PHP_GINIT(snuffleupagus),
372 NULL, 633 PHP_GSHUTDOWN(snuffleupagus),
373 NULL, 634 NULL,
374 STANDARD_MODULE_PROPERTIES_EX}; 635 STANDARD_MODULE_PROPERTIES_EX};
375 636