1 /*-
2  * SSLproxy
3  *
4  * Copyright (c) 2017-2021, Soner Tari <sonertari@gmail.com>.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  * 1. Redistributions of source code must retain the above copyright notice,
10  *    this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  *    this list of conditions and the following disclaimer in the documentation
13  *    and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "opts.h"
29 #include "filter.h"
30 
31 #include "sys.h"
32 #include "log.h"
33 #include "util.h"
34 
35 ACM_DEFINE (char);
36 
37 #define free_list(list, type) do { \
38 	while (list) { \
39 		type *next = (list)->next; \
40 		free(list); \
41 		list = next; \
42 	} \
43 } while (0)
44 
45 #define append_list(list, value, type) do { \
46 	type *l = *list; \
47 	while (l) { \
48 		if (!l->next) \
49 			break; \
50 		l = l->next; \
51 	} \
52 	if (l) \
53 		l->next = value; \
54 	else \
55 		*list = value; \
56 } while (0)
57 
58 #define match_acm(acm, haystack, value) do { \
59 	const ACState(char) *state = ACM_reset(acm); \
60 	for (char *c = haystack; *c; c++) { \
61 		if (ACM_match(state, *c)) { \
62 			ACM_get_match(state, 0, 0, (void **)&value); \
63 			break; \
64 		} \
65 	} \
66 } while (0)
67 
68 #ifndef WITHOUT_USERAUTH
69 void
filter_userlist_free(userlist_t * ul)70 filter_userlist_free(userlist_t *ul)
71 {
72 	while (ul) {
73 		userlist_t *next = ul->next;
74 		free(ul->user);
75 		free(ul);
76 		ul = next;
77 	}
78 }
79 
80 int
filter_userlist_copy(userlist_t * userlist,const char * argv0,userlist_t ** ul)81 filter_userlist_copy(userlist_t *userlist, const char *argv0, userlist_t **ul)
82 {
83 	while (userlist) {
84 		userlist_t *du = malloc(sizeof(userlist_t));
85 		if (!du)
86 			return oom_return(argv0);
87 		memset(du, 0, sizeof(userlist_t));
88 
89 		du->user = strdup(userlist->user);
90 		if (!du->user)
91 			return oom_return(argv0);
92 
93 		append_list(ul, du, userlist_t);
94 
95 		userlist = userlist->next;
96 	}
97 	return 0;
98 }
99 
100 char *
filter_userlist_str(userlist_t * u)101 filter_userlist_str(userlist_t *u)
102 {
103 	char *us = NULL;
104 
105 	if (!u) {
106 		us = strdup("");
107 		if (!us)
108 			return oom_return_na_null();
109 		goto out;
110 	}
111 
112 	while (u) {
113 		char *nus;
114 		if (asprintf(&nus, "%s%s%s", STRORNONE(us), us ? "," : "", u->user) < 0) {
115 			goto err;
116 		}
117 
118 		if (us)
119 			free(us);
120 		us = nus;
121 		u = u->next;
122 	}
123 	goto out;
124 err:
125 	if (us) {
126 		free(us);
127 		us = NULL;
128 	}
129 out:
130 	return us;
131 }
132 
133 // Limit the number of users to max 50
134 #define MAX_USERS 50
135 
136 int
filter_userlist_set(char * value,unsigned int line_num,userlist_t ** list,const char * listname)137 filter_userlist_set(char *value, unsigned int line_num, userlist_t **list, const char *listname)
138 {
139 	// Delimiter can be either or all of ",", " ", and "\t"
140 	// Using space as a delimiter disables spaces in user names too
141 	// user1[,user2[,user3]]
142 	char *argv[sizeof(char *) * MAX_USERS];
143 	int argc = 0;
144 	char *p, *last = NULL;
145 
146 	// strtok_r() removes all delimiters around user names, and does not return empty tokens
147 	for ((p = strtok_r(value, ", \t", &last));
148 		 p;
149 		 (p = strtok_r(NULL, ", \t", &last))) {
150 		if (argc < MAX_USERS) {
151 			argv[argc++] = p;
152 		} else {
153 			fprintf(stderr, "Too many arguments in user list, max users allowed %d, on line %d\n", MAX_USERS, line_num);
154 			return -1;
155 		}
156 	}
157 
158 	if (!argc) {
159 		fprintf(stderr, "%s requires at least one parameter on line %d\n", listname, line_num);
160 		return -1;
161 	}
162 
163 	// Override the copied global list, if any
164 	if (*list) {
165 		filter_userlist_free(*list);
166 		*list = NULL;
167 	}
168 
169 	while (argc--) {
170 		userlist_t *ul = malloc(sizeof(userlist_t));
171 		if (!ul)
172 			return oom_return_na();
173 		memset(ul, 0, sizeof(userlist_t));
174 
175 		ul->user = strdup(argv[argc]);
176 		if (!ul->user)
177 			return oom_return_na();
178 
179 		append_list(list, ul, userlist_t);
180 	}
181 	return 0;
182 }
183 #endif /* !WITHOUT_USERAUTH */
184 
185 static void
filter_value_free(value_t * value)186 filter_value_free(value_t *value)
187 {
188 	while (value) {
189 		value_t *next = value->next;
190 		free(value->value);
191 		free(value);
192 		value = next;
193 	}
194 }
195 
196 void
filter_macro_free(opts_t * opts)197 filter_macro_free(opts_t *opts)
198 {
199 	macro_t *macro = opts->macro;
200 	while (macro) {
201 		macro_t *next = macro->next;
202 		free(macro->name);
203 		filter_value_free(macro->value);
204 		free(macro);
205 		macro = next;
206 	}
207 	opts->macro = NULL;
208 }
209 
210 static void
filter_rule_free(filter_rule_t * rule)211 filter_rule_free(filter_rule_t *rule)
212 {
213 	if (rule->dstip)
214 		free(rule->dstip);
215 	if (rule->sni)
216 		free(rule->sni);
217 	if (rule->cn)
218 		free(rule->cn);
219 	if (rule->host)
220 		free(rule->host);
221 	if (rule->uri)
222 		free(rule->uri);
223 	if (rule->port)
224 		free(rule->port);
225 	if (rule->ip)
226 		free(rule->ip);
227 #ifndef WITHOUT_USERAUTH
228 	if (rule->user)
229 		free(rule->user);
230 	if (rule->desc)
231 		free(rule->desc);
232 #endif /* !WITHOUT_USERAUTH */
233 	if (rule->action.conn_opts)
234 		conn_opts_free(rule->action.conn_opts);
235 	free(rule);
236 }
237 
238 void
filter_rules_free(opts_t * opts)239 filter_rules_free(opts_t *opts)
240 {
241 	filter_rule_t *rule = opts->filter_rules;
242 	while (rule) {
243 		filter_rule_t *next = rule->next;
244 		filter_rule_free(rule);
245 		rule = next;
246 	}
247 	opts->filter_rules = NULL;
248 }
249 
250 #define free_port(p) do { \
251 	if ((*p)->action.conn_opts) \
252 		conn_opts_free((*p)->action.conn_opts); \
253 	free((*p)->port); \
254 	free(*p); \
255 } while (0)
256 
257 static void
free_port_func(void * p)258 free_port_func(void *p)
259 {
260 	free_port((filter_port_t **)&p);
261 }
262 
263 static void
filter_port_btree_free(kbtree_t (port)* btree)264 filter_port_btree_free(kbtree_t(port) *btree)
265 {
266 	if (btree) {
267 		__kb_traverse(filter_port_p_t, btree, free_port);
268 		__kb_destroy(btree);
269 	}
270 }
271 
272 #define free_site(p) do { \
273 	if ((*p)->action.conn_opts) \
274 		conn_opts_free((*p)->action.conn_opts); \
275 	free((*p)->site); \
276 	filter_port_btree_free((*p)->port_btree); \
277 	if ((*p)->port_acm) \
278 		ACM_release((*p)->port_acm); \
279 	if ((*p)->port_all) \
280 		free_port_func((*p)->port_all); \
281 	free(*p); \
282 } while (0)
283 
284 static void
free_site_func(void * s)285 free_site_func(void *s)
286 {
287 	free_site((filter_site_t **)&s);
288 }
289 
290 static void
filter_list_free(filter_list_t * list)291 filter_list_free(filter_list_t *list)
292 {
293 	if (list->ip_btree) {
294 		__kb_traverse(filter_site_p_t, list->ip_btree, free_site);
295 		__kb_destroy(list->ip_btree);
296 	}
297 	if (list->ip_acm)
298 		ACM_release(list->ip_acm);
299 	if (list->ip_all)
300 		free_site_func(list->ip_all);
301 
302 	if (list->sni_btree) {
303 		__kb_traverse(filter_site_p_t, list->sni_btree, free_site);
304 		__kb_destroy(list->sni_btree);
305 	}
306 	if (list->sni_acm)
307 		ACM_release(list->sni_acm);
308 	if (list->sni_all)
309 		free_site_func(list->sni_all);
310 
311 	if (list->cn_btree) {
312 		__kb_traverse(filter_site_p_t, list->cn_btree, free_site);
313 		__kb_destroy(list->cn_btree);
314 	}
315 	if (list->cn_acm)
316 		ACM_release(list->cn_acm);
317 	if (list->cn_all)
318 		free_site_func(list->cn_all);
319 
320 	if (list->host_btree) {
321 		__kb_traverse(filter_site_p_t, list->host_btree, free_site);
322 		__kb_destroy(list->host_btree);
323 	}
324 	if (list->host_acm)
325 		ACM_release(list->host_acm);
326 	if (list->host_all)
327 		free_site_func(list->host_all);
328 
329 	if (list->uri_btree) {
330 		__kb_traverse(filter_site_p_t, list->uri_btree, free_site);
331 		__kb_destroy(list->uri_btree);
332 	}
333 	if (list->uri_acm)
334 		ACM_release(list->uri_acm);
335 	if (list->uri_all)
336 		free_site_func(list->uri_all);
337 
338 	free(list);
339 }
340 
341 #ifndef WITHOUT_USERAUTH
342 #define free_desc(p) do { \
343 	free((*p)->desc); \
344 	filter_list_free((*p)->list); \
345 	free(*p); \
346 } while (0)
347 
348 static void
filter_user_free(filter_user_t * user)349 filter_user_free(filter_user_t *user)
350 {
351 	free(user->user);
352 	filter_list_free(user->list);
353 
354 	if (user->desc_btree) {
355 		__kb_traverse(filter_desc_p_t, user->desc_btree, free_desc);
356 		__kb_destroy(user->desc_btree);
357 	}
358 
359 	if (user->desc_acm)
360 		ACM_release(user->desc_acm);
361 }
362 
363 #define free_user(p) do { \
364 	filter_user_free(*p); \
365 	free(*p); \
366 } while (0)
367 #endif /* !WITHOUT_USERAUTH */
368 
369 #define free_ip(p) do { \
370 	free((*p)->ip); \
371 	filter_list_free((*p)->list); \
372 	free(*p); \
373 } while (0)
374 
375 void
filter_free(opts_t * opts)376 filter_free(opts_t *opts)
377 {
378 	if (!opts->filter)
379 		return;
380 
381 	filter_t *pf = opts->filter;
382 #ifndef WITHOUT_USERAUTH
383 	if (pf->user_btree) {
384 		__kb_traverse(filter_user_p_t, pf->user_btree, free_user);
385 		__kb_destroy(pf->user_btree);
386 	}
387 
388 	if (pf->user_acm)
389 		ACM_release(pf->user_acm);
390 
391 	if (pf->desc_btree) {
392 		__kb_traverse(filter_desc_p_t, pf->desc_btree, free_desc);
393 		__kb_destroy(pf->desc_btree);
394 	}
395 
396 	if (pf->desc_acm)
397 		ACM_release(pf->desc_acm);
398 
399 	filter_list_free(pf->all_user);
400 #endif /* !WITHOUT_USERAUTH */
401 
402 	if (pf->ip_btree) {
403 		__kb_traverse(filter_ip_p_t, pf->ip_btree, free_ip);
404 		__kb_destroy(pf->ip_btree);
405 	}
406 
407 	if (pf->ip_acm)
408 		ACM_release(pf->ip_acm);
409 
410 	filter_list_free(pf->all);
411 
412 	free(opts->filter);
413 	opts->filter = NULL;
414 }
415 
416 int
filter_macro_copy(macro_t * macro,const char * argv0,opts_t * opts)417 filter_macro_copy(macro_t *macro, const char *argv0, opts_t *opts)
418 {
419 	while (macro) {
420 		macro_t *m = malloc(sizeof(macro_t));
421 		if (!m)
422 			return oom_return(argv0);
423 		memset(m, 0, sizeof(macro_t));
424 
425 		m->name = strdup(macro->name);
426 		if (!m->name)
427 			return oom_return(argv0);
428 
429 		value_t *value = macro->value;
430 		while (value) {
431 			value_t *v = malloc(sizeof(value_t));
432 			if (!v)
433 				return oom_return(argv0);
434 			memset(v, 0, sizeof(value_t));
435 
436 			v->value = strdup(value->value);
437 			if (!v->value)
438 				return oom_return(argv0);
439 
440 			append_list(&m->value, v, value_t);
441 			value = value->next;
442 		}
443 
444 		append_list(&opts->macro, m, macro_t);
445 		macro = macro->next;
446 	}
447 	return 0;
448 }
449 
450 int
filter_rule_copy(filter_rule_t * rule,const char * argv0,opts_t * opts,tmp_opts_t * tmp_opts)451 filter_rule_copy(filter_rule_t *rule, const char *argv0, opts_t *opts, tmp_opts_t *tmp_opts)
452 {
453 	while (rule) {
454 		filter_rule_t *r = malloc(sizeof(filter_rule_t));
455 		if (!r)
456 			return oom_return(argv0);
457 		memset(r, 0, sizeof(filter_rule_t));
458 
459 		r->all_conns = rule->all_conns;
460 
461 #ifndef WITHOUT_USERAUTH
462 		r->all_users = rule->all_users;
463 
464 		if (rule->user) {
465 			r->user = strdup(rule->user);
466 			if (!r->user)
467 				return oom_return(argv0);
468 		}
469 		r->exact_user = rule->exact_user;
470 
471 		if (rule->desc) {
472 			r->desc = strdup(rule->desc);
473 			if (!r->desc)
474 				return oom_return(argv0);
475 		}
476 		r->exact_desc = rule->exact_desc;
477 #endif /* !WITHOUT_USERAUTH */
478 
479 		if (rule->ip) {
480 			r->ip = strdup(rule->ip);
481 			if (!r->ip)
482 				return oom_return(argv0);
483 		}
484 		r->exact_ip = rule->exact_ip;
485 
486 		if (rule->dstip) {
487 			r->dstip = strdup(rule->dstip);
488 			if (!r->dstip)
489 				return oom_return(argv0);
490 		}
491 		if (rule->sni) {
492 			r->sni = strdup(rule->sni);
493 			if (!r->sni)
494 				return oom_return(argv0);
495 		}
496 		if (rule->cn) {
497 			r->cn = strdup(rule->cn);
498 			if (!r->cn)
499 				return oom_return(argv0);
500 		}
501 		if (rule->host) {
502 			r->host = strdup(rule->host);
503 			if (!r->host)
504 				return oom_return(argv0);
505 		}
506 		if (rule->uri) {
507 			r->uri = strdup(rule->uri);
508 			if (!r->uri)
509 				return oom_return(argv0);
510 		}
511 
512 		r->exact_dstip = rule->exact_dstip;
513 		r->exact_sni = rule->exact_sni;
514 		r->exact_cn = rule->exact_cn;
515 		r->exact_host = rule->exact_host;
516 		r->exact_uri = rule->exact_uri;
517 
518 		r->all_dstips = rule->all_dstips;
519 		r->all_snis = rule->all_snis;
520 		r->all_cns = rule->all_cns;
521 		r->all_hosts = rule->all_hosts;
522 		r->all_uris = rule->all_uris;
523 
524 		if (rule->port) {
525 			r->port = strdup(rule->port);
526 			if (!r->port)
527 				return oom_return(argv0);
528 		}
529 		r->all_ports = rule->all_ports;
530 		r->exact_port = rule->exact_port;
531 
532 		// The action field is not a pointer, hence the direct assignment (copy)
533 		r->action = rule->action;
534 
535 		// But deep copy for conn_opts
536 		if (rule->action.conn_opts) {
537 			r->action.conn_opts = conn_opts_copy(rule->action.conn_opts, argv0, tmp_opts);
538 			if (!r->action.conn_opts)
539 				return oom_return(argv0);
540 		}
541 
542 		append_list(&opts->filter_rules, r, filter_rule_t);
543 
544 		rule = rule->next;
545 	}
546 	return 0;
547 }
548 
549 static char *
filter_value_str(value_t * value)550 filter_value_str(value_t *value)
551 {
552 	char *s = NULL;
553 
554 	while (value) {
555 		char *p;
556 		if (asprintf(&p, "%s%s%s", STRORNONE(s), s ? ", " : "", value->value) < 0) {
557 			goto err;
558 		}
559 		if (s)
560 			free(s);
561 		s = p;
562 		value = value->next;
563 	}
564 	goto out;
565 err:
566 	if (s) {
567 		free(s);
568 		s = NULL;
569 	}
570 out:
571 	return s;
572 }
573 
574 char *
filter_macro_str(macro_t * macro)575 filter_macro_str(macro_t *macro)
576 {
577 	char *s = NULL;
578 
579 	if (!macro) {
580 		s = strdup("");
581 		if (!s)
582 			return oom_return_na_null();
583 		goto out;
584 	}
585 
586 	while (macro) {
587 		char *v = filter_value_str(macro->value);
588 
589 		char *p;
590 		if (asprintf(&p, "%s%smacro %s = %s", STRORNONE(s), NLORNONE(s), macro->name, STRORNONE(v)) < 0) {
591 			if (v)
592 				free(v);
593 			goto err;
594 		}
595 		if (v)
596 			free(v);
597 		if (s)
598 			free(s);
599 		s = p;
600 		macro = macro->next;
601 	}
602 	goto out;
603 err:
604 	if (s) {
605 		free(s);
606 		s = NULL;
607 	}
608 out:
609 	return s;
610 }
611 
612 static char *
filter_rule_site_str(filter_rule_t * rule,char * site,unsigned int exact_site,unsigned int all_sites,char * apply_to,int rule_num)613 filter_rule_site_str(filter_rule_t *rule, char *site, unsigned int exact_site, unsigned int all_sites, char *apply_to, int rule_num)
614 {
615 	char *s = NULL;
616 
617 	char *copts_str = conn_opts_str(rule->action.conn_opts);
618 	if (!copts_str)
619 		return oom_return_na_null();
620 
621 	char *rule_num_str = NULL;
622 	if (rule_num >= 0) {
623 		if (asprintf(&rule_num_str, " %d", rule_num) < 0)
624 			goto err;
625 	} else {
626 		rule_num_str = strdup("");
627 		if (!rule_num_str)
628 			goto err;
629 	}
630 
631 	if (asprintf(&s, "filter rule%s: %s=%s, dstport=%s, srcip=%s"
632 #ifndef WITHOUT_USERAUTH
633 		", user=%s, desc=%s"
634 #endif /* !WITHOUT_USERAUTH */
635 		", exact=%s|%s|%s"
636 #ifndef WITHOUT_USERAUTH
637 		"|%s|%s"
638 #endif /* !WITHOUT_USERAUTH */
639 		", all=%s|"
640 #ifndef WITHOUT_USERAUTH
641 		"%s|"
642 #endif /* !WITHOUT_USERAUTH */
643 		"%s|%s, action=%s|%s|%s|%s|%s, log=%s|%s|%s|%s|%s"
644 #ifndef WITHOUT_MIRROR
645 		"|%s"
646 #endif /* !WITHOUT_MIRROR */
647 		", precedence=%d"
648 #ifdef DEBUG_PROXY
649 		", line=%d"
650 #endif /* DEBUG_PROXY */
651 		"%s%s\n",
652 		rule_num_str, apply_to, site, STRORNONE(rule->port), STRORNONE(rule->ip),
653 #ifndef WITHOUT_USERAUTH
654 		STRORNONE(rule->user), STRORNONE(rule->desc),
655 #endif /* !WITHOUT_USERAUTH */
656 		exact_site ? "site" : "", rule->exact_port ? "port" : "", rule->exact_ip ? "ip" : "",
657 #ifndef WITHOUT_USERAUTH
658 		rule->exact_user ? "user" : "", rule->exact_desc ? "desc" : "",
659 #endif /* !WITHOUT_USERAUTH */
660 		rule->all_conns ? "conns" : "",
661 #ifndef WITHOUT_USERAUTH
662 		rule->all_users ? "users" : "",
663 #endif /* !WITHOUT_USERAUTH */
664 		all_sites ? "sites" : "", rule->all_ports ? "ports" : "",
665 		rule->action.divert ? "divert" : "", rule->action.split ? "split" : "", rule->action.pass ? "pass" : "", rule->action.block ? "block" : "", rule->action.match ? "match" : "",
666 		rule->action.log_connect ? (rule->action.log_connect == 1 ? "!connect" : "connect") : "", rule->action.log_master ? (rule->action.log_master == 1 ? "!master" : "master") : "",
667 		rule->action.log_cert ? (rule->action.log_cert == 1 ? "!cert" : "cert") : "", rule->action.log_content ? (rule->action.log_content == 1 ? "!content" : "content") : "",
668 		rule->action.log_pcap ? (rule->action.log_pcap == 1 ? "!pcap" : "pcap") : "",
669 #ifndef WITHOUT_MIRROR
670 		rule->action.log_mirror ? (rule->action.log_mirror == 1 ? "!mirror" : "mirror") : "",
671 #endif /* !WITHOUT_MIRROR */
672 		rule->action.precedence,
673 #ifdef DEBUG_PROXY
674 		rule->action.line_num,
675 #endif /* DEBUG_PROXY */
676 		strlen(copts_str) ? "\n  " : "", copts_str) < 0) {
677 		s = NULL;
678 	}
679 err:
680 	if (rule_num_str)
681 		free(rule_num_str);
682 	free(copts_str);
683 	return s;
684 }
685 
686 static char *
filter_rule_site_all_str(filter_rule_t * rule,int rule_num)687 filter_rule_site_all_str(filter_rule_t *rule, int rule_num)
688 {
689 	char *s = NULL;
690 
691 	char *dstip = NULL;
692 	char *sni = NULL;
693 	char *cn = NULL;
694 	char *host = NULL;
695 	char *uri = NULL;
696 
697 	if (rule->dstip) {
698 		dstip = filter_rule_site_str(rule, rule->dstip, rule->exact_dstip, rule->all_dstips, "dstip", rule_num);
699 		if (!dstip)
700 			goto err;
701 	}
702 	if (rule->sni) {
703 		sni = filter_rule_site_str(rule, rule->sni, rule->exact_sni, rule->all_snis, "sni", rule_num);
704 		if (!sni)
705 			goto err;
706 	}
707 	if (rule->cn) {
708 		cn = filter_rule_site_str(rule, rule->cn, rule->exact_cn, rule->all_cns, "cn", rule_num);
709 		if (!cn)
710 			goto err;
711 	}
712 	if (rule->host) {
713 		host = filter_rule_site_str(rule, rule->host, rule->exact_host, rule->all_hosts, "host", rule_num);
714 		if (!host)
715 			goto err;
716 	}
717 	if (rule->uri) {
718 		uri = filter_rule_site_str(rule, rule->uri, rule->exact_uri, rule->all_uris, "uri", rule_num);
719 		if (!uri)
720 			goto err;
721 	}
722 
723 	if (asprintf(&s, "%s%s%s%s%s", STRORNONE(dstip), STRORNONE(sni), STRORNONE(cn), STRORNONE(host), STRORNONE(uri)) < 0) {
724 		s = NULL;
725 	}
726 err:
727 	if (dstip)
728 		free(dstip);
729 	if (sni)
730 		free(sni);
731 	if (cn)
732 		free(cn);
733 	if (host)
734 		free(host);
735 	if (uri)
736 		free(uri);
737 	return s;
738 }
739 
740 char *
filter_rule_str(filter_rule_t * rule)741 filter_rule_str(filter_rule_t *rule)
742 {
743 	char *frs = NULL;
744 
745 	if (!rule) {
746 		frs = strdup("");
747 		if (!frs)
748 			return oom_return_na_null();
749 		goto out;
750 	}
751 
752 	int count = 0;
753 	while (rule) {
754 		char *p = filter_rule_site_all_str(rule, count);
755 		if (!p)
756 			goto err;
757 
758 		char *nfrs;
759 		if (asprintf(&nfrs, "%s%s", STRORNONE(frs), p) < 0) {
760 			free(p);
761 			goto err;
762 		}
763 		free(p);
764 		if (frs)
765 			free(frs);
766 		frs = nfrs;
767 		rule = rule->next;
768 		count++;
769 	}
770 	goto out;
771 err:
772 	if (frs) {
773 		free(frs);
774 		frs = NULL;
775 	}
776 out:
777 	return frs;
778 }
779 
780 static char *
filter_port_str(filter_port_list_t * port_list)781 filter_port_str(filter_port_list_t *port_list)
782 {
783 	char *s = NULL;
784 
785 	int count = 0;
786 	while (port_list) {
787 		char *copts_str = conn_opts_str(port_list->port->action.conn_opts);
788 		if (!copts_str)
789 			goto err;
790 
791 		char *p;
792 		if (asprintf(&p, "%s\n          %d: %s (%s%s, action=%s|%s|%s|%s|%s, log=%s|%s|%s|%s|%s"
793 #ifndef WITHOUT_MIRROR
794 				"|%s"
795 #endif /* !WITHOUT_MIRROR */
796 				", precedence=%d"
797 #ifdef DEBUG_PROXY
798 				", line=%d"
799 #endif /* DEBUG_PROXY */
800 				"%s%s)", STRORNONE(s), count,
801 				port_list->port->port, port_list->port->all_ports ? "all_ports, " : "", port_list->port->exact ? "exact" : "substring",
802 				port_list->port->action.divert ? "divert" : "", port_list->port->action.split ? "split" : "", port_list->port->action.pass ? "pass" : "", port_list->port->action.block ? "block" : "", port_list->port->action.match ? "match" : "",
803 				port_list->port->action.log_connect ? (port_list->port->action.log_connect == 1 ? "!connect" : "connect") : "", port_list->port->action.log_master ? (port_list->port->action.log_master == 1 ? "!master" : "master") : "",
804 				port_list->port->action.log_cert ? (port_list->port->action.log_cert == 1 ? "!cert" : "cert") : "", port_list->port->action.log_content ? (port_list->port->action.log_content == 1 ? "!content" : "content") : "",
805 				port_list->port->action.log_pcap ? (port_list->port->action.log_pcap == 1 ? "!pcap" : "pcap") : "",
806 #ifndef WITHOUT_MIRROR
807 				port_list->port->action.log_mirror ? (port_list->port->action.log_mirror == 1 ? "!mirror" : "mirror") : "",
808 #endif /* !WITHOUT_MIRROR */
809 				port_list->port->action.precedence,
810 #ifdef DEBUG_PROXY
811 				port_list->port->action.line_num,
812 #endif /* DEBUG_PROXY */
813 				strlen(copts_str) ? "\n            " : "", copts_str) < 0) {
814 			if (copts_str)
815 				free(copts_str);
816 			goto err;
817 		}
818 		if (copts_str)
819 			free(copts_str);
820 		if (s)
821 			free(s);
822 		s = p;
823 		port_list = port_list->next;
824 		count++;
825 	}
826 	goto out;
827 err:
828 	if (s) {
829 		free(s);
830 		s = NULL;
831 	}
832 out:
833 	return s;
834 }
835 
836 #define build_port_list(p) do { \
837 	filter_port_list_t *s = malloc(sizeof(filter_port_list_t)); \
838 	memset(s, 0, sizeof(filter_port_list_t)); \
839 	s->port = *p; \
840 	append_list(&port, s, filter_port_list_t); \
841 } while (0)
842 
843 static filter_port_list_t *port_list_acm = NULL;
844 
845 static void
build_port_list_acm(UNUSED MatchHolder (char)match,void * v)846 build_port_list_acm(UNUSED MatchHolder(char) match, void *v)
847 {
848 	filter_port_t *port = v;
849 
850 	filter_port_list_t *p = malloc(sizeof(filter_port_list_t));
851 	memset(p, 0, sizeof(filter_port_list_t));
852 	p->port = port;
853 
854 	append_list(&port_list_acm, p, filter_port_list_t);
855 }
856 
857 static char *
filter_sites_str(filter_site_list_t * site_list)858 filter_sites_str(filter_site_list_t *site_list)
859 {
860 	char *s = NULL;
861 
862 	int count = 0;
863 	while (site_list) {
864 		filter_port_list_t *port = NULL;
865 
866 		if (site_list->site->port_btree)
867 			__kb_traverse(filter_port_p_t, site_list->site->port_btree, build_port_list);
868 
869 		char *ports_exact = filter_port_str(port);
870 		free_list(port, filter_port_list_t);
871 
872 		if (site_list->site->port_acm)
873 			ACM_foreach_keyword(site_list->site->port_acm, build_port_list_acm);
874 
875 		char *ports_substring = filter_port_str(port_list_acm);
876 		free_list(port_list_acm, filter_port_list_t);
877 		port_list_acm = NULL;
878 
879 		if (site_list->site->port_all)
880 			build_port_list_acm((MatchHolder(char)){0}, site_list->site->port_all);
881 
882 		char *ports_all = filter_port_str(port_list_acm);
883 		free_list(port_list_acm, filter_port_list_t);
884 		port_list_acm = NULL;
885 
886 		char *copts_str = conn_opts_str(site_list->site->action.conn_opts);
887 		if (!copts_str)
888 			goto err;
889 
890 		char *p;
891 		if (asprintf(&p, "%s\n      %d: %s (%s%s, action=%s|%s|%s|%s|%s, log=%s|%s|%s|%s|%s"
892 #ifndef WITHOUT_MIRROR
893 				"|%s"
894 #endif /* !WITHOUT_MIRROR */
895 				", precedence=%d"
896 #ifdef DEBUG_PROXY
897 				", line=%d"
898 #endif /* DEBUG_PROXY */
899 				"%s%s)%s%s%s%s%s%s",
900 				STRORNONE(s), count,
901 				site_list->site->site, site_list->site->all_sites ? "all_sites, " : "", site_list->site->exact ? "exact" : "substring",
902 				site_list->site->action.divert ? "divert" : "", site_list->site->action.split ? "split" : "", site_list->site->action.pass ? "pass" : "", site_list->site->action.block ? "block" : "", site_list->site->action.match ? "match" : "",
903 				site_list->site->action.log_connect ? (site_list->site->action.log_connect == 1 ? "!connect" : "connect") : "", site_list->site->action.log_master ? (site_list->site->action.log_master == 1 ? "!master" : "master") : "",
904 				site_list->site->action.log_cert ? (site_list->site->action.log_cert == 1 ? "!cert" : "cert") : "", site_list->site->action.log_content ? (site_list->site->action.log_content == 1 ? "!content" : "content") : "",
905 				site_list->site->action.log_pcap ? (site_list->site->action.log_pcap == 1 ? "!pcap" : "pcap") : "",
906 #ifndef WITHOUT_MIRROR
907 				site_list->site->action.log_mirror ? (site_list->site->action.log_mirror == 1 ? "!mirror" : "mirror") : "",
908 #endif /* !WITHOUT_MIRROR */
909 				site_list->site->action.precedence,
910 #ifdef DEBUG_PROXY
911 				site_list->site->action.line_num,
912 #endif /* DEBUG_PROXY */
913 				strlen(copts_str) ? "\n        " : "", copts_str,
914 				ports_exact ? "\n        port exact:" : "", STRORNONE(ports_exact),
915 				ports_substring ? "\n        port substring:" : "", STRORNONE(ports_substring),
916 				ports_all ? "\n        port all:" : "", STRORNONE(ports_all)) < 0) {
917 			if (ports_exact)
918 				free(ports_exact);
919 			if (ports_substring)
920 				free(ports_substring);
921 			if (ports_all)
922 				free(ports_all);
923 			if (copts_str)
924 				free(copts_str);
925 			goto err;
926 		}
927 		if (ports_exact)
928 			free(ports_exact);
929 		if (ports_substring)
930 			free(ports_substring);
931 		if (ports_all)
932 			free(ports_all);
933 		if (copts_str)
934 			free(copts_str);
935 		if (s)
936 			free(s);
937 		s = p;
938 		site_list = site_list->next;
939 		count++;
940 	}
941 	goto out;
942 err:
943 	if (s) {
944 		free(s);
945 		s = NULL;
946 	}
947 out:
948 	return s;
949 }
950 
951 static char *
filter_list_sub_str(filter_site_list_t * list,char * old_s,const char * name)952 filter_list_sub_str(filter_site_list_t *list, char *old_s, const char *name)
953 {
954 	char *new_s = NULL;
955 	char *s = filter_sites_str(list);
956 	if (asprintf(&new_s, "%s%s    %s:%s", STRORNONE(old_s), NLORNONE(old_s), name, STRORNONE(s)) < 0) {
957 		// @todo Handle oom, and don't just use STRORNONE()
958 		new_s = NULL;
959 	}
960 	if (s)
961 		free(s);
962 	if (old_s)
963 		free(old_s);
964 	return new_s;
965 }
966 
967 static filter_site_list_t *site_list_acm = NULL;
968 
969 static void
build_site_list_acm(UNUSED MatchHolder (char)match,void * v)970 build_site_list_acm(UNUSED MatchHolder(char) match, void *v)
971 {
972 	filter_site_t *site = v;
973 
974 	filter_site_list_t *s = malloc(sizeof(filter_site_list_t));
975 	memset(s, 0, sizeof(filter_site_list_t));
976 	s->site = site;
977 
978 	append_list(&site_list_acm, s, filter_site_list_t);
979 }
980 
981 static void
filter_tmp_site_list_free(filter_site_list_t ** list)982 filter_tmp_site_list_free(filter_site_list_t **list)
983 {
984 	free_list(*list, filter_site_list_t);
985 	*list = NULL;
986 }
987 
988 static char *
filter_list_str(filter_list_t * list)989 filter_list_str(filter_list_t *list)
990 {
991 	char *s = NULL;
992 	filter_site_list_t *site = NULL;
993 
994 #define build_site_list(p) do { \
995 	filter_site_list_t *s = malloc(sizeof(filter_site_list_t)); \
996 	memset(s, 0, sizeof(filter_site_list_t)); \
997 	s->site = *p; \
998 	append_list(&site, s, filter_site_list_t); \
999 } while (0)
1000 
1001 	if (list->ip_btree) {
1002 		__kb_traverse(filter_site_p_t, list->ip_btree, build_site_list);
1003 		s = filter_list_sub_str(site, s, "ip exact");
1004 		filter_tmp_site_list_free(&site);
1005 	}
1006 	if (list->ip_acm) {
1007 		ACM_foreach_keyword(list->ip_acm, build_site_list_acm);
1008 		s = filter_list_sub_str(site_list_acm, s, "ip substring");
1009 		filter_tmp_site_list_free(&site_list_acm);
1010 	}
1011 	if (list->ip_all) {
1012 		build_site_list_acm((MatchHolder(char)){0}, list->ip_all);
1013 		s = filter_list_sub_str(site_list_acm, s, "ip all");
1014 		filter_tmp_site_list_free(&site_list_acm);
1015 	}
1016 
1017 	if (list->sni_btree) {
1018 		__kb_traverse(filter_site_p_t, list->sni_btree, build_site_list);
1019 		s = filter_list_sub_str(site, s, "sni exact");
1020 		filter_tmp_site_list_free(&site);
1021 	}
1022 	if (list->sni_acm) {
1023 		ACM_foreach_keyword(list->sni_acm, build_site_list_acm);
1024 		s = filter_list_sub_str(site_list_acm, s, "sni substring");
1025 		filter_tmp_site_list_free(&site_list_acm);
1026 	}
1027 	if (list->sni_all) {
1028 		build_site_list_acm((MatchHolder(char)){0}, list->sni_all);
1029 		s = filter_list_sub_str(site_list_acm, s, "sni all");
1030 		filter_tmp_site_list_free(&site_list_acm);
1031 	}
1032 
1033 	if (list->cn_btree) {
1034 		__kb_traverse(filter_site_p_t, list->cn_btree, build_site_list);
1035 		s = filter_list_sub_str(site, s, "cn exact");
1036 		filter_tmp_site_list_free(&site);
1037 	}
1038 	if (list->cn_acm) {
1039 		ACM_foreach_keyword(list->cn_acm, build_site_list_acm);
1040 		s = filter_list_sub_str(site_list_acm, s, "cn substring");
1041 		filter_tmp_site_list_free(&site_list_acm);
1042 	}
1043 	if (list->cn_all) {
1044 		build_site_list_acm((MatchHolder(char)){0}, list->cn_all);
1045 		s = filter_list_sub_str(site_list_acm, s, "cn all");
1046 		filter_tmp_site_list_free(&site_list_acm);
1047 	}
1048 
1049 	if (list->host_btree) {
1050 		__kb_traverse(filter_site_p_t, list->host_btree, build_site_list);
1051 		s = filter_list_sub_str(site, s, "host exact");
1052 		filter_tmp_site_list_free(&site);
1053 	}
1054 	if (list->host_acm) {
1055 		ACM_foreach_keyword(list->host_acm, build_site_list_acm);
1056 		s = filter_list_sub_str(site_list_acm, s, "host substring");
1057 		filter_tmp_site_list_free(&site_list_acm);
1058 	}
1059 	if (list->host_all) {
1060 		build_site_list_acm((MatchHolder(char)){0}, list->host_all);
1061 		s = filter_list_sub_str(site_list_acm, s, "host all");
1062 		filter_tmp_site_list_free(&site_list_acm);
1063 	}
1064 
1065 	if (list->uri_btree) {
1066 		__kb_traverse(filter_site_p_t, list->uri_btree, build_site_list);
1067 		s = filter_list_sub_str(site, s, "uri exact");
1068 		filter_tmp_site_list_free(&site);
1069 	}
1070 	if (list->uri_acm) {
1071 		ACM_foreach_keyword(list->uri_acm, build_site_list_acm);
1072 		s = filter_list_sub_str(site_list_acm, s, "uri substring");
1073 		filter_tmp_site_list_free(&site_list_acm);
1074 	}
1075 	if (list->uri_all) {
1076 		build_site_list_acm((MatchHolder(char)){0}, list->uri_all);
1077 		s = filter_list_sub_str(site_list_acm, s, "uri all");
1078 		filter_tmp_site_list_free(&site_list_acm);
1079 	}
1080 	return s;
1081 }
1082 
1083 static char *
filter_ip_list_str(filter_ip_list_t * ip_list)1084 filter_ip_list_str(filter_ip_list_t *ip_list)
1085 {
1086 	char *s = NULL;
1087 
1088 	int count = 0;
1089 	while (ip_list) {
1090 		char *list = filter_list_str(ip_list->ip->list);
1091 
1092 		char *p;
1093 		if (asprintf(&p, "%s%s  ip %d %s (%s)=\n%s", STRORNONE(s), NLORNONE(s),
1094 				count, ip_list->ip->ip, ip_list->ip->exact ? "exact" : "substring", STRORNONE(list)) < 0) {
1095 			if (list)
1096 				free(list);
1097 			goto err;
1098 		}
1099 		if (list)
1100 			free(list);
1101 		if (s)
1102 			free(s);
1103 		s = p;
1104 		ip_list = ip_list->next;
1105 		count++;
1106 	}
1107 	goto out;
1108 err:
1109 	if (s) {
1110 		free(s);
1111 		s = NULL;
1112 	}
1113 out:
1114 	return s;
1115 }
1116 
1117 static char *
filter_ip_btree_str(kbtree_t (ip)* btree)1118 filter_ip_btree_str(kbtree_t(ip) *btree)
1119 {
1120 	if (!btree)
1121 		return NULL;
1122 
1123 #define build_ip_list(p) do { \
1124 	filter_ip_list_t *i = malloc(sizeof(filter_ip_list_t)); \
1125 	memset(i, 0, sizeof(filter_ip_list_t)); \
1126 	i->ip = *p; \
1127 	append_list(&ip, i, filter_ip_list_t); \
1128 } while (0)
1129 
1130 	filter_ip_list_t *ip = NULL;
1131 	__kb_traverse(filter_ip_p_t, btree, build_ip_list);
1132 
1133 	char *s = filter_ip_list_str(ip);
1134 
1135 	free_list(ip, filter_ip_list_t);
1136 	return s;
1137 }
1138 
1139 static filter_ip_list_t *ip_list_acm = NULL;
1140 
1141 static void
build_ip_list_acm(UNUSED MatchHolder (char)match,void * v)1142 build_ip_list_acm(UNUSED MatchHolder(char) match, void *v)
1143 {
1144 	filter_ip_t *ip = v;
1145 
1146 	filter_ip_list_t *i = malloc(sizeof(filter_ip_list_t));
1147 	memset(i, 0, sizeof(filter_ip_list_t));
1148 	i->ip = ip;
1149 
1150 	append_list(&ip_list_acm, i, filter_ip_list_t);
1151 }
1152 
1153 static char *
filter_ip_acm_str(ACMachine (char)* acm)1154 filter_ip_acm_str(ACMachine(char) *acm)
1155 {
1156 	if (!acm)
1157 		return NULL;
1158 
1159 	ACM_foreach_keyword(acm, build_ip_list_acm);
1160 
1161 	char *s = filter_ip_list_str(ip_list_acm);
1162 
1163 	free_list(ip_list_acm, filter_ip_list_t);
1164 	ip_list_acm = NULL;
1165 	return s;
1166 }
1167 
1168 #ifndef WITHOUT_USERAUTH
1169 static char *
filter_user_list_str(filter_user_list_t * user)1170 filter_user_list_str(filter_user_list_t *user)
1171 {
1172 	char *s = NULL;
1173 
1174 	int count = 0;
1175 	while (user) {
1176 		// Make sure the user has a filter rule
1177 		// It is possible to have users without any filter rule,
1178 		// but the user exists because it has desc filters,
1179 		// so the current user should not have any desc
1180 		if (user->user->desc_btree || user->user->desc_acm)
1181 			goto skip;
1182 
1183 		char *list = filter_list_str(user->user->list);
1184 
1185 		char *p = NULL;
1186 
1187 		if (list) {
1188 			if (asprintf(&p, "%s%s  user %d %s (%s)=\n%s", STRORNONE(s), NLORNONE(s),
1189 					count, user->user->user, user->user->exact ? "exact" : "substring", list) < 0) {
1190 				free(list);
1191 				goto err;
1192 			}
1193 			free(list);
1194 		}
1195 		if (s)
1196 			free(s);
1197 		s = p;
1198 		count++;
1199 skip:
1200 		user = user->next;
1201 	}
1202 	goto out;
1203 err:
1204 	if (s) {
1205 		free(s);
1206 		s = NULL;
1207 	}
1208 out:
1209 	return s;
1210 }
1211 
1212 #define build_user_list(p) do { \
1213 	filter_user_list_t *u = malloc(sizeof(filter_user_list_t)); \
1214 	memset(u, 0, sizeof(filter_user_list_t)); \
1215 	u->user = *p; \
1216 	append_list(&user, u, filter_user_list_t); \
1217 } while (0)
1218 
1219 static char *
filter_user_btree_str(kbtree_t (user)* btree)1220 filter_user_btree_str(kbtree_t(user) *btree)
1221 {
1222 	if (!btree)
1223 		return NULL;
1224 
1225 	filter_user_list_t *user = NULL;
1226 	__kb_traverse(filter_user_p_t, btree, build_user_list);
1227 
1228 	char *s = filter_user_list_str(user);
1229 
1230 	free_list(user, filter_user_list_t);
1231 	return s;
1232 }
1233 
1234 static filter_user_list_t *user_list_acm = NULL;
1235 
1236 static void
build_user_list_acm(UNUSED MatchHolder (char)match,void * v)1237 build_user_list_acm(UNUSED MatchHolder(char) match, void *v)
1238 {
1239 	filter_user_t *user = v;
1240 
1241 	filter_user_list_t *u = malloc(sizeof(filter_user_list_t));
1242 	memset(u, 0, sizeof(filter_user_list_t));
1243 	u->user = user;
1244 
1245 	append_list(&user_list_acm, u, filter_user_list_t);
1246 }
1247 
1248 static char *
filter_user_acm_str(ACMachine (char)* acm)1249 filter_user_acm_str(ACMachine(char) *acm)
1250 {
1251 	if (!acm)
1252 		return NULL;
1253 
1254 	ACM_foreach_keyword(acm, build_user_list_acm);
1255 
1256 	char *s = filter_user_list_str(user_list_acm);
1257 
1258 	free_list(user_list_acm, filter_user_list_t);
1259 	user_list_acm = NULL;
1260 	return s;
1261 }
1262 
1263 static char *
filter_desc_list_str(filter_desc_list_t * desc)1264 filter_desc_list_str(filter_desc_list_t *desc)
1265 {
1266 	char *s = NULL;
1267 
1268 	int count = 0;
1269 	while (desc) {
1270 		char *list = filter_list_str(desc->desc->list);
1271 
1272 		char *p;
1273 		if (asprintf(&p, "%s%s   desc %d %s (%s)=\n%s", STRORNONE(s), NLORNONE(s),
1274 				count, desc->desc->desc, desc->desc->exact ? "exact" : "substring", STRORNONE(list)) < 0) {
1275 			if (list)
1276 				free(list);
1277 			goto err;
1278 		}
1279 		if (list)
1280 			free(list);
1281 		if (s)
1282 			free(s);
1283 		s = p;
1284 		desc = desc->next;
1285 		count++;
1286 	}
1287 	goto out;
1288 err:
1289 	if (s) {
1290 		free(s);
1291 		s = NULL;
1292 	}
1293 out:
1294 	return s;
1295 }
1296 
1297 static char *
filter_desc_btree_str(kbtree_t (desc)* btree)1298 filter_desc_btree_str(kbtree_t(desc) *btree)
1299 {
1300 	if (!btree)
1301 		return NULL;
1302 
1303 #define build_desc_list(p) do { \
1304 	filter_desc_list_t *d = malloc(sizeof(filter_desc_list_t)); \
1305 	memset(d, 0, sizeof(filter_desc_list_t)); \
1306 	d->desc = *p; \
1307 	append_list(&desc, d, filter_desc_list_t); \
1308 } while (0)
1309 
1310 	filter_desc_list_t *desc = NULL;
1311 	__kb_traverse(filter_desc_p_t, btree, build_desc_list);
1312 
1313 	char *s = filter_desc_list_str(desc);
1314 
1315 	free_list(desc, filter_desc_list_t);
1316 	return s;
1317 }
1318 
1319 static filter_desc_list_t *desc_list_acm = NULL;
1320 
1321 static void
build_desc_list_acm(UNUSED MatchHolder (char)match,void * v)1322 build_desc_list_acm(UNUSED MatchHolder(char) match, void *v)
1323 {
1324 	filter_desc_t *desc = v;
1325 
1326 	filter_desc_list_t *d = malloc(sizeof(filter_desc_list_t));
1327 	memset(d, 0, sizeof(filter_desc_list_t));
1328 	d->desc = desc;
1329 
1330 	append_list(&desc_list_acm, d, filter_desc_list_t);
1331 }
1332 
1333 static char *
filter_desc_acm_str(ACMachine (char)* acm)1334 filter_desc_acm_str(ACMachine(char) *acm)
1335 {
1336 	if (!acm)
1337 		return NULL;
1338 
1339 	ACM_foreach_keyword(acm, build_desc_list_acm);
1340 
1341 	char *s = filter_desc_list_str(desc_list_acm);
1342 
1343 	free_list(desc_list_acm, filter_desc_list_t);
1344 	desc_list_acm = NULL;
1345 	return s;
1346 }
1347 
1348 static char *
filter_userdesc_list_str(filter_user_list_t * user)1349 filter_userdesc_list_str(filter_user_list_t *user)
1350 {
1351 	char *s = NULL;
1352 
1353 	int count = 0;
1354 	while (user) {
1355 		// Make sure the current user has a desc
1356 		if (!user->user->desc_btree && !user->user->desc_acm)
1357 			goto skip;
1358 
1359 		char *list_exact = filter_desc_btree_str(user->user->desc_btree);
1360 		char *list_substr = filter_desc_acm_str(user->user->desc_acm);
1361 
1362 		char *p = NULL;
1363 		if (asprintf(&p, "%s%s user %d %s (%s)=%s%s%s%s", STRORNONE(s), NLORNONE(s),
1364 				count, user->user->user, user->user->exact ? "exact" : "substring",
1365 				list_exact ? "\n  desc exact:\n" : "", STRORNONE(list_exact),
1366 				list_substr ? "\n  desc substring:\n" : "", STRORNONE(list_substr)
1367 				) < 0) {
1368 			if (list_exact)
1369 				free(list_exact);
1370 			if (list_substr)
1371 				free(list_substr);
1372 			goto err;
1373 		}
1374 		if (list_exact)
1375 			free(list_exact);
1376 		if (list_substr)
1377 			free(list_substr);
1378 		if (s)
1379 			free(s);
1380 		s = p;
1381 		count++;
1382 skip:
1383 		user = user->next;
1384 	}
1385 	goto out;
1386 err:
1387 	if (s) {
1388 		free(s);
1389 		s = NULL;
1390 	}
1391 out:
1392 	return s;
1393 }
1394 
1395 static char *
filter_userdesc_btree_str(kbtree_t (user)* btree)1396 filter_userdesc_btree_str(kbtree_t(user) *btree)
1397 {
1398 	if (!btree)
1399 		return NULL;
1400 
1401 	filter_user_list_t *user = NULL;
1402 	__kb_traverse(filter_user_p_t, btree, build_user_list);
1403 
1404 	char *s = filter_userdesc_list_str(user);
1405 
1406 	free_list(user, filter_user_list_t);
1407 	return s;
1408 }
1409 
1410 static char *
filter_userdesc_acm_str(ACMachine (char)* acm)1411 filter_userdesc_acm_str(ACMachine(char) *acm)
1412 {
1413 	if (!acm)
1414 		return NULL;
1415 
1416 	ACM_foreach_keyword(acm, build_user_list_acm);
1417 
1418 	char *s = filter_userdesc_list_str(user_list_acm);
1419 
1420 	free_list(user_list_acm, filter_user_list_t);
1421 	user_list_acm = NULL;
1422 	return s;
1423 }
1424 
1425 #endif /* !WITHOUT_USERAUTH */
1426 
1427 char *
filter_str(filter_t * filter)1428 filter_str(filter_t *filter)
1429 {
1430 	char *fs = NULL;
1431 #ifndef WITHOUT_USERAUTH
1432 	char *userdesc_filter_exact = NULL;
1433 	char *userdesc_filter_substr = NULL;
1434 	char *user_filter_exact = NULL;
1435 	char *user_filter_substr = NULL;
1436 	char *desc_filter_exact = NULL;
1437 	char *desc_filter_substr = NULL;
1438 	char *user_filter_all = NULL;
1439 #endif /* !WITHOUT_USERAUTH */
1440 	char *ip_filter_exact = NULL;
1441 	char *ip_filter_substr = NULL;
1442 	char *filter_all = NULL;
1443 
1444 	if (!filter) {
1445 		fs = strdup("");
1446 		if (!fs)
1447 			return oom_return_na_null();
1448 		goto out;
1449 	}
1450 
1451 #ifndef WITHOUT_USERAUTH
1452 	userdesc_filter_exact = filter_userdesc_btree_str(filter->user_btree);
1453 	userdesc_filter_substr = filter_userdesc_acm_str(filter->user_acm);
1454 	user_filter_exact = filter_user_btree_str(filter->user_btree);
1455 	user_filter_substr = filter_user_acm_str(filter->user_acm);
1456 	desc_filter_exact = filter_desc_btree_str(filter->desc_btree);
1457 	desc_filter_substr = filter_desc_acm_str(filter->desc_acm);
1458 	user_filter_all = filter_list_str(filter->all_user);
1459 #endif /* !WITHOUT_USERAUTH */
1460 	ip_filter_exact = filter_ip_btree_str(filter->ip_btree);
1461 	ip_filter_substr = filter_ip_acm_str(filter->ip_acm);
1462 	filter_all = filter_list_str(filter->all);
1463 
1464 	if (asprintf(&fs, "filter=>\n"
1465 #ifndef WITHOUT_USERAUTH
1466 			"userdesc_filter_exact->%s%s\n"
1467 			"userdesc_filter_substring->%s%s\n"
1468 			"user_filter_exact->%s%s\n"
1469 			"user_filter_substring->%s%s\n"
1470 			"desc_filter_exact->%s%s\n"
1471 			"desc_filter_substring->%s%s\n"
1472 			"user_filter_all->%s%s\n"
1473 #endif /* !WITHOUT_USERAUTH */
1474 			"ip_filter_exact->%s%s\n"
1475 			"ip_filter_substring->%s%s\n"
1476 			"filter_all->%s%s\n",
1477 #ifndef WITHOUT_USERAUTH
1478 			NLORNONE(userdesc_filter_exact), STRORNONE(userdesc_filter_exact),
1479 			NLORNONE(userdesc_filter_substr), STRORNONE(userdesc_filter_substr),
1480 			NLORNONE(user_filter_exact), STRORNONE(user_filter_exact),
1481 			NLORNONE(user_filter_substr), STRORNONE(user_filter_substr),
1482 			NLORNONE(desc_filter_exact), STRORNONE(desc_filter_exact),
1483 			NLORNONE(desc_filter_substr), STRORNONE(desc_filter_substr),
1484 			NLORNONE(user_filter_all), STRORNONE(user_filter_all),
1485 #endif /* !WITHOUT_USERAUTH */
1486 			NLORNONE(ip_filter_exact), STRORNONE(ip_filter_exact),
1487 			NLORNONE(ip_filter_substr), STRORNONE(ip_filter_substr),
1488 			NLORNONE(filter_all), STRORNONE(filter_all)) < 0) {
1489 		// fs is undefined
1490 		goto err;
1491 	}
1492 	goto out;
1493 err:
1494 	if (fs) {
1495 		free(fs);
1496 		fs = NULL;
1497 	}
1498 out:
1499 #ifndef WITHOUT_USERAUTH
1500 	if (userdesc_filter_exact)
1501 		free(userdesc_filter_exact);
1502 	if (userdesc_filter_substr)
1503 		free(userdesc_filter_substr);
1504 	if (user_filter_exact)
1505 		free(user_filter_exact);
1506 	if (user_filter_substr)
1507 		free(user_filter_substr);
1508 	if (desc_filter_exact)
1509 		free(desc_filter_exact);
1510 	if (desc_filter_substr)
1511 		free(desc_filter_substr);
1512 	if (user_filter_all)
1513 		free(user_filter_all);
1514 #endif /* !WITHOUT_USERAUTH */
1515 	if (ip_filter_exact)
1516 		free(ip_filter_exact);
1517 	if (ip_filter_substr)
1518 		free(ip_filter_substr);
1519 	if (filter_all)
1520 		free(filter_all);
1521 	return fs;
1522 }
1523 
1524 #ifdef DEBUG_OPTS
1525 static void
filter_rule_dbg_print(filter_rule_t * rule)1526 filter_rule_dbg_print(filter_rule_t *rule)
1527 {
1528 	char *s = filter_rule_site_all_str(rule, -1);
1529 	if (!s)
1530 		return;
1531 	log_dbg_printf("%s", s);
1532 	free(s);
1533 }
1534 #endif /* DEBUG_OPTS */
1535 
1536 #define MAX_SITE_LEN 200
1537 
1538 int
filter_passsite_set(opts_t * opts,UNUSED conn_opts_t * conn_opts,char * value,unsigned int line_num)1539 filter_passsite_set(opts_t *opts, UNUSED conn_opts_t *conn_opts, char *value, unsigned int line_num)
1540 {
1541 #define MAX_PASSSITE_TOKENS 3
1542 
1543 	// site[*] [(clientaddr|user|*) [desc]]
1544 	char *argv[sizeof(char *) * MAX_PASSSITE_TOKENS];
1545 	int argc = 0;
1546 	char *p, *last = NULL;
1547 
1548 	for ((p = strtok_r(value, " ", &last));
1549 		 p;
1550 		 (p = strtok_r(NULL, " ", &last))) {
1551 		if (argc < MAX_PASSSITE_TOKENS) {
1552 			argv[argc++] = p;
1553 		} else {
1554 			fprintf(stderr, "Too many arguments in passsite option on line %d\n", line_num);
1555 			return -1;
1556 		}
1557 	}
1558 
1559 	if (!argc) {
1560 		fprintf(stderr, "Filter rule requires at least one parameter on line %d\n", line_num);
1561 		return -1;
1562 	}
1563 
1564 	filter_rule_t *rule = malloc(sizeof(filter_rule_t));
1565 	if (!rule)
1566 		return oom_return_na();
1567 	memset(rule, 0, sizeof(filter_rule_t));
1568 
1569 	// The for loop with strtok_r() above does not output empty strings
1570 	// So, no need to check if the length of argv[0] > 0
1571 	size_t len = strlen(argv[0]);
1572 
1573 	if (len > MAX_SITE_LEN) {
1574 		fprintf(stderr, "Filter site too long %zu > %d on line %d\n", len, MAX_SITE_LEN, line_num);
1575 		return -1;
1576 	}
1577 
1578 	unsigned int exact_site = 0;
1579 	unsigned int all_sites = 0;
1580 	if (argv[0][len - 1] == '*') {
1581 		exact_site = 0;
1582 		len--;
1583 		argv[0][len] = '\0';
1584 		// site == "*" ?
1585 		if (len == 0)
1586 			all_sites = 1;
1587 	} else {
1588 		exact_site = 1;
1589 	}
1590 
1591 	rule->sni = strdup(argv[0]);
1592 	if (!rule->sni)
1593 		return oom_return_na();
1594 	rule->exact_sni = exact_site;
1595 	rule->all_snis = all_sites;
1596 
1597 	rule->cn = strdup(argv[0]);
1598 	if (!rule->cn)
1599 		return oom_return_na();
1600 	rule->exact_cn = exact_site;
1601 	rule->all_cns = all_sites;
1602 
1603 	// precedence can only go up not down
1604 	rule->action.precedence = 0;
1605 
1606 	if (argc == 1) {
1607 		// Apply filter rule to all conns
1608 		// Equivalent to "site *" without user auth
1609 		rule->all_conns = 1;
1610 	}
1611 
1612 	if (argc > 1) {
1613 		if (!strcmp(argv[1], "*")) {
1614 #ifndef WITHOUT_USERAUTH
1615 			// Apply filter rule to all users perhaps with desc
1616 			rule->action.precedence++;
1617 			rule->all_users = 1;
1618 		} else if (sys_isuser(argv[1])) {
1619 			if (!conn_opts->user_auth) {
1620 				fprintf(stderr, "User filter requires user auth on line %d\n", line_num);
1621 				return -1;
1622 			}
1623 			rule->action.precedence += 2;
1624 			rule->user = strdup(argv[1]);
1625 			if (!rule->user)
1626 				return oom_return_na();
1627 #else /* !WITHOUT_USERAUTH */
1628 			// Apply filter rule to all conns, if USERAUTH is disabled, ip == '*'
1629 			rule->all_conns = 1;
1630 #endif /* WITHOUT_USERAUTH */
1631 		} else {
1632 			rule->action.precedence++;
1633 			rule->ip = strdup(argv[1]);
1634 			if (!rule->ip)
1635 				return oom_return_na();
1636 		}
1637 	}
1638 
1639 	if (argc > 2) {
1640 		if (rule->ip) {
1641 			fprintf(stderr, "Ip filter cannot define desc filter"
1642 #ifndef WITHOUT_USERAUTH
1643 					", or user '%s' does not exist"
1644 #endif /* !WITHOUT_USERAUTH */
1645 					" on line %d\n",
1646 #ifndef WITHOUT_USERAUTH
1647 					rule->ip,
1648 #endif /* !WITHOUT_USERAUTH */
1649 					line_num);
1650 			return -1;
1651 		}
1652 #ifndef WITHOUT_USERAUTH
1653 		if (!conn_opts->user_auth) {
1654 			fprintf(stderr, "Keyword filter requires user auth on line %d\n", line_num);
1655 			return -1;
1656 		}
1657 		rule->action.precedence++;
1658 		rule->desc = strdup(argv[2]);
1659 		if (!rule->desc)
1660 			return oom_return_na();
1661 #endif /* !WITHOUT_USERAUTH */
1662 	}
1663 
1664 	rule->action.precedence++;
1665 	rule->action.pass = 1;
1666 
1667 	append_list(&opts->filter_rules, rule, filter_rule_t);
1668 
1669 #ifdef DEBUG_OPTS
1670 	filter_rule_dbg_print(rule);
1671 #endif /* DEBUG_OPTS */
1672 	return 0;
1673 }
1674 
1675 static macro_t *
filter_macro_find(macro_t * macro,char * name)1676 filter_macro_find(macro_t *macro, char *name)
1677 {
1678 	while (macro) {
1679 		if (equal(macro->name, name)) {
1680 			return macro;
1681 		}
1682 		macro = macro->next;
1683 	}
1684 	return NULL;
1685 }
1686 
1687 int
filter_macro_set(opts_t * opts,char * value,unsigned int line_num)1688 filter_macro_set(opts_t *opts, char *value, unsigned int line_num)
1689 {
1690 #define MAX_MACRO_TOKENS 50
1691 
1692 	// $name value1 [value2 [value3] ...]
1693 	char *argv[sizeof(char *) * MAX_MACRO_TOKENS];
1694 	int argc = 0;
1695 	char *p, *last = NULL;
1696 
1697 	for ((p = strtok_r(value, " ", &last));
1698 		 p;
1699 		 (p = strtok_r(NULL, " ", &last))) {
1700 		if (argc < MAX_MACRO_TOKENS) {
1701 			argv[argc++] = p;
1702 		} else {
1703 			fprintf(stderr, "Too many arguments in macro definition on line %d\n", line_num);
1704 			return -1;
1705 		}
1706 	}
1707 
1708 	if (argc < 2) {
1709 		fprintf(stderr, "Macro definition requires at least two arguments on line %d\n", line_num);
1710 		return -1;
1711 	}
1712 
1713 	if (argv[0][0] != '$') {
1714 		fprintf(stderr, "Macro name should start with '$' on line %d\n", line_num);
1715 		return -1;
1716 	}
1717 
1718 	if (filter_macro_find(opts->macro, argv[0])) {
1719 		fprintf(stderr, "Macro name '%s' already exists on line %d\n", argv[0], line_num);
1720 		return -1;
1721 	}
1722 
1723 	macro_t *macro = malloc(sizeof(macro_t));
1724 	if (!macro)
1725 		return oom_return_na();
1726 	memset(macro, 0, sizeof(macro_t));
1727 
1728 	macro->name = strdup(argv[0]);
1729 	if (!macro->name)
1730 		return oom_return_na();
1731 
1732 	int i = 1;
1733 	while (i < argc) {
1734 		// Do not allow macro within macro, no recursive macro definitions
1735 		if (argv[i][0] == '$') {
1736 			fprintf(stderr, "Invalid macro value '%s' on line %d\n", argv[i], line_num);
1737 			return -1;
1738 		}
1739 
1740 		value_t *v = malloc(sizeof(value_t));
1741 		if (!v)
1742 			return oom_return_na();
1743 		memset(v, 0, sizeof(value_t));
1744 
1745 		v->value = strdup(argv[i++]);
1746 		if (!v->value)
1747 			return oom_return_na();
1748 
1749 		append_list(&macro->value, v, value_t);
1750 	}
1751 
1752 	append_list(&opts->macro, macro, macro_t);
1753 
1754 #ifdef DEBUG_OPTS
1755 	char *s = filter_value_str(macro->value);
1756 	if (!s)
1757 		return oom_return_na();
1758 	log_dbg_printf("Macro: %s = %s\n", macro->name, s);
1759 	free(s);
1760 #endif /* DEBUG_OPTS */
1761 	return 0;
1762 }
1763 
1764 static char * WUNRES
filter_site_set(filter_rule_t * rule,const char * name,const char * site,unsigned int line_num)1765 filter_site_set(filter_rule_t *rule, const char *name, const char *site, unsigned int line_num)
1766 {
1767 	// The for loop with strtok_r() does not output empty strings
1768 	// So, no need to check if the length of site > 0
1769 	size_t len = strlen(site);
1770 
1771 	if (len > MAX_SITE_LEN) {
1772 		fprintf(stderr, "Filter site too long %zu > %d on line %d\n", len, MAX_SITE_LEN, line_num);
1773 		return NULL;
1774 	}
1775 
1776 	// Don't modify site, site is reused in macro expansion
1777 	char *s = strdup(site);
1778 	if (!s)
1779 		return oom_return_na_null();
1780 
1781 	unsigned int exact_site = 0;
1782 	unsigned int all_sites = 0;
1783 	if (s[len - 1] == '*') {
1784 		exact_site = 0;
1785 		len--;
1786 		s[len] = '\0';
1787 		// site == "*" ?
1788 		if (len == 0)
1789 			all_sites = 1;
1790 	} else {
1791 		exact_site = 1;
1792 	}
1793 
1794 	// redundant?
1795 	if (equal(s, "*"))
1796 		all_sites = 1;
1797 
1798 	if (equal(name, "ip") || equal(name, "DstIp")) {
1799 		rule->dstip = s;
1800 		rule->exact_dstip = exact_site;
1801 		rule->all_dstips = all_sites;
1802 	}
1803 	else if (equal(name, "sni") || equal(name, "SNI")) {
1804 		rule->sni = s;
1805 		rule->exact_sni = exact_site;
1806 		rule->all_snis = all_sites;
1807 	}
1808 	else if (equal(name, "cn") || equal(name, "CN")) {
1809 		rule->cn = s;
1810 		rule->exact_cn = exact_site;
1811 		rule->all_cns = all_sites;
1812 	}
1813 	else if (equal(name, "host") || equal(name, "Host")) {
1814 		rule->host = s;
1815 		rule->exact_host = exact_site;
1816 		rule->all_hosts = all_sites;
1817 	}
1818 	else if (equal(name, "uri") || equal(name, "URI")) {
1819 		rule->uri = s;
1820 		rule->exact_uri = exact_site;
1821 		rule->all_uris = all_sites;
1822 	}
1823 
1824 	return s;
1825 }
1826 
1827 static int WUNRES
filter_port_set(filter_rule_t * rule,const char * port,unsigned int line_num)1828 filter_port_set(filter_rule_t *rule, const char *port, unsigned int line_num)
1829 {
1830 #define MAX_PORT_LEN 6
1831 
1832 	size_t len = strlen(port);
1833 
1834 	if (len > MAX_PORT_LEN) {
1835 		fprintf(stderr, "Filter port too long %zu > %d on line %d\n", len, MAX_PORT_LEN, line_num);
1836 		return -1;
1837 	}
1838 
1839 	rule->port = strdup(port);
1840 	if (!rule->port)
1841 		return oom_return_na();
1842 
1843 	if (rule->port[len - 1] == '*') {
1844 		rule->exact_port = 0;
1845 		len--;
1846 		rule->port[len] = '\0';
1847 		// site == "*" ?
1848 		if (len == 0)
1849 			rule->all_ports = 1;
1850 	} else {
1851 		rule->exact_port = 1;
1852 	}
1853 
1854 	// redundant?
1855 	if (equal(rule->port, "*"))
1856 		rule->all_ports = 1;
1857 	return 0;
1858 }
1859 
1860 static int WUNRES
filter_is_exact(const char * arg)1861 filter_is_exact(const char *arg)
1862 {
1863 	return arg[strlen(arg) - 1] != '*';
1864 }
1865 
1866 static int WUNRES
filter_is_all(const char * arg)1867 filter_is_all(const char *arg)
1868 {
1869 	return equal(arg, "*");
1870 }
1871 
1872 static int WUNRES
filter_field_set(char ** field,const char * arg,unsigned int line_num)1873 filter_field_set(char **field, const char *arg, unsigned int line_num)
1874 {
1875 	// The for loop with strtok_r() does not output empty strings
1876 	// So, no need to check if the length of field > 0
1877 	size_t len = strlen(arg);
1878 
1879 	if (len > MAX_SITE_LEN) {
1880 		fprintf(stderr, "Filter field too long %zu > %d on line %d\n", len, MAX_SITE_LEN, line_num);
1881 		return -1;
1882 	}
1883 
1884 	*field = strdup(arg);
1885 	if (!*field)
1886 		return oom_return_na();
1887 
1888 	if ((*field)[len - 1] == '*')
1889 		(*field)[len - 1] = '\0';
1890 	return 0;
1891 }
1892 
1893 static int WUNRES
filter_arg_index_inc(int i,int argc,char * last,unsigned int line_num)1894 filter_arg_index_inc(int i, int argc, char *last, unsigned int line_num)
1895 {
1896 	if (i + 1 < argc) {
1897 		return i + 1;
1898 	} else {
1899 		fprintf(stderr, "Not enough arguments in filter rule after '%s' on line %d\n", last, line_num);
1900 		return -1;
1901 	}
1902 }
1903 
1904 static int WUNRES
filter_rule_translate(opts_t * opts,const char * name,int argc,char ** argv,unsigned int line_num)1905 filter_rule_translate(opts_t *opts, const char *name, int argc, char **argv, unsigned int line_num)
1906 {
1907 	filter_rule_t *rule = malloc(sizeof(filter_rule_t));
1908 	if (!rule)
1909 		return oom_return_na();
1910 	memset(rule, 0, sizeof(filter_rule_t));
1911 
1912 	if (equal(name, "Divert"))
1913 		rule->action.divert = 1;
1914 	else if (equal(name, "Split"))
1915 		rule->action.split = 1;
1916 	else if (equal(name, "Pass"))
1917 		rule->action.pass = 1;
1918 	else if (equal(name, "Block"))
1919 		rule->action.block = 1;
1920 	else if (equal(name, "Match"))
1921 		rule->action.match = 1;
1922 
1923 	// precedence can only go up not down
1924 	rule->action.precedence = 0;
1925 
1926 	int done_from = 0;
1927 	int done_site = 0;
1928 	int i = 0;
1929 	while (i < argc) {
1930 		if (equal(argv[i], "*")) {
1931 			i++;
1932 		}
1933 		else if (equal(argv[i], "from")) {
1934 			if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
1935 				return -1;
1936 #ifndef WITHOUT_USERAUTH
1937 			if (equal(argv[i], "user") || equal(argv[i], "desc")) {
1938 				// The existence of user or desc should increment precedence, all_users or not
1939 				// user spec is more specific than ip spec
1940 				rule->action.precedence++;
1941 
1942 				if (equal(argv[i], "user")) {
1943 					if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
1944 						return -1;
1945 
1946 					rule->all_users = filter_is_all(argv[i]);
1947 
1948 					if (!rule->all_users) {
1949 						rule->exact_user = filter_is_exact(argv[i]);
1950 						if (filter_field_set(&rule->user, argv[i], line_num) == -1)
1951 							return -1;
1952 						rule->action.precedence++;
1953 					}
1954 					i++;
1955 				}
1956 
1957 				if (i < argc && equal(argv[i], "desc")) {
1958 					if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
1959 						return -1;
1960 
1961 					if (filter_is_all(argv[i])) {
1962 						if (!rule->user) {
1963 							rule->all_users = 1;
1964 						}
1965 					}
1966 					else {
1967 						rule->exact_desc = filter_is_exact(argv[i]);
1968 						if (filter_field_set(&rule->desc, argv[i], line_num) == -1)
1969 							return -1;
1970 						rule->action.precedence++;
1971 					}
1972 					i++;
1973 				}
1974 
1975 				done_from = 1;
1976 			}
1977 			else
1978 #endif /* !WITHOUT_USERAUTH */
1979 			if (equal(argv[i], "ip")) {
1980 				if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
1981 					return -1;
1982 
1983 				rule->all_conns = filter_is_all(argv[i]);
1984 
1985 				if (!rule->all_conns) {
1986 					rule->exact_ip = filter_is_exact(argv[i]);
1987 					if (filter_field_set(&rule->ip, argv[i], line_num) == -1)
1988 						return -1;
1989 					rule->action.precedence++;
1990 				}
1991 				i++;
1992 				done_from = 1;
1993 			}
1994 			else if (equal(argv[i], "*")) {
1995 				i++;
1996 			}
1997 		}
1998 		else if (equal(argv[i], "to")) {
1999 			if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
2000 				return -1;
2001 
2002 			if (equal(argv[i], "ip") || equal(argv[i], "sni") || equal(argv[i], "cn") || equal(argv[i], "host") || equal(argv[i], "uri") ||
2003 					equal(argv[i], "port")) {
2004 				if (equal(argv[i], "ip") || equal(argv[i], "sni") || equal(argv[i], "cn") || equal(argv[i], "host") || equal(argv[i], "uri")) {
2005 					char *name = argv[i];
2006 
2007 					if ((i = filter_arg_index_inc(i, argc, name, line_num)) == -1)
2008 						return -1;
2009 
2010 					char *value = argv[i++];
2011 
2012 					if (!filter_site_set(rule, name, value, line_num))
2013 						return -1;
2014 
2015 					rule->action.precedence++;
2016 					done_site = 1;
2017 				}
2018 
2019 				if (i < argc && equal(argv[i], "port")) {
2020 					if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
2021 						return -1;
2022 
2023 					rule->action.precedence++;
2024 
2025 					if (filter_port_set(rule, argv[i++], line_num) == -1)
2026 						return -1;
2027 				}
2028 			}
2029 			else if (equal(argv[i], "*")) {
2030 				i++;
2031 			}
2032 		}
2033 		else if (equal(argv[i], "log")) {
2034 			if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
2035 				return -1;
2036 
2037 			// Log actions increase rule precedence too, but this effects log actions only, not the precedence of filter actions
2038 			rule->action.precedence++;
2039 
2040 			if (equal(argv[i], "connect") || equal(argv[i], "master") || equal(argv[i], "cert") || equal(argv[i], "content") || equal(argv[i], "pcap") ||
2041 				equal(argv[i], "!connect") || equal(argv[i], "!master") || equal(argv[i], "!cert") || equal(argv[i], "!content") || equal(argv[i], "!pcap")
2042 #ifndef WITHOUT_MIRROR
2043 				|| equal(argv[i], "mirror") || equal(argv[i], "!mirror")
2044 #endif /* !WITHOUT_MIRROR */
2045 				) {
2046 				do {
2047 					if (equal(argv[i], "connect"))
2048 						rule->action.log_connect = 2;
2049 					else if (equal(argv[i], "master"))
2050 						rule->action.log_master = 2;
2051 					else if (equal(argv[i], "cert"))
2052 						rule->action.log_cert = 2;
2053 					else if (equal(argv[i], "content"))
2054 						rule->action.log_content = 2;
2055 					else if (equal(argv[i], "pcap"))
2056 						rule->action.log_pcap = 2;
2057 					else if (equal(argv[i], "!connect"))
2058 						rule->action.log_connect = 1;
2059 					else if (equal(argv[i], "!master"))
2060 						rule->action.log_master = 1;
2061 					else if (equal(argv[i], "!cert"))
2062 						rule->action.log_cert = 1;
2063 					else if (equal(argv[i], "!content"))
2064 						rule->action.log_content = 1;
2065 					else if (equal(argv[i], "!pcap"))
2066 						rule->action.log_pcap = 1;
2067 #ifndef WITHOUT_MIRROR
2068 					else if (equal(argv[i], "mirror"))
2069 						rule->action.log_mirror = 2;
2070 					else if (equal(argv[i], "!mirror"))
2071 						rule->action.log_mirror = 1;
2072 #endif /* !WITHOUT_MIRROR */
2073 
2074 					if (++i == argc)
2075 						break;
2076 				} while (equal(argv[i], "connect") || equal(argv[i], "master") || equal(argv[i], "cert") || equal(argv[i], "content") || equal(argv[i], "pcap") ||
2077 						 equal(argv[i], "!connect") || equal(argv[i], "!master") || equal(argv[i], "!cert") || equal(argv[i], "!content") || equal(argv[i], "!pcap")
2078 #ifndef WITHOUT_MIRROR
2079 					|| equal(argv[i], "mirror") || equal(argv[i], "!mirror")
2080 #endif /* !WITHOUT_MIRROR */
2081 					);
2082 			}
2083 			else if (equal(argv[i], "*")) {
2084 				rule->action.log_connect = 2;
2085 				rule->action.log_master = 2;
2086 				rule->action.log_cert = 2;
2087 				rule->action.log_content = 2;
2088 				rule->action.log_pcap = 2;
2089 #ifndef WITHOUT_MIRROR
2090 				rule->action.log_mirror = 2;
2091 #endif /* !WITHOUT_MIRROR */
2092 				i++;
2093 			}
2094 			else if (equal(argv[i], "!*")) {
2095 				rule->action.log_connect = 1;
2096 				rule->action.log_master = 1;
2097 				rule->action.log_cert = 1;
2098 				rule->action.log_content = 1;
2099 				rule->action.log_pcap = 1;
2100 #ifndef WITHOUT_MIRROR
2101 				rule->action.log_mirror = 1;
2102 #endif /* !WITHOUT_MIRROR */
2103 				i++;
2104 			}
2105 		}
2106 	}
2107 
2108 	if (!done_from) {
2109 		rule->all_conns = 1;
2110 	}
2111 	if (!done_site) {
2112 		rule->dstip = strdup("");
2113 		if (!rule->dstip)
2114 			return oom_return_na();
2115 		rule->all_dstips = 1;
2116 
2117 		rule->sni = strdup("");
2118 		if (!rule->sni)
2119 			return oom_return_na();
2120 		rule->all_snis = 1;
2121 
2122 		rule->cn = strdup("");
2123 		if (!rule->cn)
2124 			return oom_return_na();
2125 		rule->all_cns = 1;
2126 
2127 		rule->host = strdup("");
2128 		if (!rule->host)
2129 			return oom_return_na();
2130 		rule->all_hosts = 1;
2131 
2132 		rule->uri = strdup("");
2133 		if (!rule->uri)
2134 			return oom_return_na();
2135 		rule->all_uris = 1;
2136 	}
2137 
2138 #ifdef DEBUG_PROXY
2139 	rule->action.line_num = line_num;
2140 #endif /* DEBUG_PROXY */
2141 
2142 	append_list(&opts->filter_rules, rule, filter_rule_t);
2143 
2144 #ifdef DEBUG_OPTS
2145 	filter_rule_dbg_print(rule);
2146 #endif /* DEBUG_OPTS */
2147 	return 0;
2148 }
2149 
2150 static int WUNRES
2151 filter_rule_parse(opts_t *opts, conn_opts_t *conn_opts, const char *name, int argc, char **argv, unsigned int line_num);
2152 
2153 // Max = from(1) + user(2) + desc(2) + to(1) + sni(2) + port(2) + log(16 with macro)
2154 #define MAX_FILTER_RULE_TOKENS 26
2155 
2156 static int WUNRES
filter_rule_macro_expand(opts_t * opts,conn_opts_t * conn_opts,const char * name,int argc,char ** argv,int i,unsigned int line_num)2157 filter_rule_macro_expand(opts_t *opts, conn_opts_t *conn_opts, const char *name, int argc, char **argv, int i, unsigned int line_num)
2158 {
2159 	if (argv[i][0] == '$') {
2160 		macro_t *macro;
2161 		if ((macro = filter_macro_find(opts->macro, argv[i]))) {
2162 			value_t *value = macro->value;
2163 			while (value) {
2164 				// Prevent infinite macro expansion, macros do not allow it, but macro expansion should detect it too
2165 				if (value->value[0] == '$') {
2166 					fprintf(stderr, "Invalid macro value '%s' on line %d\n", value->value, line_num);
2167 					return -1;
2168 				}
2169 
2170 				char *expanded_argv[sizeof(char *) * MAX_FILTER_RULE_TOKENS];
2171 				memcpy(expanded_argv, argv, sizeof expanded_argv);
2172 
2173 				expanded_argv[i] = value->value;
2174 
2175 				if (filter_rule_parse(opts, conn_opts, name, argc, expanded_argv, line_num) == -1)
2176 					return -1;
2177 
2178 				value = value->next;
2179 			}
2180 			// End of macro expansion, the caller must stop processing the rule
2181 			return 1;
2182 		}
2183 		else {
2184 			fprintf(stderr, "No such macro '%s' on line %d\n", argv[i], line_num);
2185 			return -1;
2186 		}
2187 	}
2188 	return 0;
2189 }
2190 
2191 static int WUNRES
filter_rule_parse(opts_t * opts,conn_opts_t * conn_opts,const char * name,int argc,char ** argv,unsigned int line_num)2192 filter_rule_parse(opts_t *opts, conn_opts_t *conn_opts, const char *name, int argc, char **argv, unsigned int line_num)
2193 {
2194 	int done_all = 0;
2195 	int done_from = 0;
2196 	int done_to = 0;
2197 	int done_log = 0;
2198 	int rv = 0;
2199 	int i = 0;
2200 	while (i < argc) {
2201 		if (equal(argv[i], "*")) {
2202 			if (done_all) {
2203 				fprintf(stderr, "Only one '*' statement allowed on line %d\n", line_num);
2204 				return -1;
2205 			}
2206 			if (++i > argc) {
2207 				fprintf(stderr, "Too many arguments for '*' on line %d\n", line_num);
2208 				return -1;
2209 			}
2210 			done_all = 1;
2211 		}
2212 		else if (equal(argv[i], "from")) {
2213 			if (done_from) {
2214 				fprintf(stderr, "Only one 'from' statement allowed on line %d\n", line_num);
2215 				return -1;
2216 			}
2217 
2218 			if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
2219 				return -1;
2220 #ifndef WITHOUT_USERAUTH
2221 			if (equal(argv[i], "user") || equal(argv[i], "desc")) {
2222 				if (equal(argv[i], "user")) {
2223 					if (!conn_opts->user_auth) {
2224 						fprintf(stderr, "User filter requires user auth on line %d\n", line_num);
2225 						return -1;
2226 					}
2227 
2228 					if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
2229 						return -1;
2230 
2231 					if (argv[i][strlen(argv[i]) - 1] == '*') {
2232 						// Nothing to do for '*' or substring search for 'user*'
2233 					}
2234 					else if ((rv = filter_rule_macro_expand(opts, conn_opts, name, argc, argv, i, line_num)) != 0) {
2235 						return rv;
2236 					}
2237 					else if (!sys_isuser(argv[i])) {
2238 						fprintf(stderr, "No such user '%s' on line %d\n", argv[i], line_num);
2239 						return -1;
2240 					}
2241 					i++;
2242 				}
2243 
2244 				// It is possible to define desc without user (i.e. * or all_users), hence no 'else' here
2245 				if (i < argc && equal(argv[i], "desc")) {
2246 					if (!conn_opts->user_auth) {
2247 						fprintf(stderr, "Desc filter requires user auth on line %d\n", line_num);
2248 						return -1;
2249 					}
2250 
2251 					if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
2252 						return -1;
2253 
2254 					if (argv[i][strlen(argv[i]) - 1] == '*') {
2255 						// Nothing to do for '*' or substring search for 'desc*'
2256 					}
2257 					else if ((rv = filter_rule_macro_expand(opts, conn_opts, name, argc, argv, i, line_num)) != 0) {
2258 						return rv;
2259 					}
2260 					i++;
2261 				}
2262 
2263 				done_from = 1;
2264 			}
2265 			else
2266 #endif /* !WITHOUT_USERAUTH */
2267 			if (equal(argv[i], "ip")) {
2268 				if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
2269 					return -1;
2270 
2271 				if (argv[i][strlen(argv[i]) - 1] == '*') {
2272 					// Nothing to do for '*' or substring search for 'ip*'
2273 					}
2274 				else if ((rv = filter_rule_macro_expand(opts, conn_opts, name, argc, argv, i, line_num)) != 0) {
2275 					return rv;
2276 				}
2277 				i++;
2278 				done_from = 1;
2279 			}
2280 			else if (equal(argv[i], "*")) {
2281 				i++;
2282 			}
2283 			else {
2284 				fprintf(stderr, "Unknown argument in filter rule at '%s' on line %d\n", argv[i], line_num);
2285 				return -1;
2286 			}
2287 		}
2288 		else if (equal(argv[i], "to")) {
2289 			if (done_to) {
2290 				fprintf(stderr, "Only one 'to' statement allowed on line %d\n", line_num);
2291 				return -1;
2292 			}
2293 
2294 			if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
2295 				return -1;
2296 
2297 			if (equal(argv[i], "ip") || equal(argv[i], "sni") || equal(argv[i], "cn") || equal(argv[i], "host") || equal(argv[i], "uri") ||
2298 					equal(argv[i], "port")) {
2299 				if (equal(argv[i], "ip") || equal(argv[i], "sni") || equal(argv[i], "cn") || equal(argv[i], "host") || equal(argv[i], "uri")) {
2300 					if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
2301 						return -1;
2302 
2303 					if ((rv = filter_rule_macro_expand(opts, conn_opts, name, argc, argv, i, line_num)) != 0) {
2304 						return rv;
2305 					}
2306 					i++;
2307 				}
2308 
2309 				// It is possible to define port without site (i.e. * or all_sites), hence no 'else' here
2310 				if (i < argc && equal(argv[i], "port")) {
2311 					if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
2312 						return -1;
2313 
2314 					if ((rv = filter_rule_macro_expand(opts, conn_opts, name, argc, argv, i, line_num)) != 0) {
2315 						return rv;
2316 					}
2317 					i++;
2318 				}
2319 				done_to = 1;
2320 			}
2321 			else if (equal(argv[i], "*")) {
2322 				i++;
2323 			}
2324 			else {
2325 				fprintf(stderr, "Unknown argument in filter rule at '%s' on line %d\n", argv[i], line_num);
2326 				return -1;
2327 			}
2328 		}
2329 		else if (equal(argv[i], "log")) {
2330 			if (done_log) {
2331 				fprintf(stderr, "Only one 'log' statement allowed on line %d\n", line_num);
2332 				return -1;
2333 			}
2334 
2335 			if ((i = filter_arg_index_inc(i, argc, argv[i], line_num)) == -1)
2336 				return -1;
2337 
2338 			if (equal(argv[i], "connect") || equal(argv[i], "master") || equal(argv[i], "cert") || equal(argv[i], "content") || equal(argv[i], "pcap") ||
2339 				equal(argv[i], "!connect") || equal(argv[i], "!master") || equal(argv[i], "!cert") || equal(argv[i], "!content") || equal(argv[i], "!pcap")
2340 #ifndef WITHOUT_MIRROR
2341 				|| equal(argv[i], "mirror") || equal(argv[i], "!mirror")
2342 #endif /* !WITHOUT_MIRROR */
2343 				|| argv[i][0] == '$') {
2344 				do {
2345 					if ((rv = filter_rule_macro_expand(opts, conn_opts, name, argc, argv, i, line_num)) != 0) {
2346 						return rv;
2347 					}
2348 					if (++i == argc)
2349 						break;
2350 				} while (equal(argv[i], "connect") || equal(argv[i], "master") || equal(argv[i], "cert") || equal(argv[i], "content") || equal(argv[i], "pcap") ||
2351 						 equal(argv[i], "!connect") || equal(argv[i], "!master") || equal(argv[i], "!cert") || equal(argv[i], "!content") || equal(argv[i], "!pcap")
2352 #ifndef WITHOUT_MIRROR
2353 					|| equal(argv[i], "mirror") || equal(argv[i], "!mirror")
2354 #endif /* !WITHOUT_MIRROR */
2355 					|| argv[i][0] == '$');
2356 
2357 				done_log = 1;
2358 			}
2359 			else if (equal(argv[i], "*")) {
2360 				i++;
2361 				done_log = 1;
2362 			}
2363 			else if (equal(argv[i], "!*")) {
2364 				i++;
2365 				done_log = 1;
2366 			}
2367 			else {
2368 				fprintf(stderr, "Unknown argument in filter rule at '%s' on line %d\n", argv[i], line_num);
2369 				return -1;
2370 			}
2371 		}
2372 		else {
2373 			fprintf(stderr, "Unknown argument in filter rule at '%s' on line %d\n", argv[i], line_num);
2374 				return -1;
2375 		}
2376 	}
2377 
2378 	// All checks passed and all macros expanded, if any
2379 	return filter_rule_translate(opts, name, argc, argv, line_num);
2380 }
2381 
2382 int
filter_rule_set(opts_t * opts,conn_opts_t * conn_opts,const char * name,char * value,unsigned int line_num)2383 filter_rule_set(opts_t *opts, conn_opts_t *conn_opts, const char *name, char *value, unsigned int line_num)
2384 {
2385 	char *argv[sizeof(char *) * MAX_FILTER_RULE_TOKENS];
2386 	int argc = 0;
2387 	char *p, *last = NULL;
2388 
2389 	for ((p = strtok_r(value, " ", &last));
2390 		 p;
2391 		 (p = strtok_r(NULL, " ", &last))) {
2392 		if (argc < MAX_FILTER_RULE_TOKENS) {
2393 			argv[argc++] = p;
2394 		} else {
2395 			fprintf(stderr, "Too many arguments in filter rule on line %d\n", line_num);
2396 			return -1;
2397 		}
2398 	}
2399 
2400 	return filter_rule_parse(opts, conn_opts, name, argc, argv, line_num);
2401 }
2402 
2403 static int WUNRES
filter_rule_struct_translate(filter_rule_t * rule,UNUSED conn_opts_t * conn_opts,const char * name,char * value,unsigned int line_num)2404 filter_rule_struct_translate(filter_rule_t *rule, UNUSED conn_opts_t *conn_opts, const char *name, char *value, unsigned int line_num)
2405 {
2406 	if (equal(name, "Action")) {
2407 		if (equal(value, "Divert"))
2408 			rule->action.divert = 1;
2409 		else if (equal(value, "Split"))
2410 			rule->action.split = 1;
2411 		else if (equal(value, "Pass"))
2412 			rule->action.pass = 1;
2413 		else if (equal(value, "Block"))
2414 			rule->action.block = 1;
2415 		else if (equal(value, "Match"))
2416 			rule->action.match = 1;
2417 		else {
2418 			fprintf(stderr, "Error in conf: Unknown Action '%s' on line %d\n", value, line_num);
2419 			return -1;
2420 		}
2421 	}
2422 #ifndef WITHOUT_USERAUTH
2423 	else if (equal(name, "User")) {
2424 		if (!conn_opts->user_auth) {
2425 			fprintf(stderr, "User filter requires user auth on line %d\n", line_num);
2426 			return -1;
2427 		}
2428 
2429 		if (value[strlen(value) - 1] != '*' && !sys_isuser(value)) {
2430 			fprintf(stderr, "No such user '%s' on line %d\n", value, line_num);
2431 			return -1;
2432 		}
2433 
2434 		if (!rule->desc && !rule->all_users) {
2435 			rule->action.precedence++;
2436 		}
2437 
2438 		rule->all_users = filter_is_all(value);
2439 
2440 		if (!rule->all_users) {
2441 			rule->exact_user = filter_is_exact(value);
2442 			if (filter_field_set(&rule->user, value, line_num) == -1)
2443 				return -1;
2444 			rule->action.precedence++;
2445 		}
2446 	}
2447 	else if (equal(name, "Desc")) {
2448 		if (!conn_opts->user_auth) {
2449 			fprintf(stderr, "Desc filter requires user auth on line %d\n", line_num);
2450 			return -1;
2451 		}
2452 
2453 		if (!rule->user && !rule->all_users) {
2454 			rule->action.precedence++;
2455 		}
2456 
2457 		if (filter_is_all(value)) {
2458 			if (!rule->user) {
2459 				rule->all_users = 1;
2460 			}
2461 		}
2462 		else {
2463 			rule->exact_desc = filter_is_exact(value);
2464 			if (filter_field_set(&rule->desc, value, line_num) == -1)
2465 				return -1;
2466 			rule->action.precedence++;
2467 		}
2468 	}
2469 #endif /* !WITHOUT_USERAUTH */
2470 	else if (equal(name, "SrcIp")) {
2471 		rule->all_conns = filter_is_all(value);
2472 
2473 		if (!rule->all_conns) {
2474 			rule->exact_ip = filter_is_exact(value);
2475 			if (filter_field_set(&rule->ip, value, line_num) == -1)
2476 				return -1;
2477 			rule->action.precedence++;
2478 		}
2479 	}
2480 	else if (equal(name, "SNI") || equal(name, "CN") || equal(name, "Host") || equal(name, "URI") || equal(name, "DstIp")) {
2481 		if (!filter_site_set(rule, name, value, line_num))
2482 			return -1;
2483 	}
2484 	else if (equal(name, "DstPort")) {
2485 		rule->action.precedence++;
2486 
2487 		if (filter_port_set(rule, value, line_num) == -1)
2488 			return -1;
2489 	}
2490 	else if (equal(name, "Log")) {
2491 		// We don't support $macros within multi valued Log lines, i.e. cannot mix log actions with $macros
2492 		// use either log actions concat with spaces or just a $macro, and no point trying to support it either
2493 #define MAX_LOG_TOKENS 14
2494 		int argc = 0;
2495 		char *p, *last = NULL;
2496 
2497 		for ((p = strtok_r(value, " ", &last));
2498 			 p;
2499 			 (p = strtok_r(NULL, " ", &last))) {
2500 			if (argc < MAX_LOG_TOKENS) {
2501 				argc++;
2502 
2503 				if (equal(p, "connect"))
2504 					rule->action.log_connect = 2;
2505 				else if (equal(p, "master"))
2506 					rule->action.log_master = 2;
2507 				else if (equal(p, "cert"))
2508 					rule->action.log_cert = 2;
2509 				else if (equal(p, "content"))
2510 					rule->action.log_content = 2;
2511 				else if (equal(p, "pcap"))
2512 					rule->action.log_pcap = 2;
2513 				else if (equal(p, "!connect"))
2514 					rule->action.log_connect = 1;
2515 				else if (equal(p, "!master"))
2516 					rule->action.log_master = 1;
2517 				else if (equal(p, "!cert"))
2518 					rule->action.log_cert = 1;
2519 				else if (equal(p, "!content"))
2520 					rule->action.log_content = 1;
2521 				else if (equal(p, "!pcap"))
2522 					rule->action.log_pcap = 1;
2523 #ifndef WITHOUT_MIRROR
2524 				else if (equal(p, "mirror"))
2525 					rule->action.log_mirror = 2;
2526 				else if (equal(p, "!mirror"))
2527 					rule->action.log_mirror = 1;
2528 #endif /* !WITHOUT_MIRROR */
2529 				else if (equal(p, "*")) {
2530 					rule->action.log_connect = 2;
2531 					rule->action.log_master = 2;
2532 					rule->action.log_cert = 2;
2533 					rule->action.log_content = 2;
2534 					rule->action.log_pcap = 2;
2535 #ifndef WITHOUT_MIRROR
2536 					rule->action.log_mirror = 2;
2537 #endif /* !WITHOUT_MIRROR */
2538 				}
2539 				else if (equal(p, "!*")) {
2540 					rule->action.log_connect = 1;
2541 					rule->action.log_master = 1;
2542 					rule->action.log_cert = 1;
2543 					rule->action.log_content = 1;
2544 					rule->action.log_pcap = 1;
2545 #ifndef WITHOUT_MIRROR
2546 					rule->action.log_mirror = 1;
2547 #endif /* !WITHOUT_MIRROR */
2548 				}
2549 				else {
2550 					fprintf(stderr, "Error in conf: Unknown Log '%s' on line %d\n", p, line_num);
2551 					return -1;
2552 				}
2553 			} else {
2554 				fprintf(stderr, "Too many Log arguments in filter rule on line %d\n", line_num);
2555 				return -1;
2556 			}
2557 		}
2558 	}
2559 	else if (equal(name, "ReconnectSSL")) {
2560 		// Already processed by the parser
2561 	}
2562 	else {
2563 		// This should have been handled by the parser, but in case
2564 		fprintf(stderr, "Error in conf: Unknown option '%s' on line %d\n", name, line_num);
2565 		return -1;
2566 	}
2567 	return 0;
2568 }
2569 
2570 static int WUNRES
filter_rule_struct_translate_nvls(opts_t * opts,name_value_lines_t nvls[],int nvls_size,conn_opts_t * conn_opts,const char * argv0,tmp_opts_t * tmp_opts,filter_parse_state_t parse_state)2571 filter_rule_struct_translate_nvls(opts_t *opts, name_value_lines_t nvls[], int nvls_size, conn_opts_t *conn_opts,
2572 		const char *argv0, tmp_opts_t *tmp_opts, filter_parse_state_t parse_state)
2573 {
2574 	filter_rule_t *rule = malloc(sizeof(filter_rule_t));
2575 	if (!rule)
2576 		return oom_return_na();
2577 	memset(rule, 0, sizeof(filter_rule_t));
2578 
2579 	for (int i = 0; i < nvls_size; i++) {
2580 		if (filter_rule_struct_translate(rule, conn_opts, nvls[i].name, nvls[i].value, nvls[i].line_num) == -1) {
2581 			filter_rule_free(rule);
2582 			return -1;
2583 		}
2584 	}
2585 
2586 	if (!rule->ip
2587 #ifndef WITHOUT_USERAUTH
2588 		&& !rule->all_users && !rule->user && !rule->desc
2589 #endif /* !WITHOUT_USERAUTH */
2590 		) {
2591 		rule->all_conns = 1;
2592 	}
2593 	if (!rule->dstip && !rule->sni && !rule->cn && !rule->host && !rule->uri) {
2594 		rule->dstip = strdup("");
2595 		if (!rule->dstip)
2596 			return oom_return_na();
2597 		rule->all_dstips = 1;
2598 
2599 		rule->sni = strdup("");
2600 		if (!rule->sni)
2601 			return oom_return_na();
2602 		rule->all_snis = 1;
2603 
2604 		rule->cn = strdup("");
2605 		if (!rule->cn)
2606 			return oom_return_na();
2607 		rule->all_cns = 1;
2608 
2609 		rule->host = strdup("");
2610 		if (!rule->host)
2611 			return oom_return_na();
2612 		rule->all_hosts = 1;
2613 
2614 		rule->uri = strdup("");
2615 		if (!rule->uri)
2616 			return oom_return_na();
2617 		rule->all_uris = 1;
2618 	}
2619 	else {
2620 		// Increment precedence for dst site only once here, we allow for multi site struct rules
2621 		rule->action.precedence++;
2622 	}
2623 
2624 	// Increment precedence for log action only once here, if any specified, because otherwise
2625 	// we would inc it multiple times while translating Log specifications, since we allow for multiple Log lines
2626 	if (rule->action.log_connect || rule->action.log_master || rule->action.log_cert || rule->action.log_content || rule->action.log_pcap
2627 #ifndef WITHOUT_MIRROR
2628 			|| rule->action.log_mirror
2629 #endif /* !WITHOUT_MIRROR */
2630 			) {
2631 		rule->action.precedence++;
2632 	}
2633 
2634 	// Set conn_opts only if the rule specifies any conn option to override the global or proxyspec conn options
2635 	if (parse_state.conn_opts) {
2636 		rule->action.conn_opts = conn_opts_copy(conn_opts, argv0, tmp_opts);
2637 		if (!rule->action.conn_opts) {
2638 			filter_rule_free(rule);
2639 			return oom_return_na();
2640 		}
2641 	}
2642 
2643 #ifdef DEBUG_PROXY
2644 	rule->action.line_num = tmp_opts->line_num;
2645 #endif /* DEBUG_PROXY */
2646 
2647 	append_list(&opts->filter_rules, rule, filter_rule_t);
2648 
2649 #ifdef DEBUG_OPTS
2650 	filter_rule_dbg_print(rule);
2651 #endif /* DEBUG_OPTS */
2652 	return 0;
2653 }
2654 
2655 static int WUNRES
filter_rule_struct_macro_expand(opts_t * opts,name_value_lines_t nvls[],int nvls_size,conn_opts_t * conn_opts,const char * argv0,tmp_opts_t * tmp_opts,filter_parse_state_t parse_state)2656 filter_rule_struct_macro_expand(opts_t *opts, name_value_lines_t nvls[], int nvls_size, conn_opts_t *conn_opts,
2657 		const char *argv0, tmp_opts_t *tmp_opts, filter_parse_state_t parse_state)
2658 {
2659 	for (int i = 0; i < nvls_size; i++) {
2660 		if (nvls[i].value[0] == '$') {
2661 			macro_t *macro;
2662 			if ((macro = filter_macro_find(opts->macro, nvls[i].value))) {
2663 				value_t *value = macro->value;
2664 				while (value) {
2665 					// Prevent infinite macro expansion, macros do not allow it, but macro expansion should detect it too
2666 					if (value->value[0] == '$') {
2667 						fprintf(stderr, "Invalid macro value '%s' on line %d\n", value->value, nvls[i].line_num);
2668 						return -1;
2669 					}
2670 
2671 					name_value_lines_t n[nvls_size];
2672 					memcpy(n, nvls, sizeof(name_value_lines_t) * nvls_size);
2673 
2674 					n[i].value = value->value;
2675 
2676 					if (filter_rule_struct_macro_expand(opts, n, nvls_size, conn_opts, argv0, tmp_opts, parse_state) == -1)
2677 						return -1;
2678 
2679 					value = value->next;
2680 				}
2681 				// End of macro expansion, the caller must stop processing the rule
2682 				return 1;
2683 			}
2684 			else {
2685 				fprintf(stderr, "No such macro '%s' on line %d\n", nvls[i].value, nvls[i].line_num);
2686 				return -1;
2687 			}
2688 		}
2689 	}
2690 
2691 	if (filter_rule_struct_translate_nvls(opts, nvls, nvls_size, conn_opts, argv0, tmp_opts, parse_state) == -1)
2692 		return -1;
2693 	return 0;
2694 }
2695 
2696 static int WUNRES
filter_rule_struct_parse(name_value_lines_t nvls[],int * nvls_size,conn_opts_t * conn_opts,const char * argv0,char * name,char * value,unsigned int line_num,tmp_opts_t * tmp_opts,filter_parse_state_t * parse_state)2697 filter_rule_struct_parse(name_value_lines_t nvls[], int *nvls_size, conn_opts_t *conn_opts, const char *argv0,
2698 		char *name, char *value, unsigned int line_num, tmp_opts_t *tmp_opts, filter_parse_state_t *parse_state)
2699 {
2700 	// Closing brace '}' is the only option without a value
2701 	// and only allowed in structured filtering rules and proxyspecs
2702 	if ((!value || !strlen(value)) && !equal(name, "}")) {
2703 		fprintf(stderr, "Error in conf: No value assigned for %s on line %d\n", name, line_num);
2704 		return -1;
2705 	}
2706 
2707 	if (equal(name, "}")) {
2708 #ifdef DEBUG_OPTS
2709 		log_dbg_printf("FilterRule } on line %d\n", line_num);
2710 #endif /* DEBUG_OPTS */
2711 		if (!parse_state->action) {
2712 			fprintf(stderr, "Incomplete FilterRule on line %d\n", line_num);
2713 			return -1;
2714 		}
2715 		// Return 2 to indicate the end of structured filter rule
2716 		return 2;
2717 	}
2718 
2719 	int rv = set_conn_opts_option(conn_opts, argv0, name, value, line_num, tmp_opts);
2720 	if (rv == -1) {
2721 		fprintf(stderr, "Error in conf: '%s' on line %d\n", name, line_num);
2722 		return -1;
2723 	} else if (rv == 0) {
2724 		parse_state->conn_opts = 1;
2725 		return 0;
2726 	}
2727 
2728 	if (equal(name, "Action")) {
2729 		if (parse_state->action) {
2730 			fprintf(stderr, "Error in conf: Only one Action spec allowed '%s' on line %d\n", value, line_num);
2731 			return -1;
2732 		}
2733 		parse_state->action = 1;
2734 	}
2735 	else if (equal(name, "User")) {
2736 		if (parse_state->user) {
2737 			fprintf(stderr, "Error in conf: Only one User spec allowed '%s' on line %d\n", value, line_num);
2738 			return -1;
2739 		}
2740 		if (parse_state->srcip) {
2741 			fprintf(stderr, "Error in conf: Cannot specify both SrcIp and User '%s' on line %d\n", value, line_num);
2742 			return -1;
2743 		}
2744 		parse_state->user = 1;
2745 	}
2746 	else if (equal(name, "Desc")) {
2747 		if (parse_state->desc) {
2748 			fprintf(stderr, "Error in conf: Only one Desc spec allowed '%s' on line %d\n", value, line_num);
2749 			return -1;
2750 		}
2751 		if (parse_state->srcip) {
2752 			fprintf(stderr, "Error in conf: Cannot specify both SrcIp and Desc '%s' on line %d\n", value, line_num);
2753 			return -1;
2754 		}
2755 		parse_state->desc = 1;
2756 	}
2757 	else if (equal(name, "SrcIp")) {
2758 		if (parse_state->srcip) {
2759 			fprintf(stderr, "Error in conf: Only one SrcIp spec allowed '%s' on line %d\n", value, line_num);
2760 			return -1;
2761 		}
2762 		if (parse_state->user || parse_state->desc) {
2763 			fprintf(stderr, "Error in conf: Cannot specify both User/Desc and SrcIp '%s' on line %d\n", value, line_num);
2764 			return -1;
2765 		}
2766 		parse_state->srcip = 1;
2767 	}
2768 	else if (equal(name, "SNI")) {
2769 		if (parse_state->sni) {
2770 			fprintf(stderr, "Error in conf: Only one SNI spec allowed '%s' on line %d\n", value, line_num);
2771 			return -1;
2772 		}
2773 		parse_state->sni = 1;
2774 	}
2775 	else if (equal(name, "CN")) {
2776 		if (parse_state->cn) {
2777 			fprintf(stderr, "Error in conf: Only one CN spec allowed '%s' on line %d\n", value, line_num);
2778 			return -1;
2779 		}
2780 		parse_state->cn = 1;
2781 	}
2782 	else if (equal(name, "Host")) {
2783 		if (parse_state->host) {
2784 			fprintf(stderr, "Error in conf: Only one Host spec allowed '%s' on line %d\n", value, line_num);
2785 			return -1;
2786 		}
2787 		parse_state->host = 1;
2788 	}
2789 	else if (equal(name, "URI")) {
2790 		if (parse_state->uri) {
2791 			fprintf(stderr, "Error in conf: Only one URI spec allowed '%s' on line %d\n", value, line_num);
2792 			return -1;
2793 		}
2794 		parse_state->uri = 1;
2795 	}
2796 	else if (equal(name, "DstIp")) {
2797 		if (parse_state->dstip) {
2798 			fprintf(stderr, "Error in conf: Only one DstIp spec allowed '%s' on line %d\n", value, line_num);
2799 			return -1;
2800 		}
2801 		parse_state->dstip = 1;
2802 	}
2803 	else if (equal(name, "DstPort")) {
2804 		if (parse_state->dstport) {
2805 			fprintf(stderr, "Error in conf: Only one DstPort spec allowed '%s' on line %d\n", value, line_num);
2806 			return -1;
2807 		}
2808 		parse_state->dstport = 1;
2809 	}
2810 	else if (equal(name, "Log")) {
2811 		// Log can be used more than once to define multiple log actions, if not using macros
2812 	}
2813 	else if (equal(name, "ReconnectSSL")) {
2814 		if (parse_state->reconnect_ssl) {
2815 			fprintf(stderr, "Error in conf: Only one ReconnectSSL spec allowed '%s' on line %d\n", value, line_num);
2816 			return -1;
2817 		}
2818 		parse_state->reconnect_ssl = 1;
2819 
2820 		int yes = check_value_yesno(value, "ReconnectSSL", line_num);
2821 		if (yes == -1)
2822 			return -1;
2823 		conn_opts->reconnect_ssl = yes;
2824 #ifdef DEBUG_OPTS
2825 		log_dbg_printf("ReconnectSSL: %u\n", conn_opts->reconnect_ssl);
2826 #endif /* DEBUG_OPTS */
2827 	}
2828 	else {
2829 		fprintf(stderr, "Error in conf: Unknown option '%s' on line %d\n", name, line_num);
2830 		return -1;
2831 	}
2832 
2833 	nvls[*nvls_size].name = strdup(name);
2834 	nvls[*nvls_size].value = strdup(value);
2835 	nvls[*nvls_size].line_num = line_num;
2836 	(*nvls_size)++;
2837 
2838 	return 0;
2839 }
2840 
2841 int
load_filterrule_struct(opts_t * opts,conn_opts_t * conn_opts,const char * argv0,unsigned int * line_num,FILE * f,tmp_opts_t * orig_tmp_opts)2842 load_filterrule_struct(opts_t *opts, conn_opts_t *conn_opts, const char *argv0, unsigned int *line_num, FILE *f, tmp_opts_t *orig_tmp_opts)
2843 {
2844 	int retval = -1;
2845 	char *name, *value;
2846 	char *line = NULL;
2847 	size_t line_len;
2848 	int i;
2849 
2850 	filter_parse_state_t parse_state;
2851 	memset(&parse_state, 0, sizeof(filter_parse_state_t));
2852 
2853 #define MAX_NVLS_SIZE 100
2854 	int nvls_size = 0;
2855 	name_value_lines_t nvls[MAX_NVLS_SIZE];
2856 
2857 	conn_opts_t *copts = conn_opts_copy(conn_opts, argv0, orig_tmp_opts);
2858 	if (!copts)
2859 		return -1;
2860 
2861 	// Operate on a local copy of orig_tmp_opts, do not modify the orig_tmp_opts
2862 	// otherwise struct filtering rules can override global or proxyspec options
2863 	tmp_opts_t *tmp_opts = tmp_opts_copy(orig_tmp_opts);
2864 	if (!tmp_opts) {
2865 		retval = -1;
2866 		goto err;
2867 	}
2868 
2869 #ifdef DEBUG_PROXY
2870 	tmp_opts->line_num = *line_num;
2871 #endif /* DEBUG_PROXY */
2872 
2873 	int closing_brace = 0;
2874 
2875 	while (!feof(f) && !closing_brace) {
2876 		if (getline(&line, &line_len, f) == -1) {
2877 			break;
2878 		}
2879 		if (line == NULL) {
2880 			fprintf(stderr, "Error in conf file: getline() returns NULL line after line %d\n", *line_num);
2881 			retval = -1;
2882 			goto err;
2883 		}
2884 		(*line_num)++;
2885 
2886 		/* Skip white space */
2887 		for (name = line; *name == ' ' || *name == '\t'; name++);
2888 
2889 		/* Skip comments and empty lines */
2890 		if ((name[0] == '\0') || (name[0] == '#') || (name[0] == ';') ||
2891 			(name[0] == '\r') || (name[0] == '\n')) {
2892 			continue;
2893 		}
2894 
2895 		retval = get_name_value(name, &value, ' ', *line_num);
2896 		if (retval == 0) {
2897 			retval = filter_rule_struct_parse(nvls, &nvls_size, copts, argv0, name, value, *line_num, tmp_opts, &parse_state);
2898 		}
2899 		if (retval == -1) {
2900 			goto err;
2901 		} else if (retval == 2) {
2902 			closing_brace = 1;
2903 		}
2904 
2905 		if (nvls_size >= MAX_NVLS_SIZE) {
2906 			fprintf(stderr, "Error in conf file: max allowed lines reached in struct FilterRule on line %d\n", *line_num);
2907 			retval = -1;
2908 			goto err;
2909 		}
2910 
2911 		free(line);
2912 		line = NULL;
2913 	}
2914 
2915 	if (!closing_brace) {
2916 		fprintf(stderr, "Error in conf file: struct FilterRule has no closing brace '}' after line %d\n", *line_num);
2917 		retval = -1;
2918 		goto err;
2919 	}
2920 
2921 	if (filter_rule_struct_macro_expand(opts, nvls, nvls_size, copts, argv0, tmp_opts, parse_state) == -1) {
2922 		retval = -1;
2923 		goto err;
2924 	}
2925 
2926 	retval = 0;
2927 err:
2928 	conn_opts_free(copts);
2929 	if (tmp_opts)
2930 		tmp_opts_free(tmp_opts);
2931 	for (i = 0; i < nvls_size; i++) {
2932 		free(nvls[i].name);
2933 		free(nvls[i].value);
2934 	}
2935 	if (line)
2936 		free(line);
2937 	return retval;
2938 }
2939 
2940 static filter_port_t *
filter_port_exact_match(kbtree_t (port)* btree,char * p)2941 filter_port_exact_match(kbtree_t(port) *btree, char *p)
2942 {
2943 	if (!btree)
2944 		return NULL;
2945 	filter_port_t **port = kb_get(port, btree, p);
2946 	return port ? *port : NULL;
2947 }
2948 
2949 static filter_port_t *
filter_port_substring_match(ACMachine (char)* acm,char * port)2950 filter_port_substring_match(ACMachine(char) *acm, char *port)
2951 {
2952 	if (!acm)
2953 		return NULL;
2954 	filter_port_t *p = NULL;
2955 	match_acm(acm, port, p);
2956 	return p;
2957 }
2958 
2959 filter_port_t *
filter_port_find(filter_site_t * site,char * p)2960 filter_port_find(filter_site_t *site, char *p)
2961 {
2962 	filter_port_t *port;
2963 	if ((port = filter_port_exact_match(site->port_btree, p)))
2964 		return port;
2965 	if ((port = filter_port_substring_match(site->port_acm, p)))
2966 		return port;
2967 	return site->port_all;
2968 }
2969 
2970 static filter_port_t *
filter_port_substring_exact_match(ACMachine (char)* acm,char * p)2971 filter_port_substring_exact_match(ACMachine(char) *acm, char *p)
2972 {
2973 	if (acm) {
2974 		const ACState(char) *state = ACM_reset(acm);
2975 		for (char *c = p; *c; c++) {
2976 			size_t nb = ACM_match(state, *c);
2977 			for (size_t j = 0; j < nb; j++) {
2978 				filter_port_t *value;
2979 				ACM_get_match(state, j, 0, (void **)&value);
2980 				// ACM matches any substring, make sure the match is exact
2981 				if (equal(value->port, p))
2982 					return value;
2983 			}
2984 		}
2985 	}
2986 	return NULL;
2987 }
2988 
2989 static filter_port_t *
filter_port_find_exact(filter_site_t * site,filter_rule_t * rule)2990 filter_port_find_exact(filter_site_t *site, filter_rule_t *rule)
2991 {
2992 	if (rule->all_ports)
2993 		return site->port_all;
2994 	else if (rule->exact_port)
2995 		return filter_port_exact_match(site->port_btree, rule->port);
2996 	else
2997 		return filter_port_substring_exact_match(site->port_acm, rule->port);
2998 }
2999 
3000 static int NONNULL(1,2) WUNRES
filter_port_add(filter_site_t * site,filter_rule_t * rule,const char * argv0,tmp_opts_t * tmp_opts)3001 filter_port_add(filter_site_t *site, filter_rule_t *rule, const char *argv0, tmp_opts_t *tmp_opts)
3002 {
3003 	filter_port_t *port = filter_port_find_exact(site, rule);
3004 	if (!port) {
3005 		port = malloc(sizeof(filter_port_t));
3006 		if (!port)
3007 			return oom_return_na();
3008 		memset(port, 0, sizeof(filter_port_t));
3009 
3010 		port->port = strdup(rule->port);
3011 		if (!port->port)
3012 			return oom_return_na();
3013 
3014 		if (rule->all_ports) {
3015 			site->port_all = port;
3016 		}
3017 		else if (rule->exact_port) {
3018 			if (!site->port_btree)
3019 				if (!(site->port_btree = kb_init(port, KB_DEFAULT_SIZE)))
3020 					return oom_return_na();
3021 
3022 			kb_put(port, site->port_btree, port);
3023 		}
3024 		else {
3025 			if (!site->port_acm)
3026 				if (!(site->port_acm = ACM_create(char)))
3027 					return oom_return_na();
3028 
3029 			Keyword(char) k;
3030 			ACM_KEYWORD_SET(k, port->port, strlen(port->port));
3031 			ACM_register_keyword(site->port_acm, k, port, free_port_func);
3032 		}
3033 	}
3034 
3035 	port->all_ports = rule->all_ports;
3036 	port->exact = rule->exact_port;
3037 
3038 	// Do not override the specs of port rules at higher precedence
3039 	// precedence can only go up not down
3040 	if (rule->action.precedence >= port->action.precedence) {
3041 		// Multiple rules can set an action for the same port, hence the bit-wise OR
3042 		port->action.divert |= rule->action.divert;
3043 		port->action.split |= rule->action.split;
3044 		port->action.pass |= rule->action.pass;
3045 		port->action.block |= rule->action.block;
3046 		port->action.match |= rule->action.match;
3047 
3048 		// Multiple log actions can be set for the same port
3049 		// Multiple rules can enable/disable or don't change a log action for the same port
3050 		// 0: don't change, 1: disable, 2: enable
3051 		if (rule->action.log_connect)
3052 			port->action.log_connect = rule->action.log_connect;
3053 		if (rule->action.log_master)
3054 			port->action.log_master = rule->action.log_master;
3055 		if (rule->action.log_cert)
3056 			port->action.log_cert = rule->action.log_cert;
3057 		if (rule->action.log_content)
3058 			port->action.log_content = rule->action.log_content;
3059 		if (rule->action.log_pcap)
3060 			port->action.log_pcap = rule->action.log_pcap;
3061 #ifndef WITHOUT_MIRROR
3062 		if (rule->action.log_mirror)
3063 			port->action.log_mirror = rule->action.log_mirror;
3064 #endif /* !WITHOUT_MIRROR */
3065 
3066 		if (rule->action.conn_opts) {
3067 			if (port->action.conn_opts)
3068 				conn_opts_free(port->action.conn_opts);
3069 			port->action.conn_opts = conn_opts_copy(rule->action.conn_opts, argv0, tmp_opts);
3070 			if (!port->action.conn_opts)
3071 				return oom_return_na();
3072 		}
3073 
3074 		port->action.precedence = rule->action.precedence;
3075 #ifdef DEBUG_PROXY
3076 		port->action.line_num = rule->action.line_num;
3077 #endif /* DEBUG_PROXY */
3078 	}
3079 	return 0;
3080 }
3081 
3082 filter_site_t *
filter_site_exact_match(kbtree_t (site)* btree,char * s)3083 filter_site_exact_match(kbtree_t(site) *btree, char *s)
3084 {
3085 	if (!btree)
3086 		return NULL;
3087 	filter_site_t **site = kb_get(site, btree, s);
3088 	return site ? *site : NULL;
3089 }
3090 
3091 filter_site_t *
filter_site_substring_match(ACMachine (char)* acm,char * site)3092 filter_site_substring_match(ACMachine(char) *acm, char *site)
3093 {
3094 	if (!acm)
3095 		return NULL;
3096 	filter_site_t *s = NULL;
3097 	match_acm(acm, site, s);
3098 	return s;
3099 }
3100 
3101 filter_site_t *
filter_site_find(kbtree_t (site)* btree,ACMachine (char)* acm,filter_site_t * all,char * s)3102 filter_site_find(kbtree_t(site) *btree, ACMachine(char) *acm, filter_site_t *all, char *s)
3103 {
3104 	filter_site_t *site;
3105 	if ((site = filter_site_exact_match(btree, s)))
3106 		return site;
3107 	if ((site = filter_site_substring_match(acm, s)))
3108 		return site;
3109 	return all;
3110 }
3111 
3112 static filter_site_t *
filter_site_substring_exact_match(ACMachine (char)* acm,char * s)3113 filter_site_substring_exact_match(ACMachine(char) *acm, char *s)
3114 {
3115 	if (acm) {
3116 		const ACState(char) *state = ACM_reset(acm);
3117 		for (char *c = s; *c; c++) {
3118 			size_t nb = ACM_match(state, *c);
3119 			for (size_t j = 0; j < nb; j++) {
3120 				filter_site_t *value;
3121 				ACM_get_match(state, j, 0, (void **)&value);
3122 				// ACM matches any substring, make sure the match is exact
3123 				if (equal(value->site, s))
3124 					return value;
3125 			}
3126 		}
3127 	}
3128 	return NULL;
3129 }
3130 
3131 static filter_site_t *
filter_site_find_exact(kbtree_t (site)* btree,ACMachine (char)* acm,filter_site_t * all,char * s,unsigned int exact_site,unsigned int all_sites)3132 filter_site_find_exact(kbtree_t(site) *btree, ACMachine(char) *acm, filter_site_t *all, char *s, unsigned int exact_site, unsigned int all_sites)
3133 {
3134 	if (all_sites)
3135 		return all;
3136 	else if (exact_site)
3137 		return filter_site_exact_match(btree, s);
3138 	else
3139 		return filter_site_substring_exact_match(acm, s);
3140 }
3141 
3142 static int NONNULL(3) WUNRES
filter_site_add(kbtree_t (site)** btree,ACMachine (char)** acm,filter_site_t ** all,filter_rule_t * rule,char * s,unsigned int exact_site,unsigned int all_sites,const char * argv0,tmp_opts_t * tmp_opts)3143 filter_site_add(kbtree_t(site) **btree, ACMachine(char) **acm, filter_site_t **all, filter_rule_t *rule, char *s, unsigned int exact_site, unsigned int all_sites, const char *argv0, tmp_opts_t *tmp_opts)
3144 {
3145 	filter_site_t *site = filter_site_find_exact(*btree, *acm, *all, s, exact_site, all_sites);
3146 	if (!site) {
3147 		site = malloc(sizeof(filter_site_t));
3148 		if (!site)
3149 			return oom_return_na();
3150 		memset(site, 0, sizeof(filter_site_t));
3151 
3152 		site->site = strdup(s);
3153 		if (!site->site)
3154 			return oom_return_na();
3155 
3156 		if (all_sites) {
3157 			*all = site;
3158 		}
3159 		else if (exact_site) {
3160 			if (!*btree)
3161 				if (!(*btree = kb_init(site, KB_DEFAULT_SIZE)))
3162 					return oom_return_na();
3163 
3164 			kb_put(site, *btree, site);
3165 		}
3166 		else {
3167 			if (!*acm)
3168 				if (!(*acm = ACM_create(char)))
3169 					return oom_return_na();
3170 
3171 			Keyword(char) k;
3172 			ACM_KEYWORD_SET(k, site->site, strlen(site->site));
3173 			ACM_register_keyword(*acm, k, site, free_site_func);
3174 		}
3175 	}
3176 
3177 	site->all_sites = all_sites;
3178 	site->exact = exact_site;
3179 
3180 	// Do not override the specs of a site with a port rule
3181 	// Port rule is added as a new port under the same site
3182 	// hence 'if else', not just 'if'
3183 	if (rule->port) {
3184 		if (filter_port_add(site, rule, argv0, tmp_opts) == -1)
3185 			return -1;
3186 	}
3187 	// Do not override the specs of site rules at higher precedence
3188 	// precedence can only go up not down
3189 	else if (rule->action.precedence >= site->action.precedence) {
3190 		// Multiple rules can set an action for the same site, hence the bit-wise OR
3191 		site->action.divert |= rule->action.divert;
3192 		site->action.split |= rule->action.split;
3193 		site->action.pass |= rule->action.pass;
3194 		site->action.block |= rule->action.block;
3195 		site->action.match |= rule->action.match;
3196 
3197 		// Multiple log actions can be set for the same site
3198 		// Multiple rules can enable/disable or don't change a log action for the same site
3199 		// 0: don't change, 1: disable, 2: enable
3200 		if (rule->action.log_connect)
3201 			site->action.log_connect = rule->action.log_connect;
3202 		if (rule->action.log_master)
3203 			site->action.log_master = rule->action.log_master;
3204 		if (rule->action.log_cert)
3205 			site->action.log_cert = rule->action.log_cert;
3206 		if (rule->action.log_content)
3207 			site->action.log_content = rule->action.log_content;
3208 		if (rule->action.log_pcap)
3209 			site->action.log_pcap = rule->action.log_pcap;
3210 #ifndef WITHOUT_MIRROR
3211 		if (rule->action.log_mirror)
3212 			site->action.log_mirror = rule->action.log_mirror;
3213 #endif /* !WITHOUT_MIRROR */
3214 
3215 		if (rule->action.conn_opts) {
3216 			if (site->action.conn_opts)
3217 				conn_opts_free(site->action.conn_opts);
3218 			site->action.conn_opts = conn_opts_copy(rule->action.conn_opts, argv0, tmp_opts);
3219 			if (!site->action.conn_opts)
3220 				return oom_return_na();
3221 		}
3222 
3223 		site->action.precedence = rule->action.precedence;
3224 #ifdef DEBUG_PROXY
3225 		site->action.line_num = rule->action.line_num;
3226 #endif /* DEBUG_PROXY */
3227 	}
3228 	return 0;
3229 }
3230 
3231 static int
filter_sitelist_add(filter_list_t * list,filter_rule_t * rule,const char * argv0,tmp_opts_t * tmp_opts)3232 filter_sitelist_add(filter_list_t *list, filter_rule_t *rule, const char *argv0, tmp_opts_t *tmp_opts)
3233 {
3234 	if (rule->dstip) {
3235 		if (filter_site_add(&list->ip_btree, &list->ip_acm, &list->ip_all, rule, rule->dstip, rule->exact_dstip, rule->all_dstips, argv0, tmp_opts) == -1)
3236 			return -1;
3237 	}
3238 	if (rule->sni) {
3239 		if (filter_site_add(&list->sni_btree, &list->sni_acm, &list->sni_all, rule, rule->sni, rule->exact_sni, rule->all_snis, argv0, tmp_opts) == -1)
3240 			return -1;
3241 	}
3242 	if (rule->cn) {
3243 		if (filter_site_add(&list->cn_btree, &list->cn_acm, &list->cn_all, rule, rule->cn, rule->exact_cn, rule->all_cns, argv0, tmp_opts) == -1)
3244 			return -1;
3245 	}
3246 	if (rule->host) {
3247 		if (filter_site_add(&list->host_btree, &list->host_acm, &list->host_all, rule, rule->host, rule->exact_host, rule->all_hosts, argv0, tmp_opts) == -1)
3248 			return -1;
3249 	}
3250 	if (rule->uri) {
3251 		if (filter_site_add(&list->uri_btree, &list->uri_acm, &list->uri_all, rule, rule->uri, rule->exact_uri, rule->all_uris, argv0, tmp_opts) == -1)
3252 			return -1;
3253 	}
3254 	return 0;
3255 }
3256 
3257 filter_ip_t *
filter_ip_exact_match(kbtree_t (ip)* btree,char * i)3258 filter_ip_exact_match(kbtree_t(ip) *btree, char *i)
3259 {
3260 	if (!btree)
3261 		return NULL;
3262 	filter_ip_t **ip = kb_get(ip, btree, i);
3263 	return ip ? *ip : NULL;
3264 }
3265 
3266 filter_ip_t *
filter_ip_substring_match(ACMachine (char)* acm,char * ip)3267 filter_ip_substring_match(ACMachine(char) *acm, char *ip)
3268 {
3269 	if (!acm)
3270 		return NULL;
3271 	filter_ip_t *i = NULL;
3272 	match_acm(acm, ip, i);
3273 	return i;
3274 }
3275 
3276 static filter_ip_t *
filter_ip_substring_exact_match(ACMachine (char)* acm,char * i)3277 filter_ip_substring_exact_match(ACMachine(char) *acm, char *i)
3278 {
3279 	if (acm) {
3280 		const ACState(char) *state = ACM_reset(acm);
3281 		for (char *c = i; *c; c++) {
3282 			size_t nb = ACM_match(state, *c);
3283 			for (size_t j = 0; j < nb; j++) {
3284 				filter_ip_t *value;
3285 				ACM_get_match(state, j, 0, (void **)&value);
3286 				// ACM matches any substring, make sure the match is exact
3287 				if (equal(value->ip, i))
3288 					return value;
3289 			}
3290 		}
3291 	}
3292 	return NULL;
3293 }
3294 
3295 static filter_ip_t *
filter_ip_find_exact(filter_t * filter,filter_rule_t * rule)3296 filter_ip_find_exact(filter_t *filter, filter_rule_t *rule)
3297 {
3298 	if (rule->exact_ip)
3299 		return filter_ip_exact_match(filter->ip_btree, rule->ip);
3300 	else
3301 		return filter_ip_substring_exact_match(filter->ip_acm, rule->ip);
3302 }
3303 
3304 static void
free_ip_func(void * i)3305 free_ip_func(void *i)
3306 {
3307 	free_ip((filter_ip_t **)&i);
3308 }
3309 
3310 static filter_ip_t *
filter_ip_get(filter_t * filter,filter_rule_t * rule)3311 filter_ip_get(filter_t *filter, filter_rule_t *rule)
3312 {
3313 	filter_ip_t *ip = filter_ip_find_exact(filter, rule);
3314 	if (!ip) {
3315 		ip = malloc(sizeof(filter_ip_t));
3316 		if (!ip)
3317 			return oom_return_na_null();
3318 		memset(ip, 0, sizeof(filter_ip_t));
3319 
3320 		ip->list = malloc(sizeof(filter_list_t));
3321 		if (!ip->list)
3322 			return oom_return_na_null();
3323 		memset(ip->list, 0, sizeof(filter_list_t));
3324 
3325 		ip->ip = strdup(rule->ip);
3326 		if (!ip->ip)
3327 			return oom_return_na_null();
3328 
3329 		ip->exact = rule->exact_ip;
3330 
3331 		if (rule->exact_ip) {
3332 			if (!filter->ip_btree)
3333 				if (!(filter->ip_btree = kb_init(ip, KB_DEFAULT_SIZE)))
3334 					return oom_return_na_null();
3335 
3336 			kb_put(ip, filter->ip_btree, ip);
3337 		}
3338 		else {
3339 			if (!filter->ip_acm)
3340 				if (!(filter->ip_acm = ACM_create(char)))
3341 					return oom_return_na_null();
3342 
3343 			Keyword(char) k;
3344 			ACM_KEYWORD_SET(k, ip->ip, strlen(ip->ip));
3345 			ACM_register_keyword(filter->ip_acm, k, ip, free_ip_func);
3346 		}
3347 	}
3348 	return ip;
3349 }
3350 
3351 #ifndef WITHOUT_USERAUTH
3352 filter_desc_t *
filter_desc_exact_match(kbtree_t (desc)* btree,char * k)3353 filter_desc_exact_match(kbtree_t(desc) *btree, char *k)
3354 {
3355 	if (!btree)
3356 		return NULL;
3357 	filter_desc_t **desc = kb_get(desc, btree, k);
3358 	return desc ? *desc : NULL;
3359 }
3360 
3361 filter_desc_t *
filter_desc_substring_match(ACMachine (char)* acm,char * desc)3362 filter_desc_substring_match(ACMachine(char) *acm, char *desc)
3363 {
3364 	if (!acm)
3365 		return NULL;
3366 	filter_desc_t *k = NULL;
3367 	match_acm(acm, desc, k);
3368 	return k;
3369 }
3370 
3371 static filter_desc_t *
filter_desc_substring_exact_match(ACMachine (char)* acm,char * k)3372 filter_desc_substring_exact_match(ACMachine(char) *acm, char *k)
3373 {
3374 	if (acm) {
3375 		const ACState(char) *state = ACM_reset(acm);
3376 		for (char *c = k; *c; c++) {
3377 			size_t nb = ACM_match(state, *c);
3378 			for (size_t j = 0; j < nb; j++) {
3379 				filter_desc_t *value;
3380 				ACM_get_match(state, j, 0, (void **)&value);
3381 				// ACM matches any substring, make sure the match is exact
3382 				if (equal(value->desc, k))
3383 					return value;
3384 			}
3385 		}
3386 	}
3387 	return NULL;
3388 }
3389 
3390 static filter_desc_t *
filter_desc_find_exact(filter_t * filter,filter_user_t * user,filter_rule_t * rule)3391 filter_desc_find_exact(filter_t *filter, filter_user_t *user, filter_rule_t *rule)
3392 {
3393 	if (rule->exact_desc)
3394 		return filter_desc_exact_match(user ? user->desc_btree : filter->desc_btree, rule->desc);
3395 	else
3396 		return filter_desc_substring_exact_match(user ? user->desc_acm : filter->desc_acm, rule->desc);
3397 }
3398 
3399 static void
free_desc_func(void * k)3400 free_desc_func(void *k)
3401 {
3402 	free_desc((filter_desc_t **)&k);
3403 }
3404 
3405 static filter_desc_t *
filter_desc_get(filter_t * filter,filter_user_t * user,filter_rule_t * rule)3406 filter_desc_get(filter_t *filter, filter_user_t *user, filter_rule_t *rule)
3407 {
3408 	filter_desc_t *desc = filter_desc_find_exact(filter, user, rule);
3409 	if (!desc) {
3410 		desc = malloc(sizeof(filter_desc_t));
3411 		if (!desc)
3412 			return oom_return_na_null();
3413 		memset(desc, 0, sizeof(filter_desc_t));
3414 
3415 		desc->list = malloc(sizeof(filter_list_t));
3416 		if (!desc->list)
3417 			return oom_return_na_null();
3418 		memset(desc->list, 0, sizeof(filter_list_t));
3419 
3420 		desc->desc = strdup(rule->desc);
3421 		if (!desc->desc)
3422 			return oom_return_na_null();
3423 
3424 		desc->exact = rule->exact_desc;
3425 
3426 		if (rule->exact_desc) {
3427 			kbtree_t(desc) **btree = user ? &user->desc_btree : &filter->desc_btree;
3428 			if (!*btree)
3429 				if (!(*btree = kb_init(desc, KB_DEFAULT_SIZE)))
3430 					return oom_return_na_null();
3431 
3432 			kb_put(desc, *btree, desc);
3433 		}
3434 		else {
3435 			ACMachine(char) **acm = user ? &user->desc_acm : &filter->desc_acm;
3436 			if (!*acm)
3437 				if (!(*acm = ACM_create(char)))
3438 					return oom_return_na_null();
3439 
3440 			Keyword(char) k;
3441 			ACM_KEYWORD_SET(k, desc->desc, strlen(desc->desc));
3442 			ACM_register_keyword(*acm, k, desc, free_desc_func);
3443 		}
3444 	}
3445 	return desc;
3446 }
3447 
3448 filter_user_t *
filter_user_exact_match(kbtree_t (user)* btree,char * u)3449 filter_user_exact_match(kbtree_t(user) *btree, char *u)
3450 {
3451 	if (!btree)
3452 		return NULL;
3453 	filter_user_t **user = kb_get(user, btree, u);
3454 	return user ? *user : NULL;
3455 }
3456 
3457 filter_user_t *
filter_user_substring_match(ACMachine (char)* acm,char * user)3458 filter_user_substring_match(ACMachine(char) *acm, char *user)
3459 {
3460 	if (!acm)
3461 		return NULL;
3462 	filter_user_t *u = NULL;
3463 	match_acm(acm, user, u);
3464 	return u;
3465 }
3466 
3467 static filter_user_t *
filter_user_substring_exact_match(ACMachine (char)* acm,char * u)3468 filter_user_substring_exact_match(ACMachine(char) *acm, char *u)
3469 {
3470 	if (acm) {
3471 		const ACState(char) *state = ACM_reset(acm);
3472 		for (char *c = u; *c; c++) {
3473 			size_t nb = ACM_match(state, *c);
3474 			for (size_t j = 0; j < nb; j++) {
3475 				filter_user_t *value;
3476 				ACM_get_match(state, j, 0, (void **)&value);
3477 				// ACM matches any substring, make sure the match is exact
3478 				if (equal(value->user, u))
3479 					return value;
3480 			}
3481 		}
3482 	}
3483 	return NULL;
3484 }
3485 
3486 static filter_user_t *
filter_user_find_exact(filter_t * filter,filter_rule_t * rule)3487 filter_user_find_exact(filter_t *filter, filter_rule_t *rule)
3488 {
3489 	if (rule->exact_user)
3490 		return filter_user_exact_match(filter->user_btree, rule->user);
3491 	else
3492 		return filter_user_substring_exact_match(filter->user_acm, rule->user);
3493 }
3494 
3495 static void
free_user_func(void * u)3496 free_user_func(void *u)
3497 {
3498 	free_user((filter_user_t **)&u);
3499 }
3500 
3501 static filter_user_t *
filter_user_get(filter_t * filter,filter_rule_t * rule)3502 filter_user_get(filter_t *filter, filter_rule_t *rule)
3503 {
3504 	filter_user_t *user = filter_user_find_exact(filter, rule);
3505 	if (!user) {
3506 		user = malloc(sizeof(filter_user_t));
3507 		if (!user)
3508 			return oom_return_na_null();
3509 		memset(user, 0, sizeof(filter_user_t));
3510 
3511 		user->list = malloc(sizeof(filter_list_t));
3512 		if (!user->list)
3513 			return oom_return_na_null();
3514 		memset(user->list, 0, sizeof(filter_list_t));
3515 
3516 		user->user = strdup(rule->user);
3517 		if (!user->user)
3518 			return oom_return_na_null();
3519 
3520 		user->exact = rule->exact_user;
3521 
3522 		if (rule->exact_user) {
3523 			if (!filter->user_btree)
3524 				if (!(filter->user_btree = kb_init(user, KB_DEFAULT_SIZE)))
3525 					return oom_return_na_null();
3526 
3527 			kb_put(user, filter->user_btree, user);
3528 		}
3529 		else {
3530 			if (!filter->user_acm)
3531 				if (!(filter->user_acm = ACM_create(char)))
3532 					return oom_return_na_null();
3533 
3534 			Keyword(char) k;
3535 			ACM_KEYWORD_SET(k, user->user, strlen(user->user));
3536 			ACM_register_keyword(filter->user_acm, k, user, free_user_func);
3537 		}
3538 	}
3539 	return user;
3540 }
3541 #endif /* WITHOUT_USERAUTH */
3542 
3543 /*
3544  * Translates filtering rules into data structures.
3545  * Never pass NULL as rule param.
3546  * Otherwise, we must return NULL, but NULL retval means oom.
3547  */
3548 filter_t *
filter_set(filter_rule_t * rule,const char * argv0,tmp_opts_t * tmp_opts)3549 filter_set(filter_rule_t *rule, const char *argv0, tmp_opts_t *tmp_opts)
3550 {
3551 	filter_t *filter = malloc(sizeof(filter_t));
3552 	if (!filter)
3553 		return oom_return_na_null();
3554 	memset(filter, 0, sizeof(filter_t));
3555 
3556 #ifndef WITHOUT_USERAUTH
3557 	filter->all_user = malloc(sizeof(filter_list_t));
3558 	if (!filter->all_user)
3559 		return oom_return_na_null();
3560 	memset(filter->all_user, 0, sizeof(filter_list_t));
3561 #endif /* WITHOUT_USERAUTH */
3562 
3563 	filter->all = malloc(sizeof(filter_list_t));
3564 	if (!filter->all)
3565 		return oom_return_na_null();
3566 	memset(filter->all, 0, sizeof(filter_list_t));
3567 
3568 	while (rule) {
3569 #ifndef WITHOUT_USERAUTH
3570 		if (rule->user) {
3571 			filter_user_t *user = filter_user_get(filter, rule);
3572 			if (!user)
3573 				return NULL;
3574 			if (rule->desc) {
3575 				filter_desc_t *desc = filter_desc_get(filter, user, rule);
3576 				if (!desc)
3577 					return NULL;
3578 				if (filter_sitelist_add(desc->list, rule, argv0, tmp_opts) == -1)
3579 					return NULL;
3580 			}
3581 			else {
3582 				if (filter_sitelist_add(user->list, rule, argv0, tmp_opts) == -1)
3583 					return NULL;
3584 			}
3585 		}
3586 		else if (rule->desc) {
3587 			filter_desc_t *desc = filter_desc_get(filter, NULL, rule);
3588 			if (!desc)
3589 				return NULL;
3590 			if (filter_sitelist_add(desc->list, rule, argv0, tmp_opts) == -1)
3591 				return NULL;
3592 		}
3593 		else if (rule->all_users) {
3594 			if (filter_sitelist_add(filter->all_user, rule, argv0, tmp_opts) == -1)
3595 				return NULL;
3596 		}
3597 		else
3598 #endif /* WITHOUT_USERAUTH */
3599 		if (rule->ip) {
3600 			 filter_ip_t *ip = filter_ip_get(filter, rule);
3601 			if (!ip)
3602 				return NULL;
3603 			if (filter_sitelist_add(ip->list, rule, argv0, tmp_opts) == -1)
3604 				return NULL;
3605 		}
3606 		else if (rule->all_conns) {
3607 			if (filter_sitelist_add(filter->all, rule, argv0, tmp_opts) == -1)
3608 				return NULL;
3609 		}
3610 		rule = rule->next;
3611 	}
3612 	return filter;
3613 }
3614 
3615 /* vim: set noet ft=c: */
3616