From 401521222e55a2558a11920c7e8a61e4cff52583 Mon Sep 17 00:00:00 2001 From: Libor Peltan <libor.peltan@nic.cz> Date: Tue, 5 May 2020 11:36:49 +0200 Subject: [PATCH] ksignzone implemented --- .gitignore | 1 + Knot.files | 1 + distro/deb/knot.install | 2 + doc/.gitignore | 1 + doc/Makefile.am | 4 + doc/conf.py | 1 + doc/man/ksignzone.8in | 77 +++++++++++++++ doc/man_ksignzone.rst | 51 ++++++++++ doc/utilities.rst | 1 + src/utils/Makefile.inc | 8 +- src/utils/ksignzone/main.c | 198 +++++++++++++++++++++++++++++++++++++ 11 files changed, 344 insertions(+), 1 deletion(-) create mode 100644 doc/man/ksignzone.8in create mode 100644 doc/man_ksignzone.rst create mode 100644 src/utils/ksignzone/main.c diff --git a/.gitignore b/.gitignore index 01aa679731..122a9b2373 100644 --- a/.gitignore +++ b/.gitignore @@ -74,6 +74,7 @@ src/libknot/libknot.h /src/knotd /src/knsec3hash /src/knsupdate +/src/ksignzone /src/kzonecheck /src/kxdpgun diff --git a/Knot.files b/Knot.files index 2d36c61b0a..49406af381 100644 --- a/Knot.files +++ b/Knot.files @@ -491,6 +491,7 @@ src/utils/knsupdate/knsupdate_exec.h src/utils/knsupdate/knsupdate_main.c src/utils/knsupdate/knsupdate_params.c src/utils/knsupdate/knsupdate_params.h +src/utils/ksignzone/main.c src/utils/kzonecheck/main.c src/utils/kzonecheck/zone_check.c src/utils/kzonecheck/zone_check.h diff --git a/distro/deb/knot.install b/distro/deb/knot.install index b49ae1c58e..151f63aaf8 100644 --- a/distro/deb/knot.install +++ b/distro/deb/knot.install @@ -7,6 +7,7 @@ usr/sbin/keymgr usr/sbin/kjournalprint usr/sbin/knotc usr/sbin/knotd +usr/sbin/ksignzone usr/share/man/man1/knsec3hash.1 usr/share/man/man1/kzonecheck.1 usr/share/man/man5/knot.conf.5 @@ -15,3 +16,4 @@ usr/share/man/man8/keymgr.8 usr/share/man/man8/kjournalprint.8 usr/share/man/man8/knotc.8 usr/share/man/man8/knotd.8 +usr/share/man/man8/ksignzone.8 diff --git a/doc/.gitignore b/doc/.gitignore index 849671a79d..cbd3bad569 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -15,5 +15,6 @@ /man/knot1to2.1 /man/knsec3hash.1 /man/knsupdate.1 +/man/ksignzone.8 /man/kzonecheck.1 /man/kxdpgun.8 diff --git a/doc/Makefile.am b/doc/Makefile.am index f66fa553b7..bdd7eee3bb 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -9,6 +9,7 @@ MANPAGES_IN = \ man/khost.1in \ man/knsupdate.1in \ man/knsec3hash.1in \ + man/ksignzone.8in \ man/kzonecheck.1in \ man/kxdpgun.8in @@ -22,6 +23,7 @@ MANPAGES_RST = \ man_khost.rst \ man_knsupdate.rst \ man_knsec3hash.rst \ + man_ksignzone.rst \ man_kzonecheck.rst \ man_kxdpgun.rst @@ -97,6 +99,7 @@ man_MANS += \ man/kcatalogprint.8 \ man/keymgr.8 \ man/kjournalprint.8 \ + man/ksignzone.8 \ man/kzonecheck.1 endif # HAVE_DAEMON @@ -121,6 +124,7 @@ man/kdig.1: man/kdig.1in man/khost.1: man/khost.1in man/knsupdate.1: man/knsupdate.1in man/knsec3hash.1: man/knsec3hash.1in +man/ksignzone.8: man/ksignzone.8in man/kzonecheck.1: man/kzonecheck.1in man/kxdpgun.8: man/kxdpgun.8in diff --git a/doc/conf.py b/doc/conf.py index e0c2d9d216..b83be0a222 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -233,6 +233,7 @@ man_pages = [ ('man_khost', 'khost', 'Simple DNS lookup utility', author, 1), ('man_knsec3hash', 'knsec3hash', 'Simple utility to compute NSEC3 hash', author, 1), ('man_knsupdate', 'knsupdate', 'Dynamic DNS update utility', author, 1), + ('man_ksignzone', 'ksignzone', 'DNSSEC signing utility', author, 8), ('man_kzonecheck', 'kzonecheck', 'Knot DNS zone check tool', author, 1), ('man_kxdpgun', 'kxdpgun', 'XDP-powered DNS benchmarking tool', author, 8), ] diff --git a/doc/man/ksignzone.8in b/doc/man/ksignzone.8in new file mode 100644 index 0000000000..d7a80c628f --- /dev/null +++ b/doc/man/ksignzone.8in @@ -0,0 +1,77 @@ +.\" Man page generated from reStructuredText. +. +.TH "KSIGNZONE" "8" "@RELEASE_DATE@" "@VERSION@" "Knot DNS" +.SH NAME +ksignzone \- DNSSEC signing utility +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBksignzone\fP \fIoptions\fP \fIzone_name\fP +.SH DESCRIPTION +.sp +This utility reads the zone\(aqs zone file, signs the zone according to given +configuration file, and writes the signed zone file back. +.SS Options +.INDENT 0.0 +.TP +\fB\-c\fP \fIconf\-file\fP +Knot DNS configuration file (same as for knotd). +\fIThis option is obligatory.\fP +.TP +\fB\-o\fP \fIoutdir\fP +Write the output zone file to different directory than configured. +.TP +\fB\-R\fP +Allow key roll\-overs nad NSEC3 re\-salt. +.TP +\fB\-h\fP, \fB\-\-help\fP +Print the program help. +.TP +\fB\-V\fP, \fB\-\-version\fP +Print the program version. +.UNINDENT +.SS Parameters +.INDENT 0.0 +.TP +\fIzone_name\fP +A name of the zone to be signed. +.UNINDENT +.SH EXIT VALUES +.sp +Exit status of 0 means successful operation. Any other exit status indicates +an error. +.SH SEE ALSO +.sp +\fBknotd(8)\fP, \fBknot.conf(5)\fP\&. +.SH AUTHOR +CZ.NIC Labs <https://www.knot-dns.cz> +.SH COPYRIGHT +Copyright 2010–2020, CZ.NIC, z.s.p.o. +.\" Generated by docutils manpage writer. +. diff --git a/doc/man_ksignzone.rst b/doc/man_ksignzone.rst new file mode 100644 index 0000000000..a4635cea16 --- /dev/null +++ b/doc/man_ksignzone.rst @@ -0,0 +1,51 @@ +.. highlight:: console + +ksignzone – DNSSEC signing utility +================================== + +Synopsis +-------- + +:program:`ksignzone` *options* *zone_name* + +Description +----------- + +This utility reads the zone's zone file, signs the zone according to given +configuration file, and writes the signed zone file back. + +Options +....... + +**-c** *conf-file* + Knot DNS configuration file (same as for knotd). + *This option is obligatory.* + +**-o** *outdir* + Write the output zone file to different directory than configured. + +**-R** + Allow key roll-overs nad NSEC3 re-salt. + +**-h**, **--help** + Print the program help. + +**-V**, **--version** + Print the program version. + +Parameters +.......... + +*zone_name* + A name of the zone to be signed. + +Exit values +----------- + +Exit status of 0 means successful operation. Any other exit status indicates +an error. + +See Also +-------- + +:manpage:`knotd(8)`, :manpage:`knot.conf(5)`. diff --git a/doc/utilities.rst b/doc/utilities.rst index b797c19244..4a104059b8 100644 --- a/doc/utilities.rst +++ b/doc/utilities.rst @@ -20,4 +20,5 @@ the server. This section collects manual pages for all provided binaries: man_khost man_knsec3hash man_knsupdate + man_ksignzone man_kxdpgun diff --git a/src/utils/Makefile.inc b/src/utils/Makefile.inc index eff5563eca..d632a2f546 100644 --- a/src/utils/Makefile.inc +++ b/src/utils/Makefile.inc @@ -130,13 +130,16 @@ knotd_LDFLAGS = $(AM_LDFLAGS) -rdynamic if HAVE_UTILS bin_PROGRAMS += kzonecheck -sbin_PROGRAMS += keymgr kjournalprint kcatalogprint +sbin_PROGRAMS += keymgr kjournalprint kcatalogprint ksignzone kzonecheck_SOURCES = \ utils/kzonecheck/main.c \ utils/kzonecheck/zone_check.c \ utils/kzonecheck/zone_check.h +ksignzone_SOURCES = \ + utils/ksignzone/main.c + keymgr_SOURCES = \ utils/keymgr/bind_privkey.c \ utils/keymgr/bind_privkey.h \ @@ -154,6 +157,9 @@ kcatalogprint_SOURCES = \ kzonecheck_CPPFLAGS = $(AM_CPPFLAGS) $(lmdb_CFLAGS) kzonecheck_LDADD = libcontrib.la libknotd.la +ksignzone_CPPFLAGS = $(AM_CPPFLAGS) $(gnutls_CFLAGS) $(lmdb_CFLAGS) +ksignzone_LDADD = libcontrib.la libknotd.la libknotus.la libdnssec.la \ + libzscanner.la keymgr_CPPFLAGS = $(AM_CPPFLAGS) $(gnutls_CFLAGS) $(lmdb_CFLAGS) keymgr_LDADD = libcontrib.la libknotd.la libknotus.la libdnssec.la \ libzscanner.la diff --git a/src/utils/ksignzone/main.c b/src/utils/ksignzone/main.c new file mode 100644 index 0000000000..b80039995f --- /dev/null +++ b/src/utils/ksignzone/main.c @@ -0,0 +1,198 @@ +/* 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/>. + */ + +#include <getopt.h> +#include <stdlib.h> + +#include "knot/conf/conf.h" +#include "knot/updates/zone-update.h" +#include "knot/zone/zone-load.h" +#include "knot/zone/zonefile.h" +#include "utils/common/params.h" + +#define PROGRAM_NAME "ksignzone" + +static const char *global_outdir = NULL; + +// copy-pasted from keymgr +static bool init_conf(const char *confdb) +{ + size_t max_conf_size = (size_t)CONF_MAPSIZE * 1024 * 1024; + + conf_flag_t flags = CONF_FNOHOSTNAME | CONF_FOPTMODULES; + if (confdb != NULL) { + flags |= CONF_FREADONLY; + } + + conf_t *new_conf = NULL; + int ret = conf_new(&new_conf, conf_schema, confdb, max_conf_size, flags); + if (ret != KNOT_EOK) { + printf("Failed opening configuration database %s (%s)\n", + (confdb == NULL ? "" : confdb), knot_strerror(ret)); + return false; + } + conf_update(new_conf, CONF_UPD_FNONE); + return true; +} + +static void print_help(void) +{ + printf("Usage: %s -c <knot_conf> [-R] [-o <outdir>] zone_name\n", PROGRAM_NAME); +} + +int main(int argc, char *argv[]) +{ + const char *confile = NULL, *zone_str = NULL; + knot_dname_t *zone_name = NULL; + zone_contents_t *unsigned_conts = NULL; + zone_t *zone_struct = NULL; + zone_update_t up = { 0 }; + knot_lmdb_db_t kasp_db = { 0 }; + zone_sign_roll_flags_t rollover = 0; + zone_sign_reschedule_t next_sign = { 0 }; + + int opt; + struct option opts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + }; + while ((opt = getopt_long(argc, argv, "Vhc:Ro:", opts, NULL)) != -1) { + switch (opt) { + case 'V': + print_version(PROGRAM_NAME); + return EXIT_SUCCESS; + case 'h': + print_help(); + return EXIT_SUCCESS; + case 'c': + confile = optarg; + break; + case 'R': + rollover = KEY_ROLL_ALLOW_ALL; + break; + case 'o': + global_outdir = optarg; + break; + default: + print_help(); + return EXIT_FAILURE; + } + } + if (confile == NULL || argc - optind != 1) { + print_help(); + return EXIT_FAILURE; + } + + zone_str = argv[optind]; + zone_name = knot_dname_from_str_alloc(zone_str); + if (zone_name == NULL) { + printf("inproper zone name\n"); + return EXIT_FAILURE; + } + + if (!init_conf(NULL)) { + free(zone_name); + return EXIT_FAILURE; + } + + int ret = conf_import(conf(), confile, true, false); + if (ret != KNOT_EOK) { + printf("Failed opening configuration file %s (%s)\n", + confile, knot_strerror(ret)); + goto fail; + } + + conf_val_t val = conf_zone_get(conf(), C_DOMAIN, zone_name); + if (val.code != KNOT_EOK) { + printf("Zone not found in configuation (%s)\n", knot_strerror(val.code)); + ret = val.code; + goto fail; + } + val = conf_zone_get(conf(), C_DNSSEC_POLICY, zone_name); + if (val.code != KNOT_EOK) { + printf("Waring: DNSSEC policy not configured for zone %s, taking defaults.\n", zone_str); + } + + zone_struct = zone_new(zone_name); + if (zone_struct == NULL) { + printf("out of memory\n"); + ret = KNOT_ENOMEM; + goto fail; + } + + ret = zone_load_contents(conf(), zone_name, &unsigned_conts, false); + if (ret != KNOT_EOK) { + printf("Failed to load zone contents (%s)\n", knot_strerror(ret)); + goto fail; + } + + ret = zone_update_from_contents(&up, zone_struct, unsigned_conts, UPDATE_FULL); + if (ret != KNOT_EOK) { + printf("Failed to initialize zone update (%s)\n", knot_strerror(ret)); + zone_contents_deep_free(unsigned_conts); + goto fail; + } + + kasp_db_ensure_init(&kasp_db, conf()); + zone_struct->kaspdb = &kasp_db; + + ret = knot_dnssec_zone_sign(&up, 0, rollover, &next_sign); + if (ret == KNOT_DNSSEC_ENOKEY) { // exception: allow generating initial keys + rollover = KEY_ROLL_ALLOW_ALL; + ret = knot_dnssec_zone_sign(&up, 0, rollover, &next_sign); + } + if (ret != KNOT_EOK) { + printf("Failed to sign the zone (%s)\n", knot_strerror(ret)); + zone_update_clear(&up); + goto fail; + } + + if (global_outdir == NULL) { + char *zonefile = conf_zonefile(conf(), zone_name); + ret = zonefile_write(zonefile, up.new_cont); + free(zonefile); + } else { + zone_contents_t *temp = zone_struct->contents; + zone_struct->contents = up.new_cont; + ret = zone_dump_to_dir(conf(), zone_struct, global_outdir); + zone_struct->contents = temp; + } + zone_update_clear(&up); + if (ret != KNOT_EOK) { + printf("Failed to flush signed zone file (%s)\n", knot_strerror(ret)); + goto fail; + } + + printf("Next sign: %lu\n", next_sign.next_sign); + if (rollover) { + printf("Next roll-over: %lu\n", next_sign.next_rollover); + if (next_sign.next_nsec3resalt) { + printf("Next NSEC3 re-sign: %lu\n", next_sign.next_nsec3resalt); + } + if (next_sign.plan_ds_check) { + printf("Submit keys to parent zone.\n"); + } + } + +fail: + if (kasp_db.path != NULL) { + knot_lmdb_deinit(&kasp_db); + } + zone_free(&zone_struct); + conf_free(conf()); + free(zone_name); + return ret == KNOT_EOK ? EXIT_SUCCESS : EXIT_FAILURE; +} -- GitLab