Skip to content

Commit

Permalink
Merge pull request #1448 from tempesta-tech/avb-1398
Browse files Browse the repository at this point in the history
Resetting the counter of redirected requests after a timeout during which the client must block
  • Loading branch information
avbelov23 authored Sep 11, 2020
2 parents 4069f60 + 3a4a0df commit ced0918
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 32 deletions.
15 changes: 14 additions & 1 deletion etc/js_challenge.js.tpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
var c_name = "[% STICKY_NAME %]";
var delay_min = [% DELAY_MIN %];
var delay_range = [% DELAY_RANGE %];
/*
* If JS challenge are enabled, then Location header cannot be used to add
* rmark to the url. We pass rmark through the response body.
*/
var TFW_DONT_CHANGE_NAME = "";

function cookieVal(input, c_name) {
var re = new RegExp("(.*;)?\s*" + c_name + "=([0-9a-f]+)")
Expand All @@ -15,7 +20,15 @@ var c_val = cookieVal(document.cookie, c_name)
if (navigator.cookieEnabled && !!c_val) {
var ts = "0x" + c_val.substr(0, 16);
setTimeout(function() {
location.reload();
var url = location.pathname;
/* If there is rmark */
if (TFW_DONT_CHANGE_NAME) {
url = TFW_DONT_CHANGE_NAME + location.pathname;
const regex = /\/__tfw=[0-9a-z]+/;
if (location.pathname.search(regex) != -1)
url = location.pathname.replace(regex, TFW_DONT_CHANGE_NAME);
}
location.replace(url);
}, delay_min + Number(ts) % delay_range);
} else {
document.write("<h3 align='center' style='color:red'>"
Expand Down
4 changes: 4 additions & 0 deletions etc/js_challenge.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
</h2>
<p></p>&nbsp;<p></p>

<h3 align='center'>
Please check that you browser supports JavaScript.
</h3>

<script>[% INCLUDE js_challenge.js.tpl %]</script>
</body>
</html>
2 changes: 0 additions & 2 deletions tempesta_fw/http_msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@
#define S_VERSION11 "HTTP/1.1"
#define S_0 S_VERSION11 " "

#define SLEN(s) (sizeof(s) - 1)

/*
* The size of the buffer to store the value for ':status' pseudo-header
* of HTTP/2-response.
Expand Down
59 changes: 42 additions & 17 deletions tempesta_fw/http_sess.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "hash.h"
#include "http_msg.h"
#include "http_sess.h"
#include "http_sess_conf.h"
#include "vhost.h"
#include "filter.h"
#include "tdb.h"
Expand Down Expand Up @@ -174,25 +175,22 @@ tfw_http_redir_mark_prepare(RedirMarkVal *mv, char *buf, unsigned int buf_len,
}

static int
tfw_http_sticky_build_redirect(TfwHttpReq *req, StickyVal *sv, RedirMarkVal *mv)
tfw_http_sticky_build_redirect(TfwHttpReq *req, StickyVal *sv, RedirMarkVal *mv,
bool jsch_allow)
{
unsigned long ts_be64 = cpu_to_be64(sv->ts);
TfwStr c_chunks[3], m_chunks[3], cookie = { 0 }, rmark = { 0 };
TfwStr c_chunks[3], m_chunks[3], r_chunks[5], cookie = { 0 }, rmark = { 0 };
TfwHttpResp *resp;
char c_buf[sizeof(*sv) * 2], m_buf[sizeof(*mv) * 2];
TfwStr *body = NULL;
TfwStr body = { 0 };
TfwStickyCookie *sticky;
int r;
int r, code;

WARN_ON_ONCE(!list_empty(&req->fwd_list));
WARN_ON_ONCE(!list_empty(&req->nip_list));
if (WARN_ON_ONCE(!req->vhost))
return TFW_HTTP_SESS_FAILURE;

sticky = req->vhost->cookie;
if (sticky->js_challenge)
body = &sticky->js_challenge->body;

/*
* TODO: #598 rate limit requests with invalid cookie value.
* Non-challengeable requests also must be rate limited.
Expand All @@ -207,6 +205,26 @@ tfw_http_sticky_build_redirect(TfwHttpReq *req, StickyVal *sv, RedirMarkVal *mv)
if (mv)
tfw_http_redir_mark_prepare(mv, m_buf, sizeof(m_buf), m_chunks,
sizeof(m_chunks), &rmark);

sticky = req->vhost->cookie;
if (sticky->js_challenge && jsch_allow) {
if (mv) {
if (sticky->js_challenge->body.nchunks != 2)
return TFW_HTTP_SESS_FAILURE;

r_chunks[0] = sticky->js_challenge->body.chunks[0];
r_chunks[1] = rmark.chunks[0];
r_chunks[2] = rmark.chunks[1];
r_chunks[3] = rmark.chunks[2];
r_chunks[4] = sticky->js_challenge->body.chunks[1];
body.chunks = r_chunks;
body.len = r_chunks[0].len + rmark.len + r_chunks[4].len;
body.nchunks = 5;
} else {
body = sticky->js_challenge->body;
}
}

/*
* Form the cookie as:
*
Expand Down Expand Up @@ -234,11 +252,10 @@ tfw_http_sticky_build_redirect(TfwHttpReq *req, StickyVal *sv, RedirMarkVal *mv)
cookie.nchunks++;
}

code = jsch_allow ? sticky->redirect_code: TFW_REDIR_STATUS_CODE_DFLT;
r = TFW_MSG_H2(req)
? tfw_h2_prep_redirect(resp, sticky->redirect_code, &rmark,
&cookie, body)
: tfw_h1_prep_redirect(resp, sticky->redirect_code, &rmark,
&cookie, body);
? tfw_h2_prep_redirect(resp, code, &rmark, &cookie, &body)
: tfw_h1_prep_redirect(resp, code, &rmark, &cookie, &body);
if (r) {
tfw_http_msg_free((TfwHttpMsg *)resp);
return TFW_HTTP_SESS_FAILURE;
Expand Down Expand Up @@ -659,10 +676,14 @@ tfw_http_sess_check_redir_mark(TfwHttpReq *req, RedirMarkVal *mv)
unsigned long tmt = HZ * (unsigned long)req->vhost->cookie->tmt_sec;

if (tfw_http_redir_mark_get(req, &mark_val)) {
if (tfw_http_redir_mark_verify(req, &mark_val, mv)
|| ++mv->att_no > max_misses
|| (tmt && mv->ts + tmt < jiffies))
{
int r = tfw_http_redir_mark_verify(req, &mark_val, mv);

if (!r && tmt && mv->ts + tmt < jiffies) {
mv->ts = jiffies;
mv->att_no = 0;
}

if (r || ++mv->att_no > max_misses) {
tfw_filter_block_ip(&req->conn->peer->addr);
return TFW_HTTP_SESS_VIOLATE;
}
Expand Down Expand Up @@ -699,7 +720,7 @@ tfw_http_sticky_challenge_start(TfwHttpReq *req, TfwStickyCookie *sticky,
return TFW_HTTP_SESS_FAILURE;
}

return tfw_http_sticky_build_redirect(req, sv, mvp);
return tfw_http_sticky_build_redirect(req, sv, mvp, true);
}

/*
Expand Down Expand Up @@ -1125,6 +1146,7 @@ tfw_http_sess_obtain(TfwHttpReq *req)
TfwStr *c_val = &ctx.cookie_val;
TdbRec *rec;
TdbGetAllocCtx tdb_ctx = { 0 };
TfwStr mark_val = {};

/*
* If vhost is not known, then request is to be dropped. Don't save the
Expand Down Expand Up @@ -1193,6 +1215,9 @@ tfw_http_sess_obtain(TfwHttpReq *req)
*/
tdb_rec_put(rec);

if (tfw_http_redir_mark_get(req, &mark_val))
return tfw_http_sticky_build_redirect(req, sv, NULL, false);

return TFW_HTTP_SESS_SUCCESS;
}

Expand Down
90 changes: 78 additions & 12 deletions tempesta_fw/http_sess_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,26 @@ static TfwVhost *cur_vhost;
#define STICKY_NAME_DEFAULT "__tfw"

static const unsigned int tfw_cfg_jsch_code_dflt = 503;
static const unsigned short tfw_cfg_redirect_st_code_dflt = 302;
#define TFW_CFG_JS_PATH "/etc/tempesta/js_challenge.html"

void
tfw_http_sess_cookie_clean(TfwVhost *vhost)
{
TfwStickyCookie *sticky = vhost->cookie;
TfwStr *c;

if (sticky->shash)
crypto_free_shash(sticky->shash);

if (!sticky->js_challenge)
return;

if (sticky->js_challenge->body.data)
free_pages((unsigned long)sticky->js_challenge->body.data,
c = TFW_STR_CHUNK(&sticky->js_challenge->body, 0);
if (c && c->data) {
free_pages((unsigned long)c->data,
get_order(sticky->js_challenge->body.len));
kfree(sticky->js_challenge->body.chunks);
}
kfree(sticky->js_challenge);

return;
Expand Down Expand Up @@ -112,7 +115,7 @@ tfw_http_sess_cfgop_finish(TfwVhost *vhost, TfwCfgSpec *cs)
sticky->enforce = true;
sticky->redirect_code = sticky->js_challenge->st_code;
} else {
sticky->redirect_code = tfw_cfg_redirect_st_code_dflt;
sticky->redirect_code = TFW_REDIR_STATUS_CODE_DFLT;
}

cur_vhost = NULL;
Expand Down Expand Up @@ -155,16 +158,35 @@ int
tfw_cfgop_cookie_set_options(TfwStickyCookie *sticky, const char *options)
{
size_t len = strlen(options);
if (len + 2 > STICKY_OPT_MAXLEN) {
if (sticky->options.len + 2 + len > STICKY_OPT_MAXLEN) {
T_ERR_NL("http_sess: too long cookie options length.\n");
return -EINVAL;
}
sticky->options.data = sticky->options_str;
sticky->options.len = len + 2;

sticky->options_str[0] = ';';
sticky->options_str[1] = ' ';
memcpy(&sticky->options_str[2], options, len);
sticky->options_str[sticky->options.len + 0] = ';';
sticky->options_str[sticky->options.len + 1] = ' ';
memcpy(&sticky->options_str[sticky->options.len + 2], options, len);

sticky->options.len += len + 2;

return 0;
}

int
tfw_cfgop_cookie_set_path(TfwStickyCookie *sticky, const char *path_val)
{
static const char path[] = "; path=";
size_t path_val_len = strlen(path_val);
if (sticky->options.len + SLEN(path) + path_val_len > STICKY_OPT_MAXLEN) {
T_ERR_NL("http_sess: too long cookie path length.\n");
return -EINVAL;
}

memcpy(&sticky->options_str[sticky->options.len], path, SLEN(path));
memcpy(&sticky->options_str[sticky->options.len + SLEN(path)], path_val,
path_val_len);

sticky->options.len += SLEN(path) + path_val_len;

return 0;
}
Expand All @@ -176,9 +198,12 @@ tfw_cfgop_cookie_set(TfwCfgSpec *cs, TfwCfgEntry *ce)
int r;
const char *key, *val, *name_val = STICKY_NAME_DEFAULT;
TfwStickyCookie *sticky;
bool was_path = false;

BUG_ON(!cur_vhost);
sticky = cur_vhost->cookie;
sticky->options.data = sticky->options_str;
sticky->options.len = 0;

if (!TFW_STR_EMPTY(&sticky->name)) {
T_ERR_NL("http_sess: 'cookie' and 'learn' directives can't be "
Expand Down Expand Up @@ -211,13 +236,33 @@ tfw_cfgop_cookie_set(TfwCfgSpec *cs, TfwCfgEntry *ce)
" attribute: '%s'\n", cs->name, val);
return -EINVAL;
}
} else if (!strcasecmp(key, "path")) {
if (tfw_cfgop_cookie_set_path(sticky, val))
{
T_ERR_NL("%s: invalid value for 'path'"
" attribute: '%s'\n", cs->name, val);
return -EINVAL;
}
was_path = true;
} else {
T_ERR_NL("%s: unsupported attribute: '%s=%s'.\n",
cs->name, key, val);
return -EINVAL;
}
}

if (!was_path) {
static const char dflt_path[] = "; path=/";
if (sticky->options.len + SLEN(dflt_path) > STICKY_OPT_MAXLEN) {
T_ERR_NL("http_sess: too long cookie options or path length.\n");
return -EINVAL;
}
memcpy(&sticky->options_str[sticky->options.len], dflt_path,
SLEN(dflt_path));

sticky->options.len += SLEN(dflt_path);
}

if ((r = __tfw_cfgop_cookie_set_name(sticky, name_val)))
return r;

Expand Down Expand Up @@ -452,14 +497,34 @@ tfw_cfgop_jsch_set_body(TfwCfgSpec *cs, TfwCfgJsCh *js_ch, const char *script)
{
char *body_data;
size_t sz;
char *rbegin, *rend, *p;

body_data = tfw_http_msg_body_dup(script, &sz);
if (!body_data)
return -ENOMEM;
js_ch->body.len = sz;
js_ch->body.data = body_data;
if ((p = strstr(body_data, "TFW_DONT_CHANGE_NAME"))) {
if (!(rbegin = strchr(p, '"') + 1))
goto err;
if (!(rend = strchr(rbegin, '"')))
goto err;
} else {
goto err;
}
js_ch->body.chunks = kzalloc(sizeof(TfwStr) * 2, GFP_KERNEL);
js_ch->body.chunks[0] = (TfwStr) { .data = body_data,
.len = rbegin - body_data};
js_ch->body.chunks[1] = (TfwStr) { .data = rend,
.len = body_data + sz - rend };
js_ch->body.len = js_ch->body.chunks[0].len + js_ch->body.chunks[1].len;
js_ch->body.nchunks = 2;

return 0;

err:
T_ERR_NL("%s: can't find TFW_DONT_CHANGE_NAME in JS challenge script\n",
cs->name);
free_pages((unsigned long)body_data, get_order(sz));
return -EINVAL;
}

static int
Expand Down Expand Up @@ -597,6 +662,7 @@ TfwCfgSpec tfw_http_sess_specs[] = {
.name = "js_challenge",
.handler = tfw_cfgop_js_challenge,
.allow_none = true,
.allow_reconfig = true,
},
{ 0 }
};
2 changes: 2 additions & 0 deletions tempesta_fw/http_sess_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include "cfg.h"
#include "http_types.h"

#define TFW_REDIR_STATUS_CODE_DFLT 302

extern TfwCfgSpec tfw_http_sess_specs[];

int tfw_http_sess_cfgop_begin(TfwVhost *vhost, TfwCfgSpec *cs, TfwCfgEntry *ce);
Expand Down
2 changes: 2 additions & 0 deletions tempesta_fw/str.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ size_t tfw_ultohex(unsigned long ai, char *buf, unsigned int len);
/* The chunk contains only WS characters. */
#define TFW_STR_OWS 0x100

#define SLEN(s) (sizeof(s) - 1)

/*
* @ptr - pointer to string data or array of nested strings;
* @skb - socket buffer containing the string data;
Expand Down

0 comments on commit ced0918

Please sign in to comment.