]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
libknot: extend TLS API to be used for non-DNS communication
authorDaniel Salzman <daniel.salzman@nic.cz>
Sat, 8 Mar 2025 19:40:34 +0000 (20:40 +0100)
committerDaniel Salzman <daniel.salzman@nic.cz>
Mon, 19 May 2025 06:19:15 +0000 (08:19 +0200)
distro/pkg/deb/libknot15.symbols
src/knot/events/handlers/update.c
src/knot/query/requestor.c
src/knot/query/tls-requestor.c
src/knot/server/tcp-handler.c
src/libknot/quic/quic.c
src/libknot/quic/tls.c
src/libknot/quic/tls.h
src/libknot/quic/tls_common.c
src/libknot/quic/tls_common.h

index 48599c9428790cc7cb8e64ce04ffabd8ba814bc8..50406c129119ec38d51ceb5b05547bbd517eeedf 100644 (file)
@@ -201,8 +201,8 @@ libknot.so.15 libknot15 #MINVER#
  knot_tls_pin@Base 3.4.0
  knot_tls_pin_check@Base 3.4.0
  knot_tls_priority@Base 3.4.7
- knot_tls_recv_dns@Base 3.4.0
- knot_tls_send_dns@Base 3.4.0
+ knot_tls_recv@Base 3.5.0
+ knot_tls_send@Base 3.5.0
  knot_tls_session@Base 3.4.0
  knot_tls_session_available@Base 3.4.1
  knot_tls_session_load@Base 3.4.1
index ebb18b50daa5fb9868c151d63c5c11b9ca6c7dbe..287369d93f156d8f224cf662f7fd9990ee74a2fb 100644 (file)
@@ -382,8 +382,8 @@ static void send_update_response(conf_t *conf, zone_t *zone, knot_request_t *req
                }
 
                if (net_is_stream(req->fd) && req->tls_req_ctx.conn != NULL) {
-                       (void)knot_tls_send_dns(req->tls_req_ctx.conn,
-                                               req->resp->wire, req->resp->size);
+                       (void)knot_tls_send(req->tls_req_ctx.conn,
+                                           req->resp->wire, req->resp->size);
                        knot_tls_conn_block(req->tls_req_ctx.conn, false);
                }
 #ifdef ENABLE_QUIC
