mirror of
https://gitlab.nic.cz/knot/knot-dns.git
synced 2026-05-28 04:02:31 -04:00
libknot: extend TLS API to be used for non-DNS communication
This commit is contained in:
parent
c7b11a74c9
commit
fb9b2cc102
10 changed files with 92 additions and 61 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue