]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
daemon TLS: increase wire-buffer size
authorGrigorii Demidov <grigorii.demidov@nic.cz>
Thu, 15 Nov 2018 13:29:55 +0000 (14:29 +0100)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 28 Nov 2018 13:23:46 +0000 (14:23 +0100)
When decoding large packets, gnutls gives the application chunks
of size 16kb. So that tls session wirebuffer must be at least
KNOT_WIRE_MAX_PKTSIZE + 16kb.  (message re-formatted by vcunat)

NEWS
daemon/io.c
daemon/io.h
daemon/session.c
daemon/session.h
daemon/worker.c

diff --git a/NEWS b/NEWS
index dcd94a1686472111d56aa428ef2cecc41f0173ac..26f441aa9e79f872c724d72537f655d0c40ebcb8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -18,7 +18,7 @@ Bugfixes
 --------
 - http module: only run prometheus in parent process if using --forks=N,
   as the submodule collects metrics from all sub-processes as well.
-- policy.TLS_FORWARD fixes (!714)
+- TLS fixes for corner cases (!714, !700)
 - fix build with -DNOVERBOSELOG (#424)
 
 Improvements
index d752a51065e0eae676fa04c598ababc4842f5b84..ee65d4d9396d73d289cb8cab57e89009bbdc9c56 100644 (file)
@@ -108,7 +108,7 @@ static int udp_bind_finalize(uv_handle_t *handle)
 {
        check_bufsize(handle);
        /* Handle is already created, just create context. */
-       struct session *s = session_new(handle);
+       struct session *s = session_new(handle, false);
        assert(s);
        session_flags(s)->outgoing = false;
        return io_start_read(handle);
@@ -254,7 +254,8 @@ static void _tcp_accept(uv_stream_t *master, int status, bool tls)
        if (!client) {
                return;
        }
-       int res = io_create(master->loop, (uv_handle_t *)client, SOCK_STREAM, AF_UNSPEC);
+       int res = io_create(master->loop, (uv_handle_t *)client,
+                           SOCK_STREAM, AF_UNSPEC, tls);
        if (res) {
                if (res == UV_EMFILE) {
                        worker->too_many_open = true;
@@ -268,22 +269,20 @@ static void _tcp_accept(uv_stream_t *master, int status, bool tls)
        }
 
        /* struct session was allocated \ borrowed from memory pool. */
-       struct session *session = client->data;
-       assert(session_flags(session)->outgoing == false);
+       struct session *s = client->data;
+       assert(session_flags(s)->outgoing == false);
+       assert(session_flags(s)->has_tls == tls);
 
        if (uv_accept(master, (uv_stream_t *)client) != 0) {
                /* close session, close underlying uv handles and
                 * deallocate (or return to memory pool) memory. */
-               session_close(session);
+               session_close(s);
                return;
        }
 
        /* Set deadlines for TCP connection and start reading.
         * It will re-check every half of a request time limit if the connection
         * is idle and should be terminated, this is an educated guess. */
-       struct session *s = client->data;
-       assert(session_flags(s)->outgoing == false);
-
        struct sockaddr *peer = session_get_peer(s);
        int peer_len = sizeof(union inaddr);
        int ret = uv_tcp_getpeername(client, peer, &peer_len);
@@ -297,7 +296,6 @@ static void _tcp_accept(uv_stream_t *master, int status, bool tls)
        uint64_t idle_in_timeout = net->tcp.in_idle_timeout;
 
        uint64_t timeout = KR_CONN_RTT_MAX / 2;
-       session_flags(s)->has_tls = tls;
        if (tls) {
                timeout += TLS_MAX_HANDSHAKE_TIME;
                struct tls_ctx_t *ctx = session_tls_get_server_ctx(s);
@@ -415,7 +413,7 @@ int tcp_bindfd_tls(uv_tcp_t *handle, int fd, int tcp_backlog)
        return _tcp_bindfd(handle, fd, tls_accept, tcp_backlog);
 }
 
-int io_create(uv_loop_t *loop, uv_handle_t *handle, int type, unsigned family)
+int io_create(uv_loop_t *loop, uv_handle_t *handle, int type, unsigned family, bool has_tls)
 {
        int ret = -1;
        if (type == SOCK_DGRAM) {
@@ -427,7 +425,7 @@ int io_create(uv_loop_t *loop, uv_handle_t *handle, int type, unsigned family)
        if (ret != 0) {
                return ret;
        }
-       struct session *s = session_new(handle);
+       struct session *s = session_new(handle, has_tls);
        if (s == NULL) {
                ret = -1;
        }
index 34e33fccf31f7fa3a75bed8a5b972a045b90c1a9..1d41569b2997504af55b55202dcf0fc6c7c1f41d 100644 (file)
@@ -35,8 +35,10 @@ void tcp_timeout_trigger(uv_timer_t *timer);
 
 /** Initialize the handle, incl. ->data = struct session * instance.
  * \param type = SOCK_*
- * \param family = AF_* */
-int io_create(uv_loop_t *loop, uv_handle_t *handle, int type, unsigned family);
+ * \param family = AF_*
+ * \param has_tls has meanings only when type is SOCK_STREAM */
+int io_create(uv_loop_t *loop, uv_handle_t *handle, int type,
+             unsigned family, bool has_tls);
 void io_deinit(uv_handle_t *handle);
 void io_free(uv_handle_t *handle);
 
index 74aa038f59caafbc8911165240c7ca7115322ef3..4e18b6a3e71d2c5384e622d5184b112aaa57d249 100644 (file)
@@ -26,6 +26,8 @@
 #include "daemon/io.h"
 #include "lib/generic/queue.h"
 
+#define TLS_CHUNK_SIZE (16 * 1024)
+
 /* Per-session (TCP or UDP) persistent structure,
  * that exists between remote counterpart and a local socket.
  */
@@ -301,7 +303,7 @@ struct session *session_get(uv_handle_t *h)
        return h->data;
 }
 
-struct session *session_new(uv_handle_t *handle)
+struct session *session_new(uv_handle_t *handle, bool has_tls)
 {
        if (!handle) {
                return NULL;
@@ -314,13 +316,20 @@ struct session *session_new(uv_handle_t *handle)
        queue_init(session->waiting);
        session->tasks = trie_create(NULL);
        if (handle->type == UV_TCP) {
-               uint8_t *wire_buf = malloc(KNOT_WIRE_MAX_PKTSIZE);
+               size_t wire_buffer_size = KNOT_WIRE_MAX_PKTSIZE;
+               if (has_tls) {
+                       /* When decoding large packets,
+                        * gnutls gives the application chunks of size 16 kb each. */
+                       wire_buffer_size += TLS_CHUNK_SIZE;
+                       session->sflags.has_tls = true;
+               }
+               uint8_t *wire_buf = malloc(wire_buffer_size);
                if (!wire_buf) {
                        free(session);
                        return NULL;
                }
                session->wire_buf = wire_buf;
-               session->wire_buf_size = KNOT_WIRE_MAX_PKTSIZE;
+               session->wire_buf_size = wire_buffer_size;
        } else if (handle->type == UV_UDP) {
                /* We use the singleton buffer from worker for all UDP (!)
                 * libuv documentation doesn't really guarantee this is OK,
@@ -533,13 +542,13 @@ knot_pkt_t *session_produce_packet(struct session *session, knot_mm_t *mm)
        const uv_handle_t *handle = session->handle;
        uint8_t *msg_start = &session->wire_buf[session->wire_buf_start_idx];
        ssize_t wirebuf_msg_data_size = session->wire_buf_end_idx - session->wire_buf_start_idx;
-       uint16_t msg_size = wirebuf_msg_data_size;
+       uint16_t msg_size = 0;
        
        if (!handle) {
                session->sflags.wirebuf_error = true;
                return NULL;
        } else if (handle->type == UV_TCP) {
-               if (msg_size < 2) {
+               if (wirebuf_msg_data_size < 2) {
                        return NULL;
                }
                msg_size = knot_wire_read_u16(msg_start);
@@ -550,13 +559,21 @@ knot_pkt_t *session_produce_packet(struct session *session, knot_mm_t *mm)
                if (msg_size + 2 > wirebuf_msg_data_size) {
                        return NULL;
                }
+               if (msg_size == 0) {
+                       session->sflags.wirebuf_error = true;
+                       return NULL;
+               }
                msg_start += 2;
+       } else if (wirebuf_msg_data_size < UINT16_MAX) {
+               msg_size = wirebuf_msg_data_size;
+       } else {
+               session->sflags.wirebuf_error = true;
+               return NULL;
        }
 
+
        knot_pkt_t *pkt = knot_pkt_new(msg_start, msg_size, mm);
-       if (pkt) {
-               session->sflags.wirebuf_error = false;
-       }
+       session->sflags.wirebuf_error = (pkt == NULL);
        return pkt;
 }
 
@@ -679,7 +696,7 @@ size_t session_wirebuf_get_len(struct session *session)
 
 size_t session_wirebuf_get_size(struct session *session)
 {
-       return sizeof(session->wire_buf);
+       return session->wire_buf_size;
 }
 
 uint8_t *session_wirebuf_get_free_start(struct session *session)
index a537213e03372abf34024473768c1a63b5fbe7a8..654f93193f8da6f36fb91f673d602ab5150aad47 100644 (file)
@@ -32,8 +32,9 @@ struct session_flags {
        bool wirebuf_error : 1; /**< True: last operation with wirebuf ended up with an error. */
 };
 
-/* Allocate new session for a libuv handle. */
-struct session *session_new(uv_handle_t *handle);
+/* Allocate new session for a libuv handle.
+ * If handle->tyoe is UV_UDP, tls parameter will be ignored. */
+struct session *session_new(uv_handle_t *handle, bool has_tls);
 /* Clear and free given session. */
 void session_free(struct session *session);
 /* Clear session. */
index aafcf9027c14da0f94dd8707eaffc4e86bda8450..5173eacd290430813d4ef6612bc31baae40470ed 100644 (file)
@@ -131,7 +131,8 @@ static inline struct worker_ctx *get_worker(void)
 
 /*! @internal Create a UDP/TCP handle for an outgoing AF_INET* connection.
  *  socktype is SOCK_* */
-static uv_handle_t *ioreq_spawn(struct worker_ctx *worker, int socktype, sa_family_t family)
+static uv_handle_t *ioreq_spawn(struct worker_ctx *worker,
+                               int socktype, sa_family_t family, bool has_tls)
 {
        bool precond = (socktype == SOCK_DGRAM || socktype == SOCK_STREAM)
                        && (family == AF_INET  || family == AF_INET6);
@@ -147,7 +148,7 @@ static uv_handle_t *ioreq_spawn(struct worker_ctx *worker, int socktype, sa_fami
        if (!handle) {
                return NULL;
        }
-       int ret = io_create(worker->loop, handle, socktype, family);
+       int ret = io_create(worker->loop, handle, socktype, family, has_tls);
        if (ret) {
                if (ret == UV_EMFILE) {
                        worker->too_many_open = true;
@@ -941,7 +942,7 @@ static uv_handle_t *retransmit(struct qr_task *task)
                if (kr_resolve_checkout(&ctx->req, NULL, (struct sockaddr *)choice, SOCK_DGRAM, task->pktbuf) != 0) {
                        return ret;
                }
-               ret = ioreq_spawn(ctx->worker, SOCK_DGRAM, choice->sin6_family);
+               ret = ioreq_spawn(ctx->worker, SOCK_DGRAM, choice->sin6_family, false);
                if (!ret) {
                        return ret;
                }
@@ -1202,52 +1203,54 @@ static int tcp_task_existing_connection(struct session *session, struct qr_task
        return kr_ok();
 }
 
-static int tcp_task_make_connection(struct session *session, struct qr_task *task,
-                                   const struct sockaddr *addr /* , knot_pkt_t *packet */)
+static int tcp_task_make_connection(struct qr_task *task, const struct sockaddr *addr)
 {
        struct request_ctx *ctx = task->ctx;
        struct worker_ctx *worker = ctx->worker;
 
+       /* Check if there must be TLS */
+       struct engine *engine = worker->engine;
+       struct network *net = &engine->net;
+       const char *key = tcpsess_key(addr);
+       struct tls_client_ctx_t *tls_ctx = NULL;
+       struct tls_client_paramlist_entry *entry = map_get(&net->tls_client_params, key);
+       if (entry) {
+               /* Address is configured to be used with TLS.
+                * We need to allocate auxiliary data structure. */
+               tls_ctx = tls_client_ctx_new(entry, worker);
+               if (!tls_ctx) {
+                       return kr_error(EINVAL);
+               }
+       }
+
        uv_connect_t *conn = malloc(sizeof(uv_connect_t));
        if (!conn) {
+               tls_client_ctx_free(tls_ctx);
                return kr_error(EINVAL);
        }
-       uv_handle_t *client = ioreq_spawn(worker, SOCK_STREAM,
-                                                 addr->sa_family);
+       bool has_tls = (tls_ctx != NULL);
+       uv_handle_t *client = ioreq_spawn(worker, SOCK_STREAM, addr->sa_family, has_tls);
        if (!client) {
+               tls_client_ctx_free(tls_ctx);
                free(conn);
                return kr_error(EINVAL);
        }
-       session = client->data;
+       struct session *session = client->data;
+       assert(session_flags(session)->has_tls == has_tls);
+       if (has_tls) {
+               tls_client_ctx_set_session(tls_ctx, session);
+               session_tls_set_client_ctx(session, tls_ctx);
+       }
 
        /* Add address to the waiting list.
         * Now it "is waiting to be connected to." */
        int ret = worker_add_tcp_waiting(ctx->worker, addr, session);
        if (ret < 0) {
                free(conn);
+               session_close(session);
                return kr_error(EINVAL);
        }
 
-       /* Check if there must be TLS */
-       struct engine *engine = ctx->worker->engine;
-       struct network *net = &engine->net;
-       const char *key = tcpsess_key(addr);
-       struct tls_client_paramlist_entry *entry = map_get(&net->tls_client_params, key);
-       if (entry) {
-               /* Address is configured to be used with TLS.
-                * We need to allocate auxiliary data structure. */
-               assert(session_tls_get_client_ctx(session) == NULL);
-               struct tls_client_ctx_t *tls_ctx = tls_client_ctx_new(entry, worker);
-               if (!tls_ctx) {
-                       worker_del_tcp_waiting(ctx->worker, addr);
-                       free(conn);
-                       return kr_error(EINVAL);
-               }
-               tls_client_ctx_set_session(tls_ctx, session);
-               session_tls_set_client_ctx(session, tls_ctx);
-               session_flags(session)->has_tls = true;
-       }
-
        conn->data = session;
        /*  Store peer address for the session. */
        struct sockaddr *peer = session_get_peer(session);
@@ -1259,6 +1262,7 @@ static int tcp_task_make_connection(struct session *session, struct qr_task *tas
        if (ret != 0) {
                worker_del_tcp_waiting(ctx->worker, addr);
                free(conn);
+               session_close(session);
                return kr_error(EINVAL);
        }
 
@@ -1273,6 +1277,7 @@ static int tcp_task_make_connection(struct session *session, struct qr_task *tas
                session_timer_stop(session);
                worker_del_tcp_waiting(ctx->worker, addr);
                free(conn);
+               session_close(session);
                return kr_error(EAGAIN);
        }
 
@@ -1283,6 +1288,7 @@ static int tcp_task_make_connection(struct session *session, struct qr_task *tas
                session_timer_stop(session);
                worker_del_tcp_waiting(ctx->worker, addr);
                free(conn);
+               session_close(session);
                return kr_error(EINVAL);
        }
 
@@ -1321,7 +1327,7 @@ static int tcp_task_step(struct qr_task *task,
                ret = tcp_task_existing_connection(session, task);
        } else {
                /* Make connection. */
-               ret = tcp_task_make_connection(session, task, addr);
+               ret = tcp_task_make_connection(task, addr);
        }
 
        if (ret != kr_ok()) {