summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/config.m41
-rw-r--r--src/php_snuffleupagus.h1
-rw-r--r--src/snuffleupagus.c46
-rw-r--r--src/sp_config.c13
-rw-r--r--src/sp_config.h26
-rw-r--r--src/sp_config_keywords.c77
-rw-r--r--src/sp_config_keywords.h2
-rw-r--r--src/sp_ini.c126
-rw-r--r--src/sp_ini.h2
9 files changed, 281 insertions, 13 deletions
diff --git a/src/config.m4 b/src/config.m4
index e4cc1f5..1410565 100644
--- a/src/config.m4
+++ b/src/config.m4
@@ -7,6 +7,7 @@ sources="$sources sp_disabled_functions.c sp_execute.c sp_upload_validation.c"
7sources="$sources sp_cookie_encryption.c sp_network_utils.c tweetnacl.c" 7sources="$sources sp_cookie_encryption.c sp_network_utils.c tweetnacl.c"
8sources="$sources sp_config_keywords.c sp_var_parser.c sp_var_value.c sp_tree.c" 8sources="$sources sp_config_keywords.c sp_var_parser.c sp_var_value.c sp_tree.c"
9sources="$sources sp_pcre_compat.c sp_crypt.c sp_session.c sp_sloppy.c sp_wrapper.c" 9sources="$sources sp_pcre_compat.c sp_crypt.c sp_session.c sp_sloppy.c sp_wrapper.c"
10sources="$sources sp_ini.c"
10 11
11PHP_ARG_ENABLE(snuffleupagus, whether to enable snuffleupagus support, 12PHP_ARG_ENABLE(snuffleupagus, whether to enable snuffleupagus support,
12[ --enable-snuffleupagus Enable snuffleupagus support]) 13[ --enable-snuffleupagus Enable snuffleupagus support])
diff --git a/src/php_snuffleupagus.h b/src/php_snuffleupagus.h
index 5b2b414..be4d306 100644
--- a/src/php_snuffleupagus.h
+++ b/src/php_snuffleupagus.h
@@ -85,6 +85,7 @@ typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS);
85#include "sp_session.h" 85#include "sp_session.h"
86#include "sp_sloppy.h" 86#include "sp_sloppy.h"
87#include "sp_wrapper.h" 87#include "sp_wrapper.h"
88#include "sp_ini.h"
88 89
89extern zend_module_entry snuffleupagus_module_entry; 90extern zend_module_entry snuffleupagus_module_entry;
90#define phpext_snuffleupagus_ptr &snuffleupagus_module_entry 91#define phpext_snuffleupagus_ptr &snuffleupagus_module_entry
diff --git a/src/snuffleupagus.c b/src/snuffleupagus.c
index 8c7ecbf..2ee94a1 100644
--- a/src/snuffleupagus.c
+++ b/src/snuffleupagus.c
@@ -81,18 +81,6 @@ static PHP_GINIT_FUNCTION(snuffleupagus) {
81 snuffleupagus_globals->is_config_valid = SP_CONFIG_NONE; 81 snuffleupagus_globals->is_config_valid = SP_CONFIG_NONE;
82 snuffleupagus_globals->in_eval = 0; 82 snuffleupagus_globals->in_eval = 0;
83 83
84#define SP_INIT_HT(F) \
85 snuffleupagus_globals->F = pemalloc(sizeof(*(snuffleupagus_globals->F)), 1); \
86 zend_hash_init(snuffleupagus_globals->F, 10, NULL, NULL, 1);
87 SP_INIT_HT(disabled_functions_hook);
88 SP_INIT_HT(sp_internal_functions_hook);
89 SP_INIT_HT(sp_eval_blacklist_functions_hook);
90 SP_INIT_HT(config.config_disabled_functions);
91 SP_INIT_HT(config.config_disabled_functions_hooked);
92 SP_INIT_HT(config.config_disabled_functions_ret);
93 SP_INIT_HT(config.config_disabled_functions_ret_hooked);
94#undef SP_INIT_HT
95
96#define SP_INIT(F) \ 84#define SP_INIT(F) \
97 snuffleupagus_globals->config.F = \ 85 snuffleupagus_globals->config.F = \
98 pecalloc(sizeof(*(snuffleupagus_globals->config.F)), 1, 1); 86 pecalloc(sizeof(*(snuffleupagus_globals->config.F)), 1, 1);
@@ -109,10 +97,24 @@ static PHP_GINIT_FUNCTION(snuffleupagus) {
109 SP_INIT(config_eval); 97 SP_INIT(config_eval);
110 SP_INIT(config_wrapper); 98 SP_INIT(config_wrapper);
111 SP_INIT(config_session); 99 SP_INIT(config_session);
100 SP_INIT(config_ini);
112 SP_INIT(config_disabled_functions_reg); 101 SP_INIT(config_disabled_functions_reg);
113 SP_INIT(config_disabled_functions_reg_ret); 102 SP_INIT(config_disabled_functions_reg_ret);
114#undef SP_INIT 103#undef SP_INIT
115 104
105#define SP_INIT_HT(F) \
106 snuffleupagus_globals->F = pemalloc(sizeof(*(snuffleupagus_globals->F)), 1); \
107 zend_hash_init(snuffleupagus_globals->F, 10, NULL, NULL, 1);
108 SP_INIT_HT(disabled_functions_hook);
109 SP_INIT_HT(sp_internal_functions_hook);
110 SP_INIT_HT(sp_eval_blacklist_functions_hook);
111 SP_INIT_HT(config.config_disabled_functions);
112 SP_INIT_HT(config.config_disabled_functions_hooked);
113 SP_INIT_HT(config.config_disabled_functions_ret);
114 SP_INIT_HT(config.config_disabled_functions_ret_hooked);
115 SP_INIT_HT(config.config_ini->entries);
116#undef SP_INIT_HT
117
116#define SP_INIT_NULL(F) snuffleupagus_globals->config.F = NULL; 118#define SP_INIT_NULL(F) snuffleupagus_globals->config.F = NULL;
117 SP_INIT_NULL(config_disabled_functions_reg->disabled_functions); 119 SP_INIT_NULL(config_disabled_functions_reg->disabled_functions);
118 SP_INIT_NULL(config_disabled_functions_reg_ret->disabled_functions); 120 SP_INIT_NULL(config_disabled_functions_reg_ret->disabled_functions);
@@ -131,6 +133,11 @@ PHP_MINIT_FUNCTION(snuffleupagus) {
131} 133}
132 134
133PHP_MSHUTDOWN_FUNCTION(snuffleupagus) { 135PHP_MSHUTDOWN_FUNCTION(snuffleupagus) {
136 sp_log_debug("(MSHUTDOWN)");
137 unhook_functions(SNUFFLEUPAGUS_G(sp_internal_functions_hook));
138 unhook_functions(SNUFFLEUPAGUS_G(disabled_functions_hook));
139 unhook_functions(SNUFFLEUPAGUS_G(sp_eval_blacklist_functions_hook));
140 if (SNUFFLEUPAGUS_G(config).config_ini->enable) { sp_unhook_ini(); }
134 UNREGISTER_INI_ENTRIES(); 141 UNREGISTER_INI_ENTRIES();
135 142
136 return SUCCESS; 143 return SUCCESS;
@@ -142,6 +149,12 @@ static inline void free_disabled_functions_hashtable(HashTable *const ht) {
142 ZEND_HASH_FOREACH_END(); 149 ZEND_HASH_FOREACH_END();
143} 150}
144 151
152static inline void free_config_ini_entries(HashTable *const ht) {
153 void *ptr = NULL;
154 ZEND_HASH_FOREACH_PTR(ht, ptr) { sp_free_ini_entry(ptr); pefree(ptr, 1); }
155 ZEND_HASH_FOREACH_END();
156}
157
145static PHP_GSHUTDOWN_FUNCTION(snuffleupagus) { 158static PHP_GSHUTDOWN_FUNCTION(snuffleupagus) {
146 sp_log_debug("(GSHUTDOWN)"); 159 sp_log_debug("(GSHUTDOWN)");
147#define FREE_HT(F) \ 160#define FREE_HT(F) \
@@ -158,6 +171,9 @@ static PHP_GSHUTDOWN_FUNCTION(snuffleupagus) {
158 FREE_HT_LIST(config_disabled_functions_ret); 171 FREE_HT_LIST(config_disabled_functions_ret);
159 FREE_HT_LIST(config_disabled_functions_ret_hooked); 172 FREE_HT_LIST(config_disabled_functions_ret_hooked);
160#undef FREE_HT_LIST 173#undef FREE_HT_LIST
174
175 free_config_ini_entries(snuffleupagus_globals->config.config_ini->entries);
176 FREE_HT(config.config_ini->entries);
161#undef FREE_HT 177#undef FREE_HT
162 178
163#define FREE_LST_DISABLE(L) \ 179#define FREE_LST_DISABLE(L) \
@@ -174,6 +190,7 @@ static PHP_GSHUTDOWN_FUNCTION(snuffleupagus) {
174 FREE_LST(config_wrapper->whitelist); 190 FREE_LST(config_wrapper->whitelist);
175#undef FREE_LST 191#undef FREE_LST
176 192
193
177#define FREE_CFG(C) pefree(snuffleupagus_globals->config.C, 1); 194#define FREE_CFG(C) pefree(snuffleupagus_globals->config.C, 1);
178#define FREE_CFG_ZSTR(C) sp_free_zstr(snuffleupagus_globals->config.C); 195#define FREE_CFG_ZSTR(C) sp_free_zstr(snuffleupagus_globals->config.C);
179 FREE_CFG(config_random); 196 FREE_CFG(config_random);
@@ -194,6 +211,7 @@ static PHP_GSHUTDOWN_FUNCTION(snuffleupagus) {
194 FREE_CFG(config_eval); 211 FREE_CFG(config_eval);
195 FREE_CFG(config_wrapper); 212 FREE_CFG(config_wrapper);
196 FREE_CFG(config_session); 213 FREE_CFG(config_session);
214 FREE_CFG(config_ini);
197 FREE_CFG(config_disabled_functions_reg); 215 FREE_CFG(config_disabled_functions_reg);
198 FREE_CFG(config_disabled_functions_reg_ret); 216 FREE_CFG(config_disabled_functions_reg_ret);
199#undef FREE_CFG 217#undef FREE_CFG
@@ -332,6 +350,10 @@ static PHP_INI_MH(OnUpdateConfiguration) {
332 hook_execute(); 350 hook_execute();
333 hook_cookies(); 351 hook_cookies();
334 352
353 if (SNUFFLEUPAGUS_G(config).config_ini->enable) {
354 sp_hook_ini();
355 }
356
335 if (true == SNUFFLEUPAGUS_G(config).config_global_strict->enable) { 357 if (true == SNUFFLEUPAGUS_G(config).config_global_strict->enable) {
336 if (!zend_get_extension(PHP_SNUFFLEUPAGUS_EXTNAME)) { 358 if (!zend_get_extension(PHP_SNUFFLEUPAGUS_EXTNAME)) {
337 zend_extension_entry.startup = NULL; 359 zend_extension_entry.startup = NULL;
diff --git a/src/sp_config.c b/src/sp_config.c
index db3f12d..667867b 100644
--- a/src/sp_config.c
+++ b/src/sp_config.c
@@ -23,6 +23,8 @@ static sp_config_tokens const sp_func[] = {
23 {.func = parse_session, .token = SP_TOKEN_SESSION_ENCRYPTION}, 23 {.func = parse_session, .token = SP_TOKEN_SESSION_ENCRYPTION},
24 {.func = parse_sloppy_comparison, .token = SP_TOKEN_SLOPPY_COMPARISON}, 24 {.func = parse_sloppy_comparison, .token = SP_TOKEN_SLOPPY_COMPARISON},
25 {.func = parse_wrapper_whitelist, .token = SP_TOKEN_ALLOW_WRAPPERS}, 25 {.func = parse_wrapper_whitelist, .token = SP_TOKEN_ALLOW_WRAPPERS},
26 {.func = parse_ini_protection, .token = ".ini_protection"},
27 {.func = parse_ini_entry, .token = ".ini"},
26 {NULL, NULL}}; 28 {NULL, NULL}};
27 29
28/* Top level keyword parsing */ 30/* Top level keyword parsing */
@@ -281,3 +283,14 @@ void sp_free_zstr(void *data) {
281 zend_string_release_ex((zend_string*)data, 1); 283 zend_string_release_ex((zend_string*)data, 1);
282 } 284 }
283} 285}
286
287void sp_free_ini_entry(void *data) {
288 sp_ini_entry *entry = data;
289
290 sp_free_zstr(entry->key);
291 sp_free_zstr(entry->min);
292 sp_free_zstr(entry->max);
293 sp_pcre_free(entry->regexp);
294 sp_free_zstr(entry->msg);
295 sp_free_zstr(entry->set);
296} \ No newline at end of file
diff --git a/src/sp_config.h b/src/sp_config.h
index f3b64a6..bd2530a 100644
--- a/src/sp_config.h
+++ b/src/sp_config.h
@@ -30,6 +30,8 @@ typedef enum {
30 30
31typedef enum { SP_ZEND = 0, SP_SYSLOG = 1 } sp_log_media; 31typedef enum { SP_ZEND = 0, SP_SYSLOG = 1 } sp_log_media;
32 32
33typedef enum { SP_UNSET = 0, SP_READONLY = 1, SP_READWRITE = -1 } sp_ini_permission;
34
33typedef struct { 35typedef struct {
34 int ip_version; 36 int ip_version;
35 union { 37 union {
@@ -163,6 +165,26 @@ typedef struct {
163} sp_config_upload_validation; 165} sp_config_upload_validation;
164 166
165typedef struct { 167typedef struct {
168 zend_string *key;
169 sp_ini_permission access;
170 zend_string *min;
171 zend_string *max;
172 sp_pcre *regexp;
173 bool simulation;
174 zend_string *msg;
175 zend_string *set;
176 PHP_INI_MH((*orig_onmodify));
177} sp_ini_entry;
178
179typedef struct {
180 bool enable;
181 bool simulation;
182 // sp_ini_permission access_policy;
183 bool policy_readonly;
184 HashTable *entries; // ht of sp_ini_entry
185} sp_config_ini;
186
187typedef struct {
166 sp_config_random *config_random; 188 sp_config_random *config_random;
167 sp_config_sloppy *config_sloppy; 189 sp_config_sloppy *config_sloppy;
168 sp_config_unserialize *config_unserialize; 190 sp_config_unserialize *config_unserialize;
@@ -176,6 +198,7 @@ typedef struct {
176 sp_config_eval *config_eval; 198 sp_config_eval *config_eval;
177 sp_config_wrapper *config_wrapper; 199 sp_config_wrapper *config_wrapper;
178 sp_config_session *config_session; 200 sp_config_session *config_session;
201 sp_config_ini *config_ini;
179 bool hook_execute; 202 bool hook_execute;
180 char log_media; 203 char log_media;
181 204
@@ -215,6 +238,7 @@ typedef struct {
215#define SP_TOKEN_EVAL_WHITELIST ".eval_whitelist" 238#define SP_TOKEN_EVAL_WHITELIST ".eval_whitelist"
216#define SP_TOKEN_SLOPPY_COMPARISON ".sloppy_comparison" 239#define SP_TOKEN_SLOPPY_COMPARISON ".sloppy_comparison"
217#define SP_TOKEN_ALLOW_WRAPPERS ".wrappers_whitelist" 240#define SP_TOKEN_ALLOW_WRAPPERS ".wrappers_whitelist"
241#define SP_TOKEN_INI ".ini"
218 242
219// common tokens 243// common tokens
220#define SP_TOKEN_ENABLE ".enable(" 244#define SP_TOKEN_ENABLE ".enable("
@@ -284,6 +308,6 @@ int parse_list(char *restrict, char *restrict, void *);
284void sp_free_disabled_function(void *data); 308void sp_free_disabled_function(void *data);
285void sp_free_cookie(void *data); 309void sp_free_cookie(void *data);
286void sp_free_zstr(void *data); 310void sp_free_zstr(void *data);
287 311void sp_free_ini_entry(void *data);
288 312
289#endif /* SP_CONFIG_H */ 313#endif /* SP_CONFIG_H */
diff --git a/src/sp_config_keywords.c b/src/sp_config_keywords.c
index 8080eec..e6eb05e 100644
--- a/src/sp_config_keywords.c
+++ b/src/sp_config_keywords.c
@@ -562,3 +562,80 @@ int parse_upload_validation(char *line) {
562 562
563 return ret; 563 return ret;
564} 564}
565
566int parse_ini_protection(char *line) {
567 bool disable = false, enable = false;
568 bool rw = false, ro = false; // rw is ignored, but declaring .policy_rw is valid for readability
569 sp_config_functions sp_config_ini_protection[] = {
570 {parse_empty, SP_TOKEN_ENABLE, &(enable)},
571 {parse_empty, SP_TOKEN_DISABLE, &(disable)},
572 {parse_empty, SP_TOKEN_SIMULATION, &(SNUFFLEUPAGUS_G(config).config_ini->simulation)},
573 {parse_empty, ".policy_readonly(", &ro},
574 {parse_empty, ".policy_ro(", &ro},
575 {parse_empty, ".policy_readwrite(", &rw},
576 {parse_empty, ".policy_rw(", &rw},
577 {0, 0, 0}};
578
579 int ret = parse_keywords(sp_config_ini_protection, line);
580 if (ret) { return ret; }
581
582 if (enable && disable) {
583 sp_log_err("config", "A rule can't be enabled and disabled on line %zu",
584 sp_line_no);
585 return -1;
586 }
587 if (enable || disable) {
588 SNUFFLEUPAGUS_G(config).config_ini->enable = (enable || !disable);
589 }
590
591 if (ro && rw) {
592 sp_log_err("config", "rule cannot be both read-write and read-only on line %zu", sp_line_no);
593 return -1;
594 }
595 SNUFFLEUPAGUS_G(config).config_ini->policy_readonly = ro;
596
597 return ret;
598}
599
600int parse_ini_entry(char *line) {
601 sp_ini_entry *entry = pecalloc(sizeof(sp_ini_entry), 1, 1);
602 bool rw = false, ro = false;
603
604 sp_config_functions sp_config_ini_protection[] = {
605 {parse_empty, SP_TOKEN_SIMULATION, &entry->simulation},
606 {parse_str, ".key(", &entry->key},
607 {parse_str, ".msg(", &entry->msg},
608 {parse_str, ".set(", &entry->set},
609 {parse_str, ".min(", &entry->min},
610 {parse_str, ".max(", &entry->max},
611 {parse_regexp, ".regexp(", &entry->regexp},
612 {parse_empty, ".readonly(", &ro},
613 {parse_empty, ".ro(", &ro},
614 {parse_empty, ".readwrite()", &rw},
615 {parse_empty, ".rw()", &rw},
616 {0, 0, 0}};
617
618 int ret = parse_keywords(sp_config_ini_protection, line);
619 if (ret) { goto err; }
620
621 if (!entry->key) {
622 sp_log_err("config", "A .key() must be provided on line %zu", sp_line_no);
623 ret = -1; goto err;
624 }
625
626 if (ro && rw) {
627 sp_log_err("config", "rule cannot be both read-write and read-only on line %zu", sp_line_no);
628 ret = -1; goto err;
629 }
630 entry->access = ro - rw;
631
632 zend_hash_add_ptr(SNUFFLEUPAGUS_G(config).config_ini->entries, entry->key, entry);
633 return ret;
634
635err:
636 if (entry) {
637 sp_free_ini_entry(entry);
638 pefree(entry, 1);
639 }
640 return ret;
641} \ No newline at end of file
diff --git a/src/sp_config_keywords.h b/src/sp_config_keywords.h
index a279cc9..b90c06c 100644
--- a/src/sp_config_keywords.h
+++ b/src/sp_config_keywords.h
@@ -18,5 +18,7 @@ int parse_session(char *line);
18int parse_sloppy_comparison(char *line); 18int parse_sloppy_comparison(char *line);
19int parse_wrapper_whitelist(char *line); 19int parse_wrapper_whitelist(char *line);
20int parse_log_media(char *line); 20int parse_log_media(char *line);
21int parse_ini_protection(char *line);
22int parse_ini_entry(char *line);
21 23
22#endif // __SP_CONFIG_KEYWORDS_H 24#endif // __SP_CONFIG_KEYWORDS_H
diff --git a/src/sp_ini.c b/src/sp_ini.c
new file mode 100644
index 0000000..05d7d99
--- /dev/null
+++ b/src/sp_ini.c
@@ -0,0 +1,126 @@
1#include "php_snuffleupagus.h"
2
3#define SP_INI_HAS_CHECKS_COND(entry) (entry->min || entry->max || entry->regexp)
4#define SP_INI_ACCESS_READONLY_COND(entry, cfg) (entry->access == SP_READONLY || (!entry->access && cfg->policy_readonly))
5
6static bool /* success */ sp_ini_check(zend_string *varname, zend_string *new_value, sp_ini_entry **sp_entry_p) {
7 if (!varname || ZSTR_LEN(varname) == 0) {
8 return false;
9 }
10
11 sp_config_ini *cfg = SNUFFLEUPAGUS_G(config).config_ini;
12 sp_ini_entry *entry = zend_hash_find_ptr(cfg->entries, varname);
13 if (sp_entry_p) {
14 *sp_entry_p = entry;
15 }
16 bool simulation = (cfg->simulation || (entry && entry->simulation));
17
18 if (!entry) {
19 if (cfg->policy_readonly) {
20 sp_log_auto("ini_protection", simulation, "INI setting is read-only");
21 if (simulation) { return true; }
22 return false;
23 }
24 return true;
25 }
26
27 if (SP_INI_ACCESS_READONLY_COND(entry, cfg)) {
28 sp_log_auto("ini_protection", simulation, "%s", (entry->msg ? ZSTR_VAL(entry->msg) : "INI setting is read-only"));
29 if (simulation) { return true; }
30 return false;
31 }
32
33 if (!new_value && SP_INI_HAS_CHECKS_COND(entry)) {
34 sp_log_auto("ini_protection", simulation, "new INI value must not be NULL");
35 if (simulation) { return true; }
36 return false;
37 }
38
39 if (entry->min || entry->max) {
40 zend_long lvalue = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
41 if ((entry->min && zend_atol(ZSTR_VAL(entry->min), ZSTR_LEN(entry->min)) > lvalue) ||
42 (entry->max && zend_atol(ZSTR_VAL(entry->max), ZSTR_LEN(entry->max)) < lvalue)) {
43 sp_log_auto("ini_protection", simulation, "%s", (entry->msg ? ZSTR_VAL(entry->msg) : "INI value out of range"));
44 if (simulation) { return true; }
45 return false;
46 }
47 }
48
49 if (entry->regexp) {
50 if (!sp_is_regexp_matching_len(entry->regexp, ZSTR_VAL(new_value), ZSTR_LEN(new_value))) {
51 sp_log_auto("ini_protection", simulation, "%s", (entry->msg ? ZSTR_VAL(entry->msg) : "INI value does not match regex"));
52 if (simulation) { return true; }
53 return false;
54 }
55 }
56
57 return true;
58}
59
60static PHP_INI_MH(sp_ini_onmodify) {
61 zend_ini_entry *ini_entry = entry;
62 sp_ini_entry *sp_entry = NULL;
63
64 sp_log_debug("%s =? %s", ZSTR_VAL(ini_entry->name), ZSTR_VAL(new_value));
65 if (!sp_ini_check(ini_entry->name, new_value, &sp_entry)) {
66 return FAILURE;
67 }
68
69 if (sp_entry && sp_entry->orig_onmodify) {
70 return sp_entry->orig_onmodify(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
71 }
72
73 return SUCCESS;
74}
75
76void sp_hook_ini() {
77 sp_config_ini *cfg = SNUFFLEUPAGUS_G(config).config_ini;
78 sp_ini_entry *sp_entry;
79 zend_ini_entry *ini_entry;
80 ZEND_HASH_FOREACH_PTR(cfg->entries, sp_entry)
81 sp_log_debug("hook entry `%s`", ZSTR_VAL(sp_entry->key));
82 if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), sp_entry->key)) == NULL) {
83 sp_log_warn("ini_protection", "Cannot hook INI var `%s`. Maybe a typo or the PHP extension providing this var is not loaded yet.", ZSTR_VAL(sp_entry->key));
84 continue;
85 }
86 if (SP_INI_ACCESS_READONLY_COND(sp_entry, cfg)) {
87 ini_entry->modifiable = ini_entry->orig_modifiable = 0;
88 }
89 PHP_INI_MH((*orig_onmodify)) = ini_entry->on_modify;
90
91 if (SP_INI_HAS_CHECKS_COND(sp_entry) || SP_INI_ACCESS_READONLY_COND(sp_entry, cfg)) {
92 // only hook on_modify if there is any check to perform
93 sp_entry->orig_onmodify = ini_entry->on_modify;
94 ini_entry->on_modify = sp_ini_onmodify;
95 }
96
97 if (sp_entry->set) {
98 zend_string *duplicate = zend_string_copy(sp_entry->set);
99
100 if (!orig_onmodify || orig_onmodify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, ZEND_INI_STAGE_STARTUP) == SUCCESS) {
101 ini_entry->value = duplicate;
102 } else {
103 zend_string_release(duplicate);
104 sp_log_warn("ini_protection", "Failed to set INI var `%s`.", ZSTR_VAL(sp_entry->key));
105 continue;
106 }
107 }
108 ZEND_HASH_FOREACH_END();
109}
110
111void sp_unhook_ini() {
112 sp_ini_entry *sp_entry;
113 zend_ini_entry *ini_entry;
114 ZEND_HASH_FOREACH_PTR(SNUFFLEUPAGUS_G(config).config_ini->entries, sp_entry)
115 if (!sp_entry->orig_onmodify) {
116 // not hooked or no original onmodify
117 continue;
118 }
119 if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), sp_entry->key)) == NULL) {
120 // unusual. ini entry is missing.
121 continue;
122 }
123 ini_entry->on_modify = sp_entry->orig_onmodify;
124 sp_entry->orig_onmodify = NULL;
125 ZEND_HASH_FOREACH_END();
126}
diff --git a/src/sp_ini.h b/src/sp_ini.h
new file mode 100644
index 0000000..5869539
--- /dev/null
+++ b/src/sp_ini.h
@@ -0,0 +1,2 @@
1void sp_hook_ini();
2void sp_unhook_ini(); \ No newline at end of file