=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/ssh-keygen.c,v retrieving revision 1.196 retrieving revision 1.226 diff -u -r1.196 -r1.226 --- src/usr.bin/ssh/ssh-keygen.c 2010/08/04 05:40:39 1.196 +++ src/usr.bin/ssh/ssh-keygen.c 2013/04/19 01:01:00 1.226 @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.196 2010/08/04 05:40:39 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.226 2013/04/19 01:01:00 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -40,7 +40,10 @@ #include "match.h" #include "hostfile.h" #include "dns.h" +#include "ssh.h" #include "ssh2.h" +#include "atomicio.h" +#include "krl.h" #ifdef ENABLE_PKCS11 #include "ssh-pkcs11.h" @@ -49,6 +52,7 @@ /* Number of bits in the RSA/DSA key. This value can be set on the command line. */ #define DEFAULT_BITS 2048 #define DEFAULT_BITS_DSA 1024 +#define DEFAULT_BITS_ECDSA 256 u_int32_t bits = 0; /* @@ -98,7 +102,7 @@ char *ca_key_path = NULL; /* Certificate serial number */ -long long cert_serial = 0; +unsigned long long cert_serial = 0; /* Key type when certifying */ u_int cert_key_type = SSH2_CERT_TYPE_USER; @@ -138,6 +142,8 @@ char *key_type_name = NULL; +/* Load key from this PKCS#11 provider */ +char *pkcs11provider = NULL; /* argv0 */ extern char *__progname; @@ -146,9 +152,42 @@ /* moduli.c */ int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *); -int prime_test(FILE *, FILE *, u_int32_t, u_int32_t); +int prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long, + unsigned long); static void +type_bits_valid(int type, u_int32_t *bitsp) +{ + u_int maxbits; + + if (type == KEY_UNSPEC) { + fprintf(stderr, "unknown key type %s\n", key_type_name); + exit(1); + } + if (*bitsp == 0) { + if (type == KEY_DSA) + *bitsp = DEFAULT_BITS_DSA; + else if (type == KEY_ECDSA) + *bitsp = DEFAULT_BITS_ECDSA; + else + *bitsp = DEFAULT_BITS; + } + maxbits = (type == KEY_DSA) ? + OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS; + if (*bitsp > maxbits) { + fprintf(stderr, "key bits exceeds maximum %d\n", maxbits); + exit(1); + } + if (type == KEY_DSA && *bitsp != 1024) + fatal("DSA keys must be 1024 bits"); + else if (type != KEY_ECDSA && *bitsp < 768) + fatal("Key must at least be 768 bits"); + else if (type == KEY_ECDSA && key_ecdsa_bits_to_nid(*bitsp) == -1) + fatal("Invalid ECDSA key length - valid lengths are " + "256, 384 or 521 bits"); +} + +static void ask_filename(struct passwd *pw, const char *prompt) { char buf[1024]; @@ -166,6 +205,10 @@ case KEY_DSA: name = _PATH_SSH_CLIENT_ID_DSA; break; + case KEY_ECDSA_CERT: + case KEY_ECDSA: + name = _PATH_SSH_CLIENT_ID_ECDSA; + break; case KEY_RSA_CERT: case KEY_RSA_CERT_V00: case KEY_RSA: @@ -219,6 +262,10 @@ u_char *blob; char comment[61]; + if (k->type == KEY_RSA1) { + fprintf(stderr, "version 1 keys are not supported\n"); + exit(1); + } if (key_to_blob(k, &blob, &len) <= 0) { fprintf(stderr, "key_to_blob failed\n"); exit(1); @@ -242,6 +289,7 @@ do_convert_to_pkcs8(Key *k) { switch (key_type_plain(k->type)) { + case KEY_RSA1: case KEY_RSA: if (!PEM_write_RSA_PUBKEY(stdout, k->rsa)) fatal("PEM_write_RSA_PUBKEY failed"); @@ -250,6 +298,10 @@ if (!PEM_write_DSA_PUBKEY(stdout, k->dsa)) fatal("PEM_write_DSA_PUBKEY failed"); break; + case KEY_ECDSA: + if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa)) + fatal("PEM_write_EC_PUBKEY failed"); + break; default: fatal("%s: unsupported key type %s", __func__, key_type(k)); } @@ -260,6 +312,7 @@ do_convert_to_pem(Key *k) { switch (key_type_plain(k->type)) { + case KEY_RSA1: case KEY_RSA: if (!PEM_write_RSAPublicKey(stdout, k->rsa)) fatal("PEM_write_RSAPublicKey failed"); @@ -270,6 +323,7 @@ fatal("PEM_write_DSAPublicKey failed"); break; #endif + /* XXX ECDSA? */ default: fatal("%s: unsupported key type %s", __func__, key_type(k)); } @@ -292,10 +346,6 @@ exit(1); } } - if (k->type == KEY_RSA1) { - fprintf(stderr, "version 1 keys are not supported\n"); - exit(1); - } switch (convert_format) { case FMT_RFC4716: @@ -529,6 +579,12 @@ (*k)->type = KEY_DSA; (*k)->dsa = EVP_PKEY_get1_DSA(pubkey); break; + case EVP_PKEY_EC: + *k = key_new(KEY_UNSPEC); + (*k)->type = KEY_ECDSA; + (*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey); + (*k)->ecdsa_nid = key_ecdsa_key_to_nid((*k)->ecdsa); + break; default: fatal("%s: unsupported pubkey type %d", __func__, EVP_PKEY_type(pubkey->type)); @@ -564,6 +620,7 @@ fclose(fp); return; } + /* XXX ECDSA */ #endif fatal("%s: unrecognised raw private key format", __func__); } @@ -604,6 +661,10 @@ ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL); break; + case KEY_ECDSA: + ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL, + NULL, 0, NULL, NULL); + break; case KEY_RSA: ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL); @@ -647,20 +708,38 @@ } static void -do_download(struct passwd *pw, char *pkcs11provider) +do_download(struct passwd *pw) { #ifdef ENABLE_PKCS11 Key **keys = NULL; int i, nkeys; + enum fp_rep rep; + enum fp_type fptype; + char *fp, *ra; + fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; + rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX; + pkcs11_init(0); nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys); if (nkeys <= 0) fatal("cannot read public key from pkcs11"); for (i = 0; i < nkeys; i++) { - key_write(keys[i], stdout); + if (print_fingerprint) { + fp = key_fingerprint(keys[i], fptype, rep); + ra = key_fingerprint(keys[i], SSH_FP_MD5, + SSH_FP_RANDOMART); + printf("%u %s %s (PKCS11 key)\n", key_size(keys[i]), + fp, key_type(keys[i])); + if (log_level >= SYSLOG_LEVEL_VERBOSE) + printf("%s\n", ra); + xfree(ra); + xfree(fp); + } else { + key_write(keys[i], stdout); + fprintf(stdout, "\n"); + } key_free(keys[i]); - fprintf(stdout, "\n"); } xfree(keys); pkcs11_terminate(); @@ -779,6 +858,98 @@ } static void +do_gen_all_hostkeys(struct passwd *pw) +{ + struct { + char *key_type; + char *key_type_display; + char *path; + } key_types[] = { + { "rsa1", "RSA1", _PATH_HOST_KEY_FILE }, + { "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE }, + { "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE }, + { "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE }, + { NULL, NULL, NULL } + }; + + int first = 0; + struct stat st; + Key *private, *public; + char comment[1024]; + int i, type, fd; + FILE *f; + + for (i = 0; key_types[i].key_type; i++) { + if (stat(key_types[i].path, &st) == 0) + continue; + if (errno != ENOENT) { + printf("Could not stat %s: %s", key_types[i].path, + strerror(errno)); + first = 0; + continue; + } + + if (first == 0) { + first = 1; + printf("%s: generating new host keys: ", __progname); + } + printf("%s ", key_types[i].key_type_display); + fflush(stdout); + arc4random_stir(); + type = key_type_from_name(key_types[i].key_type); + strlcpy(identity_file, key_types[i].path, sizeof(identity_file)); + bits = 0; + type_bits_valid(type, &bits); + private = key_generate(type, bits); + if (private == NULL) { + fprintf(stderr, "key_generate failed\n"); + first = 0; + continue; + } + public = key_from_private(private); + snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, + hostname); + if (!key_save_private(private, identity_file, "", comment)) { + printf("Saving the key failed: %s.\n", identity_file); + key_free(private); + key_free(public); + first = 0; + continue; + } + key_free(private); + arc4random_stir(); + strlcat(identity_file, ".pub", sizeof(identity_file)); + fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { + printf("Could not save your public key in %s\n", + identity_file); + key_free(public); + first = 0; + continue; + } + f = fdopen(fd, "w"); + if (f == NULL) { + printf("fdopen %s failed\n", identity_file); + key_free(public); + first = 0; + continue; + } + if (!key_write(public, f)) { + fprintf(stderr, "write key failed\n"); + key_free(public); + first = 0; + continue; + } + fprintf(f, " %s\n", comment); + fclose(f); + key_free(public); + + } + if (first != 0) + printf("\n"); +} + +static void printhost(FILE *f, const char *name, Key *public, int ca, int hash) { if (print_fingerprint) { @@ -923,8 +1094,14 @@ ca ? " (CA key)" : ""); printhost(out, cp, pub, ca, 0); } - if (delete_host && !c && !ca) - printhost(out, cp, pub, ca, 0); + if (delete_host) { + if (!c && !ca) + printhost(out, cp, pub, ca, 0); + else + printf("# Host %s found: " + "line %d type %s\n", name, + num, key_type(pub)); + } } else if (hash_hosts) printhost(out, cp, pub, ca, 0); } else { @@ -939,8 +1116,14 @@ printhost(out, name, pub, ca, hash_hosts && !ca); } - if (delete_host && !c && !ca) - printhost(out, cp, pub, ca, 0); + if (delete_host) { + if (!c && !ca) + printhost(out, cp, pub, ca, 0); + else + printf("# Host %s found: " + "line %d type %s\n", name, + num, key_type(pub)); + } } else if (hash_hosts) { for (cp2 = strsep(&cp, ","); cp2 != NULL && *cp2 != '\0'; @@ -1291,6 +1474,9 @@ certflags_command != NULL) add_string_option(c, "force-command", certflags_command); if ((which & OPTIONS_EXTENSIONS) != 0 && + (certflags_flags & CERTOPT_X_FWD) != 0) + add_flag_option(c, "permit-X11-forwarding"); + if ((which & OPTIONS_EXTENSIONS) != 0 && (certflags_flags & CERTOPT_AGENT_FWD) != 0) add_flag_option(c, "permit-agent-forwarding"); if ((which & OPTIONS_EXTENSIONS) != 0 && @@ -1302,14 +1488,40 @@ if ((which & OPTIONS_EXTENSIONS) != 0 && (certflags_flags & CERTOPT_USER_RC) != 0) add_flag_option(c, "permit-user-rc"); - if ((which & OPTIONS_EXTENSIONS) != 0 && - (certflags_flags & CERTOPT_X_FWD) != 0) - add_flag_option(c, "permit-X11-forwarding"); if ((which & OPTIONS_CRITICAL) != 0 && certflags_src_addr != NULL) add_string_option(c, "source-address", certflags_src_addr); } +static Key * +load_pkcs11_key(char *path) +{ +#ifdef ENABLE_PKCS11 + Key **keys = NULL, *public, *private = NULL; + int i, nkeys; + + if ((public = key_load_public(path, NULL)) == NULL) + fatal("Couldn't load CA public key \"%s\"", path); + + nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase, &keys); + debug3("%s: %d keys", __func__, nkeys); + if (nkeys <= 0) + fatal("cannot read public key from pkcs11"); + for (i = 0; i < nkeys; i++) { + if (key_equal_public(public, keys[i])) { + private = keys[i]; + continue; + } + key_free(keys[i]); + } + xfree(keys); + key_free(public); + return private; +#else + fatal("no pkcs11 support"); +#endif /* ENABLE_PKCS11 */ +} + static void do_ca_sign(struct passwd *pw, int argc, char **argv) { @@ -1320,11 +1532,6 @@ FILE *f; int v00 = 0; /* legacy keys */ - tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); - if ((ca = load_identity(tmp)) == NULL) - fatal("Couldn't load CA key \"%s\"", tmp); - xfree(tmp); - if (key_type_name != NULL) { switch (key_type_from_name(key_type_name)) { case KEY_RSA_CERT_V00: @@ -1344,6 +1551,15 @@ } } + pkcs11_init(1); + tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); + if (pkcs11provider != NULL) { + if ((ca = load_pkcs11_key(tmp)) == NULL) + fatal("No PKCS#11 key matching %s found", ca_key_path); + } else if ((ca = load_identity(tmp)) == NULL) + fatal("Couldn't load CA key \"%s\"", tmp); + xfree(tmp); + for (i = 0; i < argc; i++) { /* Split list of principals */ n = 0; @@ -1361,7 +1577,8 @@ tmp = tilde_expand_filename(argv[i], pw->pw_uid); if ((public = key_load_public(tmp, &comment)) == NULL) fatal("%s: unable to open \"%s\"", __func__, tmp); - if (public->type != KEY_RSA && public->type != KEY_DSA) + if (public->type != KEY_RSA && public->type != KEY_DSA && + public->type != KEY_ECDSA) fatal("%s: key \"%s\" type %s cannot be certified", __func__, tmp, key_type(public)); @@ -1407,7 +1624,8 @@ if (!quiet) { logit("Signed %s key %s: id \"%s\" serial %llu%s%s " "valid %s", key_cert_type(public), - out, public->cert->key_id, public->cert->serial, + out, public->cert->key_id, + (unsigned long long)public->cert->serial, cert_principals != NULL ? " for " : "", cert_principals != NULL ? cert_principals : "", fmt_validity(cert_valid_from, cert_valid_to)); @@ -1416,6 +1634,7 @@ key_free(public); xfree(out); } + pkcs11_terminate(); exit(0); } @@ -1518,7 +1737,7 @@ { char *val; - if (strcmp(opt, "clear") == 0) + if (strcasecmp(opt, "clear") == 0) certflags_flags = 0; else if (strcasecmp(opt, "no-x11-forwarding") == 0) certflags_flags &= ~CERTOPT_X_FWD; @@ -1631,8 +1850,10 @@ printf(" Signing CA: %s %s\n", key_type(key->cert->signature_key), ca_fp); printf(" Key ID: \"%s\"\n", key->cert->key_id); - if (!v00) - printf(" Serial: %llu\n", key->cert->serial); + if (!v00) { + printf(" Serial: %llu\n", + (unsigned long long)key->cert->serial); + } printf(" Valid: %s\n", fmt_validity(key->cert->valid_after, key->cert->valid_before)); printf(" Principals: "); @@ -1664,10 +1885,234 @@ } static void +load_krl(const char *path, struct ssh_krl **krlp) +{ + Buffer krlbuf; + int fd; + + buffer_init(&krlbuf); + if ((fd = open(path, O_RDONLY)) == -1) + fatal("open %s: %s", path, strerror(errno)); + if (!key_load_file(fd, path, &krlbuf)) + fatal("Unable to load KRL"); + close(fd); + /* XXX check sigs */ + if (ssh_krl_from_blob(&krlbuf, krlp, NULL, 0) != 0 || + *krlp == NULL) + fatal("Invalid KRL file"); + buffer_free(&krlbuf); +} + +static void +update_krl_from_file(struct passwd *pw, const char *file, const Key *ca, + struct ssh_krl *krl) +{ + Key *key = NULL; + u_long lnum = 0; + char *path, *cp, *ep, line[SSH_MAX_PUBKEY_BYTES]; + unsigned long long serial, serial2; + int i, was_explicit_key, was_sha1, r; + FILE *krl_spec; + + path = tilde_expand_filename(file, pw->pw_uid); + if (strcmp(path, "-") == 0) { + krl_spec = stdin; + free(path); + path = xstrdup("(standard input)"); + } else if ((krl_spec = fopen(path, "r")) == NULL) + fatal("fopen %s: %s", path, strerror(errno)); + + if (!quiet) + printf("Revoking from %s\n", path); + while (read_keyfile_line(krl_spec, path, line, sizeof(line), + &lnum) == 0) { + was_explicit_key = was_sha1 = 0; + cp = line + strspn(line, " \t"); + /* Trim trailing space, comments and strip \n */ + for (i = 0, r = -1; cp[i] != '\0'; i++) { + if (cp[i] == '#' || cp[i] == '\n') { + cp[i] = '\0'; + break; + } + if (cp[i] == ' ' || cp[i] == '\t') { + /* Remember the start of a span of whitespace */ + if (r == -1) + r = i; + } else + r = -1; + } + if (r != -1) + cp[r] = '\0'; + if (*cp == '\0') + continue; + if (strncasecmp(cp, "serial:", 7) == 0) { + if (ca == NULL) { + fatal("revoking certificated by serial number " + "requires specification of a CA key"); + } + cp += 7; + cp = cp + strspn(cp, " \t"); + errno = 0; + serial = strtoull(cp, &ep, 0); + if (*cp == '\0' || (*ep != '\0' && *ep != '-')) + fatal("%s:%lu: invalid serial \"%s\"", + path, lnum, cp); + if (errno == ERANGE && serial == ULLONG_MAX) + fatal("%s:%lu: serial out of range", + path, lnum); + serial2 = serial; + if (*ep == '-') { + cp = ep + 1; + errno = 0; + serial2 = strtoull(cp, &ep, 0); + if (*cp == '\0' || *ep != '\0') + fatal("%s:%lu: invalid serial \"%s\"", + path, lnum, cp); + if (errno == ERANGE && serial2 == ULLONG_MAX) + fatal("%s:%lu: serial out of range", + path, lnum); + if (serial2 <= serial) + fatal("%s:%lu: invalid serial range " + "%llu:%llu", path, lnum, + (unsigned long long)serial, + (unsigned long long)serial2); + } + if (ssh_krl_revoke_cert_by_serial_range(krl, + ca, serial, serial2) != 0) { + fatal("%s: revoke serial failed", + __func__); + } + } else if (strncasecmp(cp, "id:", 3) == 0) { + if (ca == NULL) { + fatal("revoking certificated by key ID " + "requires specification of a CA key"); + } + cp += 3; + cp = cp + strspn(cp, " \t"); + if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0) + fatal("%s: revoke key ID failed", __func__); + } else { + if (strncasecmp(cp, "key:", 4) == 0) { + cp += 4; + cp = cp + strspn(cp, " \t"); + was_explicit_key = 1; + } else if (strncasecmp(cp, "sha1:", 5) == 0) { + cp += 5; + cp = cp + strspn(cp, " \t"); + was_sha1 = 1; + } else { + /* + * Just try to process the line as a key. + * Parsing will fail if it isn't. + */ + } + if ((key = key_new(KEY_UNSPEC)) == NULL) + fatal("key_new"); + if (key_read(key, &cp) != 1) + fatal("%s:%lu: invalid key", path, lnum); + if (was_explicit_key) + r = ssh_krl_revoke_key_explicit(krl, key); + else if (was_sha1) + r = ssh_krl_revoke_key_sha1(krl, key); + else + r = ssh_krl_revoke_key(krl, key); + if (r != 0) + fatal("%s: revoke key failed", __func__); + key_free(key); + } + } + if (strcmp(path, "-") != 0) + fclose(krl_spec); + free(path); +} + +static void +do_gen_krl(struct passwd *pw, int updating, int argc, char **argv) +{ + struct ssh_krl *krl; + struct stat sb; + Key *ca = NULL; + int fd, i; + char *tmp; + Buffer kbuf; + + if (*identity_file == '\0') + fatal("KRL generation requires an output file"); + if (stat(identity_file, &sb) == -1) { + if (errno != ENOENT) + fatal("Cannot access KRL \"%s\": %s", + identity_file, strerror(errno)); + if (updating) + fatal("KRL \"%s\" does not exist", identity_file); + } + if (ca_key_path != NULL) { + tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); + if ((ca = key_load_public(tmp, NULL)) == NULL) + fatal("Cannot load CA public key %s", tmp); + xfree(tmp); + } + + if (updating) + load_krl(identity_file, &krl); + else if ((krl = ssh_krl_init()) == NULL) + fatal("couldn't create KRL"); + + if (cert_serial != 0) + ssh_krl_set_version(krl, cert_serial); + if (identity_comment != NULL) + ssh_krl_set_comment(krl, identity_comment); + + for (i = 0; i < argc; i++) + update_krl_from_file(pw, argv[i], ca, krl); + + buffer_init(&kbuf); + if (ssh_krl_to_blob(krl, &kbuf, NULL, 0) != 0) + fatal("Couldn't generate KRL"); + if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) + fatal("open %s: %s", identity_file, strerror(errno)); + if (atomicio(vwrite, fd, buffer_ptr(&kbuf), buffer_len(&kbuf)) != + buffer_len(&kbuf)) + fatal("write %s: %s", identity_file, strerror(errno)); + close(fd); + buffer_free(&kbuf); + ssh_krl_free(krl); + if (ca != NULL) + key_free(ca); +} + +static void +do_check_krl(struct passwd *pw, int argc, char **argv) +{ + int i, r, ret = 0; + char *comment; + struct ssh_krl *krl; + Key *k; + + if (*identity_file == '\0') + fatal("KRL checking requires an input file"); + load_krl(identity_file, &krl); + for (i = 0; i < argc; i++) { + if ((k = key_load_public(argv[i], &comment)) == NULL) + fatal("Cannot load public key %s", argv[i]); + r = ssh_krl_check_key(krl, k); + printf("%s%s%s%s: %s\n", argv[i], + *comment ? " (" : "", comment, *comment ? ")" : "", + r == 0 ? "ok" : "REVOKED"); + if (r != 0) + ret = 1; + key_free(k); + free(comment); + } + ssh_krl_free(krl); + exit(ret); +} + +static void usage(void) { fprintf(stderr, "usage: %s [options]\n", __progname); fprintf(stderr, "Options:\n"); + fprintf(stderr, " -A Generate non-existent host keys for all key types.\n"); fprintf(stderr, " -a trials Number of trials for screening DH-GEX moduli.\n"); fprintf(stderr, " -B Show bubblebabble digest of key file.\n"); fprintf(stderr, " -b bits Number of bits in the key to create.\n"); @@ -1685,6 +2130,10 @@ fprintf(stderr, " -h Generate host certificate instead of a user certificate.\n"); fprintf(stderr, " -I key_id Key identifier to include in certificate.\n"); fprintf(stderr, " -i Import foreign format to OpenSSH key file.\n"); + fprintf(stderr, " -J number Screen this number of moduli lines.\n"); + fprintf(stderr, " -j number Start screening moduli at specified line.\n"); + fprintf(stderr, " -K checkpt Write checkpoints to this file.\n"); + fprintf(stderr, " -k Generate a KRL file.\n"); fprintf(stderr, " -L Print the contents of a certificate.\n"); fprintf(stderr, " -l Show fingerprint of key file.\n"); fprintf(stderr, " -M memory Amount of memory (MB) to use for generating DH-GEX moduli.\n"); @@ -1694,6 +2143,7 @@ fprintf(stderr, " -O option Specify a certificate option.\n"); fprintf(stderr, " -P phrase Provide old passphrase.\n"); fprintf(stderr, " -p Change passphrase of private key file.\n"); + fprintf(stderr, " -Q Test whether key(s) are revoked in KRL.\n"); fprintf(stderr, " -q Quiet.\n"); fprintf(stderr, " -R hostname Remove host from known_hosts file.\n"); fprintf(stderr, " -r hostname Print DNS resource record.\n"); @@ -1701,6 +2151,7 @@ fprintf(stderr, " -s ca_key Certify keys with CA key.\n"); fprintf(stderr, " -T file Screen candidates for DH-GEX moduli.\n"); fprintf(stderr, " -t type Specify type of key to create.\n"); + fprintf(stderr, " -u Update KRL rather than creating a new one.\n"); fprintf(stderr, " -V from:to Specify certificate validity interval.\n"); fprintf(stderr, " -v Verbose.\n"); fprintf(stderr, " -W gen Generator to use for generating DH-GEX moduli.\n"); @@ -1717,15 +2168,16 @@ main(int argc, char **argv) { char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; - char out_file[MAXPATHLEN], *pkcs11provider = NULL; - char *rr_hostname = NULL; + char *checkpoint = NULL; + char out_file[MAXPATHLEN], *ep, *rr_hostname = NULL; Key *private, *public; struct passwd *pw; struct stat st; int opt, type, fd; - u_int maxbits; u_int32_t memory = 0, generator_wanted = 0, trials = 100; int do_gen_candidates = 0, do_screen_candidates = 0; + int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0; + unsigned long start_lineno = 0, lines_to_process = 0; BIGNUM *start = NULL; FILE *f; const char *errstr; @@ -1736,7 +2188,7 @@ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); - SSLeay_add_all_algorithms(); + OpenSSL_add_all_algorithms(); log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); /* we need this for the home * directory. */ @@ -1750,11 +2202,14 @@ exit(1); } - while ((opt = getopt(argc, argv, "degiqpclBHLhvxXyF:b:f:t:D:I:P:m:N:n:" - "O:C:r:g:R:T:G:M:S:s:a:V:W:z:")) != -1) { + while ((opt = getopt(argc, argv, "ABHLQXceghiklpquvxy" + "C:D:F:G:I:J:K:M:N:O:P:R:S:T:V:W:a:b:f:g:j:m:n:r:s:t:z:")) != -1) { switch (opt) { + case 'A': + gen_all_hostkeys = 1; + break; case 'b': - bits = (u_int32_t)strtonum(optarg, 768, 32768, &errstr); + bits = (u_int32_t)strtonum(optarg, 256, 32768, &errstr); if (errstr) fatal("Bits has bad value %s (%s)", optarg, errstr); @@ -1769,6 +2224,12 @@ case 'I': cert_key_id = optarg; break; + case 'J': + lines_to_process = strtoul(optarg, NULL, 10); + break; + case 'j': + start_lineno = strtoul(optarg, NULL, 10); + break; case 'R': delete_host = 1; rr_hostname = optarg; @@ -1821,6 +2282,9 @@ case 'N': identity_new_passphrase = optarg; break; + case 'Q': + check_krl = 1; + break; case 'O': add_cert_option(optarg); break; @@ -1839,6 +2303,9 @@ cert_key_type = SSH2_CERT_TYPE_HOST; certflags_flags = 0; break; + case 'k': + gen_krl = 1; + break; case 'i': case 'X': /* import key */ @@ -1847,9 +2314,6 @@ case 'y': print_public = 1; break; - case 'd': - key_type_name = "dsa"; - break; case 's': ca_key_path = optarg; break; @@ -1859,6 +2323,9 @@ case 'D': pkcs11provider = optarg; break; + case 'u': + update_krl = 1; + break; case 'v': if (log_level == SYSLOG_LEVEL_INFO) log_level = SYSLOG_LEVEL_DEBUG1; @@ -1901,6 +2368,11 @@ sizeof(out_file)) fatal("Output filename too long"); break; + case 'K': + if (strlen(optarg) >= MAXPATHLEN) + fatal("Checkpoint filename too long"); + checkpoint = xstrdup(optarg); + break; case 'S': /* XXX - also compare length against bits */ if (BN_hex2bn(&start, optarg) == 0) @@ -1910,9 +2382,11 @@ parse_cert_times(optarg); break; case 'z': - cert_serial = strtonum(optarg, 0, LLONG_MAX, &errstr); - if (errstr) - fatal("Invalid serial number: %s", errstr); + errno = 0; + cert_serial = strtoull(optarg, &ep, 10); + if (*optarg < '0' || *optarg > '9' || *ep != '\0' || + (errno == ERANGE && cert_serial == ULLONG_MAX)) + fatal("Invalid serial number \"%s\"", optarg); break; case '?': default: @@ -1927,11 +2401,11 @@ argc -= optind; if (ca_key_path != NULL) { - if (argc < 1) { + if (argc < 1 && !gen_krl) { printf("Too few arguments.\n"); usage(); } - } else if (argc > 0) { + } else if (argc > 0 && !gen_krl && !check_krl) { printf("Too many arguments.\n"); usage(); } @@ -1940,9 +2414,17 @@ usage(); } if (print_fingerprint && (delete_host || hash_hosts)) { - printf("Cannot use -l with -D or -R.\n"); + printf("Cannot use -l with -H or -R.\n"); usage(); } + if (gen_krl) { + do_gen_krl(pw, update_krl, argc, argv); + return (0); + } + if (check_krl) { + do_check_krl(pw, argc, argv); + return (0); + } if (ca_key_path != NULL) { if (cert_key_id == NULL) fatal("Must specify key id (-I) when certifying"); @@ -1952,6 +2434,8 @@ do_show_cert(pw); if (delete_host || hash_hosts || find_host) do_known_hosts(pw, rr_hostname); + if (pkcs11provider != NULL) + do_download(pw); if (print_fingerprint || print_bubblebabble) do_fingerprint(pw); if (change_passphrase) @@ -1981,14 +2465,14 @@ _PATH_HOST_RSA_KEY_FILE, rr_hostname); n += do_print_resource_record(pw, _PATH_HOST_DSA_KEY_FILE, rr_hostname); + n += do_print_resource_record(pw, + _PATH_HOST_ECDSA_KEY_FILE, rr_hostname); if (n == 0) fatal("no keys found."); exit(0); } } - if (pkcs11provider != NULL) - do_download(pw, pkcs11provider); if (do_gen_candidates) { FILE *out = fopen(out_file, "w"); @@ -2008,7 +2492,7 @@ if (do_screen_candidates) { FILE *in; - FILE *out = fopen(out_file, "w"); + FILE *out = fopen(out_file, "a"); if (have_identity && strcmp(identity_file, "-") != 0) { if ((in = fopen(identity_file, "r")) == NULL) { @@ -2023,31 +2507,25 @@ fatal("Couldn't open moduli file \"%s\": %s", out_file, strerror(errno)); } - if (prime_test(in, out, trials, generator_wanted) != 0) + if (prime_test(in, out, trials, generator_wanted, checkpoint, + start_lineno, lines_to_process) != 0) fatal("modulus screening failed"); return (0); } + if (gen_all_hostkeys) { + do_gen_all_hostkeys(pw); + return (0); + } + arc4random_stir(); if (key_type_name == NULL) key_type_name = "rsa"; type = key_type_from_name(key_type_name); - if (type == KEY_UNSPEC) { - fprintf(stderr, "unknown key type %s\n", key_type_name); - exit(1); - } - if (bits == 0) - bits = (type == KEY_DSA) ? DEFAULT_BITS_DSA : DEFAULT_BITS; - maxbits = (type == KEY_DSA) ? - OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS; - if (bits > maxbits) { - fprintf(stderr, "key bits exceeds maximum %d\n", maxbits); - exit(1); - } - if (type == KEY_DSA && bits != 1024) - fatal("DSA keys must be 1024 bits"); + type_bits_valid(type, &bits); + if (!quiet) printf("Generating public/private %s key pair.\n", key_type_name); private = key_generate(type, bits);