From: Daniel Salzman Date: Sat, 8 Mar 2025 19:40:34 +0000 (+0100) Subject: libknot: extend TLS API to be used for non-DNS communication X-Git-Tag: v3.5.0~90 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fb9b2cc102e7c16cd39198486d6d01fdc44b00ec;p=thirdparty%2Fknot-dns.git libknot: extend TLS API to be used for non-DNS communication --- diff --git a/distro/pkg/deb/libknot15.symbols b/distro/pkg/deb/libknot15.symbols index 48599c9428..50406c1291 100644 --- a/distro/pkg/deb/libknot15.symbols +++ b/distro/pkg/deb/libknot15.symbols @@ -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 diff --git a/src/knot/events/handlers/update.c b/src/knot/events/handlers/update.c index ebb18b50da..287369d93f 100644 --- a/src/knot/events/handlers/update.c +++ b/src/knot/events/handlers/update.c @@ -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 diff --git a/src/knot/query/requestor.c b/src/knot/query/requestor.c index 4ecfe5e8cb..cb0b45af4a 100644 --- a/src/knot/query/requestor.c +++ b/src/knot/query/requestor.c @@ -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 diff --git a/src/knot/query/tls-requestor.c b/src/knot/query/tls-requestor.c index 4e539632c1..04159e48ea 100644 --- a/src/knot/query/tls-requestor.c +++ b/src/knot/query/tls-requestor.c @@ -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; diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c index abd2973789..c0b7e27e65 100644 --- a/src/knot/server/tcp-handler.c +++ b/src/knot/server/tcp-handler.c @@ -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; diff --git a/src/libknot/quic/quic.c b/src/libknot/quic/quic.c index 33fbaeea5b..9bdc4e8eb1 100644 --- a/src/libknot/quic/quic.c +++ b/src/libknot/quic/quic.c @@ -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; } diff --git a/src/libknot/quic/tls.c b/src/libknot/quic/tls.c index 41b8abaeca..5edaaf88a0 100644 --- a/src/libknot/quic/tls.c +++ b/src/libknot/quic/tls.c @@ -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); diff --git a/src/libknot/quic/tls.h b/src/libknot/quic/tls.h index d77fe13723..01454642e7 100644 --- a/src/libknot/quic/tls.h +++ b/src/libknot/quic/tls.h @@ -18,6 +18,8 @@ #include #include +#include + 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. diff --git a/src/libknot/quic/tls_common.c b/src/libknot/quic/tls_common.c index edc251cc69..70c6744f1a 100644 --- a/src/libknot/quic/tls_common.c +++ b/src/libknot/quic/tls_common.c @@ -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); } diff --git a/src/libknot/quic/tls_common.h b/src/libknot/quic/tls_common.h index b01ca5d59b..b2e73f114f 100644 --- a/src/libknot/quic/tls_common.h +++ b/src/libknot/quic/tls_common.h @@ -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.