1 /* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2
3 This program is free software: you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation, either version 3 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16
17 #include <getopt.h>
18 #include <stdlib.h>
19
20 #include "knot/conf/conf.h"
21 #include "knot/dnssec/zone-events.h"
22 #include "knot/updates/zone-update.h"
23 #include "knot/zone/zone-load.h"
24 #include "knot/zone/zonefile.h"
25 #include "utils/common/params.h"
26
27 #define PROGRAM_NAME "kzonesign"
28
29 static const char *global_outdir = NULL;
30
31 // copy-pasted from keymgr
init_conf(const char * confdb)32 static bool init_conf(const char *confdb)
33 {
34 size_t max_conf_size = (size_t)CONF_MAPSIZE * 1024 * 1024;
35
36 conf_flag_t flags = CONF_FNOHOSTNAME | CONF_FOPTMODULES;
37 if (confdb != NULL) {
38 flags |= CONF_FREADONLY;
39 }
40
41 conf_t *new_conf = NULL;
42 int ret = conf_new(&new_conf, conf_schema, confdb, max_conf_size, flags);
43 if (ret != KNOT_EOK) {
44 printf("Failed opening configuration database %s (%s)\n",
45 (confdb == NULL ? "" : confdb), knot_strerror(ret));
46 return false;
47 }
48 conf_update(new_conf, CONF_UPD_FNONE);
49 return true;
50 }
51
print_help(void)52 static void print_help(void)
53 {
54 printf("Usage: %s [parameters] -c <conf_file> <zone_name>\n"
55 "\n"
56 "Parameters:\n"
57 " -o, --outdir <dir_name> Output directory.\n"
58 " -r, --rollover Allow key rollovers and NSEC3 re-salt.\n"
59 " -t, --time <timestamp> Current time specification.\n"
60 " (default current UNIX time)\n"
61 " -h, --help Print the program help.\n"
62 " -V, --version Print the program version.\n"
63 "\n",
64 PROGRAM_NAME);
65 }
66
main(int argc,char * argv[])67 int main(int argc, char *argv[])
68 {
69 const char *confile = NULL, *zone_str = NULL;
70 knot_dname_t *zone_name = NULL;
71 zone_contents_t *unsigned_conts = NULL;
72 zone_t *zone_struct = NULL;
73 zone_update_t up = { 0 };
74 knot_lmdb_db_t kasp_db = { 0 };
75 zone_sign_roll_flags_t rollover = 0;
76 int64_t timestamp = 0;
77 zone_sign_reschedule_t next_sign = { 0 };
78
79 struct option opts[] = {
80 { "config", required_argument, NULL, 'c' },
81 { "outdir", required_argument, NULL, 'o' },
82 { "rollover", no_argument, NULL, 'r' },
83 { "time", required_argument, NULL, 't' },
84 { "help", no_argument, NULL, 'h' },
85 { "version", no_argument, NULL, 'V' },
86 { NULL }
87 };
88
89 tzset();
90
91 int opt;
92 while ((opt = getopt_long(argc, argv, "c:o:rt:hV", opts, NULL)) != -1) {
93 switch (opt) {
94 case 'c':
95 confile = optarg;
96 break;
97 case 'o':
98 global_outdir = optarg;
99 break;
100 case 'r':
101 rollover = KEY_ROLL_ALLOW_ALL;
102 break;
103 case 't':
104 timestamp = atol(optarg);
105 if (timestamp <= 0) {
106 print_help();
107 return EXIT_FAILURE;
108 }
109 break;
110 case 'h':
111 print_help();
112 return EXIT_SUCCESS;
113 case 'V':
114 print_version(PROGRAM_NAME);
115 return EXIT_SUCCESS;
116 default:
117 print_help();
118 return EXIT_FAILURE;
119 }
120 }
121 if (confile == NULL || argc - optind != 1) {
122 print_help();
123 return EXIT_FAILURE;
124 }
125
126 zone_str = argv[optind];
127 zone_name = knot_dname_from_str_alloc(zone_str);
128 if (zone_name == NULL) {
129 printf("Invalid zone name '%s'\n", zone_str);
130 return EXIT_FAILURE;
131 }
132 knot_dname_to_lower(zone_name);
133
134 if (!init_conf(NULL)) {
135 free(zone_name);
136 return EXIT_FAILURE;
137 }
138
139 int ret = conf_import(conf(), confile, true, false);
140 if (ret != KNOT_EOK) {
141 printf("Failed opening configuration file '%s' (%s)\n",
142 confile, knot_strerror(ret));
143 goto fail;
144 }
145
146 conf_val_t val = conf_zone_get(conf(), C_DOMAIN, zone_name);
147 if (val.code != KNOT_EOK) {
148 printf("Zone '%s' not configured\n", zone_str);
149 ret = val.code;
150 goto fail;
151 }
152 val = conf_zone_get(conf(), C_DNSSEC_POLICY, zone_name);
153 if (val.code != KNOT_EOK) {
154 printf("Waring: DNSSEC policy not configured for zone '%s', taking defaults\n", zone_str);
155 }
156
157 zone_struct = zone_new(zone_name);
158 if (zone_struct == NULL) {
159 printf("out of memory\n");
160 ret = KNOT_ENOMEM;
161 goto fail;
162 }
163
164 ret = zone_load_contents(conf(), zone_name, &unsigned_conts, false);
165 if (ret != KNOT_EOK) {
166 printf("Failed to load zone contents (%s)\n", knot_strerror(ret));
167 goto fail;
168 }
169
170 ret = zone_update_from_contents(&up, zone_struct, unsigned_conts, UPDATE_FULL);
171 if (ret != KNOT_EOK) {
172 printf("Failed to initialize zone update (%s)\n", knot_strerror(ret));
173 zone_contents_deep_free(unsigned_conts);
174 goto fail;
175 }
176
177 kasp_db_ensure_init(&kasp_db, conf());
178 zone_struct->kaspdb = &kasp_db;
179
180 ret = knot_dnssec_zone_sign(&up, conf(), 0, rollover, timestamp, &next_sign);
181 if (ret == KNOT_DNSSEC_ENOKEY) { // exception: allow generating initial keys
182 rollover = KEY_ROLL_ALLOW_ALL;
183 ret = knot_dnssec_zone_sign(&up, conf(), 0, rollover, timestamp, &next_sign);
184 }
185 if (ret != KNOT_EOK) {
186 printf("Failed to sign the zone (%s)\n", knot_strerror(ret));
187 zone_update_clear(&up);
188 goto fail;
189 }
190
191 if (global_outdir == NULL) {
192 char *zonefile = conf_zonefile(conf(), zone_name);
193 ret = zonefile_write(zonefile, up.new_cont);
194 free(zonefile);
195 } else {
196 zone_contents_t *temp = zone_struct->contents;
197 zone_struct->contents = up.new_cont;
198 ret = zone_dump_to_dir(conf(), zone_struct, global_outdir);
199 zone_struct->contents = temp;
200 }
201 zone_update_clear(&up);
202 if (ret != KNOT_EOK) {
203 printf("Failed to flush signed zone file (%s)\n", knot_strerror(ret));
204 goto fail;
205 }
206
207 printf("Next signing: %"KNOT_TIME_PRINTF"\n", next_sign.next_sign);
208 if (rollover) {
209 printf("Next roll-over: %"KNOT_TIME_PRINTF"\n", next_sign.next_rollover);
210 if (next_sign.next_nsec3resalt) {
211 printf("Next NSEC3 re-salt: %"KNOT_TIME_PRINTF"\n", next_sign.next_nsec3resalt);
212 }
213 if (next_sign.plan_ds_check) {
214 printf("KSK submission to parent zone needed\n");
215 }
216 }
217
218 fail:
219 if (kasp_db.path != NULL) {
220 knot_lmdb_deinit(&kasp_db);
221 }
222 zone_free(&zone_struct);
223 conf_free(conf());
224 free(zone_name);
225 return ret == KNOT_EOK ? EXIT_SUCCESS : EXIT_FAILURE;
226 }
227