index 4ecfe5e8cbf9026ab587dc83dd875caf52d60d35..cb0b45af4abc17a8128be8354bc35b158e623b6e 100644 (file)
@@ -134,7 +134,7 @@ static int request_send(knot_request_t *request, int timeout_ms, bool *reused_fd
 
        /* Send query. */
        if (use_tls(request)) {
-               ret = knot_tls_send_dns(request->tls_req_ctx.conn, wire, wire_len);
+               ret = knot_tls_send(request->tls_req_ctx.conn, wire, wire_len);
                knot_tls_req_ctx_maint(&request->tls_req_ctx, request);
        } else if (use_quic(request)) {
 #ifdef ENABLE_QUIC
@@ -175,7 +175,7 @@ static int request_recv(knot_request_t *request, int timeout_ms)
 
        /* Receive it */
        if (use_tls(request)) {
-               ret = knot_tls_recv_dns(request->tls_req_ctx.conn, resp->wire, resp->max_size);
+               ret = knot_tls_recv(request->tls_req_ctx.conn, resp->wire, resp->max_size);
                knot_tls_req_ctx_maint(&request->tls_req_ctx, request);
        } else if (use_quic(request)) {
 #ifdef ENABLE_QUIC
index 4e539632c1f980144a4eb261e94c821927666ba5..04159e48ea59cfc5e91a835bfdc6ea79c47b2e41 100644 (file)
@@ -24,7 +24,8 @@ int knot_tls_req_ctx_init(knot_tls_req_ctx_t *ctx, int fd,
        }
 
        // Use HS = 4x IO timeout, as the RMT IO timeout is usually high.
-       ctx->ctx = knot_tls_ctx_new(creds, io_timeout_ms, 4 * io_timeout_ms, false);
+       ctx->ctx = knot_tls_ctx_new(creds, io_timeout_ms, 4 * io_timeout_ms,
+                                   KNOT_TLS_CLIENT | KNOT_TLS_DNS | KNOT_TLS_EARLY_DATA);
        if (ctx->ctx == NULL) {
                knot_creds_free(creds);
                return KNOT_ENOMEM;
index abd2973789e4c8cb27ded47b69d66ab5f2185e0d..c0b7e27e6545f7c320a3e2df0e2d28b0a51dabd2 100644 (file)
@@ -172,7 +172,7 @@ static int tcp_handle(tcp_context_t *tcp, knotd_qdata_params_t *params,
                case KNOT_NET_EAGAIN: // Unfinished handshake, continue later.
                        return KNOT_EOK;
                case KNOT_EOK:        // Finished handshake, continue with receiving message.
-                       recv = knot_tls_recv_dns(params->tls_conn, rx->iov_base, rx->iov_len);
+                       recv = knot_tls_recv(params->tls_conn, rx->iov_base, rx->iov_len);
                        break;
                default:              // E.g. handshake timeout.
                        assert(ret < 0);
@@ -199,7 +199,7 @@ static int tcp_handle(tcp_context_t *tcp, knotd_qdata_params_t *params,
                if (ans->size > 0 && send_state(tcp->layer.state)) {
                        int sent;
                        if (params->tls_conn != NULL) {
-                               sent = knot_tls_send_dns(params->tls_conn, ans->wire, ans->size);
+                               sent = knot_tls_send(params->tls_conn, ans->wire, ans->size);
                        } else {
                                sent = net_dns_tcp_send(params->socket, ans->wire, ans->size,
                                                        tcp->io_timeout, NULL);
@@ -424,7 +424,8 @@ int tcp_master(dthread_t *thread)
        if (tls) {
                // Set the HS timeout to 8x the RMT IO one as the HS duration can be up to 4*roundtrip.
                tcp.tls_ctx = knot_tls_ctx_new(handler->server->quic_creds,
-                                              tcp.io_timeout, 8 * tcp.io_timeout, true);
+                                              tcp.io_timeout, 8 * tcp.io_timeout,
+                                              KNOT_TLS_SERVER | KNOT_TLS_DNS | KNOT_TLS_EARLY_DATA);
                if (tcp.tls_ctx == NULL) {
                        ret = KNOT_ENOMEM;
                        goto finish;
index 33fbaeea5bf879816bd469fcce45de30e48b6409..9bdc4e8eb16f8f090f247b0811b17280e9887d3b 100644 (file)
@@ -132,8 +132,9 @@ static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref)
 static int tls_init_conn_session(knot_quic_conn_t *conn, bool server)
 {
        int ret = knot_tls_session(&conn->tls_session, conn->quic_table->creds,
-                                  conn->quic_table->priority, true,
-                                  true, server);
+                                  conn->quic_table->priority,
+                                  (server ? KNOT_TLS_SERVER : KNOT_TLS_CLIENT) |
+                                  KNOT_TLS_QUIC | KNOT_TLS_DNS | KNOT_TLS_EARLY_DATA);
        if (ret != KNOT_EOK) {
                return TLS_CALLBACK_ERR;
        }
index 41b8abaeca9a741d4078bc75f82716d621459c0d..5edaaf88a06c3b43a70628c6a6588bb0cda5bc59 100644 (file)
@@ -29,7 +29,7 @@ typedef struct knot_tls_session {
 
 _public_
 knot_tls_ctx_t *knot_tls_ctx_new(struct knot_creds *creds, unsigned io_timeout,
-                                 unsigned hs_timeout, bool server)
+                                 unsigned hs_timeout, knot_tls_flag_t flags)
 {
        knot_tls_ctx_t *res = calloc(1, sizeof(*res));
        if (res == NULL) {
@@ -39,7 +39,7 @@ knot_tls_ctx_t *knot_tls_ctx_new(struct knot_creds *creds, unsigned io_timeout,
        res->creds = creds;
        res->handshake_timeout = hs_timeout;
        res->io_timeout = io_timeout;
-       res->server = server;
+       res->flags = flags;
 
        int ret = gnutls_priority_init2(&res->priority, knot_tls_priority(false), NULL,
                                        GNUTLS_PRIORITY_INIT_DEF_APPEND);
@@ -71,7 +71,7 @@ knot_tls_conn_t *knot_tls_conn_new(knot_tls_ctx_t *ctx, int sock_fd)
        res->fd = sock_fd;
 
        int ret = knot_tls_session(&res->session, ctx->creds, ctx->priority,
-                                  false, true, ctx->server);
+                                  ctx->flags);
        if (ret != KNOT_EOK) {
                goto fail;
        }
@@ -195,7 +195,8 @@ int knot_tls_handshake(knot_tls_conn_t *conn, bool oneshot)
                *timeout_ptr = MAX(*timeout_ptr - running_ms, 0); \
        }
 
-static ssize_t recv_data(knot_tls_conn_t *conn, void *data, size_t size, int *timeout_ptr)
+static ssize_t recv_data(knot_tls_conn_t *conn, void *data, size_t size,
+                         int *timeout_ptr, bool oneshot)
 {
        gnutls_record_set_timeout(conn->session, *timeout_ptr);
 
@@ -205,6 +206,9 @@ static ssize_t recv_data(knot_tls_conn_t *conn, void *data, size_t size, int *ti
                TIMEOUT_CTX_INIT
                res = gnutls_record_recv(conn->session, data + total, size - total);
                if (res > 0) {
+                       if (oneshot) {
+                               return res;
+                       }
                        total += res;
                } else if (res == 0) {
                        return KNOT_ECONNRESET;
@@ -220,7 +224,7 @@ static ssize_t recv_data(knot_tls_conn_t *conn, void *data, size_t size, int *ti
 }
 
 _public_
-ssize_t knot_tls_recv_dns(knot_tls_conn_t *conn, void *data, size_t size)
+ssize_t knot_tls_recv(knot_tls_conn_t *conn, void *data, size_t size)
 {
        if (conn == NULL || data == NULL) {
                return KNOT_EINVAL;
@@ -237,27 +241,31 @@ ssize_t knot_tls_recv_dns(knot_tls_conn_t *conn, void *data, size_t size)
 
        int timeout = conn->ctx->io_timeout;
 
-       uint16_t msg_len;
-       ret = recv_data(conn, &msg_len, sizeof(msg_len), &timeout);
-       if (ret != sizeof(msg_len)) {
-               return ret;
-       }
+       if (conn->ctx->flags & KNOT_TLS_DNS) {
+               uint16_t msg_len;
+               ret = recv_data(conn, &msg_len, sizeof(msg_len), &timeout, false);
+               if (ret != sizeof(msg_len)) {
+                       return ret;
+               }
 
-       msg_len = ntohs(msg_len);
-       if (size < msg_len) {
-               return KNOT_ESPACE;
-       }
+               msg_len = ntohs(msg_len);
+               if (size < msg_len) {
+                       return KNOT_ESPACE;
+               }
 
-       ret = recv_data(conn, data, msg_len, &timeout);
-       if (ret != size) {
-               return ret;
-       }
+               ret = recv_data(conn, data, msg_len, &timeout, false);
+               if (ret != size) {
+                       return ret;
+               }
 
-       return msg_len;
+               return msg_len;
+       } else {
+               return recv_data(conn, data, size, &timeout, true);
+       }
 }
 
 _public_
-ssize_t knot_tls_send_dns(knot_tls_conn_t *conn, void *data, size_t size)
+ssize_t knot_tls_send(knot_tls_conn_t *conn, void *data, size_t size)
 {
        if (conn == NULL || data == NULL || size > UINT16_MAX) {
                return KNOT_EINVAL;
@@ -271,10 +279,12 @@ ssize_t knot_tls_send_dns(knot_tls_conn_t *conn, void *data, size_t size)
        // Enable data buffering.
        gnutls_record_cork(conn->session);
 
-       uint16_t msg_len = htons(size);
-       res = gnutls_record_send(conn->session, &msg_len, sizeof(msg_len));
-       if (res != sizeof(msg_len)) {
-               return KNOT_NET_ESEND;
+       if (conn->ctx->flags & KNOT_TLS_DNS) {
+               uint16_t msg_len = htons(size);
+               res = gnutls_record_send(conn->session, &msg_len, sizeof(msg_len));
+               if (res != sizeof(msg_len)) {
+                       return KNOT_NET_ESEND;
+               }
        }
 
        res = gnutls_record_send(conn->session, data, size);
index d77fe137239c7a829be805c7129191f11270e3bf..01454642e7fe6424ac150a8e812dde12219c562b 100644 (file)
@@ -18,6 +18,8 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <libknot/quic/tls_common.h>
+
 struct gnutls_priority_st;
 
 typedef enum {
@@ -30,9 +32,9 @@ typedef enum {
 typedef struct knot_tls_ctx {
        struct knot_creds *creds;
        struct gnutls_priority_st *priority;
+       knot_tls_flag_t flags;
        unsigned handshake_timeout;
        unsigned io_timeout;
-       bool server;
 } knot_tls_ctx_t;
 
 typedef struct knot_tls_conn {
@@ -49,12 +51,12 @@ typedef struct knot_tls_conn {
  * \param creds       Certificate credentials.
  * \param io_timeout  Connections' IO-timeout (in milliseconds).
  * \param hs_timeout  Handshake timeout (in milliseconds).
- * \param server      Server context (otherwise client).
+ * \param flags       Specify client/server mode and common/dns format.
  *
  * \return Initialized context or NULL.
  */
 knot_tls_ctx_t *knot_tls_ctx_new(struct knot_creds *creds, unsigned io_timeout,
-                                 unsigned hs_timeout, bool server);
+                                 unsigned hs_timeout, knot_tls_flag_t flags);
 
 /*!
  * \brief Free DoT answering context.
@@ -118,20 +120,24 @@ int knot_tls_session_load(knot_tls_conn_t *conn, struct knot_tls_session *sessio
 int knot_tls_handshake(knot_tls_conn_t *conn, bool oneshot);
 
 /*!
- * \brief Receive a size-word-prefixed DNS message.
+ * \brief Receive a data blob.
+ *
+ * \note In the DNS mode, the two-byte-size prefix is stripped upon reception,
+ *       not stored to the buffer.
  *
  * \param conn       DoT connection.
  * \param data       Destination buffer.
  * \param size       Maximum buffer size.
  *
  * \return Either the DNS message size received or negative error code.
- *
- * \note The two-byte-size-prefix is stripped upon reception, not stored to the buffer.
  */
-ssize_t knot_tls_recv_dns(knot_tls_conn_t *conn, void *data, size_t size);
+ssize_t knot_tls_recv(knot_tls_conn_t *conn, void *data, size_t size);
 
 /*!
- * \brief Send a size-word-prefixed DNS message.
+ * \brief Send a data blob.
+ *
+ * \note In the DNS mode, the two-byte-size prefix is sended before the data
+ *       blob itself.
  *
  * \param conn      DoT connection.
  * \param data      DNS payload.
@@ -139,7 +145,7 @@ ssize_t knot_tls_recv_dns(knot_tls_conn_t *conn, void *data, size_t size);
  *
  * \return Either exactly 'size' or a negative error code.
  */
-ssize_t knot_tls_send_dns(knot_tls_conn_t *conn, void *data, size_t size);
+ssize_t knot_tls_send(knot_tls_conn_t *conn, void *data, size_t size);
 
 /*!
  * \brief Set or unset the conection's BLOCKED flag.
index edc251cc69bb72ad4b02c91fb318d480d524d683..70c6744f1a0d1ce573c169db4f5faf50fc94fc9a 100644 (file)
@@ -370,26 +370,32 @@ _public_
 int knot_tls_session(struct gnutls_session_int **session,
                      struct knot_creds *creds,
                      struct gnutls_priority_st *priority,
-                     bool quic,
-                     bool early_data,
-                     bool server)
+                     knot_tls_flag_t flags)
 {
        if (session == NULL || creds == NULL || priority == NULL) {
                return KNOT_EINVAL;
        }
 
-       const char *alpn = quic ? "\x03""doq" : "\x03""dot";
-       gnutls_init_flags_t flags = GNUTLS_NO_SIGNAL;
+       bool server = flags & KNOT_TLS_SERVER;
+       bool quic = flags & KNOT_TLS_QUIC;
+       bool early_data = flags & KNOT_TLS_EARLY_DATA;
+
+       const char *alpn = NULL;
+       if (flags & KNOT_TLS_DNS) {
+               alpn = quic ? "\x03""doq" : "\x03""dot";
+       }
+
+       gnutls_init_flags_t tls_flags = GNUTLS_NO_SIGNAL;
        if (early_data) {
-               flags |= GNUTLS_ENABLE_EARLY_DATA;
+               tls_flags |= GNUTLS_ENABLE_EARLY_DATA;
 #ifdef ENABLE_QUIC // Next flags aren't available in older GnuTLS versions.
                if (quic) {
-                       flags |= GNUTLS_NO_END_OF_EARLY_DATA;
+                       tls_flags |= GNUTLS_NO_END_OF_EARLY_DATA;
                }
 #endif
        }
 
-       int ret = gnutls_init(session, (server ? GNUTLS_SERVER : GNUTLS_CLIENT) | flags);
+       int ret = gnutls_init(session, (server ? GNUTLS_SERVER : GNUTLS_CLIENT) | tls_flags);
        if (ret == GNUTLS_E_SUCCESS) {
                gnutls_certificate_send_x509_rdn_sequence(*session, 1);
                gnutls_certificate_server_set_request(*session, GNUTLS_CERT_REQUEST);
@@ -399,8 +405,10 @@ int knot_tls_session(struct gnutls_session_int **session,
                ret = gnutls_session_ticket_enable_server(*session, &creds->tls_ticket_key);
        }
        if (ret == GNUTLS_E_SUCCESS) {
-               const gnutls_datum_t alpn_datum = { (void *)&alpn[1], alpn[0] };
-               gnutls_alpn_set_protocols(*session, &alpn_datum, 1, GNUTLS_ALPN_MANDATORY);
+               if (alpn != NULL) {
+                       const gnutls_datum_t alpn_datum = { (void *)&alpn[1], alpn[0] };
+                       gnutls_alpn_set_protocols(*session, &alpn_datum, 1, GNUTLS_ALPN_MANDATORY);
+               }
                if (early_data) {
                        gnutls_record_set_max_early_data_size(*session, 0xffffffffu);
                }
index b01ca5d59b9eb29d820bc7be864a2ffdcbcb11a4..b2e73f114ff0d1e32ee54614ce91716aa51d95ba 100644 (file)
@@ -26,6 +26,14 @@ struct gnutls_x509_crt_int;
 struct knot_creds;
 struct knot_tls_session;
 
+typedef enum {
+       KNOT_TLS_CLIENT     = 0,
+       KNOT_TLS_SERVER     = (1 << 0),
+       KNOT_TLS_QUIC       = (1 << 1),
+       KNOT_TLS_DNS        = (1 << 2),
+       KNOT_TLS_EARLY_DATA = (1 << 3),
+} knot_tls_flag_t;
+
 /*!
  * \brief Get priority string for GnuTLS priority initialization.
  *
@@ -96,18 +104,14 @@ void knot_creds_free(struct knot_creds *creds);
  * \param session      Out: initialized GnuTLS session struct.
  * \param creds        Certificate credentials.
  * \param priority     Session priority configuration.
- * \param quic         Session is for ngtcp2/QUIC (otherwise TLS).
- * \param early_data   Allow early data.
- * \param server       Should be server session (otherwise client).
+ * \param flags        TLS-related flags.
  *
  * \return KNOT_E*
  */
 int knot_tls_session(struct gnutls_session_int **session,
                      struct knot_creds *creds,
                      struct gnutls_priority_st *priority,
-                     bool quic,
-                     bool early_data,
-                     bool server);
+                     knot_tls_flag_t flags);
 
 /*!
  * \brief Gets local or remote certificate pin.