1 /*
2 * ModSecurity for Apache 2.x, http://www.modsecurity.org/
3 * Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/)
4 *
5 * You may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * If any of the files related to licensing are missing or if you have any
11 * other questions related to licensing please contact Trustwave Holdings, Inc.
12 * directly using the email address security@modsecurity.org.
13 */
14 
15 #include "msc_parsers.h"
16 #include <ctype.h>
17 
18 /**
19  *
20  */
parse_cookies_v0(modsec_rec * msr,char * _cookie_header,apr_table_t * cookies,const char * delim)21 int parse_cookies_v0(modsec_rec *msr, char *_cookie_header,
22                      apr_table_t *cookies, const char *delim)
23 {
24     char *attr_name = NULL, *attr_value = NULL;
25     char *cookie_header;
26     char *saveptr = NULL;
27     int cookie_count = 0;
28     char *p = NULL;
29 
30     if (_cookie_header == NULL) {
31         msr_log(msr, 1, "Cookie parser: Received null for argument.");
32         return -1;
33     }
34 
35     cookie_header = strdup(_cookie_header);
36     if (cookie_header == NULL) return -1;
37 
38     if(msr->txcfg->cookiev0_separator == NULL) {
39         p = apr_strtok(cookie_header, delim, &saveptr);
40     } else {
41         p = apr_strtok(cookie_header, msr->txcfg->cookiev0_separator, &saveptr);
42     }
43 
44     while(p != NULL) {
45         attr_name = NULL;
46         attr_value = NULL;
47 
48         /* ignore whitespace at the beginning of cookie name */
49         while(isspace(*p)) p++;
50         attr_name = p;
51 
52         attr_value = strstr(p, "=");
53         if (attr_value != NULL) {
54             /* terminate cookie name */
55             *attr_value = 0;
56             /* move over to the beginning of the value */
57             attr_value++;
58         }
59 
60         /* we ignore cookies with empty names */
61         if ((attr_name != NULL)&&(strlen(attr_name) != 0)) {
62             if (attr_value != NULL) {
63                 if (msr->txcfg->debuglog_level >= 5) {
64                     msr_log(msr, 5, "Adding request cookie: name \"%s\", value \"%s\"",
65                             log_escape(msr->mp, attr_name), log_escape(msr->mp, attr_value));
66                 }
67 
68                 apr_table_add(cookies, attr_name, attr_value);
69             } else {
70                 if (msr->txcfg->debuglog_level >= 5) {
71                     msr_log(msr, 5, "Adding request cookie: name \"%s\", value empty",
72                             log_escape(msr->mp, attr_name));
73                 }
74 
75                 apr_table_add(cookies, attr_name, "");
76             }
77 
78             cookie_count++;
79         }
80 
81         if(msr->txcfg->cookiev0_separator == NULL) {
82             p = apr_strtok(NULL, delim, &saveptr);
83         } else {
84             p = apr_strtok(NULL, msr->txcfg->cookiev0_separator, &saveptr);
85         }
86     }
87 
88     free(cookie_header);
89     return cookie_count;
90 }
91 
92 /**
93  *
94  */
parse_cookies_v1(modsec_rec * msr,char * _cookie_header,apr_table_t * cookies)95 int parse_cookies_v1(modsec_rec *msr, char *_cookie_header,
96         apr_table_t *cookies)
97 {
98     char *attr_name = NULL, *attr_value = NULL, *p = NULL;
99     char *prev_attr_name = NULL;
100     char *cookie_header = NULL;
101     int cookie_count = 0;
102 
103     if (_cookie_header == NULL) return -1;
104     // XXX Should it not match _v0 parser?
105     //if (_cookie_header == NULL) {
106     //    msr_log(msr, 1, "Cookie parser: Received null for argument.");
107     //    return -1;
108     //}
109 
110     cookie_header = strdup(_cookie_header);
111     if (cookie_header == NULL) return -1;
112 
113     p = cookie_header;
114     while(*p != 0) {
115         attr_name = NULL;
116         attr_value = NULL;
117 
118         /* attribute name */
119 
120         /* remove space from the beginning */
121         while((isspace(*p))&&(*p != 0)) p++;
122         attr_name = p;
123         while((*p != 0)&&(*p != '=')&&(*p != ';')&&(*p != ',')) p++;
124 
125         /* if we've reached the end of string */
126         if (*p == 0) goto add_cookie;
127 
128         /* if there is no cookie value supplied */
129         if ((*p == ';')||(*p == ',')) {
130             *p++ = 0; /* terminate the name */
131             goto add_cookie;
132         }
133 
134         /* terminate the attribute name,
135          * writing over the = character
136          */
137         *p++ = 0;
138 
139         /* attribute value */
140 
141         /* skip over the whitespace at the beginning */
142         while((isspace(*p))&&(*p != 0)) p++;
143 
144         /* no value supplied */
145         if (*p == 0) goto add_cookie;
146 
147         if (*p == '"') {
148             if (*++p == 0) goto add_cookie;
149             attr_value = p;
150             while((*p != 0)&&(*p != '"')) p++;
151             if (*p != 0) *p++ = 0;
152             else {
153                 /* Do nothing about this. */
154             }
155         } else {
156             attr_value = p;
157             while((*p != 0)&&(*p != ',')&&(*p != ';')) p++;
158             if (*p != 0) *p++ = 0;
159 
160             /* remove the whitespace from the end of cookie value */
161             if (attr_value != NULL) {
162                 char *t = attr_value;
163                 int i = 0;
164 
165                 while(*t != 0) {
166                     t++;
167                     i++;
168                 }
169 
170                 while((i-- > 0)&&(isspace(*(--t)))) *t = 0;
171             }
172         }
173 
174 add_cookie:
175 
176         /* remove the whitespace from the end of cookie name */
177         if (attr_name != NULL) {
178             char *t = attr_name;
179             int i = 0;
180 
181             while(*t != 0) {
182                 t++;
183                 i++;
184             }
185 
186             while((i-- > 0)&&(isspace(*(--t)))) *t = 0;
187         }
188 
189         /* add the cookie to the list now */
190         if ((attr_name != NULL)&&(strlen(attr_name) != 0)) {
191 
192             /* handle special attribute names */
193             if (attr_name[0] == '$') {
194                 if (prev_attr_name != NULL) {
195                     /* cookie keyword, we change the name we use
196                      * so they can have a unique name in the cookie table
197                      */
198                     attr_name = apr_psprintf(msr->mp, "$%s_%s", prev_attr_name, attr_name + 1);
199                 }
200             }
201 
202             if (attr_value != NULL) {
203                 if (msr->txcfg->debuglog_level >= 5) {
204                     msr_log(msr, 5, "Adding request cookie: name \"%s\", value \"%s\"",
205                             log_escape(msr->mp, attr_name), log_escape(msr->mp, attr_value));
206                 }
207 
208                 apr_table_add(cookies, attr_name, attr_value);
209             } else {
210                 if (msr->txcfg->debuglog_level >= 5) {
211                     msr_log(msr, 5, "Adding request cookie: name \"%s\", value empty",
212                             log_escape(msr->mp, attr_name));
213                 }
214 
215                 apr_table_add(cookies, attr_name, "");
216             }
217 
218             cookie_count++;
219 
220             /* only keep the cookie names for later */
221             if (attr_name[0] != '$') prev_attr_name = attr_name;
222         }
223 
224         /* at this point the *p is either 0 (in which case we exit), or
225          * right after the current cookie ended - we need to look for
226          * the next cookie
227          */
228         while( (*p != 0)&&( (*p == ',')||(*p == ';')||(isspace(*p)) ) ) p++;
229     }
230 
231     free(cookie_header);
232     return cookie_count;
233 }
234 
235 /**
236  *
237  */
parse_arguments(modsec_rec * msr,const char * s,apr_size_t inputlength,int argument_separator,const char * origin,apr_table_t * arguments,int * invalid_count)238 int parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength,
239         int argument_separator, const char *origin,
240         apr_table_t *arguments, int *invalid_count)
241 {
242     msc_arg *arg;
243     apr_size_t i, j;
244     char *value = NULL;
245     char *buf;
246     int status;
247     int changed;
248 
249     if (s == NULL) return -1;
250     if (inputlength == 0) return 1;
251 
252     /* Check that adding one will not overflow */
253     if (inputlength + 1 <= 0) return -1;
254 
255     buf = (char *)malloc(inputlength + 1);
256     if (buf == NULL) return -1;
257 
258     arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg));
259     arg->origin = origin;
260 
261     i = 0;
262     j = 0;
263     status = 0;
264     *invalid_count = 0;
265     while (i < inputlength) {
266         if (status == 0) {
267             /* parameter name */
268             arg->name_origin_offset = i;
269             while ((i < inputlength) && (s[i] != '=') && (s[i] != argument_separator))  {
270                 buf[j] = s[i];
271                 j++;
272                 i++;
273             }
274             buf[j++] = '\0';
275             arg->name_origin_len = i - arg->name_origin_offset;
276         } else {
277             /* parameter value */
278             arg->value_origin_offset = i;
279             while ((s[i] != argument_separator) && (i < inputlength)) {
280                 buf[j] = s[i];
281                 j++;
282                 i++;
283             }
284             buf[j++] = '\0';
285             arg->value_origin_len = i - arg->value_origin_offset;
286         }
287 
288         if (status == 0) {
289             arg->name_len = urldecode_nonstrict_inplace_ex((unsigned char *)buf, arg->name_origin_len, invalid_count, &changed);
290             arg->name = apr_pstrmemdup(msr->mp, buf, arg->name_len);
291 
292             if (s[i] == argument_separator) {
293                 /* Empty parameter */
294                 arg->value_len = 0;
295                 arg->value = "";
296 
297                 add_argument(msr, arguments, arg);
298 
299                 arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg));
300                 arg->origin = origin;
301 
302                 status = 0; /* unchanged */
303                 j = 0;
304             } else {
305                 status = 1;
306                 value = &buf[j];
307             }
308         }
309         else {
310             arg->value_len = urldecode_nonstrict_inplace_ex((unsigned char *)value, arg->value_origin_len, invalid_count, &changed);
311             arg->value = apr_pstrmemdup(msr->mp, value, arg->value_len);
312 
313             add_argument(msr, arguments, arg);
314 
315             arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg));
316             arg->origin = origin;
317 
318             status = 0;
319             j = 0;
320         }
321 
322         i++; /* skip over the separator */
323     }
324 
325     /* the last parameter was empty */
326     if (status == 1) {
327         arg->value_len = 0;
328         arg->value = "";
329 
330         add_argument(msr, arguments, arg);
331     }
332 
333     free(buf);
334 
335     return 1;
336 }
337 
338 /**
339  *
340  */
add_argument(modsec_rec * msr,apr_table_t * arguments,msc_arg * arg)341 void add_argument(modsec_rec *msr, apr_table_t *arguments, msc_arg *arg)
342 {
343     if (msr->txcfg->debuglog_level >= 5) {
344         msr_log(msr, 5, "Adding request argument (%s): name \"%s\", value \"%s\"",
345                 arg->origin, log_escape_ex(msr->mp, arg->name, arg->name_len),
346                 log_escape_ex(msr->mp, arg->value, arg->value_len));
347     }
348 
349     apr_table_addn(arguments, log_escape_nq_ex(msr->mp, arg->name, arg->name_len), (void *)arg);
350 }
351 
352