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