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