1 /* $NetBSD: dnssec-importkey.c,v 1.7 2022/09/23 12:15:21 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 /*! \file */ 17 18 #include <stdbool.h> 19 #include <stdlib.h> 20 21 #include <isc/buffer.h> 22 #include <isc/commandline.h> 23 #include <isc/hash.h> 24 #include <isc/mem.h> 25 #include <isc/print.h> 26 #include <isc/string.h> 27 #include <isc/util.h> 28 29 #include <dns/callbacks.h> 30 #include <dns/db.h> 31 #include <dns/dbiterator.h> 32 #include <dns/ds.h> 33 #include <dns/fixedname.h> 34 #include <dns/keyvalues.h> 35 #include <dns/log.h> 36 #include <dns/master.h> 37 #include <dns/name.h> 38 #include <dns/rdata.h> 39 #include <dns/rdataclass.h> 40 #include <dns/rdataset.h> 41 #include <dns/rdatasetiter.h> 42 #include <dns/rdatatype.h> 43 #include <dns/result.h> 44 45 #include <dst/dst.h> 46 47 #if USE_PKCS11 48 #include <pk11/result.h> 49 #endif /* if USE_PKCS11 */ 50 51 #include "dnssectool.h" 52 53 const char *program = "dnssec-importkey"; 54 55 static dns_rdataclass_t rdclass; 56 static dns_fixedname_t fixed; 57 static dns_name_t *name = NULL; 58 static isc_mem_t *mctx = NULL; 59 static bool setpub = false, setdel = false; 60 static bool setttl = false; 61 static isc_stdtime_t pub = 0, del = 0; 62 static dns_ttl_t ttl = 0; 63 static isc_stdtime_t syncadd = 0, syncdel = 0; 64 static bool setsyncadd = false; 65 static bool setsyncdel = false; 66 67 static isc_result_t 68 initname(char *setname) { 69 isc_result_t result; 70 isc_buffer_t buf; 71 72 name = dns_fixedname_initname(&fixed); 73 74 isc_buffer_init(&buf, setname, strlen(setname)); 75 isc_buffer_add(&buf, strlen(setname)); 76 result = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL); 77 return (result); 78 } 79 80 static void 81 db_load_from_stream(dns_db_t *db, FILE *fp) { 82 isc_result_t result; 83 dns_rdatacallbacks_t callbacks; 84 85 dns_rdatacallbacks_init(&callbacks); 86 result = dns_db_beginload(db, &callbacks); 87 if (result != ISC_R_SUCCESS) { 88 fatal("dns_db_beginload failed: %s", isc_result_totext(result)); 89 } 90 91 result = dns_master_loadstream(fp, name, name, rdclass, 0, &callbacks, 92 mctx); 93 if (result != ISC_R_SUCCESS) { 94 fatal("can't load from input: %s", isc_result_totext(result)); 95 } 96 97 result = dns_db_endload(db, &callbacks); 98 if (result != ISC_R_SUCCESS) { 99 fatal("dns_db_endload failed: %s", isc_result_totext(result)); 100 } 101 } 102 103 static isc_result_t 104 loadset(const char *filename, dns_rdataset_t *rdataset) { 105 isc_result_t result; 106 dns_db_t *db = NULL; 107 dns_dbnode_t *node = NULL; 108 char setname[DNS_NAME_FORMATSIZE]; 109 110 dns_name_format(name, setname, sizeof(setname)); 111 112 result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0, 113 NULL, &db); 114 if (result != ISC_R_SUCCESS) { 115 fatal("can't create database"); 116 } 117 118 if (strcmp(filename, "-") == 0) { 119 db_load_from_stream(db, stdin); 120 filename = "input"; 121 } else { 122 result = dns_db_load(db, filename, dns_masterformat_text, 123 DNS_MASTER_NOTTL); 124 if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) { 125 fatal("can't load %s: %s", filename, 126 isc_result_totext(result)); 127 } 128 } 129 130 result = dns_db_findnode(db, name, false, &node); 131 if (result != ISC_R_SUCCESS) { 132 fatal("can't find %s node in %s", setname, filename); 133 } 134 135 result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey, 0, 0, 136 rdataset, NULL); 137 138 if (result == ISC_R_NOTFOUND) { 139 fatal("no DNSKEY RR for %s in %s", setname, filename); 140 } else if (result != ISC_R_SUCCESS) { 141 fatal("dns_db_findrdataset"); 142 } 143 144 if (node != NULL) { 145 dns_db_detachnode(db, &node); 146 } 147 if (db != NULL) { 148 dns_db_detach(&db); 149 } 150 return (result); 151 } 152 153 static void 154 loadkey(char *filename, unsigned char *key_buf, unsigned int key_buf_size, 155 dns_rdata_t *rdata) { 156 isc_result_t result; 157 dst_key_t *key = NULL; 158 isc_buffer_t keyb; 159 isc_region_t r; 160 161 dns_rdata_init(rdata); 162 163 isc_buffer_init(&keyb, key_buf, key_buf_size); 164 165 result = dst_key_fromnamedfile(filename, NULL, DST_TYPE_PUBLIC, mctx, 166 &key); 167 if (result != ISC_R_SUCCESS) { 168 fatal("invalid keyfile name %s: %s", filename, 169 isc_result_totext(result)); 170 } 171 172 if (verbose > 2) { 173 char keystr[DST_KEY_FORMATSIZE]; 174 175 dst_key_format(key, keystr, sizeof(keystr)); 176 fprintf(stderr, "%s: %s\n", program, keystr); 177 } 178 179 result = dst_key_todns(key, &keyb); 180 if (result != ISC_R_SUCCESS) { 181 fatal("can't decode key"); 182 } 183 184 isc_buffer_usedregion(&keyb, &r); 185 dns_rdata_fromregion(rdata, dst_key_class(key), dns_rdatatype_dnskey, 186 &r); 187 188 rdclass = dst_key_class(key); 189 190 name = dns_fixedname_initname(&fixed); 191 dns_name_copynf(dst_key_name(key), name); 192 193 dst_key_free(&key); 194 } 195 196 static void 197 emit(const char *dir, dns_rdata_t *rdata) { 198 isc_result_t result; 199 char keystr[DST_KEY_FORMATSIZE]; 200 char pubname[1024]; 201 char priname[1024]; 202 isc_buffer_t buf; 203 dst_key_t *key = NULL, *tmp = NULL; 204 205 isc_buffer_init(&buf, rdata->data, rdata->length); 206 isc_buffer_add(&buf, rdata->length); 207 result = dst_key_fromdns(name, rdclass, &buf, mctx, &key); 208 if (result != ISC_R_SUCCESS) { 209 fatal("dst_key_fromdns: %s", isc_result_totext(result)); 210 } 211 212 isc_buffer_init(&buf, pubname, sizeof(pubname)); 213 result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf); 214 if (result != ISC_R_SUCCESS) { 215 fatal("Failed to build public key filename: %s", 216 isc_result_totext(result)); 217 } 218 isc_buffer_init(&buf, priname, sizeof(priname)); 219 result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf); 220 if (result != ISC_R_SUCCESS) { 221 fatal("Failed to build private key filename: %s", 222 isc_result_totext(result)); 223 } 224 225 result = dst_key_fromfile( 226 dst_key_name(key), dst_key_id(key), dst_key_alg(key), 227 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, dir, mctx, &tmp); 228 if (result == ISC_R_SUCCESS) { 229 if (dst_key_isprivate(tmp) && !dst_key_isexternal(tmp)) { 230 fatal("Private key already exists in %s", priname); 231 } 232 dst_key_free(&tmp); 233 } 234 235 dst_key_setexternal(key, true); 236 if (setpub) { 237 dst_key_settime(key, DST_TIME_PUBLISH, pub); 238 } 239 if (setdel) { 240 dst_key_settime(key, DST_TIME_DELETE, del); 241 } 242 if (setsyncadd) { 243 dst_key_settime(key, DST_TIME_SYNCPUBLISH, syncadd); 244 } 245 if (setsyncdel) { 246 dst_key_settime(key, DST_TIME_SYNCDELETE, syncdel); 247 } 248 249 if (setttl) { 250 dst_key_setttl(key, ttl); 251 } 252 253 result = dst_key_tofile(key, DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, dir); 254 if (result != ISC_R_SUCCESS) { 255 dst_key_format(key, keystr, sizeof(keystr)); 256 fatal("Failed to write key %s: %s", keystr, 257 isc_result_totext(result)); 258 } 259 printf("%s\n", pubname); 260 261 isc_buffer_clear(&buf); 262 result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf); 263 if (result != ISC_R_SUCCESS) { 264 fatal("Failed to build private key filename: %s", 265 isc_result_totext(result)); 266 } 267 printf("%s\n", priname); 268 dst_key_free(&key); 269 } 270 271 ISC_PLATFORM_NORETURN_PRE static void 272 usage(void) ISC_PLATFORM_NORETURN_POST; 273 274 static void 275 usage(void) { 276 fprintf(stderr, "Usage:\n"); 277 fprintf(stderr, " %s options [-K dir] keyfile\n\n", program); 278 fprintf(stderr, " %s options -f file [keyname]\n\n", program); 279 fprintf(stderr, "Version: %s\n", VERSION); 280 fprintf(stderr, "Options:\n"); 281 fprintf(stderr, " -f file: read key from zone file\n"); 282 fprintf(stderr, " -K <directory>: directory in which to store " 283 "the key files\n"); 284 fprintf(stderr, " -L ttl: set default key TTL\n"); 285 fprintf(stderr, " -v <verbose level>\n"); 286 fprintf(stderr, " -V: print version information\n"); 287 fprintf(stderr, " -h: print usage and exit\n"); 288 fprintf(stderr, "Timing options:\n"); 289 fprintf(stderr, " -P date/[+-]offset/none: set/unset key " 290 "publication date\n"); 291 fprintf(stderr, " -P sync date/[+-]offset/none: set/unset " 292 "CDS and CDNSKEY publication date\n"); 293 fprintf(stderr, " -D date/[+-]offset/none: set/unset key " 294 "deletion date\n"); 295 fprintf(stderr, " -D sync date/[+-]offset/none: set/unset " 296 "CDS and CDNSKEY deletion date\n"); 297 298 exit(-1); 299 } 300 301 int 302 main(int argc, char **argv) { 303 char *classname = NULL; 304 char *filename = NULL, *dir = NULL, *namestr; 305 char *endp; 306 int ch; 307 isc_result_t result; 308 isc_log_t *log = NULL; 309 dns_rdataset_t rdataset; 310 dns_rdata_t rdata; 311 isc_stdtime_t now; 312 313 dns_rdata_init(&rdata); 314 isc_stdtime_get(&now); 315 316 if (argc == 1) { 317 usage(); 318 } 319 320 isc_mem_create(&mctx); 321 322 #if USE_PKCS11 323 pk11_result_register(); 324 #endif /* if USE_PKCS11 */ 325 dns_result_register(); 326 327 isc_commandline_errprint = false; 328 329 #define CMDLINE_FLAGS "D:f:hK:L:P:v:V" 330 while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { 331 switch (ch) { 332 case 'D': 333 /* -Dsync ? */ 334 if (isoptarg("sync", argv, usage)) { 335 if (setsyncdel) { 336 fatal("-D sync specified more than " 337 "once"); 338 } 339 340 syncdel = strtotime(isc_commandline_argument, 341 now, now, &setsyncdel); 342 break; 343 } 344 /* -Ddnskey ? */ 345 (void)isoptarg("dnskey", argv, usage); 346 if (setdel) { 347 fatal("-D specified more than once"); 348 } 349 350 del = strtotime(isc_commandline_argument, now, now, 351 &setdel); 352 break; 353 case 'K': 354 dir = isc_commandline_argument; 355 if (strlen(dir) == 0U) { 356 fatal("directory must be non-empty string"); 357 } 358 break; 359 case 'L': 360 ttl = strtottl(isc_commandline_argument); 361 setttl = true; 362 break; 363 case 'P': 364 /* -Psync ? */ 365 if (isoptarg("sync", argv, usage)) { 366 if (setsyncadd) { 367 fatal("-P sync specified more than " 368 "once"); 369 } 370 371 syncadd = strtotime(isc_commandline_argument, 372 now, now, &setsyncadd); 373 break; 374 } 375 /* -Pdnskey ? */ 376 (void)isoptarg("dnskey", argv, usage); 377 if (setpub) { 378 fatal("-P specified more than once"); 379 } 380 381 pub = strtotime(isc_commandline_argument, now, now, 382 &setpub); 383 break; 384 case 'f': 385 filename = isc_commandline_argument; 386 break; 387 case 'v': 388 verbose = strtol(isc_commandline_argument, &endp, 0); 389 if (*endp != '\0') { 390 fatal("-v must be followed by a number"); 391 } 392 break; 393 case '?': 394 if (isc_commandline_option != '?') { 395 fprintf(stderr, "%s: invalid argument -%c\n", 396 program, isc_commandline_option); 397 } 398 FALLTHROUGH; 399 case 'h': 400 /* Does not return. */ 401 usage(); 402 403 case 'V': 404 /* Does not return. */ 405 version(program); 406 407 default: 408 fprintf(stderr, "%s: unhandled option -%c\n", program, 409 isc_commandline_option); 410 exit(1); 411 } 412 } 413 414 rdclass = strtoclass(classname); 415 416 if (argc < isc_commandline_index + 1 && filename == NULL) { 417 fatal("the key file name was not specified"); 418 } 419 if (argc > isc_commandline_index + 1) { 420 fatal("extraneous arguments"); 421 } 422 423 result = dst_lib_init(mctx, NULL); 424 if (result != ISC_R_SUCCESS) { 425 fatal("could not initialize dst: %s", 426 isc_result_totext(result)); 427 } 428 429 setup_logging(mctx, &log); 430 431 dns_rdataset_init(&rdataset); 432 433 if (filename != NULL) { 434 if (argc < isc_commandline_index + 1) { 435 /* using filename as zone name */ 436 namestr = filename; 437 } else { 438 namestr = argv[isc_commandline_index]; 439 } 440 441 result = initname(namestr); 442 if (result != ISC_R_SUCCESS) { 443 fatal("could not initialize name %s", namestr); 444 } 445 446 result = loadset(filename, &rdataset); 447 448 if (result != ISC_R_SUCCESS) { 449 fatal("could not load DNSKEY set: %s\n", 450 isc_result_totext(result)); 451 } 452 453 for (result = dns_rdataset_first(&rdataset); 454 result == ISC_R_SUCCESS; 455 result = dns_rdataset_next(&rdataset)) 456 { 457 dns_rdata_init(&rdata); 458 dns_rdataset_current(&rdataset, &rdata); 459 emit(dir, &rdata); 460 } 461 } else { 462 unsigned char key_buf[DST_KEY_MAXSIZE]; 463 464 loadkey(argv[isc_commandline_index], key_buf, DST_KEY_MAXSIZE, 465 &rdata); 466 467 emit(dir, &rdata); 468 } 469 470 if (dns_rdataset_isassociated(&rdataset)) { 471 dns_rdataset_disassociate(&rdataset); 472 } 473 cleanup_logging(&log); 474 dst_lib_destroy(); 475 if (verbose > 10) { 476 isc_mem_stats(mctx, stdout); 477 } 478 isc_mem_destroy(&mctx); 479 480 fflush(stdout); 481 if (ferror(stdout)) { 482 fprintf(stderr, "write error\n"); 483 return (1); 484 } else { 485 return (0); 486 } 487 } 488