Add deferred pass and block actions

We should defer pass and/or block actions as long as possible, because a
higher precedence rule in SSL filter should be able to override (cancel)
deferred pass and block actions taken by a lower precedence rule in Dst
Host filter. And in HTTP filter the same applies to deferred block
actions taken by Dst Host and SSL filters.

Also, thanks to this new deferred actions, now HTTP filter can keep
enabled divert and split modes. In other words, a higher precedence HTTP
filter rule can cancel a deferred block action set by a lower precedence
rule earlier, which was not possible before without deferred actions and
rule precedence.

And other improvements.
pull/48/head
Soner Tari 3 years ago
parent 11884271fd
commit ac3607a841

@ -383,9 +383,9 @@ In terms of possible filter actions,
- Dst Host filtering rules can take all of the filter and log actions.
- SSL filtering rules can take all of the filter and log actions.
- HTTP filtering rules take the match and block filter actions, but not the
divert, split, or pass actions. Also, HTTP filtering rules can only disable
logging.
- HTTP filtering rules can take match and block filter actions, can keep
enabled divert and split modes, but cannot take pass action. Also, HTTP
filtering rules can only disable logging.
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.

@ -159,6 +159,10 @@ protoautossl_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
}
#endif /* !WITHOUT_USERAUTH */
if (pxyconn_apply_deferred_block_action(ctx)) {
return;
}
if (autossl_ctx->clienthello_search) {
if (protoautossl_peek_and_upgrade(ctx) != 0) {
return;

@ -387,7 +387,7 @@ protohttp_filter_request_header_line(const char *line, protohttp_ctx_t *http_ctx
}
static int NONNULL(1,2)
protossl_match_host(pxy_conn_ctx_t *ctx, filter_site_t *site)
protohttp_match_host(pxy_conn_ctx_t *ctx, filter_site_t *site)
{
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
@ -415,7 +415,7 @@ protossl_match_host(pxy_conn_ctx_t *ctx, filter_site_t *site)
}
static int NONNULL(1,2)
protossl_match_uri(pxy_conn_ctx_t *ctx, filter_site_t *site)
protohttp_match_uri(pxy_conn_ctx_t *ctx, filter_site_t *site)
{
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
@ -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 (protossl_match_host(ctx, site)) {
if (protohttp_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
@ -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 (protossl_match_uri(ctx, site)) {
if (protohttp_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
@ -515,15 +515,32 @@ protohttp_apply_filter(pxy_conn_ctx_t *ctx)
int rv = 0;
unsigned int action;
if ((action = pxyconn_filter(ctx, protohttp_filter))) {
if (action & FILTER_ACTION_BLOCK) {
ctx->filter_precedence = action & FILTER_PRECEDENCE;
ctx->filter_precedence = action & FILTER_PRECEDENCE;
if (action & FILTER_ACTION_DIVERT) {
if (ctx->divert) {
// Override any deferred block action, if already in divert mode (keep divert mode)
ctx->deferred_action = FILTER_ACTION_NONE;
} else {
log_err_level_printf(LOG_WARNING, "HTTP filter cannot enable divert mode\n");
}
}
else if (action & FILTER_ACTION_SPLIT) {
if (!ctx->divert) {
// Override any deferred block action, if already in split mode (keep split mode)
ctx->deferred_action = FILTER_ACTION_NONE;
} else {
log_err_level_printf(LOG_WARNING, "HTTP filter cannot enable split mode\n");
}
}
else if (action & FILTER_ACTION_PASS) {
log_err_level_printf(LOG_WARNING, "HTTP filter cannot take pass action\n");
}
else if (action & FILTER_ACTION_BLOCK) {
ctx->deferred_action = FILTER_ACTION_NONE;
pxy_conn_term(ctx, 1);
rv = 1;
}
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 actions, any log actions are ignored too\n");
goto out;
}
//else { /* FILTER_ACTION_MATCH */ }
if (action & (FILTER_LOG_CONTENT | FILTER_LOG_PCAP
@ -532,9 +549,9 @@ protohttp_apply_filter(pxy_conn_ctx_t *ctx)
#endif /* !WITHOUT_MIRROR */
)) {
#ifndef WITHOUT_MIRROR
log_err_level_printf(LOG_WARNING, "HTTP filter cannot enable content, pcap, and mirror logging\n");
log_err_level_printf(LOG_WARNING, "HTTP filter cannot enable content, pcap, or mirror logging\n");
#else /* !WITHOUT_MIRROR */
log_err_level_printf(LOG_WARNING, "HTTP filter cannot enable content and pcap logging\n");
log_err_level_printf(LOG_WARNING, "HTTP filter cannot enable content or pcap logging\n");
#endif /* WITHOUT_MIRROR */
}
@ -555,11 +572,16 @@ protohttp_apply_filter(pxy_conn_ctx_t *ctx)
ctx->log_mirror = 0;
#endif /* !WITHOUT_MIRROR */
}
out:
// Cannot defer block action any longer
// Match action should not override any deferred action, hence no 'else if'
if (pxyconn_apply_deferred_block_action(ctx))
rv = 1;
return rv;
}
static void NONNULL(1,2,3,5)
static int WUNRES NONNULL(1,2,3,5)
protohttp_filter_request_header(struct evbuffer *inbuf, struct evbuffer *outbuf, protohttp_ctx_t *http_ctx, enum conn_type type, pxy_conn_ctx_t *ctx)
{
char *line;
@ -577,7 +599,7 @@ protohttp_filter_request_header(struct evbuffer *inbuf, struct evbuffer *outbuf,
} else {
log_finer_va("REMOVE= %s", line);
if (ctx->enomem) {
return;
return -1;
}
}
free(line);
@ -590,25 +612,28 @@ protohttp_filter_request_header(struct evbuffer *inbuf, struct evbuffer *outbuf,
}
if (http_ctx->seen_req_header) {
if (protohttp_apply_filter(ctx)) {
return;
}
if (type == CONN_TYPE_PARENT) {
if (protohttp_apply_filter(ctx)) {
return -1;
}
/* request header complete */
if ((type == CONN_TYPE_PARENT) && ctx->spec->opts->deny_ocsp) {
protohttp_ocsp_deny(ctx, http_ctx);
/* request header complete */
if (ctx->spec->opts->deny_ocsp) {
protohttp_ocsp_deny(ctx, http_ctx);
}
}
if (ctx->enomem) {
return;
return -1;
}
/* no data left after parsing headers? */
if (evbuffer_get_length(inbuf) == 0) {
return;
return 0;
}
evbuffer_add_buffer(outbuf, inbuf);
}
return 0;
}
#ifndef WITHOUT_USERAUTH
@ -798,8 +823,7 @@ protohttp_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
/* request header munging */
if (!http_ctx->seen_req_header) {
log_finest_va("HTTP Request Header, size=%zu", evbuffer_get_length(inbuf));
protohttp_filter_request_header(inbuf, outbuf, http_ctx, ctx->type, ctx);
if (ctx->enomem) {
if (protohttp_filter_request_header(inbuf, outbuf, http_ctx, ctx->type, ctx) == -1) {
return;
}
} else {
@ -986,8 +1010,7 @@ protohttp_bev_readcb_src_child(struct bufferevent *bev, pxy_conn_child_ctx_t *ct
if (!http_ctx->seen_req_header) {
log_finest_va("HTTP Request Header, size=%zu", evbuffer_get_length(inbuf));
// @todo Just remove SSLproxy line, do not filter request on the server side?
protohttp_filter_request_header(inbuf, outbuf, http_ctx, ctx->type, ctx->conn);
if (ctx->conn->enomem) {
if (protohttp_filter_request_header(inbuf, outbuf, http_ctx, ctx->type, ctx->conn) == -1) {
return;
}
} else {

@ -149,6 +149,10 @@ protopassthrough_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
}
#endif /* !WITHOUT_USERAUTH */
if (pxyconn_apply_deferred_block_action(ctx)) {
return;
}
evbuffer_add_buffer(bufferevent_get_output(ctx->srvdst.bev), bufferevent_get_input(bev));
pxy_try_set_watermark(bev, ctx, ctx->srvdst.bev);
}

@ -760,18 +760,24 @@ protossl_apply_filter(pxy_conn_ctx_t *ctx)
ctx->filter_precedence = action & FILTER_PRECEDENCE;
if (action & FILTER_ACTION_DIVERT) {
ctx->deferred_action = FILTER_ACTION_NONE;
ctx->divert = 1;
}
else if (action & FILTER_ACTION_SPLIT) {
ctx->deferred_action = FILTER_ACTION_NONE;
ctx->divert = 0;
}
else if (action & FILTER_ACTION_PASS) {
ctx->deferred_action = FILTER_ACTION_NONE;
ctx->pass = 1;
rv = 1;
}
else if (action & FILTER_ACTION_BLOCK) {
pxy_conn_term(ctx, 1);
rv = 1;
// Always defer block action, the only action we can defer from this point on
// This block action should override any deferred pass action,
// because the current rule must have a higher precedence
log_fine("Deferring block action");
ctx->deferred_action = FILTER_ACTION_BLOCK;
}
//else { /* FILTER_ACTION_MATCH */ }
@ -784,6 +790,12 @@ protossl_apply_filter(pxy_conn_ctx_t *ctx)
ctx->log_mirror = !!(action & FILTER_LOG_MIRROR);
#endif /* !WITHOUT_MIRROR */
}
// Cannot defer pass action any longer
// Match action should not override pass action, hence no 'else if'
if (pxyconn_apply_deferred_pass_action(ctx))
rv = 1;
return rv;
}
@ -830,6 +842,8 @@ protossl_srcssl_create(pxy_conn_ctx_t *ctx, SSL *origssl)
ctx->enomem = 1;
}
// Defers any block action until HTTP filter application
// or until the first src readcb of non-http protos
if (protossl_apply_filter(ctx)) {
cert_free(cert);
return NULL;
@ -1648,7 +1662,8 @@ protossl_bev_eventcb_connected_srvdst(UNUSED struct bufferevent *bev, pxy_conn_c
}
#endif /* !WITHOUT_USERAUTH */
if (prototcp_apply_filter(ctx)) {
// Defer any pass or block action until SSL filter application below
if (prototcp_apply_filter(ctx, FILTER_ACTION_PASS | FILTER_ACTION_BLOCK)) {
return;
}

@ -282,6 +282,10 @@ prototcp_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
}
#endif /* !WITHOUT_USERAUTH */
if (pxyconn_apply_deferred_block_action(ctx)) {
return;
}
struct evbuffer *inbuf = bufferevent_get_input(bev);
struct evbuffer *outbuf = bufferevent_get_output(ctx->dst.bev);
@ -551,27 +555,48 @@ prototcp_dsthost_filter(pxy_conn_ctx_t *ctx, filter_list_t *list)
}
int
prototcp_apply_filter(pxy_conn_ctx_t *ctx)
prototcp_apply_filter(pxy_conn_ctx_t *ctx, unsigned int defer_action)
{
int rv = 0;
unsigned int action;
if ((action = pxyconn_filter(ctx, prototcp_dsthost_filter))) {
ctx->filter_precedence = action & FILTER_PRECEDENCE;
// If we reach here, the matching filtering rule must have a higher precedence
// Override any deferred action, if the current rule action is not match
// Match action cannot override other filter actions
if (action & FILTER_ACTION_DIVERT) {
ctx->deferred_action = FILTER_ACTION_NONE;
ctx->divert = 1;
}
else if (action & FILTER_ACTION_SPLIT) {
ctx->deferred_action = FILTER_ACTION_NONE;
ctx->divert = 0;
}
else if (action & FILTER_ACTION_PASS) {
protopassthrough_engage(ctx);
ctx->pass = 1;
rv = 1;
if (defer_action & FILTER_ACTION_PASS) {
log_fine("Deferring pass action");
ctx->deferred_action = FILTER_ACTION_PASS;
}
else {
ctx->deferred_action = FILTER_ACTION_NONE;
protopassthrough_engage(ctx);
ctx->pass = 1;
rv = 1;
}
}
else if (action & FILTER_ACTION_BLOCK) {
pxy_conn_term(ctx, 1);
rv = 1;
if (defer_action & FILTER_ACTION_BLOCK) {
// This block action should override any deferred pass action,
// because the current rule must have a higher precedence
log_fine("Deferring block action");
ctx->deferred_action = FILTER_ACTION_BLOCK;
}
else {
pxy_conn_term(ctx, 1);
rv = 1;
}
}
//else { /* FILTER_ACTION_MATCH */ }
@ -604,7 +629,9 @@ prototcp_bev_eventcb_connected_srvdst(UNUSED struct bufferevent *bev, pxy_conn_c
}
#endif /* !WITHOUT_USERAUTH */
if (prototcp_apply_filter(ctx)) {
// Defer any block action until HTTP filter application or the first src readcb of non-http proto
// We cannot defer pass actions from this point on
if (prototcp_apply_filter(ctx, FILTER_ACTION_BLOCK)) {
return;
}

@ -53,7 +53,7 @@ void prototcp_bev_eventcb_error_src(struct bufferevent *, pxy_conn_ctx_t *) NONN
void prototcp_bev_eventcb_eof_dst(struct bufferevent *, pxy_conn_ctx_t *) NONNULL(1,2);
void prototcp_bev_eventcb_error_dst(struct bufferevent *, pxy_conn_ctx_t *) NONNULL(1,2);
int prototcp_apply_filter(pxy_conn_ctx_t *) NONNULL(1);
int prototcp_apply_filter(pxy_conn_ctx_t *, unsigned int) NONNULL(1);
void prototcp_bev_eventcb_eof_srvdst(struct bufferevent *, pxy_conn_ctx_t *) NONNULL(1,2);
void prototcp_bev_eventcb_error_srvdst(struct bufferevent *, pxy_conn_ctx_t *) NONNULL(1,2);

@ -1995,6 +1995,30 @@ pxy_userauth(pxy_conn_ctx_t *ctx)
}
#endif /* !WITHOUT_USERAUTH */
int
pxyconn_apply_deferred_pass_action(pxy_conn_ctx_t *ctx)
{
if (ctx->deferred_action & FILTER_ACTION_PASS) {
log_fine("Applying deferred pass action");
ctx->deferred_action = FILTER_ACTION_NONE;
protopassthrough_engage(ctx);
ctx->pass = 1;
return 1;
}
return 0;
}
int
pxyconn_apply_deferred_block_action(pxy_conn_ctx_t *ctx)
{
if (ctx->deferred_action & FILTER_ACTION_BLOCK) {
log_fine("Applying deferred block action");
pxy_conn_term(ctx, 1);
return 1;
}
return 0;
}
unsigned int
pxyconn_set_filter_action(pxy_conn_ctx_t *ctx, filter_site_t *site)
{

@ -333,10 +333,13 @@ struct pxy_conn_ctx {
unsigned int log_mirror : 1;
#endif /* !WITHOUT_MIRROR */
// Highest precedence applied by filtering rules
// The precedence of filtering rule applied
// precedence can only go up not down
unsigned int filter_precedence;
// Deferred filter action from an earlier filter application
unsigned int deferred_action;
#ifdef HAVE_LOCAL_PROCINFO
/* local process information */
pxy_conn_lproc_desc_t lproc;
@ -441,6 +444,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 */
int pxyconn_apply_deferred_pass_action(pxy_conn_ctx_t *) NONNULL(1);
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);
void pxy_conn_setup(evutil_socket_t, struct sockaddr *, int,

@ -396,9 +396,9 @@ In terms of possible filter actions,
.LP
- Dst Host filtering rules can take all of the filter and log actions.
- SSL filtering rules can take all of the filter and log actions.
- HTTP filtering rules take the match and block filter actions, but not the
divert, split, or pass actions. Also, HTTP filtering rules can only disable
logging.
- HTTP filtering rules can take match and block filter actions, can keep
enabled divert and split modes, but cannot take pass action. Also, HTTP
filtering rules can only disable logging.
.LP
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.

Loading…
Cancel
Save