summaryrefslogtreecommitdiff
path: root/src/libknot/sign/key.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libknot/sign/key.c')
-rw-r--r--src/libknot/sign/key.c561
1 files changed, 561 insertions, 0 deletions
diff --git a/src/libknot/sign/key.c b/src/libknot/sign/key.c
new file mode 100644
index 0000000..d721e64
--- /dev/null
+++ b/src/libknot/sign/key.c
@@ -0,0 +1,561 @@
+/* Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include <assert.h>
+#include <ctype.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "binary.h"
+#include "common.h"
+#include "common/getline.h"
+#include "dname.h"
+#include "sign/key.h"
+#include "sign/sig0.h"
+#include "tsig.h"
+#include "zscanner/scanner.h"
+
+/*!
+ * \brief Calculates keytag for RSA/MD5 algorithm.
+ */
+static uint16_t keytag_rsa_md5(const uint8_t *rdata, uint16_t rdata_len)
+{
+ uint16_t ac = 0;
+ if (rdata_len > 4) {
+ memmove(&ac, rdata + rdata_len - 3, 2);
+ }
+
+ ac = ntohs(ac);
+ return ac;
+}
+
+/*!
+ * \brief Calculates keytag from key wire.
+ */
+uint16_t knot_keytag(const uint8_t *rdata, uint16_t rdata_len)
+{
+ uint32_t ac = 0; /* assumed to be 32 bits or larger */
+
+ if (rdata[3] == 1) {
+ // different algorithm for RSA/MD5 (historical reasons)
+ return keytag_rsa_md5(rdata, rdata_len);
+ } else {
+ for(int i = 0; i < rdata_len; i++) {
+ ac += (i & 1) ? rdata[i] : rdata[i] << 8;
+ }
+
+ ac += (ac >> 16) & 0xFFFF;
+ return (uint16_t)ac & 0xFFFF;
+ }
+}
+
+/*!
+ * \brief Acts like strndup, except it adds a suffix to duplicated string.
+ */
+static char *strndup_with_suffix(const char *base, int length, char *suffix)
+{
+ int result_length = length + strlen(suffix) + 1;
+ char *result = (char *)malloc(result_length);
+ if (!result)
+ return NULL;
+
+ int ret = snprintf(result, result_length, "%.*s%s", length, base, suffix);
+ if (ret < 0 || ret >= result_length) {
+ free(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+static void key_scan_noop(const scanner_t *s)
+{
+ UNUSED(s);
+}
+
+/*!
+ * \brief Reads RR in the public key file and retrieves basic key information.
+ */
+static int get_key_info_from_public_key(const char *filename,
+ knot_dname_t **name,
+ uint16_t *keytag)
+{
+ if (!filename || !name || !keytag)
+ return KNOT_EINVAL;
+
+ FILE *keyfile = fopen(filename, "r");
+ if (!keyfile)
+ return KNOT_KEY_EPUBLIC_KEY_OPEN;
+
+ scanner_t *scanner = scanner_create(filename);
+ if (!scanner) {
+ fclose(keyfile);
+ return KNOT_ENOMEM;
+ }
+
+ scanner->process_record = key_scan_noop;
+ scanner->process_error = key_scan_noop;
+ scanner->default_ttl = 0;
+ scanner->default_class = KNOT_CLASS_IN;
+ scanner->zone_origin[0] = '\0';
+ scanner->zone_origin_length = 1;
+
+ char *buffer = NULL;
+ size_t buffer_size;
+ ssize_t read = knot_getline(&buffer, &buffer_size, keyfile);
+
+ fclose(keyfile);
+
+ if (read == -1) {
+ scanner_free(scanner);
+ return KNOT_KEY_EPUBLIC_KEY_INVALID;
+ }
+
+ if (scanner_process(buffer, buffer + read, true, scanner) != 0) {
+ free(buffer);
+ scanner_free(scanner);
+ return KNOT_KEY_EPUBLIC_KEY_INVALID;
+ }
+
+ free(buffer);
+
+ knot_dname_t *owner = knot_dname_new_from_wire(scanner->r_owner,
+ scanner->r_owner_length,
+ NULL);
+ if (!owner) {
+ scanner_free(scanner);
+ return KNOT_ENOMEM;
+ }
+
+ *name = owner;
+ *keytag = knot_keytag(scanner->r_data, scanner->r_data_length);
+
+ scanner_free(scanner);
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Extract private and public key file names from input filename.
+ *
+ * If the input file name has an empty extension (ends with a dot),
+ * extension 'private', or extension 'key', the appropriate filenames are
+ * derived from the previous part of the string. Otherwise, just append the
+ * extensions.
+ */
+static int get_key_filenames(const char *input, char **pubname, char **privname)
+{
+ assert(input);
+ assert(pubname);
+ assert(privname);
+
+ char *name_end = strrchr(input, '.');
+ size_t base_length;
+
+ if (name_end && (*(name_end + 1) == '\0' ||
+ strcmp(name_end, ".key") == 0 ||
+ strcmp(name_end, ".private") == 0)
+ ) {
+ base_length = name_end - input;
+ } else {
+ base_length = strlen(input);
+ }
+
+ *pubname = strndup_with_suffix(input, base_length, ".key");
+ if (!*pubname) {
+ return KNOT_ENOMEM;
+ }
+
+ *privname = strndup_with_suffix(input, base_length, ".private");
+ if (!*privname) {
+ free(*pubname);
+ *pubname = NULL;
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Handle storing of string type key parameter.
+ */
+static int key_param_string(const void *save_to, char *value)
+{
+ char **parameter = (char **)save_to;
+
+ free(*parameter);
+ *parameter = strdup(value);
+
+ return *parameter ? KNOT_EOK : KNOT_ENOMEM;
+}
+
+/*!
+ * \brief Handle storing of algorithm type key parameter.
+ */
+static int key_param_int(const void *save_to, char *value)
+{
+ int *parameter = (int *)save_to;
+
+ char *value_end;
+ int numeric_value = strtol(value, &value_end, 10);
+
+ if (value == value_end ||
+ (*value_end != '\0' && !isspace((unsigned char)(*value_end))))
+ return KNOT_EINVAL;
+
+ *parameter = numeric_value;
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Describes private key parameter used in key_parameters.
+ */
+struct key_parameter {
+ char *name;
+ size_t offset;
+ int (*handler)(const void *, char *);
+};
+
+#define key_offset(field) offsetof(knot_key_params_t, field)
+
+/*!
+ * \brief Table of know attributes in private key file.
+ *
+ * \todo Save some space, save base64 encoded strings as binary data.
+ */
+static const struct key_parameter key_parameters[] = {
+ { "Algorithm", key_offset(algorithm), key_param_int },
+ { "Key", key_offset(secret), key_param_string },
+ { "Modulus", key_offset(modulus), key_param_string },
+ { "PublicExponent", key_offset(public_exponent), key_param_string },
+ { "PrivateExponent", key_offset(private_exponent), key_param_string },
+ { "Prime1", key_offset(prime_one), key_param_string },
+ { "Prime2", key_offset(prime_two), key_param_string },
+ { "Exponent1", key_offset(exponent_one), key_param_string },
+ { "Exponent2", key_offset(exponent_two), key_param_string },
+ { "Coefficient", key_offset(coefficient), key_param_string },
+ { "Prime(p)", key_offset(prime), key_param_string },
+ { "Subprime(q)", key_offset(subprime), key_param_string },
+ { "Generator(g)", key_offset(generator), key_param_string },
+ { "Base(g)", key_offset(base), key_param_string },
+ { "Private_value(x)",key_offset(private_value), key_param_string },
+ { "Public_value(y)", key_offset(public_value), key_param_string },
+ { "PrivateKey", key_offset(private_key), key_param_string },
+ { NULL }
+};
+
+/*!
+ * \brief Parse one line of key file.
+ *
+ * \param key_params Key parameters to write the result into.
+ * \param line Input line pointer.
+ * \param length Input line length.
+ */
+static int parse_keyfile_line(knot_key_params_t *key_params,
+ char *line, size_t length)
+{
+ // discard line termination
+ if (length > 0 && line[length - 1] == '\n') {
+ line[length - 1] = '\0';
+ length -= 1;
+ }
+
+ // extract attribute name
+ char *separator = memchr(line, ':', length);
+ if (!separator)
+ return KNOT_EOK;
+
+ // find matching attribute
+ size_t name_length = separator - line;
+ for (int i = 0; key_parameters[i].name != NULL; i++) {
+ const struct key_parameter *current = &key_parameters[i];
+ if (memcmp(current->name, line, name_length) != 0)
+ continue;
+
+ assert(current->handler);
+
+ char *value = separator + 1;
+ while (isspace((unsigned char)(*value)))
+ value++;
+
+ void *save_to = (void *)key_params + current->offset;
+ return current->handler(save_to, value);
+ }
+
+ // attribute not supported or not required
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Reads the key file and extracts key parameters.
+ */
+int knot_load_key_params(const char *filename, knot_key_params_t *key_params)
+{
+ assert(filename);
+ assert(key_params);
+
+ int result;
+ char *public_key = NULL;
+ char *private_key = NULL;
+
+ result = get_key_filenames(filename, &public_key, &private_key);
+ if (result != KNOT_EOK) {
+ return result;
+ }
+
+ knot_dname_t *name;
+ uint16_t keytag;
+ result = get_key_info_from_public_key(public_key, &name, &keytag);
+ if (result != KNOT_EOK) {
+ free(public_key);
+ free(private_key);
+ return result;
+ }
+
+ FILE *fp = fopen(private_key, "r");
+ if (!fp) {
+ free(public_key);
+ free(private_key);
+ knot_dname_release(name);
+ return KNOT_KEY_EPRIVATE_KEY_OPEN;
+ }
+
+ key_params->name = name;
+ key_params->keytag = keytag;
+
+ char *buffer = NULL;
+ size_t buffer_size = 0;
+ ssize_t read;
+ while((read = knot_getline(&buffer, &buffer_size, fp)) > 0) {
+ if (buffer[read - 1] == '\n') {
+ read -= 1;
+ buffer[read] = '\0';
+ }
+ result = parse_keyfile_line(key_params, buffer, read);
+ if (result != KNOT_EOK)
+ break;
+ }
+ free(buffer);
+
+ fclose(fp);
+ free(public_key);
+ free(private_key);
+
+ return result;
+}
+
+static int copy_string_if_set(const char *src, char **dst)
+{
+ if (src != NULL) {
+ *dst = strdup(src);
+
+ if (*dst == NULL) {
+ return -1;
+ }
+ } else {
+ *dst = NULL;
+ }
+
+ return 0;
+}
+
+int knot_copy_key_params(const knot_key_params_t *src, knot_key_params_t *dst)
+{
+ if (src == NULL || dst == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = 0;
+
+ if (src->name != NULL) {
+ dst->name = knot_dname_deep_copy(src->name);
+ if (dst->name == NULL) {
+ ret += -1;
+ }
+ }
+
+ dst->algorithm = src->algorithm;
+ dst->keytag = src->keytag;
+
+ ret += copy_string_if_set(src->secret, &dst->secret);
+
+ ret += copy_string_if_set(src->modulus, &dst->modulus);
+ ret += copy_string_if_set(src->public_exponent, &dst->public_exponent);
+ ret += copy_string_if_set(src->private_exponent, &dst->private_exponent);
+ ret += copy_string_if_set(src->prime_one, &dst->prime_one);
+ ret += copy_string_if_set(src->prime_two, &dst->prime_two);
+ ret += copy_string_if_set(src->exponent_one, &dst->exponent_one);
+ ret += copy_string_if_set(src->exponent_two, &dst->exponent_two);
+ ret += copy_string_if_set(src->coefficient, &dst->coefficient);
+
+ ret += copy_string_if_set(src->prime, &dst->prime);
+ ret += copy_string_if_set(src->generator, &dst->generator);
+ ret += copy_string_if_set(src->subprime, &dst->subprime);
+ ret += copy_string_if_set(src->base, &dst->base);
+ ret += copy_string_if_set(src->private_value, &dst->private_value);
+ ret += copy_string_if_set(src->public_value, &dst->public_value);
+
+ ret += copy_string_if_set(src->private_key, &dst->private_key);
+
+ if (ret < 0) {
+ knot_free_key_params(dst);
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Frees the key parameters.
+ */
+int knot_free_key_params(knot_key_params_t *key_params)
+{
+ assert(key_params);
+
+ if (key_params->name)
+ knot_dname_release(key_params->name);
+
+ free(key_params->secret);
+
+ free(key_params->modulus);
+ free(key_params->public_exponent);
+ free(key_params->private_exponent);
+ free(key_params->prime_one);
+ free(key_params->prime_two);
+ free(key_params->exponent_one);
+ free(key_params->exponent_two);
+ free(key_params->coefficient);
+
+ free(key_params->prime);
+ free(key_params->generator);
+ free(key_params->subprime);
+ free(key_params->base);
+ free(key_params->private_value);
+ free(key_params->public_value);
+
+ free(key_params->private_key);
+
+ memset(key_params, '\0', sizeof(knot_key_params_t));
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Get the type of the key.
+ */
+knot_key_type_t knot_get_key_type(const knot_key_params_t *key_params)
+{
+ assert(key_params);
+
+ if (key_params->secret) {
+ return KNOT_KEY_TSIG;
+ }
+
+ if (key_params->modulus || key_params->prime || key_params->private_key) {
+ return KNOT_KEY_DNSSEC;
+ }
+
+ //! \todo TKEY key recognition
+
+ return KNOT_KEY_UNKNOWN;
+}
+
+/*!
+ * \brief Creates TSIG key from function arguments.
+ *
+ * \param name Key name (aka owner name).
+ * \param algorithm Algorithm number.
+ * \param b64secret Shared secret encoded in Base64.
+ * \param key Output TSIG key.
+ *
+ * \return Error code, KNOT_EOK when succeeded.
+ */
+static int knot_tsig_create_key_from_args(knot_dname_t *name, int algorithm,
+ const char *b64secret,
+ knot_tsig_key_t *key)
+{
+ if (!name || !b64secret || !key)
+ return KNOT_EINVAL;
+
+ knot_binary_t secret;
+ int result = knot_binary_from_base64(b64secret, &secret);
+
+ if (result != KNOT_EOK)
+ return result;
+
+ knot_dname_retain(name);
+
+ key->name = name;
+ key->secret = secret;
+ key->algorithm = algorithm;
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Creates TSIG key.
+ */
+int knot_tsig_create_key(const char *name, int algorithm,
+ const char *b64secret, knot_tsig_key_t *key)
+{
+ knot_dname_t *dname;
+ dname = knot_dname_new_from_nonfqdn_str(name, strlen(name), NULL);
+ if (!dname)
+ return KNOT_ENOMEM;
+
+ int res;
+ res = knot_tsig_create_key_from_args(dname, algorithm, b64secret, key);
+
+ knot_dname_release(dname);
+
+ return res;
+}
+
+
+/*!
+ * \brief Creates TSIG key from key parameters.
+ */
+int knot_tsig_key_from_params(const knot_key_params_t *params,
+ knot_tsig_key_t *key)
+{
+ if (!params)
+ return KNOT_EINVAL;
+
+ return knot_tsig_create_key_from_args(params->name, params->algorithm,
+ params->secret, key);
+}
+
+/*!
+ * \brief Frees TSIG key.
+ */
+int knot_tsig_key_free(knot_tsig_key_t *key)
+{
+ if (!key)
+ return KNOT_EINVAL;
+
+ knot_dname_release(key->name);
+
+ knot_binary_free(&key->secret);
+ memset(key, '\0', sizeof(knot_tsig_key_t));
+
+ return KNOT_EOK;
+}