diff --git a/Knot.files b/Knot.files index 5562463f41daed35d8c2c30034e91b8550265d2c..b7aa3eb0a015b3731387e96bbf43b9d75d4f402e 100644 --- a/Knot.files +++ b/Knot.files @@ -451,6 +451,8 @@ src/utils/keymgr/legacy/privkey.c src/utils/keymgr/legacy/privkey.h src/utils/keymgr/legacy/pubkey.c src/utils/keymgr/legacy/pubkey.h +src/utils/keymgr/options.c +src/utils/keymgr/options.h src/utils/khost/khost_main.c src/utils/khost/khost_params.c src/utils/khost/khost_params.h diff --git a/Knot.includes b/Knot.includes index 9d34de5639b1d0d8ba7d8a6d65d8d7b054228077..076d39e8627270c745a8bd79bf582fbfbc6d53e9 100644 --- a/Knot.includes +++ b/Knot.includes @@ -1,10 +1,10 @@ libtap src src/contrib +src/dnssec src/dnssec/lib src/dnssec/lib/dnssec src/dnssec/shared -src/dnssec/utils src/zscanner tests tests-fuzz diff --git a/src/Makefile.am b/src/Makefile.am index 7d40b8e4e4675d610cd02ec1e5c802a5c0a9a965..71e56e6191ea9041d352360278d9f274989f1f92 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -246,6 +246,8 @@ keymgr_SOURCES = \ utils/keymgr/legacy/privkey.h \ utils/keymgr/legacy/pubkey.c \ utils/keymgr/legacy/pubkey.h \ + utils/keymgr/options.c \ + utils/keymgr/options.h \ utils/keymgr/keymgr.c knotc_SOURCES = \ @@ -422,7 +424,8 @@ libknotd_la_LDFLAGS = $(AM_LDFLAGS) $(systemd_LIBS) $(liburcu_LIBS) libknotd_la_LIBADD = libknot.la libknot-yparser.la zscanner/libzscanner.la $(liburcu_LIBS) keymgr_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/dnssec/lib/dnssec -I$(srcdir)/dnssec $(gnutls_CFLAGS) -keymgr_LDADD = dnssec/libdnssec.la dnssec/libshared.la zscanner/libzscanner.la $(gnutls_LIBS) +keymgr_LDADD = libknotd.la libknotus.la dnssec/libdnssec.la dnssec/libshared.la \ + zscanner/libzscanner.la $(gnutls_LIBS) knotd_CPPFLAGS = $(AM_CPPFLAGS) $(liburcu_CFLAGS) knotd_LDADD = libknotd.la $(liburcu_LIBS) knotc_CPPFLAGS = $(AM_CPPFLAGS) $(libedit_CFLAGS) diff --git a/src/utils/keymgr/keymgr.c b/src/utils/keymgr/keymgr.c index 05355ce421ef4c4ed5d8b51393101890e23e2046..67bc88ee18c8c57502349fee786e230751b1834e 100644 --- a/src/utils/keymgr/keymgr.c +++ b/src/utils/keymgr/keymgr.c @@ -31,6 +31,7 @@ #include "cmdparse/parameter.h" #include "cmdparse/value.h" #include "legacy/key.h" +#include "options.h" #include "shared/dname.h" #include "shared/print.h" #include "shared/shared.h" @@ -46,12 +47,6 @@ /* -- global options ------------------------------------------------------- */ -struct options { - char *kasp_dir; -}; - -typedef struct options options_t; - static options_t global = { 0 }; /* -- internal ------------------------------------------------------------- */ @@ -95,12 +90,21 @@ static void cleanup_list(dnssec_list_t **list_ptr) /* -- frequent operations -------------------------------------------------- */ -static dnssec_kasp_t *get_kasp(void) +static dnssec_kasp_t *get_zone_kasp(const char *zone_name) { + int r = options_zone_kasp_path(&global, zone_name); + if (r != DNSSEC_EOK) { + return NULL; + } + dnssec_kasp_t *kasp = NULL; - dnssec_kasp_init_dir(&kasp); - int r = dnssec_kasp_open(kasp, global.kasp_dir); + r = options_zone_kasp_init(&global, zone_name, &kasp); + if (r != DNSSEC_EOK) { + return NULL; + } + + r = dnssec_kasp_open(kasp, global.kasp_dir); if (r != DNSSEC_EOK) { error("Cannot open KASP directory (%s).", dnssec_strerror(r)); dnssec_kasp_deinit(kasp); @@ -110,6 +114,11 @@ static dnssec_kasp_t *get_kasp(void) return kasp; } +static dnssec_kasp_t *get_kasp(void) +{ + return get_zone_kasp(NULL); +} + static dnssec_kasp_zone_t *get_zone(dnssec_kasp_t *kasp, const char *name) { dnssec_kasp_zone_t *zone = NULL; @@ -660,7 +669,7 @@ static int cmd_zone_add(int argc, char *argv[]) // create zone - _cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp(); + _cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name); if (!kasp) { return 1; } @@ -710,7 +719,7 @@ static int cmd_zone_list(int argc, char *argv[]) return 1; } - _cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp(); + _cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(match); if (!kasp) { return 1; } @@ -740,7 +749,7 @@ static int cmd_zone_show(int argc, char *argv[]) char *zone_name = argv[0]; - _cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp(); + _cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name); if (!kasp) { return 1; } @@ -796,7 +805,7 @@ static int cmd_zone_remove(int argc, char *argv[]) // delete zone - _cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp(); + _cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name); if (!kasp) { return 1; } @@ -836,7 +845,7 @@ static int cmd_zone_key_list(int argc, char *argv[]) // list the keys - _cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp(); + _cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name); if (!kasp) { return 1; } @@ -871,7 +880,7 @@ static int cmd_zone_key_show(int argc, char *argv[]) // list the keys - _cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp(); + _cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name); if (!kasp) { return 1; } @@ -960,7 +969,7 @@ static int cmd_zone_key_ds(int argc, char *argv[]) const char *zone_name = argv[0]; const char *key_name = argv[1]; - _cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp(); + _cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name); _cleanup_zone_ dnssec_kasp_zone_t *zone = get_zone(kasp, zone_name); dnssec_list_t *keys = dnssec_kasp_zone_get_keys(zone); @@ -1048,7 +1057,7 @@ static int cmd_zone_key_generate(int argc, char *argv[]) // open KASP and key store - _cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp(); + _cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(config.name); if (!kasp) { return 1; } @@ -1144,7 +1153,7 @@ static int cmd_zone_key_set(int argc, char *argv[]) // update the key - _cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp(); + _cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name); if (!kasp) { return 1; } @@ -1188,7 +1197,7 @@ static int cmd_zone_key_import(int argc, char *argv[]) char *zone_name = argv[0]; char *input_file = argv[1]; - _cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp(); + _cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name); if (!kasp) { return 1; } @@ -1275,7 +1284,7 @@ static int cmd_zone_set(int argc, char *argv[]) char *zone_name = argv[0]; - _cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp(); + _cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name); if (!kasp) { return 1; } @@ -1313,16 +1322,22 @@ static int cmd_zone_set(int argc, char *argv[]) static int cmd_zone(int argc, char *argv[]) { - static const command_t commands[] = { + static command_t commands[] = { + { "key", cmd_zone_key }, + // legacy operations { "add", cmd_zone_add }, { "list", cmd_zone_list }, { "remove", cmd_zone_remove }, { "show", cmd_zone_show }, - { "key", cmd_zone_key }, { "set", cmd_zone_set }, { NULL } }; + // disable legacy operations + if (!global.legacy) { + commands[1].name = NULL; + } + return subcommand(commands, argc, argv); } @@ -1800,15 +1815,24 @@ int main(int argc, char *argv[]) // global options static const struct option opts[] = { + { "config", required_argument, NULL, 'c' }, + { "confdb", required_argument, NULL, 'C' }, { "dir", required_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, + { "legacy", no_argument, NULL, 'l' }, { "version", no_argument, NULL, 'V' }, { NULL } }; int c = 0; - while (c = getopt_long(argc, argv, "+", opts, NULL), c != -1) { + while (c = getopt_long(argc, argv, "+c:C:d:hlV", opts, NULL), c != -1) { switch (c) { + case 'c': + global.config = optarg; + break; + case 'C': + global.confdb = optarg; + break; case 'd': free(global.kasp_dir); global.kasp_dir = strdup(optarg); @@ -1817,50 +1841,48 @@ int main(int argc, char *argv[]) print_help(); exit_code = 0; goto failed; + case 'l': + global.legacy = true; + break; case 'V': print_version(); exit_code = 0; goto failed; - case '?': - goto failed; default: - assert_unreachable(); goto failed; } } // global configuration - if (global.kasp_dir == NULL) { - char *env = getenv("KEYMGR_DIR"); - if (env) { - global.kasp_dir = strdup(env); - } else { - global.kasp_dir = getcwd(NULL, 0); - } + int r = options_init(&global); + if (r != DNSSEC_EOK) { + goto failed; } - assert(global.kasp_dir); - // subcommands - static const command_t commands[] = { - { "init", cmd_init }, + static command_t commands[] = { + { "tsig", cmd_tsig }, { "zone", cmd_zone }, + // legacy operations + { "init", cmd_init }, { "policy", cmd_policy }, { "keystore", cmd_keystore }, - { "tsig", cmd_tsig }, { NULL } }; - dnssec_crypto_init(); + // disable legacy operations + if (!global.legacy) { + commands[2].name = NULL; + } + dnssec_crypto_init(); exit_code = subcommand(commands, argc - optind, argv + optind); - -failed: dnssec_crypto_cleanup(); - free(global.kasp_dir); +failed: + options_cleanup(&global); return exit_code; } diff --git a/src/utils/keymgr/options.c b/src/utils/keymgr/options.c new file mode 100644 index 0000000000000000000000000000000000000000..370e4bdf27505cdee651bdba93d4a150d1fb6507 --- /dev/null +++ b/src/utils/keymgr/options.c @@ -0,0 +1,179 @@ +/* Copyright (C) 2016 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 <sys/stat.h> +#include <unistd.h> + +#include <dnssec/error.h> +#include "knot/conf/conf.h" +#include "knot/dnssec/context.h" +#include "options.h" +#include "shared/print.h" + +int options_init(options_t *options) +{ + if (options == NULL) { + return DNSSEC_EINVAL; + } + + if (options->kasp_dir == NULL) { + char *env = getenv("KEYMGR_DIR"); + if (env != NULL) { + options->kasp_dir = strdup(env); + } + } + + if (options->legacy) { + if (options->kasp_dir == NULL) { + options->kasp_dir = getcwd(NULL, 0); + } + return DNSSEC_EOK; + } + + if (options->config != NULL && options->confdb != NULL) { + error("Ambiguous configuration source."); + return DNSSEC_EINVAL; + } + + // Choose the optimal config source. + struct stat st; + bool import = false; + if (options->confdb != NULL) { + import = false; + } else if (options->config != NULL) { + import = true; + } else if (stat(CONF_DEFAULT_DBDIR, &st) == 0) { + import = false; + options->confdb = CONF_DEFAULT_DBDIR; + } else if (stat(CONF_DEFAULT_FILE, &st) == 0) { + import = true; + options->config = CONF_DEFAULT_FILE; + } else if (options->kasp_dir == NULL) { + options->kasp_dir = getcwd(NULL, 0); + } + + // Prepare config flags. + conf_flag_t flags = CONF_FNOHOSTNAME; + if (options->confdb != NULL) { + flags |= CONF_FREADONLY; + } + + // Open confdb. + conf_t *new_conf = NULL; + if (conf_new(&new_conf, conf_scheme, options->confdb, flags) != KNOT_EOK) { + error("Failed to open configuration database '%s'.", + (options->confdb != NULL) ? options->confdb : ""); + return DNSSEC_EINVAL; + } + + // Import the config file. + if (import) { + if (conf_import(new_conf, options->config, true) != KNOT_EOK) { + error("Failed to open configuration file '%s'.", + options->config); + conf_free(new_conf); + return DNSSEC_EINVAL; + } + } + + // Update to the new config. + conf_update(new_conf); + + return DNSSEC_EOK; +} + +void options_cleanup(options_t *options) +{ + if (options == NULL) { + return; + } + + if (!options->legacy) { + conf_update(NULL); + } + + free(options->kasp_dir); +} + +int options_zone_kasp_path(options_t *options, const char *zone_name) +{ + if (options == NULL) { + return DNSSEC_EINVAL; + } + + if (options->kasp_dir != NULL) { + return DNSSEC_EOK; + } + + if (options->legacy || zone_name == NULL) { + return DNSSEC_EINVAL; + } + + uint8_t buff[KNOT_DNAME_MAXLEN]; + + knot_dname_t *zone = knot_dname_from_str(buff, zone_name, sizeof(buff)); + if (zone == NULL || knot_dname_to_lower(zone) != KNOT_EOK) { + error("Invalid zone name."); + return DNSSEC_EINVAL; + } + + // Check if such a zone is configured. + if (!conf_rawid_exists(conf(), C_ZONE, zone, knot_dname_size(zone))) { + error("Zone not configured."); + return DNSSEC_EINVAL; + } + + conf_val_t val = conf_zone_get(conf(), C_STORAGE, zone); + char *storage = conf_abs_path(&val, NULL); + val = conf_zone_get(conf(), C_KASP_DB, zone); + options->kasp_dir = conf_abs_path(&val, storage); + free(storage); + if (options->kasp_dir == NULL) { + return DNSSEC_EINVAL; + } + + return DNSSEC_EOK; +} + +int options_zone_kasp_init(options_t *options, const char *zone_name, + dnssec_kasp_t **kasp) +{ + if (options == NULL) { + return DNSSEC_EINVAL; + } + + if (options->legacy) { + dnssec_kasp_init_dir(kasp); + return DNSSEC_EOK; + } + + if (zone_name == NULL || kasp == NULL) { + return DNSSEC_EINVAL; + } + + kdnssec_ctx_t ctx = { + .legacy = options->legacy + }; + + int ret = kdnssec_kasp_init(&ctx, options->kasp_dir, zone_name); + kdnssec_ctx_deinit(&ctx); + if (ret != DNSSEC_EOK) { + error("Failed to initialize KASP directory '%s'.", options->kasp_dir); + return ret; + } + + return kdnssec_kasp(kasp, options->legacy); +} diff --git a/src/utils/keymgr/options.h b/src/utils/keymgr/options.h new file mode 100644 index 0000000000000000000000000000000000000000..41894b986a00eb9a6b019895e68c1719159ea454 --- /dev/null +++ b/src/utils/keymgr/options.h @@ -0,0 +1,37 @@ +/* Copyright (C) 2016 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/>. +*/ + +#pragma once + +#include "knot/dnssec/context.h" + +struct options { + bool legacy; + char *kasp_dir; + char *config; + char *confdb; +}; + +typedef struct options options_t; + +int options_init(options_t *options); + +void options_cleanup(options_t *options); + +int options_zone_kasp_path(options_t *options, const char *zone_name); + +int options_zone_kasp_init(options_t *options, const char *zone_name, + dnssec_kasp_t **kasp);