From b380ab9024ecccfc31ee9448c21d6fd3d826a0aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=A1k?= <jan.hak@nic.cz> Date: Wed, 5 Mar 2025 13:21:40 +0100 Subject: [PATCH] redis: access redis using GnuTLS --- Knot.files | 7 ++- src/knot/Makefile.inc | 6 ++ src/knot/common/hiredis.c | 127 ++++++++++++++++++++++++++++++++++++++ src/knot/common/hiredis.h | 27 ++++++++ src/knot/conf/schema.c | 2 + src/knot/conf/schema.h | 2 + src/knot/zone/zonefile.c | 27 ++++++++ 7 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 src/knot/common/hiredis.c create mode 100644 src/knot/common/hiredis.h diff --git a/Knot.files b/Knot.files index 2a8ccacb22..73f709de9f 100644 --- a/Knot.files +++ b/Knot.files @@ -32,8 +32,6 @@ src/contrib/libngtcp2/ngtcp2/crypto/shared.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c -src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c -src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h @@ -52,6 +50,8 @@ src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h +src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c +src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_frame_chain.c @@ -180,6 +180,8 @@ src/knot/common/evsched.c src/knot/common/evsched.h src/knot/common/fdset.c src/knot/common/fdset.h +src/knot/common/hiredis.c +src/knot/common/hiredis.h src/knot/common/log.c src/knot/common/log.h src/knot/common/process.c @@ -566,6 +568,7 @@ src/libzscanner/scanner.h src/libzscanner/scanner.rl src/libzscanner/scanner_body.rl src/redis/knot.c +src/redis/misc/events.c src/utils/common/exec.c src/utils/common/exec.h src/utils/common/hex.c diff --git a/src/knot/Makefile.inc b/src/knot/Makefile.inc index 678b0754fa..f6c0a9c09e 100644 --- a/src/knot/Makefile.inc +++ b/src/knot/Makefile.inc @@ -228,6 +228,12 @@ libknotd_la_SOURCES = \ knot/zone/zonefile.c \ knot/zone/zonefile.h +if ENABLE_REDIS +libknotd_la_SOURCES += \ + knot/common/hiredis.c \ + knot/common/hiredis.h +endif ENABLE_REDIS + if ENABLE_QUIC libknotd_la_SOURCES += \ knot/query/quic-requestor.c \ diff --git a/src/knot/common/hiredis.c b/src/knot/common/hiredis.c new file mode 100644 index 0000000000..4c3918b3f3 --- /dev/null +++ b/src/knot/common/hiredis.c @@ -0,0 +1,127 @@ +/* Copyright (C) 2025 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 <unistd.h> +#include <hiredis/alloc.h> +#include <hiredis/sds.h> + +#include "knot/common/hiredis.h" +#include "libknot/errcode.h" +#include "libknot/quic/tls.h" + +typedef struct { + struct knot_tls_ctx *tls; + struct knot_tls_conn *conn; +} redis_tls_ctx_t; + +static void knot_redis_tls_close(redisContext *ctx); +static void knot_redis_tls_free(void *privctx); +static ssize_t knot_redis_tls_read(struct redisContext *ctx, char *buff, size_t size); +static ssize_t knot_redis_tls_write(struct redisContext *ctx); + +redisContextFuncs redisContextGnuTLSFuncs = { + .close = knot_redis_tls_close, + .free_privctx = knot_redis_tls_free, + .read = knot_redis_tls_read, + .write = knot_redis_tls_write +}; + +static void ctx_deinit(redis_tls_ctx_t *ctx) +{ + if (ctx != NULL) { + if (ctx->tls != NULL) { + knot_creds_free(ctx->tls->creds); + } + knot_tls_conn_del(ctx->conn); + knot_tls_ctx_free(ctx->tls); + hi_free(ctx); + } +} + +static void knot_redis_tls_close(redisContext *ctx) +{ + redis_tls_ctx_t *tls_ctx = ctx->privctx; + if (ctx && ctx->fd != REDIS_INVALID_FD) { + knot_tls_conn_del(tls_ctx->conn); + close(ctx->fd); + ctx->fd = REDIS_INVALID_FD; + } +} + +static void knot_redis_tls_free(void *privctx) +{ + redis_tls_ctx_t *tls_ctx = privctx; + ctx_deinit(tls_ctx); +} + +static ssize_t knot_redis_tls_read(struct redisContext *ctx, char *buff, size_t size) +{ + redis_tls_ctx_t *tls_ctx = ctx->privctx; + + int ret = knot_tls_recv(tls_ctx->conn, buff, size); + if (ret >= 0) { + return ret; + } else if (ret == KNOT_NET_ERECV || + ret == KNOT_NET_ECONNECT || + ret == KNOT_NET_EHSHAKE || + ret == KNOT_ETIMEOUT + ) { + return -1; + } + return 0; +} + +static ssize_t knot_redis_tls_write(struct redisContext *ctx) +{ + redis_tls_ctx_t *tls_ctx = ctx->privctx; + + int ret = knot_tls_send(tls_ctx->conn, ctx->obuf, sdslen(ctx->obuf)); + if (ret >= 0) { + return ret; + } else if (ret == KNOT_NET_ESEND || + ret == KNOT_NET_ECONNECT || + ret == KNOT_NET_EHSHAKE || + ret == KNOT_ETIMEOUT + ) { + return -1; + } + return 0; +} + +int hiredis_attach_gnutls(redisContext *ctx, struct knot_creds *creds) +{ + redis_tls_ctx_t *privctx = hi_calloc(1, sizeof(redis_tls_ctx_t)); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + + privctx->tls = knot_tls_ctx_new(creds, 10000, 10000, KNOT_TLS_CLIENT); + if (privctx->tls == NULL) { + ctx_deinit(privctx); + return KNOT_EINVAL; + } + + privctx->conn = knot_tls_conn_new(privctx->tls, ctx->fd); + if (privctx->conn == NULL) { + ctx_deinit(privctx); + return KNOT_ECONN; + } + + ctx->funcs = &redisContextGnuTLSFuncs; + ctx->privctx = privctx; + + return KNOT_EOK; +} diff --git a/src/knot/common/hiredis.h b/src/knot/common/hiredis.h new file mode 100644 index 0000000000..864d7cb0fc --- /dev/null +++ b/src/knot/common/hiredis.h @@ -0,0 +1,27 @@ +/* Copyright (C) 2025 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/>. + */ + +/*! + * \brief Extension of hiredis to support GnuTLS backend. + */ + +#pragma once + +#include <hiredis/hiredis.h> + +#include "libknot/quic/tls_common.h" + +int hiredis_attach_gnutls(redisContext *ctx, struct knot_creds *creds); diff --git a/src/knot/conf/schema.c b/src/knot/conf/schema.c index 4765813667..0388c53fee 100644 --- a/src/knot/conf/schema.c +++ b/src/knot/conf/schema.c @@ -331,6 +331,8 @@ static const yp_item_t desc_database[] = { { C_CATALOG_DB_MAX_SIZE, YP_TINT, YP_VINT = { MEGA(5), VIRT_MEM_LIMIT(GIGA(100)), VIRT_MEM_LIMIT(GIGA(20)), YP_SSIZE } }, { C_ZONE_DB_LISTEN, YP_TADDR, YP_VADDR = { 6379 }, YP_FNONE, { check_listen } }, + { C_ZONE_DB_TLS, YP_TBOOL, YP_VNONE }, + { C_ZONE_DB_CERT_KEY, YP_TB64, YP_VNONE, YP_FMULTI, { check_cert_pin } }, { C_COMMENT, YP_TSTR, YP_VNONE }, { NULL } }; diff --git a/src/knot/conf/schema.h b/src/knot/conf/schema.h index 922aea33e2..431da39db1 100644 --- a/src/knot/conf/schema.h +++ b/src/knot/conf/schema.h @@ -194,6 +194,8 @@ #define C_ZONEMD_VERIFY "\x0D""zonemd-verify" #define C_ZONE_BACKEND "\x0C""zone-backend" #define C_ZONE_DB_LISTEN "\x0E""zone-db-listen" +#define C_ZONE_DB_TLS "\x0B""zone-db-tls" +#define C_ZONE_DB_CERT_KEY "\x10""zone-db-cert-key" #define C_ZONE_MAX_SIZE "\x0D""zone-max-size" #define C_ZONE_MAX_TTL "\x0C""zone-max-ttl" #define C_ZSK_LIFETIME "\x0C""zsk-lifetime" diff --git a/src/knot/zone/zonefile.c b/src/knot/zone/zonefile.c index 58aa897f62..5b64461d9e 100644 --- a/src/knot/zone/zonefile.c +++ b/src/knot/zone/zonefile.c @@ -197,6 +197,9 @@ int zonefile_open(zloader_t *loader, const char *source, const knot_dname_t *ori } #ifdef ENABLE_REDIS + +#include "knot/common/hiredis.h" + redisContext *zone_rdb_connect(conf_t *conf) { conf_val_t db_listen = conf_db_param(conf, C_ZONE_DB_LISTEN); @@ -225,6 +228,30 @@ redisContext *zone_rdb_connect(conf_t *conf) return NULL; } + if (conf_get_bool(conf, C_DB, C_ZONE_DB_TLS)) { + char *cert_file = conf_tls(conf, C_CERT_FILE); + char *key_file = conf_tls(conf, C_KEY_FILE); + // + free(key_file); + free(cert_file); + + conf_val_t val = conf_db_param(conf, C_ZONE_DB_CERT_KEY); + size_t pin_len; + const uint8_t *pin = conf_bin(&val, &pin_len); + struct knot_creds *creds = knot_creds_init_peer(NULL, pin, pin_len); + if (creds == NULL) { + redisFree(rdb); + return NULL; + } + + int ret = hiredis_attach_gnutls(rdb, creds); + if (ret != KNOT_EOK) { + knot_creds_free(creds); + redisFree(rdb); + return NULL; + } + } + return rdb; } -- GitLab