summaryrefslogtreecommitdiff
path: root/src/sp_config.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sp_config.c')
-rw-r--r--src/sp_config.c440
1 files changed, 247 insertions, 193 deletions
diff --git a/src/sp_config.c b/src/sp_config.c
index c12b435..7294b0e 100644
--- a/src/sp_config.c
+++ b/src/sp_config.c
@@ -4,248 +4,302 @@
4 4
5#include "php_snuffleupagus.h" 5#include "php_snuffleupagus.h"
6 6
7size_t sp_line_no;
8 7
9static sp_config_tokens const sp_func[] = { 8static zend_result sp_process_config_root(sp_parsed_keyword *parsed_rule) {
10 {.func = parse_unserialize, .token = SP_TOKEN_UNSERIALIZE_HMAC}, 9 sp_config_keyword sp_func[] = {
11 {.func = parse_random, .token = SP_TOKEN_HARDEN_RANDOM}, 10 {parse_unserialize, SP_TOKEN_UNSERIALIZE_HMAC, &(SPCFG(unserialize))},
12 {.func = parse_log_media, .token = SP_TOKEN_LOG_MEDIA}, 11 {parse_enable, SP_TOKEN_HARDEN_RANDOM, &(SPCFG(random).enable)},
13 {.func = parse_disabled_functions, .token = SP_TOKEN_DISABLE_FUNC}, 12 {parse_log_media, SP_TOKEN_LOG_MEDIA, &(SPCFG(log_media))},
14 {.func = parse_readonly_exec, .token = SP_TOKEN_READONLY_EXEC}, 13 {parse_disabled_functions, SP_TOKEN_DISABLE_FUNC, NULL},
15 {.func = parse_global_strict, .token = SP_TOKEN_GLOBAL_STRICT}, 14 {parse_readonly_exec, SP_TOKEN_READONLY_EXEC, &(SPCFG(readonly_exec))},
16 {.func = parse_upload_validation, .token = SP_TOKEN_UPLOAD_VALIDATION}, 15 {parse_enable, SP_TOKEN_GLOBAL_STRICT, &(SPCFG(global_strict).enable)},
17 {.func = parse_cookie, .token = SP_TOKEN_COOKIE_ENCRYPTION}, 16 {parse_upload_validation, SP_TOKEN_UPLOAD_VALIDATION, &(SPCFG(upload_validation))},
18 {.func = parse_global, .token = SP_TOKEN_GLOBAL}, 17 {parse_cookie, SP_TOKEN_COOKIE_ENCRYPTION, NULL},
19 {.func = parse_auto_cookie_secure, .token = SP_TOKEN_AUTO_COOKIE_SECURE}, 18 {parse_global, SP_TOKEN_GLOBAL, NULL},
20 {.func = parse_disable_xxe, .token = SP_TOKEN_DISABLE_XXE}, 19 {parse_enable, SP_TOKEN_AUTO_COOKIE_SECURE, &(SPCFG(auto_cookie_secure).enable)},
21 {.func = parse_eval_blacklist, .token = SP_TOKEN_EVAL_BLACKLIST}, 20 {parse_enable, SP_TOKEN_XXE_PROTECTION, &(SPCFG(xxe_protection).enable)},
22 {.func = parse_eval_whitelist, .token = SP_TOKEN_EVAL_WHITELIST}, 21 {parse_eval_filter_conf, SP_TOKEN_EVAL_BLACKLIST, &(SPCFG(eval).blacklist)},
23 {.func = parse_session, .token = SP_TOKEN_SESSION_ENCRYPTION}, 22 {parse_eval_filter_conf, SP_TOKEN_EVAL_WHITELIST, &(SPCFG(eval).whitelist)},
24 {.func = parse_sloppy_comparison, .token = SP_TOKEN_SLOPPY_COMPARISON}, 23 {parse_session, SP_TOKEN_SESSION_ENCRYPTION, &(SPCFG(session))},
25 {.func = parse_wrapper_whitelist, .token = SP_TOKEN_ALLOW_WRAPPERS}, 24 {parse_enable, SP_TOKEN_SLOPPY_COMPARISON, &(SPCFG(sloppy).enable)},
26 {NULL, NULL}}; 25 {parse_wrapper_whitelist, SP_TOKEN_ALLOW_WRAPPERS, &(SPCFG(wrapper))},
26 {parse_ini_protection, SP_TOKEN_INI_PROTECTION, &(SPCFG(ini))},
27 {parse_ini_entry, SP_TOKEN_INI, NULL},
28 {NULL, NULL, NULL}};
29 return sp_process_rule(parsed_rule, sp_func);
30}
27 31
28/* Top level keyword parsing */ 32zend_result sp_parse_config(const char *filename) {
33 FILE *fd = fopen(filename, "rb");
34 if (fd == NULL) {
35 sp_log_err("config", "Could not open configuration file %s : %s", filename, strerror(errno));
36 return FAILURE;
37 }
29 38
30static int parse_line(char *line) { 39 size_t step = 8192;
31 char *ptr = line; 40 size_t max_len = step, len = 0;
41 zend_string *data = zend_string_alloc(max_len, 0);
42 char *ptr = ZSTR_VAL(data);
32 43
33 while (*ptr == ' ' || *ptr == '\t') { 44 size_t bytes;
34 ++ptr; 45 while ((bytes = fread(ptr, 1, max_len - len, fd))) {
46 len += bytes;
47 if (max_len - len <= 0) {
48 max_len += step;
49 data = zend_string_extend(data, max_len, 0);
50 ptr = ZSTR_VAL(data) + len;
51 } else {
52 ptr += bytes;
53 }
35 } 54 }
55 fclose(fd);
36 56
37 if (!*ptr || *ptr == '#' || *ptr == ';') { 57 data = zend_string_truncate(data, len, 0);
38 return 0; 58 ZSTR_VAL(data)[len] = 0;
39 }
40 59
41 if (strncmp(ptr, SP_TOKEN_BASE, strlen(SP_TOKEN_BASE))) { 60 int ret = sp_config_scan(ZSTR_VAL(data), sp_process_config_root);
42 sp_log_err("config", "Invalid configuration prefix for '%s' on line %zu",
43 line, sp_line_no);
44 return -1;
45 }
46 ptr += strlen(SP_TOKEN_BASE);
47 61
48 for (size_t i = 0; sp_func[i].func; i++) { 62 zend_string_release_ex(data, 0);
49 if (!strncmp(sp_func[i].token, ptr, strlen(sp_func[i].token))) { 63
50 return sp_func[i].func(ptr + strlen(sp_func[i].token)); 64 return ret;
65}
66
67
68zend_result sp_process_rule(sp_parsed_keyword *parsed_rule, sp_config_keyword *config_keywords) {
69 for (sp_parsed_keyword *kw = parsed_rule; kw->kw; kw++) {
70 bool found_kw = false;
71 for (sp_config_keyword *ckw = config_keywords; ckw->func; ckw++) {
72 if (kw->kwlen == strlen(ckw->token) && !strncmp(kw->kw, ckw->token, kw->kwlen)) {
73 if (ckw->func) {
74 int ret = ckw->func(ckw->token, kw, ckw->retval);
75 switch (ret) {
76 case SP_PARSER_SUCCESS:
77 break;
78 case SP_PARSER_ERROR:
79 return FAILURE;
80 case SP_PARSER_STOP:
81 return SUCCESS;
82 }
83 }
84 found_kw = true;
85 break;
86 }
87 }
88
89 if (!found_kw) {
90 zend_string *kwname = zend_string_init(kw->kw, kw->kwlen, 0);
91 sp_log_err("config", "Unexpected keyword '%s' on line %d", ZSTR_VAL(kwname), kw->lineno);
92 zend_string_release_ex(kwname, 0);
93 return FAILURE;
51 } 94 }
52 } 95 }
53 sp_log_err("config", "Invalid configuration section '%s' on line %zu", line, 96 return SUCCESS;
54 sp_line_no);
55 return -1;
56} 97}
57 98
58/* keyword parsing */ 99#define CHECK_DUPLICATE_KEYWORD(retval) \
59int parse_empty(char *restrict line, char *restrict keyword, void *retval) { 100 if (*(void**)(retval)) { \
101 sp_log_err("config", "duplicate keyword '%s' on line %zu", token, kw->lineno); \
102 return SP_PARSER_ERROR; }
103
104
105SP_PARSEKW_FN(parse_empty) {
106 if (kw->arglen) {
107 sp_log_err("config", "Unexpected argument for keyword '%s' - it should be '%s()' on line %zu", token, token, kw->lineno);
108 return SP_PARSER_ERROR;
109 }
110 if (kw->argtype != SP_ARGTYPE_EMPTY) {
111 sp_log_err("config", "Missing paranthesis for keyword '%s' - it should be '%s()' on line %zu", token, token, kw->lineno);
112 return SP_PARSER_ERROR;
113 }
60 *(bool *)retval = true; 114 *(bool *)retval = true;
61 return 0; 115 return SP_PARSER_SUCCESS;
62} 116}
63 117
64int parse_list(char *restrict line, char *restrict keyword, void *list_ptr) { 118SP_PARSEKW_FN(parse_list) {
65 zend_string *value = NULL; 119 CHECK_DUPLICATE_KEYWORD(retval);
66 sp_list_node **list = list_ptr;
67 char *token, *tmp;
68 120
69 size_t consumed = 0; 121 sp_list_node **list = retval;
70 value = get_param(&consumed, line, SP_TYPE_STR, keyword); 122 char *tok, *tmp;
71 if (!value) { 123
72 return -1; 124 SP_PARSE_ARG(value);
73 }
74 125
75 tmp = ZSTR_VAL(value); 126 tmp = ZSTR_VAL(value);
76 while (1) { 127 while (1) {
77 token = strsep(&tmp, ","); 128 tok = strsep(&tmp, ",");
78 if (token == NULL) { 129 if (tok == NULL) {
79 break; 130 break;
80 } 131 }
81 *list = sp_list_insert(*list, zend_string_init(token, strlen(token), 1)); 132 *list = sp_list_insert(*list, zend_string_init(tok, strlen(tok), 1));
82 } 133 }
134 zend_string_release(value);
83 135
84 pefree(value, 1); 136 return SP_PARSER_SUCCESS;
85 return consumed;
86} 137}
87 138
88int parse_php_type(char *restrict line, char *restrict keyword, void *retval) { 139SP_PARSEKW_FN(parse_php_type) {
89 size_t consumed = 0; 140 SP_PARSE_ARG(value);
90 zend_string *value = get_param(&consumed, line, SP_TYPE_STR, keyword); 141
91 if (value) { 142 if (zend_string_equals_literal_ci(value, "undef")) {
92 if (zend_string_equals_literal_ci(value, "undef")) { 143 *(sp_php_type *)retval = SP_PHP_TYPE_UNDEF;
93 *(sp_php_type *)retval = SP_PHP_TYPE_UNDEF; 144 } else if (zend_string_equals_literal_ci(value, "null")) {
94 } else if (zend_string_equals_literal_ci(value, "null")) { 145 *(sp_php_type *)retval = SP_PHP_TYPE_NULL;
95 *(sp_php_type *)retval = SP_PHP_TYPE_NULL; 146 } else if (zend_string_equals_literal_ci(value, "true")) {
96 } else if (zend_string_equals_literal_ci(value, "true")) { 147 *(sp_php_type *)retval = SP_PHP_TYPE_TRUE;
97 *(sp_php_type *)retval = SP_PHP_TYPE_TRUE; 148 } else if (zend_string_equals_literal_ci(value, "false")) {
98 } else if (zend_string_equals_literal_ci(value, "false")) { 149 *(sp_php_type *)retval = SP_PHP_TYPE_FALSE;
99 *(sp_php_type *)retval = SP_PHP_TYPE_FALSE; 150 } else if (zend_string_equals_literal_ci(value, "long")) {
100 } else if (zend_string_equals_literal_ci(value, "long")) { 151 *(sp_php_type *)retval = SP_PHP_TYPE_LONG;
101 *(sp_php_type *)retval = SP_PHP_TYPE_LONG; 152 } else if (zend_string_equals_literal_ci(value, "double")) {
102 } else if (zend_string_equals_literal_ci(value, "double")) { 153 *(sp_php_type *)retval = SP_PHP_TYPE_DOUBLE;
103 *(sp_php_type *)retval = SP_PHP_TYPE_DOUBLE; 154 } else if (zend_string_equals_literal_ci(value, "string")) {
104 } else if (zend_string_equals_literal_ci(value, "string")) { 155 *(sp_php_type *)retval = SP_PHP_TYPE_STRING;
105 *(sp_php_type *)retval = SP_PHP_TYPE_STRING; 156 } else if (zend_string_equals_literal_ci(value, "array")) {
106 } else if (zend_string_equals_literal_ci(value, "array")) { 157 *(sp_php_type *)retval = SP_PHP_TYPE_ARRAY;
107 *(sp_php_type *)retval = SP_PHP_TYPE_ARRAY; 158 } else if (zend_string_equals_literal_ci(value, "object")) {
108 } else if (zend_string_equals_literal_ci(value, "object")) { 159 *(sp_php_type *)retval = SP_PHP_TYPE_OBJECT;
109 *(sp_php_type *)retval = SP_PHP_TYPE_OBJECT; 160 } else if (zend_string_equals_literal_ci(value, "resource")) {
110 } else if (zend_string_equals_literal_ci(value, "resource")) { 161 *(sp_php_type *)retval = SP_PHP_TYPE_RESOURCE;
111 *(sp_php_type *)retval = SP_PHP_TYPE_RESOURCE; 162 } else if (zend_string_equals_literal_ci(value, "reference")) {
112 } else if (zend_string_equals_literal_ci(value, "reference")) { 163 *(sp_php_type *)retval = SP_PHP_TYPE_REFERENCE;
113 *(sp_php_type *)retval = SP_PHP_TYPE_REFERENCE;
114 } else {
115 pefree(value, 1);
116 sp_log_err("error",
117 "%s) is expecting a valid php type ('false', 'true',"
118 " 'array'. 'object', 'long', 'double', 'null', 'resource', "
119 "'reference', 'undef') on line %zu",
120 keyword, sp_line_no);
121 return -1;
122 }
123 pefree(value, 1);
124 return consumed;
125 } else { 164 } else {
126 return -1; 165 zend_string_release(value);
166 sp_log_err("error", ".%s() is expecting a valid php type ('false', 'true',"
167 " 'array'. 'object', 'long', 'double', 'null', 'resource', "
168 "'reference', 'undef') on line %zu", token, kw->lineno);
169 return SP_PARSER_ERROR;
127 } 170 }
171 zend_string_release(value);
172 return SP_PARSER_SUCCESS;
128} 173}
129 174
130int parse_str(char *restrict line, char *restrict keyword, void *retval) {
131 zend_string *value = NULL;
132 175
133 size_t consumed = 0; 176SP_PARSEKW_FN(parse_str) {
134 value = get_param(&consumed, line, SP_TYPE_STR, keyword); 177 CHECK_DUPLICATE_KEYWORD(retval);
135 if (value) { 178 SP_PARSE_ARG(value);
136 *(zend_string **)retval = value; 179
137 return consumed; 180 *(zend_string **)retval = value;
138 } 181
139 return -1; 182 return SP_PARSER_SUCCESS;
140} 183}
141 184
142int parse_cidr(char *restrict line, char *restrict keyword, void *retval) { 185SP_PARSEKW_FN(parse_int) {
143 size_t consumed = 0; 186 int ret = SP_PARSER_SUCCESS;
144 zend_string *value = get_param(&consumed, line, SP_TYPE_STR, keyword); 187 SP_PARSE_ARG(value);
145 sp_cidr *cidr = pecalloc(sizeof(sp_cidr), 1, 1);
146 188
147 if (value) { 189 char *endptr;
148 if (-1 == get_ip_and_cidr(ZSTR_VAL(value), cidr)) { 190 errno = 0;
149 return -1; 191 *(int*)retval = (int)strtoimax(ZSTR_VAL(value), &endptr, 10);
150 } 192 if (errno != 0 || !endptr || endptr == ZSTR_VAL(value)) {
151 *(sp_cidr **)retval = cidr; 193 sp_log_err("config", "Failed to parse arg '%s' of `%s` on line %zu", ZSTR_VAL(value), token, kw->lineno);
152 return consumed; 194 ret = SP_PARSER_ERROR;
153 } else {
154 sp_log_err("config", "%s doesn't contain a valid cidr on line %zu", line,
155 sp_line_no);
156 return -1;
157 } 195 }
196 zend_string_release(value);
197 return ret;
158} 198}
159 199
160int parse_regexp(char *restrict line, char *restrict keyword, void *retval) { 200SP_PARSEKW_FN(parse_ulong) {
161 /* TODO: Do we want to use pcre_study? 201 int ret = SP_PARSER_SUCCESS;
162 * (http://www.pcre.org/original/doc/html/pcre_study.html) 202 SP_PARSE_ARG(value);
163 * maybe not: http://sljit.sourceforge.net/pcre.html*/
164 size_t consumed = 0;
165 zend_string *value = get_param(&consumed, line, SP_TYPE_STR, keyword);
166 203
167 if (value) { 204 char *endptr;
168 sp_pcre *compiled_re = sp_pcre_compile(ZSTR_VAL(value)); 205 errno = 0;
169 if (NULL != compiled_re) { 206 *(u_long*)retval = (u_long)strtoul(ZSTR_VAL(value), &endptr, 10);
170 *(sp_pcre **)retval = compiled_re; 207 if (errno != 0 || !endptr || endptr == ZSTR_VAL(value)) {
171 return consumed; 208 sp_log_err("config", "Failed to parse arg '%s' of `%s` on line %zu", ZSTR_VAL(value), token, kw->lineno);
172 } 209 ret = SP_PARSER_ERROR;
173 } 210 }
174 char *closing_paren = strchr(line, ')'); 211 zend_string_release(value);
175 if (NULL != closing_paren) { 212 return ret;
176 closing_paren[0] = '\0';
177 }
178 sp_log_err("config",
179 "'%s)' is expecting a valid regexp, and not '%s' on line %zu",
180 keyword, line, sp_line_no);
181 return -1;
182} 213}
183 214
184int sp_parse_config(const char *conf_file) { 215SP_PARSEKW_FN(parse_cidr) {
185 FILE *fd = fopen(conf_file, "r"); 216 CHECK_DUPLICATE_KEYWORD(retval);
186 char *lineptr = NULL; 217 SP_PARSE_ARG(value);
187 size_t n = 0;
188 sp_line_no = 1;
189 218
190 if (fd == NULL) { 219 sp_cidr *cidr = pecalloc(sizeof(sp_cidr), 1, 1);
191 sp_log_err("config", "Could not open configuration file %s : %s", conf_file, 220
192 strerror(errno)); 221 if (0 != get_ip_and_cidr(ZSTR_VAL(value), cidr)) {
193 return FAILURE; 222 pefree(cidr, 1);
223 cidr = NULL;
194 } 224 }
195 225
196 while (getline(&lineptr, &n, fd) > 0) { 226 *(sp_cidr **)retval = cidr;
197 /* We trash the terminal `\n`. This simplify the display of logs. */ 227 return cidr ? SP_PARSER_SUCCESS : SP_PARSER_ERROR;
198 if (lineptr[strlen(lineptr) - 1] == '\n') { 228}
199 if (strlen(lineptr) >= 2 && lineptr[strlen(lineptr) - 2] == '\r') { 229
200 lineptr[strlen(lineptr) - 2] = '\0'; 230SP_PARSEKW_FN(parse_regexp) {
201 } else { 231 CHECK_DUPLICATE_KEYWORD(retval);
202 lineptr[strlen(lineptr) - 1] = '\0'; 232 SP_PARSE_ARG(value);
203 } 233
204 } 234 sp_regexp *compiled_re = sp_regexp_compile(value);
205 if (parse_line(lineptr) == -1) { 235 if (!compiled_re) {
206 fclose(fd); 236 sp_log_err("config", "Invalid regexp '%s' for '.%s()' on line %zu", ZSTR_VAL(value), token, kw->lineno);
207 free(lineptr); 237 zend_string_release_ex(value, 1);
208 return FAILURE; 238 return SP_PARSER_ERROR;
209 }
210 free(lineptr);
211 lineptr = NULL;
212 n = 0;
213 sp_line_no++;
214 } 239 }
215 fclose(fd); 240
216 return SUCCESS; 241 *(sp_regexp **)retval = compiled_re;
242
243 return SP_PARSER_SUCCESS;
217} 244}
218 245
219void sp_disabled_function_list_free(sp_list_node *list) { 246void sp_free_disabled_function(void *data) {
220 sp_list_node *cursor = list; 247 sp_disabled_function *df = data;
221 while (cursor) {
222 sp_disabled_function *df = cursor->data;
223 if (df) {
224 sp_list_free(df->functions_list);
225 sp_list_free(df->param_array_keys);
226 sp_list_free(df->var_array_keys);
227 248
228 sp_pcre_free(df->r_filename); 249 sp_free_zstr(df->textual_representation);
229 sp_pcre_free(df->r_function);
230 sp_pcre_free(df->r_param);
231 sp_pcre_free(df->r_ret);
232 sp_pcre_free(df->r_value);
233 sp_pcre_free(df->r_key);
234 250
235 sp_tree_free(df->param); 251 sp_free_zstr(df->filename);
236 sp_tree_free(df->var); 252 sp_regexp_free(df->r_filename);
237 } 253
238 cursor = cursor->next; 254 sp_free_zstr(df->function);
239 } 255 sp_regexp_free(df->r_function);
256 sp_list_free(df->functions_list, free);
257
258 sp_free_zstr(df->hash);
259
260 sp_tree_free(df->param);
261 sp_regexp_free(df->r_param);
262
263 sp_regexp_free(df->r_ret);
264 sp_free_zstr(df->ret);
265
266 sp_regexp_free(df->r_value);
267 sp_free_zstr(df->value);
268
269 sp_regexp_free(df->r_key);
270 sp_free_zstr(df->key);
271
272 sp_free_zstr(df->dump);
273 sp_free_zstr(df->alias);
274
275 // sp_list_free(df->param_array_keys);
276 // sp_list_free(df->var_array_keys);
277
278 sp_tree_free(df->var);
279
280 pefree(df->cidr, 1);
240} 281}
241 282
242void sp_cookie_list_free(sp_list_node *list) { 283void sp_free_cookie(void *data) {
243 sp_list_node *cursor = list; 284 sp_cookie *c = data;
244 while (cursor) { 285 if (c->name)
245 sp_cookie *c = cursor->data; 286 zend_string_release_ex(c->name, 1);
246 if (c) { 287 sp_regexp_free(c->name_r);
247 sp_pcre_free(c->name_r); 288}
248 } 289
249 cursor = cursor->next; 290void sp_free_zstr(void *data) {
291 if (data) {
292 zend_string_release_ex((zend_string*)data, 1);
250 } 293 }
251} 294}
295
296void sp_free_ini_entry(void *data) {
297 sp_ini_entry *entry = data;
298
299 sp_free_zstr(entry->key);
300 sp_free_zstr(entry->min);
301 sp_free_zstr(entry->max);
302 sp_regexp_free(entry->regexp);
303 sp_free_zstr(entry->msg);
304 sp_free_zstr(entry->set);
305}