diff options
Diffstat (limited to '')
-rw-r--r-- | src/libdnssec/nsec.h | 155 | ||||
-rw-r--r-- | src/libdnssec/nsec/bitmap.c | 142 | ||||
-rw-r--r-- | src/libdnssec/nsec/hash.c | 125 | ||||
-rw-r--r-- | src/libdnssec/nsec/nsec.c | 130 |
4 files changed, 552 insertions, 0 deletions
diff --git a/src/libdnssec/nsec.h b/src/libdnssec/nsec.h new file mode 100644 index 0000000..19808b0 --- /dev/null +++ b/src/libdnssec/nsec.h @@ -0,0 +1,155 @@ +/* Copyright (C) 2020 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/>. + */ + +/*! + * \file + * + * \addtogroup nsec + * + * \brief NSEC bitmap and NSEC3 hash computation API. + * + * The module provides interface for computation of NSEC3 hashes and for + * construction of bit maps used in NSEC and NSEC3 records. + * + * @{ + */ + +#pragma once + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> + +#include <libdnssec/binary.h> + +/*! + * DNSSEC NSEC3 algorithm numbers. + */ +typedef enum dnssec_nsec_algorithm { + DNSSEC_NSEC3_ALGORITHM_UNKNOWN = 0, + DNSSEC_NSEC3_ALGORITHM_SHA1 = 1, +} dnssec_nsec3_algorithm_t; + +/*! + * DNSSEC NSEC3 parameters. + */ +typedef struct dnssec_nsec3_params { + dnssec_nsec3_algorithm_t algorithm; /*!< NSEC3 algorithm. */ + uint8_t flags; /*!< NSEC3 flags. */ + uint16_t iterations; /*!< NSEC3 iterations count. */ + dnssec_binary_t salt; /*!< NSEC3 salt. */ +} dnssec_nsec3_params_t; + +/*! + * Free NSEC3 parameters. + */ +void dnssec_nsec3_params_free(dnssec_nsec3_params_t *params); + +/*! + * Parse NSEC3 parameters from NSEC3PARAM RDATA. + * + * \param params Output parameters. + * \param rdata NSEC3PARAM RDATA. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_nsec3_params_from_rdata(dnssec_nsec3_params_t *params, + const dnssec_binary_t *rdata); + +/*! + * Check if NSEC3 parameters match. + * + * \param params1 NSEC3 parameters 1. + * \param params2 NSEC3 parameters 2. + * + * \return True if match or if both NULL. + */ +bool dnssec_nsec3_params_match(const dnssec_nsec3_params_t *params1, + const dnssec_nsec3_params_t *params2); + +/*! + * Check whether a given NSEC bitmap contains a given RR type. + * + * \param bitmap Bitmap of an NSEC record. + * \param size Size of the bitmap. + * \param type RR type to check for. + * + * \return true if bitmap contains type, false otherwise. + */ +bool dnssec_nsec_bitmap_contains(const uint8_t *bitmap, uint16_t size, uint16_t type); + +/*! + * Compute NSEC3 hash for given data. + * + * \todo Input data must be converted to lowercase! + * + * \param[in] data Data to be hashed (usually domain name). + * \param[in] params NSEC3 parameters. + * \param[out] hash Computed hash (will be allocated or resized). + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_nsec3_hash(const dnssec_binary_t *data, + const dnssec_nsec3_params_t *params, + dnssec_binary_t *hash); + +/*! + * Get length of raw NSEC3 hash for a given algorithm. + * + * \param algorithm NSEC3 algorithm number. + * + * \return Length of raw NSEC3 hash, zero on error. + */ +size_t dnssec_nsec3_hash_length(dnssec_nsec3_algorithm_t algorithm); + +struct dnssec_nsec_bitmap; + +/*! + * Context for encoding of RR types bitmap used in NSEC/NSEC3. + */ +typedef struct dnssec_nsec_bitmap dnssec_nsec_bitmap_t; + +/*! + * Allocate new bit map encoding context. + */ +dnssec_nsec_bitmap_t *dnssec_nsec_bitmap_new(void); + +/*! + * Clear existing bit map encoding context. + */ +void dnssec_nsec_bitmap_clear(dnssec_nsec_bitmap_t *bitmap); + +/*! + * Free bit map encoding context. + */ +void dnssec_nsec_bitmap_free(dnssec_nsec_bitmap_t *bitmap); + +/*! + * Add one RR type into the bitmap. + */ +void dnssec_nsec_bitmap_add(dnssec_nsec_bitmap_t *bitmap, uint16_t type); + +/*! + * Compute the size of the encoded bitmap. + */ +size_t dnssec_nsec_bitmap_size(const dnssec_nsec_bitmap_t *bitmap); + +/*! + * Write encoded bitmap into the given buffer. + */ +void dnssec_nsec_bitmap_write(const dnssec_nsec_bitmap_t *bitmap, uint8_t *output); + +/*! @} */ diff --git a/src/libdnssec/nsec/bitmap.c b/src/libdnssec/nsec/bitmap.c new file mode 100644 index 0000000..05f8cfa --- /dev/null +++ b/src/libdnssec/nsec/bitmap.c @@ -0,0 +1,142 @@ +/* Copyright (C) 2018 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 <limits.h> +#include <stdint.h> +#include <string.h> + +#include "libdnssec/nsec.h" +#include "libdnssec/shared/shared.h" + +#define BITMAP_WINDOW_SIZE 256 +#define BITMAP_WINDOW_BYTES (BITMAP_WINDOW_SIZE/CHAR_BIT) +#define BITMAP_WINDOW_COUNT 256 + +/*! + * One window of an NSEC bitmap. + */ +typedef struct window { + uint8_t used; + uint8_t data[BITMAP_WINDOW_BYTES]; +} window_t; + +struct dnssec_nsec_bitmap { + int used; + window_t windows[BITMAP_WINDOW_COUNT]; +}; + +/* -- public API ----------------------------------------------------------- */ + +/*! + * Allocate new bit map encoding context. + */ +_public_ +dnssec_nsec_bitmap_t *dnssec_nsec_bitmap_new(void) +{ + dnssec_nsec_bitmap_t *bitmap = malloc(sizeof(*bitmap)); + if (!bitmap) { + return NULL; + } + + dnssec_nsec_bitmap_clear(bitmap); + + return bitmap; +} + +/*! + * Clear existing bit map encoding context. + */ +_public_ +void dnssec_nsec_bitmap_clear(dnssec_nsec_bitmap_t *bitmap) +{ + clear_struct(bitmap); +} + +/*! + * Free bit map encoding context. + */ +_public_ +void dnssec_nsec_bitmap_free(dnssec_nsec_bitmap_t *bitmap) +{ + free(bitmap); +} + +/*! + * Add one RR type into the bitmap. + */ +_public_ +void dnssec_nsec_bitmap_add(dnssec_nsec_bitmap_t *bitmap, uint16_t type) +{ + int win = type / BITMAP_WINDOW_SIZE; + int bit = type % BITMAP_WINDOW_SIZE; + + if (bitmap->used <= win) { + bitmap->used = win + 1; + } + + int win_byte = bit / CHAR_BIT; + int win_bit = bit % CHAR_BIT; + + window_t *window = &bitmap->windows[win]; + window->data[win_byte] |= 0x80 >> win_bit; + if (window->used <= win_byte) { + window->used = win_byte + 1; + } +} + +/*! + * Compute the size of the encoded bitmap. + */ +_public_ +size_t dnssec_nsec_bitmap_size(const dnssec_nsec_bitmap_t *bitmap) +{ + size_t result = 0; + + for (int i = 0; i < bitmap->used; i++) { + int used = bitmap->windows[i].used; + if (used == 0) { + continue; + } + + result += 2 + used; // windows number, window size, data + } + + return result; +} + +/*! + * Write encoded bitmap into the given buffer. + */ +_public_ +void dnssec_nsec_bitmap_write(const dnssec_nsec_bitmap_t *bitmap, uint8_t *output) +{ + uint8_t *write_ptr = output; + for (int win = 0; win < bitmap->used; win++) { + int used = bitmap->windows[win].used; + if (used == 0) { + continue; + } + + *write_ptr = (uint8_t)win; + write_ptr += 1; + + *write_ptr = (uint8_t)used; + write_ptr += 1; + + memmove(write_ptr, bitmap->windows[win].data, used); + write_ptr += used; + } +} diff --git a/src/libdnssec/nsec/hash.c b/src/libdnssec/nsec/hash.c new file mode 100644 index 0000000..b5d46ab --- /dev/null +++ b/src/libdnssec/nsec/hash.c @@ -0,0 +1,125 @@ +/* Copyright (C) 2018 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 <gnutls/gnutls.h> +#include <gnutls/crypto.h> +#include <string.h> + +#include "libdnssec/error.h" +#include "libdnssec/nsec.h" +#include "libdnssec/shared/shared.h" + +/*! + * Compute NSEC3 hash for given data and algorithm. + * + * \see RFC 5155 + * + * \todo Input data should be converted to lowercase. + */ +static int nsec3_hash(gnutls_digest_algorithm_t algorithm, int iterations, + const dnssec_binary_t *salt, const dnssec_binary_t *data, + dnssec_binary_t *hash) +{ + assert(salt); + assert(data); + assert(hash); + + int hash_size = gnutls_hash_get_len(algorithm); + if (hash_size <= 0) { + return DNSSEC_NSEC3_HASHING_ERROR; + } + + int result = dnssec_binary_resize(hash, hash_size); + if (result != DNSSEC_EOK) { + return result; + } + + _cleanup_hash_ gnutls_hash_hd_t digest = NULL; + result = gnutls_hash_init(&digest, algorithm); + if (result < 0) { + return DNSSEC_NSEC3_HASHING_ERROR; + } + + const uint8_t *in = data->data; + size_t in_size = data->size; + + for (int i = 0; i <= iterations; i++) { + result = gnutls_hash(digest, in, in_size); + if (result < 0) { + return DNSSEC_NSEC3_HASHING_ERROR; + } + + result = gnutls_hash(digest, salt->data, salt->size); + if (result < 0) { + return DNSSEC_NSEC3_HASHING_ERROR; + } + + gnutls_hash_output(digest, hash->data); + + in = hash->data; + in_size = hash->size; + } + + return DNSSEC_EOK; +} + +/*! + * Get GnuTLS digest algorithm from DNSSEC algorithm number. + */ +static gnutls_digest_algorithm_t algorithm_d2g(dnssec_nsec3_algorithm_t dnssec) +{ + switch (dnssec) { + case DNSSEC_NSEC3_ALGORITHM_SHA1: return GNUTLS_DIG_SHA1; + default: return GNUTLS_DIG_UNKNOWN; + } +} + +/* -- public API ----------------------------------------------------------- */ + +/*! + * Compute NSEC3 hash for given data. + */ +_public_ +int dnssec_nsec3_hash(const dnssec_binary_t *data, + const dnssec_nsec3_params_t *params, + dnssec_binary_t *hash) +{ + if (!data || !params || !hash) { + return DNSSEC_EINVAL; + } + + gnutls_digest_algorithm_t algorithm = algorithm_d2g(params->algorithm); + if (algorithm == GNUTLS_DIG_UNKNOWN) { + return DNSSEC_INVALID_NSEC3_ALGORITHM; + } + + return nsec3_hash(algorithm, params->iterations, ¶ms->salt, data, hash); +} + +/*! + * Get length of raw NSEC3 hash for a given algorithm. + */ +_public_ +size_t dnssec_nsec3_hash_length(dnssec_nsec3_algorithm_t algorithm) +{ + gnutls_digest_algorithm_t gnutls = algorithm_d2g(algorithm); + if (gnutls == GNUTLS_DIG_UNKNOWN) { + return 0; + } + + return gnutls_hash_get_len(gnutls); +} diff --git a/src/libdnssec/nsec/nsec.c b/src/libdnssec/nsec/nsec.c new file mode 100644 index 0000000..2e71598 --- /dev/null +++ b/src/libdnssec/nsec/nsec.c @@ -0,0 +1,130 @@ +/* Copyright (C) 2018 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 "libdnssec/nsec.h" +#include "libdnssec/shared/shared.h" +#include "libdnssec/shared/binary_wire.h" + +#include "libdnssec/binary.h" +#include "libdnssec/error.h" + +/*! + * Free NSEC3 parameters. + */ +_public_ +void dnssec_nsec3_params_free(dnssec_nsec3_params_t *params) +{ + if (!params) { + return; + } + + dnssec_binary_free(¶ms->salt); + clear_struct(params); +} + +/*! + * Parse NSEC3 parameters from NSEC3PARAM RDATA. + * + * \see RFC 5155 (section 4.2) + */ +_public_ +int dnssec_nsec3_params_from_rdata(dnssec_nsec3_params_t *params, + const dnssec_binary_t *rdata) +{ + if (!params || !rdata || !rdata->data) { + return DNSSEC_EINVAL; + } + + dnssec_nsec3_params_t new_params = { 0 }; + + wire_ctx_t wire = binary_init(rdata); + + if (wire_ctx_available(&wire) < 5) { + return DNSSEC_MALFORMED_DATA; + } + + new_params.algorithm = wire_ctx_read_u8(&wire); + new_params.flags = wire_ctx_read_u8(&wire); + new_params.iterations = wire_ctx_read_u16(&wire); + new_params.salt.size = wire_ctx_read_u8(&wire); + + if (wire_ctx_available(&wire) != new_params.salt.size) { + return DNSSEC_MALFORMED_DATA; + } + + new_params.salt.data = malloc(new_params.salt.size); + if (new_params.salt.data == NULL) { + return DNSSEC_ENOMEM; + } + + binary_read(&wire, &new_params.salt); + assert(wire_ctx_offset(&wire) == rdata->size); + + *params = new_params; + + return DNSSEC_EOK; +} + +_public_ +bool dnssec_nsec_bitmap_contains(const uint8_t *bitmap, uint16_t size, uint16_t type) +{ + if (!bitmap || size == 0) { + return false; + } + + const uint8_t type_hi = (type >> 8); // Which window block contains type. + const uint8_t type_lo = (type & 0xff); + const uint8_t bitmap_idx = (type_lo >> 3); // Which byte in the window block contains type. + const uint8_t bit_mask = 1 << (7 - (type_lo & 0x07)); // Which bit in the byte represents type. + + size_t bitmap_pos = 0; + while (bitmap_pos + 3 <= size) { + uint8_t block_idx = bitmap[bitmap_pos++]; // Skip window block No. + uint8_t block_size = bitmap[bitmap_pos++]; // Skip window block size. + + // Size checks. + if (block_size == 0 || bitmap_pos + block_size > size) { + return false; + } + + // Check whether we found the correct window block. + if (block_idx == type_hi) { + if (bitmap_idx < block_size) { + // Check if the bit for type is set. + return bitmap[bitmap_pos + bitmap_idx] & bit_mask; + } + return false; + } else { + bitmap_pos += block_size; + } + } + + return false; +} + +_public_ +bool dnssec_nsec3_params_match(const dnssec_nsec3_params_t *params1, + const dnssec_nsec3_params_t *params2) +{ + if (params1 != NULL && params2 != NULL) { + return (params1->algorithm == params2->algorithm && + params1->flags == params2->flags && + params1->iterations == params2->iterations && + dnssec_binary_cmp(¶ms1->salt, ¶ms2->salt) == 0); + } else { + return (params1 == NULL && params2 == NULL); + } +} |