diff options
Diffstat (limited to 'src/knot/nameserver/tsig_ctx.c')
-rw-r--r-- | src/knot/nameserver/tsig_ctx.c | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/src/knot/nameserver/tsig_ctx.c b/src/knot/nameserver/tsig_ctx.c new file mode 100644 index 0000000..05383b1 --- /dev/null +++ b/src/knot/nameserver/tsig_ctx.c @@ -0,0 +1,189 @@ +/* Copyright (C) 2019 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 <assert.h> + +#include "knot/nameserver/tsig_ctx.h" +#include "contrib/string.h" +#include "libknot/libknot.h" + +/*! + * Maximal total size for unsigned messages. + */ +static const size_t TSIG_BUFFER_MAX_SIZE = (UINT16_MAX * 100); + +void tsig_init(tsig_ctx_t *ctx, const knot_tsig_key_t *key) +{ + if (!ctx) { + return; + } + + memzero(ctx, sizeof(*ctx)); + ctx->key = key; +} + +void tsig_cleanup(tsig_ctx_t *ctx) +{ + if (!ctx) { + return; + } + + free(ctx->buffer); + memzero(ctx, sizeof(*ctx)); +} + +void tsig_reset(tsig_ctx_t *ctx) +{ + if (!ctx) { + return; + } + + const knot_tsig_key_t *backup = ctx->key; + tsig_cleanup(ctx); + tsig_init(ctx, backup); +} + +int tsig_sign_packet(tsig_ctx_t *ctx, knot_pkt_t *packet) +{ + if (!ctx || !packet) { + return KNOT_EINVAL; + } + + if (ctx->key == NULL) { + return KNOT_EOK; + } + + int ret = KNOT_ERROR; + if (ctx->digest_size == 0) { + ctx->digest_size = dnssec_tsig_algorithm_size(ctx->key->algorithm); + ret = knot_tsig_sign(packet->wire, &packet->size, packet->max_size, + NULL, 0, + ctx->digest, &ctx->digest_size, + ctx->key, 0, 0); + } else { + uint8_t previous_digest[ctx->digest_size]; + memcpy(previous_digest, ctx->digest, ctx->digest_size); + + ret = knot_tsig_sign_next(packet->wire, &packet->size, packet->max_size, + previous_digest, ctx->digest_size, + ctx->digest, &ctx->digest_size, + ctx->key, packet->wire, packet->size); + } + + return ret; +} + +static int update_ctx_after_verify(tsig_ctx_t *ctx, knot_rrset_t *tsig_rr) +{ + assert(ctx); + assert(tsig_rr); + + if (ctx->digest_size != knot_tsig_rdata_mac_length(tsig_rr)) { + return KNOT_EMALF; + } + + memcpy(ctx->digest, knot_tsig_rdata_mac(tsig_rr), ctx->digest_size); + ctx->prev_signed_time = knot_tsig_rdata_time_signed(tsig_rr); + ctx->unsigned_count = 0; + ctx->buffer_used = 0; + + return KNOT_EOK; +} + +static int buffer_add_packet(tsig_ctx_t *ctx, knot_pkt_t *packet) +{ + size_t need = ctx->buffer_used + packet->size; + + // Inflate the buffer if necessary. + + if (need > TSIG_BUFFER_MAX_SIZE) { + return KNOT_ENOMEM; + } + + if (need > ctx->buffer_size) { + uint8_t *buffer = realloc(ctx->buffer, need); + if (!buffer) { + return KNOT_ENOMEM; + } + + ctx->buffer = buffer; + ctx->buffer_size = need; + } + + // Buffer the packet. + + uint8_t *write = ctx->buffer + ctx->buffer_used; + memcpy(write, packet->wire, packet->size); + ctx->buffer_used = need; + + return KNOT_EOK; +} + +int tsig_verify_packet(tsig_ctx_t *ctx, knot_pkt_t *packet) +{ + if (!ctx || !packet) { + return KNOT_EINVAL; + } + + if (ctx->key == NULL) { + return KNOT_EOK; + } + + int ret = buffer_add_packet(ctx, packet); + if (ret != KNOT_EOK) { + return ret; + } + + // Unsigned packet. + + if (packet->tsig_rr == NULL) { + ctx->unsigned_count += 1; + return KNOT_EOK; + } + + // Signed packet. + + if (ctx->prev_signed_time == 0) { + ret = knot_tsig_client_check(packet->tsig_rr, ctx->buffer, + ctx->buffer_used, ctx->digest, + ctx->digest_size, ctx->key, 0); + } else { + ret = knot_tsig_client_check_next(packet->tsig_rr, ctx->buffer, + ctx->buffer_used, ctx->digest, + ctx->digest_size, ctx->key, + ctx->prev_signed_time); + } + + if (ret != KNOT_EOK) { + return ret; + } + + ret = update_ctx_after_verify(ctx, packet->tsig_rr); + if (ret != KNOT_EOK) { + return ret; + } + + return KNOT_EOK; +} + +unsigned tsig_unsigned_count(tsig_ctx_t *ctx) +{ + if (!ctx) { + return -1; + } + + return ctx->unsigned_count; +} |