/* * keygen is a small programs that generate a dnskey and private key * for a particular domain. * * (c) NLnet Labs, 2005 - 2008 * See the file LICENSE for the license */ #include "config.h" #include #include #include #include #include #ifdef HAVE_SSL static void usage(FILE *fp, char *prog) { fprintf(fp, "%s -a [-b bits] [-r /dev/random] [-v] domain\n", prog); fprintf(fp, " generate a new key pair for domain\n"); fprintf(fp, " -a \tuse the specified algorithm (-a list to"); fprintf(fp, " show a list)\n"); fprintf(fp, " -k\t\tset the flags to 257; key signing key\n"); fprintf(fp, " -b \tspecify the keylength\n"); fprintf(fp, " -r \tspecify a random device (defaults to /dev/random)\n"); fprintf(fp, "\t\tto seed the random generator with\n"); fprintf(fp, " -v\t\tshow the version and exit\n"); fprintf(fp, " The following files will be created:\n"); fprintf(fp, " K++.key\tPublic key in RR format\n"); fprintf(fp, " K++.private\tPrivate key in key format\n"); fprintf(fp, " K++.ds\tDS in RR format (only for DNSSEC KSK keys)\n"); fprintf(fp, " The base name (K++ will be printed to stdout\n"); } static void show_algorithms(FILE *out) { ldns_lookup_table *lt = ldns_signing_algorithms; fprintf(out, "Possible algorithms:\n"); while (lt->name) { fprintf(out, "%s\n", lt->name); lt++; } } int main(int argc, char *argv[]) { int c; int fd; char *prog; /* default key size */ uint16_t def_bits = 1024; uint16_t bits = def_bits; bool had_bits = false; bool ksk; FILE *file; FILE *random; char *filename; char *owner; ldns_signing_algorithm algorithm; ldns_rdf *domain; ldns_rr *pubkey; ldns_key *key; ldns_rr *ds; prog = strdup(argv[0]); algorithm = 0; random = NULL; ksk = false; /* don't create a ksk per default */ while ((c = getopt(argc, argv, "a:kb:r:v")) != -1) { switch (c) { case 'a': if (algorithm != 0) { fprintf(stderr, "The -a argument can only be used once\n"); exit(1); } if (strncmp(optarg, "list", 5) == 0) { show_algorithms(stdout); exit(EXIT_SUCCESS); } algorithm = ldns_get_signing_algorithm_by_name(optarg); if (algorithm == 0) { fprintf(stderr, "Algorithm %s not found\n", optarg); show_algorithms(stderr); exit(EXIT_FAILURE); } break; case 'b': bits = (uint16_t) atoi(optarg); if (bits == 0) { fprintf(stderr, "%s: %s %d", prog, "Can not parse the -b argument, setting it to the default\n", (int) def_bits); bits = def_bits; } else had_bits = true; break; case 'k': ksk = true; break; case 'r': random = fopen(optarg, "r"); if (!random) { fprintf(stderr, "Cannot open random file %s: %s\n", optarg, strerror(errno)); exit(EXIT_FAILURE); } break; case 'v': printf("DNSSEC key generator version %s (ldns version %s)\n", LDNS_VERSION, ldns_version()); exit(EXIT_SUCCESS); break; default: usage(stderr, prog); exit(EXIT_FAILURE); } } argc -= optind; argv += optind; if (algorithm == 0) { printf("Please use the -a argument to provide an algorithm\n"); exit(1); } if (argc != 1) { usage(stderr, prog); exit(EXIT_FAILURE); } free(prog); /* check whether key size is within RFC boundaries */ switch (algorithm) { case LDNS_SIGN_RSAMD5: case LDNS_SIGN_RSASHA1: case LDNS_SIGN_RSASHA1_NSEC3: case LDNS_SIGN_RSASHA256: case LDNS_SIGN_RSASHA512: if (bits < 512 || bits > 4096) { fprintf(stderr, "For RSA, the key size must be between "); fprintf(stderr, " 512 and 4096 bits. Aborting.\n"); exit(1); } break; case LDNS_SIGN_DSA: case LDNS_SIGN_DSA_NSEC3: if (bits < 512 || bits > 1024) { fprintf(stderr, "For DSA, the key size must be between "); fprintf(stderr, " 512 and 1024 bits. Aborting.\n"); exit(1); } break; #ifdef USE_GOST case LDNS_SIGN_ECC_GOST: if(!ldns_key_EVP_load_gost_id()) { fprintf(stderr, "error: libcrypto does not provide GOST\n"); exit(EXIT_FAILURE); } break; #endif #ifdef USE_ECDSA case LDNS_SIGN_ECDSAP256SHA256: case LDNS_SIGN_ECDSAP384SHA384: break; #endif case LDNS_SIGN_HMACMD5: if (!had_bits) { bits = 512; } else if (bits < 1 || bits > 512) { fprintf(stderr, "For hmac-md5, the key size must be "); fprintf(stderr, "between 1 and 512 bits. Aborting.\n"); exit(1); } break; case LDNS_SIGN_HMACSHA1: if (!had_bits) { bits = 160; } else if (bits < 1 || bits > 160) { fprintf(stderr, "For hmac-sha1, the key size must be "); fprintf(stderr, "between 1 and 160 bits. Aborting.\n"); exit(1); } break; case LDNS_SIGN_HMACSHA224: if (!had_bits) { bits = 224; } else if (bits < 1 || bits > 224) { fprintf(stderr, "For hmac-sha224, the key size must be "); fprintf(stderr, "between 1 and 224 bits. Aborting.\n"); exit(1); } break; case LDNS_SIGN_HMACSHA256: if (!had_bits) { bits = 256; } else if (bits < 1 || bits > 256) { fprintf(stderr, "For hmac-sha256, the key size must be "); fprintf(stderr, "between 1 and 256 bits. Aborting.\n"); exit(1); } break; case LDNS_SIGN_HMACSHA384: if (!had_bits) { bits = 384; } else if (bits < 1 || bits > 384) { fprintf(stderr, "For hmac-sha384, the key size must be "); fprintf(stderr, "between 1 and 384 bits. Aborting.\n"); exit(1); } break; case LDNS_SIGN_HMACSHA512: if (!had_bits) { bits = 512; } else if (bits < 1 || bits > 512) { fprintf(stderr, "For hmac-sha512, the key size must be "); fprintf(stderr, "between 1 and 512 bits. Aborting.\n"); exit(1); } break; default: break; } if (!random) { random = fopen("/dev/random", "r"); if (!random) { fprintf(stderr, "Cannot open random file %s: %s\n", optarg, strerror(errno)); exit(EXIT_FAILURE); } } (void)ldns_init_random(random, (unsigned int) bits/8); fclose(random); /* create an rdf from the domain name */ domain = ldns_dname_new_frm_str(argv[0]); /* generate a new key */ key = ldns_key_new_frm_algorithm(algorithm, bits); if(!key) { fprintf(stderr, "cannot generate key of algorithm %s\n", ldns_pkt_algorithm2str((ldns_algorithm)algorithm)); exit(EXIT_FAILURE); } /* set the owner name in the key - this is a /separate/ step */ ldns_key_set_pubkey_owner(key, domain); /* ksk flag */ if (ksk) { ldns_key_set_flags(key, ldns_key_flags(key) + 1); } /* create the public from the ldns_key */ pubkey = ldns_key2rr(key); if (!pubkey) { fprintf(stderr, "Could not extract the public key from the key structure..."); ldns_key_deep_free(key); exit(EXIT_FAILURE); } owner = ldns_rdf2str(ldns_rr_owner(pubkey)); /* calculate and set the keytag */ ldns_key_set_keytag(key, ldns_calc_keytag(pubkey)); /* build the DS record */ switch (algorithm) { #ifdef USE_ECDSA case LDNS_SIGN_ECDSAP384SHA384: ds = ldns_key_rr2ds(pubkey, LDNS_SHA384); break; case LDNS_SIGN_ECDSAP256SHA256: #endif #ifdef USE_ED25519 case LDNS_SIGN_ED25519: #endif #ifdef USE_ED448 case LDNS_SIGN_ED448: #endif case LDNS_SIGN_RSASHA256: case LDNS_SIGN_RSASHA512: ds = ldns_key_rr2ds(pubkey, LDNS_SHA256); break; case LDNS_SIGN_ECC_GOST: #ifdef USE_GOST ds = ldns_key_rr2ds(pubkey, LDNS_HASH_GOST); #else ds = ldns_key_rr2ds(pubkey, LDNS_SHA256); #endif break; default: ds = ldns_key_rr2ds(pubkey, LDNS_SHA1); break; } /* print the public key RR to .key */ filename = LDNS_XMALLOC(char, strlen(owner) + 17); snprintf(filename, strlen(owner) + 16, "K%s+%03u+%05u.key", owner, algorithm, (unsigned int) ldns_key_keytag(key)); file = fopen(filename, "w"); if (!file) { fprintf(stderr, "Unable to open %s: %s\n", filename, strerror(errno)); ldns_key_deep_free(key); free(owner); ldns_rr_free(pubkey); ldns_rr_free(ds); LDNS_FREE(filename); exit(EXIT_FAILURE); } else { /* temporarily set question so that TTL is not printed */ ldns_rr_set_question(pubkey, true); ldns_rr_print(file, pubkey); ldns_rr_set_question(pubkey, false); fclose(file); LDNS_FREE(filename); } /* print the priv key to stderr */ filename = LDNS_XMALLOC(char, strlen(owner) + 21); snprintf(filename, strlen(owner) + 20, "K%s+%03u+%05u.private", owner, algorithm, (unsigned int) ldns_key_keytag(key)); /* use open() here to prevent creating world-readable private keys (CVE-2014-3209)*/ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (fd < 0) { goto fail; } file = fdopen(fd, "w"); if (!file) { goto fail; } ldns_key_print(file, key); fclose(file); LDNS_FREE(filename); /* print the DS to .ds */ if (ksk && algorithm != LDNS_SIGN_HMACMD5 && algorithm != LDNS_SIGN_HMACSHA1 && algorithm != LDNS_SIGN_HMACSHA224 && algorithm != LDNS_SIGN_HMACSHA256 && algorithm != LDNS_SIGN_HMACSHA384 && algorithm != LDNS_SIGN_HMACSHA512) { filename = LDNS_XMALLOC(char, strlen(owner) + 16); snprintf(filename, strlen(owner) + 15, "K%s+%03u+%05u.ds", owner, algorithm, (unsigned int) ldns_key_keytag(key)); file = fopen(filename, "w"); if (!file) { fprintf(stderr, "Unable to open %s: %s\n", filename, strerror(errno)); ldns_key_deep_free(key); free(owner); ldns_rr_free(pubkey); ldns_rr_free(ds); LDNS_FREE(filename); exit(EXIT_FAILURE); } else { /* temporarily set question so that TTL is not printed */ ldns_rr_set_question(ds, true); ldns_rr_print(file, ds); ldns_rr_set_question(ds, false); fclose(file); LDNS_FREE(filename); } } fprintf(stdout, "K%s+%03u+%05u\n", owner, algorithm, (unsigned int) ldns_key_keytag(key)); ldns_key_deep_free(key); free(owner); ldns_rr_free(pubkey); ldns_rr_free(ds); exit(EXIT_SUCCESS); fail: fprintf(stderr, "Unable to open %s: %s\n", filename, strerror(errno)); ldns_key_deep_free(key); free(owner); ldns_rr_free(pubkey); ldns_rr_free(ds); LDNS_FREE(filename); exit(EXIT_FAILURE); } #else int main(int argc, char **argv) { fprintf(stderr, "ldns-keygen needs OpenSSL support, which has not been compiled in\n"); return 1; } #endif /* HAVE_SSL */