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