diff --git a/NEWS.md b/NEWS.md index 07752af..392c20e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,15 +5,17 @@ (Divert|Split|Pass|Block|Match) ([from ( - user (username|$macro|*) [desc keyword]| - ip (clientaddr|$macro|*)| + user (username|$macro|*) [desc (keyword|$macro|*)]| + desc (keyword|$macro|*)| + ip (clientip|$macro|*)| *)] [to ( sni (servername[*]|$macro|*)| cn (commonname[*]|$macro|*)| host (host[*]|$macro|*)| uri (uri[*]|$macro|*)| - ip (serveraddr|$macro|*)| + ip (serverip[*]|$macro|*) [port (serverport[*]|$macro|*)]| + port (serverport[*]|$macro|*)| *)] [log ([[!]connect] [[!]master] [[!]cert] [[!]content] [[!]pcap] [[!]mirror] [$macro]|*|!*)] diff --git a/README.md b/README.md index dd9f51a..bdfc7fa 100644 --- a/README.md +++ b/README.md @@ -295,15 +295,17 @@ The syntax of filtering rules is as follows: (Divert|Split|Pass|Block|Match) ([from ( - user (username|$macro|*) [desc keyword]| - ip (clientaddr|$macro|*)| + user (username|$macro|*) [desc (keyword|$macro|*)]| + desc (keyword|$macro|*)| + ip (clientip|$macro|*)| *)] [to ( sni (servername[*]|$macro|*)| cn (commonname[*]|$macro|*)| host (host[*]|$macro|*)| uri (uri[*]|$macro|*)| - ip (serveraddr|$macro|*)| + ip (serverip[*]|$macro|*) [port (serverport[*]|$macro|*)]| + port (serverport[*]|$macro|*)| *)] [log ([[!]connect] [[!]master] [[!]cert] [[!]content] [[!]pcap] [[!]mirror] [$macro]|*|!*)] @@ -318,7 +320,7 @@ user or description keyword, or `*` for all. - The `to` part defines destination filter based on server IP address, SNI or Common Names of SSL connections, Host or URI fields in HTTP Request headers, or `*` for all. - + Dst Host type of rules use `ip` site field + + Dst Host type of rules use `ip` and `port` site fields + SSL type of rules use `sni` and `cn` site fields + HTTP type of rules use `host` and `uri` site fields - The proxyspec handling the connection defines the protocol filter for the diff --git a/src/opts.c b/src/opts.c index 15cbb6d..4fcadac 100644 --- a/src/opts.c +++ b/src/opts.c @@ -217,6 +217,8 @@ opts_free_filter_rules(opts_t *opts) while (rule) { filter_rule_t *next = rule->next; free(rule->site); + if (rule->port) + free(rule->port); if (rule->ip) free(rule->ip); #ifndef WITHOUT_USERAUTH @@ -231,11 +233,23 @@ opts_free_filter_rules(opts_t *opts) opts->filter_rules = NULL; } +static void +opts_free_filter_port(filter_port_t *port) +{ + while (port) { + filter_port_t *p = port->next; + free(port->port); + free(port); + port = p; + } +} + static filter_site_t * opts_free_filter_site(filter_site_t *site) { filter_site_t *s = site->next; free(site->site); + opts_free_filter_port(site->port); free(site); return s; } @@ -879,64 +893,60 @@ clone_global_opts(global_t *global, const char *argv0, tmp_global_opts_t *tmp_gl filter_rule_t *rule = global->opts->filter_rules; while (rule) { - filter_rule_t *fr = malloc(sizeof(filter_rule_t)); - if (!fr) + filter_rule_t *r = malloc(sizeof(filter_rule_t)); + if (!r) return oom_return_null(argv0); - memset(fr, 0, sizeof(filter_rule_t)); + memset(r, 0, sizeof(filter_rule_t)); - if (rule->site) { - fr->site = strdup(rule->site); - if (!fr->site) + r->all_conns = rule->all_conns; + +#ifndef WITHOUT_USERAUTH + r->all_users = rule->all_users; + + if (rule->user) { + r->user = strdup(rule->user); + if (!r->user) return oom_return_null(argv0); } - fr->exact = rule->exact; - if (rule->ip) { - fr->ip = strdup(rule->ip); - if (!fr->ip) + if (rule->keyword) { + r->keyword = strdup(rule->keyword); + if (!r->keyword) return oom_return_null(argv0); } -#ifndef WITHOUT_USERAUTH - if (rule->user) { - fr->user = strdup(rule->user); - if (!fr->user) +#endif /* !WITHOUT_USERAUTH */ + + if (rule->ip) { + r->ip = strdup(rule->ip); + if (!r->ip) return oom_return_null(argv0); } - if (rule->keyword) { - fr->keyword = strdup(rule->keyword); - if (!fr->keyword) + + if (rule->site) { + r->site = strdup(rule->site); + if (!r->site) return oom_return_null(argv0); } + r->all_sites = rule->all_sites; + r->exact = rule->exact; - fr->all_users = rule->all_users; -#endif /* !WITHOUT_USERAUTH */ - fr->all_conns = rule->all_conns; - fr->all_sites = rule->all_sites; - - fr->divert = rule->divert; - fr->split = rule->split; - fr->pass = rule->pass; - fr->block = rule->block; - fr->match = rule->match; - - fr->log_connect = rule->log_connect; - fr->log_master = rule->log_master; - fr->log_cert = rule->log_cert; - fr->log_content = rule->log_content; - fr->log_pcap = rule->log_pcap; -#ifndef WITHOUT_MIRROR - fr->log_mirror = rule->log_mirror; -#endif /* !WITHOUT_MIRROR */ + if (rule->port) { + r->port = strdup(rule->port); + if (!r->port) + return oom_return_null(argv0); + } + r->all_ports = rule->all_ports; + r->exact_port = rule->exact_port; - fr->dstip = rule->dstip; - fr->sni = rule->sni; - fr->cn = rule->cn; - fr->host = rule->host; - fr->uri = rule->uri; + r->dstip = rule->dstip; + r->sni = rule->sni; + r->cn = rule->cn; + r->host = rule->host; + r->uri = rule->uri; - fr->precedence = rule->precedence; + r->action = rule->action; - opts_append_to_filter_rules(&opts->filter_rules, fr); + opts_append_to_filter_rules(&opts->filter_rules, r); rule = rule->next; } @@ -1345,7 +1355,7 @@ filter_rule_str(filter_rule_t *rule) int count = 0; while (rule) { char *p; - if (asprintf(&p, "site=%s, %s, ip=%s" + if (asprintf(&p, "site=%s, %s, port=%s, %s, ip=%s" #ifndef WITHOUT_USERAUTH ", user=%s, keyword=%s" #endif /* !WITHOUT_USERAUTH */ @@ -1353,12 +1363,14 @@ filter_rule_str(filter_rule_t *rule) #ifndef WITHOUT_USERAUTH "|%s" #endif /* !WITHOUT_USERAUTH */ - "|%s, action=%s|%s|%s|%s|%s, log=%s|%s|%s|%s|%s" + "|%s|%s, action=%s|%s|%s|%s|%s, log=%s|%s|%s|%s|%s" #ifndef WITHOUT_MIRROR "|%s" #endif /* !WITHOUT_MIRROR */ ", apply to=%s|%s|%s|%s|%s, precedence=%d", - rule->site, rule->exact ? "exact" : "substring", STRORNONE(rule->ip), + rule->site, rule->exact ? "exact" : "substring", + STRORNONE(rule->port), rule->port ? (rule->exact_port ? "exact_port" : "substring_port") : "", + STRORNONE(rule->ip), #ifndef WITHOUT_USERAUTH STRORNONE(rule->user), STRORNONE(rule->keyword), #endif /* !WITHOUT_USERAUTH */ @@ -1366,16 +1378,16 @@ filter_rule_str(filter_rule_t *rule) #ifndef WITHOUT_USERAUTH rule->all_users ? "users" : "", #endif /* !WITHOUT_USERAUTH */ - rule->all_sites ? "sites" : "", - rule->divert ? "divert" : "", rule->split ? "split" : "", rule->pass ? "pass" : "", rule->block ? "block" : "", rule->match ? "match" : "", - rule->log_connect ? (rule->log_connect == 1 ? "!connect" : "connect") : "", rule->log_master ? (rule->log_master == 1 ? "!master" : "master") : "", - rule->log_cert ? (rule->log_cert == 1 ? "!cert" : "cert") : "", rule->log_content ? (rule->log_content == 1 ? "!content" : "content") : "", - rule->log_pcap ? (rule->log_pcap == 1 ? "!pcap" : "pcap") : "", + rule->all_sites ? "sites" : "", rule->all_ports ? "ports" : "", + rule->action.divert ? "divert" : "", rule->action.split ? "split" : "", rule->action.pass ? "pass" : "", rule->action.block ? "block" : "", rule->action.match ? "match" : "", + rule->action.log_connect ? (rule->action.log_connect == 1 ? "!connect" : "connect") : "", rule->action.log_master ? (rule->action.log_master == 1 ? "!master" : "master") : "", + rule->action.log_cert ? (rule->action.log_cert == 1 ? "!cert" : "cert") : "", rule->action.log_content ? (rule->action.log_content == 1 ? "!content" : "content") : "", + rule->action.log_pcap ? (rule->action.log_pcap == 1 ? "!pcap" : "pcap") : "", #ifndef WITHOUT_MIRROR - rule->log_mirror ? (rule->log_mirror == 1 ? "!mirror" : "mirror") : "", + rule->action.log_mirror ? (rule->action.log_mirror == 1 ? "!mirror" : "mirror") : "", #endif /* !WITHOUT_MIRROR */ rule->dstip ? "dstip" : "", rule->sni ? "sni" : "", rule->cn ? "cn" : "", rule->host ? "host" : "", rule->uri ? "uri" : "", - rule->precedence) < 0) { + rule->action.precedence) < 0) { goto err; } char *nfrs; @@ -1401,28 +1413,72 @@ out: return frs; } +static char * +filter_port_str(filter_port_t *port) +{ + char *s = NULL; + + int count = 0; + while (port) { + char *p; + if (asprintf(&p, "%s\n %d: %s (%s%s, action=%s|%s|%s|%s|%s, log=%s|%s|%s|%s|%s" +#ifndef WITHOUT_MIRROR + "|%s" +#endif /* !WITHOUT_MIRROR */ + ", precedence=%d)", STRORNONE(s), count, + port->port, port->all_ports ? "all_ports, " : "", port->exact ? "exact" : "substring", + port->action.divert ? "divert" : "", port->action.split ? "split" : "", port->action.pass ? "pass" : "", port->action.block ? "block" : "", port->action.match ? "match" : "", + port->action.log_connect ? (port->action.log_connect == 1 ? "!connect" : "connect") : "", port->action.log_master ? (port->action.log_master == 1 ? "!master" : "master") : "", + port->action.log_cert ? (port->action.log_cert == 1 ? "!cert" : "cert") : "", port->action.log_content ? (port->action.log_content == 1 ? "!content" : "content") : "", + port->action.log_pcap ? (port->action.log_pcap == 1 ? "!pcap" : "pcap") : "", +#ifndef WITHOUT_MIRROR + port->action.log_mirror ? (port->action.log_mirror == 1 ? "!mirror" : "mirror") : "", +#endif /* !WITHOUT_MIRROR */ + port->action.precedence) < 0) { + goto err; + } + if (s) + free(s); + s = p; + port = port->next; + count++; + } + goto out; +err: + if (s) { + free(s); + s = NULL; + } +out: + return s; +} + static char * filter_sites_str(filter_site_t *site) { char *s = NULL; + char *ports = NULL; int count = 0; while (site) { + ports = filter_port_str(site->port); + char *p; if (asprintf(&p, "%s\n %d: %s (%s%s, action=%s|%s|%s|%s|%s, log=%s|%s|%s|%s|%s" #ifndef WITHOUT_MIRROR "|%s" #endif /* !WITHOUT_MIRROR */ - ", precedence=%d)", STRORNONE(s), count, + ", precedence=%d)%s%s", STRORNONE(s), count, site->site, site->all_sites ? "all_sites, " : "", site->exact ? "exact" : "substring", - site->divert ? "divert" : "", site->split ? "split" : "", site->pass ? "pass" : "", site->block ? "block" : "", site->match ? "match" : "", - site->log_connect ? (site->log_connect == 1 ? "!connect" : "connect") : "", site->log_master ? (site->log_master == 1 ? "!master" : "master") : "", - site->log_cert ? (site->log_cert == 1 ? "!cert" : "cert") : "", site->log_content ? (site->log_content == 1 ? "!content" : "content") : "", - site->log_pcap ? (site->log_pcap == 1 ? "!pcap" : "pcap") : "", + site->action.divert ? "divert" : "", site->action.split ? "split" : "", site->action.pass ? "pass" : "", site->action.block ? "block" : "", site->action.match ? "match" : "", + site->action.log_connect ? (site->action.log_connect == 1 ? "!connect" : "connect") : "", site->action.log_master ? (site->action.log_master == 1 ? "!master" : "master") : "", + site->action.log_cert ? (site->action.log_cert == 1 ? "!cert" : "cert") : "", site->action.log_content ? (site->action.log_content == 1 ? "!content" : "content") : "", + site->action.log_pcap ? (site->action.log_pcap == 1 ? "!pcap" : "pcap") : "", #ifndef WITHOUT_MIRROR - site->log_mirror ? (site->log_mirror == 1 ? "!mirror" : "mirror") : "", + site->action.log_mirror ? (site->action.log_mirror == 1 ? "!mirror" : "mirror") : "", #endif /* !WITHOUT_MIRROR */ - site->precedence) < 0) { + site->action.precedence, + ports ? "\n port:" : "", STRORNONE(ports)) < 0) { goto err; } if (s) @@ -1543,6 +1599,10 @@ filter_users_str(filter_user_t *user) int count = 0; while (user) { + // Make sure the current user does not have any keyword + if (user->keyword) + goto skip; + list = filter_list_str(user->list); char *p = NULL; @@ -1559,8 +1619,9 @@ filter_users_str(filter_user_t *user) if (s) free(s); s = p; - user = user->next; count++; +skip: + user = user->next; } goto out; err: @@ -1616,6 +1677,10 @@ filter_userkeywords_str(filter_user_t *user) int count = 0; while (user) { + // Make sure the current user has a keyword + if (!user->keyword) + goto skip; + list = filter_keywords_str(user->keyword); char *p = NULL; @@ -1628,8 +1693,9 @@ filter_userkeywords_str(filter_user_t *user) if (s) free(s); s = p; - user = user->next; count++; +skip: + user = user->next; } goto out; err: @@ -2586,11 +2652,11 @@ opts_unset_validate_proto(opts_t *opts) opts->validate_proto = 0; } +#ifdef DEBUG_OPTS static void opts_dbg_print_filter_rule(filter_rule_t *rule) { -#ifdef DEBUG_OPTS - log_dbg_printf("Filter rule: %s, %s, %s" + log_dbg_printf("Filter rule: %s, %s, %s, %s, %s" #ifndef WITHOUT_USERAUTH ", %s, %s" #endif /* !WITHOUT_USERAUTH */ @@ -2598,12 +2664,14 @@ opts_dbg_print_filter_rule(filter_rule_t *rule) #ifndef WITHOUT_USERAUTH "%s|" #endif /* !WITHOUT_USERAUTH */ - "%s, action=%s|%s|%s|%s|%s, log=%s|%s|%s|%s|%s" + "%s|%s, action=%s|%s|%s|%s|%s, log=%s|%s|%s|%s|%s" #ifndef WITHOUT_MIRROR "|%s" #endif /* !WITHOUT_MIRROR */ ", apply to=%s|%s|%s|%s|%s, precedence=%d\n", - rule->site, rule->exact ? "exact" : "substring", STRORNONE(rule->ip), + rule->site, rule->exact ? "exact" : "substring", + STRORNONE(rule->port), rule->port ? (rule->exact_port ? "exact_port" : "substring_port") : "", + STRORNONE(rule->ip), #ifndef WITHOUT_USERAUTH STRORNONE(rule->user), STRORNONE(rule->keyword), #endif /* !WITHOUT_USERAUTH */ @@ -2611,18 +2679,18 @@ opts_dbg_print_filter_rule(filter_rule_t *rule) #ifndef WITHOUT_USERAUTH rule->all_users ? "users" : "", #endif /* !WITHOUT_USERAUTH */ - rule->all_sites ? "sites" : "", - rule->divert ? "divert" : "", rule->split ? "split" : "", rule->pass ? "pass" : "", rule->block ? "block" : "", rule->match ? "match" : "", - rule->log_connect ? (rule->log_connect == 1 ? "!connect" : "connect") : "", rule->log_master ? (rule->log_master == 1 ? "!master" : "master") : "", - rule->log_cert ? (rule->log_cert == 1 ? "!cert" : "cert") : "", rule->log_content ? (rule->log_content == 1 ? "!content" : "content") : "", - rule->log_pcap ? (rule->log_pcap == 1 ? "!pcap" : "pcap") : "", + rule->all_sites ? "sites" : "", rule->all_ports ? "ports" : "", + rule->action.divert ? "divert" : "", rule->action.split ? "split" : "", rule->action.pass ? "pass" : "", rule->action.block ? "block" : "", rule->action.match ? "match" : "", + rule->action.log_connect ? (rule->action.log_connect == 1 ? "!connect" : "connect") : "", rule->action.log_master ? (rule->action.log_master == 1 ? "!master" : "master") : "", + rule->action.log_cert ? (rule->action.log_cert == 1 ? "!cert" : "cert") : "", rule->action.log_content ? (rule->action.log_content == 1 ? "!content" : "content") : "", + rule->action.log_pcap ? (rule->action.log_pcap == 1 ? "!pcap" : "pcap") : "", #ifndef WITHOUT_MIRROR - rule->log_mirror ? (rule->log_mirror == 1 ? "!mirror" : "mirror") : "", + rule->action.log_mirror ? (rule->action.log_mirror == 1 ? "!mirror" : "mirror") : "", #endif /* !WITHOUT_MIRROR */ rule->dstip ? "dstip" : "", rule->sni ? "sni" : "", rule->cn ? "cn" : "", rule->host ? "host" : "", rule->uri ? "uri" : "", - rule->precedence); -#endif /* DEBUG_OPTS */ + rule->action.precedence); } +#endif /* DEBUG_OPTS */ #define MAX_SITE_LEN 200 @@ -2682,7 +2750,7 @@ opts_set_passsite(opts_t *opts, char *value, int line_num) return oom_return_na(); // precedence can only go up not down - rule->precedence = 0; + rule->action.precedence = 0; if (argc == 1) { // Apply filter rule to all conns @@ -2694,14 +2762,14 @@ opts_set_passsite(opts_t *opts, char *value, int line_num) if (!strcmp(argv[1], "*")) { #ifndef WITHOUT_USERAUTH // Apply filter rule to all users perhaps with keyword - rule->precedence++; + rule->action.precedence++; rule->all_users = 1; } else if (sys_isuser(argv[1])) { if (!opts->user_auth) { fprintf(stderr, "User filter requires user auth on line %d\n", line_num); return -1; } - rule->precedence += 2; + rule->action.precedence += 2; rule->user = strdup(argv[1]); if (!rule->user) return oom_return_na(); @@ -2710,7 +2778,7 @@ opts_set_passsite(opts_t *opts, char *value, int line_num) rule->all_conns = 1; #endif /* WITHOUT_USERAUTH */ } else { - rule->precedence++; + rule->action.precedence++; rule->ip = strdup(argv[1]); if (!rule->ip) return oom_return_na(); @@ -2735,21 +2803,23 @@ opts_set_passsite(opts_t *opts, char *value, int line_num) fprintf(stderr, "Keyword filter requires user auth on line %d\n", line_num); return -1; } - rule->precedence++; + rule->action.precedence++; rule->keyword = strdup(argv[2]); if (!rule->keyword) return oom_return_na(); #endif /* !WITHOUT_USERAUTH */ } - rule->precedence++; + rule->action.precedence++; rule->sni = 1; rule->cn = 1; - rule->pass = 1; + rule->action.pass = 1; opts_append_to_filter_rules(&opts->filter_rules, rule); +#ifdef DEBUG_OPTS opts_dbg_print_filter_rule(rule); +#endif /* DEBUG_OPTS */ return 0; } @@ -2873,6 +2943,45 @@ opts_set_site(filter_rule_t *rule, const char *site, int line_num) return 0; } +static int WUNRES +opts_set_port(filter_rule_t *rule, const char *port, int line_num) +{ +#define MAX_PORT_LEN 6 + + size_t len = strlen(port); + + if (len > MAX_PORT_LEN) { + fprintf(stderr, "Filter port too long %zu > %d on line %d\n", len, MAX_PORT_LEN, line_num); + return -1; + } + + rule->port = strdup(port); + if (!rule->port) + return oom_return_na(); + + if (rule->port[len - 1] == '*') { + rule->exact_port = 0; + len--; + rule->port[len] = '\0'; + // site == "*" ? + if (len == 0) + rule->all_ports = 1; + } else { + rule->exact_port = 1; + } + + // redundant? + if (equal(rule->port, "*")) + rule->all_ports = 1; + + if (!rule->site) { + rule->site = strdup(""); + if (!rule->site) + return oom_return_na(); + } + return 0; +} + static int WUNRES opts_inc_arg_index(int i, int argc, char *last, int line_num) { @@ -2889,15 +2998,17 @@ filter_rule_translate(opts_t *opts, const char *name, int argc, char **argv, int { //(Divert|Split|Pass|Block|Match) // ([from ( - // user (username|$macro|*) [desc keyword]| - // ip (clientaddr|$macro|*)| + // user (username|$macro|*) [desc (keyword|$macro|*)]| + // desc (keyword|$macro|*)| + // ip (clientip|$macro|*)| // *)] // [to ( // sni (servername[*]|$macro|*)| // cn (commonname[*]|$macro|*)| // host (host[*]|$macro|*)| // uri (uri[*]|$macro|*)| - // ip (serveraddr|$macro|*)| + // ip (serverip[*]|$macro|*) [port (serverport[*]|$macro|*)]| + // port (serverport[*]|$macro|*)| // *)] // [log ([[!]connect] [[!]master] [[!]cert] // [[!]content] [[!]pcap] [[!]mirror] [$macro]|*|!*)] @@ -2909,18 +3020,18 @@ filter_rule_translate(opts_t *opts, const char *name, int argc, char **argv, int memset(rule, 0, sizeof(filter_rule_t)); if (equal(name, "Divert")) - rule->divert = 1; + rule->action.divert = 1; else if (equal(name, "Split")) - rule->split = 1; + rule->action.split = 1; else if (equal(name, "Pass")) - rule->pass = 1; + rule->action.pass = 1; else if (equal(name, "Block")) - rule->block = 1; + rule->action.block = 1; else if (equal(name, "Match")) - rule->match = 1; + rule->action.match = 1; // precedence can only go up not down - rule->precedence = 0; + rule->action.precedence = 0; int done_from = 0; int done_to = 0; @@ -2938,12 +3049,12 @@ filter_rule_translate(opts_t *opts, const char *name, int argc, char **argv, int if ((i = opts_inc_arg_index(i, argc, argv[i], line_num)) == -1) return -1; - rule->precedence++; + rule->action.precedence++; if (equal(argv[i], "*")) { rule->all_users = 1; } else { - rule->precedence++; + rule->action.precedence++; rule->user = strdup(argv[i]); if (!rule->user) return oom_return_na(); @@ -2954,7 +3065,7 @@ filter_rule_translate(opts_t *opts, const char *name, int argc, char **argv, int if (i < argc && equal(argv[i], "desc")) { if ((i = opts_inc_arg_index(i, argc, argv[i], line_num)) == -1) return -1; - rule->precedence++; + rule->action.precedence++; rule->keyword = strdup(argv[i++]); if (!rule->keyword) return oom_return_na(); @@ -2971,7 +3082,7 @@ filter_rule_translate(opts_t *opts, const char *name, int argc, char **argv, int if (equal(argv[i], "*")) { rule->all_conns = 1; } else { - rule->precedence++; + rule->action.precedence++; rule->ip = strdup(argv[i]); if (!rule->ip) return oom_return_na(); @@ -2987,8 +3098,8 @@ filter_rule_translate(opts_t *opts, const char *name, int argc, char **argv, int if ((i = opts_inc_arg_index(i, argc, argv[i], line_num)) == -1) return -1; - if (equal(argv[i], "sni") || equal(argv[i], "cn") || equal(argv[i], "host") || equal(argv[i], "uri") || equal(argv[i], "ip")) { - rule->precedence++; + if (equal(argv[i], "sni") || equal(argv[i], "cn") || equal(argv[i], "host") || equal(argv[i], "uri")) { + rule->action.precedence++; if (equal(argv[i], "sni")) rule->sni = 1; else if (equal(argv[i], "cn")) @@ -2997,8 +3108,6 @@ filter_rule_translate(opts_t *opts, const char *name, int argc, char **argv, int rule->host = 1; else if (equal(argv[i], "uri")) rule->uri = 1; - else if (equal(argv[i], "ip")) - rule->dstip = 1; if ((i = opts_inc_arg_index(i, argc, argv[i], line_num)) == -1) return -1; @@ -3008,6 +3117,31 @@ filter_rule_translate(opts_t *opts, const char *name, int argc, char **argv, int done_to = 1; } + else if (equal(argv[i], "ip") || equal(argv[i], "port")) { + rule->dstip = 1; + + if (equal(argv[i], "ip")) { + if ((i = opts_inc_arg_index(i, argc, argv[i], line_num)) == -1) + return -1; + + // Just ip spec should not increase rule precedence + + if (opts_set_site(rule, argv[i++], line_num) == -1) + return -1; + } + + if (i < argc && equal(argv[i], "port")) { + if ((i = opts_inc_arg_index(i, argc, argv[i], line_num)) == -1) + return -1; + + rule->action.precedence++; + + if (opts_set_port(rule, argv[i++], line_num) == -1) + return -1; + } + + done_to = 1; + } else if (equal(argv[i], "*")) { i++; } @@ -3016,7 +3150,7 @@ filter_rule_translate(opts_t *opts, const char *name, int argc, char **argv, int if ((i = opts_inc_arg_index(i, argc, argv[i], line_num)) == -1) return -1; - rule->precedence++; + rule->action.precedence++; if (equal(argv[i], "connect") || equal(argv[i], "master") || equal(argv[i], "cert") || equal(argv[i], "content") || equal(argv[i], "pcap") || equal(argv[i], "!connect") || equal(argv[i], "!master") || equal(argv[i], "!cert") || equal(argv[i], "!content") || equal(argv[i], "!pcap") @@ -3026,30 +3160,30 @@ filter_rule_translate(opts_t *opts, const char *name, int argc, char **argv, int ) { do { if (equal(argv[i], "connect")) - rule->log_connect = 2; + rule->action.log_connect = 2; else if (equal(argv[i], "master")) - rule->log_master = 2; + rule->action.log_master = 2; else if (equal(argv[i], "cert")) - rule->log_cert = 2; + rule->action.log_cert = 2; else if (equal(argv[i], "content")) - rule->log_content = 2; + rule->action.log_content = 2; else if (equal(argv[i], "pcap")) - rule->log_pcap = 2; + rule->action.log_pcap = 2; else if (equal(argv[i], "!connect")) - rule->log_connect = 1; + rule->action.log_connect = 1; else if (equal(argv[i], "!master")) - rule->log_master = 1; + rule->action.log_master = 1; else if (equal(argv[i], "!cert")) - rule->log_cert = 1; + rule->action.log_cert = 1; else if (equal(argv[i], "!content")) - rule->log_content = 1; + rule->action.log_content = 1; else if (equal(argv[i], "!pcap")) - rule->log_pcap = 1; + rule->action.log_pcap = 1; #ifndef WITHOUT_MIRROR else if (equal(argv[i], "mirror")) - rule->log_mirror = 2; + rule->action.log_mirror = 2; else if (equal(argv[i], "!mirror")) - rule->log_mirror = 1; + rule->action.log_mirror = 1; #endif /* !WITHOUT_MIRROR */ if (++i == argc) @@ -3062,24 +3196,24 @@ filter_rule_translate(opts_t *opts, const char *name, int argc, char **argv, int ); } else if (equal(argv[i], "*")) { - rule->log_connect = 2; - rule->log_master = 2; - rule->log_cert = 2; - rule->log_content = 2; - rule->log_pcap = 2; + rule->action.log_connect = 2; + rule->action.log_master = 2; + rule->action.log_cert = 2; + rule->action.log_content = 2; + rule->action.log_pcap = 2; #ifndef WITHOUT_MIRROR - rule->log_mirror = 2; + rule->action.log_mirror = 2; #endif /* !WITHOUT_MIRROR */ i++; } else if (equal(argv[i], "!*")) { - rule->log_connect = 1; - rule->log_master = 1; - rule->log_cert = 1; - rule->log_content = 1; - rule->log_pcap = 1; + rule->action.log_connect = 1; + rule->action.log_master = 1; + rule->action.log_cert = 1; + rule->action.log_content = 1; + rule->action.log_pcap = 1; #ifndef WITHOUT_MIRROR - rule->log_mirror = 1; + rule->action.log_mirror = 1; #endif /* !WITHOUT_MIRROR */ i++; } @@ -3103,7 +3237,9 @@ filter_rule_translate(opts_t *opts, const char *name, int argc, char **argv, int opts_append_to_filter_rules(&opts->filter_rules, rule); +#ifdef DEBUG_OPTS opts_dbg_print_filter_rule(rule); +#endif /* DEBUG_OPTS */ return 0; } @@ -3120,7 +3256,7 @@ filter_rule_expand_macro(opts_t *opts, const char *name, int argc, char **argv, if ((macro = opts_find_macro(opts->macro, argv[i]))) { value_t *value = macro->value; while (value) { - // Prevent infinite macro expansion + // Prevent infinite macro expansion, macros do not allow it, but macro expansion should detect it too if (value->value[0] == '$') { fprintf(stderr, "Invalid macro value '%s' on line %d\n", value->value, line_num); return -1; @@ -3250,7 +3386,7 @@ filter_rule_parse(opts_t *opts, const char *name, int argc, char **argv, int lin if ((i = opts_inc_arg_index(i, argc, argv[i], line_num)) == -1) return -1; - if (equal(argv[i], "sni") || equal(argv[i], "cn") || equal(argv[i], "host") || equal(argv[i], "uri") || equal(argv[i], "ip")) { + if (equal(argv[i], "sni") || equal(argv[i], "cn") || equal(argv[i], "host") || equal(argv[i], "uri")) { if ((i = opts_inc_arg_index(i, argc, argv[i], line_num)) == -1) return -1; @@ -3261,6 +3397,30 @@ filter_rule_parse(opts_t *opts, const char *name, int argc, char **argv, int lin done_to = 1; } + else if (equal(argv[i], "ip") || equal(argv[i], "port")) { + if (equal(argv[i], "ip")) { + if ((i = opts_inc_arg_index(i, argc, argv[i], line_num)) == -1) + return -1; + + if ((rv = filter_rule_expand_macro(opts, name, argc, argv, i, line_num)) != 0) { + return rv; + } + i++; + } + + // It is possible to define port without ip (i.e. * or all_sites), hence no 'else' here + if (i < argc && equal(argv[i], "port")) { + if ((i = opts_inc_arg_index(i, argc, argv[i], line_num)) == -1) + return -1; + + if ((rv = filter_rule_expand_macro(opts, name, argc, argv, i, line_num)) != 0) { + return rv; + } + i++; + } + + done_to = 1; + } else if (equal(argv[i], "*")) { i++; } @@ -3343,6 +3503,88 @@ opts_set_filter_rule(opts_t *opts, const char *name, char *value, int line_num) return filter_rule_parse(opts, name, argc, argv, line_num); } +static filter_port_t * +opts_find_port(filter_port_t *port, filter_rule_t *rule) +{ + while (port) { + if ((port->exact == rule->exact_port) && !strcmp(port->port, rule->port)) + break; + port = port->next; + } + return port; +} + +static filter_port_t * +opts_add_port(filter_port_t *port, filter_rule_t *rule) +{ + int prepend = 1; + if (port && port->all_ports) { + // all_ports should be at the beginning of the port list for performance reasons + // it effectively disables the rest of the list, but we keep the rest for reporting + prepend = 0; + } + + filter_port_t *p = opts_find_port(port, rule); + if (!p) { + p = malloc(sizeof(filter_port_t)); + if (!p) + return oom_return_na_null(); + memset(p, 0, sizeof(filter_port_t)); + p->port = strdup(rule->port); + if (!p->port) + return oom_return_na_null(); + + if (prepend) { + p->next = port; + } else { + // Insert the new port after the head + // If prepend is 0, port is never NULL + p->next = port->next; + port->next = p; + } + } else { + // If the port exists, we should return the head of the port list + // i.e. we have not prepended anything + prepend = 0; + } + + // Do not override the specs of port rules at higher precedence + // precedence can only go up not down + if (rule->action.precedence >= p->action.precedence) { + p->all_ports = rule->all_ports; + p->exact = rule->exact_port; + + // Multiple rules can set an action for the same port, hence the bit-wise OR + p->action.divert |= rule->action.divert; + p->action.split |= rule->action.split; + p->action.pass |= rule->action.pass; + p->action.block |= rule->action.block; + p->action.match |= rule->action.match; + + // Multiple log actions can be set for the same port + // Multiple rules can enable/disable or don't change a log action for the same port + // 0: don't change, 1: disable, 2: enable + if (rule->action.log_connect) + p->action.log_connect = rule->action.log_connect; + if (rule->action.log_master) + p->action.log_master = rule->action.log_master; + if (rule->action.log_cert) + p->action.log_cert = rule->action.log_cert; + if (rule->action.log_content) + p->action.log_content = rule->action.log_content; + if (rule->action.log_pcap) + p->action.log_pcap = rule->action.log_pcap; +#ifndef WITHOUT_MIRROR + if (rule->action.log_mirror) + p->action.log_mirror = rule->action.log_mirror; +#endif /* !WITHOUT_MIRROR */ + + p->action.precedence = rule->action.precedence; + } + + return prepend ? p : port; +} + static filter_site_t * opts_find_site(filter_site_t *site, filter_rule_t *rule) { @@ -3388,38 +3630,44 @@ opts_add_site(filter_site_t *site, filter_rule_t *rule) prepend = 0; } + s->all_sites = rule->all_sites; + s->exact = rule->exact; + + // Do not override the specs of a site with a port rule + // Port rule is added as a new port under the same site + // hence 'if else', not just 'if' + if (rule->port) { + s->port = opts_add_port(s->port, rule); + } // Do not override the specs of site rules at higher precedence // precedence can only go up not down - if (rule->precedence >= s->precedence) { - s->all_sites = rule->all_sites; - s->exact = rule->exact; - + else if (rule->action.precedence >= s->action.precedence) { // Multiple rules can set an action for the same site, hence the bit-wise OR - s->divert |= rule->divert; - s->split |= rule->split; - s->pass |= rule->pass; - s->block |= rule->block; - s->match |= rule->match; + s->action.divert |= rule->action.divert; + s->action.split |= rule->action.split; + s->action.pass |= rule->action.pass; + s->action.block |= rule->action.block; + s->action.match |= rule->action.match; // Multiple log actions can be set for the same site // Multiple rules can enable/disable or don't change a log action for the same site // 0: don't change, 1: disable, 2: enable - if (rule->log_connect) - s->log_connect = rule->log_connect; - if (rule->log_master) - s->log_master = rule->log_master; - if (rule->log_cert) - s->log_cert = rule->log_cert; - if (rule->log_content) - s->log_content = rule->log_content; - if (rule->log_pcap) - s->log_pcap = rule->log_pcap; + if (rule->action.log_connect) + s->action.log_connect = rule->action.log_connect; + if (rule->action.log_master) + s->action.log_master = rule->action.log_master; + if (rule->action.log_cert) + s->action.log_cert = rule->action.log_cert; + if (rule->action.log_content) + s->action.log_content = rule->action.log_content; + if (rule->action.log_pcap) + s->action.log_pcap = rule->action.log_pcap; #ifndef WITHOUT_MIRROR - if (rule->log_mirror) - s->log_mirror = rule->log_mirror; + if (rule->action.log_mirror) + s->action.log_mirror = rule->action.log_mirror; #endif /* !WITHOUT_MIRROR */ - s->precedence = rule->precedence; + s->action.precedence = rule->action.precedence; } return prepend ? s : site; diff --git a/src/opts.h b/src/opts.h index 95d6315..3f54817 100644 --- a/src/opts.h +++ b/src/opts.h @@ -188,16 +188,7 @@ typedef struct macro { struct macro *next; } macro_t; -typedef struct filter_rule { - char *site; - unsigned int all_sites : 1; /* 1 to match all sites == '*' */ - unsigned int exact : 1; /* 1 for exact, 0 for substring match */ - - unsigned int all_conns : 1; /* 1 to apply to all src ips and users */ -#ifndef WITHOUT_USERAUTH - unsigned int all_users : 1; /* 1 to apply to all users */ -#endif /* !WITHOUT_USERAUTH */ - +typedef struct filter_action { // Filter action unsigned int divert : 1; unsigned int split : 1; @@ -216,44 +207,66 @@ typedef struct filter_rule { unsigned int log_mirror : 2; #endif /* !WITHOUT_MIRROR */ - // Conn field to apply filter to - unsigned int dstip : 1; /* 1 to apply to dst ip */ - unsigned int host : 1; /* 1 to apply to http host */ - unsigned int uri : 1; /* 1 to apply to http uri */ - unsigned int sni : 1; /* 1 to apply to sni */ - unsigned int cn : 1; /* 1 to apply to common names */ + // Precedence is used in rule application + // More specific rules have higher precedence + unsigned int precedence; +} filter_action_t; + +typedef struct filter_rule { + // from: source filter + unsigned int all_conns : 1; /* 1 to apply to all src ips and users */ + #ifndef WITHOUT_USERAUTH + unsigned int all_users : 1; /* 1 to apply to all users */ + char *user; char *keyword; #endif /* !WITHOUT_USERAUTH */ char *ip; - // Precedence is used in rule application - // More specific rules have higher precedence - unsigned int precedence; + + // to: target filter + char *site; + unsigned int all_sites : 1; /* 1 to match all sites == '*' */ + unsigned int exact : 1; /* 1 for exact, 0 for substring match */ + + // Used with dstip filters only, i.e. if the site is an ip address + // This is not for the src ip in the 'from' part of rules + char *port; + unsigned int all_ports : 1; /* 1 to match all ports == '*' */ + unsigned int exact_port : 1; /* 1 for exact, 0 for substring match */ + + // Conn field to apply filter to + unsigned int dstip : 1; /* 1 to apply to dst ip */ + unsigned int host : 1; /* 1 to apply to http host */ + unsigned int uri : 1; /* 1 to apply to http uri */ + unsigned int sni : 1; /* 1 to apply to sni */ + unsigned int cn : 1; /* 1 to apply to common names */ + + struct filter_action action; + struct filter_rule *next; } filter_rule_t; +typedef struct filter_port { + char *port; + unsigned int all_ports : 1; + unsigned int exact : 1; + + struct filter_action action; + + struct filter_port *next; +} filter_port_t; + typedef struct filter_site { char *site; unsigned int all_sites : 1; unsigned int exact : 1; - unsigned int divert : 1; - unsigned int split : 1; - unsigned int pass : 1; - unsigned int block : 1; - unsigned int match : 1; + // Used with dstip filters only, i.e. if the site is an ip address + struct filter_port *port; - unsigned int log_connect : 2; - unsigned int log_master : 2; - unsigned int log_cert : 2; - unsigned int log_content : 2; - unsigned int log_pcap : 2; -#ifndef WITHOUT_MIRROR - unsigned int log_mirror : 2; -#endif /* !WITHOUT_MIRROR */ + struct filter_action action; - unsigned int precedence; struct filter_site *next; } filter_site_t; diff --git a/src/protohttp.c b/src/protohttp.c index adb6685..733a878 100644 --- a/src/protohttp.c +++ b/src/protohttp.c @@ -387,12 +387,12 @@ protohttp_filter_request_header_line(const char *line, protohttp_ctx_t *http_ctx } static int NONNULL(1,2) -protohttp_match_host(pxy_conn_ctx_t *ctx, filter_site_t *site) +protohttp_filter_match_host(pxy_conn_ctx_t *ctx, filter_site_t *site) { protohttp_ctx_t *http_ctx = ctx->protoctx->arg; - if (site->precedence < ctx->filter_precedence) { - log_finest_va("Rule precedence lower than conn filter precedence %d < %d: %s, %s", site->precedence, ctx->filter_precedence, site->site, http_ctx->http_host); + if (site->action.precedence < ctx->filter_precedence) { + log_finest_va("Rule precedence lower than conn filter precedence %d < %d: %s, %s", site->action.precedence, ctx->filter_precedence, site->site, http_ctx->http_host); return 0; } @@ -415,12 +415,12 @@ protohttp_match_host(pxy_conn_ctx_t *ctx, filter_site_t *site) } static int NONNULL(1,2) -protohttp_match_uri(pxy_conn_ctx_t *ctx, filter_site_t *site) +protohttp_filter_match_uri(pxy_conn_ctx_t *ctx, filter_site_t *site) { protohttp_ctx_t *http_ctx = ctx->protoctx->arg; - if (site->precedence < ctx->filter_precedence) { - log_finest_va("Rule precedence lower than conn filter precedence %d < %d: %s, %s", site->precedence, ctx->filter_precedence, site->site, http_ctx->http_uri); + if (site->action.precedence < ctx->filter_precedence) { + log_finest_va("Rule precedence lower than conn filter precedence %d < %d: %s, %s", site->action.precedence, ctx->filter_precedence, site->site, http_ctx->http_uri); return 0; } @@ -450,7 +450,7 @@ protohttp_filter(pxy_conn_ctx_t *ctx, filter_list_t *list) if (http_ctx->http_host) { filter_site_t *site = list->host; while (site) { - if (protohttp_match_host(ctx, site)) { + if (protohttp_filter_match_host(ctx, site)) { // Do not print the surrounding slashes log_err_level_printf(LOG_INFO, "Found site: %s for %s:%s, %s:%s" #ifndef WITHOUT_USERAUTH @@ -462,7 +462,7 @@ protohttp_filter(pxy_conn_ctx_t *ctx, filter_list_t *list) STRORDASH(ctx->user), STRORDASH(ctx->desc), #endif /* !WITHOUT_USERAUTH */ STRORDASH(http_ctx->http_host)); - return pxyconn_set_filter_action(ctx, site); + return pxyconn_set_filter_action(ctx, site->action, site->site); } site = site->next; } @@ -480,7 +480,7 @@ protohttp_filter(pxy_conn_ctx_t *ctx, filter_list_t *list) if (http_ctx->http_uri) { filter_site_t *site = list->uri; while (site) { - if (protohttp_match_uri(ctx, site)) { + if (protohttp_filter_match_uri(ctx, site)) { // Do not print the surrounding slashes log_err_level_printf(LOG_INFO, "Found site: %s for %s:%s, %s:%s" #ifndef WITHOUT_USERAUTH @@ -492,7 +492,7 @@ protohttp_filter(pxy_conn_ctx_t *ctx, filter_list_t *list) STRORDASH(ctx->user), STRORDASH(ctx->desc), #endif /* !WITHOUT_USERAUTH */ STRORDASH(http_ctx->http_uri)); - return pxyconn_set_filter_action(ctx, site); + return pxyconn_set_filter_action(ctx, site->action, site->site); } site = site->next; } diff --git a/src/protossl.c b/src/protossl.c index 7d7ac62..8f13431 100644 --- a/src/protossl.c +++ b/src/protossl.c @@ -591,10 +591,10 @@ protossl_srccert_create(pxy_conn_ctx_t *ctx) } static int NONNULL(1,2) -protossl_match_sni(pxy_conn_ctx_t *ctx, filter_site_t *site) +protossl_filter_match_sni(pxy_conn_ctx_t *ctx, filter_site_t *site) { - if (site->precedence < ctx->filter_precedence) { - log_finest_va("Rule precedence lower than conn filter precedence %d < %d: %s, %s", site->precedence, ctx->filter_precedence, site->site, ctx->sslctx->sni); + if (site->action.precedence < ctx->filter_precedence) { + log_finest_va("Rule precedence lower than conn filter precedence %d < %d: %s, %s", site->action.precedence, ctx->filter_precedence, site->site, ctx->sslctx->sni); return 0; } @@ -617,10 +617,10 @@ protossl_match_sni(pxy_conn_ctx_t *ctx, filter_site_t *site) } static int NONNULL(1,2) -protossl_match_cn(pxy_conn_ctx_t *ctx, filter_site_t *site) +protossl_filter_match_cn(pxy_conn_ctx_t *ctx, filter_site_t *site) { - if (site->precedence < ctx->filter_precedence) { - log_finest_va("Rule precedence lower than conn filter precedence %d < %d: %s, %s", site->precedence, ctx->filter_precedence, site->site, ctx->sslctx->ssl_names); + if (site->action.precedence < ctx->filter_precedence) { + log_finest_va("Rule precedence lower than conn filter precedence %d < %d: %s, %s", site->action.precedence, ctx->filter_precedence, site->site, ctx->sslctx->ssl_names); return 0; } @@ -692,8 +692,7 @@ protossl_filter(pxy_conn_ctx_t *ctx, filter_list_t *list) if (ctx->sslctx->sni) { filter_site_t *site = list->sni; while (site) { - if (protossl_match_sni(ctx, site)) { - // Do not print the surrounding slashes + if (protossl_filter_match_sni(ctx, site)) { log_err_level_printf(LOG_INFO, "Found site: %s for %s:%s, %s:%s" #ifndef WITHOUT_USERAUTH ", %s, %s" @@ -704,7 +703,7 @@ protossl_filter(pxy_conn_ctx_t *ctx, filter_list_t *list) STRORDASH(ctx->user), STRORDASH(ctx->desc), #endif /* !WITHOUT_USERAUTH */ STRORDASH(ctx->sslctx->sni)); - return pxyconn_set_filter_action(ctx, site); + return pxyconn_set_filter_action(ctx, site->action, site->site); } site = site->next; } @@ -722,8 +721,7 @@ protossl_filter(pxy_conn_ctx_t *ctx, filter_list_t *list) if (ctx->sslctx->ssl_names) { filter_site_t *site = list->cn; while (site) { - if (protossl_match_cn(ctx, site)) { - // Do not print the surrounding slashes + if (protossl_filter_match_cn(ctx, site)) { log_err_level_printf(LOG_INFO, "Found site: %s for %s:%s, %s:%s" #ifndef WITHOUT_USERAUTH ", %s, %s" @@ -734,7 +732,7 @@ protossl_filter(pxy_conn_ctx_t *ctx, filter_list_t *list) STRORDASH(ctx->user), STRORDASH(ctx->desc), #endif /* !WITHOUT_USERAUTH */ STRORDASH(ctx->sslctx->ssl_names)); - return pxyconn_set_filter_action(ctx, site); + return pxyconn_set_filter_action(ctx, site->action, site->site); } site = site->next; } diff --git a/src/prototcp.c b/src/prototcp.c index 04b1745..e2372f1 100644 --- a/src/prototcp.c +++ b/src/prototcp.c @@ -509,30 +509,79 @@ prototcp_bev_eventcb_connected_dst(struct bufferevent *bev, pxy_conn_ctx_t *ctx) } static int NONNULL(1,2) -prototcp_filter_match(pxy_conn_ctx_t *ctx, filter_site_t *site) +prototcp_filter_match_port(pxy_conn_ctx_t *ctx, filter_port_t *port) { - if (site->precedence < ctx->filter_precedence) { - log_finest_va("Rule precedence lower than conn filter precedence %d < %d: %s, %s", site->precedence, ctx->filter_precedence, site->site, ctx->dsthost_str); + if (port->action.precedence < ctx->filter_precedence) { + log_finest_va("Rule port precedence lower than conn filter precedence %d < %d: %s, %s", port->action.precedence, ctx->filter_precedence, port->port, ctx->dsthost_str); return 0; } + if (port->all_ports) { + log_finest_va("Match all dst ports: %s, %s", port->port, ctx->dstport_str); + return 1; + } + else if (port->exact) { + if (!strcmp(ctx->dstport_str, port->port)) { + log_finest_va("Match exact with port: %s, %s", port->port, ctx->dstport_str); + return 1; + } + } + else { + if (strstr(ctx->dstport_str, port->port)) { + log_finest_va("Match substring in dst port: %s, %s", port->port, ctx->dstport_str); + return 1; + } + } + return 0; +} + +static filter_action_t * NONNULL(1,2) +prototcp_filter_match_ip(pxy_conn_ctx_t *ctx, filter_site_t *site) +{ + // Port spec determines the precedence of a site rule, unless the rule does not have any port + if (!site->port && (site->action.precedence < ctx->filter_precedence)) { + log_finest_va("Rule precedence lower than conn filter precedence %d < %d: %s, %s", site->action.precedence, ctx->filter_precedence, site->site, ctx->dsthost_str); + return NULL; + } + + filter_action_t *action = NULL; + if (site->all_sites) { log_finest_va("Match all dst: %s, %s", site->site, ctx->dsthost_str); - return 1; + action = &site->action; } else if (site->exact) { if (!strcmp(ctx->dsthost_str, site->site)) { log_finest_va("Match exact with dst: %s, %s", site->site, ctx->dsthost_str); - return 1; + action = &site->action; } } else { if (strstr(ctx->dsthost_str, site->site)) { log_finest_va("Match substring in dst: %s, %s", site->site, ctx->dsthost_str); - return 1; + action = &site->action; } } - return 0; + + if (action) { + log_err_level_printf(LOG_INFO, "Found site: %s for %s:%s, %s:%s\n", site->site, + STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str)); + + if (site->port) { + filter_port_t *port = site->port; + while (port) { + if (prototcp_filter_match_port(ctx, port)) { + log_err_level_printf(LOG_INFO, "Found port: %s for %s:%s, %s:%s\n", port->port, + STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str)); + return &port->action; + } + port = port->next; + } + log_finest_va("No filter match with port: %s:%s, %s:%s", + STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str)); + } + } + return action; } static unsigned int NONNULL(1,2) @@ -541,11 +590,9 @@ prototcp_dsthost_filter(pxy_conn_ctx_t *ctx, filter_list_t *list) if (ctx->dsthost_str) { filter_site_t *site = list->ip; while (site) { - if (prototcp_filter_match(ctx, site)) { - log_err_level_printf(LOG_INFO, "Found site: %s for %s:%s, %s:%s\n", site->site, - STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str)); - return pxyconn_set_filter_action(ctx, site); - } + filter_action_t *action = prototcp_filter_match_ip(ctx, site); + if (action) + return pxyconn_set_filter_action(ctx, *action, site->site); site = site->next; } log_finest_va("No filter match with ip: %s:%s, %s:%s", diff --git a/src/pxyconn.c b/src/pxyconn.c index 2b8d19e..a99e00e 100644 --- a/src/pxyconn.c +++ b/src/pxyconn.c @@ -2007,63 +2007,63 @@ pxyconn_apply_deferred_block_action(pxy_conn_ctx_t *ctx) } unsigned int -pxyconn_set_filter_action(pxy_conn_ctx_t *ctx, filter_site_t *site) +pxyconn_set_filter_action(pxy_conn_ctx_t *ctx, filter_action_t a, char *site) { unsigned int action = FILTER_ACTION_NONE; - if (site->divert) { - log_err_level_printf(LOG_INFO, "Site filter divert action for %s, precedence %d\n", site->site, site->precedence); + if (a.divert) { + log_err_level_printf(LOG_INFO, "Filter divert action for %s, precedence %d\n", site, a.precedence); action = FILTER_ACTION_DIVERT; } - else if (site->split) { - log_err_level_printf(LOG_INFO, "Site filter split action for %s, precedence %d\n", site->site, site->precedence); + else if (a.split) { + log_err_level_printf(LOG_INFO, "Filter split action for %s, precedence %d\n", site, a.precedence); action = FILTER_ACTION_SPLIT; } - else if (site->pass) { + else if (a.pass) { // Ignore pass action if already in passthrough mode if (!ctx->pass) { - log_err_level_printf(LOG_INFO, "Site filter pass action for %s, precedence %d\n", site->site, site->precedence); + log_err_level_printf(LOG_INFO, "Filter pass action for %s, precedence %d\n", site, a.precedence); action = FILTER_ACTION_PASS; } } - else if (site->block) { - log_err_level_printf(LOG_INFO, "Site filter block action for %s, precedence %d\n", site->site, site->precedence); + else if (a.block) { + log_err_level_printf(LOG_INFO, "Filter block action for %s, precedence %d\n", site, a.precedence); action = FILTER_ACTION_BLOCK; } - else if (site->match) { - log_err_level_printf(LOG_INFO, "Site filter match action for %s, precedence %d\n", site->site, site->precedence); + else if (a.match) { + log_err_level_printf(LOG_INFO, "Filter match action for %s, precedence %d\n", site, a.precedence); action = FILTER_ACTION_MATCH; } // Multiple log actions can be defined, hence no 'else' // 0: don't change, 1: disable, 2: enable - if (site->log_connect) { - log_err_level_printf(LOG_INFO, "Site filter %s connect log for %s, precedence %d\n", site->log_connect % 2 ? "disable" : "enable", site->site, site->precedence); - action |= (site->log_connect % 2) ? FILTER_LOG_NOCONNECT : FILTER_LOG_CONNECT; + if (a.log_connect) { + log_err_level_printf(LOG_INFO, "Filter %s connect log for %s, precedence %d\n", a.log_connect % 2 ? "disable" : "enable", site, a.precedence); + action |= (a.log_connect % 2) ? FILTER_LOG_NOCONNECT : FILTER_LOG_CONNECT; } - if (site->log_master) { - log_err_level_printf(LOG_INFO, "Site filter %s master log for %s, precedence %d\n", site->log_master % 2 ? "disable" : "enable", site->site, site->precedence); - action |= (site->log_master % 2) ? FILTER_LOG_NOMASTER : FILTER_LOG_MASTER; + if (a.log_master) { + log_err_level_printf(LOG_INFO, "Filter %s master log for %s, precedence %d\n", a.log_master % 2 ? "disable" : "enable", site, a.precedence); + action |= (a.log_master % 2) ? FILTER_LOG_NOMASTER : FILTER_LOG_MASTER; } - if (site->log_cert) { - log_err_level_printf(LOG_INFO, "Site filter %s cert log for %s, precedence %d\n", site->log_cert % 2 ? "disable" : "enable", site->site, site->precedence); - action |= (site->log_cert % 2) ? FILTER_LOG_NOCERT : FILTER_LOG_CERT; + if (a.log_cert) { + log_err_level_printf(LOG_INFO, "Filter %s cert log for %s, precedence %d\n", a.log_cert % 2 ? "disable" : "enable", site, a.precedence); + action |= (a.log_cert % 2) ? FILTER_LOG_NOCERT : FILTER_LOG_CERT; } - if (site->log_content) { - log_err_level_printf(LOG_INFO, "Site filter %s content log for %s, precedence %d\n", site->log_content % 2 ? "disable" : "enable", site->site, site->precedence); - action |= (site->log_content % 2) ? FILTER_LOG_NOCONTENT : FILTER_LOG_CONTENT; + if (a.log_content) { + log_err_level_printf(LOG_INFO, "Filter %s content log for %s, precedence %d\n", a.log_content % 2 ? "disable" : "enable", site, a.precedence); + action |= (a.log_content % 2) ? FILTER_LOG_NOCONTENT : FILTER_LOG_CONTENT; } - if (site->log_pcap) { - log_err_level_printf(LOG_INFO, "Site filter %s pcap log for %s, precedence %d\n", site->log_pcap % 2 ? "disable" : "enable", site->site, site->precedence); - action |= (site->log_pcap % 2) ? FILTER_LOG_NOPCAP : FILTER_LOG_PCAP; + if (a.log_pcap) { + log_err_level_printf(LOG_INFO, "Filter %s pcap log for %s, precedence %d\n", a.log_pcap % 2 ? "disable" : "enable", site, a.precedence); + action |= (a.log_pcap % 2) ? FILTER_LOG_NOPCAP : FILTER_LOG_PCAP; } #ifndef WITHOUT_MIRROR - if (site->log_mirror) { - log_err_level_printf(LOG_INFO, "Site filter %s mirror log for %s, precedence %d\n", site->log_mirror % 2 ? "disable" : "enable", site->site, site->precedence); - action |= (site->log_mirror % 2) ? FILTER_LOG_NOMIRROR : FILTER_LOG_MIRROR; + if (a.log_mirror) { + log_err_level_printf(LOG_INFO, "Filter %s mirror log for %s, precedence %d\n", a.log_mirror % 2 ? "disable" : "enable", site, a.precedence); + action |= (a.log_mirror % 2) ? FILTER_LOG_NOMIRROR : FILTER_LOG_MIRROR; } #endif /* !WITHOUT_MIRROR */ - action |= site->precedence; + action |= a.precedence; return action; } diff --git a/src/pxyconn.h b/src/pxyconn.h index 0841a69..f26f8a2 100644 --- a/src/pxyconn.h +++ b/src/pxyconn.h @@ -444,9 +444,9 @@ int pxy_is_listuser(userlist_t *, const char * void pxy_classify_user(pxy_conn_ctx_t *) NONNULL(1); void pxy_userauth(pxy_conn_ctx_t *) NONNULL(1); #endif /* !WITHOUT_USERAUTH */ -int pxyconn_apply_deferred_block_action(pxy_conn_ctx_t *) NONNULL(1); -unsigned int pxyconn_set_filter_action(pxy_conn_ctx_t *, filter_site_t *) NONNULL(1,2); -unsigned int pxyconn_filter(pxy_conn_ctx_t *, proto_filter_func_t) NONNULL(1); +int pxyconn_apply_deferred_block_action(pxy_conn_ctx_t *) NONNULL(1) WUNRES; +unsigned int pxyconn_set_filter_action(pxy_conn_ctx_t *, filter_action_t, char *) NONNULL(1,3) WUNRES; +unsigned int pxyconn_filter(pxy_conn_ctx_t *, proto_filter_func_t) NONNULL(1) WUNRES; void pxy_conn_setup(evutil_socket_t, struct sockaddr *, int, pxy_thrmgr_ctx_t *, proxyspec_t *, global_t *, evutil_socket_t) diff --git a/src/sslproxy.1 b/src/sslproxy.1 index 617d9b2..3c15fc5 100644 --- a/src/sslproxy.1 +++ b/src/sslproxy.1 @@ -308,15 +308,17 @@ The syntax of filtering rules is as follows: (Divert|Split|Pass|Block|Match) ([from ( - user (username|$macro|*) [desc keyword]| - ip (clientaddr|$macro|*)| + user (username|$macro|*) [desc (keyword|$macro|*)]| + desc (keyword|$macro|*)| + ip (clientip|$macro|*)| *)] [to ( sni (servername[*]|$macro|*)| cn (commonname[*]|$macro|*)| host (host[*]|$macro|*)| uri (uri[*]|$macro|*)| - ip (serveraddr|$macro|*)| + ip (serverip[*]|$macro|*) [port (serverport[*]|$macro|*)]| + port (serverport[*]|$macro|*)| *)] [log ([[!]connect] [[!]master] [[!]cert] [[!]content] [[!]pcap] [[!]mirror] [$macro]|*|!*)] @@ -331,7 +333,7 @@ user or description keyword, or * for all. - The to part defines destination filter based on server IP address, SNI or Common Names of SSL connections, Host or URI fields in HTTP Request headers, or * for all. - + Dst Host type of rules use ip site field + + Dst Host type of rules use ip and port site fields + SSL type of rules use sni and cn site fields + HTTP type of rules use host and uri site fields .br diff --git a/src/sslproxy.conf b/src/sslproxy.conf index 98b1b99..d792963 100644 --- a/src/sslproxy.conf +++ b/src/sslproxy.conf @@ -291,15 +291,17 @@ PassUsers admin # #(Divert|Split|Pass|Block|Match) # ([from ( -# user (username|$macro|*) [desc keyword]| -# ip (clientaddr|$macro|*)| +# user (username|$macro|*) [desc (keyword|$macro|*)]| +# desc (keyword|$macro|*)| +# ip (clientip|$macro|*)| # *)] # [to ( # sni (servername[*]|$macro|*)| # cn (commonname[*]|$macro|*)| # host (host[*]|$macro|*)| # uri (uri[*]|$macro|*)| -# ip (serveraddr|$macro|*)| +# ip (serverip[*]|$macro|*) [port (serverport[*]|$macro|*)]| +# port (serverport[*]|$macro|*)| # *)] # [log ([[!]connect] [[!]master] [[!]cert] # [[!]content] [[!]pcap] [[!]mirror] [$macro]|*|!*)] diff --git a/src/sslproxy.conf.5 b/src/sslproxy.conf.5 index b6c5c9c..3d79336 100644 --- a/src/sslproxy.conf.5 +++ b/src/sslproxy.conf.5 @@ -353,15 +353,17 @@ The syntax of filtering rules is as follows: (Divert|Split|Pass|Block|Match) ([from ( - user (username|$macro|*) [desc keyword]| - ip (clientaddr|$macro|*)| + user (username|$macro|*) [desc (keyword|$macro|*)]| + desc (keyword|$macro|*)| + ip (clientip|$macro|*)| *)] [to ( sni (servername[*]|$macro|*)| cn (commonname[*]|$macro|*)| host (host[*]|$macro|*)| uri (uri[*]|$macro|*)| - ip (serveraddr|$macro|*)| + ip (serverip[*]|$macro|*) [port (serverport[*]|$macro|*)]| + port (serverport[*]|$macro|*)| *)] [log ([[!]connect] [[!]master] [[!]cert] [[!]content] [[!]pcap] [[!]mirror] [$macro]|*|!*)] diff --git a/tests/check/filter.t.c b/tests/check/filter.t.c index 32d641f..8e49ded 100644 --- a/tests/check/filter.t.c +++ b/tests/check/filter.t.c @@ -452,6 +452,27 @@ START_TEST(set_filter_rule_04) fail_unless(rv == 0, "failed to parse rule"); free(s); + s = strdup("to ip * port *"); + rv = opts_set_filter_rule(opts, "Divert", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip * port *"); + rv = opts_set_filter_rule(opts, "Split", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip * port *"); + rv = opts_set_filter_rule(opts, "Pass", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip * port *"); + rv = opts_set_filter_rule(opts, "Block", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip * port *"); + rv = opts_set_filter_rule(opts, "Match", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip 192.168.0.1"); rv = opts_set_filter_rule(opts, "Divert", s, 0); fail_unless(rv == 0, "failed to parse rule"); @@ -473,28 +494,96 @@ START_TEST(set_filter_rule_04) fail_unless(rv == 0, "failed to parse rule"); free(s); - s = strdup("$macro 192.168.0.1 192.168.0.2"); + s = strdup("to ip 192.168.0.1 port *"); + rv = opts_set_filter_rule(opts, "Divert", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip 192.168.0.1 port *"); + rv = opts_set_filter_rule(opts, "Split", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip 192.168.0.1 port *"); + rv = opts_set_filter_rule(opts, "Pass", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip 192.168.0.1 port *"); + rv = opts_set_filter_rule(opts, "Block", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip 192.168.0.1 port *"); + rv = opts_set_filter_rule(opts, "Match", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + + s = strdup("to ip * port 443"); + rv = opts_set_filter_rule(opts, "Divert", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip * port 443"); + rv = opts_set_filter_rule(opts, "Split", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip * port 443"); + rv = opts_set_filter_rule(opts, "Pass", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip * port 443"); + rv = opts_set_filter_rule(opts, "Block", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip * port 443"); + rv = opts_set_filter_rule(opts, "Match", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + + s = strdup("to ip 192.168.0.1 port 443"); + rv = opts_set_filter_rule(opts, "Divert", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip 192.168.0.1 port 443"); + rv = opts_set_filter_rule(opts, "Split", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip 192.168.0.1 port 443"); + rv = opts_set_filter_rule(opts, "Pass", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip 192.168.0.1 port 443"); + rv = opts_set_filter_rule(opts, "Block", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + s = strdup("to ip 192.168.0.1 port 443"); + rv = opts_set_filter_rule(opts, "Match", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + + s = strdup("$macro1 192.168.0.1 192.168.0.2"); + rv = opts_set_macro(opts, s, 0); + fail_unless(rv == 0, "failed to set macro"); + free(s); + + s = strdup("$macro2 443 444"); rv = opts_set_macro(opts, s, 0); fail_unless(rv == 0, "failed to set macro"); free(s); - s = strdup("to ip $macro"); + s = strdup("to ip $macro1 port $macro2"); rv = opts_set_filter_rule(opts, "Divert", s, 0); fail_unless(rv == 1, "failed to parse rule"); free(s); - s = strdup("to ip $macro"); + s = strdup("to ip $macro1 port $macro2"); rv = opts_set_filter_rule(opts, "Split", s, 0); fail_unless(rv == 1, "failed to parse rule"); free(s); - s = strdup("to ip $macro"); + s = strdup("to ip $macro1 port $macro2"); rv = opts_set_filter_rule(opts, "Pass", s, 0); fail_unless(rv == 1, "failed to parse rule"); free(s); - s = strdup("to ip $macro"); + s = strdup("to ip $macro1 port $macro2"); rv = opts_set_filter_rule(opts, "Block", s, 0); fail_unless(rv == 1, "failed to parse rule"); free(s); - s = strdup("to ip $macro"); + s = strdup("to ip $macro1 port $macro2"); rv = opts_set_filter_rule(opts, "Match", s, 0); fail_unless(rv == 1, "failed to parse rule"); free(s); @@ -1270,12 +1359,12 @@ START_TEST(set_filter_rule_07) s = filter_rule_str(opts->filter_rules); fail_unless(!strcmp(s, - "filter rule 0: site=, substring, ip=, user=, keyword=, all=conns||sites, action=divert||||, log=|||||, apply to=dstip|sni|cn|host|uri, precedence=0\n" - "filter rule 1: site=, substring, ip=, user=, keyword=, all=conns||sites, action=|split|||, log=|||||, apply to=dstip|sni|cn|host|uri, precedence=0\n" - "filter rule 2: site=, substring, ip=, user=, keyword=, all=conns||sites, action=||pass||, log=|||||, apply to=dstip|sni|cn|host|uri, precedence=0\n" - "filter rule 3: site=, substring, ip=, user=, keyword=, all=|users|sites, action=|||block|, log=|||||, apply to=dstip|sni|cn|host|uri, precedence=1\n" - "filter rule 4: site=, substring, ip=, user=, keyword=desc, all=|users|sites, action=||||match, log=|||||, apply to=dstip|sni|cn|host|uri, precedence=2\n" - "filter rule 5: site=, substring, ip=, user=, keyword=, all=conns||sites, action=||||match, log=connect|master|cert|content|pcap|mirror, apply to=dstip|sni|cn|host|uri, precedence=1"), + "filter rule 0: site=, substring, port=, , ip=, user=, keyword=, all=conns||sites|, action=divert||||, log=|||||, apply to=dstip|sni|cn|host|uri, precedence=0\n" + "filter rule 1: site=, substring, port=, , ip=, user=, keyword=, all=conns||sites|, action=|split|||, log=|||||, apply to=dstip|sni|cn|host|uri, precedence=0\n" + "filter rule 2: site=, substring, port=, , ip=, user=, keyword=, all=conns||sites|, action=||pass||, log=|||||, apply to=dstip|sni|cn|host|uri, precedence=0\n" + "filter rule 3: site=, substring, port=, , ip=, user=, keyword=, all=|users|sites|, action=|||block|, log=|||||, apply to=dstip|sni|cn|host|uri, precedence=1\n" + "filter rule 4: site=, substring, port=, , ip=, user=, keyword=desc, all=|users|sites|, action=||||match, log=|||||, apply to=dstip|sni|cn|host|uri, precedence=2\n" + "filter rule 5: site=, substring, port=, , ip=, user=, keyword=, all=conns||sites|, action=||||match, log=connect|master|cert|content|pcap|mirror, apply to=dstip|sni|cn|host|uri, precedence=1"), "failed to parse rule: %s", s); free(s); @@ -1347,7 +1436,7 @@ START_TEST(set_filter_rule_08) fail_unless(rv == 0, "failed to parse rule"); free(s); - // Block action at precedence 2 is not applied to a site of the same rule at precedence 3 now + // Block action at precedence 1 is not applied to a site of the same rule at precedence 2 now s = strdup("from ip 192.168.0.1 to ip 192.168.0.2"); rv = opts_set_filter_rule(opts, "Block", s, 0); fail_unless(rv == 0, "failed to parse rule"); @@ -1386,15 +1475,15 @@ START_TEST(set_filter_rule_08) s = filter_rule_str(opts->filter_rules); fail_unless(!strcmp(s, - "filter rule 0: site=192.168.0.2, exact, ip=192.168.0.1, user=, keyword=, all=||, action=divert||||, log=|||||, apply to=dstip||||, precedence=2\n" - "filter rule 1: site=192.168.0.2, exact, ip=192.168.0.1, user=, keyword=, all=||, action=|split|||, log=connect|master|cert|content|pcap|mirror, apply to=dstip||||, precedence=3\n" - "filter rule 2: site=192.168.0.2, exact, ip=192.168.0.1, user=, keyword=, all=||, action=||pass||, log=!connect||!cert||!pcap|, apply to=dstip||||, precedence=3\n" - "filter rule 3: site=192.168.0.2, exact, ip=192.168.0.1, user=, keyword=, all=||, action=|||block|, log=|||||, apply to=dstip||||, precedence=2\n" - "filter rule 4: site=192.168.0.3, exact, ip=192.168.0.1, user=, keyword=, all=||, action=||||match, log=|||||, apply to=dstip||||, precedence=2\n" - "filter rule 5: site=192.168.0.1, exact, ip=192.168.0.2, user=, keyword=, all=||, action=||||match, log=|||||, apply to=dstip||||, precedence=2\n" - "filter rule 6: site=, substring, ip=192.168.0.2, user=, keyword=, all=||sites, action=||||match, log=|||||, apply to=dstip||||, precedence=2\n" - "filter rule 7: site=192.168.0., substring, ip=192.168.0.2, user=, keyword=, all=||, action=||||match, log=|||||, apply to=dstip||||, precedence=2\n" - "filter rule 8: site=192.168.0.3, exact, ip=192.168.0.2, user=, keyword=, all=||, action=||||match, log=|||||, apply to=dstip||||, precedence=2"), + "filter rule 0: site=192.168.0.2, exact, port=, , ip=192.168.0.1, user=, keyword=, all=|||, action=divert||||, log=|||||, apply to=dstip||||, precedence=1\n" + "filter rule 1: site=192.168.0.2, exact, port=, , ip=192.168.0.1, user=, keyword=, all=|||, action=|split|||, log=connect|master|cert|content|pcap|mirror, apply to=dstip||||, precedence=2\n" + "filter rule 2: site=192.168.0.2, exact, port=, , ip=192.168.0.1, user=, keyword=, all=|||, action=||pass||, log=!connect||!cert||!pcap|, apply to=dstip||||, precedence=2\n" + "filter rule 3: site=192.168.0.2, exact, port=, , ip=192.168.0.1, user=, keyword=, all=|||, action=|||block|, log=|||||, apply to=dstip||||, precedence=1\n" + "filter rule 4: site=192.168.0.3, exact, port=, , ip=192.168.0.1, user=, keyword=, all=|||, action=||||match, log=|||||, apply to=dstip||||, precedence=1\n" + "filter rule 5: site=192.168.0.1, exact, port=, , ip=192.168.0.2, user=, keyword=, all=|||, action=||||match, log=|||||, apply to=dstip||||, precedence=1\n" + "filter rule 6: site=, substring, port=, , ip=192.168.0.2, user=, keyword=, all=||sites|, action=||||match, log=|||||, apply to=dstip||||, precedence=1\n" + "filter rule 7: site=192.168.0., substring, port=, , ip=192.168.0.2, user=, keyword=, all=|||, action=||||match, log=|||||, apply to=dstip||||, precedence=1\n" + "filter rule 8: site=192.168.0.3, exact, port=, , ip=192.168.0.2, user=, keyword=, all=|||, action=||||match, log=|||||, apply to=dstip||||, precedence=1"), "failed to parse rule: %s", s); free(s); @@ -1414,18 +1503,18 @@ START_TEST(set_filter_rule_08) "ip_filter->\n" " ip 0 192.168.0.2= \n" " ip: \n" -" 0: (all_sites, substring, action=||||match, log=|||||, precedence=2)\n" -" 1: 192.168.0.3 (exact, action=||||match, log=|||||, precedence=2)\n" -" 2: 192.168.0. (substring, action=||||match, log=|||||, precedence=2)\n" -" 3: 192.168.0.1 (exact, action=||||match, log=|||||, precedence=2)\n" +" 0: (all_sites, substring, action=||||match, log=|||||, precedence=1)\n" +" 1: 192.168.0.3 (exact, action=||||match, log=|||||, precedence=1)\n" +" 2: 192.168.0. (substring, action=||||match, log=|||||, precedence=1)\n" +" 3: 192.168.0.1 (exact, action=||||match, log=|||||, precedence=1)\n" " sni: \n" " cn: \n" " host: \n" " uri: \n" " ip 1 192.168.0.1= \n" " ip: \n" -" 0: 192.168.0.3 (exact, action=||||match, log=|||||, precedence=2)\n" -" 1: 192.168.0.2 (exact, action=divert|split|pass||, log=!connect|master|!cert|content|!pcap|mirror, precedence=3)\n" +" 0: 192.168.0.3 (exact, action=||||match, log=|||||, precedence=1)\n" +" 1: 192.168.0.2 (exact, action=divert|split|pass||, log=!connect|master|!cert|content|!pcap|mirror, precedence=2)\n" " sni: \n" " cn: \n" " host: \n" @@ -1448,6 +1537,135 @@ START_TEST(set_filter_rule_09) int rv; opts_t *opts = opts_new(); + s = strdup("from ip 192.168.0.1 to ip 192.168.0.2 port 443"); + rv = opts_set_filter_rule(opts, "Divert", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + + s = strdup("from ip 192.168.0.1 to ip 192.168.0.2 port 443 log connect master cert content pcap mirror"); + rv = opts_set_filter_rule(opts, "Split", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + + s = strdup("from ip 192.168.0.1 to ip 192.168.0.2 port 443 log !connect !cert !pcap"); + rv = opts_set_filter_rule(opts, "Pass", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + + // Block action at precedence 2 is not applied to a port of the same rule at precedence 3 now + s = strdup("from ip 192.168.0.1 to ip 192.168.0.2 port 443"); + rv = opts_set_filter_rule(opts, "Block", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + + // Add another target, the following port rules should not change this site rule + s = strdup("from ip 192.168.0.1 to ip 192.168.0.3 log !mirror"); + rv = opts_set_filter_rule(opts, "Match", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + + // Add another target port + s = strdup("from ip 192.168.0.1 to ip 192.168.0.3 port 443"); + rv = opts_set_filter_rule(opts, "Match", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + + // Add another target port + s = strdup("from ip 192.168.0.1 to ip 192.168.0.3 port 80"); + rv = opts_set_filter_rule(opts, "Match", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + + // Add another source + s = strdup("from ip 192.168.0.2 to ip 192.168.0.1 port 443"); + rv = opts_set_filter_rule(opts, "Match", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + + // The order of sites does not match the order of rules, it is the reverse + // But all_sites should always be the first element + s = strdup("from ip 192.168.0.2 to ip 192.168.0.1 port *"); + rv = opts_set_filter_rule(opts, "Match", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + + // Search substring + s = strdup("from ip 192.168.0.2 to ip 192.168.0.1 port 80*"); + rv = opts_set_filter_rule(opts, "Match", s, 0); + fail_unless(rv == 0, "failed to parse rule"); + free(s); + + s = filter_rule_str(opts->filter_rules); + fail_unless(!strcmp(s, + "filter rule 0: site=192.168.0.2, exact, port=443, exact_port, ip=192.168.0.1, user=, keyword=, all=|||, action=divert||||, log=|||||, apply to=dstip||||, precedence=2\n" + "filter rule 1: site=192.168.0.2, exact, port=443, exact_port, ip=192.168.0.1, user=, keyword=, all=|||, action=|split|||, log=connect|master|cert|content|pcap|mirror, apply to=dstip||||, precedence=3\n" + "filter rule 2: site=192.168.0.2, exact, port=443, exact_port, ip=192.168.0.1, user=, keyword=, all=|||, action=||pass||, log=!connect||!cert||!pcap|, apply to=dstip||||, precedence=3\n" + "filter rule 3: site=192.168.0.2, exact, port=443, exact_port, ip=192.168.0.1, user=, keyword=, all=|||, action=|||block|, log=|||||, apply to=dstip||||, precedence=2\n" + "filter rule 4: site=192.168.0.3, exact, port=, , ip=192.168.0.1, user=, keyword=, all=|||, action=||||match, log=|||||!mirror, apply to=dstip||||, precedence=2\n" + "filter rule 5: site=192.168.0.3, exact, port=443, exact_port, ip=192.168.0.1, user=, keyword=, all=|||, action=||||match, log=|||||, apply to=dstip||||, precedence=2\n" + "filter rule 6: site=192.168.0.3, exact, port=80, exact_port, ip=192.168.0.1, user=, keyword=, all=|||, action=||||match, log=|||||, apply to=dstip||||, precedence=2\n" + "filter rule 7: site=192.168.0.1, exact, port=443, exact_port, ip=192.168.0.2, user=, keyword=, all=|||, action=||||match, log=|||||, apply to=dstip||||, precedence=2\n" + "filter rule 8: site=192.168.0.1, exact, port=, substring_port, ip=192.168.0.2, user=, keyword=, all=|||ports, action=||||match, log=|||||, apply to=dstip||||, precedence=2\n" + "filter rule 9: site=192.168.0.1, exact, port=80, substring_port, ip=192.168.0.2, user=, keyword=, all=|||, action=||||match, log=|||||, apply to=dstip||||, precedence=2"), + "failed to parse rule: %s", s); + free(s); + + opts->filter = opts_set_filter(opts->filter_rules); + + s = filter_str(opts->filter); + fail_unless(!strcmp(s, "filter=>\n" +"userkeyword_filter->\n" +"user_filter->\n" +"keyword_filter->\n" +"all_user_filter->\n" +" ip: \n" +" sni: \n" +" cn: \n" +" host: \n" +" uri: \n" +"ip_filter->\n" +" ip 0 192.168.0.2= \n" +" ip: \n" +" 0: 192.168.0.1 (exact, action=||||, log=|||||, precedence=0)\n" +" port:\n" +" 0: (all_ports, substring, action=||||match, log=|||||, precedence=2)\n" +" 1: 80 (substring, action=||||match, log=|||||, precedence=2)\n" +" 2: 443 (exact, action=||||match, log=|||||, precedence=2)\n" +" sni: \n" +" cn: \n" +" host: \n" +" uri: \n" +" ip 1 192.168.0.1= \n" +" ip: \n" +" 0: 192.168.0.3 (exact, action=||||match, log=|||||!mirror, precedence=2)\n" +" port:\n" +" 0: 80 (exact, action=||||match, log=|||||, precedence=2)\n" +" 1: 443 (exact, action=||||match, log=|||||, precedence=2)\n" +" 1: 192.168.0.2 (exact, action=||||, log=|||||, precedence=0)\n" +" port:\n" +" 0: 443 (exact, action=divert|split|pass||, log=!connect|master|!cert|content|!pcap|mirror, precedence=3)\n" +" sni: \n" +" cn: \n" +" host: \n" +" uri: \n" +"all_filter->\n" +" ip: \n" +" sni: \n" +" cn: \n" +" host: \n" +" uri: \n"), "failed to translate rule: %s", s); + free(s); + + opts_free(opts); +} +END_TEST + +START_TEST(set_filter_rule_10) +{ + char *s; + int rv; + opts_t *opts = opts_new(); + opts->user_auth = 1; s = strdup("from user root to sni example.com"); @@ -1504,15 +1722,15 @@ START_TEST(set_filter_rule_09) s = filter_rule_str(opts->filter_rules); fail_unless(!strcmp(s, - "filter rule 0: site=example.com, exact, ip=, user=root, keyword=, all=||, action=divert||||, log=|||||, apply to=|sni|||, precedence=3\n" - "filter rule 1: site=example.com, exact, ip=, user=root, keyword=, all=||, action=|split|||, log=connect|master|cert|content|pcap|mirror, apply to=|sni|||, precedence=4\n" - "filter rule 2: site=example.com, exact, ip=, user=root, keyword=, all=||, action=||pass||, log=!connect||!cert||!pcap|, apply to=|sni|||, precedence=4\n" - "filter rule 3: site=example.com, exact, ip=, user=root, keyword=, all=||, action=|||block|, log=|||||, apply to=|sni|||, precedence=3\n" - "filter rule 4: site=example2.com, exact, ip=, user=root, keyword=, all=||, action=||||match, log=|||||, apply to=|sni|||, precedence=3\n" - "filter rule 5: site=example.com, exact, ip=, user=daemon, keyword=, all=||, action=||||match, log=|||||, apply to=|sni|||, precedence=3\n" - "filter rule 6: site=, substring, ip=, user=daemon, keyword=, all=||sites, action=||||match, log=|||||, apply to=|sni|||, precedence=3\n" - "filter rule 7: site=.example.com, substring, ip=, user=daemon, keyword=, all=||, action=||||match, log=|||||, apply to=|sni|||, precedence=3\n" - "filter rule 8: site=example3.com, exact, ip=, user=daemon, keyword=, all=||, action=||||match, log=|||||, apply to=|sni|||, precedence=3"), + "filter rule 0: site=example.com, exact, port=, , ip=, user=root, keyword=, all=|||, action=divert||||, log=|||||, apply to=|sni|||, precedence=3\n" + "filter rule 1: site=example.com, exact, port=, , ip=, user=root, keyword=, all=|||, action=|split|||, log=connect|master|cert|content|pcap|mirror, apply to=|sni|||, precedence=4\n" + "filter rule 2: site=example.com, exact, port=, , ip=, user=root, keyword=, all=|||, action=||pass||, log=!connect||!cert||!pcap|, apply to=|sni|||, precedence=4\n" + "filter rule 3: site=example.com, exact, port=, , ip=, user=root, keyword=, all=|||, action=|||block|, log=|||||, apply to=|sni|||, precedence=3\n" + "filter rule 4: site=example2.com, exact, port=, , ip=, user=root, keyword=, all=|||, action=||||match, log=|||||, apply to=|sni|||, precedence=3\n" + "filter rule 5: site=example.com, exact, port=, , ip=, user=daemon, keyword=, all=|||, action=||||match, log=|||||, apply to=|sni|||, precedence=3\n" + "filter rule 6: site=, substring, port=, , ip=, user=daemon, keyword=, all=||sites|, action=||||match, log=|||||, apply to=|sni|||, precedence=3\n" + "filter rule 7: site=.example.com, substring, port=, , ip=, user=daemon, keyword=, all=|||, action=||||match, log=|||||, apply to=|sni|||, precedence=3\n" + "filter rule 8: site=example3.com, exact, port=, , ip=, user=daemon, keyword=, all=|||, action=||||match, log=|||||, apply to=|sni|||, precedence=3"), "failed to parse rule: %s", s); free(s); @@ -1560,7 +1778,7 @@ START_TEST(set_filter_rule_09) } END_TEST -START_TEST(set_filter_rule_10) +START_TEST(set_filter_rule_11) { char *s; int rv; @@ -1646,19 +1864,19 @@ START_TEST(set_filter_rule_10) s = filter_rule_str(opts->filter_rules); fail_unless(!strcmp(s, - "filter rule 0: site=example.com, exact, ip=, user=root, keyword=desc, all=||, action=divert||||, log=|||||, apply to=||cn||, precedence=4\n" - "filter rule 1: site=example.com, exact, ip=, user=root, keyword=desc, all=||, action=|split|||, log=connect|master|cert|content|pcap|mirror, apply to=||cn||, precedence=5\n" - "filter rule 2: site=example.com, exact, ip=, user=root, keyword=desc, all=||, action=||pass||, log=!connect||!cert||!pcap|, apply to=||cn||, precedence=5\n" - "filter rule 3: site=example.com, exact, ip=, user=root, keyword=desc, all=||, action=|||block|, log=|||||, apply to=||cn||, precedence=4\n" - "filter rule 4: site=example2.com, exact, ip=, user=root, keyword=desc, all=||, action=||||match, log=|||||, apply to=||cn||, precedence=4\n" - "filter rule 5: site=example.com, exact, ip=, user=daemon, keyword=desc, all=||, action=||||match, log=|||||, apply to=||cn||, precedence=4\n" - "filter rule 6: site=, substring, ip=, user=daemon, keyword=desc, all=||sites, action=||||match, log=|||||, apply to=||cn||, precedence=4\n" - "filter rule 7: site=.example.com, substring, ip=, user=daemon, keyword=desc, all=||, action=||||match, log=|||||, apply to=||cn||, precedence=4\n" - "filter rule 8: site=example3.com, exact, ip=, user=daemon, keyword=desc, all=||, action=||||match, log=|||||, apply to=||cn||, precedence=4\n" - "filter rule 9: site=example4.com, exact, ip=, user=daemon, keyword=desc2, all=||, action=||||match, log=|||||, apply to=||cn||, precedence=4\n" - "filter rule 10: site=example5.com, exact, ip=, user=, keyword=desc, all=|users|, action=||||match, log=|||||, apply to=||cn||, precedence=3\n" - "filter rule 11: site=, substring, ip=, user=, keyword=desc, all=|users|sites, action=||||match, log=|||||, apply to=|||host|, precedence=3\n" - "filter rule 12: site=example6.com, exact, ip=, user=, keyword=desc3, all=|users|, action=||||match, log=|||||, apply to=||||uri, precedence=3"), + "filter rule 0: site=example.com, exact, port=, , ip=, user=root, keyword=desc, all=|||, action=divert||||, log=|||||, apply to=||cn||, precedence=4\n" + "filter rule 1: site=example.com, exact, port=, , ip=, user=root, keyword=desc, all=|||, action=|split|||, log=connect|master|cert|content|pcap|mirror, apply to=||cn||, precedence=5\n" + "filter rule 2: site=example.com, exact, port=, , ip=, user=root, keyword=desc, all=|||, action=||pass||, log=!connect||!cert||!pcap|, apply to=||cn||, precedence=5\n" + "filter rule 3: site=example.com, exact, port=, , ip=, user=root, keyword=desc, all=|||, action=|||block|, log=|||||, apply to=||cn||, precedence=4\n" + "filter rule 4: site=example2.com, exact, port=, , ip=, user=root, keyword=desc, all=|||, action=||||match, log=|||||, apply to=||cn||, precedence=4\n" + "filter rule 5: site=example.com, exact, port=, , ip=, user=daemon, keyword=desc, all=|||, action=||||match, log=|||||, apply to=||cn||, precedence=4\n" + "filter rule 6: site=, substring, port=, , ip=, user=daemon, keyword=desc, all=||sites|, action=||||match, log=|||||, apply to=||cn||, precedence=4\n" + "filter rule 7: site=.example.com, substring, port=, , ip=, user=daemon, keyword=desc, all=|||, action=||||match, log=|||||, apply to=||cn||, precedence=4\n" + "filter rule 8: site=example3.com, exact, port=, , ip=, user=daemon, keyword=desc, all=|||, action=||||match, log=|||||, apply to=||cn||, precedence=4\n" + "filter rule 9: site=example4.com, exact, port=, , ip=, user=daemon, keyword=desc2, all=|||, action=||||match, log=|||||, apply to=||cn||, precedence=4\n" + "filter rule 10: site=example5.com, exact, port=, , ip=, user=, keyword=desc, all=|users||, action=||||match, log=|||||, apply to=||cn||, precedence=3\n" + "filter rule 11: site=, substring, port=, , ip=, user=, keyword=desc, all=|users|sites|, action=||||match, log=|||||, apply to=|||host|, precedence=3\n" + "filter rule 12: site=example6.com, exact, port=, , ip=, user=, keyword=desc3, all=|users||, action=||||match, log=|||||, apply to=||||uri, precedence=3"), "failed to parse rule: %s", s); free(s); @@ -1695,34 +1913,100 @@ START_TEST(set_filter_rule_10) " host: \n" " uri: \n" "user_filter->\n" -" user 0 daemon= \n" +"keyword_filter->\n" +" keyword 0 desc3= \n" " ip: \n" " sni: \n" " cn: \n" " host: \n" " uri: \n" -" user 1 root= \n" +" 0: example6.com (exact, action=||||match, log=|||||, precedence=3)\n" +" keyword 1 desc= \n" " ip: \n" " sni: \n" " cn: \n" +" 0: example5.com (exact, action=||||match, log=|||||, precedence=3)\n" " host: \n" +" 0: (all_sites, substring, action=||||match, log=|||||, precedence=3)\n" " uri: \n" -"keyword_filter->\n" -" keyword 0 desc3= \n" +"all_user_filter->\n" " ip: \n" " sni: \n" " cn: \n" " host: \n" " uri: \n" -" 0: example6.com (exact, action=||||match, log=|||||, precedence=3)\n" -" keyword 1 desc= \n" +"ip_filter->\n" +"all_filter->\n" " ip: \n" " sni: \n" " cn: \n" -" 0: example5.com (exact, action=||||match, log=|||||, precedence=3)\n" " host: \n" -" 0: (all_sites, substring, action=||||match, log=|||||, precedence=3)\n" -" uri: \n" +" uri: \n"), "failed to translate rule: %s", s); + free(s); + + opts_free(opts); +} +END_TEST + +START_TEST(set_filter_rule_12) +{ + char *s; + int rv; + opts_t *opts = opts_new(); + + s = strdup("$ips 192.168.0.1 192.168.0.2"); + rv = opts_set_macro(opts, s, 0); + fail_unless(rv == 0, "failed to set macro"); + free(s); + + s = strdup("$dstips 192.168.0.3 192.168.0.4"); + rv = opts_set_macro(opts, s, 0); + fail_unless(rv == 0, "failed to set macro"); + free(s); + + s = strdup("$ports 80 443"); + rv = opts_set_macro(opts, s, 0); + fail_unless(rv == 0, "failed to set macro"); + free(s); + + s = strdup("$logs !master !pcap"); + rv = opts_set_macro(opts, s, 0); + fail_unless(rv == 0, "failed to set macro"); + free(s); + + s = strdup("from ip $ips to ip $dstips port $ports log $logs"); + rv = opts_set_filter_rule(opts, "Match", s, 0); + fail_unless(rv == 1, "failed to parse rule"); + free(s); + + s = filter_rule_str(opts->filter_rules); + fail_unless(!strcmp(s, + "filter rule 0: site=192.168.0.4, exact, port=443, exact_port, ip=192.168.0.2, user=, keyword=, all=|||, action=||||match, log=||||!pcap|, apply to=dstip||||, precedence=3\n" + "filter rule 1: site=192.168.0.4, exact, port=443, exact_port, ip=192.168.0.2, user=, keyword=, all=|||, action=||||match, log=|!master||||, apply to=dstip||||, precedence=3\n" + "filter rule 2: site=192.168.0.4, exact, port=80, exact_port, ip=192.168.0.2, user=, keyword=, all=|||, action=||||match, log=||||!pcap|, apply to=dstip||||, precedence=3\n" + "filter rule 3: site=192.168.0.4, exact, port=80, exact_port, ip=192.168.0.2, user=, keyword=, all=|||, action=||||match, log=|!master||||, apply to=dstip||||, precedence=3\n" + "filter rule 4: site=192.168.0.3, exact, port=443, exact_port, ip=192.168.0.2, user=, keyword=, all=|||, action=||||match, log=||||!pcap|, apply to=dstip||||, precedence=3\n" + "filter rule 5: site=192.168.0.3, exact, port=443, exact_port, ip=192.168.0.2, user=, keyword=, all=|||, action=||||match, log=|!master||||, apply to=dstip||||, precedence=3\n" + "filter rule 6: site=192.168.0.3, exact, port=80, exact_port, ip=192.168.0.2, user=, keyword=, all=|||, action=||||match, log=||||!pcap|, apply to=dstip||||, precedence=3\n" + "filter rule 7: site=192.168.0.3, exact, port=80, exact_port, ip=192.168.0.2, user=, keyword=, all=|||, action=||||match, log=|!master||||, apply to=dstip||||, precedence=3\n" + "filter rule 8: site=192.168.0.4, exact, port=443, exact_port, ip=192.168.0.1, user=, keyword=, all=|||, action=||||match, log=||||!pcap|, apply to=dstip||||, precedence=3\n" + "filter rule 9: site=192.168.0.4, exact, port=443, exact_port, ip=192.168.0.1, user=, keyword=, all=|||, action=||||match, log=|!master||||, apply to=dstip||||, precedence=3\n" + "filter rule 10: site=192.168.0.4, exact, port=80, exact_port, ip=192.168.0.1, user=, keyword=, all=|||, action=||||match, log=||||!pcap|, apply to=dstip||||, precedence=3\n" + "filter rule 11: site=192.168.0.4, exact, port=80, exact_port, ip=192.168.0.1, user=, keyword=, all=|||, action=||||match, log=|!master||||, apply to=dstip||||, precedence=3\n" + "filter rule 12: site=192.168.0.3, exact, port=443, exact_port, ip=192.168.0.1, user=, keyword=, all=|||, action=||||match, log=||||!pcap|, apply to=dstip||||, precedence=3\n" + "filter rule 13: site=192.168.0.3, exact, port=443, exact_port, ip=192.168.0.1, user=, keyword=, all=|||, action=||||match, log=|!master||||, apply to=dstip||||, precedence=3\n" + "filter rule 14: site=192.168.0.3, exact, port=80, exact_port, ip=192.168.0.1, user=, keyword=, all=|||, action=||||match, log=||||!pcap|, apply to=dstip||||, precedence=3\n" + "filter rule 15: site=192.168.0.3, exact, port=80, exact_port, ip=192.168.0.1, user=, keyword=, all=|||, action=||||match, log=|!master||||, apply to=dstip||||, precedence=3"), + "failed to parse rule: %s", s); + free(s); + + opts->filter = opts_set_filter(opts->filter_rules); + + s = filter_str(opts->filter); + fail_unless(!strcmp(s, "filter=>\n" +"userkeyword_filter->\n" +"user_filter->\n" +"keyword_filter->\n" "all_user_filter->\n" " ip: \n" " sni: \n" @@ -1730,6 +2014,34 @@ START_TEST(set_filter_rule_10) " host: \n" " uri: \n" "ip_filter->\n" +" ip 0 192.168.0.1= \n" +" ip: \n" +" 0: 192.168.0.3 (exact, action=||||, log=|||||, precedence=0)\n" +" port:\n" +" 0: 80 (exact, action=||||match, log=|!master|||!pcap|, precedence=3)\n" +" 1: 443 (exact, action=||||match, log=|!master|||!pcap|, precedence=3)\n" +" 1: 192.168.0.4 (exact, action=||||, log=|||||, precedence=0)\n" +" port:\n" +" 0: 80 (exact, action=||||match, log=|!master|||!pcap|, precedence=3)\n" +" 1: 443 (exact, action=||||match, log=|!master|||!pcap|, precedence=3)\n" +" sni: \n" +" cn: \n" +" host: \n" +" uri: \n" +" ip 1 192.168.0.2= \n" +" ip: \n" +" 0: 192.168.0.3 (exact, action=||||, log=|||||, precedence=0)\n" +" port:\n" +" 0: 80 (exact, action=||||match, log=|!master|||!pcap|, precedence=3)\n" +" 1: 443 (exact, action=||||match, log=|!master|||!pcap|, precedence=3)\n" +" 1: 192.168.0.4 (exact, action=||||, log=|||||, precedence=0)\n" +" port:\n" +" 0: 80 (exact, action=||||match, log=|!master|||!pcap|, precedence=3)\n" +" 1: 443 (exact, action=||||match, log=|!master|||!pcap|, precedence=3)\n" +" sni: \n" +" cn: \n" +" host: \n" +" uri: \n" "all_filter->\n" " ip: \n" " sni: \n" @@ -1742,7 +2054,7 @@ START_TEST(set_filter_rule_10) } END_TEST -START_TEST(set_filter_rule_11) +START_TEST(set_filter_rule_13) { char *s; int rv; @@ -1780,30 +2092,30 @@ START_TEST(set_filter_rule_11) s = filter_rule_str(opts->filter_rules); fail_unless(!strcmp(s, - "filter rule 0: site=site2, exact, ip=, user=daemon, keyword=desc2, all=||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" - "filter rule 1: site=site2, exact, ip=, user=daemon, keyword=desc2, all=||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" - "filter rule 2: site=site2, exact, ip=, user=daemon, keyword=desc2, all=||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5\n" - "filter rule 3: site=site1, exact, ip=, user=daemon, keyword=desc2, all=||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" - "filter rule 4: site=site1, exact, ip=, user=daemon, keyword=desc2, all=||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" - "filter rule 5: site=site1, exact, ip=, user=daemon, keyword=desc2, all=||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5\n" - "filter rule 6: site=site2, exact, ip=, user=daemon, keyword=desc1, all=||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" - "filter rule 7: site=site2, exact, ip=, user=daemon, keyword=desc1, all=||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" - "filter rule 8: site=site2, exact, ip=, user=daemon, keyword=desc1, all=||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5\n" - "filter rule 9: site=site1, exact, ip=, user=daemon, keyword=desc1, all=||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" - "filter rule 10: site=site1, exact, ip=, user=daemon, keyword=desc1, all=||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" - "filter rule 11: site=site1, exact, ip=, user=daemon, keyword=desc1, all=||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5\n" - "filter rule 12: site=site2, exact, ip=, user=root, keyword=desc2, all=||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" - "filter rule 13: site=site2, exact, ip=, user=root, keyword=desc2, all=||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" - "filter rule 14: site=site2, exact, ip=, user=root, keyword=desc2, all=||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5\n" - "filter rule 15: site=site1, exact, ip=, user=root, keyword=desc2, all=||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" - "filter rule 16: site=site1, exact, ip=, user=root, keyword=desc2, all=||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" - "filter rule 17: site=site1, exact, ip=, user=root, keyword=desc2, all=||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5\n" - "filter rule 18: site=site2, exact, ip=, user=root, keyword=desc1, all=||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" - "filter rule 19: site=site2, exact, ip=, user=root, keyword=desc1, all=||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" - "filter rule 20: site=site2, exact, ip=, user=root, keyword=desc1, all=||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5\n" - "filter rule 21: site=site1, exact, ip=, user=root, keyword=desc1, all=||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" - "filter rule 22: site=site1, exact, ip=, user=root, keyword=desc1, all=||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" - "filter rule 23: site=site1, exact, ip=, user=root, keyword=desc1, all=||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5"), + "filter rule 0: site=site2, exact, port=, , ip=, user=daemon, keyword=desc2, all=|||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" + "filter rule 1: site=site2, exact, port=, , ip=, user=daemon, keyword=desc2, all=|||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" + "filter rule 2: site=site2, exact, port=, , ip=, user=daemon, keyword=desc2, all=|||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5\n" + "filter rule 3: site=site1, exact, port=, , ip=, user=daemon, keyword=desc2, all=|||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" + "filter rule 4: site=site1, exact, port=, , ip=, user=daemon, keyword=desc2, all=|||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" + "filter rule 5: site=site1, exact, port=, , ip=, user=daemon, keyword=desc2, all=|||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5\n" + "filter rule 6: site=site2, exact, port=, , ip=, user=daemon, keyword=desc1, all=|||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" + "filter rule 7: site=site2, exact, port=, , ip=, user=daemon, keyword=desc1, all=|||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" + "filter rule 8: site=site2, exact, port=, , ip=, user=daemon, keyword=desc1, all=|||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5\n" + "filter rule 9: site=site1, exact, port=, , ip=, user=daemon, keyword=desc1, all=|||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" + "filter rule 10: site=site1, exact, port=, , ip=, user=daemon, keyword=desc1, all=|||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" + "filter rule 11: site=site1, exact, port=, , ip=, user=daemon, keyword=desc1, all=|||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5\n" + "filter rule 12: site=site2, exact, port=, , ip=, user=root, keyword=desc2, all=|||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" + "filter rule 13: site=site2, exact, port=, , ip=, user=root, keyword=desc2, all=|||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" + "filter rule 14: site=site2, exact, port=, , ip=, user=root, keyword=desc2, all=|||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5\n" + "filter rule 15: site=site1, exact, port=, , ip=, user=root, keyword=desc2, all=|||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" + "filter rule 16: site=site1, exact, port=, , ip=, user=root, keyword=desc2, all=|||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" + "filter rule 17: site=site1, exact, port=, , ip=, user=root, keyword=desc2, all=|||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5\n" + "filter rule 18: site=site2, exact, port=, , ip=, user=root, keyword=desc1, all=|||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" + "filter rule 19: site=site2, exact, port=, , ip=, user=root, keyword=desc1, all=|||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" + "filter rule 20: site=site2, exact, port=, , ip=, user=root, keyword=desc1, all=|||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5\n" + "filter rule 21: site=site1, exact, port=, , ip=, user=root, keyword=desc1, all=|||, action=||||match, log=|||||mirror, apply to=|sni|||, precedence=5\n" + "filter rule 22: site=site1, exact, port=, , ip=, user=root, keyword=desc1, all=|||, action=||||match, log=|||content||, apply to=|sni|||, precedence=5\n" + "filter rule 23: site=site1, exact, port=, , ip=, user=root, keyword=desc1, all=|||, action=||||match, log=connect|||||, apply to=|sni|||, precedence=5"), "failed to parse rule: %s", s); free(s); @@ -1847,18 +2159,6 @@ START_TEST(set_filter_rule_11) " host: \n" " uri: \n" "user_filter->\n" -" user 0 root= \n" -" ip: \n" -" sni: \n" -" cn: \n" -" host: \n" -" uri: \n" -" user 1 daemon= \n" -" ip: \n" -" sni: \n" -" cn: \n" -" host: \n" -" uri: \n" "keyword_filter->\n" "all_user_filter->\n" " ip: \n" @@ -1898,6 +2198,8 @@ filter_suite(void) tcase_add_test(tc, set_filter_rule_09); tcase_add_test(tc, set_filter_rule_10); tcase_add_test(tc, set_filter_rule_11); + tcase_add_test(tc, set_filter_rule_12); + tcase_add_test(tc, set_filter_rule_13); suite_add_tcase(s, tc); return s; diff --git a/tests/check/opts.t.c b/tests/check/opts.t.c index 6ce2b60..f36fa5b 100644 --- a/tests/check/opts.t.c +++ b/tests/check/opts.t.c @@ -649,7 +649,7 @@ START_TEST(opts_set_passsite_01) ps = filter_rule_str(opts->filter_rules); #ifndef WITHOUT_USERAUTH - fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, ip=, user=, keyword=, all=conns||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=1"), "failed parsing passite example.com: %s", ps); + fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, port=, , ip=, user=, keyword=, all=conns|||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=1"), "failed parsing passite example.com: %s", ps); #else /* WITHOUT_USERAUTH */ fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, ip=, all=conns|, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=1"), "failed parsing passite example.com: %s", ps); #endif /* WITHOUT_USERAUTH */ @@ -679,7 +679,7 @@ START_TEST(opts_set_passsite_02) ps = filter_rule_str(opts->filter_rules); #ifndef WITHOUT_USERAUTH - fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, ip=192.168.0.1, user=, keyword=, all=||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=2"), "failed parsing passite example.com 192.168.0.1: %s", ps); + fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, port=, , ip=192.168.0.1, user=, keyword=, all=|||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=2"), "failed parsing passite example.com 192.168.0.1: %s", ps); #else /* WITHOUT_USERAUTH */ fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, ip=192.168.0.1, all=|, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=2"), "failed parsing passite example.com 192.168.0.1: %s", ps); #endif /* !WITHOUT_USERAUTH */ @@ -709,7 +709,7 @@ START_TEST(opts_set_passsite_03) fail_unless(!opts->filter_rules->next, "next set"); ps = filter_rule_str(opts->filter_rules); - fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, ip=, user=root, keyword=, all=||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=3"), "failed parsing passite example.com root: %s", ps); + fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, port=, , ip=, user=root, keyword=, all=|||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=3"), "failed parsing passite example.com root: %s", ps); free(ps); opts_free(opts); @@ -736,7 +736,7 @@ START_TEST(opts_set_passsite_04) fail_unless(!opts->filter_rules->next, "next set"); ps = filter_rule_str(opts->filter_rules); - fail_unless(!strcmp(ps, "filter rule 0: site=*.google.com, exact, ip=, user=, keyword=android, all=|users|, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=3"), "failed parsing passite *.google.com * android: %s", ps); + fail_unless(!strcmp(ps, "filter rule 0: site=*.google.com, exact, port=, , ip=, user=, keyword=android, all=|users||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=3"), "failed parsing passite *.google.com * android: %s", ps); free(ps); opts_free(opts); @@ -791,11 +791,11 @@ START_TEST(opts_set_passsite_05) fail_unless(opts->filter_rules->next->next->next, "next->next->next not set"); fail_unless(opts->filter_rules->next->next->next->next, "next->next->next->next not set"); fail_unless(!opts->filter_rules->next->next->next->next->next, "next->next->next->next->next set"); - fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, ip=, user=, keyword=, all=conns||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=1\n" - "filter rule 1: site=example.com, exact, ip=, user=, keyword=, all=|users|, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=2\n" - "filter rule 2: site=example.com, exact, ip=192.168.0.1, user=, keyword=, all=||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=2\n" - "filter rule 3: site=example.com, exact, ip=, user=root, keyword=, all=||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=3\n" - "filter rule 4: site=*.google.com, exact, ip=, user=, keyword=android, all=|users|, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=3"), + fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, port=, , ip=, user=, keyword=, all=conns|||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=1\n" + "filter rule 1: site=example.com, exact, port=, , ip=, user=, keyword=, all=|users||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=2\n" + "filter rule 2: site=example.com, exact, port=, , ip=192.168.0.1, user=, keyword=, all=|||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=2\n" + "filter rule 3: site=example.com, exact, port=, , ip=, user=root, keyword=, all=|||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=3\n" + "filter rule 4: site=*.google.com, exact, port=, , ip=, user=, keyword=android, all=|users||, action=||pass||, log=|||||, apply to=|sni|cn||, precedence=3"), "failed parsing multiple passites: %s", ps); #else /* WITHOUT_USERAUTH */ fail_unless(!opts->filter_rules->next->next->next, "next->next->next set");