diff --git a/NEWS b/NEWS index 9d8cde991c91052e85da18049a56c82e44337824..b0d4ed670fd35d9e6f8ab80051947690d3917ad5 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ Knot Resolver X.Y.X (yyyy-mm-dd) Bugfixes -------- - fix map() command on 32-bit platforms; regressed in 5.2.0 (!1093) +- doh2: restrict endpoints to doh and dns-query (#636, !1104) Knot Resolver 5.2.0 (2020-11-11) diff --git a/daemon/http.c b/daemon/http.c index beccac2020af1fdb7f67c99b003b451b84c656f8..a44ecdd069f24ac0cc7b652c3c6565d88271e211 100644 --- a/daemon/http.c +++ b/daemon/http.c @@ -19,6 +19,7 @@ #include "lib/layer/iterate.h" /* kr_response_classify */ #include "lib/cache/util.h" +#include "contrib/cleanup.h" #include "contrib/base64url.h" #define MAKE_NV(K, KS, V, VS) \ @@ -130,6 +131,72 @@ static int send_data_callback(nghttp2_session *h2, nghttp2_frame *frame, const u return 0; } +/* + * Check endpoint and uri path + */ +static int check_uri(const char* uri_path) +{ + static const char key[] = "dns="; + static const char *delim = "&"; + static const char *endpoins[] = {"dns-query", "doh"}; + char *beg; + char *end_prev; + ssize_t endpoint_len; + ssize_t ret; + + if (!uri_path) + return kr_error(EINVAL); + + auto_free char *path = malloc(sizeof(*path) * (strlen(uri_path) + 1)); + if (!path) + return kr_error(ENOMEM); + + memcpy(path, uri_path, strlen(uri_path)); + path[strlen(uri_path)] = '\0'; + + char *query_mark = strstr(path, "?"); + + /* calculating of endpoint_len - for POST or GET method */ + endpoint_len = (query_mark) ? query_mark - path - 1 : strlen(path) - 1; + + /* check endpoint */ + ret = -1; + for(int i = 0; i < sizeof(endpoins)/sizeof(*endpoins); i++) + { + if (strlen(endpoins[i]) != endpoint_len) + continue; + ret = strncmp(path + 1, endpoins[i], strlen(endpoins[i])); + if (!ret) + break; + } + + if (ret) /* no endpoint found */ + return -1; + if (endpoint_len == strlen(path) - 1) /* done for POST method */ + return 0; + + /* go over key:value pair */ + beg = strtok(query_mark + 1, delim); + if (beg) { + while (beg != NULL) { + if (!strncmp(beg, key, 4)) { /* dns variable in path found */ + break; + } + end_prev = beg + strlen(beg); + beg = strtok(NULL, delim); + if (beg-1 != end_prev) { /* detect && */ + return -1; + } + } + + if (!beg) { /* no dns variable in path */ + return -1; + } + } + + return 0; +} + /* * Process a query from URI path if there's base64url encoded dns variable. */ @@ -217,7 +284,6 @@ static int header_callback(nghttp2_session *h2, const nghttp2_frame *frame, struct http_ctx *ctx = (struct http_ctx *)user_data; int32_t stream_id = frame->hd.stream_id; - if (frame->hd.type != NGHTTP2_HEADERS) return 0; @@ -229,6 +295,11 @@ static int header_callback(nghttp2_session *h2, const nghttp2_frame *frame, } if (!strcasecmp(":path", (const char *)name)) { + if (check_uri((const char *)value) < 0) { + refuse_stream(h2, stream_id); + return 0; + } + ctx->uri_path = malloc(sizeof(*ctx->uri_path) * (valuelen + 1)); if (!ctx->uri_path) return kr_error(ENOMEM); diff --git a/tests/config/doh2.test.lua b/tests/config/doh2.test.lua index a97fc0692cae8156326061662f4072ee3b54c1b4..2c4779e90aeb406c57dce20d8c2b104093486c53 100644 --- a/tests/config/doh2.test.lua +++ b/tests/config/doh2.test.lua @@ -96,7 +96,7 @@ else local function start_server() local request = require('http.request') local ssl_ctx = require('openssl.ssl.context') - uri_templ = string.format('https://%s:%d/dns_query', host, port) + uri_templ = string.format('https://%s:%d/dns-query', host, port) req_templ = assert(request.new_from_uri(uri_templ)) req_templ.headers:upsert('content-type', 'application/dns-message') req_templ.ctx = ssl_ctx.new()