Add Match action and connect|content|pcap|mirror log actions in filtering rules

- Match action is added to be used with log actions only, the other
filter actions can specify log actions too
- Log actions do not configure any loggers. Global loggers for
respective log actions should have been configured for those log actions
to have any effect.
- If no filter rules are defined for a proxyspec, all log actions are
enabled. Otherwise, all log actions are disabled, and filtering rules
should enable them specifically.
- Fix max number of tokens in proxyspec and filter parsers
- Fix issues with rejecting unknown args in filter rule parser
- Do not use filter_rules field of proxyspec after config finished, it
is used for filter configuration and freed afterwards
pull/48/head
Soner Tari 3 years ago
parent 0787e74bd1
commit f0c2ca6819

@ -1,4 +1,26 @@
### SSLproxy 0.8.7
- Add filtering rules:
(Divert|Split|Pass|Block|Match)
([from (
user (username|*) [desc keyword]|
ip (clientaddr|*)|
*)]
[to (
sni (servername[*]|*)|
cn (commonname[*]|*)|
host (host[*]|*)|
uri (uri[*]|*)|
ip (serveraddr|*)|
*)]
[log ([connect] [content] [pcap] [mirror]|*)]
|*)
- Add -Q test config option.
### SSLproxy 0.8.4 2021-08-29
- Add split mode of operation similar to SSLsplit. In split mode, packets

@ -296,8 +296,8 @@ These user control lists can be defined globally or per-proxyspec.
### Filtering rules
SSLproxy can divert, split, pass, or block connections based on filtering
rules. Filtering rules can be defined globally or per-proxyspec.
SSLproxy can divert, split, pass, block, or match connections based on
filtering rules. Filtering rules can be defined globally or per-proxyspec.
- Divert action diverts packets to listening program, allowing SSL inspection
by listening program and content logging of packets
@ -307,21 +307,24 @@ allowing content logging of packets
- Pass action passes the connection through by engaging passthrough mode,
effectively disabling SSL inspection and content logging of packets
- Block action terminates the connection
- Match action is used to specify log actions for matching connections without
changing their filter actions
The syntax of filtering rules is as follows:
(Divert|Split|Pass|Block)
(Divert|Split|Pass|Block|Match)
([from (
user (username|*) [desc keyword]|
ip (clientaddr|*)|
*)]
user (username|*) [desc keyword]|
ip (clientaddr|*)|
*)]
[to (
sni (servername[*]|*)|
cn (commonname[*]|*)|
host (host[*]|*)|
uri (uri[*]|*)|
ip (serveraddr|*)|
*)]
sni (servername[*]|*)|
cn (commonname[*]|*)|
host (host[*]|*)|
uri (uri[*]|*)|
ip (serveraddr|*)|
*)]
[log ([connect] [content] [pcap] [mirror]|*)]
|*)
The definition of which connections the rule action will be applied to is
@ -337,18 +340,40 @@ Common Names of SSL connections, Host or URI fields in HTTP Request headers, or
- The proxyspec handling the connection defines the protocol filter for the
connection.
If and how a connection should be logged is specified using the `log` part of
filtering rules. `connect` enables logging connection information to connect
log file, `content` enables logging packet contents to content log file,
`pcap` enables writing packets to pcap files, and `mirror` enables mirroring
packets to mirror interfaces or targets.
For example, if the following rules are defined in a structured HTTPS proxyspec,
Split from user soner desc notebook to sni example.com
Split from user soner desc notebook to sni example.com log content
Pass from user soner desc android to cn .fbcdn.net*
The first filtering rule above splits but does not divert HTTPS connections
from the user soner who has logged in with a description containing the keyword
notebook to SSL sites with the SNI of example.com. The second rule passes
through HTTPS connections from the user soner who has logged in with a
description containing the keyword android to SSL sites with the Common Names
containing the substring .fbcdn.net anywhere in it (notice the asterisk at the
end). Note that the second example is a filtering rule you can use to resolve
notebook to SSL sites with the SNI of example.com. Also, the rule specifies
that the packet contents of the matching connection be written to content log
file configured globally.
The second rule passes through HTTPS connections from the user soner who has
logged in with a description containing the keyword android to SSL sites with
the Common Names containing the substring .fbcdn.net anywhere in it (notice
the asterisk at the end). Since connection contents cannot be written to log
files in passthrough mode, the rule does not specify any log action.
The default filter action is Divert. So, if those are the only filtering rules
in that proxyspec, the other connections are diverted to the listening program
specified in that proxyspec, without writing any logs.
If you want to enable, say, connect logging for the other connections handled
by that proxyspec, without changing their default Divert filter action, you
can add a third filtering rule to that proxyspec:
Match * log connect
Note that the second example above is a filtering rule you can use to resolve
one of the certificate issues preventing the Facebook application on Android
smartphones to connect to the Internet behind sslproxy.
@ -373,10 +398,21 @@ In terms of possible filter actions,
- HTTP filter rules can take the block action, but not divert, split, or pass
actions.
Log actions do not configure any loggers. Global content loggers for
respective log actions should have been configured for those log actions to
have any effect.
If no filter rules are defined for a proxyspec, all logging actions for that
proxyspec are enabled. Otherwise, all log actions are disabled, and filtering
rules should enable them specifically. Note that if logging is disabled by
filtering rules, the loggers create the log files, but they remain empty.
You can append an asterisk `*` to site field of filtering rules for substring
matching. Otherwise, the filter searches for an exact match with the site field
in the rule.
The order of `from`, `to`, and `log` parts is not important.
If the UserAuth option is disabled, only client IP addresses can be used in
the from part of filtering rules.
@ -403,7 +439,7 @@ on each line. PassSite rules can search for exact or substring matches.
Logging options include traditional SSLproxy connect and content log files as
well as PCAP files and mirroring decrypted traffic to a network interface.
Additionally, certificates, master secrets and local process information can be
logged.
logged. Filtering rules can selectively modify connection logging.
See the manual pages sslproxy(1) and sslproxy.conf(5) for details on using
SSLproxy, setting up the various NAT engines, and for examples.

