summaryrefslogtreecommitdiff
path: root/src/sp_ini.c
diff options
context:
space:
mode:
authorjvoisin2022-03-20 18:20:45 +0100
committerjvoisin2022-03-20 18:20:45 +0100
commit81dd7f2ef07af306fe83d7755cbac4529aa9fc8d (patch)
tree32cc44c6231b30db5ac7b15699297863460784aa /src/sp_ini.c
parent83b01942dfc80474cc05e09aeef4b44307a7120b (diff)
parentc38df1077a6c1dfbca1baca049214d053e2e7684 (diff)
Merge remote-tracking branch 'sektioneins/master'
Diffstat (limited to 'src/sp_ini.c')
-rw-r--r--src/sp_ini.c142
1 files changed, 142 insertions, 0 deletions
diff --git a/src/sp_ini.c b/src/sp_ini.c
new file mode 100644
index 0000000..7fec297
--- /dev/null
+++ b/src/sp_ini.c
@@ -0,0 +1,142 @@
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
6#define sp_log_auto2(feature, is_simulation, drop, ...) \
7 sp_log_msgf(feature, ((is_simulation || !drop) ? SP_LOG_WARN : SP_LOG_ERROR), \
8 (is_simulation ? SP_TYPE_SIMULATION : (drop ? SP_TYPE_DROP : SP_TYPE_LOG)), \
9 __VA_ARGS__)
10#define sp_log_ini_check_violation(...) if (simulation || cfg->policy_drop || (entry && entry->drop) || !cfg->policy_silent_fail) { \
11 sp_log_auto2("ini_protection", simulation, (cfg->policy_drop || (entry && entry->drop)), __VA_ARGS__); \
12 }
13
14
15static bool /* success */ sp_ini_check(zend_string *varname, zend_string *new_value, sp_ini_entry **sp_entry_p) {
16 if (!varname || ZSTR_LEN(varname) == 0) {
17 return false;
18 }
19
20 sp_config_ini *cfg = &(SPCFG(ini));
21 sp_ini_entry *entry = zend_hash_find_ptr(cfg->entries, varname);
22 if (sp_entry_p) {
23 *sp_entry_p = entry;
24 }
25 bool simulation = (cfg->simulation || (entry && entry->simulation));
26
27 if (!entry) {
28 if (cfg->policy_readonly) {
29 if (!cfg->policy_silent_ro) {
30 sp_log_ini_check_violation("INI setting is read-only");
31 }
32 return simulation;
33 }
34 return true;
35 }
36
37 // we have an entry.
38
39 if (SP_INI_ACCESS_READONLY_COND(entry, cfg)) {
40 if (!cfg->policy_silent_ro) {
41 sp_log_ini_check_violation("%s", (entry->msg ? ZSTR_VAL(entry->msg) : "INI setting is read-only"));
42 }
43 return simulation;
44 }
45
46 if (!new_value || ZSTR_LEN(new_value) == 0) {
47 if (entry->allow_null) {
48 return true; // allow NULL value and skip other tests
49 }
50 if (SP_INI_HAS_CHECKS_COND(entry)) {
51 sp_log_ini_check_violation("new INI value must not be NULL or empty");
52 return simulation;
53 }
54 return true; // no new_value, but no checks to perform
55 }
56
57 // we have a new_value.
58
59 if (entry->min || entry->max) {
60 zend_long lvalue = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
61 if ((entry->min && zend_atol(ZSTR_VAL(entry->min), ZSTR_LEN(entry->min)) > lvalue) ||
62 (entry->max && zend_atol(ZSTR_VAL(entry->max), ZSTR_LEN(entry->max)) < lvalue)) {
63 sp_log_ini_check_violation("%s", (entry->msg ? ZSTR_VAL(entry->msg) : "INI value out of range"));
64 return simulation;
65 }
66 }
67
68 if (entry->regexp) {
69 if (!sp_is_regexp_matching_zstr(entry->regexp, new_value)) {
70 sp_log_ini_check_violation("%s", (entry->msg ? ZSTR_VAL(entry->msg) : "INI value does not match regex"));
71 return simulation;
72 }
73 }
74
75 return true;
76}
77
78static PHP_INI_MH(sp_ini_onmodify) {
79 zend_ini_entry *ini_entry = entry;
80 sp_ini_entry *sp_entry = NULL;
81
82 if (!sp_ini_check(ini_entry->name, new_value, &sp_entry)) {
83 return FAILURE;
84 }
85
86 if (sp_entry && sp_entry->orig_onmodify) {
87 return sp_entry->orig_onmodify(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
88 }
89
90 return SUCCESS;
91}
92
93void sp_hook_ini() {
94 sp_config_ini *cfg = &(SPCFG(ini));
95 sp_ini_entry *sp_entry;
96 zend_ini_entry *ini_entry;
97 ZEND_HASH_FOREACH_PTR(cfg->entries, sp_entry)
98 if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), sp_entry->key)) == NULL) {
99 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));
100 continue;
101 }
102 if (SP_INI_ACCESS_READONLY_COND(sp_entry, cfg) && (cfg->policy_silent_ro || cfg->policy_silent_fail) && !sp_entry->drop && !(sp_entry->simulation || cfg->simulation)) {
103 ini_entry->modifiable = ini_entry->orig_modifiable = 0;
104 }
105 PHP_INI_MH((*orig_onmodify)) = ini_entry->on_modify;
106
107 if (SP_INI_HAS_CHECKS_COND(sp_entry) || SP_INI_ACCESS_READONLY_COND(sp_entry, cfg)) {
108 // only hook on_modify if there is any check to perform
109 sp_entry->orig_onmodify = ini_entry->on_modify;
110 ini_entry->on_modify = sp_ini_onmodify;
111 }
112
113 if (sp_entry->set) {
114 zend_string *duplicate = zend_string_copy(sp_entry->set);
115
116 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) {
117 ini_entry->value = duplicate;
118 } else {
119 zend_string_release(duplicate);
120 sp_log_warn("ini_protection", "Failed to set INI var `%s`.", ZSTR_VAL(sp_entry->key));
121 continue;
122 }
123 }
124 ZEND_HASH_FOREACH_END();
125}
126
127void sp_unhook_ini() {
128 sp_ini_entry *sp_entry;
129 zend_ini_entry *ini_entry;
130 ZEND_HASH_FOREACH_PTR(SPCFG(ini).entries, sp_entry)
131 if (!sp_entry->orig_onmodify) {
132 // not hooked or no original onmodify
133 continue;
134 }
135 if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), sp_entry->key)) == NULL) {
136 // unusual. ini entry is missing.
137 continue;
138 }
139 ini_entry->on_modify = sp_entry->orig_onmodify;
140 sp_entry->orig_onmodify = NULL;
141 ZEND_HASH_FOREACH_END();
142}