1/* 2 * Copyright (C) 2007-2011 Internet Systems Consortium, Inc. ("ISC") 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 * PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17/* $Id: dnssec-keyfromlabel.c,v 1.32.14.4 2011/11/30 00:51:38 marka Exp $ */ 18 19/*! \file */ 20 21#include <config.h> 22 23#include <ctype.h> 24#include <stdlib.h> 25 26#include <isc/buffer.h> 27#include <isc/commandline.h> 28#include <isc/entropy.h> 29#include <isc/mem.h> 30#include <isc/region.h> 31#include <isc/print.h> 32#include <isc/string.h> 33#include <isc/util.h> 34 35#include <dns/dnssec.h> 36#include <dns/fixedname.h> 37#include <dns/keyvalues.h> 38#include <dns/log.h> 39#include <dns/name.h> 40#include <dns/rdataclass.h> 41#include <dns/result.h> 42#include <dns/secalg.h> 43 44#include <dst/dst.h> 45 46#include "dnssectool.h" 47 48#define MAX_RSA 4096 /* should be long enough... */ 49 50const char *program = "dnssec-keyfromlabel"; 51int verbose; 52 53#define DEFAULT_ALGORITHM "RSASHA1" 54#define DEFAULT_NSEC3_ALGORITHM "NSEC3RSASHA1" 55 56static const char *algs = "RSA | RSAMD5 | DH | DSA | RSASHA1 |" 57 " NSEC3DSA | NSEC3RSASHA1 |" 58 " RSASHA256 | RSASHA512 | ECCGOST"; 59 60ISC_PLATFORM_NORETURN_PRE static void 61usage(void) ISC_PLATFORM_NORETURN_POST; 62 63static void 64usage(void) { 65 fprintf(stderr, "Usage:\n"); 66 fprintf(stderr, " %s -l label [options] name\n\n", 67 program); 68 fprintf(stderr, "Version: %s\n", VERSION); 69 fprintf(stderr, "Required options:\n"); 70 fprintf(stderr, " -l label: label of the key pair\n"); 71 fprintf(stderr, " name: owner of the key\n"); 72 fprintf(stderr, "Other options:\n"); 73 fprintf(stderr, " -a algorithm: %s\n", algs); 74 fprintf(stderr, " (default: RSASHA1, or " 75 "NSEC3RSASHA1 if using -3)\n"); 76 fprintf(stderr, " -3: use NSEC3-capable algorithm\n"); 77 fprintf(stderr, " -c class (default: IN)\n"); 78#ifdef USE_PKCS11 79 fprintf(stderr, " -E enginename (default: pkcs11)\n"); 80#else 81 fprintf(stderr, " -E enginename\n"); 82#endif 83 fprintf(stderr, " -f keyflag: KSK | REVOKE\n"); 84 fprintf(stderr, " -K directory: directory in which to place " 85 "key files\n"); 86 fprintf(stderr, " -k: generate a TYPE=KEY key\n"); 87 fprintf(stderr, " -n nametype: ZONE | HOST | ENTITY | USER | OTHER\n"); 88 fprintf(stderr, " (DNSKEY generation defaults to ZONE\n"); 89 fprintf(stderr, " -p protocol: default: 3 [dnssec]\n"); 90 fprintf(stderr, " -t type: " 91 "AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF " 92 "(default: AUTHCONF)\n"); 93 fprintf(stderr, " -y: permit keys that might collide\n"); 94 fprintf(stderr, " -v verbose level\n"); 95 fprintf(stderr, "Date options:\n"); 96 fprintf(stderr, " -P date/[+-]offset: set key publication date\n"); 97 fprintf(stderr, " -A date/[+-]offset: set key activation date\n"); 98 fprintf(stderr, " -R date/[+-]offset: set key revocation date\n"); 99 fprintf(stderr, " -I date/[+-]offset: set key inactivation date\n"); 100 fprintf(stderr, " -D date/[+-]offset: set key deletion date\n"); 101 fprintf(stderr, " -G: generate key only; do not set -P or -A\n"); 102 fprintf(stderr, " -C: generate a backward-compatible key, omitting" 103 " all dates\n"); 104 fprintf(stderr, "Output:\n"); 105 fprintf(stderr, " K<name>+<alg>+<id>.key, " 106 "K<name>+<alg>+<id>.private\n"); 107 108 exit (-1); 109} 110 111int 112main(int argc, char **argv) { 113 char *algname = NULL, *freeit = NULL; 114 char *nametype = NULL, *type = NULL; 115 const char *directory = NULL; 116#ifdef USE_PKCS11 117 const char *engine = "pkcs11"; 118#else 119 const char *engine = NULL; 120#endif 121 char *classname = NULL; 122 char *endp; 123 dst_key_t *key = NULL; 124 dns_fixedname_t fname; 125 dns_name_t *name; 126 isc_uint16_t flags = 0, kskflag = 0, revflag = 0; 127 dns_secalg_t alg; 128 isc_boolean_t oldstyle = ISC_FALSE; 129 isc_mem_t *mctx = NULL; 130 int ch; 131 int protocol = -1, signatory = 0; 132 isc_result_t ret; 133 isc_textregion_t r; 134 char filename[255]; 135 isc_buffer_t buf; 136 isc_log_t *log = NULL; 137 isc_entropy_t *ectx = NULL; 138 dns_rdataclass_t rdclass; 139 int options = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC; 140 char *label = NULL; 141 isc_stdtime_t publish = 0, activate = 0, revoke = 0; 142 isc_stdtime_t inactive = 0, delete = 0; 143 isc_stdtime_t now; 144 isc_boolean_t setpub = ISC_FALSE, setact = ISC_FALSE; 145 isc_boolean_t setrev = ISC_FALSE, setinact = ISC_FALSE; 146 isc_boolean_t setdel = ISC_FALSE; 147 isc_boolean_t unsetpub = ISC_FALSE, unsetact = ISC_FALSE; 148 isc_boolean_t unsetrev = ISC_FALSE, unsetinact = ISC_FALSE; 149 isc_boolean_t unsetdel = ISC_FALSE; 150 isc_boolean_t genonly = ISC_FALSE; 151 isc_boolean_t use_nsec3 = ISC_FALSE; 152 isc_boolean_t avoid_collisions = ISC_TRUE; 153 isc_boolean_t exact; 154 unsigned char c; 155 156 if (argc == 1) 157 usage(); 158 159 RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS); 160 161 dns_result_register(); 162 163 isc_commandline_errprint = ISC_FALSE; 164 165 isc_stdtime_get(&now); 166 167 while ((ch = isc_commandline_parse(argc, argv, 168 "3a:Cc:E:f:K:kl:n:p:t:v:yFhGP:A:R:I:D:")) != -1) 169 { 170 switch (ch) { 171 case '3': 172 use_nsec3 = ISC_TRUE; 173 break; 174 case 'a': 175 algname = isc_commandline_argument; 176 break; 177 case 'C': 178 oldstyle = ISC_TRUE; 179 break; 180 case 'c': 181 classname = isc_commandline_argument; 182 break; 183 case 'E': 184 engine = isc_commandline_argument; 185 break; 186 case 'f': 187 c = (unsigned char)(isc_commandline_argument[0]); 188 if (toupper(c) == 'K') 189 kskflag = DNS_KEYFLAG_KSK; 190 else if (toupper(c) == 'R') 191 revflag = DNS_KEYFLAG_REVOKE; 192 else 193 fatal("unknown flag '%s'", 194 isc_commandline_argument); 195 break; 196 case 'K': 197 directory = isc_commandline_argument; 198 ret = try_dir(directory); 199 if (ret != ISC_R_SUCCESS) 200 fatal("cannot open directory %s: %s", 201 directory, isc_result_totext(ret)); 202 break; 203 case 'k': 204 options |= DST_TYPE_KEY; 205 break; 206 case 'l': 207 label = isc_mem_strdup(mctx, isc_commandline_argument); 208 break; 209 case 'n': 210 nametype = isc_commandline_argument; 211 break; 212 case 'p': 213 protocol = strtol(isc_commandline_argument, &endp, 10); 214 if (*endp != '\0' || protocol < 0 || protocol > 255) 215 fatal("-p must be followed by a number " 216 "[0..255]"); 217 break; 218 case 't': 219 type = isc_commandline_argument; 220 break; 221 case 'v': 222 verbose = strtol(isc_commandline_argument, &endp, 0); 223 if (*endp != '\0') 224 fatal("-v must be followed by a number"); 225 break; 226 case 'y': 227 avoid_collisions = ISC_FALSE; 228 break; 229 case 'G': 230 genonly = ISC_TRUE; 231 break; 232 case 'P': 233 if (setpub || unsetpub) 234 fatal("-P specified more than once"); 235 236 if (strcasecmp(isc_commandline_argument, "none")) { 237 setpub = ISC_TRUE; 238 publish = strtotime(isc_commandline_argument, 239 now, now); 240 } else { 241 unsetpub = ISC_TRUE; 242 } 243 break; 244 case 'A': 245 if (setact || unsetact) 246 fatal("-A specified more than once"); 247 248 if (strcasecmp(isc_commandline_argument, "none")) { 249 setact = ISC_TRUE; 250 activate = strtotime(isc_commandline_argument, 251 now, now); 252 } else { 253 unsetact = ISC_TRUE; 254 } 255 break; 256 case 'R': 257 if (setrev || unsetrev) 258 fatal("-R specified more than once"); 259 260 if (strcasecmp(isc_commandline_argument, "none")) { 261 setrev = ISC_TRUE; 262 revoke = strtotime(isc_commandline_argument, 263 now, now); 264 } else { 265 unsetrev = ISC_TRUE; 266 } 267 break; 268 case 'I': 269 if (setinact || unsetinact) 270 fatal("-I specified more than once"); 271 272 if (strcasecmp(isc_commandline_argument, "none")) { 273 setinact = ISC_TRUE; 274 inactive = strtotime(isc_commandline_argument, 275 now, now); 276 } else { 277 unsetinact = ISC_TRUE; 278 } 279 break; 280 case 'D': 281 if (setdel || unsetdel) 282 fatal("-D specified more than once"); 283 284 if (strcasecmp(isc_commandline_argument, "none")) { 285 setdel = ISC_TRUE; 286 delete = strtotime(isc_commandline_argument, 287 now, now); 288 } else { 289 unsetdel = ISC_TRUE; 290 } 291 break; 292 case 'F': 293 /* Reserved for FIPS mode */ 294 /* FALLTHROUGH */ 295 case '?': 296 if (isc_commandline_option != '?') 297 fprintf(stderr, "%s: invalid argument -%c\n", 298 program, isc_commandline_option); 299 /* FALLTHROUGH */ 300 case 'h': 301 usage(); 302 303 default: 304 fprintf(stderr, "%s: unhandled option -%c\n", 305 program, isc_commandline_option); 306 exit(1); 307 } 308 } 309 310 if (ectx == NULL) 311 setup_entropy(mctx, NULL, &ectx); 312 ret = dst_lib_init2(mctx, ectx, engine, 313 ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY); 314 if (ret != ISC_R_SUCCESS) 315 fatal("could not initialize dst: %s", 316 isc_result_totext(ret)); 317 318 setup_logging(verbose, mctx, &log); 319 320 if (label == NULL) 321 fatal("the key label was not specified"); 322 if (argc < isc_commandline_index + 1) 323 fatal("the key name was not specified"); 324 if (argc > isc_commandline_index + 1) 325 fatal("extraneous arguments"); 326 327 if (strchr(label, ':') == NULL && 328 engine != NULL && strlen(engine) != 0U) { 329 char *l; 330 int len; 331 332 len = strlen(label) + strlen(engine) + 2; 333 l = isc_mem_allocate(mctx, len); 334 if (l == NULL) 335 fatal("cannot allocate memory"); 336 snprintf(l, len, "%s:%s", engine, label); 337 isc_mem_free(mctx, label); 338 label = l; 339 } 340 341 if (algname == NULL) { 342 if (use_nsec3) 343 algname = strdup(DEFAULT_NSEC3_ALGORITHM); 344 else 345 algname = strdup(DEFAULT_ALGORITHM); 346 if (algname == NULL) 347 fatal("strdup failed"); 348 freeit = algname; 349 if (verbose > 0) 350 fprintf(stderr, "no algorithm specified; " 351 "defaulting to %s\n", algname); 352 } 353 354 if (strcasecmp(algname, "RSA") == 0) { 355 fprintf(stderr, "The use of RSA (RSAMD5) is not recommended.\n" 356 "If you still wish to use RSA (RSAMD5) please " 357 "specify \"-a RSAMD5\"\n"); 358 return (1); 359 } else { 360 r.base = algname; 361 r.length = strlen(algname); 362 ret = dns_secalg_fromtext(&alg, &r); 363 if (ret != ISC_R_SUCCESS) 364 fatal("unknown algorithm %s", algname); 365 if (alg == DST_ALG_DH) 366 options |= DST_TYPE_KEY; 367 } 368 369 if (use_nsec3 && 370 alg != DST_ALG_NSEC3DSA && alg != DST_ALG_NSEC3RSASHA1 && 371 alg != DST_ALG_RSASHA256 && alg != DST_ALG_RSASHA512 && 372 alg != DST_ALG_ECCGOST) { 373 fatal("%s is incompatible with NSEC3; " 374 "do not use the -3 option", algname); 375 } 376 377 if (type != NULL && (options & DST_TYPE_KEY) != 0) { 378 if (strcasecmp(type, "NOAUTH") == 0) 379 flags |= DNS_KEYTYPE_NOAUTH; 380 else if (strcasecmp(type, "NOCONF") == 0) 381 flags |= DNS_KEYTYPE_NOCONF; 382 else if (strcasecmp(type, "NOAUTHCONF") == 0) { 383 flags |= (DNS_KEYTYPE_NOAUTH | DNS_KEYTYPE_NOCONF); 384 } 385 else if (strcasecmp(type, "AUTHCONF") == 0) 386 /* nothing */; 387 else 388 fatal("invalid type %s", type); 389 } 390 391 if (nametype == NULL) { 392 if ((options & DST_TYPE_KEY) != 0) /* KEY */ 393 fatal("no nametype specified"); 394 flags |= DNS_KEYOWNER_ZONE; /* DNSKEY */ 395 } else if (strcasecmp(nametype, "zone") == 0) 396 flags |= DNS_KEYOWNER_ZONE; 397 else if ((options & DST_TYPE_KEY) != 0) { /* KEY */ 398 if (strcasecmp(nametype, "host") == 0 || 399 strcasecmp(nametype, "entity") == 0) 400 flags |= DNS_KEYOWNER_ENTITY; 401 else if (strcasecmp(nametype, "user") == 0) 402 flags |= DNS_KEYOWNER_USER; 403 else 404 fatal("invalid KEY nametype %s", nametype); 405 } else if (strcasecmp(nametype, "other") != 0) /* DNSKEY */ 406 fatal("invalid DNSKEY nametype %s", nametype); 407 408 rdclass = strtoclass(classname); 409 410 if (directory == NULL) 411 directory = "."; 412 413 if ((options & DST_TYPE_KEY) != 0) /* KEY */ 414 flags |= signatory; 415 else if ((flags & DNS_KEYOWNER_ZONE) != 0) { /* DNSKEY */ 416 flags |= kskflag; 417 flags |= revflag; 418 } 419 420 if (protocol == -1) 421 protocol = DNS_KEYPROTO_DNSSEC; 422 else if ((options & DST_TYPE_KEY) == 0 && 423 protocol != DNS_KEYPROTO_DNSSEC) 424 fatal("invalid DNSKEY protocol: %d", protocol); 425 426 if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) { 427 if ((flags & DNS_KEYFLAG_SIGNATORYMASK) != 0) 428 fatal("specified null key with signing authority"); 429 } 430 431 if ((flags & DNS_KEYFLAG_OWNERMASK) == DNS_KEYOWNER_ZONE && 432 alg == DNS_KEYALG_DH) 433 fatal("a key with algorithm '%s' cannot be a zone key", 434 algname); 435 436 dns_fixedname_init(&fname); 437 name = dns_fixedname_name(&fname); 438 isc_buffer_init(&buf, argv[isc_commandline_index], 439 strlen(argv[isc_commandline_index])); 440 isc_buffer_add(&buf, strlen(argv[isc_commandline_index])); 441 ret = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL); 442 if (ret != ISC_R_SUCCESS) 443 fatal("invalid key name %s: %s", argv[isc_commandline_index], 444 isc_result_totext(ret)); 445 446 isc_buffer_init(&buf, filename, sizeof(filename) - 1); 447 448 /* associate the key */ 449 ret = dst_key_fromlabel(name, alg, flags, protocol, 450 rdclass, engine, label, NULL, mctx, &key); 451 isc_entropy_stopcallbacksources(ectx); 452 453 if (ret != ISC_R_SUCCESS) { 454 char namestr[DNS_NAME_FORMATSIZE]; 455 char algstr[DNS_SECALG_FORMATSIZE]; 456 dns_name_format(name, namestr, sizeof(namestr)); 457 dns_secalg_format(alg, algstr, sizeof(algstr)); 458 fatal("failed to get key %s/%s: %s\n", 459 namestr, algstr, isc_result_totext(ret)); 460 /* NOTREACHED */ 461 exit(-1); 462 } 463 464 /* 465 * Set key timing metadata (unless using -C) 466 * 467 * Publish and activation dates are set to "now" by default, but 468 * can be overridden. Creation date is always set to "now". 469 */ 470 if (!oldstyle) { 471 dst_key_settime(key, DST_TIME_CREATED, now); 472 473 if (genonly && (setpub || setact)) 474 fatal("cannot use -G together with -P or -A options"); 475 476 if (setpub) 477 dst_key_settime(key, DST_TIME_PUBLISH, publish); 478 else if (setact) 479 dst_key_settime(key, DST_TIME_PUBLISH, activate); 480 else if (!genonly && !unsetpub) 481 dst_key_settime(key, DST_TIME_PUBLISH, now); 482 483 if (setact) 484 dst_key_settime(key, DST_TIME_ACTIVATE, activate); 485 else if (!genonly && !unsetact) 486 dst_key_settime(key, DST_TIME_ACTIVATE, now); 487 488 if (setrev) { 489 if (kskflag == 0) 490 fprintf(stderr, "%s: warning: Key is " 491 "not flagged as a KSK, but -R " 492 "was used. Revoking a ZSK is " 493 "legal, but undefined.\n", 494 program); 495 dst_key_settime(key, DST_TIME_REVOKE, revoke); 496 } 497 498 if (setinact) 499 dst_key_settime(key, DST_TIME_INACTIVE, inactive); 500 501 if (setdel) 502 dst_key_settime(key, DST_TIME_DELETE, delete); 503 } else { 504 if (setpub || setact || setrev || setinact || 505 setdel || unsetpub || unsetact || 506 unsetrev || unsetinact || unsetdel || genonly) 507 fatal("cannot use -C together with " 508 "-P, -A, -R, -I, -D, or -G options"); 509 /* 510 * Compatibility mode: Private-key-format 511 * should be set to 1.2. 512 */ 513 dst_key_setprivateformat(key, 1, 2); 514 } 515 516 /* 517 * Do not overwrite an existing key. Warn LOUDLY if there 518 * is a risk of ID collision due to this key or another key 519 * being revoked. 520 */ 521 if (key_collision(key, name, directory, mctx, &exact)) { 522 isc_buffer_clear(&buf); 523 ret = dst_key_buildfilename(key, 0, directory, &buf); 524 if (ret != ISC_R_SUCCESS) 525 fatal("dst_key_buildfilename returned: %s\n", 526 isc_result_totext(ret)); 527 if (exact) 528 fatal("%s: %s already exists\n", program, filename); 529 530 if (avoid_collisions) 531 fatal("%s: %s could collide with another key upon " 532 "revokation\n", program, filename); 533 534 fprintf(stderr, "%s: WARNING: Key %s could collide with " 535 "another key upon revokation. If you plan " 536 "to revoke keys, destroy this key and " 537 "generate a different one.\n", 538 program, filename); 539 } 540 541 ret = dst_key_tofile(key, options, directory); 542 if (ret != ISC_R_SUCCESS) { 543 char keystr[DST_KEY_FORMATSIZE]; 544 dst_key_format(key, keystr, sizeof(keystr)); 545 fatal("failed to write key %s: %s\n", keystr, 546 isc_result_totext(ret)); 547 } 548 549 isc_buffer_clear(&buf); 550 ret = dst_key_buildfilename(key, 0, NULL, &buf); 551 if (ret != ISC_R_SUCCESS) 552 fatal("dst_key_buildfilename returned: %s\n", 553 isc_result_totext(ret)); 554 printf("%s\n", filename); 555 dst_key_free(&key); 556 557 cleanup_logging(&log); 558 cleanup_entropy(&ectx); 559 dst_lib_destroy(); 560 dns_name_destroy(); 561 if (verbose > 10) 562 isc_mem_stats(mctx, stdout); 563 isc_mem_free(mctx, label); 564 isc_mem_destroy(&mctx); 565 566 if (freeit != NULL) 567 free(freeit); 568 569 return (0); 570} 571