@ -896,7 +896,11 @@ errout:
* On failure, lb is not freed.
*/
int
log_content_submit(log_content_ctx_t *ctx, logbuf_t *lb, int is_request)
log_content_submit(log_content_ctx_t *ctx, logbuf_t *lb, int is_request, int log_content, int log_pcap
#ifndef WITHOUT_MIRROR
, int log_mirror
#endif /* !WITHOUT_MIRROR */
)
{
unsigned long prepflags = 0;
logbuf_t *lbpcap, *lbmirror;
@ -909,26 +913,26 @@ log_content_submit(log_content_ctx_t *ctx, logbuf_t *lb, int is_request)
return -1;
lbpcap = lbmirror = lb;
if (content_file_log) {
if (content_pcap_log) {
if (log_content && content_file_log) {
if (log_pcap && content_pcap_log) {
lbpcap = logbuf_new_deepcopy(lb, 1);
if (!lbpcap)
goto errout;
}
#ifndef WITHOUT_MIRROR
if (content_mirror_log) {
if (log_mirror && content_mirror_log) {
lbmirror = logbuf_new_deepcopy(lb, 1);
if (!lbmirror)
goto errout;
}
} else if (content_pcap_log && content_mirror_log) {
} else if (log_pcap && content_pcap_log && log_mirror && content_mirror_log) {
lbmirror = logbuf_new_deepcopy(lb, 1);
if (!lbmirror)
goto errout;
#endif /* !WITHOUT_MIRROR */
}
if (content_pcap_log) {
if (log_pcap && content_pcap_log) {
if (logger_submit(content_pcap_log, ctx->pcap,
prepflags, lbpcap) == -1) {
goto errout;
@ -936,7 +940,7 @@ log_content_submit(log_content_ctx_t *ctx, logbuf_t *lb, int is_request)
lbpcap = NULL;
}
#ifndef WITHOUT_MIRROR
if (content_mirror_log) {
if (log_mirror && content_mirror_log) {
if (logger_submit(content_mirror_log, ctx->mirror,
prepflags, lbmirror) == -1) {
goto errout;
@ -944,7 +948,7 @@ log_content_submit(log_content_ctx_t *ctx, logbuf_t *lb, int is_request)
lbmirror = NULL;
}
#endif /* !WITHOUT_MIRROR */
if (content_file_log) {
if (log_content && content_file_log) {
if (logger_submit(content_file_log, ctx->file,
prepflags, lb) == -1) {
return -1;
@ -960,7 +964,12 @@ errout:
}
int
log_content_close(log_content_ctx_t *ctx, int by_requestor)
log_content_close(log_content_ctx_t *ctx, int by_requestor, int log_content, int log_pcap
#ifndef WITHOUT_MIRROR
, int log_mirror
#endif /* !WITHOUT_MIRROR */
)
{
unsigned long prepflags = PREPFLAG_EOF;
unsigned long ctl;
@ -977,7 +986,7 @@ log_content_close(log_content_ctx_t *ctx, int by_requestor)
* closing the file. The logger_close() call will actually close the
* log. Some logs prefer to use the close callback for logging the
* close event to the log. */
if (content_file_log && ctx->file) {
if (log_content && content_file_log && ctx->file) {
if (logger_submit(content_file_log, ctx->file,
prepflags, NULL) == -1) {
return -1;
@ -987,7 +996,7 @@ log_content_close(log_content_ctx_t *ctx, int by_requestor)
}
ctx->file = NULL;
}
if (content_pcap_log && ctx->pcap) {
if (log_pcap && content_pcap_log && ctx->pcap) {
if (logger_submit(content_pcap_log, ctx->pcap,
prepflags, NULL) == -1) {
return -1;
@ -998,7 +1007,7 @@ log_content_close(log_content_ctx_t *ctx, int by_requestor)
ctx->pcap = NULL;
}
#ifndef WITHOUT_MIRROR
if (content_mirror_log && ctx->mirror) {
if (log_mirror && content_mirror_log && ctx->mirror) {
if (logger_submit(content_mirror_log, ctx->mirror,
prepflags, NULL) == -1) {
return -1;

@ -147,9 +147,16 @@ int log_content_open(log_content_ctx_t *, global_t *,
const struct sockaddr *, socklen_t,
char *, char *, char *, char *,
char *, char *, char *) NONNULL(1,2,3) WUNRES;
int log_content_submit(log_content_ctx_t *, logbuf_t *, int)
NONNULL(1,2) WUNRES;
int log_content_close(log_content_ctx_t *, int) NONNULL(1) WUNRES;
int log_content_submit(log_content_ctx_t *, logbuf_t *, int, int, int
#ifndef WITHOUT_MIRROR
, int
#endif /* !WITHOUT_MIRROR */
) NONNULL(1,2) WUNRES;
int log_content_close(log_content_ctx_t *, int, int, int
#ifndef WITHOUT_MIRROR
, int
#endif /* !WITHOUT_MIRROR */
) NONNULL(1) WUNRES;
int log_content_split_pathspec(const char *, char **,
char **) NONNULL(1,2,3) WUNRES;

@ -783,6 +783,14 @@ clone_global_opts(global_t *global, const char *argv0, tmp_global_opts_t *tmp_gl
fr->split = rule->split;
fr->pass = rule->pass;
fr->block = rule->block;
fr->match = rule->match;
fr->log_connect = rule->log_connect;
fr->log_content = rule->log_content;
fr->log_pcap = rule->log_pcap;
#ifndef WITHOUT_MIRROR
fr->log_mirror = rule->log_mirror;
#endif /* !WITHOUT_MIRROR */
fr->dstip = rule->dstip;
fr->sni = rule->sni;
@ -1129,7 +1137,11 @@ filter_rule_str(filter_rule_t *rule)
#ifndef WITHOUT_USERAUTH
"|%s"
#endif /* !WITHOUT_USERAUTH */
"|%s, action=%s|%s|%s|%s, apply to=%s|%s|%s|%s|%s",
"|%s, action=%s|%s|%s|%s|%s, log=%s|%s|%s"
#ifndef WITHOUT_MIRROR
"|%s"
#endif /* !WITHOUT_MIRROR */
", apply to=%s|%s|%s|%s|%s",
rule->site, rule->exact ? "exact" : "substring", STRORNONE(rule->ip),
#ifndef WITHOUT_USERAUTH
STRORNONE(rule->user), STRORNONE(rule->keyword),
@ -1139,7 +1151,11 @@ filter_rule_str(filter_rule_t *rule)
rule->all_users ? "users" : "",
#endif /* !WITHOUT_USERAUTH */
rule->all_sites ? "sites" : "",
rule->divert ? "divert" : "", rule->split ? "split" : "", rule->pass ? "pass" : "", rule->block ? "block" : "",
rule->divert ? "divert" : "", rule->split ? "split" : "", rule->pass ? "pass" : "", rule->block ? "block" : "", rule->match ? "match" : "",
rule->log_connect ? "connect" : "", rule->log_content ? "content" : "", rule->log_pcap ? "pcap" : "",
#ifndef WITHOUT_MIRROR
rule->log_mirror ? "mirror" : "",
#endif /* !WITHOUT_MIRROR */
rule->dstip ? "dstip" : "", rule->sni ? "sni" : "", rule->cn ? "cn" : "", rule->host ? "host" : "", rule->uri ? "uri" : ""
) < 0) {
goto err;
@ -1175,9 +1191,17 @@ filter_sites_str(filter_site_t *site)
int count = 0;
while (site) {
char *p;
if (asprintf(&p, "%s\n %d: %s (%s%s, action=%s|%s|%s|%s)", STRORNONE(s), count,
if (asprintf(&p, "%s\n %d: %s (%s%s, action=%s|%s|%s|%s|%s, log=%s|%s|%s"
#ifndef WITHOUT_MIRROR
"|%s"
#endif /* !WITHOUT_MIRROR */
")", 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->divert ? "divert" : "", site->split ? "split" : "", site->pass ? "pass" : "", site->block ? "block" : "", site->match ? "match" : "",
site->log_connect ? "connect" : "", site->log_content ? "content" : "", site->log_pcap ? "pcap" : ""
#ifndef WITHOUT_MIRROR
, site->log_mirror ? "mirror" : ""
#endif /* !WITHOUT_MIRROR */
) < 0) {
goto err;
}
@ -2301,7 +2325,7 @@ opts_set_passsite(opts_t *opts, char *value, int line_num)
{
#define MAX_PASSSITE_TOKENS 3
// site[*] [(clientaddr|user|*) [description keyword]]
// site[*] [(clientaddr|user|*) [keyword]]
char *argv[sizeof(char *) * MAX_PASSSITE_TOKENS];
int argc = 0;
char *p, *last = NULL;
@ -2312,7 +2336,8 @@ opts_set_passsite(opts_t *opts, char *value, int line_num)
if (argc < MAX_PASSSITE_TOKENS) {
argv[argc++] = p;
} else {
break;
fprintf(stderr, "Too many arguments in passsite option on line %d\n", line_num);
exit(EXIT_FAILURE);
}
}
@ -2409,7 +2434,11 @@ opts_set_passsite(opts_t *opts, char *value, int line_num)
#ifndef WITHOUT_USERAUTH
"%s|"
#endif /* !WITHOUT_USERAUTH */
"%s, action=%s|%s|%s|%s, , apply to=%s|%s|%s|%s|%s\n",
"%s, action=%s|%s|%s|%s|%s, log=%s|%s|%s"
#ifndef WITHOUT_MIRROR
"|%s"
#endif /* !WITHOUT_MIRROR */
", apply to=%s|%s|%s|%s|%s\n",
rule->site, rule->exact ? "exact" : "substring", STRORNONE(rule->ip),
#ifndef WITHOUT_USERAUTH
STRORNONE(rule->user), STRORNONE(rule->keyword),
@ -2419,7 +2448,11 @@ opts_set_passsite(opts_t *opts, char *value, int line_num)
rule->all_users ? "users" : "",
#endif /* !WITHOUT_USERAUTH */
rule->all_sites ? "sites" : "",
rule->divert ? "divert" : "", rule->split ? "split" : "", rule->pass ? "pass" : "", rule->block ? "block" : "",
rule->divert ? "divert" : "", rule->split ? "split" : "", rule->pass ? "pass" : "", rule->block ? "block" : "", rule->match ? "match" : "",
rule->log_connect ? "connect" : "", rule->log_content ? "content" : "", rule->log_pcap ? "pcap" : "",
#ifndef WITHOUT_MIRROR
rule->log_mirror ? "mirror" : "",
#endif /* !WITHOUT_MIRROR */
rule->dstip ? "dstip" : "", rule->sni ? "sni" : "", rule->cn ? "cn" : "", rule->host ? "host" : "", rule->uri ? "uri" : "");
#endif /* DEBUG_OPTS */
}
@ -2464,25 +2497,25 @@ opts_inc_arg_index(int i, int argc, char *last, int line_num)
}
}
void
opts_set_filter_rule(opts_t *opts, const char *name, char *value, int line_num)
{
#define MAX_FILTER_RULE_TOKENS 10
//(Divert|Split|Pass|Block)
// ([from (
// user (username|*) [desc keyword]|
// ip (clientaddr|*)|
// *)]
// [to (
// sni (servername[*]|*)|
// cn (commonname[*]|*)|
// host (host[*]|*)|
// uri (uri[*]|*)|
// ip (serveraddr|*)|
// *)]
static void
filter_rule_parse(opts_t *opts, const char *name, char *value, int line_num)
{
#define MAX_FILTER_RULE_TOKENS 13
//(Divert|Split|Pass|Block|Match)
// ([from (
// user (username|*) [desc keyword]|
// ip (clientaddr|*)|
// *)]
// [to (
// sni (servername[*]|*)|
// cn (commonname[*]|*)|
// host (host[*]|*)|
// uri (uri[*]|*)|
// ip (serveraddr|*)|
// *)]
// [log ([connect] [content] [pcap] [mirror]|*)]
// |*)
// [(log (connect|content|separate|dir))*]
char *argv[sizeof(char *) * MAX_FILTER_RULE_TOKENS];
int argc = 0;
@ -2494,7 +2527,8 @@ opts_set_filter_rule(opts_t *opts, const char *name, char *value, int line_num)
if (argc < MAX_FILTER_RULE_TOKENS) {
argv[argc++] = p;
} else {
break;
fprintf(stderr, "Too many arguments in filter rule on line %d\n", line_num);
exit(EXIT_FAILURE);
}
}
@ -2509,10 +2543,13 @@ opts_set_filter_rule(opts_t *opts, const char *name, char *value, int line_num)
rule->pass = 1;
else if (equal(name, "Block"))
rule->block = 1;
else if (equal(name, "Match"))
rule->match = 1;
int done_all = 0;
int done_from = 0;
int done_to = 0;
int done_all = 0;
int done_log = 0;
int i = 0;
while (i < argc) {
if (equal(argv[i], "*")) {
@ -2596,11 +2633,16 @@ opts_set_filter_rule(opts_t *opts, const char *name, char *value, int line_num)
i = opts_inc_arg_index(i, argc, argv[i], line_num);
if (equal(argv[i], "sni") || equal(argv[i], "cn") || equal(argv[i], "host") || equal(argv[i], "uri") || equal(argv[i], "ip")) {
rule->sni = equal(argv[i], "sni") ? 1 : 0;
rule->cn = equal(argv[i], "cn") ? 1 : 0;
rule->host = equal(argv[i], "host") ? 1 : 0;
rule->uri = equal(argv[i], "uri") ? 1 : 0;
rule->dstip = equal(argv[i], "ip") ? 1 : 0;
if (equal(argv[i], "sni"))
rule->sni = 1;
else if (equal(argv[i], "cn"))
rule->cn = 1;
else if (equal(argv[i], "host"))
rule->host = 1;
else if (equal(argv[i], "uri"))
rule->uri = 1;
else if (equal(argv[i], "ip"))
rule->dstip = 1;
i = opts_inc_arg_index(i, argc, argv[i], line_num);
opts_set_site(rule, argv[i++], line_num);
@ -2611,13 +2653,59 @@ opts_set_filter_rule(opts_t *opts, const char *name, char *value, int line_num)
i++;
}
else {
fprintf(stderr, "Not enough arguments in filter rule after '%s' on line %d\n", argv[i], line_num);
fprintf(stderr, "Unknown argument in filter rule at '%s' on line %d\n", argv[i], line_num);
exit(EXIT_FAILURE);
}
}
else if (equal(argv[i], "log")) {
if (done_log) {
fprintf(stderr, "Only one 'log' statement allowed on line %d\n", line_num);
exit(EXIT_FAILURE);
}
i = opts_inc_arg_index(i, argc, argv[i], line_num);
if (equal(argv[i], "connect") || equal(argv[i], "content") || equal(argv[i], "pcap")
#ifndef WITHOUT_MIRROR
|| equal(argv[i], "mirror")
#endif /* !WITHOUT_MIRROR */
) {
do {
if (equal(argv[i], "connect"))
rule->log_connect = 1;
else if (equal(argv[i], "content"))
rule->log_content = 1;
else if (equal(argv[i], "pcap"))
rule->log_pcap = 1;
#ifndef WITHOUT_MIRROR
else if (equal(argv[i], "mirror"))
rule->log_mirror = 1;
#endif /* !WITHOUT_MIRROR */
if (++i == argc)
break;
} while (equal(argv[i], "connect") || equal(argv[i], "content") || equal(argv[i], "pcap")
#ifndef WITHOUT_MIRROR
|| equal(argv[i], "mirror")
#endif /* !WITHOUT_MIRROR */
);
done_log = 1;
}
else if (equal(argv[i], "*")) {
rule->log_connect = 1;
rule->log_content = 1;
rule->log_pcap = 1;
#ifndef WITHOUT_MIRROR
rule->log_mirror = 1;
#endif /* !WITHOUT_MIRROR */
i++;
done_log = 1;
}
else {
fprintf(stderr, "Unknown argument in filter rule at '%s' on line %d\n", argv[i], line_num);
exit(EXIT_FAILURE);
}
}
// Not implemented yet
//else if (equal(argv[i], "log")) {
//}
else {
fprintf(stderr, "Unknown argument in filter rule at '%s' on line %d\n", argv[i], line_num);
exit(EXIT_FAILURE);
@ -2648,7 +2736,11 @@ opts_set_filter_rule(opts_t *opts, const char *name, char *value, int line_num)
#ifndef WITHOUT_USERAUTH
"%s|"
#endif /* !WITHOUT_USERAUTH */
"%s, action=%s|%s|%s|%s, , apply to=%s|%s|%s|%s|%s\n",
"%s, action=%s|%s|%s|%s|%s, log=%s|%s|%s"
#ifndef WITHOUT_MIRROR
"|%s"
#endif /* !WITHOUT_MIRROR */
", apply to=%s|%s|%s|%s|%s\n",
rule->site, rule->exact ? "exact" : "substring", STRORNONE(rule->ip),
#ifndef WITHOUT_USERAUTH
STRORNONE(rule->user), STRORNONE(rule->keyword),
@ -2658,7 +2750,11 @@ opts_set_filter_rule(opts_t *opts, const char *name, char *value, int line_num)
rule->all_users ? "users" : "",
#endif /* !WITHOUT_USERAUTH */
rule->all_sites ? "sites" : "",
rule->divert ? "divert" : "", rule->split ? "split" : "", rule->pass ? "pass" : "", rule->block ? "block" : "",
rule->divert ? "divert" : "", rule->split ? "split" : "", rule->pass ? "pass" : "", rule->block ? "block" : "", rule->match ? "match" : "",
rule->log_connect ? "connect" : "", rule->log_content ? "content" : "", rule->log_pcap ? "pcap" : "",
#ifndef WITHOUT_MIRROR
rule->log_mirror ? "mirror" : "",
#endif /* !WITHOUT_MIRROR */
rule->dstip ? "dstip" : "", rule->sni ? "sni" : "", rule->cn ? "cn" : "", rule->host ? "host" : "", rule->uri ? "uri" : "");
#endif /* DEBUG_OPTS */
}
@ -2709,15 +2805,20 @@ opts_add_site(filter_site_t *site, filter_rule_t *rule)
s->all_sites = rule->all_sites;
s->exact = rule->exact;
// Multiple rules can set the action for the same site, hence no 'else'
if (rule->divert)
s->divert = 1;
if (rule->split)
s->split = 1;
if (rule->pass)
s->pass = 1;
if (rule->block)
s->block = 1;
// Multiple rules can set an action for the same site, hence bit-wise OR
s->divert |= rule->divert;
s->split |= rule->split;
s->pass |= rule->pass;
s->block |= rule->block;
s->match |= rule->match;
// Multiple log actions can be set for the same site, hence bit-wise OR
s->log_connect |= rule->log_connect;
s->log_content |= rule->log_content;
s->log_pcap |= rule->log_pcap;
#ifndef WITHOUT_MIRROR
s->log_mirror |= rule->log_mirror;
#endif /* !WITHOUT_MIRROR */
return prepend ? s : site;
}
@ -2942,7 +3043,8 @@ opts_set_userlist(char *value, int line_num, userlist_t **list, const char *list
if (argc < MAX_USERS) {
argv[argc++] = p;
} else {
break;
fprintf(stderr, "Too many arguments in user list, max users allowed %d, on line %d\n", MAX_USERS, line_num);
exit(EXIT_FAILURE);
}
}
@ -3666,12 +3768,12 @@ set_option(opts_t *opts, const char *argv0,
#endif /* DEBUG_OPTS */
} else if (equal(name, "PassSite")) {
opts_set_passsite(opts, value, line_num);
} else if (equal(name, "Split") || equal(name, "Pass") || equal(name, "Block")) {
opts_set_filter_rule(opts, name, value, line_num);
} else if (equal(name, "Split") || equal(name, "Pass") || equal(name, "Block") || equal(name, "Match")) {
filter_rule_parse(opts, name, value, line_num);
} else if (equal(name, "Divert")) {
yes = is_yesno(value);
if (yes == -1) {
opts_set_filter_rule(opts, name, value, line_num);
filter_rule_parse(opts, name, value, line_num);
} else {
yes ? opts_set_divert(opts) : opts_unset_divert(opts);
}
@ -3797,10 +3899,10 @@ get_name_value(char **name, char **value, const char sep, int line_num)
return 0;
}
#define MAX_TOKENS 10
#define MAX_TOKENS 8
static void
load_proxyspec_line(global_t *global, const char *argv0, char *value, char **natengine, tmp_global_opts_t *tmp_global_opts)
load_proxyspec_line(global_t *global, const char *argv0, char *value, char **natengine, int line_num, tmp_global_opts_t *tmp_global_opts)
{
/* Use MAX_TOKENS instead of computing the actual number of tokens in value */
char **argv = malloc(sizeof(char *) * MAX_TOKENS);
@ -3815,7 +3917,8 @@ load_proxyspec_line(global_t *global, const char *argv0, char *value, char **nat
if (argc < MAX_TOKENS) {
argv[argc++] = p;
} else {
break;
fprintf(stderr, "Too many arguments in proxyspec on line %d\n", line_num);
exit(EXIT_FAILURE);
}
}
@ -4003,7 +4106,7 @@ set_global_option(global_t *global, const char *argv0,
goto leave;
}
} else {
load_proxyspec_line(global, argv0, value, natengine, tmp_global_opts);
load_proxyspec_line(global, argv0, value, natengine, *line_num, tmp_global_opts);
}
} else if (equal(name, "ConnIdleTimeout")) {
unsigned int i = atoi(value);

@ -48,14 +48,12 @@
#define STRORDASH(x) (((x)&&*(x))?(x):"-")
#define STRORNONE(x) (((x)&&*(x))?(x):"")
enum filter_action {
FILTER_ACTION_MATCH = -1,
FILTER_ACTION_NONE = 0,
FILTER_ACTION_DIVERT,
FILTER_ACTION_SPLIT,
FILTER_ACTION_PASS,
FILTER_ACTION_BLOCK,
};
#define FILTER_ACTION_NONE 0x0
#define FILTER_ACTION_MATCH 0x2
#define FILTER_ACTION_DIVERT 0x4
#define FILTER_ACTION_SPLIT 0x8
#define FILTER_ACTION_PASS 0x10
#define FILTER_ACTION_BLOCK 0x20
#ifndef WITHOUT_USERAUTH
typedef struct userlist {
@ -177,6 +175,15 @@ typedef struct filter_rule {
unsigned int split : 1;
unsigned int pass : 1;
unsigned int block : 1;
unsigned int match : 1;
// Log action
unsigned int log_connect : 1;
unsigned int log_content : 1;
unsigned int log_pcap : 1;
#ifndef WITHOUT_MIRROR
unsigned int log_mirror : 1;
#endif /* !WITHOUT_MIRROR */
// Conn field to apply filter to
unsigned int dstip : 1; /* 1 to apply to dst ip */
@ -200,6 +207,13 @@ typedef struct filter_site {
unsigned int split : 1;
unsigned int pass : 1;
unsigned int block : 1;
unsigned int match : 1;
unsigned int log_connect : 1;
unsigned int log_content : 1;
unsigned int log_pcap : 1;
#ifndef WITHOUT_MIRROR
unsigned int log_mirror : 1;
#endif /* !WITHOUT_MIRROR */
struct filter_site *next;
} filter_site_t;

@ -42,6 +42,9 @@
static void NONNULL(1)
protohttp_log_connect(pxy_conn_ctx_t *ctx)
{
if (!ctx->log_connect)
return;
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
char *msg;
@ -429,8 +432,7 @@ protossl_match_uri(pxy_conn_ctx_t *ctx, filter_site_t *site)
return 0;
}
static enum filter_action protohttp_filter(pxy_conn_ctx_t *, filter_list_t *) NONNULL(1,2);
static enum filter_action
static unsigned char NONNULL(1,2)
protohttp_filter(pxy_conn_ctx_t *ctx, filter_list_t *list)
{
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
@ -500,15 +502,16 @@ protohttp_filter(pxy_conn_ctx_t *ctx, filter_list_t *list)
static int
protohttp_apply_filter(pxy_conn_ctx_t *ctx)
{
enum filter_action action;
unsigned char action;
if ((action = pxyconn_filter(ctx, protohttp_filter))) {
if (action == FILTER_ACTION_BLOCK) {
if (action & FILTER_ACTION_BLOCK) {
pxy_conn_term(ctx, 1);
return 1;
}
else if (action == FILTER_ACTION_DIVERT || action == FILTER_ACTION_SPLIT || action == FILTER_ACTION_PASS) {
else if (action & (FILTER_ACTION_DIVERT | FILTER_ACTION_SPLIT | FILTER_ACTION_PASS)) {
log_err_level_printf(LOG_WARNING, "HTTP filter cannot take divert, split, or pass action\n");
}
//else { /* FILTER_ACTION_MATCH */ }
}
return 0;
}

@ -96,7 +96,7 @@ protossl_log_ssl_error(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
ERR_GET_FUNC(sslerr), STRORDASH(ERR_func_error_string(sslerr)));
}
}
if (ctx->spec->opts->filter_rules && !ctx->pass) {
if (ctx->spec->opts->filter && !ctx->pass) {
log_err_level_printf(LOG_WARNING, "Closing on ssl error without filter match: %s:%s, %s:%s, "
#ifndef WITHOUT_USERAUTH
"%s, %s, "
@ -676,8 +676,7 @@ protossl_match_cn(pxy_conn_ctx_t *ctx, filter_site_t *site)
return 0;
}
static enum filter_action protossl_filter(pxy_conn_ctx_t *, filter_list_t *) NONNULL(1,2);
static enum filter_action
static unsigned char NONNULL(1,2)
protossl_filter(pxy_conn_ctx_t *ctx, filter_list_t *list)
{
if (ctx->sslctx->sni) {
@ -745,22 +744,23 @@ protossl_filter(pxy_conn_ctx_t *ctx, filter_list_t *list)
static int
protossl_apply_filter(pxy_conn_ctx_t *ctx)
{
enum filter_action action;
unsigned char action;
if ((action = pxyconn_filter(ctx, protossl_filter))) {
if (action == FILTER_ACTION_DIVERT) {
if (action & FILTER_ACTION_DIVERT) {
ctx->divert = 1;
}
else if (action == FILTER_ACTION_SPLIT) {
else if (action & FILTER_ACTION_SPLIT) {
ctx->divert = 0;
}
else if (action == FILTER_ACTION_PASS) {
else if (action & FILTER_ACTION_PASS) {
ctx->pass = 1;
return 1;
}
else if (action == FILTER_ACTION_BLOCK) {
else if (action & FILTER_ACTION_BLOCK) {
pxy_conn_term(ctx, 1);
return 1;
}
//else { /* FILTER_ACTION_MATCH */ }
}
return 0;
}
@ -800,7 +800,7 @@ protossl_srcssl_create(pxy_conn_ctx_t *ctx, SSL *origssl)
protossl_debug_crt(cert->crt);
}
if (WANT_CONNECT_LOG(ctx) || ctx->spec->opts->filter_rules) {
if (WANT_CONNECT_LOG(ctx) || ctx->spec->opts->filter) {
ctx->sslctx->ssl_names = ssl_x509_names_to_str(ctx->sslctx->origcrt ?
ctx->sslctx->origcrt :
cert->crt);
@ -906,7 +906,7 @@ protossl_ossl_servername_cb(SSL *ssl, UNUSED int *al, void *arg)
"certificate:\n");
protossl_debug_crt(newcrt);
}
if (WANT_CONNECT_LOG(ctx) || ctx->spec->opts->filter_rules) {
if (WANT_CONNECT_LOG(ctx) || ctx->spec->opts->filter) {
if (ctx->sslctx->ssl_names) {
free(ctx->sslctx->ssl_names);
}

@ -526,9 +526,7 @@ prototcp_filter_match(pxy_conn_ctx_t *ctx, filter_site_t *site)
return 0;
}
// The editor complains without this forward declaration for the NONNULL attribute
static enum filter_action prototcp_dsthost_filter(pxy_conn_ctx_t *, filter_list_t *) NONNULL(1,2);
static enum filter_action
static unsigned char NONNULL(1,2)
prototcp_dsthost_filter(pxy_conn_ctx_t *ctx, filter_list_t *list)
{
if (ctx->dsthost_str) {
@ -550,23 +548,24 @@ prototcp_dsthost_filter(pxy_conn_ctx_t *ctx, filter_list_t *list)
int
prototcp_apply_filter(pxy_conn_ctx_t *ctx)
{
enum filter_action action;
unsigned char action;
if ((action = pxyconn_filter(ctx, prototcp_dsthost_filter))) {
if (action == FILTER_ACTION_DIVERT) {
if (action & FILTER_ACTION_DIVERT) {
ctx->divert = 1;
}
else if (action == FILTER_ACTION_SPLIT) {
else if (action & FILTER_ACTION_SPLIT) {
ctx->divert = 0;
}
else if (action == FILTER_ACTION_PASS) {
else if (action & FILTER_ACTION_PASS) {
protopassthrough_engage(ctx);
ctx->pass = 1;
return 1;
}
else if (action == FILTER_ACTION_BLOCK) {
else if (action & FILTER_ACTION_BLOCK) {
pxy_conn_term(ctx, 1);
return 1;
}
//else { /* FILTER_ACTION_MATCH */ }
}
return 0;
}

@ -172,6 +172,16 @@ proxy_conn_ctx_new(evutil_socket_t fd,
ctx->spec = spec;
ctx->divert = spec->opts->divert;
// Enable all logging for conn if proxyspec does not have any filter
if (!spec->opts->filter) {
ctx->log_connect = 1;
ctx->log_content = 1;
ctx->log_pcap = 1;
#ifndef WITHOUT_MIRROR
ctx->log_mirror = 1;
#endif /* !WITHOUT_MIRROR */
}
ctx->proto = proxy_setup_proto(ctx);
if (ctx->proto == PROTO_ERROR) {
free(ctx);

@ -319,7 +319,11 @@ pxy_conn_ctx_free(pxy_conn_ctx_t *ctx, int by_requestor)
log_finest("ENTER");
if (WANT_CONTENT_LOG(ctx)) {
if (log_content_close(&ctx->logctx, by_requestor) == -1) {
if (log_content_close(&ctx->logctx, by_requestor, ctx->log_content, ctx->log_pcap
#ifndef WITHOUT_MIRROR
, ctx->log_mirror
#endif /* !WITHOUT_MIRROR */
) == -1) {
log_err_level_printf(LOG_WARNING, "Content log close failed\n");
}
}
@ -463,6 +467,9 @@ pxy_conn_term(pxy_conn_ctx_t *ctx, int by_requestor)
void
pxy_log_connect_nonhttp(pxy_conn_ctx_t *ctx)
{
if (!ctx->log_connect)
return;
char *msg;
#ifdef HAVE_LOCAL_PROCINFO
char *lpi = NULL;
@ -573,9 +580,17 @@ out:
return;
}
int
static int NONNULL(1)
pxy_log_content_inbuf(pxy_conn_ctx_t *ctx, struct evbuffer *inbuf, int req)
{
if (!ctx->log_content && !ctx->log_pcap
#ifndef WITHOUT_MIRROR
&& !ctx->log_mirror
#endif /* !WITHOUT_MIRROR */
) {
return 0;
}
size_t sz = evbuffer_get_length(inbuf);
unsigned char *buf = malloc(sz);
if (!buf) {
@ -594,7 +609,11 @@ pxy_log_content_inbuf(pxy_conn_ctx_t *ctx, struct evbuffer *inbuf, int req)
}
memcpy(lb->buf, buf, lb->sz);
free(buf);
if (log_content_submit(&ctx->logctx, lb, req) == -1) {
if (log_content_submit(&ctx->logctx, lb, req, ctx->log_content, ctx->log_pcap
#ifndef WITHOUT_MIRROR
, ctx->log_mirror
#endif /* !WITHOUT_MIRROR */
) == -1) {
logbuf_free(lb);
log_err_level_printf(LOG_WARNING, "Content log submission failed\n");
return -1;
@ -1511,7 +1530,7 @@ pxy_bev_writecb_child(struct bufferevent *bev, void *arg)
}
}
int
static int NONNULL(1,3)
pxy_bev_eventcb_postexec_logging_and_stats(struct bufferevent *bev, short events, pxy_conn_ctx_t *ctx)
{
if (ctx->term || ctx->enomem) {
@ -1973,35 +1992,61 @@ pxy_userauth(pxy_conn_ctx_t *ctx)
}
#endif /* !WITHOUT_USERAUTH */
enum filter_action
unsigned char
pxyconn_set_filter_action(pxy_conn_ctx_t *ctx, filter_site_t *site)
{
unsigned char action = FILTER_ACTION_NONE;
if (site->divert) {
log_err_level_printf(LOG_INFO, "Site filter divert action for %s\n", site->site);
return FILTER_ACTION_DIVERT;
action = FILTER_ACTION_DIVERT;
}
else if (site->split) {
log_err_level_printf(LOG_INFO, "Site filter split action for %s\n", site->site);
return FILTER_ACTION_SPLIT;
action = FILTER_ACTION_SPLIT;
}
else if (site->pass) {
// Ignore pass action if already in passthrough mode
if (!ctx->pass) {
log_err_level_printf(LOG_INFO, "Site filter pass action for %s\n", site->site);
return FILTER_ACTION_PASS;
action = FILTER_ACTION_PASS;
}
}
else if (site->block) {
log_err_level_printf(LOG_INFO, "Site filter block action for %s\n", site->site);
return FILTER_ACTION_BLOCK;
action = FILTER_ACTION_BLOCK;
}
else if (site->match) {
log_err_level_printf(LOG_INFO, "Site filter match action for %s\n", site->site);
action = FILTER_ACTION_MATCH;
}
// Multiple log actions can be defined, hence no 'else'
// Log actions can only enable logging not disable, hence set to 1
if (site->log_connect) {
log_err_level_printf(LOG_INFO, "Site filter connect log for %s\n", site->site);
ctx->log_connect = 1;
}
return FILTER_ACTION_MATCH;
if (site->log_content) {
log_err_level_printf(LOG_INFO, "Site filter content log for %s\n", site->site);
ctx->log_content = 1;
}
if (site->log_pcap) {
log_err_level_printf(LOG_INFO, "Site filter pcap log for %s\n", site->site);
ctx->log_pcap = 1;
}
#ifndef WITHOUT_MIRROR
if (site->log_mirror) {
log_err_level_printf(LOG_INFO, "Site filter mirror log for %s\n", site->site);
ctx->log_mirror = 1;
}
#endif /* !WITHOUT_MIRROR */
return action;
}
enum filter_action
unsigned char
pxyconn_filter(pxy_conn_ctx_t *ctx, proto_filter_func_t filtercb)
{
enum filter_action action = FILTER_ACTION_NONE;
unsigned char action = FILTER_ACTION_NONE;
filter_t *filter = ctx->spec->opts->filter;
if (filter) {

@ -78,7 +78,7 @@ typedef void (*proto_classify_user_func_t)(pxy_conn_ctx_t *);
typedef void (*child_connect_func_t)(pxy_conn_child_ctx_t *);
typedef void (*child_proto_free_func_t)(pxy_conn_child_ctx_t *);
typedef enum filter_action (*proto_filter_func_t)(pxy_conn_ctx_t *, filter_list_t *);
typedef unsigned char (*proto_filter_func_t)(pxy_conn_ctx_t *, filter_list_t *);
/*
* Proxy connection context state, describes a proxy connection
@ -320,6 +320,17 @@ struct pxy_conn_ctx {
unsigned int divert : 1; /* 1 to divert conn */
unsigned int pass : 1; /* 1 to pass conn through */
// Enable logging of conn for specific logger types
// Global logging options should be configured for these to write logs
// Default to all logging if no filter rules defined in proxyspec
// Otherwise, logging is disabled, so filter rules should enable each log action specifically
unsigned int log_connect : 1;
unsigned int log_content : 1;
unsigned int log_pcap : 1;
#ifndef WITHOUT_MIRROR
unsigned int log_mirror : 1;
#endif /* !WITHOUT_MIRROR */
#ifdef HAVE_LOCAL_PROCINFO
/* local process information */
pxy_conn_lproc_desc_t lproc;
@ -372,7 +383,6 @@ int pxy_prepare_logging_local_procinfo(pxy_conn_ctx_t *) NONNULL(1);
void pxy_log_connect_src(pxy_conn_ctx_t *) NONNULL(1);
void pxy_log_connect_srvdst(pxy_conn_ctx_t *) NONNULL(1);
int pxy_log_content_inbuf(pxy_conn_ctx_t *, struct evbuffer *, int) NONNULL(1);
void pxy_log_connect_nonhttp(pxy_conn_ctx_t *) NONNULL(1);
void pxy_log_dbg_evbuf_info(pxy_conn_ctx_t *, pxy_conn_desc_t *, pxy_conn_desc_t *) NONNULL(1,2,3);
@ -403,7 +413,6 @@ void pxy_conn_free_children(pxy_conn_ctx_t *) NONNULL(1);
int pxy_setup_child_listener(pxy_conn_ctx_t *) NONNULL(1);
int pxy_bev_readcb_preexec_logging_and_stats(struct bufferevent *, pxy_conn_ctx_t *) NONNULL(1,2);
int pxy_bev_eventcb_postexec_logging_and_stats(struct bufferevent *, short , pxy_conn_ctx_t *) NONNULL(1,3);
void pxy_bev_readcb(struct bufferevent *, void *);
void pxy_bev_writecb(struct bufferevent *, void *);
@ -426,8 +435,8 @@ 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 */
enum filter_action pxyconn_set_filter_action(pxy_conn_ctx_t *, filter_site_t *) NONNULL(1,2);
enum filter_action pxyconn_filter(pxy_conn_ctx_t *, proto_filter_func_t) NONNULL(1);
unsigned char pxyconn_set_filter_action(pxy_conn_ctx_t *, filter_site_t *) NONNULL(1,2);
unsigned char pxyconn_filter(pxy_conn_ctx_t *, proto_filter_func_t) NONNULL(1);
void pxy_conn_setup(evutil_socket_t, struct sockaddr *, int,
pxy_thrmgr_ctx_t *, proxyspec_t *, global_t *,
evutil_socket_t)

@ -307,7 +307,7 @@ are diverted to listening programs.
These user control lists can be defined globally or per-proxyspec.
.SH Filtering rules
.LP
SSLproxy can divert, split, pass, or block connections based on filtering
SSLproxy can divert, split, pass, block, or match connections based on filtering
rules. Filtering rules can be defined globally or per-proxyspec.
.LP
- Divert action diverts packets to listening program, allowing SSL inspection
@ -318,21 +318,24 @@ allowing content logging of packets
- Pass action passes the connection through by engaging passthrough mode,
effectively disabling SSL inspection and content logging of packets
- Block action terminates the connection
- Match action is used to specify log actions for matching connections without
changing their filter actions
.LP
The syntax of filtering rules is as follows:
(Divert|Split|Pass|Block)
(Divert|Split|Pass|Block|Match)
([from (
user (username|*) [desc keyword]|
ip (clientaddr|*)|
*)]
user (username|*) [desc keyword]|
ip (clientaddr|*)|
*)]
[to (
sni (servername[*]|*)|
cn (commonname[*]|*)|
host (host[*]|*)|
uri (uri[*]|*)|
ip (serveraddr|*)|
*)]
sni (servername[*]|*)|
cn (commonname[*]|*)|
host (host[*]|*)|
uri (uri[*]|*)|
ip (serveraddr|*)|
*)]
[log ([connect] [content] [pcap] [mirror]|*)]
|*)
.LP
The definition of which connections the rule action will be applied to is
@ -348,18 +351,40 @@ cn, and HTTP type of rules use host and uri site fields.
- The proxyspec handling the connection defines the protocol filter for the
connection.
.LP
If and how a connection should be logged is specified using the log part of
filtering rules. connect enables logging connection information to connect
log file, content enables logging packet contents to content log file,
pcap enables writing packets to pcap files, and mirror enables mirroring
packets to mirror interfaces or targets.
.LP
For example, if the following rules are defined in a structured HTTPS proxyspec,
.LP
Split from user soner desc notebook to sni example.com
Pass from user soner desc android to cn .fbcdn.net*
Split from user soner desc notebook to sni example.com log content
Pass from user soner desc android to cn .fbcdn.net*
.LP
The first filtering rule above splits but does not divert HTTPS connections
from the user soner who has logged in with a description containing the keyword
notebook to SSL sites with the SNI of example.com. The second rule passes
through HTTPS connections from the user soner who has logged in with a
description containing the keyword android to SSL sites with the Common Names
containing the substring .fbcdn.net anywhere in it (notice the asterisk at the
end). Note that the second example is a filtering rule you can use to resolve
notebook to SSL sites with the SNI of example.com. Also, the rule specifies
that the packet contents of the matching connection be written to content log
file configured globally.
.LP
The second rule passes through HTTPS connections from the user soner who has
logged in with a description containing the keyword android to SSL sites with
the Common Names containing the substring .fbcdn.net anywhere in it (notice
the asterisk at the end). Since connection contents cannot be written to log
files in passthrough mode, the rule does not specify any log action.
.LP
The default filter action is Divert. So, if those are the only filtering rules
in that proxyspec, the other connections are diverted to the listening program
specified in that proxyspec, without writing any logs.
.LP
If you want to enable, say, connect logging for the other connections handled
by that proxyspec, without changing their default Divert filter action, you
can add a third filtering rule to that proxyspec:
.LP
Match * log connect
.LP
Note that the second example above is a filtering rule you can use to resolve
one of the certificate issues preventing the Facebook application on Android
smartphones to connect to the Internet behind sslproxy.
.LP
@ -384,10 +409,21 @@ In terms of possible filter actions,
- HTTP filter rules can take the block action, but not divert, split, or pass
actions.
.LP
Log actions do not configure any loggers. Global content loggers for
respective log actions should have been configured for those log actions to
have any effect.
.LP
If no filter rules are defined for a proxyspec, all logging actions for that
proxyspec are enabled. Otherwise, all log actions are disabled, and filtering
rules should enable them specifically. Note that if logging is disabled by
filtering rules, the loggers create the log files, but they remain empty.
.LP
You can append an asterisk * to site field of filtering rules for substring
matching. Otherwise, the filter searches for an exact match with the site field
in the rule.
.LP
The order of from, to, and log parts is not important.
.LP
If the UserAuth option is disabled, only client IP addresses can be used in
the from part of filtering rules.
.SH Excluding sites from SSL inspection
@ -410,7 +446,7 @@ on each line. PassSite rules can search for exact or substring matches.
Logging options include traditional SSLproxy connect and content log files as
well as PCAP files and mirroring decrypted traffic to a network interface.
Additionally, certificates, master secrets and local process information can be
logged.
logged. Filtering rules can selectively modify connection logging.
.SH OPTIONS
.TP
.B \-a \fIpemfile\fP
@ -591,6 +627,9 @@ from the client, since the connection from the client cannot easily be retried.
Specifically, \fB-P\fP does not currently work for clients that do not accept
forged certificates.
.TP
.B \-Q
Quit after loading and testing configuration.
.TP
.B \-q \fIcrlurl\fP
Set CRL distribution point (CDP) \fIcrlurl\fP on forged leaf certificates.
Some clients, such as some .NET applications, reject certificates that do not

@ -274,18 +274,19 @@ PassUsers admin
# Filter rules
#
# (Divert|Split|Pass|Block)
# ([from (
# user (username|*) [desc keyword]|
# ip (clientaddr|*)|
# *)]
# [to (
# sni (servername[*]|*)|
# cn (commonname[*]|*)|
# host (host[*]|*)|
# uri (uri[*]|*)|
# ip (serveraddr|*)|
# *)]
# (Divert|Split|Pass|Block|Match)
# ([from (
# user (username|*) [desc keyword]|
# ip (clientaddr|*)|
# *)]
# [to (
# sni (servername[*]|*)|
# cn (commonname[*]|*)|
# host (host[*]|*)|
# uri (uri[*]|*)|
# ip (serveraddr|*)|
# *)]
# [log ([connect] [content] [pcap] [mirror]|*)]
# |*)
#
# Filter rules using host or uri can only take block action,
@ -295,7 +296,7 @@ PassUsers admin
# Pass to cn example.com
#
#Divert from ip 192.168.0.1 to sni example.com
#Split from user soner to sni example.com
#Split from user soner to sni example.com log content
#Pass from user * desc android to sni *.google.com
#Block from user soner desc android to cn .fbcdn.net*

@ -332,18 +332,19 @@ Block filtering rule terminates the connection.
The syntax of filtering rules is as follows:
.br
(Divert|Split|Pass|Block)
([from (
user (username|*) [desc keyword]|
ip (clientaddr|*)|
*)]
[to (
sni (servername[*]|*)|
cn (commonname[*]|*)|
host (host[*]|*)|
uri (uri[*]|*)|
ip (serveraddr|*)|
*)]
(Divert|Split|Pass|Block|Match)
([from (
user (username|*) [desc keyword]|
ip (clientaddr|*)|
*)]
[to (
sni (servername[*]|*)|
cn (commonname[*]|*)|
host (host[*]|*)|
uri (uri[*]|*)|
ip (serveraddr|*)|
*)]
[log ([connect] [content] [pcap] [mirror]|*)]
|*)
.br

@ -700,9 +700,9 @@ 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|, apply to=|sni|cn||"), "failed parsing passite example.com: %s", ps);
fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, ip=, user=, keyword=, all=conns||, action=||pass||, log=|||, apply to=|sni|cn||"), "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|, apply to=|sni|cn||"), "failed parsing passite example.com: %s", ps);
fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, ip=, all=conns|, action=||pass||, log=|||, apply to=|sni|cn||"), "failed parsing passite example.com: %s", ps);
#endif /* WITHOUT_USERAUTH */
free(ps);
@ -730,9 +730,9 @@ 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|, apply to=|sni|cn||"), "failed parsing passite example.com 192.168.0.1: %s", ps);
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||"), "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|, apply to=|sni|cn||"), "failed parsing passite example.com 192.168.0.1: %s", ps);
fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, ip=192.168.0.1, all=|, action=||pass||, log=|||, apply to=|sni|cn||"), "failed parsing passite example.com 192.168.0.1: %s", ps);
#endif /* !WITHOUT_USERAUTH */
free(ps);
@ -760,7 +760,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|, apply to=|sni|cn||"), "failed parsing passite example.com root: %s", ps);
fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, ip=, user=root, keyword=, all=||, action=||pass||, log=|||, apply to=|sni|cn||"), "failed parsing passite example.com root: %s", ps);
free(ps);
opts_free(opts);
@ -787,7 +787,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|, apply to=|sni|cn||"), "failed parsing passite *.google.com * android: %s", ps);
fail_unless(!strcmp(ps, "filter rule 0: site=*.google.com, exact, ip=, user=, keyword=android, all=|users|, action=||pass||, log=|||, apply to=|sni|cn||"), "failed parsing passite *.google.com * android: %s", ps);
free(ps);
opts_free(opts);
@ -842,17 +842,17 @@ 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=*.google.com, exact, ip=, user=, keyword=android, all=|users|, action=||pass|, apply to=|sni|cn||\n"
"filter rule 1: site=example.com, exact, ip=, user=root, keyword=, all=||, action=||pass|, apply to=|sni|cn||\n"
"filter rule 2: site=example.com, exact, ip=192.168.0.1, user=, keyword=, all=||, action=||pass|, apply to=|sni|cn||\n"
"filter rule 3: site=example.com, exact, ip=, user=, keyword=, all=|users|, action=||pass|, apply to=|sni|cn||\n"
"filter rule 4: site=example.com, exact, ip=, user=, keyword=, all=conns||, action=||pass|, apply to=|sni|cn||"),
fail_unless(!strcmp(ps, "filter rule 0: site=*.google.com, exact, ip=, user=, keyword=android, all=|users|, action=||pass||, log=|||, apply to=|sni|cn||\n"
"filter rule 1: site=example.com, exact, ip=, user=root, keyword=, all=||, action=||pass||, log=|||, apply to=|sni|cn||\n"
"filter rule 2: site=example.com, exact, ip=192.168.0.1, user=, keyword=, all=||, action=||pass||, log=|||, apply to=|sni|cn||\n"
"filter rule 3: site=example.com, exact, ip=, user=, keyword=, all=|users|, action=||pass||, log=|||, apply to=|sni|cn||\n"
"filter rule 4: site=example.com, exact, ip=, user=, keyword=, all=conns||, action=||pass||, log=|||, apply to=|sni|cn||"),
"failed parsing multiple passites: %s", ps);
#else /* WITHOUT_USERAUTH */
fail_unless(!opts->filter_rules->next->next->next, "next->next->next set");
fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, ip=192.168.0.1, all=|, action=||pass|, apply to=|sni|cn||\n"
"filter rule 1: site=example.com, exact, ip=, all=conns|, action=||pass|, apply to=|sni|cn||\n"
"filter rule 2: site=example.com, exact, ip=, all=conns|, action=||pass|, apply to=|sni|cn||"),
fail_unless(!strcmp(ps, "filter rule 0: site=example.com, exact, ip=192.168.0.1, all=|, action=||pass||, log=|||, apply to=|sni|cn||\n"
"filter rule 1: site=example.com, exact, ip=, all=conns|, action=||pass||, log=|||, apply to=|sni|cn||\n"
"filter rule 2: site=example.com, exact, ip=, all=conns|, action=||pass||, log=|||, apply to=|sni|cn||"),
"failed parsing multiple passites: %s", ps);
#endif /* WITHOUT_USERAUTH */
free(ps);

Loading…
Cancel
Save