1 #ifdef HAVE_CONFIG_H
2 #include "common.h"
3 #else
4 #include "common-static.h"
5 #endif
6 #include "c_icap/c-icap.h"
7 #include "c_icap/request.h"
8 #include "c_icap/array.h"
9 #include "c_icap/debug.h"
10 #include "c_icap/simple_api.h"
11 #include "c_icap/body.h"
12 #include "filters.h"
13 
14 #include <assert.h>
15 
16 typedef struct srv_cf_matcher {
17     const char *name;
18     void *(*parse_data)(const char *argv[]);
19     void (*free_data)(void *data);
20     int (*match)(const void *data, ci_request_t *req, ci_membuf_t *body);
21 } srv_cf_matcher_t;
22 
23 struct FilterResult {
24     const srv_cf_user_filter_t *matchingFilter;
25     int score;
26     int count;
27 };
28 
29 ci_ptr_dyn_array_t *FILTERS = NULL;
30 
31 int replacePartsToBody(ci_membuf_t *body, ci_membuf_t *newbody, ci_list_t *replacements, ci_list_t *tags);
32 
srv_cf_filters_reset()33 void srv_cf_filters_reset()
34 {
35     if (!FILTERS)
36         return;
37     ci_ptr_dyn_array_destroy(FILTERS);
38     FILTERS = NULL;
39 }
40 
srv_cf_action_str(int action)41 const char *srv_cf_action_str(int action)
42 {
43     switch(action) {
44     case CF_AC_NONE:
45         return "none";
46         break;
47     case CF_AC_BLOCK:
48         return "block";
49         break;
50     case CF_AC_ALLOW:
51         return "allow";
52         break;
53     case CF_AC_ADD_HEADER:
54         return "add_header";
55         break;
56     case CF_AC_REPLACE:
57         return "replace";
58         break;
59     }
60     return "unknown";
61 }
62 
63 #if 0
64 static int print_action(void *data, const void *element)
65 {
66     int dlevel = *(int *)data;
67     const srv_cf_action_cfg_t *a = (srv_cf_action_cfg_t *) element;
68     ci_debug_printf(dlevel, "Action\n\t: %s score{%s%c%d}\n",
69                     srv_cf_action_str(a->action),
70                     (a->matchingFilter != NULL ? a->matchingFilter->name : "-"),
71                     a->scoreOperator == CF_OP_LESS ? '<' : (a->scoreOperator == CF_OP_GREATER ? '>' : '='),
72                     a->score
73         );
74     return 0;
75 }
76 #endif
77 
print_srv_cf_user_filter_data(void * data,const void * element)78 static int print_srv_cf_user_filter_data(void *data, const void *element)
79 {
80     int dlevel = *(int *)data;
81     const srv_cf_user_filter_data_t *fd = (srv_cf_user_filter_data_t *) element;
82     ci_debug_printf(dlevel, "\t: %s%s%s%s, /%s/ %d\n",
83                     fd->type == BodyRegex ? "body" : (fd->type == HeaderRegex? "header": (fd->type == RequestHeaderRegex? "request_header" : "url")),
84                     fd->header != NULL ? "{" : "",
85                     fd->header != NULL ? fd->header : "",
86                     fd->header != NULL ? "}" : "",
87                     fd->regex_str,
88                     fd->score
89         );
90     return 0;
91 }
92 
print_user_filter(void * data,const char * name,const void * element)93 static int print_user_filter(void *data, const char *name, const void *element)
94 {
95     int dlevel = *(int *)data;
96     srv_cf_user_filter_t *filter = (srv_cf_user_filter_t *)element;
97     ci_debug_printf(dlevel, "Filter %s\n", filter->name);
98     ci_list_iterate(filter->data, data, print_srv_cf_user_filter_data);
99     return 0;
100 }
101 
srv_cf_filters_debug_print(int dlevel)102 void srv_cf_filters_debug_print(int dlevel)
103 {
104     /*We should pop elements and free them*/
105     if (FILTERS)
106         ci_ptr_dyn_array_iterate(FILTERS, &dlevel, print_user_filter);
107 }
108 
matchBodyRegex(const srv_cf_user_filter_data_t * fd,ci_request_t * req,ci_membuf_t * body,int * count,ci_list_t * matches)109 static int matchBodyRegex(const srv_cf_user_filter_data_t *fd, ci_request_t *req, ci_membuf_t *body, int *count, ci_list_t *matches)
110 {
111     int score, ret;
112     const char *str = body->buf;
113     int str_len = ci_membuf_size(body);
114 
115     score = 0;
116     ret = ci_regex_apply(fd->regex_compiled, str, str_len, fd->recursive, matches, fd);
117 
118     if (ret > 0) {
119         ci_debug_printf(5, "matchBodyRegex:Match rule type:%d regex:%s score:%d, count: %d\n", fd->type, fd->regex_str, fd->score, ret);
120         if (count)
121             *count += ret;
122         score = ret * fd->score;
123     }
124 
125     return score;
126 }
127 
matchHeaderRegex(const srv_cf_user_filter_data_t * fd,ci_headers_list_t * headers,int * count,ci_list_t * matches)128 static int matchHeaderRegex(const srv_cf_user_filter_data_t *fd, ci_headers_list_t *headers, int *count, ci_list_t *matches)
129 {
130     int i;
131     const char *header;
132 
133     if (fd->header) {
134         if ((header = ci_headers_search(headers, fd->header))) {
135             ci_debug_printf(3, "matchHeaderRegex:Apply to the header %s the regex '%s'\n", header, fd->regex_str);
136             if (ci_regex_apply(fd->regex_compiled, header, -1, 0, matches, fd)) {
137                 ci_debug_printf(3, "matchHeaderRegex:Match rule type:%d, regex:%s, header: %s, score:%d\n", fd->type, fd->regex_str, fd->header, fd->score);
138                 if (count)
139                     ++(*count);
140                 return fd->score;
141             }
142         }
143     } else {
144         /*Apply to all headers*/
145         for (i = 0; i < headers->used; ++i) {
146             if (ci_regex_apply(fd->regex_compiled, headers->headers[i], -1, 0, matches, fd)) {
147                 ci_debug_printf(3, "matchHeaderRegex: Match rule type:%d regex:%s, score:%d\n", fd->type, fd->regex_str, fd->score);
148                 if (count)
149                     ++(*count);
150                 return fd->score;
151             }
152         }
153     }
154     return 0;
155 }
156 
matchResponseHeaderRegex(const srv_cf_user_filter_data_t * fd,ci_request_t * req,ci_membuf_t * body,int * count,ci_list_t * matches)157 static int matchResponseHeaderRegex(const srv_cf_user_filter_data_t *fd, ci_request_t *req, ci_membuf_t *body, int *count, ci_list_t *matches)
158 {
159     ci_headers_list_t *headers;
160 
161     if (!(headers = ci_http_response_headers(req)))
162         return 0;
163 
164     return matchHeaderRegex(fd, headers, count, matches);
165 }
166 
matchRequestHeaderRegex(const srv_cf_user_filter_data_t * fd,ci_request_t * req,ci_membuf_t * body,int * count,ci_list_t * matches)167 static int matchRequestHeaderRegex(const srv_cf_user_filter_data_t *fd, ci_request_t *req, ci_membuf_t *body, int *count, ci_list_t *matches)
168 {
169     ci_headers_list_t *headers;
170 
171     if (!(headers = ci_http_request_headers(req)))
172         return 0;
173 
174     return matchHeaderRegex(fd, headers, count, matches);
175 }
176 
177 #define header_end(e) (e == '\0' || e == '\n' || e == '\r')
get_full_http_request_url(ci_request_t * req,char * buf,int buf_size)178 static int get_full_http_request_url(ci_request_t * req, char *buf, int buf_size)
179 {
180     ci_headers_list_t *heads;
181     const char *str, *host;
182     int i, bytes;
183     /*The request must have the form:
184       GET url HTTP/X.X
185     */
186     if (!(heads = ci_http_request_headers(req)))
187         return 0;
188 
189     if (!heads->used)
190         return 0;
191 
192     str = heads->headers[0];
193 
194     if ((str = strchr(str, ' ')) == NULL) { /*Ignore method i*/
195         return 0;
196     }
197     while (*str == ' ') /*ignore spaces*/
198         str++;
199 
200     bytes = 0;
201     if (*str == '/' && (host = ci_headers_value(heads,"Host"))) {
202         /*Looks like a transparent proxy, we do not know the protocol lets try
203           to preserve the major part of the url....
204         */
205         for (i=0; (i < buf_size-1) && !header_end(host[i]) && !isspace(host[i]); i++) {
206             buf[i] = host[i];
207         }
208         buf += i;
209         buf_size -= i;
210         bytes = i;
211     }
212 
213     /*copy the url...*/
214     for (i=0; (i < buf_size-1) && !header_end(str[i]) && !isspace(str[i]); i++) {
215         buf[i] = str[i];
216     }
217     buf[i] = '\0';
218     bytes += i;
219     return bytes;
220 }
221 
matchUrlRegex(const srv_cf_user_filter_data_t * fd,ci_request_t * req,char * url,int url_size,int * count,ci_list_t * matches)222 static int matchUrlRegex(const srv_cf_user_filter_data_t *fd, ci_request_t *req, char *url, int url_size, int *count, ci_list_t *matches)
223 {
224     if (ci_regex_apply(fd->regex_compiled, url, url_size, 0, matches, fd)) {
225         if (count)
226             ++(*count);
227 
228         ci_debug_printf(3, "Match rule, type:%d regex:%s, score:%d\n", fd->type, fd->regex_str, fd->score);
229         return fd->score;
230     }
231     return 0;
232 }
233 
234 #define URL_MAX_SIZE 65535
235 struct FilterApplyData {
236     ci_request_t *req;
237     ci_membuf_t *body;
238     char url[URL_MAX_SIZE];
239     int url_size;
240     int compute_replacements;
241     ci_list_t *filterResults;
242     ci_list_t *replaceParts; /*list of ci_regex_replace_part_t*/
243 };
244 
apply_filter_step(struct FilterApplyData * fad,const srv_cf_user_filter_data_t * fd,int * score)245 static int apply_filter_step(struct FilterApplyData *fad, const srv_cf_user_filter_data_t *fd, int *score)
246 {
247     int matchCount;
248     ci_request_t *req = fad->req;
249     ci_membuf_t *body= fad->body;
250 
251     ci_debug_printf(5, "apply_filter_step:Start filter applying\n");
252 
253     matchCount = 0;
254     *score = 0;
255     if (fd->type == BodyRegex) {
256         *score = matchBodyRegex(fd, req, body, &matchCount, fad->replaceParts);
257     } else if (fd->type == HeaderRegex) {
258         *score = matchResponseHeaderRegex(fd, req, body, &matchCount, fad->replaceParts);
259     } else if (fd->type == RequestHeaderRegex) {
260         *score = matchRequestHeaderRegex(fd, req, body, &matchCount, NULL /*can not replace*/);
261     } else if (fd->type == UrlRegex) {
262         if (!(fad->url_size))
263             fad->url_size = get_full_http_request_url(req, fad->url, URL_MAX_SIZE);
264 
265         if (fad->url_size)
266             *score = matchUrlRegex(fd, req, fad->url, fad->url_size, &matchCount, NULL /*can not replace*/);
267     }
268     ci_debug_printf(5, "apply_filter_step: score:%d, matchCount:%d\n", *score, matchCount);
269     return matchCount;
270 }
271 
apply_filter(struct FilterApplyData * fad,const srv_cf_user_filter_t * filter)272 static int apply_filter( struct FilterApplyData *fad, const srv_cf_user_filter_t *filter)
273 {
274     struct FilterResult result;
275     const srv_cf_user_filter_data_t *fd;
276     int matchCount, score, stepScore;
277 
278     ci_debug_printf(5, "Will apply filter %s\n", filter->name);
279 
280     matchCount = 0;
281     stepScore = 0;
282     for (fd = ci_list_first(filter->data); fd != NULL; fd = ci_list_next(filter->data)) {
283 
284         if (/*Need to replace parts &&*/ fad->replaceParts == NULL) {
285             fad->replaceParts = ci_regex_create_match_list();
286         }
287 
288         if (fd) {
289             score = 0;
290             matchCount += apply_filter_step(fad, fd, &score);
291             stepScore += score;
292         }
293     }
294 
295 
296     if (stepScore) {
297         result.matchingFilter = filter;
298         result.count = matchCount;
299         result.score = stepScore;
300         ci_debug_printf(3, "apply_filter: Match result for rule %s, count:%d, score: %d\n",
301                         result.matchingFilter->name, result.count, result.score);
302         if (fad->filterResults == NULL)
303             fad->filterResults = ci_list_create(32768, sizeof(struct FilterResult));
304         ci_list_push_back(fad->filterResults, &result);
305     }
306 
307     return 0;
308 }
309 
membuf_terminate(ci_membuf_t * mem)310 static int membuf_terminate(ci_membuf_t *mem)
311 {
312     if (mem->bufsize > mem->endpos)
313         mem->buf[mem->endpos] = '\0';
314     else {
315         char buf[1];
316         buf[0] = '\0';
317         if (ci_membuf_write(mem, buf, 1, 0) <= 0) // not able to write ???
318             return 0;
319         --mem->endpos;
320     }
321 
322     return 1;
323 }
324 
findFilterResult(ci_list_t * results,const srv_cf_user_filter_t * f)325 const struct FilterResult *findFilterResult(ci_list_t *results, const srv_cf_user_filter_t *f)
326 {
327     ci_list_item_t *item;
328     if (!results)
329         return NULL;
330 
331     for (item = results->items; item != NULL; item = item->next) {
332         const struct FilterResult *fr;
333         fr = item->item;
334         ci_debug_printf(3, "Check if %s/%p and %s/%p matches\n", fr->matchingFilter->name, fr->matchingFilter, f->name, f);
335         if (fr && fr->matchingFilter == f) {
336             return fr;
337             ci_debug_printf(3, "Found rule %s, count: %d, score:%d\n", fr->matchingFilter->name, fr->count, fr->score);
338         }
339     }
340 
341     return NULL;
342 }
343 
srv_cf_print_scores_list(ci_list_t * scores,char * buf,int buf_size)344 int srv_cf_print_scores_list(ci_list_t *scores, char *buf, int buf_size)
345 {
346     ci_list_item_t *item;
347     char *str;
348     int bytes, str_len;
349     if (!scores || buf_size <= 1)
350         return 0;
351 
352     str = buf;
353     str_len = buf_size;
354     for (item = scores->items; item != NULL && str_len > 0; item = item->next) {
355         const struct FilterResult *fr;
356         fr = item->item;
357         bytes = snprintf(str, str_len, "%s%s=%d", (str != buf ? ",": ""), fr->matchingFilter->name, fr->score);
358         str += bytes;
359         str_len -= bytes;
360     }
361     if (str_len <= 0) {
362         buf[buf_size -1] = '\0';
363         return buf_size;
364     }
365     return (buf_size - str_len);
366 }
367 
cmp_replacement_func(const void * obj,const void * user_data,size_t user_data_size)368 int cmp_replacement_func(const void *obj, const void *user_data, size_t user_data_size)
369 {
370     const ci_regex_replace_part_t *listRepl = (const ci_regex_replace_part_t *)obj;
371     const ci_regex_replace_part_t *cmpRepl = (const ci_regex_replace_part_t *)user_data;
372     assert(user_data_size == sizeof(ci_regex_replace_part_t));
373 
374     ci_debug_printf(5, "will compare (%p<>%p): %d-%d <> %d-%d :", listRepl, cmpRepl, (int)listRepl->matches[0].s, (int)listRepl->matches[0].e, (int)cmpRepl->matches[0].s, (int)cmpRepl->matches[0].e);
375     /*If they are the same object stop searching*/
376     if (listRepl == cmpRepl){
377         ci_debug_printf(5,"the same\n");
378         return 0;
379     }
380     const srv_cf_user_filter_data_t *list_filter_data = (const srv_cf_user_filter_data_t *)listRepl->user_data;
381     const srv_cf_user_filter_data_t *cmp_filter_data = (const srv_cf_user_filter_data_t *)cmpRepl->user_data;
382     /*if they are not the same type are not equal*/
383     if (list_filter_data->type != cmp_filter_data->type){
384         ci_debug_printf(5,"no same type\n");
385         return -1;
386     }
387 
388     if ((list_filter_data->type == HeaderRegex || list_filter_data->type == RequestHeaderRegex)) {
389         /*if one of two objects does not have header definition are not equal*/
390         if ((!list_filter_data->header && cmp_filter_data->header) ||
391             (list_filter_data->header && !cmp_filter_data->header)) {
392             ci_debug_printf(5,"no header one of them\n");
393             return -1;
394         }
395         /*if one of two objects have header definition and header names are not the same then
396           they are not equal*/
397         if (list_filter_data->header && cmp_filter_data->header && strcmp(list_filter_data->header, cmp_filter_data->header) !=0) {
398             ci_debug_printf(5,"different headers\n");
399             return -1;
400         }
401     }
402 
403     /*Now check if the replacements intercepts*/
404     if ((listRepl->matches[0].s <= cmpRepl->matches[0].s && listRepl->matches[0].e >= cmpRepl->matches[0].s) ||
405         (listRepl->matches[0].s <= cmpRepl->matches[0].e && listRepl->matches[0].e >= cmpRepl->matches[0].e)
406         ) {
407         ci_debug_printf(5,"1\n");
408         return 0;
409     }
410     if ((cmpRepl->matches[0].s <= listRepl->matches[0].s && cmpRepl->matches[0].e >= listRepl->matches[0].s) ||
411         (cmpRepl->matches[0].s <= listRepl->matches[0].e && cmpRepl->matches[0].e >= listRepl->matches[0].e)
412         ) {
413                 ci_debug_printf(5,"2\n");
414         return 0;
415     }
416     ci_debug_printf(5,"not matches\n");
417     return -1;
418 }
419 
remove_overlaped_replacement(ci_list_t * replaceParts)420 void remove_overlaped_replacement(ci_list_t *replaceParts)
421 {
422     ci_regex_replace_part_t *replacement;
423     const ci_regex_replace_part_t *tmp;
424 
425 
426     if (!replaceParts)
427         return;
428     for (replacement = ci_list_first(replaceParts); replacement != NULL; replacement = ci_list_next(replaceParts)) {
429         const srv_cf_user_filter_data_t *filter_data = (const srv_cf_user_filter_data_t *)replacement->user_data;
430         ci_debug_printf(5, "Check %p of type %d '%s':start=%d,end=%d\n",replacement, filter_data->type, filter_data->regex_str, (int)replacement->matches[0].s,  (int)replacement->matches[0].e)
431         tmp = ci_list_search2(replaceParts, replacement, cmp_replacement_func);
432         if (tmp && tmp != replacement) {
433             ci_debug_printf(5, "\tReplacement (%p<>%p) will be removed\n",replacement, tmp);
434             ci_list_remove(replaceParts, replacement);
435         }
436     }
437 }
438 
439 
cmp_replace_part_t_func(const void * obj1,const void * obj2,size_t user_data_size)440 int cmp_replace_part_t_func(const void *obj1, const void *obj2, size_t user_data_size)
441 {
442     int ret;
443     const ci_regex_replace_part_t *repl1 = obj1;
444     const ci_regex_replace_part_t *repl2 = obj2;
445     const srv_cf_user_filter_data_t *repl1_filter_data = (const srv_cf_user_filter_data_t *)repl1->user_data;
446     const srv_cf_user_filter_data_t *repl2_filter_data = (const srv_cf_user_filter_data_t *)repl2->user_data;
447     assert(user_data_size == sizeof(ci_regex_replace_part_t));
448 
449 
450     /*if they are not the same type are not equal*/
451     if (repl1_filter_data->type != repl2_filter_data->type)
452         return (repl1_filter_data->type - repl2_filter_data->type);
453 
454     /*if one of two objects does not have header definition are not equal*/
455     if (!repl1_filter_data->header && repl2_filter_data->header)
456         return -1;
457 
458     if (repl1_filter_data->header && !repl2_filter_data->header)
459         return 1;
460 
461     /*if both of two objects have header definition and header names are not the same then
462       they are not equal*/
463     if (repl1_filter_data->header && repl2_filter_data->header &&
464         (ret = strcmp(repl1_filter_data->header, repl2_filter_data->header)) != 0)
465         return ret;
466 
467     /*The tow objects refers to the same data, compare start of their segments*/
468     return (repl1->matches[0].s - repl2->matches[0].s);
469 }
470 
apply_filters_list(const srv_cf_profile_t * prof,struct FilterApplyData * fad)471 int apply_filters_list(const srv_cf_profile_t *prof, struct FilterApplyData *fad)
472 {
473     srv_cf_filter_apply_t *prp;
474     int filtersCount = 0;
475     for (prp = ci_list_first(prof->filters); prp != NULL; prp = ci_list_next(prof->filters)) {
476         if (prp->filter) {
477             ci_debug_printf(5, "Will apply filter %s\n", prp->filter->name);
478             apply_filter(fad, prp->filter);
479             ++filtersCount;
480         }
481     }
482     return filtersCount;
483 }
484 
srv_cf_apply_actions(ci_request_t * req,const srv_cf_profile_t * profile,ci_membuf_t * body,srv_cf_results_t * result,struct ci_fmt_entry * fmtTable)485 int srv_cf_apply_actions(ci_request_t *req, const srv_cf_profile_t *profile, ci_membuf_t *body, srv_cf_results_t *result, struct ci_fmt_entry *fmtTable)
486 {
487     char buf[1024];
488     struct FilterApplyData fad;
489     const struct FilterResult *fr;
490     const srv_cf_action_cfg_t *actionEntry;
491     ci_list_t *replaceInfoTags = NULL;  /*list of (const char *) */
492     int filtersCount;
493     int i;
494 
495     ci_debug_printf(5, "Going to do content filtering!\n");
496 
497     /*Null tetrminate the membuf*/
498     if (!membuf_terminate(body))
499         return  0;
500 
501     fad.req = req;
502     fad.body = body;
503     fad.filterResults = NULL;
504     fad.replaceParts = NULL;
505     fad.url[0] = '\0';
506     fad.url_size = 0;
507 
508     filtersCount = apply_filters_list(profile, &fad);
509 
510     if (!filtersCount) {
511         ci_debug_printf(2, "No filters configured for profile :%s!\n", profile->name);
512         return 0;
513     }
514 
515 
516     if (fad.filterResults) {
517         ci_debug_printf(5, "There are filter results\n");
518         for (fr = ci_list_first(fad.filterResults); fr != NULL; fr = ci_list_next(fad.filterResults)) {
519             ci_debug_printf(3, "Match rule %s, count: %d, score:%d\n", fr->matchingFilter->name, fr->count, fr->score);
520         }
521     } else {
522         ci_debug_printf(5, "There are not filter results!\n");
523     }
524 
525     const srv_cf_action_cfg_t *doAction = NULL;
526     if (profile->actions) {
527         for (actionEntry = ci_list_first(profile->actions); actionEntry != NULL && doAction == NULL; actionEntry = ci_list_next(profile->actions)) {
528             fr = findFilterResult(fad.filterResults, actionEntry->matchingFilter);
529 
530             if (fr &&
531                 ((actionEntry->scoreOperator == CF_OP_LESS && fr->score < actionEntry->score) ||
532                  (actionEntry->scoreOperator == CF_OP_GREATER && fr->score > actionEntry->score) ||
533                  (actionEntry->scoreOperator == CF_OP_EQUAL && fr->score == actionEntry->score))
534                 )
535             {
536                 /*Store to result the latest action*/
537                 result->action = actionEntry;
538                 result->action_score = fr->score;
539                 result->action_matchesCount = fr->count;
540                 if (actionEntry->action == CF_AC_REPLACE) {
541                     if (fad.replaceParts && actionEntry->replaceInfo) {
542                         if (!replaceInfoTags)
543                             replaceInfoTags = ci_list_create(1024, 0);/*zero size object means store pointers*/
544                         for (i = 0; actionEntry->replaceInfo[i] != NULL; ++i )
545                             ci_list_push_back(replaceInfoTags, actionEntry->replaceInfo[i]);
546                     }
547                 } else if (actionEntry->action == CF_AC_ADD_HEADER) {
548                     if (actionEntry->header[0]) {
549                         if (!result->addHeaders)
550                             result->addHeaders = ci_headers_create();
551                         if (ci_format_text(req, actionEntry->header, buf, sizeof(buf), fmtTable))
552                             ci_headers_add(result->addHeaders, buf);
553                     }
554                 } else if (actionEntry->action == CF_AC_BLOCK || actionEntry->action == CF_AC_ALLOW)
555                     doAction = actionEntry; /*Final Action*/
556             }
557         }
558     }
559 
560     if (doAction) {
561         ci_debug_printf(3, "Found action : %s\n", srv_cf_action_str(doAction->action));
562     }
563     result->scores = fad.filterResults;
564 
565     if (replaceInfoTags) {
566         ci_debug_printf(3, "DO REPLACE BODY!\n");
567         ci_membuf_t *newbody = ci_membuf_new_sized((ci_membuf_size(body)));
568         if (replacePartsToBody(body, newbody, fad.replaceParts, replaceInfoTags))
569             result->replaceBody = newbody;
570         ci_list_destroy(replaceInfoTags);
571         replaceInfoTags = NULL;
572     }
573 
574     /*
575      The list must destroyed by the caller function!
576       ci_list_destroy(fad.filterResults);
577     */
578     ci_list_destroy(fad.replaceParts);
579     return doAction != NULL;
580 }
581 
getReplacementForFilterRegex(const srv_cf_user_filter_data_t * filter_data,ci_list_t * replaceInfoTags)582 const char *getReplacementForFilterRegex(const srv_cf_user_filter_data_t *filter_data, ci_list_t *replaceInfoTags)
583 {
584     const char *tag;
585     const char *val;
586     if (!filter_data->infoStrings)
587         return NULL;
588 
589     for (tag = ci_list_first(replaceInfoTags); tag != NULL; tag = ci_list_next(replaceInfoTags)) {
590         if (tag && (val = ci_str_array_search(filter_data->infoStrings, tag)) != NULL)
591             return val;
592     }
593 
594     return NULL;
595 }
596 
replacePartsToBody(ci_membuf_t * body,ci_membuf_t * newbody,ci_list_t * replacements,ci_list_t * replaceInfoTags)597 int replacePartsToBody(ci_membuf_t *body, ci_membuf_t *newbody, ci_list_t *replacements, ci_list_t *replaceInfoTags)
598 {
599     ci_regex_replace_part_t *rpart;
600     const srv_cf_user_filter_data_t *filter_data;
601 
602     if (!replaceInfoTags)
603         return 0;
604 
605     ci_debug_printf(5, "Initial list:\n");
606     for (rpart = ci_list_first(replacements); rpart != NULL; rpart = ci_list_next(replacements)) {
607         filter_data = (const srv_cf_user_filter_data_t *)rpart->user_data;
608         ci_debug_printf(5, "\tReplace text type: %d regex:'%s' segment:%d-%d\n", (int)filter_data->type,  filter_data->regex_str,  (int)rpart->matches[0].s, (int)rpart->matches[0].e);
609     }
610 
611     remove_overlaped_replacement(replacements);
612     ci_list_sort2(replacements, cmp_replace_part_t_func);
613 
614     ci_debug_printf(5, "Final list:\n");
615     for (rpart = ci_list_first(replacements); rpart != NULL; rpart = ci_list_next(replacements)) {
616         filter_data = (const srv_cf_user_filter_data_t *)rpart->user_data;
617         ci_debug_printf(5, "\tReplace text type: %d regex:'%s' segment:%d-%d\n", (int)filter_data->type,  filter_data->regex_str,  (int)rpart->matches[0].s, (int)rpart->matches[0].e);
618     }
619 
620     int i;
621     const char *replaceWithStr = " $1_$2_$3 ";
622     size_t pos = 0;
623     const char *data, *s;
624     data = s = body->buf;
625     for (rpart = ci_list_first(replacements); rpart != NULL; rpart = ci_list_next(replacements)) {
626         filter_data = (const srv_cf_user_filter_data_t *)rpart->user_data;
627         if (filter_data->type != BodyRegex)
628             continue;
629         if (!(replaceWithStr = getReplacementForFilterRegex(filter_data, replaceInfoTags)))
630             continue;
631         pos = rpart->matches[0].s;
632         ci_debug_printf(5,"Will Add %lu of %s\n", (unsigned long)(pos - (s - data)), s);
633         ci_membuf_write(newbody, s, pos - (s - data), 0);
634         for (i = 0; i < strlen(replaceWithStr); ++i) {
635             if (replaceWithStr[i] == '$' && (i == 0 || replaceWithStr[i-1] != '\\')
636                 && replaceWithStr[i +1] >= '0' && replaceWithStr[i +1] <= '9') {
637                 ci_membuf_write(newbody,
638                                 data + rpart->matches[replaceWithStr[i + 1] - '0' ].s,
639                                 rpart->matches[replaceWithStr[i + 1] - '0' ].e - rpart->matches[replaceWithStr[i + 1] - '0' ].s,
640                                 0);
641                 ++i;
642             } else
643                 ci_membuf_write(newbody, replaceWithStr+i, 1, 0);
644         }
645         s = data + rpart->matches[0].e;
646     }
647     if (s && (body->endpos - (s - data)) > 0)
648         ci_membuf_write(newbody, s, body->endpos - (s - data), 0);
649 
650     ci_membuf_write(newbody, "", 0, 1);
651     return 1;
652 }
653 
free_srv_cf_user_filter_data(struct srv_cf_user_filter_data * fd)654 void free_srv_cf_user_filter_data(struct srv_cf_user_filter_data *fd)
655 {
656     if (fd->header)
657         free(fd->header);
658     if (fd->regex_str) {
659         free(fd->regex_str);
660         ci_regex_free(fd->regex_compiled);
661     }
662     if (fd->infoStrings)
663         ci_str_array_destroy(fd->infoStrings);
664     free(fd);
665 }
666 
free_srv_cf_user_filter(srv_cf_user_filter_t * fdef)667 void free_srv_cf_user_filter(srv_cf_user_filter_t *fdef)
668 {
669     srv_cf_user_filter_data_t *fd;
670     if (fdef->name)
671         free(fdef->name);
672     if (fdef->data) {
673         while(ci_list_pop(fdef->data, &fd) != NULL) {
674             free_srv_cf_user_filter_data(fd);
675         }
676         ci_list_destroy(fdef->data);
677     }
678     free(fdef);
679 }
680 
loadRulesFromFile(srv_cf_user_filter_t * filter,const char * file,int type,const char * typeArg)681 int loadRulesFromFile(srv_cf_user_filter_t *filter, const char *file, int type, const char *typeArg)
682 {
683     struct srv_cf_user_filter_data *fd = NULL;
684     int lineNumber = 0;
685     char line[65536];
686     char *s, *e;
687     char *infoName, *infoVal;
688 
689     FILE *f;
690     if ((f = fopen(file, "r+")) == NULL) {
691         ci_debug_printf(1, "Error opening file: %s\n", file);
692         return 0;
693     }
694 
695     while(fgets(line, sizeof(line) - 1, f)) {
696         lineNumber++;
697         line[sizeof(line) - 1] = '\0';
698         e = line + strlen(line);
699         // Remove spaces at the end of line.
700         while (e > line && index(" \t\r\n", *e)) {
701             *e = '\0';
702             --e;
703         }
704 
705         s = line + strspn(line, " \t\r\n");
706 
707         if (*s == '#' || *s == '\0') /*this is a comment or empty line*/
708             continue;
709 
710         fd = malloc(sizeof(struct srv_cf_user_filter_data));
711         if (!fd) {
712             ci_debug_printf(1, "Error allocation memory, while parsing file '%s'!\n", file);
713             fclose(f);
714             return 0;
715         }
716         fd->type = type;
717         fd->header = typeArg ? strdup(typeArg) : NULL;
718         fd->regex_str = NULL;
719         fd->regex_flags = 0;
720         fd->recursive = 0;
721         fd->regex_compiled = NULL;
722         fd->score = 0;
723         fd->infoStrings = NULL;
724 
725         while(*s) {
726             if (strncmp(s, "score=", 6) == 0) {
727                 s += 6;
728                 fd->score = strtol(s, &e, 10);
729                 if (s == e) {
730                     ci_debug_printf(1, "Error parsing file: %s, line %d: '%s'\n", file, lineNumber, s);
731                     free_srv_cf_user_filter_data(fd);
732                     fclose(f);
733                     return 0;
734                 }
735             } else if (strncmp(s, "info{", 5) == 0) {
736                 infoName = s + 5;
737                 if ((e = strchr(infoName, '}')) == NULL ||  *(e + 1) != '=') {
738                     ci_debug_printf(1, "Error parsing file '%s', line %d,  Expecting info{InfoName}=InfoValue got '%s'\n", file, lineNumber, s);
739                     free_srv_cf_user_filter_data(fd);
740                     fclose(f);
741                     return 0;
742                 }
743                 *e = '\0';
744                 infoVal = e + 2;
745                 e = infoVal + strcspn(infoVal, " \t\r");
746                 if (!e) {
747                     ci_debug_printf(1, "Error parsing file '%s', line %d,  expecting regex expression at the end of line\n", file, lineNumber);
748                     free_srv_cf_user_filter_data(fd);
749                     fclose(f);
750                     return 0;
751                 }
752                 *e = '\0';
753                 ++e;
754                 if (!fd->infoStrings) {
755                     fd->infoStrings = ci_str_array_new(1024);
756                 }
757                 ci_str_array_add(fd->infoStrings, infoName, infoVal);
758             } else
759                 break;
760             s = e + strspn(e, " \t\r"); /*should point to space*/
761         }
762 
763         if ((fd->regex_str = ci_regex_parse(s, &fd->regex_flags, &fd->recursive))) {
764             fd->regex_compiled = ci_regex_build(fd->regex_str, fd->regex_flags);
765         }
766         if (!fd->regex_compiled) {
767             ci_debug_printf(1, "Error parsing file '%s', line %d,  regex expression: '%s'\n", file, lineNumber, fd->regex_str);
768             free_srv_cf_user_filter_data(fd);
769             fclose(f);
770             return 0;
771         }
772 
773         if (!ci_list_push_back(filter->data, fd)) {
774             ci_debug_printf(1, "Unable to add rule: %s\n", fd->regex_str);
775             free_srv_cf_user_filter_data(fd);
776             fclose(f);
777             return 0;
778         }
779     }
780     fclose(f);
781     return 1;
782 }
783 
srv_cf_cfg_match(const char * directive,const char ** argv,void * setdata)784 int srv_cf_cfg_match(const char *directive,const char **argv,void *setdata)
785 {
786     int argc, i, type;
787     char *name, *infoName, *infoVal;
788     srv_cf_user_filter_t *filter;
789     struct srv_cf_user_filter_data *fd = NULL;
790     const char *rulesFromFile = NULL;
791 
792     for (argc = 0; argv[argc] != NULL; ++argc);
793 
794     if (argc < 3) {
795         ci_debug_printf(1, "Missing arguments for '%s' cfg parameter!\n", directive);
796         return 0;
797     }
798 
799     name = strdup(argv[0]);
800     char *typeParam = strdup(argv[1]);
801     char *typeArg = NULL;
802     char *e;
803     if ((typeArg = strchr(typeParam, '{'))) {
804         *typeArg = '\0';
805         typeArg++;
806         e = strchr(typeArg, '}');
807         if (e)
808             *e = '\0';
809     }
810     ci_debug_printf(4, "Type parameter: %s arg:%s\n", typeParam, typeArg);
811 
812     if (strcasecmp(typeParam, "body") == 0)
813         type = BodyRegex;
814     else if (strcasecmp(typeParam, "header") == 0)
815         type = HeaderRegex;
816     else if (strcasecmp(typeParam, "request_header") == 0 || strcasecmp(typeParam, "requestHeader") == 0)
817         type = RequestHeaderRegex;
818     else if (strcasecmp(typeParam, "url") == 0)
819         type = UrlRegex;
820     else {
821         ci_debug_printf(1, "Expecting [body|header|request_header|url], got '%s'!\n", typeParam);
822         free(typeParam);
823         return 0;
824     }
825     free(typeParam);
826 
827 
828     if (strncasecmp(argv[2], "file:", 5) == 0) {
829         rulesFromFile = argv[2] + 5;
830     }
831 
832     if (!rulesFromFile) {
833         fd = malloc(sizeof(struct srv_cf_user_filter_data));
834         if (!fd) {
835             ci_debug_printf(1, "Error allocation memory!\n");
836             return 0;
837         }
838         fd->type = type;
839         fd->header = typeArg ? strdup(typeArg) : NULL;
840         fd->regex_str = NULL;
841         fd->regex_flags = 0;
842         fd->recursive = 0;
843         fd->regex_compiled = NULL;
844         fd->score = 0;
845         fd->infoStrings = NULL;
846 
847 
848         if ((fd->regex_str = ci_regex_parse(argv[2], &fd->regex_flags, &fd->recursive))) {
849             fd->regex_compiled = ci_regex_build(fd->regex_str, fd->regex_flags);
850         }
851         if (!fd->regex_compiled) {
852             ci_debug_printf(1, "Error parsing regex expression: %s\n", fd->regex_str);
853             free_srv_cf_user_filter_data(fd);
854             return 0;
855         }
856 
857         fd->score = 1;
858 
859         if (argc > 3) {
860             for (i = 3; i < argc; ++i) {
861                 if (strncmp(argv[i], "score=", 6) == 0) {
862                     fd->score = strtol((argv[i] + 6), NULL, 10);
863                 } if (strncmp(argv[i], "info{", 5) == 0) {
864                     ci_debug_printf(7, "Got: %s\n", argv[i]);
865                     char *tmp = strdup(argv[i]);
866                     infoName = tmp + 5;
867                     if ((e = strchr(tmp, '}')) == NULL ||  *(e + 1) != '=') {
868                         ci_debug_printf(1, "srv_cf_cfg_match: parse error: Expecting info{InfoName}=InfoValue got '%s'\n", tmp);
869                         free_srv_cf_user_filter_data(fd);
870                         free(tmp);
871                         return 0;
872                     }
873                     *e = '\0';
874                     infoVal = e + 2;
875                     ci_debug_printf(7, "Got Name '%s', got value: '%s'\n", infoName, infoVal);
876                     if (!fd->infoStrings) {
877                         fd->infoStrings = ci_str_array_new(1024);
878                     }
879                     ci_str_array_add(fd->infoStrings, infoName, infoVal);
880                     free(tmp);
881                 } else {
882                     /*error*/
883                 }
884             }
885         }
886     }
887 
888     if (!FILTERS)
889         FILTERS = ci_ptr_dyn_array_new(4096);
890 
891     filter = (void *)ci_ptr_dyn_array_search(FILTERS, name);
892     if (!filter) {
893         filter = (srv_cf_user_filter_t *)malloc(sizeof(srv_cf_user_filter_t));
894         filter->name = name;
895         ci_ptr_dyn_array_add(FILTERS, name, filter);
896         filter->data = ci_list_create(4096, 0); /*zero sized object mean store pointers*/
897     } else {
898         free(name);
899     }
900 
901     if (rulesFromFile) {
902         assert(!fd);
903         return loadRulesFromFile(filter, rulesFromFile, type, typeArg);
904     }
905 
906     assert(fd);
907 
908     if (!ci_list_push_back(filter->data, fd)) {
909         ci_debug_printf(1, "Unable to add rule: %s\n", fd->regex_str);
910         free_srv_cf_user_filter_data(fd);
911         return 0;
912     }
913 
914     return 1;
915 }
916 
srv_cf_action_parse(const char * str)917 int srv_cf_action_parse(const char *str)
918 {
919     if (strcasecmp(str, "block") == 0)
920         return CF_AC_BLOCK;
921     else if (strcasecmp(str, "allow") == 0)
922         return CF_AC_ALLOW;
923     else if (strcasecmp(str, "addheader") == 0 || strcasecmp(str, "add_header") == 0)
924         return CF_AC_ADD_HEADER;
925     else if (strcasecmp(str, "replace") == 0)
926         return CF_AC_REPLACE;
927     else
928         return CF_AC_NONE;
929 }
930 
srv_cf_action_score_parse(const char * str,int * scoreOperator,int * score)931 const srv_cf_user_filter_t *srv_cf_action_score_parse(const char *str, int *scoreOperator, int *score)
932 {
933     const srv_cf_user_filter_t *fd = NULL;
934     char *scoreParam = strdup(str);
935     char *scoreArg = NULL;
936     char *e;
937     *score = 0;
938     *scoreOperator = -1;
939     if ((scoreArg = strchr(scoreParam, '{'))) {
940         *scoreArg = '\0';
941         scoreArg++;
942         if ((e = strchr(scoreArg, '}')))
943             *e = '\0';
944     }
945     if (strcasecmp(scoreParam, "score") != 0 || !scoreArg) {
946         ci_debug_printf(1, "Expecting 'score{...}' argument, got '%s'\n", scoreParam);
947         free(scoreParam);
948         return NULL;
949     }
950     ci_debug_printf(4, "Score parameter: %s argument:%s\n", scoreParam, scoreArg);
951     size_t pos = strcspn(scoreArg, ">=<");
952     char *op = scoreArg + pos;
953     if (*op != '\0') {
954         *scoreOperator = *op == '>' ? CF_OP_GREATER : (*op == '<' ? CF_OP_LESS : CF_OP_EQUAL);
955         *op = '\0';
956         if (*(op + 1) != '\0')
957             *score = strtol(op + 1, NULL, 10);
958     } else {
959         *scoreOperator = CF_OP_GREATER;
960     }
961     if (FILTERS && !(fd = ci_ptr_dyn_array_search(FILTERS, scoreArg))) {
962         ci_debug_printf(1, "Filter definition for '%s' not found\n", scoreArg);
963     }
964     /* End of parsing, free temporary buffer*/
965     free(scoreParam);
966 
967     return fd;
968 }
969 
970