summaryrefslogtreecommitdiff
path: root/src/sp_config_keywords.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_config_keywords.c
parent83b01942dfc80474cc05e09aeef4b44307a7120b (diff)
parentc38df1077a6c1dfbca1baca049214d053e2e7684 (diff)
Merge remote-tracking branch 'sektioneins/master'
Diffstat (limited to 'src/sp_config_keywords.c')
-rw-r--r--src/sp_config_keywords.c647
1 files changed, 297 insertions, 350 deletions
diff --git a/src/sp_config_keywords.c b/src/sp_config_keywords.c
index 325ee6c..9a9b608 100644
--- a/src/sp_config_keywords.c
+++ b/src/sp_config_keywords.c
@@ -1,321 +1,246 @@
1#include "php_snuffleupagus.h" 1#include "php_snuffleupagus.h"
2 2
3static int parse_enable(char *line, bool *restrict retval, 3#define SP_SET_ENABLE_DISABLE(enable, disable, varname) \
4 bool *restrict simulation) { 4 if (enable && disable) { \
5 sp_log_err("config", "A rule can't be enabled and disabled on line %zu", parsed_rule->lineno); \
6 return SP_PARSER_ERROR; \
7 } \
8 if (enable || disable) { \
9 (varname) = (enable || !disable); \
10 }
11
12#define SP_PROCESS_CONFIG_KEYWORDS(CMD) if (sp_process_rule(&(parsed_rule[1]), config_keywords) != SUCCESS) { CMD; }
13#define SP_PROCESS_CONFIG_KEYWORDS_ERR() SP_PROCESS_CONFIG_KEYWORDS(return SP_PARSER_ERROR)
14
15SP_PARSE_FN(parse_enable) {
5 bool enable = false, disable = false; 16 bool enable = false, disable = false;
6 sp_config_functions sp_config_funcs[] = { 17 sp_config_keyword config_keywords[] = {
7 {parse_empty, SP_TOKEN_ENABLE, &(enable)}, 18 {parse_empty, SP_TOKEN_ENABLE, &(enable)},
8 {parse_empty, SP_TOKEN_DISABLE, &(disable)}, 19 {parse_empty, SP_TOKEN_DISABLE, &(disable)},
9 {parse_empty, SP_TOKEN_SIMULATION, simulation},
10 {0, 0, 0}}; 20 {0, 0, 0}};
11 21
12 int ret = parse_keywords(sp_config_funcs, line); 22 SP_PROCESS_CONFIG_KEYWORDS_ERR();
13
14 if (0 != ret) {
15 return ret;
16 }
17
18 if (!(enable ^ disable)) {
19 sp_log_err("config", "A rule can't be enabled and disabled on line %zu",
20 sp_line_no);
21 return -1;
22 }
23 23
24 *retval = enable; 24 SP_SET_ENABLE_DISABLE(enable, disable, *(bool*)retval);
25 25
26 return ret; 26 return SP_PARSER_STOP;
27} 27}
28 28
29int parse_session(char *line) { 29SP_PARSE_FN(parse_session) {
30 sp_config_session *session = pecalloc(sizeof(sp_config_session), 1, 0); 30 sp_config_session *cfg = retval;
31 31
32 sp_config_functions sp_config_funcs_session_encryption[] = { 32 sp_config_keyword config_keywords[] = {
33 {parse_empty, SP_TOKEN_ENCRYPT, &(session->encrypt)}, 33 {parse_empty, SP_TOKEN_ENCRYPT, &(cfg->encrypt)},
34 {parse_empty, SP_TOKEN_SIMULATION, &(session->simulation)}, 34 {parse_empty, SP_TOKEN_SIMULATION, &(cfg->simulation)},
35 {parse_empty, SP_TOKEN_SIM, &(cfg->simulation)},
36 {parse_ulong, SP_TOKEN_SID_MIN_LENGTH, &(cfg->sid_min_length)},
37 {parse_ulong, SP_TOKEN_SID_MAX_LENGTH, &(cfg->sid_max_length)},
35 {0, 0, 0}}; 38 {0, 0, 0}};
36 int ret = parse_keywords(sp_config_funcs_session_encryption, line);
37 if (0 != ret) {
38 return ret;
39 }
40 39
41#if (!HAVE_PHP_SESSION || defined(COMPILE_DL_SESSION)) 40 SP_PROCESS_CONFIG_KEYWORDS_ERR();
42 sp_log_err(
43 "config",
44 "You're trying to use the session cookie encryption feature "
45 "on line %zu without having session support statically built into PHP. "
46 "This isn't supported, see "
47 "https://github.com/jvoisin/snuffleupagus/issues/278 for details.",
48 sp_line_no);
49 pefree(session, 0);
50 return -1;
51#endif
52 41
53 if (session->encrypt) { 42 if (cfg->encrypt) {
54 if (0 == (SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var)) { 43 if (!SPCFG(cookies_env_var)) {
55 sp_log_err( 44 sp_log_err("config", "You're trying to use the session cookie encryption feature "
56 "config", 45 "on line %zu without having set the `.cookie_env_var` option in "
57 "You're trying to use the session cookie encryption feature " 46 "`sp.global`: please set it first", parsed_rule->lineno);
58 "on line %zu without having set the `.cookie_env_var` option in" 47 return SP_PARSER_ERROR;
59 "`sp.global`: please set it first", 48 } else if (!SPCFG(encryption_key)) {
60 sp_line_no); 49 sp_log_err("config", "You're trying to use the session cookie encryption feature "
61 pefree(session, 0); 50 "on line %zu without having set the `.secret_key` option in "
62 return -1; 51 "`sp.global`: please set it first", parsed_rule->lineno);
63 } else if (0 == 52 return SP_PARSER_ERROR;
64 (SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key)) {
65 sp_log_err("config",
66 "You're trying to use the session cookie encryption feature "
67 "on line %zu without having set the `.secret_key` option in"
68 "`sp.global`: please set it first",
69 sp_line_no);
70 pefree(session, 0);
71 return -1;
72 } 53 }
73 } 54 }
74 55
75 SNUFFLEUPAGUS_G(config).config_session->encrypt = session->encrypt; 56 return SP_PARSER_STOP;
76 SNUFFLEUPAGUS_G(config).config_session->simulation = session->simulation;
77 pefree(session, 0);
78 return ret;
79}
80
81int parse_random(char *line) {
82 return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_random->enable),
83 NULL);
84} 57}
85 58
86int parse_log_media(char *line) { 59SP_PARSEKW_FN(parse_log_media) {
87 size_t consumed = 0; 60 SP_PARSE_ARG(value);
88 zend_string *value = 61
89 get_param(&consumed, line, SP_TYPE_STR, SP_TOKEN_LOG_MEDIA); 62 if (!strcmp(ZSTR_VAL(value), "php")) {
90 63 *(char*)retval = SP_ZEND;
91 if (value) { 64 zend_string_release_ex(value, 1);
92 if (!strcmp(ZSTR_VAL(value), "php")) { 65 return SP_PARSER_SUCCESS;
93 SNUFFLEUPAGUS_G(config).log_media = SP_ZEND; 66 } else if (!strcmp(ZSTR_VAL(value), "syslog")) {
94 return 0; 67 *(char*)retval = SP_SYSLOG;
95 } else if (!strcmp(ZSTR_VAL(value), "syslog")) { 68 zend_string_release_ex(value, 1);
96 SNUFFLEUPAGUS_G(config).log_media = SP_SYSLOG; 69 return SP_PARSER_SUCCESS;
97 return 0;
98 }
99 } 70 }
100 sp_log_err("config", "%s) only supports 'syslog' or 'php', on line %zu",
101 SP_TOKEN_LOG_MEDIA, sp_line_no);
102 return -1;
103}
104 71
105int parse_sloppy_comparison(char *line) { 72 sp_log_err("config", "." SP_TOKEN_LOG_MEDIA "() only supports 'syslog' or 'php' on line %zu", kw->lineno);
106 return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_sloppy->enable),
107 NULL);
108}
109
110int parse_disable_xxe(char *line) {
111 return parse_enable(
112 line, &(SNUFFLEUPAGUS_G(config).config_disable_xxe->enable), NULL);
113}
114
115int parse_auto_cookie_secure(char *line) {
116 return parse_enable(
117 line, &(SNUFFLEUPAGUS_G(config).config_auto_cookie_secure->enable), NULL);
118}
119 73
120int parse_global_strict(char *line) { 74 return SP_PARSER_ERROR;
121 return parse_enable(
122 line, &(SNUFFLEUPAGUS_G(config).config_global_strict->enable), NULL);
123} 75}
124 76
125int parse_unserialize(char *line) { 77SP_PARSE_FN(parse_unserialize) {
126 bool enable = false, disable = false; 78 bool enable = false, disable = false;
127 sp_config_unserialize *unserialize = 79 sp_config_unserialize *cfg = (sp_config_unserialize*)retval;
128 SNUFFLEUPAGUS_G(config).config_unserialize;
129 80
130 sp_config_functions sp_config_funcs[] = { 81 sp_config_keyword config_keywords[] = {
131 {parse_empty, SP_TOKEN_ENABLE, &(enable)}, 82 {parse_empty, SP_TOKEN_ENABLE, &(enable)},
132 {parse_empty, SP_TOKEN_DISABLE, &(disable)}, 83 {parse_empty, SP_TOKEN_DISABLE, &(disable)},
133 {parse_empty, SP_TOKEN_SIMULATION, &(unserialize->simulation)}, 84 {parse_empty, SP_TOKEN_SIMULATION, &(cfg->simulation)},
134 {parse_str, SP_TOKEN_DUMP, &(unserialize->dump)}, 85 {parse_empty, SP_TOKEN_SIM, &(cfg->simulation)},
86 {parse_str, SP_TOKEN_DUMP, &(cfg->dump)},
135 {0, 0, 0}}; 87 {0, 0, 0}};
136 88
137 unserialize->textual_representation = zend_string_init(line, strlen(line), 1); 89 SP_PROCESS_CONFIG_KEYWORDS_ERR();
138 90
139 int ret = parse_keywords(sp_config_funcs, line); 91 SP_SET_ENABLE_DISABLE(enable, disable, cfg->enable);
140 if (0 != ret) {
141 return ret;
142 }
143
144 if (!(enable ^ disable)) {
145 sp_log_err("config", "A rule can't be enabled and disabled on line %zu",
146 sp_line_no);
147 return -1;
148 }
149 92
150 SNUFFLEUPAGUS_G(config).config_unserialize->enable = enable; 93 cfg->textual_representation = sp_get_textual_representation(parsed_rule);
151 94
152 return ret; 95 return SP_PARSER_STOP;
153} 96}
154 97
155int parse_readonly_exec(char *line) { 98SP_PARSE_FN(parse_readonly_exec) {
156 bool enable = false, disable = false; 99 bool enable = false, disable = false;
157 sp_config_readonly_exec *readonly_exec = 100 sp_config_readonly_exec *cfg = (sp_config_readonly_exec*)retval;
158 SNUFFLEUPAGUS_G(config).config_readonly_exec;
159 101
160 sp_config_functions sp_config_funcs[] = { 102 sp_config_keyword config_keywords[] = {
161 {parse_empty, SP_TOKEN_ENABLE, &(enable)}, 103 {parse_empty, SP_TOKEN_ENABLE, &(enable)},
162 {parse_empty, SP_TOKEN_DISABLE, &(disable)}, 104 {parse_empty, SP_TOKEN_DISABLE, &(disable)},
163 {parse_empty, SP_TOKEN_SIMULATION, &(readonly_exec->simulation)}, 105 {parse_empty, SP_TOKEN_SIMULATION, &(cfg->simulation)},
164 {parse_str, SP_TOKEN_DUMP, &(readonly_exec->dump)}, 106 {parse_empty, SP_TOKEN_SIM, &(cfg->simulation)},
107 {parse_str, SP_TOKEN_DUMP, &(cfg->dump)},
165 {0, 0, 0}}; 108 {0, 0, 0}};
166 109
167 readonly_exec->textual_representation = 110 SP_PROCESS_CONFIG_KEYWORDS_ERR();
168 zend_string_init(line, strlen(line), 1);
169 int ret = parse_keywords(sp_config_funcs, line);
170
171 if (0 != ret) {
172 return ret;
173 }
174 111
175 if (!(enable ^ disable)) { 112 cfg->textual_representation = sp_get_textual_representation(parsed_rule);
176 sp_log_err("config", "A rule can't be enabled and disabled on line %zu",
177 sp_line_no);
178 return -1;
179 }
180 113
181 SNUFFLEUPAGUS_G(config).config_readonly_exec->enable = enable; 114 SP_SET_ENABLE_DISABLE(enable, disable, cfg->enable);
182 115
183 return ret; 116 return SP_PARSER_STOP;
184} 117}
185 118
186int parse_global(char *line) { 119SP_PARSE_FN(parse_global) {
187 sp_config_functions sp_config_funcs_global[] = { 120 sp_config_keyword config_keywords[] = {
188 {parse_str, SP_TOKEN_ENCRYPTION_KEY, 121 {parse_str, SP_TOKEN_ENCRYPTION_KEY, &(SPCFG(encryption_key))},
189 &(SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key)}, 122 {parse_str, SP_TOKEN_ENV_VAR, &(SPCFG(cookies_env_var))},
190 {parse_str, SP_TOKEN_ENV_VAR, 123 {parse_log_media, SP_TOKEN_LOG_MEDIA, &(SPCFG(log_media))},
191 &(SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var)}, 124 {parse_ulong, SP_TOKEN_MAX_EXECUTION_DEPTH, &(SPCFG(max_execution_depth))},
125 {parse_enable, SP_TOKEN_SERVER_ENCODE, &(SPCFG(server_encode))},
126 {parse_enable, SP_TOKEN_SERVER_STRIP, &(SPCFG(server_strip))},
127 {parse_enable, SP_TOKEN_SHOW_OLD_PHP_WARNING, &(SPCFG(show_old_php_warning))},
192 {0, 0, 0}}; 128 {0, 0, 0}};
193 return parse_keywords(sp_config_funcs_global, line); 129
130 SP_PROCESS_CONFIG_KEYWORDS_ERR();
131
132 if (SPCFG(encryption_key)) {
133 if (ZSTR_LEN(SPCFG(encryption_key)) < 10) {
134 sp_log_err("config", "The encryption key set on line %zu is too short. please use at least 10 bytes", parsed_rule->lineno);
135 return SP_PARSER_ERROR;
136 }
137 if (zend_string_equals_literal(SPCFG(encryption_key), "YOU _DO_ NEED TO CHANGE THIS WITH SOME RANDOM CHARACTERS.") ||
138 zend_string_equals_literal(SPCFG(encryption_key), "c6a0e02b3b818f7559d5f85303d8fe44")) {
139 sp_log_err("config", "The encryption key set on line %zu is an unchanged dummy value. please use a unique secret.", parsed_rule->lineno);
140 return SP_PARSER_ERROR;
141 }
142 }
143
144 return SP_PARSER_STOP;
194} 145}
195 146
196static int parse_eval_filter_conf(char *line, sp_list_node **list) { 147SP_PARSE_FN(parse_eval_filter_conf) {
197 sp_config_eval *eval = SNUFFLEUPAGUS_G(config).config_eval; 148 sp_config_eval *cfg = &(SPCFG(eval));
198 149
199 sp_config_functions sp_config_funcs[] = { 150 sp_config_keyword config_keywords[] = {
200 {parse_list, SP_TOKEN_LIST, list}, 151 {parse_list, SP_TOKEN_LIST, retval},
201 {parse_empty, SP_TOKEN_SIMULATION, 152 {parse_empty, SP_TOKEN_SIMULATION, &(cfg->simulation)},
202 &(SNUFFLEUPAGUS_G(config).config_eval->simulation)}, 153 {parse_empty, SP_TOKEN_SIM, &(cfg->simulation)},
203 {parse_str, SP_TOKEN_DUMP, &(SNUFFLEUPAGUS_G(config).config_eval->dump)}, 154 {parse_str, SP_TOKEN_DUMP, &(cfg->dump)},
204 {0, 0, 0}}; 155 {0, 0, 0}};
205 156
206 eval->textual_representation = zend_string_init(line, strlen(line), 1); 157 SP_PROCESS_CONFIG_KEYWORDS_ERR();
207 158
208 int ret = parse_keywords(sp_config_funcs, line); 159 cfg->textual_representation = sp_get_textual_representation(parsed_rule);
209 if (0 != ret) {
210 return ret;
211 }
212 160
213 return SUCCESS; 161 return SP_PARSER_STOP;
214} 162}
215 163
216int parse_wrapper_whitelist(char *line) { 164SP_PARSE_FN(parse_wrapper_whitelist) {
217 SNUFFLEUPAGUS_G(config).config_wrapper->enabled = true; 165 sp_config_wrapper *cfg = (sp_config_wrapper*)retval;
218 sp_config_functions sp_config_funcs[] = { 166
219 {parse_list, SP_TOKEN_LIST, 167 sp_config_keyword config_keywords[] = {
220 &SNUFFLEUPAGUS_G(config).config_wrapper->whitelist}, 168 {parse_list, SP_TOKEN_LIST, &cfg->whitelist},
221 {0, 0, 0}}; 169 {0, 0, 0}};
222 int ret = parse_keywords(sp_config_funcs, line);
223 if (0 != ret) {
224 return ret;
225 }
226 return SUCCESS;
227}
228 170
229int parse_eval_blacklist(char *line) { 171 SP_PROCESS_CONFIG_KEYWORDS_ERR();
230 return parse_eval_filter_conf( 172
231 line, &SNUFFLEUPAGUS_G(config).config_eval->blacklist); 173 cfg->enabled = true;
232}
233 174
234int parse_eval_whitelist(char *line) { 175 return SP_PARSER_STOP;
235 return parse_eval_filter_conf(
236 line, &SNUFFLEUPAGUS_G(config).config_eval->whitelist);
237} 176}
238 177
239int parse_cookie(char *line) { 178SP_PARSE_FN(parse_cookie) {
240 int ret = 0;
241 zend_string *samesite = NULL; 179 zend_string *samesite = NULL;
242 sp_cookie *cookie = pecalloc(sizeof(sp_cookie), 1, 1); 180 sp_cookie *cookie = pecalloc(sizeof(sp_cookie), 1, 1);
243 181
244 sp_config_functions sp_config_funcs_cookie_encryption[] = { 182 sp_config_keyword config_keywords[] = {
245 {parse_str, SP_TOKEN_NAME, &(cookie->name)}, 183 {parse_str, SP_TOKEN_NAME, &(cookie->name)},
246 {parse_regexp, SP_TOKEN_NAME_REGEXP, &(cookie->name_r)}, 184 {parse_regexp, SP_TOKEN_NAME_REGEXP, &(cookie->name_r)},
247 {parse_str, SP_TOKEN_SAMESITE, &samesite}, 185 {parse_str, SP_TOKEN_SAMESITE, &samesite},
248 {parse_empty, SP_TOKEN_ENCRYPT, &cookie->encrypt}, 186 {parse_empty, SP_TOKEN_ENCRYPT, &cookie->encrypt},
249 {parse_empty, SP_TOKEN_SIMULATION, &cookie->simulation}, 187 {parse_empty, SP_TOKEN_SIMULATION, &cookie->simulation},
188 {parse_empty, SP_TOKEN_SIM, &cookie->simulation},
250 {0, 0, 0}}; 189 {0, 0, 0}};
251 190
252 ret = parse_keywords(sp_config_funcs_cookie_encryption, line); 191 SP_PROCESS_CONFIG_KEYWORDS(goto err);
253 if (0 != ret) {
254 return ret;
255 }
256 192
257 if (cookie->encrypt) { 193 if (cookie->encrypt) {
258 if (0 == (SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var)) { 194 if (!SPCFG(cookies_env_var)) {
259 sp_log_err( 195 sp_log_err("config", "You're trying to use the cookie encryption feature on line %zu "
260 "config", 196 "without having set the `." SP_TOKEN_ENV_VAR "` option in `sp.global`: please set it first", parsed_rule->lineno);
261 "You're trying to use the cookie encryption feature" 197 goto err;
262 "on line %zu without having set the `.cookie_env_var` option in" 198 } else if (!SPCFG(encryption_key)) {
263 "`sp.global`: please set it first", 199 sp_log_err("config", "You're trying to use the cookie encryption feature "
264 sp_line_no); 200 "on line %zu without having set the `." SP_TOKEN_ENCRYPTION_KEY "` option in "
265 return -1; 201 "`sp." SP_TOKEN_GLOBAL "`: please set it first", parsed_rule->lineno);
266 } else if (0 == 202 goto err;
267 (SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key)) {
268 sp_log_err(
269 "config",
270 "You're trying to use the cookie encryption feature"
271 "on line %zu without having set the `.encryption_key` option in"
272 "`sp.global`: please set it first",
273 sp_line_no);
274 return -1;
275 } 203 }
276 } else if (!samesite) { 204 } else if (!samesite) {
277 sp_log_err("config", 205 sp_log_err("config", "You must specify a at least one action to a cookie on line %zu", parsed_rule->lineno);
278 "You must specify a at least one action to a cookie on line " 206 goto err;
279 "%zu",
280 sp_line_no);
281 return -1;
282 } 207 }
283 if ((!cookie->name || 0 == ZSTR_LEN(cookie->name)) && !cookie->name_r) { 208 if ((!cookie->name || 0 == ZSTR_LEN(cookie->name)) && !cookie->name_r) {
284 sp_log_err("config", 209 sp_log_err("config", "You must specify a cookie name/regexp on line %zu", parsed_rule->lineno);
285 "You must specify a cookie name/regexp on line " 210 goto err;
286 "%zu",
287 sp_line_no);
288 return -1;
289 } 211 }
290 if (cookie->name && cookie->name_r) { 212 if (cookie->name && cookie->name_r) {
291 sp_log_err("config", 213 sp_log_err("config", "name and name_r are mutually exclusive on line %zu", parsed_rule->lineno);
292 "name and name_r are mutually exclusive on line " 214 goto err;
293 "%zu",
294 sp_line_no);
295 return -1;
296 } 215 }
297 if (samesite) { 216 if (samesite) {
298 if (zend_string_equals_literal_ci(samesite, SP_TOKEN_SAMESITE_LAX)) { 217 if (zend_string_equals_literal_ci(samesite, SP_TOKEN_SAMESITE_LAX)) {
299 cookie->samesite = lax; 218 cookie->samesite = lax;
300 } else if (zend_string_equals_literal_ci(samesite, 219 } else if (zend_string_equals_literal_ci(samesite, SP_TOKEN_SAMESITE_STRICT)) {
301 SP_TOKEN_SAMESITE_STRICT)) {
302 cookie->samesite = strict; 220 cookie->samesite = strict;
303 } else { 221 } else {
304 sp_log_err( 222 sp_log_err("config", "'%s' is an invalid value to samesite (expected " SP_TOKEN_SAMESITE_LAX " or " SP_TOKEN_SAMESITE_STRICT ") on line %zu",
305 "config", 223 ZSTR_VAL(samesite), parsed_rule->lineno);
306 "%s is an invalid value to samesite (expected %s or %s) on line " 224 goto err;
307 "%zu",
308 ZSTR_VAL(samesite), SP_TOKEN_SAMESITE_LAX, SP_TOKEN_SAMESITE_STRICT,
309 sp_line_no);
310 return -1;
311 } 225 }
312 } 226 }
313 SNUFFLEUPAGUS_G(config).config_cookie->cookies = 227
314 sp_list_insert(SNUFFLEUPAGUS_G(config).config_cookie->cookies, cookie); 228 SPCFG(cookie).cookies = sp_list_insert(SPCFG(cookie).cookies, cookie);
315 return SUCCESS; 229
230 return SP_PARSER_STOP;
231
232err:
233 if (samesite) {
234 zend_string_release(samesite);
235 }
236 if (cookie) {
237 sp_free_cookie(cookie);
238 pefree(cookie, 1);
239 }
240 return SP_PARSER_ERROR;
316} 241}
317 242
318int add_df_to_hashtable(HashTable *ht, sp_disabled_function *df) { 243static int add_df_to_hashtable(HashTable *ht, sp_disabled_function *df) {
319 zval *list = zend_hash_find(ht, df->function); 244 zval *list = zend_hash_find(ht, df->function);
320 245
321 if (NULL == list) { 246 if (NULL == list) {
@@ -326,19 +251,19 @@ int add_df_to_hashtable(HashTable *ht, sp_disabled_function *df) {
326 return SUCCESS; 251 return SUCCESS;
327} 252}
328 253
329int parse_disabled_functions(char *line) { 254SP_PARSE_FN(parse_disabled_functions) {
330 int ret = 0; 255 int ret = SP_PARSER_ERROR;
331 bool enable = true, disable = false, allow = false, drop = false; 256 bool enable = false, disable = false, allow = false, drop = false;
332 zend_string *pos = NULL, *var = NULL, *param = NULL; 257 zend_string *var = NULL, *param = NULL;
333 zend_string *line_number = NULL;
334 sp_disabled_function *df = pecalloc(sizeof(*df), 1, 1); 258 sp_disabled_function *df = pecalloc(sizeof(*df), 1, 1);
335 df->pos = -1; 259 df->pos = -1;
336 260
337 sp_config_functions sp_config_funcs_disabled_functions[] = { 261 sp_config_keyword config_keywords[] = {
338 {parse_empty, SP_TOKEN_ENABLE, &(enable)}, 262 {parse_empty, SP_TOKEN_ENABLE, &(enable)},
339 {parse_empty, SP_TOKEN_DISABLE, &(disable)}, 263 {parse_empty, SP_TOKEN_DISABLE, &(disable)},
340 {parse_str, SP_TOKEN_ALIAS, &(df->alias)}, 264 {parse_str, SP_TOKEN_ALIAS, &(df->alias)},
341 {parse_empty, SP_TOKEN_SIMULATION, &(df->simulation)}, 265 {parse_empty, SP_TOKEN_SIMULATION, &(df->simulation)},
266 {parse_empty, SP_TOKEN_SIM, &(df->simulation)},
342 {parse_str, SP_TOKEN_FILENAME, &(df->filename)}, 267 {parse_str, SP_TOKEN_FILENAME, &(df->filename)},
343 {parse_regexp, SP_TOKEN_FILENAME_REGEXP, &(df->r_filename)}, 268 {parse_regexp, SP_TOKEN_FILENAME_REGEXP, &(df->r_filename)},
344 {parse_str, SP_TOKEN_FUNCTION, &(df->function)}, 269 {parse_str, SP_TOKEN_FUNCTION, &(df->function)},
@@ -359,86 +284,54 @@ int parse_disabled_functions(char *line) {
359 {parse_regexp, SP_TOKEN_RET_REGEXP, &(df->r_ret)}, 284 {parse_regexp, SP_TOKEN_RET_REGEXP, &(df->r_ret)},
360 {parse_php_type, SP_TOKEN_RET_TYPE, &(df->ret_type)}, 285 {parse_php_type, SP_TOKEN_RET_TYPE, &(df->ret_type)},
361 {parse_str, SP_TOKEN_LOCAL_VAR, &(var)}, 286 {parse_str, SP_TOKEN_LOCAL_VAR, &(var)},
362 {parse_str, SP_TOKEN_VALUE_ARG_POS, &(pos)}, 287 {parse_int, SP_TOKEN_VALUE_ARG_POS, &(df->pos)},
363 {parse_str, SP_TOKEN_LINE_NUMBER, &(line_number)}, 288 {parse_ulong, SP_TOKEN_LINE_NUMBER, &(df->line)},
364 {0, 0, 0}}; 289 {0, 0, 0}};
365 290
366 ret = parse_keywords(sp_config_funcs_disabled_functions, line); 291 SP_PROCESS_CONFIG_KEYWORDS(goto out);
367 292
368 if (0 != ret) { 293 SP_SET_ENABLE_DISABLE(enable, disable, enable);
369 return ret; 294 if (disable) {
295 ret = SP_PARSER_STOP; goto out;
370 } 296 }
371 297
372#define MUTUALLY_EXCLUSIVE(X, Y, STR1, STR2) \ 298#define MUTUALLY_EXCLUSIVE(X, Y, STR1, STR2) \
373 if (X && Y) { \ 299 if (X && Y) { \
374 sp_log_err("config", \ 300 sp_log_err("config", "Invalid configuration line for 'sp.disabled_functions': '.%s' and '.%s' are mutually exclusive on line %zu", STR1, STR2, parsed_rule->lineno); \
375 "Invalid configuration line: 'sp.disabled_functions%s': " \ 301 goto out; \
376 "'.%s' and '.%s' are mutually exclusive on line %zu", \
377 line, STR1, STR2, sp_line_no); \
378 return -1; \
379 } 302 }
380 303
381 MUTUALLY_EXCLUSIVE(df->r_value, df->value, "r_value", "regexp"); 304 MUTUALLY_EXCLUSIVE(df->value, df->r_value, "value", "value_r");
382 MUTUALLY_EXCLUSIVE(df->r_function, df->function, "r_function", "function"); 305 MUTUALLY_EXCLUSIVE(df->r_function, df->function, "function", "function_r");
383 MUTUALLY_EXCLUSIVE(df->r_filename, df->filename, "r_filename", "filename"); 306 MUTUALLY_EXCLUSIVE(df->filename, df->r_filename, "filename", "filename_r");
384 MUTUALLY_EXCLUSIVE(df->r_ret, df->ret, "r_ret", "ret"); 307 MUTUALLY_EXCLUSIVE(df->ret, df->r_ret, "ret", "ret_r");
385 MUTUALLY_EXCLUSIVE(df->r_key, df->key, "r_key", "key"); 308 MUTUALLY_EXCLUSIVE(df->key, df->r_key, "key", "key_r");
386 MUTUALLY_EXCLUSIVE(pos, param, "pos", "param"); 309 MUTUALLY_EXCLUSIVE((df->pos >= 0), param, "pos", "param");
387 MUTUALLY_EXCLUSIVE(pos, df->r_param, "pos", "param_r"); 310 MUTUALLY_EXCLUSIVE((df->pos >= 0), df->r_param, "pos", "param_r");
388 MUTUALLY_EXCLUSIVE(param, df->r_param, "param", "param_r"); 311 MUTUALLY_EXCLUSIVE(param, df->r_param, "param", "param_r");
389 MUTUALLY_EXCLUSIVE((df->r_key || df->key), (df->r_value || df->value), "key", "value"); 312 MUTUALLY_EXCLUSIVE((df->r_key || df->key), (df->r_value || df->value), "key", "value");
390 MUTUALLY_EXCLUSIVE((df->r_ret || df->ret || df->ret_type), (df->r_param || param), "ret", "param"); 313 MUTUALLY_EXCLUSIVE((df->r_ret || df->ret || df->ret_type), (df->r_param || param), "ret", "param");
391 MUTUALLY_EXCLUSIVE((df->r_ret || df->ret || df->ret_type), (var), "ret", "var"); 314 MUTUALLY_EXCLUSIVE((df->r_ret || df->ret || df->ret_type), (var), "ret", "var");
392 MUTUALLY_EXCLUSIVE((df->r_ret || df->ret || df->ret_type), (df->value || df->r_value), "ret", "value"); 315 MUTUALLY_EXCLUSIVE((df->r_ret || df->ret || df->ret_type), (df->value || df->r_value), "ret", "value");
316
393#undef MUTUALLY_EXCLUSIVE 317#undef MUTUALLY_EXCLUSIVE
394 318
395 if (!(df->r_function || df->function)) { 319 if (!(df->r_function || df->function)) {
396 sp_log_err("config", 320 sp_log_err("config", "Invalid configuration line: 'sp.disabled_functions': must take a function name on line %zu", parsed_rule->lineno);
397 "Invalid configuration line: 'sp.disabled_functions%s':" 321 goto out;
398 " must take a function name on line %zu",
399 line, sp_line_no);
400 return -1;
401 } 322 }
402 if (df->filename && (*ZSTR_VAL(df->filename) != '/') && 323 if (df->filename && (*ZSTR_VAL(df->filename) != '/') &&
403 (0 != strncmp(ZSTR_VAL(df->filename), "phar://", strlen("phar://")))) { 324 (0 != strncmp(ZSTR_VAL(df->filename), ZEND_STRL("phar://")))) {
404 sp_log_err( 325 sp_log_err("config", "Invalid configuration line: 'sp.disabled_functions': '.filename' must be an absolute path or a phar archive on line %zu", parsed_rule->lineno);
405 "config", 326 goto out;
406 "Invalid configuration line: 'sp.disabled_functions%s':"
407 "'.filename' must be an absolute path or a phar archive on line %zu",
408 line, sp_line_no);
409 return -1;
410 } 327 }
411 if (!(allow ^ drop)) { 328 if (!(allow ^ drop)) {
412 sp_log_err("config", 329 sp_log_err("config", "Invalid configuration line: 'sp.disabled_functions': The rule must either be a `drop` or `allow` one on line %zu", parsed_rule->lineno);
413 "Invalid configuration line: 'sp.disabled_functions%s': The " 330 goto out;
414 "rule must either be a `drop` or `allow` one on line %zu",
415 line, sp_line_no);
416 return -1;
417 } 331 }
418 332
419 if (pos) {
420 errno = 0;
421 char *endptr;
422 df->pos = (int)strtol(ZSTR_VAL(pos), &endptr, 10);
423 if (errno != 0 || endptr == ZSTR_VAL(pos)) {
424 sp_log_err("config", "Failed to parse arg '%s' of `pos` on line %zu",
425 ZSTR_VAL(pos), sp_line_no);
426 return -1;
427 }
428 }
429
430 if (line_number) {
431 errno = 0;
432 char *endptr;
433 df->line = (unsigned int)strtoul(ZSTR_VAL(line_number), &endptr, 10);
434 if (errno != 0 || endptr == ZSTR_VAL(line_number)) {
435 sp_log_err("config", "Failed to parse arg '%s' of `line` on line %zu",
436 ZSTR_VAL(line_number), sp_line_no);
437 return -1;
438 }
439 }
440 df->allow = allow; 333 df->allow = allow;
441 df->textual_representation = zend_string_init(line, strlen(line), 1); 334 df->textual_representation = sp_get_textual_representation(parsed_rule);
442 335
443 if (df->function) { 336 if (df->function) {
444 df->functions_list = parse_functions_list(ZSTR_VAL(df->function)); 337 df->functions_list = parse_functions_list(ZSTR_VAL(df->function));
@@ -453,108 +346,162 @@ int parse_disabled_functions(char *line) {
453 new[0] = '$'; 346 new[0] = '$';
454 memcpy(new + 1, ZSTR_VAL(param), ZSTR_LEN(param)); 347 memcpy(new + 1, ZSTR_VAL(param), ZSTR_LEN(param));
455 df->param = sp_parse_var(new); 348 df->param = sp_parse_var(new);
456 free(new); 349 pefree(new, 1);
457 } else { 350 } else {
458 df->param = sp_parse_var(ZSTR_VAL(param)); 351 df->param = sp_parse_var(ZSTR_VAL(param));
459 } 352 }
460 if (!df->param) { 353 if (!df->param) {
461 sp_log_err("config", "Invalid value '%s' for `param` on line %zu", 354 sp_log_err("config", "Invalid value '%s' for `param` on line %zu", ZSTR_VAL(param), parsed_rule->lineno);
462 ZSTR_VAL(param), sp_line_no); 355 goto out;
463 return -1;
464 } 356 }
465 } 357 }
466
467 if (var) { 358 if (var) {
468 if (ZSTR_LEN(var)) { 359 if (ZSTR_LEN(var)) {
469 df->var = sp_parse_var(ZSTR_VAL(var)); 360 df->var = sp_parse_var(ZSTR_VAL(var));
470 if (!df->var) { 361 if (!df->var) {
471 sp_log_err("config", "Invalid value '%s' for `var` on line %zu", 362 sp_log_err("config", "Invalid value '%s' for `var` on line %zu", ZSTR_VAL(var), parsed_rule->lineno);
472 ZSTR_VAL(var), sp_line_no); 363 goto out;
473 return -1;
474 } 364 }
475 } else { 365 } else {
476 sp_log_err("config", "Empty value in `var` on line %zu", sp_line_no); 366 sp_log_err("config", "Empty value in `var` on line %zu", parsed_rule->lineno);
477 return -1; 367 goto out;
478 } 368 }
479 } 369 }
480 370
481 if (true == disable) {
482 return ret;
483 }
484
485 if (df->function && zend_string_equals_literal(df->function, "print")) { 371 if (df->function && zend_string_equals_literal(df->function, "print")) {
486 zend_string_release(df->function); 372 zend_string_release(df->function);
487 df->function = zend_string_init("echo", sizeof("echo") - 1, 1); 373 df->function = zend_string_init(ZEND_STRL("echo"), 1);
488 } 374 }
489 375
490 if (df->function && !df->functions_list) { 376 if (df->function && !df->functions_list) {
491 if (df->ret || df->r_ret || df->ret_type) { 377 if (df->ret || df->r_ret || df->ret_type) {
492 add_df_to_hashtable(SNUFFLEUPAGUS_G(config).config_disabled_functions_ret, 378 add_df_to_hashtable(SPCFG(disabled_functions_ret), df);
493 df);
494 } else { 379 } else {
495 add_df_to_hashtable(SNUFFLEUPAGUS_G(config).config_disabled_functions, 380 add_df_to_hashtable(SPCFG(disabled_functions), df);
496 df);
497 } 381 }
498 } else { 382 } else {
499 if (df->ret || df->r_ret || df->ret_type) { 383 if (df->ret || df->r_ret || df->ret_type) {
500 SNUFFLEUPAGUS_G(config) 384 SPCFG(disabled_functions_reg_ret).disabled_functions = sp_list_insert(SPCFG(disabled_functions_reg_ret).disabled_functions, df);
501 .config_disabled_functions_reg_ret->disabled_functions =
502 sp_list_insert(
503 SNUFFLEUPAGUS_G(config)
504 .config_disabled_functions_reg_ret->disabled_functions,
505 df);
506 } else { 385 } else {
507 SNUFFLEUPAGUS_G(config) 386 SPCFG(disabled_functions_reg).disabled_functions = sp_list_insert(SPCFG(disabled_functions_reg).disabled_functions, df);
508 .config_disabled_functions_reg->disabled_functions =
509 sp_list_insert(SNUFFLEUPAGUS_G(config)
510 .config_disabled_functions_reg->disabled_functions,
511 df);
512 } 387 }
513 } 388 }
389 return SP_PARSER_STOP;
390
391out:
392 if (df) {
393 sp_free_disabled_function(df);
394 pefree(df, 1);
395 }
396 if (param) { zend_string_release(param); }
397 if (var) { zend_string_release(var); }
398
514 return ret; 399 return ret;
515} 400}
516 401
517int parse_upload_validation(char *line) { 402SP_PARSE_FN(parse_upload_validation) {
518 bool disable = false, enable = false; 403 bool disable = false, enable = false;
519 sp_config_functions sp_config_funcs_upload_validation[] = { 404 sp_config_upload_validation *cfg = (sp_config_upload_validation*)retval;
520 {parse_str, SP_TOKEN_UPLOAD_SCRIPT, 405
521 &(SNUFFLEUPAGUS_G(config).config_upload_validation->script)}, 406 sp_config_keyword config_keywords[] = {
522 {parse_empty, SP_TOKEN_SIMULATION,
523 &(SNUFFLEUPAGUS_G(config).config_upload_validation->simulation)},
524 {parse_empty, SP_TOKEN_ENABLE, &(enable)}, 407 {parse_empty, SP_TOKEN_ENABLE, &(enable)},
525 {parse_empty, SP_TOKEN_DISABLE, &(disable)}, 408 {parse_empty, SP_TOKEN_DISABLE, &(disable)},
409 {parse_str, SP_TOKEN_UPLOAD_SCRIPT, &(cfg->script)},
410 {parse_empty, SP_TOKEN_SIMULATION, &(cfg->simulation)},
411 {parse_empty, SP_TOKEN_SIM, &(cfg->simulation)},
526 {0, 0, 0}}; 412 {0, 0, 0}};
527 413
528 int ret = parse_keywords(sp_config_funcs_upload_validation, line); 414 SP_PROCESS_CONFIG_KEYWORDS_ERR();
415 SP_SET_ENABLE_DISABLE(enable, disable, cfg->enable);
529 416
530 if (0 != ret) { 417 if (!cfg->script) {
531 return ret; 418 sp_log_err("config", "The `script` directive is mandatory in '.%s' on line %zu", token, parsed_rule->lineno);
419 return SP_PARSER_ERROR;
420 } else if (-1 == access(ZSTR_VAL(cfg->script), F_OK)) {
421 sp_log_err("config", "The `script` (%s) doesn't exist on line %zu", ZSTR_VAL(cfg->script), parsed_rule->lineno);
422 return SP_PARSER_ERROR;
532 } 423 }
533 424
534 if (!(enable ^ disable)) { 425 return SP_PARSER_STOP;
535 sp_log_err("config", "A rule can't be enabled and disabled on line %zu", 426}
536 sp_line_no); 427
537 return -1; 428SP_PARSE_FN(parse_ini_protection) {
429 bool disable = false, enable = false;
430 bool rw = false, ro = false; // rw is ignored, but declaring .policy_rw is valid for readability
431 sp_config_ini *cfg = (sp_config_ini*)retval;
432 sp_config_keyword config_keywords[] = {
433 {parse_empty, "enable", &(enable)},
434 {parse_empty, "disable", &(disable)},
435 {parse_empty, "simulation", &cfg->simulation},
436 {parse_empty, "sim", &cfg->simulation},
437 {parse_empty, "policy_readonly", &ro},
438 {parse_empty, "policy_ro", &ro},
439 {parse_empty, "policy_readwrite", &rw},
440 {parse_empty, "policy_rw", &rw},
441 {parse_empty, "policy_silent_ro", &cfg->policy_silent_ro},
442 {parse_empty, "policy_silent_fail", &cfg->policy_silent_fail},
443 {parse_empty, "policy_no_log", &cfg->policy_silent_fail},
444 {parse_empty, "policy_drop", &cfg->policy_drop},
445 {0, 0, 0}};
446
447 SP_PROCESS_CONFIG_KEYWORDS_ERR();
448
449 SP_SET_ENABLE_DISABLE(enable, disable, cfg->enable);
450
451 if (ro && rw) {
452 sp_log_err("config", "rule cannot be both read-write and read-only on line %zu", parsed_rule->lineno);
453 return SP_PARSER_ERROR;
538 } 454 }
539 SNUFFLEUPAGUS_G(config).config_upload_validation->enable = enable; 455 cfg->policy_readonly = ro;
540 456
541 zend_string const *script = 457 if (cfg->policy_silent_fail && cfg->policy_drop) {
542 SNUFFLEUPAGUS_G(config).config_upload_validation->script; 458 sp_log_err("config", "policy cannot be drop and silent at the same time on line %zu", parsed_rule->lineno);
459 return SP_PARSER_ERROR;
460 }
461 return SP_PARSER_STOP;
462}
463
464SP_PARSE_FN(parse_ini_entry) {
465 sp_ini_entry *entry = pecalloc(sizeof(sp_ini_entry), 1, 1);
466 bool rw = false, ro = false;
467
468 sp_config_keyword config_keywords[] = {
469 {parse_empty, "simulation", &entry->simulation},
470 {parse_empty, "sim", &entry->simulation},
471 {parse_str, "key", &entry->key},
472 {parse_str, "msg", &entry->msg},
473 {parse_str, "set", &entry->set},
474 {parse_str, "min", &entry->min},
475 {parse_str, "max", &entry->max},
476 {parse_regexp, "regexp", &entry->regexp},
477 {parse_empty, "readonly", &ro},
478 {parse_empty, "ro", &ro},
479 {parse_empty, "readwrite", &rw},
480 {parse_empty, "rw", &rw},
481 {parse_empty, "drop", &entry->drop},
482 {parse_empty, "allow_null", &entry->allow_null},
483 {0, 0, 0}};
543 484
544 if (!script) { 485 SP_PROCESS_CONFIG_KEYWORDS(goto err);
545 sp_log_err("config", 486
546 "The `script` directive is mandatory in '%s' on line %zu", line, 487 if (!entry->key) {
547 sp_line_no); 488 sp_log_err("config", "A .key() must be provided on line %zu", parsed_rule->lineno);
548 return -1; 489 goto err;
549 } else if (-1 == access(ZSTR_VAL(script), F_OK)) {
550 sp_log_err("config", "The `script` (%s) doesn't exist on line %zu",
551 ZSTR_VAL(script), sp_line_no);
552 return -1;
553 } else if (-1 == access(ZSTR_VAL(script), X_OK)) {
554 sp_log_err("config", "The `script` (%s) isn't executable on line %zu",
555 ZSTR_VAL(script), sp_line_no);
556 return -1;
557 } 490 }
558 491
559 return ret; 492 if (ro && rw) {
493 sp_log_err("config", "rule cannot be both read-write and read-only on line %zu", parsed_rule->lineno);
494 goto err;
495 }
496 entry->access = ro - rw;
497
498 zend_hash_add_ptr(SPCFG(ini).entries, entry->key, entry);
499 return SP_PARSER_STOP;
500
501err:
502 if (entry) {
503 sp_free_ini_entry(entry);
504 pefree(entry, 1);
505 }
506 return SP_PARSER_ERROR;
560} 507}