=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/ssh-keygen.c,v retrieving revision 1.17 retrieving revision 1.25.2.4 diff -u -r1.17 -r1.25.2.4 --- src/usr.bin/ssh/ssh-keygen.c 2000/03/16 20:56:14 1.17 +++ src/usr.bin/ssh/ssh-keygen.c 2001/03/12 15:44:16 1.25.2.4 @@ -2,25 +2,32 @@ * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland * All rights reserved - * Created: Mon Mar 27 02:26:40 1995 ylo * Identity and host key generation and maintenance. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" -RCSID("$Id: ssh-keygen.c,v 1.17 2000/03/16 20:56:14 markus Exp $"); +RCSID("$OpenBSD: ssh-keygen.c,v 1.25.2.4 2001/03/12 15:44:16 jason Exp $"); -#include "rsa.h" -#include "ssh.h" +#include +#include + #include "xmalloc.h" -#include "fingerprint.h" +#include "key.h" +#include "authfile.h" +#include "uuencode.h" +#include "buffer.h" +#include "bufaux.h" +#include "pathnames.h" +#include "log.h" +#include "readpass.h" -/* Generated private key. */ -RSA *private_key; - -/* Generated public key. */ -RSA *public_key; - -/* Number of bits in the RSA key. This value can be changed on the command line. */ +/* Number of bits in the RSA/DSA key. This value can be changed on the command line. */ int bits = 1024; /* @@ -53,17 +60,43 @@ /* This is set to the new comment if given on the command line. */ char *identity_comment = NULL; +/* Dump public key file in format used by real and the original SSH 2 */ +int convert_to_ssh2 = 0; +int convert_from_ssh2 = 0; +int print_public = 0; + +/* default to RSA for SSH-1 */ +char *key_type_name = "rsa1"; + /* argv0 */ extern char *__progname; +char hostname[MAXHOSTNAMELEN]; + void ask_filename(struct passwd *pw, const char *prompt) { char buf[1024]; - snprintf(identity_file, sizeof(identity_file), "%s/%s", - pw->pw_dir, SSH_CLIENT_IDENTITY); - printf("%s (%s): ", prompt, identity_file); - fflush(stdout); + char *name = NULL; + + switch (key_type_from_name(key_type_name)) { + case KEY_RSA1: + name = _PATH_SSH_CLIENT_IDENTITY; + break; + case KEY_DSA: + name = _PATH_SSH_CLIENT_ID_DSA; + break; + case KEY_RSA: + name = _PATH_SSH_CLIENT_ID_RSA; + break; + default: + fprintf(stderr, "bad key type"); + exit(1); + break; + } + snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name); + fprintf(stderr, "%s (%s): ", prompt, identity_file); + fflush(stderr); if (fgets(buf, sizeof(buf), stdin) == NULL) exit(1); if (strchr(buf, '\n')) @@ -73,15 +106,226 @@ have_identity = 1; } +int +try_load_key(char *filename, Key *k) +{ + int success = 1; + if (!load_private_key(filename, "", k, NULL)) { + char *pass = read_passphrase("Enter passphrase: ", 1); + if (!load_private_key(filename, pass, k, NULL)) { + success = 0; + } + memset(pass, 0, strlen(pass)); + xfree(pass); + } + return success; +} + +#define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" +#define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----" +#define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" +#define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb + void +do_convert_to_ssh2(struct passwd *pw) +{ + Key *k; + int len; + u_char *blob; + struct stat st; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + k = key_new(KEY_UNSPEC); + if (!try_load_key(identity_file, k)) { + fprintf(stderr, "load failed\n"); + exit(1); + } + key_to_blob(k, &blob, &len); + fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); + fprintf(stdout, + "Comment: \"%d-bit %s, converted from OpenSSH by %s@%s\"\n", + key_size(k), key_type(k), + pw->pw_name, hostname); + dump_base64(stdout, blob, len); + fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); + key_free(k); + xfree(blob); + exit(0); +} + +void +buffer_get_bignum_bits(Buffer *b, BIGNUM *value) +{ + int bits = buffer_get_int(b); + int bytes = (bits + 7) / 8; + if (buffer_len(b) < bytes) + fatal("buffer_get_bignum_bits: input buffer too small"); + BN_bin2bn((u_char *)buffer_ptr(b), bytes, value); + buffer_consume(b, bytes); +} + +Key * +do_convert_private_ssh2_from_blob(char *blob, int blen) +{ + Buffer b; + DSA *dsa; + Key *key = NULL; + int ignore, magic, rlen; + char *type, *cipher; + + buffer_init(&b); + buffer_append(&b, blob, blen); + + magic = buffer_get_int(&b); + if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { + error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC); + buffer_free(&b); + return NULL; + } + ignore = buffer_get_int(&b); + type = buffer_get_string(&b, NULL); + cipher = buffer_get_string(&b, NULL); + ignore = buffer_get_int(&b); + ignore = buffer_get_int(&b); + ignore = buffer_get_int(&b); + xfree(type); + + if (strcmp(cipher, "none") != 0) { + error("unsupported cipher %s", cipher); + xfree(cipher); + buffer_free(&b); + return NULL; + } + xfree(cipher); + + key = key_new(KEY_DSA); + dsa = key->dsa; + dsa->priv_key = BN_new(); + if (dsa->priv_key == NULL) { + error("alloc priv_key failed"); + key_free(key); + return NULL; + } + buffer_get_bignum_bits(&b, dsa->p); + buffer_get_bignum_bits(&b, dsa->g); + buffer_get_bignum_bits(&b, dsa->q); + buffer_get_bignum_bits(&b, dsa->pub_key); + buffer_get_bignum_bits(&b, dsa->priv_key); + rlen = buffer_len(&b); + if(rlen != 0) + error("do_convert_private_ssh2_from_blob: remaining bytes in key blob %d", rlen); + buffer_free(&b); + return key; +} + +void +do_convert_from_ssh2(struct passwd *pw) +{ + Key *k; + int blen; + char line[1024], *p; + char blob[8096]; + char encoded[8096]; + struct stat st; + int escaped = 0, private = 0, ok; + FILE *fp; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + fp = fopen(identity_file, "r"); + if (fp == NULL) { + perror(identity_file); + exit(1); + } + encoded[0] = '\0'; + while (fgets(line, sizeof(line), fp)) { + if (!(p = strchr(line, '\n'))) { + fprintf(stderr, "input line too long.\n"); + exit(1); + } + if (p > line && p[-1] == '\\') + escaped++; + if (strncmp(line, "----", 4) == 0 || + strstr(line, ": ") != NULL) { + if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) + private = 1; + fprintf(stderr, "ignore: %s", line); + continue; + } + if (escaped) { + escaped--; + fprintf(stderr, "escaped: %s", line); + continue; + } + *p = '\0'; + strlcat(encoded, line, sizeof(encoded)); + } + blen = uudecode(encoded, (u_char *)blob, sizeof(blob)); + if (blen < 0) { + fprintf(stderr, "uudecode failed.\n"); + exit(1); + } + k = private ? + do_convert_private_ssh2_from_blob(blob, blen) : + key_from_blob(blob, blen); + if (k == NULL) { + fprintf(stderr, "decode blob failed.\n"); + exit(1); + } + ok = private ? + PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) : + key_write(k, stdout); + if (!ok) { + fprintf(stderr, "key write failed"); + exit(1); + } + key_free(k); + fprintf(stdout, "\n"); + fclose(fp); + exit(0); +} + +void +do_print_public(struct passwd *pw) +{ + Key *k; + struct stat st; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + k = key_new(KEY_UNSPEC); + if (!try_load_key(identity_file, k)) { + fprintf(stderr, "load failed\n"); + exit(1); + } + if (!key_write(k, stdout)) + fprintf(stderr, "key_write failed"); + key_free(k); + fprintf(stdout, "\n"); + exit(0); +} + +void do_fingerprint(struct passwd *pw) { + FILE *f; - BIGNUM *e, *n; - RSA *public_key; + Key *public; char *comment = NULL, *cp, *ep, line[16*1024]; - int i, skip = 0, num = 1, invalid = 1; - unsigned int ignore; + int i, skip = 0, num = 1, invalid = 1, success = 0; struct stat st; if (!have_identity) @@ -90,21 +334,26 @@ perror(identity_file); exit(1); } - - public_key = RSA_new(); - if (load_public_key(identity_file, public_key, &comment)) { - printf("%d %s %s\n", BN_num_bits(public_key->n), - fingerprint(public_key->e, public_key->n), - comment); - RSA_free(public_key); + public = key_new(KEY_RSA1); + if (load_public_key(identity_file, public, &comment)) { + success = 1; + } else { + key_free(public); + public = key_new(KEY_UNSPEC); + if (try_load_public_key(identity_file, public, &comment)) + success = 1; + else + debug("try_load_public_key KEY_UNSPEC failed"); + } + if (success) { + printf("%d %s %s\n", key_size(public), key_fingerprint(public), comment); + key_free(public); + xfree(comment); exit(0); } - RSA_free(public_key); f = fopen(identity_file, "r"); if (f != NULL) { - n = BN_new(); - e = BN_new(); while (fgets(line, sizeof(line), f)) { i = strlen(line) - 1; if (line[i] != '\n') { @@ -139,18 +388,25 @@ *cp++ = '\0'; } ep = cp; - if (auth_rsa_read_key(&cp, &ignore, e, n)) { - invalid = 0; - comment = *cp ? cp : comment; - printf("%d %s %s\n", BN_num_bits(n), - fingerprint(e, n), - comment ? comment : "no comment"); + public = key_new(KEY_RSA1); + if (key_read(public, &cp) != 1) { + cp = ep; + key_free(public); + public = key_new(KEY_UNSPEC); + if (key_read(public, &cp) != 1) { + key_free(public); + continue; + } } + comment = *cp ? cp : comment; + printf("%d %s %s\n", key_size(public), + key_fingerprint(public), + comment ? comment : "no comment"); + invalid = 0; } - BN_free(e); - BN_free(n); fclose(f); } + key_free(public); if (invalid) { printf("%s is not a valid key file.\n", identity_file); exit(1); @@ -168,7 +424,9 @@ char *comment; char *old_passphrase, *passphrase1, *passphrase2; struct stat st; - RSA *private_key; + Key *private; + Key *public; + int type = KEY_RSA1; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -176,22 +434,21 @@ perror(identity_file); exit(1); } - public_key = RSA_new(); - if (!load_public_key(identity_file, public_key, NULL)) { - printf("%s is not a valid key file.\n", identity_file); - exit(1); + public = key_new(type); + if (!load_public_key(identity_file, public, NULL)) { + type = KEY_UNSPEC; + } else { + /* Clear the public key since we are just about to load the whole file. */ + key_free(public); } - /* Clear the public key since we are just about to load the whole file. */ - RSA_free(public_key); - /* Try to load the file with empty passphrase. */ - private_key = RSA_new(); - if (!load_private_key(identity_file, "", private_key, &comment)) { + private = key_new(type); + if (!load_private_key(identity_file, "", private, &comment)) { if (identity_passphrase) old_passphrase = xstrdup(identity_passphrase); else old_passphrase = read_passphrase("Enter old passphrase: ", 1); - if (!load_private_key(identity_file, old_passphrase, private_key, &comment)) { + if (!load_private_key(identity_file, old_passphrase, private, &comment)) { memset(old_passphrase, 0, strlen(old_passphrase)); xfree(old_passphrase); printf("Bad passphrase.\n"); @@ -226,19 +483,19 @@ } /* Save the file using the new passphrase. */ - if (!save_private_key(identity_file, passphrase1, private_key, comment)) { + if (!save_private_key(identity_file, passphrase1, private, comment)) { printf("Saving the key failed: %s: %s.\n", identity_file, strerror(errno)); memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); - RSA_free(private_key); + key_free(private); xfree(comment); exit(1); } /* Destroy the passphrase and the copy of the key in memory. */ memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); - RSA_free(private_key); /* Destroys contents */ + key_free(private); /* Destroys contents */ xfree(comment); printf("Your identification has been saved with the new passphrase.\n"); @@ -252,11 +509,11 @@ do_change_comment(struct passwd *pw) { char new_comment[1024], *comment; - RSA *private_key; + Key *private; + Key *public; char *passphrase; struct stat st; FILE *f; - char *tmpbuf; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -268,14 +525,15 @@ * Try to load the public key from the file the verify that it is * readable and of the proper format. */ - public_key = RSA_new(); - if (!load_public_key(identity_file, public_key, NULL)) { + public = key_new(KEY_RSA1); + if (!load_public_key(identity_file, public, NULL)) { printf("%s is not a valid key file.\n", identity_file); + printf("Comments are only supported in RSA1 keys\n"); exit(1); } - private_key = RSA_new(); - if (load_private_key(identity_file, "", private_key, &comment)) + private = key_new(KEY_RSA1); + if (load_private_key(identity_file, "", private, &comment)) passphrase = xstrdup(""); else { if (identity_passphrase) @@ -285,7 +543,7 @@ else passphrase = read_passphrase("Enter passphrase: ", 1); /* Try to load using the passphrase. */ - if (!load_private_key(identity_file, passphrase, private_key, &comment)) { + if (!load_private_key(identity_file, passphrase, private, &comment)) { memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); printf("Bad passphrase.\n"); @@ -301,7 +559,7 @@ fflush(stdout); if (!fgets(new_comment, sizeof(new_comment), stdin)) { memset(passphrase, 0, strlen(passphrase)); - RSA_free(private_key); + key_free(private); exit(1); } if (strchr(new_comment, '\n')) @@ -309,18 +567,18 @@ } /* Save the file using the new passphrase. */ - if (!save_private_key(identity_file, passphrase, private_key, new_comment)) { + if (!save_private_key(identity_file, passphrase, private, new_comment)) { printf("Saving the key failed: %s: %s.\n", identity_file, strerror(errno)); memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); - RSA_free(private_key); + key_free(private); xfree(comment); exit(1); } memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); - RSA_free(private_key); + key_free(private); strlcat(identity_file, ".pub", sizeof(identity_file)); f = fopen(identity_file, "w"); @@ -328,13 +586,10 @@ printf("Could not save your public key in %s\n", identity_file); exit(1); } - fprintf(f, "%d ", BN_num_bits(public_key->n)); - tmpbuf = BN_bn2dec(public_key->e); - fprintf(f, "%s ", tmpbuf); - free(tmpbuf); - tmpbuf = BN_bn2dec(public_key->n); - fprintf(f, "%s %s\n", tmpbuf, new_comment); - free(tmpbuf); + if (!key_write(public, f)) + fprintf(stderr, "write key failed"); + key_free(public); + fprintf(f, " %s\n", new_comment); fclose(f); xfree(comment); @@ -346,8 +601,7 @@ void usage(void) { - printf("ssh-keygen version %s\n", SSH_VERSION); - printf("Usage: %s [-b bits] [-p] [-c] [-l] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname); + printf("Usage: %s [-lpqxXyc] [-t type] [-b bits] [-f file] [-C comment] [-N new-pass] [-P pass]\n", __progname); exit(1); } @@ -359,29 +613,29 @@ { char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2; struct passwd *pw; - char *tmpbuf; - int opt; + int opt, type; struct stat st; FILE *f; - char hostname[MAXHOSTNAMELEN]; + Key *private; + Key *public; + extern int optind; extern char *optarg; - /* check if RSA support exists */ - if (rsa_alive() == 0) { - fprintf(stderr, - "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", - __progname); - exit(1); - } + SSLeay_add_all_algorithms(); + /* we need this for the home * directory. */ pw = getpwuid(getuid()); if (!pw) { printf("You don't exist, go away!\n"); exit(1); } + if (gethostname(hostname, sizeof(hostname)) < 0) { + perror("gethostname"); + exit(1); + } - while ((opt = getopt(ac, av, "qpclb:f:P:N:C:")) != EOF) { + while ((opt = getopt(ac, av, "dqpclRxXyb:f:t:P:N:C:")) != -1) { switch (opt) { case 'b': bits = atoi(optarg); @@ -424,6 +678,31 @@ quiet = 1; break; + case 'R': + /* unused */ + exit(0); + break; + + case 'x': + convert_to_ssh2 = 1; + break; + + case 'X': + convert_from_ssh2 = 1; + break; + + case 'y': + print_public = 1; + break; + + case 'd': + key_type_name = "dsa"; + break; + + case 't': + key_type_name = optarg; + break; + case '?': default: usage(); @@ -443,25 +722,37 @@ do_change_passphrase(pw); if (change_comment) do_change_comment(pw); + if (convert_to_ssh2) + do_convert_to_ssh2(pw); + if (convert_from_ssh2) + do_convert_from_ssh2(pw); + if (print_public) + do_print_public(pw); arc4random_stir(); - if (quiet) - rsa_set_verbose(0); + 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 (!quiet) + printf("Generating public/private %s key pair.\n", key_type_name); + private = key_generate(type, bits); + if (private == NULL) { + fprintf(stderr, "key_generate failed"); + exit(1); + } + public = key_from_private(private); - /* Generate the rsa key pair. */ - private_key = RSA_new(); - public_key = RSA_new(); - rsa_generate_key(private_key, public_key, bits); - if (!have_identity) ask_filename(pw, "Enter file in which to save the key"); /* Create ~/.ssh directory if it doesn\'t already exist. */ - snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, SSH_USER_DIR); + snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR); if (strstr(identity_file, dotsshdir) != NULL && stat(dotsshdir, &st) < 0) { - if (mkdir(dotsshdir, 0755) < 0) + if (mkdir(dotsshdir, 0700) < 0) error("Could not create directory '%s'.", dotsshdir); else if (!quiet) printf("Created directory '%s'.\n", dotsshdir); @@ -504,18 +795,14 @@ if (identity_comment) { strlcpy(comment, identity_comment, sizeof(comment)); } else { - /* Create default commend field for the passphrase. */ - if (gethostname(hostname, sizeof(hostname)) < 0) { - perror("gethostname"); - exit(1); - } + /* Create default commend field for the passphrase. */ snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); } /* Save the key with the given passphrase and comment. */ - if (!save_private_key(identity_file, passphrase1, private_key, comment)) { + if (!save_private_key(identity_file, passphrase1, private, comment)) { printf("Saving the key failed: %s: %s.\n", - identity_file, strerror(errno)); + identity_file, strerror(errno)); memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); exit(1); @@ -525,7 +812,7 @@ xfree(passphrase1); /* Clear the private key and the random number generator. */ - RSA_free(private_key); + key_free(private); arc4random_stir(); if (!quiet) @@ -537,21 +824,18 @@ printf("Could not save your public key in %s\n", identity_file); exit(1); } - fprintf(f, "%d ", BN_num_bits(public_key->n)); - tmpbuf = BN_bn2dec(public_key->e); - fprintf(f, "%s ", tmpbuf); - free(tmpbuf); - tmpbuf = BN_bn2dec(public_key->n); - fprintf(f, "%s %s\n", tmpbuf, comment); - free(tmpbuf); + if (!key_write(public, f)) + fprintf(stderr, "write key failed"); + fprintf(f, " %s\n", comment); fclose(f); if (!quiet) { - printf("Your public key has been saved in %s.\n", identity_file); + printf("Your public key has been saved in %s.\n", + identity_file); printf("The key fingerprint is:\n"); - printf("%d %s %s\n", BN_num_bits(public_key->n), - fingerprint(public_key->e, public_key->n), - comment); + printf("%s %s\n", key_fingerprint(public), comment); } + + key_free(public); exit(0); }