src/libknot/quic/quic.h
src/libknot/quic/quic_conn.c
src/libknot/quic/quic_conn.h
+src/libknot/quic/tls_common.c
+src/libknot/quic/tls_common.h
src/libknot/rdata.h
src/libknot/rdataset.c
src/libknot/rdataset.h
libknot/packet/wire.h \
libknot/probe/data.h \
libknot/probe/probe.h \
+ libknot/quic/tls_common.h \
libknot/rdata.h \
libknot/rdataset.h \
libknot/rrset-dump.h \
libknot/packet/rrset-wire.c \
libknot/probe/data.c \
libknot/probe/probe.c \
+ libknot/quic/tls_common.c \
libknot/rdataset.c \
libknot/rrset-dump.c \
libknot/rrset.c \
return ret;
}
-static int tls_anti_replay_db_add_func(void *dbf, time_t exp_time,
- const gnutls_datum_t *key,
- const gnutls_datum_t *data)
-{
- return 0;
-}
-
-static void tls_session_ticket_key_free(gnutls_datum_t *ticket)
-{
- memzero(ticket->data, ticket->size);
- gnutls_free(ticket->data);
-}
-
-static int self_key(gnutls_x509_privkey_t *privkey, const char *key_file)
-{
- gnutls_datum_t data = { 0 };
-
- int ret = gnutls_x509_privkey_init(privkey);
- if (ret != GNUTLS_E_SUCCESS) {
- return ret;
- }
-
- int fd = open(key_file, O_RDONLY);
- if (fd != -1) {
- struct stat stat;
- if (fstat(fd, &stat) != 0 ||
- (data.data = gnutls_malloc(stat.st_size)) == NULL ||
- read(fd, data.data, stat.st_size) != stat.st_size) {
- ret = GNUTLS_E_KEYFILE_ERROR;
- goto finish;
- }
-
- data.size = stat.st_size;
- ret = gnutls_x509_privkey_import_pkcs8(*privkey, &data, GNUTLS_X509_FMT_PEM,
- NULL, GNUTLS_PKCS_PLAIN);
- if (ret != GNUTLS_E_SUCCESS) {
- goto finish;
- }
- } else {
- ret = gnutls_x509_privkey_generate(*privkey, GNUTLS_PK_EDDSA_ED25519,
- GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_ED25519), 0);
- if (ret != GNUTLS_E_SUCCESS) {
- goto finish;
- }
-
- ret = gnutls_x509_privkey_export2_pkcs8(*privkey, GNUTLS_X509_FMT_PEM, NULL,
- GNUTLS_PKCS_PLAIN, &data);
- if (ret != GNUTLS_E_SUCCESS ||
- (fd = open(key_file, O_WRONLY | O_CREAT, 0600)) == -1 ||
- write(fd, data.data, data.size) != data.size) {
- ret = GNUTLS_E_KEYFILE_ERROR;
- goto finish;
- }
- }
-
-finish:
- close(fd);
- gnutls_free(data.data);
- if (ret != GNUTLS_E_SUCCESS) {
- gnutls_x509_privkey_deinit(*privkey);
- *privkey = NULL;
- }
- return ret;
-}
-
-static int self_signed_cert(gnutls_certificate_credentials_t tls_cert,
- const char *key_file)
-{
- gnutls_x509_privkey_t privkey = NULL;
- gnutls_x509_crt_t cert = NULL;
-
- char *hostname = sockaddr_hostname();
- if (hostname == NULL) {
- return GNUTLS_E_MEMORY_ERROR;
- }
-
- int ret;
- uint8_t serial[16];
- gnutls_rnd(GNUTLS_RND_NONCE, serial, sizeof(serial));
- // Clear the left-most bit to be a positive number (two's complement form).
- serial[0] &= 0x7F;
-
-#define CHK(cmd) if ((ret = (cmd)) != GNUTLS_E_SUCCESS) { goto finish; }
-#define NOW_DAYS(days) (time(NULL) + 24 * 3600 * (days))
-
- CHK(self_key(&privkey, key_file));
-
- CHK(gnutls_x509_crt_init(&cert));
- CHK(gnutls_x509_crt_set_version(cert, 3));
- CHK(gnutls_x509_crt_set_serial(cert, serial, sizeof(serial)));
- CHK(gnutls_x509_crt_set_activation_time(cert, NOW_DAYS(-1)));
- CHK(gnutls_x509_crt_set_expiration_time(cert, NOW_DAYS(10 * 365)));
- CHK(gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0,
- hostname, strlen(hostname)));
- CHK(gnutls_x509_crt_set_key(cert, privkey));
- CHK(gnutls_x509_crt_sign2(cert, cert, privkey, GNUTLS_DIG_SHA512, 0));
-
- ret = gnutls_certificate_set_x509_key(tls_cert, &cert, 1, privkey);
-
-finish:
- free(hostname);
- gnutls_x509_crt_deinit(cert);
- gnutls_x509_privkey_deinit(privkey);
-
- return ret;
-}
-
-_public_
-struct knot_quic_creds *knot_quic_init_creds(const char *cert_file,
- const char *key_file)
-{
- knot_quic_creds_t *creds = calloc(1, sizeof(*creds));
- if (creds == NULL) {
- return NULL;
- }
-
- int ret = gnutls_certificate_allocate_credentials(&creds->tls_cert);
- if (ret != GNUTLS_E_SUCCESS) {
- goto fail;
- }
-
- ret = gnutls_anti_replay_init(&creds->tls_anti_replay);
- if (ret != GNUTLS_E_SUCCESS) {
- goto fail;
- }
- gnutls_anti_replay_set_add_function(creds->tls_anti_replay, tls_anti_replay_db_add_func);
- gnutls_anti_replay_set_ptr(creds->tls_anti_replay, NULL);
-
- if (cert_file != NULL) {
- ret = gnutls_certificate_set_x509_key_file(creds->tls_cert,
- cert_file, key_file,
- GNUTLS_X509_FMT_PEM);
- } else {
- ret = self_signed_cert(creds->tls_cert, key_file);
- }
- if (ret != GNUTLS_E_SUCCESS) {
- goto fail;
- }
-
- ret = gnutls_session_ticket_key_generate(&creds->tls_ticket_key);
- if (ret != GNUTLS_E_SUCCESS) {
- goto fail;
- }
-
- return creds;
-fail:
- knot_quic_free_creds(creds);
- return NULL;
-}
-
-_public_
-struct knot_quic_creds *knot_quic_init_creds_peer(const struct knot_quic_creds *local_creds,
- const uint8_t *peer_pin,
- uint8_t peer_pin_len)
-{
- knot_quic_creds_t *creds = calloc(1, sizeof(*creds) + peer_pin_len);
- if (creds == NULL) {
- return NULL;
- }
-
- if (local_creds != NULL) {
- creds->peer = true;
- creds->tls_cert = local_creds->tls_cert;
- } else {
- int ret = gnutls_certificate_allocate_credentials(&creds->tls_cert);
- if (ret != GNUTLS_E_SUCCESS) {
- free(creds);
- return NULL;
- }
- }
-
- if (peer_pin_len > 0 && peer_pin != NULL) {
- memcpy(creds->peer_pin, peer_pin, peer_pin_len);
- creds->peer_pin_len = peer_pin_len;
- }
-
- return creds;
-}
-
-_public_
-int knot_quic_creds_cert(struct knot_quic_creds *creds, struct gnutls_x509_crt_int **cert)
-{
- if (creds == NULL || cert == NULL) {
- return KNOT_EINVAL;
- }
-
- gnutls_x509_crt_t *certs;
- unsigned cert_count;
- int ret = gnutls_certificate_get_x509_crt(creds->tls_cert, 0, &certs, &cert_count);
- if (ret == GNUTLS_E_SUCCESS) {
- if (cert_count == 0) {
- gnutls_x509_crt_deinit(*certs);
- return KNOT_ENOENT;
- }
- *cert = *certs;
- free(certs);
- }
- return ret;
-}
-
-_public_
-void knot_quic_free_creds(struct knot_quic_creds *creds)
-{
- if (creds == NULL) {
- return;
- }
-
- if (!creds->peer && creds->tls_cert != NULL) {
- gnutls_certificate_free_credentials(creds->tls_cert);
- }
- gnutls_anti_replay_deinit(creds->tls_anti_replay);
- if (creds->tls_ticket_key.data != NULL) {
- tls_session_ticket_key_free(&creds->tls_ticket_key);
- }
- free(creds);
-}
-
static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref)
{
return ((knot_quic_conn_t *)conn_ref->user_data)->conn;
#include <netinet/in.h>
#include "libknot/quic/quic_conn.h"
-
-#define KNOT_QUIC_PIN_LEN 32
+#include "libknot/quic/tls_common.h"
#define KNOT_QUIC_HANDLE_RET_CLOSE 2000
// RFC 9250
#define KNOT_QUIC_ERR_EXCESSIVE_LOAD 0x4
-struct gnutls_x509_crt_int;
-struct knot_quic_creds;
struct knot_quic_session;
typedef enum {
*/
int knot_quic_session_load(knot_quic_conn_t *conn, struct knot_quic_session *session);
-/*!
- * \brief Init server TLS certificate for DoQ.
- *
- * \param cert_file X509 certificate PEM file path/name (NULL if auto-generated).
- * \param key_file Key PEM file path/name.
- *
- * \return Initialized creds.
- */
-struct knot_quic_creds *knot_quic_init_creds(const char *cert_file,
- const char *key_file);
-
-/*!
- * \brief Init peer TLS certificate for DoQ.
- *
- * \param local_creds Local credentials if server.
- * \param peer_pin Optional peer certificate pin to check.
- * \param peer_pin_len Length of the peer pin. Set 0 if not specified.
- *
- * \return Initialized creds.
- */
-struct knot_quic_creds *knot_quic_init_creds_peer(const struct knot_quic_creds *local_creds,
- const uint8_t *peer_pin,
- uint8_t peer_pin_len);
-
-/*!
- * \brief Gets the certificate from credentials.
- *
- * \param creds TLS credentials.
- * \param cert Output certificate.
- *
- * \return KNOT_E*
- */
-int knot_quic_creds_cert(struct knot_quic_creds *creds, struct gnutls_x509_crt_int **cert);
-
-/*!
- * \brief Deinit server TLS certificate for DoQ.
- */
-void knot_quic_free_creds(struct knot_quic_creds *creds);
-
/*!
* \brief Returns timeout value for the connection.
*/
--- /dev/null
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "libknot/quic/tls_common.h"
+
+#include "contrib/sockaddr.h"
+#include "contrib/string.h"
+#include "libknot/attribute.h"
+#include "libknot/error.h"
+
+#include <fcntl.h>
+#include <gnutls/crypto.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+typedef struct knot_quic_creds {
+ gnutls_certificate_credentials_t tls_cert;
+ gnutls_anti_replay_t tls_anti_replay;
+ gnutls_datum_t tls_ticket_key;
+ bool peer;
+ uint8_t peer_pin_len;
+ uint8_t peer_pin[];
+} knot_quic_creds_t;
+
+static int tls_anti_replay_db_add_func(void *dbf, time_t exp_time,
+ const gnutls_datum_t *key,
+ const gnutls_datum_t *data)
+{
+ return 0;
+}
+
+static void tls_session_ticket_key_free(gnutls_datum_t *ticket)
+{
+ memzero(ticket->data, ticket->size);
+ gnutls_free(ticket->data);
+}
+
+static int self_key(gnutls_x509_privkey_t *privkey, const char *key_file)
+{
+ gnutls_datum_t data = { 0 };
+
+ int ret = gnutls_x509_privkey_init(privkey);
+ if (ret != GNUTLS_E_SUCCESS) {
+ return ret;
+ }
+
+ int fd = open(key_file, O_RDONLY);
+ if (fd != -1) {
+ struct stat stat;
+ if (fstat(fd, &stat) != 0 ||
+ (data.data = gnutls_malloc(stat.st_size)) == NULL ||
+ read(fd, data.data, stat.st_size) != stat.st_size) {
+ ret = GNUTLS_E_KEYFILE_ERROR;
+ goto finish;
+ }
+
+ data.size = stat.st_size;
+ ret = gnutls_x509_privkey_import_pkcs8(*privkey, &data, GNUTLS_X509_FMT_PEM,
+ NULL, GNUTLS_PKCS_PLAIN);
+ if (ret != GNUTLS_E_SUCCESS) {
+ goto finish;
+ }
+ } else {
+ ret = gnutls_x509_privkey_generate(*privkey, GNUTLS_PK_EDDSA_ED25519,
+ GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_ED25519), 0);
+ if (ret != GNUTLS_E_SUCCESS) {
+ goto finish;
+ }
+
+ ret = gnutls_x509_privkey_export2_pkcs8(*privkey, GNUTLS_X509_FMT_PEM, NULL,
+ GNUTLS_PKCS_PLAIN, &data);
+ if (ret != GNUTLS_E_SUCCESS ||
+ (fd = open(key_file, O_WRONLY | O_CREAT, 0600)) == -1 ||
+ write(fd, data.data, data.size) != data.size) {
+ ret = GNUTLS_E_KEYFILE_ERROR;
+ goto finish;
+ }
+ }
+
+finish:
+ close(fd);
+ gnutls_free(data.data);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_x509_privkey_deinit(*privkey);
+ *privkey = NULL;
+ }
+ return ret;
+}
+
+static int self_signed_cert(gnutls_certificate_credentials_t tls_cert,
+ const char *key_file)
+{
+ gnutls_x509_privkey_t privkey = NULL;
+ gnutls_x509_crt_t cert = NULL;
+
+ char *hostname = sockaddr_hostname();
+ if (hostname == NULL) {
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ int ret;
+ uint8_t serial[16];
+ gnutls_rnd(GNUTLS_RND_NONCE, serial, sizeof(serial));
+ // Clear the left-most bit to be a positive number (two's complement form).
+ serial[0] &= 0x7F;
+
+#define CHK(cmd) if ((ret = (cmd)) != GNUTLS_E_SUCCESS) { goto finish; }
+#define NOW_DAYS(days) (time(NULL) + 24 * 3600 * (days))
+
+ CHK(self_key(&privkey, key_file));
+
+ CHK(gnutls_x509_crt_init(&cert));
+ CHK(gnutls_x509_crt_set_version(cert, 3));
+ CHK(gnutls_x509_crt_set_serial(cert, serial, sizeof(serial)));
+ CHK(gnutls_x509_crt_set_activation_time(cert, NOW_DAYS(-1)));
+ CHK(gnutls_x509_crt_set_expiration_time(cert, NOW_DAYS(10 * 365)));
+ CHK(gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0,
+ hostname, strlen(hostname)));
+ CHK(gnutls_x509_crt_set_key(cert, privkey));
+ CHK(gnutls_x509_crt_sign2(cert, cert, privkey, GNUTLS_DIG_SHA512, 0));
+
+ ret = gnutls_certificate_set_x509_key(tls_cert, &cert, 1, privkey);
+
+finish:
+ free(hostname);
+ gnutls_x509_crt_deinit(cert);
+ gnutls_x509_privkey_deinit(privkey);
+
+ return ret;
+}
+
+_public_
+struct knot_quic_creds *knot_quic_init_creds(const char *cert_file,
+ const char *key_file)
+{
+ knot_quic_creds_t *creds = calloc(1, sizeof(*creds));
+ if (creds == NULL) {
+ return NULL;
+ }
+
+ int ret = gnutls_certificate_allocate_credentials(&creds->tls_cert);
+ if (ret != GNUTLS_E_SUCCESS) {
+ goto fail;
+ }
+
+ ret = gnutls_anti_replay_init(&creds->tls_anti_replay);
+ if (ret != GNUTLS_E_SUCCESS) {
+ goto fail;
+ }
+ gnutls_anti_replay_set_add_function(creds->tls_anti_replay, tls_anti_replay_db_add_func);
+ gnutls_anti_replay_set_ptr(creds->tls_anti_replay, NULL);
+
+ if (cert_file != NULL) {
+ ret = gnutls_certificate_set_x509_key_file(creds->tls_cert,
+ cert_file, key_file,
+ GNUTLS_X509_FMT_PEM);
+ } else {
+ ret = self_signed_cert(creds->tls_cert, key_file);
+ }
+ if (ret != GNUTLS_E_SUCCESS) {
+ goto fail;
+ }
+
+ ret = gnutls_session_ticket_key_generate(&creds->tls_ticket_key);
+ if (ret != GNUTLS_E_SUCCESS) {
+ goto fail;
+ }
+
+ return creds;
+fail:
+ knot_quic_free_creds(creds);
+ return NULL;
+}
+
+_public_
+struct knot_quic_creds *knot_quic_init_creds_peer(const struct knot_quic_creds *local_creds,
+ const uint8_t *peer_pin,
+ uint8_t peer_pin_len)
+{
+ knot_quic_creds_t *creds = calloc(1, sizeof(*creds) + peer_pin_len);
+ if (creds == NULL) {
+ return NULL;
+ }
+
+ if (local_creds != NULL) {
+ creds->peer = true;
+ creds->tls_cert = local_creds->tls_cert;
+ } else {
+ int ret = gnutls_certificate_allocate_credentials(&creds->tls_cert);
+ if (ret != GNUTLS_E_SUCCESS) {
+ free(creds);
+ return NULL;
+ }
+ }
+
+ if (peer_pin_len > 0 && peer_pin != NULL) {
+ memcpy(creds->peer_pin, peer_pin, peer_pin_len);
+ creds->peer_pin_len = peer_pin_len;
+ }
+
+ return creds;
+}
+
+_public_
+int knot_quic_creds_cert(struct knot_quic_creds *creds, struct gnutls_x509_crt_int **cert)
+{
+ if (creds == NULL || cert == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ gnutls_x509_crt_t *certs;
+ unsigned cert_count;
+ int ret = gnutls_certificate_get_x509_crt(creds->tls_cert, 0, &certs, &cert_count);
+ if (ret == GNUTLS_E_SUCCESS) {
+ if (cert_count == 0) {
+ gnutls_x509_crt_deinit(*certs);
+ return KNOT_ENOENT;
+ }
+ *cert = *certs;
+ free(certs);
+ }
+ return ret;
+}
+
+_public_
+void knot_quic_free_creds(struct knot_quic_creds *creds)
+{
+ if (creds == NULL) {
+ return;
+ }
+
+ if (!creds->peer && creds->tls_cert != NULL) {
+ gnutls_certificate_free_credentials(creds->tls_cert);
+ }
+ gnutls_anti_replay_deinit(creds->tls_anti_replay);
+ if (creds->tls_ticket_key.data != NULL) {
+ tls_session_ticket_key_free(&creds->tls_ticket_key);
+ }
+ free(creds);
+}
--- /dev/null
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#define KNOT_QUIC_PIN_LEN 32
+
+struct gnutls_session_int;
+struct gnutls_x509_crt_int;
+struct knot_quic_creds;
+
+/*!
+ * \brief Init server TLS certificate for DoQ.
+ *
+ * \param cert_file X509 certificate PEM file path/name (NULL if auto-generated).
+ * \param key_file Key PEM file path/name.
+ *
+ * \return Initialized creds.
+ */
+struct knot_quic_creds *knot_quic_init_creds(const char *cert_file,
+ const char *key_file);
+
+/*!
+ * \brief Init peer TLS certificate for DoQ.
+ *
+ * \param local_creds Local credentials if server.
+ * \param peer_pin Optional peer certificate pin to check.
+ * \param peer_pin_len Length of the peer pin. Set 0 if not specified.
+ *
+ * \return Initialized creds.
+ */
+struct knot_quic_creds *knot_quic_init_creds_peer(const struct knot_quic_creds *local_creds,
+ const uint8_t *peer_pin,
+ uint8_t peer_pin_len);
+
+/*!
+ * \brief Gets the certificate from credentials.
+ *
+ * \param creds TLS credentials.
+ * \param cert Output certificate.
+ *
+ * \return KNOT_E*
+ */
+int knot_quic_creds_cert(struct knot_quic_creds *creds, struct gnutls_x509_crt_int **cert);
+
+/*!
+ * \brief Deinit server TLS certificate for DoQ.
+ */
+void knot_quic_free_creds(struct knot_quic_creds *creds);