From d4a8b7e34dd619a4debf9a206c81db26d1402ea6 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Wed, 27 Oct 1999 13:42:43 +1000 Subject: Initial revision --- ssh-keygen.c | 552 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 552 insertions(+) create mode 100644 ssh-keygen.c (limited to 'ssh-keygen.c') diff --git a/ssh-keygen.c b/ssh-keygen.c new file mode 100644 index 000000000..2ba64e756 --- /dev/null +++ b/ssh-keygen.c @@ -0,0 +1,552 @@ +/* + +ssh-keygen.c + +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. + +*/ + +#include "includes.h" +RCSID("$Id: ssh-keygen.c,v 1.1 1999/10/27 03:42:45 damien Exp $"); + +#include "rsa.h" +#include "ssh.h" +#include "xmalloc.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. */ +int bits = 1024; + +/* Flag indicating that we just want to change the passphrase. This can be + set on the command line. */ +int change_passphrase = 0; + +/* Flag indicating that we just want to change the comment. This can be set + on the command line. */ +int change_comment = 0; + +int quiet = 0; + +/* This is set to the identity file name if given on the command line. */ +char *identity_file = NULL; + +/* This is set to the passphrase if given on the command line. */ +char *identity_passphrase = NULL; + +/* This is set to the new passphrase if given on the command line. */ +char *identity_new_passphrase = NULL; + +/* This is set to the new comment if given on the command line. */ +char *identity_comment = NULL; + +/* Perform changing a passphrase. The argument is the passwd structure + for the current user. */ + +void +do_change_passphrase(struct passwd *pw) +{ + char buf[1024], *comment; + char *old_passphrase, *passphrase1, *passphrase2; + struct stat st; + RSA *private_key; + + /* Read key file name. */ + if (identity_file != NULL) { + strncpy(buf, identity_file, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + } else { + printf("Enter file in which the key is ($HOME/%s): ", SSH_CLIENT_IDENTITY); + fflush(stdout); + if (fgets(buf, sizeof(buf), stdin) == NULL) + exit(1); + if (strchr(buf, '\n')) + *strchr(buf, '\n') = 0; + if (strcmp(buf, "") == 0) + snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); + } + + /* Check if the file exists. */ + if (stat(buf, &st) < 0) + { + perror(buf); + exit(1); + } + + /* 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(buf, public_key, NULL)) + { + printf("%s is not a valid key file.\n", buf); + exit(1); + } + /* 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(buf, "", private_key, &comment)) { + /* Read passphrase from the user. */ + if (identity_passphrase) + old_passphrase = xstrdup(identity_passphrase); + else + old_passphrase = read_passphrase("Enter old passphrase: ", 1); + /* Try to load using the passphrase. */ + if (!load_private_key(buf, old_passphrase, private_key, &comment)) + { + memset(old_passphrase, 0, strlen(old_passphrase)); + xfree(old_passphrase); + printf("Bad passphrase.\n"); + exit(1); + } + /* Destroy the passphrase. */ + memset(old_passphrase, 0, strlen(old_passphrase)); + xfree(old_passphrase); + } + printf("Key has comment '%s'\n", comment); + + /* Ask the new passphrase (twice). */ + if (identity_new_passphrase) + { + passphrase1 = xstrdup(identity_new_passphrase); + passphrase2 = NULL; + } + else + { + passphrase1 = + read_passphrase("Enter new passphrase (empty for no passphrase): ", 1); + passphrase2 = read_passphrase("Enter same passphrase again: ", 1); + + /* Verify that they are the same. */ + if (strcmp(passphrase1, passphrase2) != 0) + { + memset(passphrase1, 0, strlen(passphrase1)); + memset(passphrase2, 0, strlen(passphrase2)); + xfree(passphrase1); + xfree(passphrase2); + printf("Pass phrases do not match. Try again.\n"); + exit(1); + } + /* Destroy the other copy. */ + memset(passphrase2, 0, strlen(passphrase2)); + xfree(passphrase2); + } + + /* Save the file using the new passphrase. */ + if (!save_private_key(buf, passphrase1, private_key, comment)) + { + printf("Saving the key failed: %s: %s.\n", + buf, strerror(errno)); + memset(passphrase1, 0, strlen(passphrase1)); + xfree(passphrase1); + RSA_free(private_key); + 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 */ + xfree(comment); + + printf("Your identification has been saved with the new passphrase.\n"); + exit(0); +} + +/* Change the comment of a private key file. */ + +void +do_change_comment(struct passwd *pw) +{ + char buf[1024], new_comment[1024], *comment; + RSA *private_key; + char *passphrase; + struct stat st; + FILE *f; + char *tmpbuf; + + /* Read key file name. */ + if (identity_file) + { + strncpy(buf, identity_file, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + } + else + { + printf("Enter file in which the key is ($HOME/%s): ", + SSH_CLIENT_IDENTITY); + fflush(stdout); + if (fgets(buf, sizeof(buf), stdin) == NULL) + exit(1); + if (strchr(buf, '\n')) + *strchr(buf, '\n') = 0; + if (strcmp(buf, "") == 0) + snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); + } + + /* Check if the file exists. */ + if (stat(buf, &st) < 0) + { + perror(buf); + exit(1); + } + + /* 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(buf, public_key, NULL)) + { + printf("%s is not a valid key file.\n", buf); + exit(1); + } + + private_key = RSA_new(); + /* Try to load the file with empty passphrase. */ + if (load_private_key(buf, "", private_key, &comment)) + passphrase = xstrdup(""); + else + { + /* Read passphrase from the user. */ + if (identity_passphrase) + passphrase = xstrdup(identity_passphrase); + else + if (identity_new_passphrase) + passphrase = xstrdup(identity_new_passphrase); + else + passphrase = read_passphrase("Enter passphrase: ", 1); + /* Try to load using the passphrase. */ + if (!load_private_key(buf, passphrase, private_key, &comment)) + { + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + printf("Bad passphrase.\n"); + exit(1); + } + } + printf("Key now has comment '%s'\n", comment); + + if (identity_comment) + { + strncpy(new_comment, identity_comment, sizeof(new_comment)); + new_comment[sizeof(new_comment) - 1] = '\0'; + } + else + { + printf("Enter new comment: "); + fflush(stdout); + if (!fgets(new_comment, sizeof(new_comment), stdin)) + { + memset(passphrase, 0, strlen(passphrase)); + RSA_free(private_key); + exit(1); + } + + /* Remove terminating newline from comment. */ + if (strchr(new_comment, '\n')) + *strchr(new_comment, '\n') = 0; + } + + /* Save the file using the new passphrase. */ + if (!save_private_key(buf, passphrase, private_key, new_comment)) + { + printf("Saving the key failed: %s: %s.\n", + buf, strerror(errno)); + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + RSA_free(private_key); + xfree(comment); + exit(1); + } + + /* Destroy the passphrase and the private key in memory. */ + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + RSA_free(private_key); + + /* Save the public key in text format in a file with the same name but + .pub appended. */ + strcat(buf, ".pub"); + f = fopen(buf, "w"); + if (!f) + { + printf("Could not save your public key in %s\n", buf); + 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); + fclose(f); + + xfree(comment); + + printf("The comment in your key file has been changed.\n"); + exit(0); +} + +/* Main program for key management. */ + +int +main(int ac, char **av) +{ + char buf[16384], buf2[1024], *passphrase1, *passphrase2; + struct passwd *pw; + char *tmpbuf; + int opt; + struct stat st; + FILE *f; + char hostname[MAXHOSTNAMELEN]; + extern int optind; + extern char *optarg; + + /* check if RSA support exists */ + if (rsa_alive() == 0) { + extern char *__progname; + + fprintf(stderr, + "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", + __progname); + exit(1); + } + + /* Get user\'s passwd structure. We need this for the home directory. */ + pw = getpwuid(getuid()); + if (!pw) + { + printf("You don't exist, go away!\n"); + exit(1); + } + + /* Create ~/.ssh directory if it doesn\'t already exist. */ + snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_USER_DIR); + if (stat(buf, &st) < 0) + if (mkdir(buf, 0755) < 0) + error("Could not create directory '%s'.", buf); + + /* Parse command line arguments. */ + while ((opt = getopt(ac, av, "qpcb:f:P:N:C:")) != EOF) + { + switch (opt) + { + case 'b': + bits = atoi(optarg); + if (bits < 512 || bits > 32768) + { + printf("Bits has bad value.\n"); + exit(1); + } + break; + + case 'p': + change_passphrase = 1; + break; + + case 'c': + change_comment = 1; + break; + + case 'f': + identity_file = optarg; + break; + + case 'P': + identity_passphrase = optarg; + break; + + case 'N': + identity_new_passphrase = optarg; + break; + + case 'C': + identity_comment = optarg; + break; + + case 'q': + quiet = 1; + break; + + case '?': + default: + printf("ssh-keygen version %s\n", SSH_VERSION); + printf("Usage: %s [-b bits] [-p] [-c] [-f file] [-P pass] [-N new-pass] [-C comment]\n", av[0]); + exit(1); + } + } + if (optind < ac) + { + printf("Too many arguments.\n"); + exit(1); + } + if (change_passphrase && change_comment) + { + printf("Can only have one of -p and -c.\n"); + exit(1); + } + + /* If the user requested to change the passphrase, do it now. This + function never returns. */ + if (change_passphrase) + do_change_passphrase(pw); + + /* If the user requested to change the comment, do it now. This function + never returns. */ + if (change_comment) + do_change_comment(pw); + + arc4random_stir(); + + if (quiet) + rsa_set_verbose(0); + + /* Generate the rsa key pair. */ + private_key = RSA_new(); + public_key = RSA_new(); + rsa_generate_key(private_key, public_key, bits); + + ask_file_again: + + /* Ask for a file to save the key in. */ + if (identity_file) + { + strncpy(buf, identity_file, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + } + else + { + printf("Enter file in which to save the key ($HOME/%s): ", + SSH_CLIENT_IDENTITY); + fflush(stdout); + if (fgets(buf, sizeof(buf), stdin) == NULL) + exit(1); + if (strchr(buf, '\n')) + *strchr(buf, '\n') = 0; + if (strcmp(buf, "") == 0) + snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); + } + + /* If the file aready exists, ask the user to confirm. */ + if (stat(buf, &st) >= 0) + { + printf("%s already exists.\n", buf); + printf("Overwrite (y/n)? "); + fflush(stdout); + if (fgets(buf2, sizeof(buf2), stdin) == NULL) + exit(1); + if (buf2[0] != 'y' && buf2[0] != 'Y') + exit(1); + } + + /* Ask for a passphrase (twice). */ + if (identity_passphrase) + passphrase1 = xstrdup(identity_passphrase); + else + if (identity_new_passphrase) + passphrase1 = xstrdup(identity_new_passphrase); + else + { + passphrase_again: + passphrase1 = + read_passphrase("Enter passphrase (empty for no passphrase): ", 1); + passphrase2 = read_passphrase("Enter same passphrase again: ", 1); + if (strcmp(passphrase1, passphrase2) != 0) + { + /* The passphrases do not match. Clear them and retry. */ + memset(passphrase1, 0, strlen(passphrase1)); + memset(passphrase2, 0, strlen(passphrase2)); + xfree(passphrase1); + xfree(passphrase2); + printf("Passphrases do not match. Try again.\n"); + goto passphrase_again; + } + /* Clear the other copy of the passphrase. */ + memset(passphrase2, 0, strlen(passphrase2)); + xfree(passphrase2); + } + + /* Create default commend field for the passphrase. The user can later + edit this field. */ + if (identity_comment) + { + strlcpy(buf2, identity_comment, sizeof(buf2)); + } + else + { + if (gethostname(hostname, sizeof(hostname)) < 0) + { + perror("gethostname"); + exit(1); + } + snprintf(buf2, sizeof buf2, "%s@%s", pw->pw_name, hostname); + } + + /* Save the key with the given passphrase and comment. */ + if (!save_private_key(buf, passphrase1, private_key, buf2)) + { + printf("Saving the key failed: %s: %s.\n", + buf, strerror(errno)); + memset(passphrase1, 0, strlen(passphrase1)); + xfree(passphrase1); + goto ask_file_again; + } + /* Clear the passphrase. */ + memset(passphrase1, 0, strlen(passphrase1)); + xfree(passphrase1); + + /* Clear the private key and the random number generator. */ + RSA_free(private_key); + arc4random_stir(); + + if (!quiet) + printf("Your identification has been saved in %s.\n", buf); + + /* Display the public key on the screen. */ + if (!quiet) { + printf("Your public key is:\n"); + printf("%d ", BN_num_bits(public_key->n)); + tmpbuf = BN_bn2dec(public_key->e); + printf("%s ", tmpbuf); + free(tmpbuf); + tmpbuf = BN_bn2dec(public_key->n); + printf("%s %s\n", tmpbuf, buf2); + free(tmpbuf); + } + + /* Save the public key in text format in a file with the same name but + .pub appended. */ + strcat(buf, ".pub"); + f = fopen(buf, "w"); + if (!f) + { + printf("Could not save your public key in %s\n", buf); + 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, buf2); + free(tmpbuf); + fclose(f); + + if (!quiet) + printf("Your public key has been saved in %s\n", buf); + + exit(0); +} -- cgit v1.2.3