/* 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/09/26 20:53:37 deraadt Exp $"); #ifndef HAVE_GETHOSTNAME #include #endif #include "randoms.h" #include "rsa.h" #include "ssh.h" #include "xmalloc.h" /* Generated private key. */ RSAPrivateKey private_key; /* Generated public key. */ RSAPublicKey public_key; /* Random number generator state. */ RandomState state; /* 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; /* 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; RSAPrivateKey private_key; char *old_passphrase, *passphrase1, *passphrase2; struct stat st; /* 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) sprintf(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. */ 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_clear_public_key(&public_key); /* Try to load the file with empty passphrase. */ 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, &state)) { printf("Saving the key failed: %s: %s.\n", buf, strerror(errno)); memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); rsa_clear_private_key(&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_clear_private_key(&private_key); 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; RSAPrivateKey private_key; char *passphrase; struct stat st; FILE *f; /* 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) sprintf(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. */ if (!load_public_key(buf, &public_key, NULL)) { printf("%s is not a valid key file.\n", buf); exit(1); } /* 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_clear_private_key(&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, &state)) { printf("Saving the key failed: %s: %s.\n", buf, strerror(errno)); memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); rsa_clear_private_key(&private_key); xfree(comment); exit(1); } /* Destroy the passphrase and the private key in memory. */ memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); rsa_clear_private_key(&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 ", public_key.bits); mpz_out_str(f, 10, &public_key.e); fprintf(f, " "); mpz_out_str(f, 10, &public_key.n); fprintf(f, " %s\n", new_comment); 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; int opt; struct stat st; FILE *f; #ifdef HAVE_GETHOSTNAME char hostname[257]; #else struct utsname uts; #endif extern int optind; extern char *optarg; /* 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. */ sprintf(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, "pcb: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 '?': 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); /* Initialize random number generator. This may take a while if the user has no seed file, so display a message to the user. */ printf("Initializing random number generator...\n"); sprintf(buf, "%s/%s", pw->pw_dir, SSH_CLIENT_SEEDFILE); random_initialize(&state, buf); /* Save random seed so we don\'t need to do all that time-consuming environmental noise collection the next time. */ random_save(&state, buf); /* Generate the rsa key pair. */ rsa_generate_key(&private_key, &public_key, &state, bits); /* Save the state again, just to remove any fear that the previous state could be used to recreate the key. (That should not be possible anyway since the pool is stirred after save and some noise is added.) */ random_save(&state, buf); 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) sprintf(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) { strncpy(buf2, identity_comment, sizeof(buf2)); buf2[sizeof(buf2) - 1] = '\0'; } else { #ifdef HAVE_GETHOSTNAME if (gethostname(hostname, sizeof(hostname)) < 0) { perror("gethostname"); exit(1); } sprintf(buf2, "%s@%s", pw->pw_name, hostname); #else if (uname(&uts) < 0) { perror("uname"); exit(1); } sprintf(buf2, "%s@%s", pw->pw_name, uts.nodename); #endif } /* Save the key with the given passphrase and comment. */ if (!save_private_key(buf, passphrase1, &private_key, buf2, &state)) { 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_clear_private_key(&private_key); random_clear(&state); printf("Your identification has been saved in %s.\n", buf); /* Display the public key on the screen. */ printf("Your public key is:\n"); printf("%d ", public_key.bits); mpz_out_str(stdout, 10, &public_key.e); printf(" "); mpz_out_str(stdout, 10, &public_key.n); printf(" %s\n", buf2); /* 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 ", public_key.bits); mpz_out_str(f, 10, &public_key.e); fprintf(f, " "); mpz_out_str(f, 10, &public_key.n); fprintf(f, " %s\n", buf2); fclose(f); printf("Your public key has been saved in %s\n", buf); exit(0); }