1 #if defined(__FreeBSD__)
2 #define _WITH_GETLINE
3 #endif
4
5 #include "php_snuffleupagus.h"
6
7 size_t sp_line_no;
8
9 sp_config_tokens const sp_func[] = {
10 {.func = parse_unserialize, .token = SP_TOKEN_UNSERIALIZE_HMAC},
11 {.func = parse_random, .token = SP_TOKEN_HARDEN_RANDOM},
12 {.func = parse_log_media, .token = SP_TOKEN_LOG_MEDIA},
13 {.func = parse_disabled_functions, .token = SP_TOKEN_DISABLE_FUNC},
14 {.func = parse_readonly_exec, .token = SP_TOKEN_READONLY_EXEC},
15 {.func = parse_global_strict, .token = SP_TOKEN_GLOBAL_STRICT},
16 {.func = parse_upload_validation, .token = SP_TOKEN_UPLOAD_VALIDATION},
17 {.func = parse_cookie, .token = SP_TOKEN_COOKIE_ENCRYPTION},
18 {.func = parse_global, .token = SP_TOKEN_GLOBAL},
19 {.func = parse_auto_cookie_secure, .token = SP_TOKEN_AUTO_COOKIE_SECURE},
20 {.func = parse_disable_xxe, .token = SP_TOKEN_DISABLE_XXE},
21 {.func = parse_eval_blacklist, .token = SP_TOKEN_EVAL_BLACKLIST},
22 {.func = parse_eval_whitelist, .token = SP_TOKEN_EVAL_WHITELIST},
23 {.func = parse_session, .token = SP_TOKEN_SESSION_ENCRYPTION},
24 {.func = parse_sloppy_comparison, .token = SP_TOKEN_SLOPPY_COMPARISON},
25 {.func = parse_wrapper_whitelist, .token = SP_TOKEN_ALLOW_WRAPPERS},
26 {NULL, NULL}};
27
28 /* Top level keyword parsing */
29
parse_line(char * line)30 static int parse_line(char *line) {
31 char *ptr = line;
32
33 while (*ptr == ' ' || *ptr == '\t') {
34 ++ptr;
35 }
36
37 if (!*ptr || *ptr == '#' || *ptr == ';') {
38 return 0;
39 }
40
41 if (strncmp(ptr, SP_TOKEN_BASE, strlen(SP_TOKEN_BASE))) {
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
48 for (size_t i = 0; sp_func[i].func; i++) {
49 if (!strncmp(sp_func[i].token, ptr, strlen(sp_func[i].token))) {
50 return sp_func[i].func(ptr + strlen(sp_func[i].token));
51 }
52 }
53 sp_log_err("config", "Invalid configuration section '%s' on line %zu", line,
54 sp_line_no);
55 return -1;
56 }
57
58 /* keyword parsing */
parse_empty(char * restrict line,char * restrict keyword,void * retval)59 int parse_empty(char *restrict line, char *restrict keyword, void *retval) {
60 *(bool *)retval = true;
61 return 0;
62 }
63
parse_list(char * restrict line,char * restrict keyword,void * list_ptr)64 int parse_list(char *restrict line, char *restrict keyword, void *list_ptr) {
65 zend_string *value = NULL;
66 sp_list_node **list = list_ptr;
67 char *token, *tmp;
68
69 size_t consumed = 0;
70 value = get_param(&consumed, line, SP_TYPE_STR, keyword);
71 if (!value) {
72 return -1;
73 }
74
75 tmp = ZSTR_VAL(value);
76 while ((token = strtok_r(tmp, ",", &tmp))) {
77 *list = sp_list_insert(*list, zend_string_init(token, strlen(token), 1));
78 }
79
80 pefree(value, 1);
81 return consumed;
82 }
83
parse_php_type(char * restrict line,char * restrict keyword,void * retval)84 int parse_php_type(char *restrict line, char *restrict keyword, void *retval) {
85 size_t consumed = 0;
86 zend_string *value = get_param(&consumed, line, SP_TYPE_STR, keyword);
87 if (value) {
88 if (zend_string_equals_literal_ci(value, "undef")) {
89 *(sp_php_type *)retval = SP_PHP_TYPE_UNDEF;
90 } else if (zend_string_equals_literal_ci(value, "null")) {
91 *(sp_php_type *)retval = SP_PHP_TYPE_NULL;
92 } else if (zend_string_equals_literal_ci(value, "true")) {
93 *(sp_php_type *)retval = SP_PHP_TYPE_TRUE;
94 } else if (zend_string_equals_literal_ci(value, "false")) {
95 *(sp_php_type *)retval = SP_PHP_TYPE_FALSE;
96 } else if (zend_string_equals_literal_ci(value, "long")) {
97 *(sp_php_type *)retval = SP_PHP_TYPE_LONG;
98 } else if (zend_string_equals_literal_ci(value, "double")) {
99 *(sp_php_type *)retval = SP_PHP_TYPE_DOUBLE;
100 } else if (zend_string_equals_literal_ci(value, "string")) {
101 *(sp_php_type *)retval = SP_PHP_TYPE_STRING;
102 } else if (zend_string_equals_literal_ci(value, "array")) {
103 *(sp_php_type *)retval = SP_PHP_TYPE_ARRAY;
104 } else if (zend_string_equals_literal_ci(value, "object")) {
105 *(sp_php_type *)retval = SP_PHP_TYPE_OBJECT;
106 } else if (zend_string_equals_literal_ci(value, "resource")) {
107 *(sp_php_type *)retval = SP_PHP_TYPE_RESOURCE;
108 } else if (zend_string_equals_literal_ci(value, "reference")) {
109 *(sp_php_type *)retval = SP_PHP_TYPE_REFERENCE;
110 } else {
111 pefree(value, 1);
112 sp_log_err("error",
113 "%s) is expecting a valid php type ('false', 'true',"
114 " 'array'. 'object', 'long', 'double', 'null', 'resource', "
115 "'reference', 'undef') on line %zu",
116 keyword, sp_line_no);
117 return -1;
118 }
119 pefree(value, 1);
120 return consumed;
121 } else {
122 return -1;
123 }
124 }
125
parse_str(char * restrict line,char * restrict keyword,void * retval)126 int parse_str(char *restrict line, char *restrict keyword, void *retval) {
127 zend_string *value = NULL;
128
129 size_t consumed = 0;
130 value = get_param(&consumed, line, SP_TYPE_STR, keyword);
131 if (value) {
132 *(zend_string **)retval = value;
133 return consumed;
134 }
135 return -1;
136 }
137
parse_cidr(char * restrict line,char * restrict keyword,void * retval)138 int parse_cidr(char *restrict line, char *restrict keyword, void *retval) {
139 size_t consumed = 0;
140 zend_string *value = get_param(&consumed, line, SP_TYPE_STR, keyword);
141 sp_cidr *cidr = pecalloc(sizeof(sp_cidr), 1, 1);
142
143 if (value) {
144 if (-1 == get_ip_and_cidr(ZSTR_VAL(value), cidr)) {
145 return -1;
146 }
147 *(sp_cidr **)retval = cidr;
148 return consumed;
149 } else {
150 sp_log_err("config", "%s doesn't contain a valid cidr on line %zu", line,
151 sp_line_no);
152 return -1;
153 }
154 }
155
parse_regexp(char * restrict line,char * restrict keyword,void * retval)156 int parse_regexp(char *restrict line, char *restrict keyword, void *retval) {
157 /* TODO: Do we want to use pcre_study?
158 * (http://www.pcre.org/original/doc/html/pcre_study.html)
159 * maybe not: http://sljit.sourceforge.net/pcre.html*/
160 size_t consumed = 0;
161 zend_string *value = get_param(&consumed, line, SP_TYPE_STR, keyword);
162
163 if (value) {
164 sp_pcre *compiled_re = sp_pcre_compile(ZSTR_VAL(value));
165 if (NULL != compiled_re) {
166 *(sp_pcre **)retval = compiled_re;
167 return consumed;
168 }
169 }
170 char *closing_paren = strchr(line, ')');
171 if (NULL != closing_paren) {
172 closing_paren[0] = '\0';
173 }
174 sp_log_err("config",
175 "'%s)' is expecting a valid regexp, and not '%s' on line %zu",
176 keyword, line, sp_line_no);
177 return -1;
178 }
179
sp_parse_config(const char * conf_file)180 int sp_parse_config(const char *conf_file) {
181 FILE *fd = fopen(conf_file, "r");
182 char *lineptr = NULL;
183 size_t n = 0;
184 sp_line_no = 1;
185
186 if (fd == NULL) {
187 sp_log_err("config", "Could not open configuration file %s : %s", conf_file,
188 strerror(errno));
189 return FAILURE;
190 }
191
192 while (getline(&lineptr, &n, fd) > 0) {
193 /* We trash the terminal `\n`. This simplify the display of logs. */
194 if (lineptr[strlen(lineptr) - 1] == '\n') {
195 if (strlen(lineptr) >= 2 && lineptr[strlen(lineptr) - 2] == '\r') {
196 lineptr[strlen(lineptr) - 2] = '\0';
197 } else {
198 lineptr[strlen(lineptr) - 1] = '\0';
199 }
200 }
201 if (parse_line(lineptr) == -1) {
202 fclose(fd);
203 free(lineptr);
204 return FAILURE;
205 }
206 free(lineptr);
207 lineptr = NULL;
208 n = 0;
209 sp_line_no++;
210 }
211 fclose(fd);
212 return SUCCESS;
213 }
214
sp_disabled_function_list_free(sp_list_node * list)215 void sp_disabled_function_list_free(sp_list_node *list) {
216 sp_list_node *cursor = list;
217 while (cursor) {
218 sp_disabled_function *df = cursor->data;
219 if (df && df->functions_list) sp_list_free(df->functions_list);
220 if (df) {
221 sp_tree_free(df->param);
222 sp_tree_free(df->var);
223 }
224 cursor = cursor->next;
225 }
226 }
227