summaryrefslogtreecommitdiff
path: root/src/sp_ini.c
diff options
context:
space:
mode:
authorBen Fuhrmannek2021-08-06 20:23:52 +0200
committerBen Fuhrmannek2021-08-06 20:23:52 +0200
commit2392c46836ceea520fa2a45369c8d638aadb943c (patch)
treefe4b5eb10a49b2b45c4d7bc24de54fd3888c168e /src/sp_ini.c
parent260f17f112e2d081783c6dc102f81666ac2435d9 (diff)
implemented ini settings protection
Diffstat (limited to 'src/sp_ini.c')
-rw-r--r--src/sp_ini.c126
1 files changed, 126 insertions, 0 deletions
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}