- Merged OpenBSD CVS changes
   - [ChangeLog.Ylonen] noone needs this anymore
   - [authfd.c] close-on-exec for auth-socket, ok deraadt
   - [hostfile.c]
     in known_hosts key lookup the entry for the bits does not need
     to match, all the information is contained in n and e. This
     solves the problem with buggy servers announcing the wrong
     modulus length.  markus and me.
   - [serverloop.c]
     bugfix: check for space if child has terminated, from:
     iedowse@maths.tcd.ie
   - [ssh-add.1 ssh-add.c ssh-keygen.1 ssh-keygen.c sshconnect.c]
     [fingerprint.c fingerprint.h]
     rsa key fingerprints, idea from Bjoern Groenvall <bg@sics.se>
   - [ssh-agent.1] typo
   - [ssh.1] add OpenSSH information to AUTHOR section. okay markus@
   - [sshd.c]
     force logging to stderr while loading private key file
     (lost while converting to new log-levels)
diff --git a/ssh-keygen.c b/ssh-keygen.c
index 10289ca..deb08dd 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -14,11 +14,12 @@
 */
 
 #include "includes.h"
-RCSID("$Id: ssh-keygen.c,v 1.4 1999/11/15 06:10:57 damien Exp $");
+RCSID("$Id: ssh-keygen.c,v 1.5 1999/11/17 06:29:08 damien Exp $");
 
 #include "rsa.h"
 #include "ssh.h"
 #include "xmalloc.h"
+#include "fingerprint.h"
 
 #ifdef HAVE___PROGNAME
 extern char *__progname;
@@ -46,6 +47,9 @@
 
 int quiet = 0;
 
+/* Flag indicating that we just want to see the key fingerprint */
+int print_fingerprint = 0;
+
 /* This is set to the identity file name if given on the command line. */
 char *identity_file = NULL;
 
@@ -61,42 +65,101 @@
 /* Perform changing a passphrase.  The argument is the passwd structure
    for the current user. */
 
-void
-do_change_passphrase(struct passwd *pw)
+char *
+get_filename(struct passwd *pw, const char *prompt)
 {
-  char buf[1024], *comment;
-  char *old_passphrase, *passphrase1, *passphrase2;
-  struct stat st;
-  RSA *private_key;
+  char buf[1024], default_file[1024];
 
   /* Read key file name. */
   if (identity_file != NULL) {
-      strncpy(buf, identity_file, sizeof(buf));
-      buf[sizeof(buf) - 1] = '\0';
+    return xstrdup(identity_file);
   } else {
-    printf("Enter file in which the key is ($HOME/%s): ", SSH_CLIENT_IDENTITY);
+    snprintf(default_file, sizeof default_file, "%s/%s",
+             pw->pw_dir, SSH_CLIENT_IDENTITY);
+    printf("%s (%s): ", prompt, default_file);
     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);
+      return xstrdup(default_file);
   }
+  return xstrdup(buf);
+}
 
-  /* Check if the file exists. */
-  if (stat(buf, &st) < 0)
+void
+do_fingerprint(struct passwd *pw)
+{
+  char *file, *comment;
+  RSA *public_key;
+  struct stat st;
+
+  file = get_filename(pw, "Enter file in which the key is");
+  if (stat(file, &st) < 0)
     {
-      perror(buf);
+      perror(file);
+      exit(1);
+    }
+  public_key = RSA_new();
+  if (!load_public_key(file, public_key, &comment)) {
+    char *cp, line[1024];
+    BIGNUM *e, *n;
+    int dummy, invalid = 0;
+    FILE *f = fopen(file, "r");
+    n = BN_new();
+    e = BN_new();
+    if (f && fgets(line, sizeof(line), f)) {
+      cp = line;
+      line[strlen(line)-1] = '\0';
+      if (auth_rsa_read_key(&cp, &dummy, e, n)) {
+        public_key->e = e;
+        public_key->n = n;
+	comment = xstrdup(cp ? cp : "no comment");
+      } else {
+        invalid = 1;
+      } 
+    } else {
+      invalid = 1;
+    }
+    if (invalid) {
+      printf("%s is not a valid key file.\n", file);
+      BN_free(e);
+      BN_free(n);
+      exit(1);
+    }
+  }
+    
+  printf("%d %s %s\n", BN_num_bits(public_key->n),
+	 fingerprint(public_key->e, public_key->n),
+	 comment);
+  RSA_free(public_key);
+  exit(0);
+}
+
+
+void
+do_change_passphrase(struct passwd *pw)
+{
+  char *file, *comment;
+  char *old_passphrase, *passphrase1, *passphrase2;
+  struct stat st;
+  RSA *private_key;
+
+  file = get_filename(pw, "Enter file in which the key is");
+  /* Check if the file exists. */
+  if (stat(file, &st) < 0)
+    {
+      perror(file);
       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))
+  if (!load_public_key(file, public_key, NULL))
     {
-      printf("%s is not a valid key file.\n", buf);
+      printf("%s is not a valid key file.\n", file);
       exit(1);
     }
   /* Clear the public key since we are just about to load the whole file. */
@@ -104,14 +167,14 @@
 
   /* Try to load the file with empty passphrase. */
   private_key = RSA_new();
-  if (!load_private_key(buf, "", private_key, &comment)) {
+  if (!load_private_key(file, "", 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))
+    if (!load_private_key(file, old_passphrase, private_key, &comment))
       {
 	memset(old_passphrase, 0, strlen(old_passphrase));
 	xfree(old_passphrase);
@@ -152,10 +215,10 @@
     }
 
   /* Save the file using the new passphrase. */
-  if (!save_private_key(buf, passphrase1, private_key, comment))
+  if (!save_private_key(file, passphrase1, private_key, comment))
     {
       printf("Saving the key failed: %s: %s.\n",
-	     buf, strerror(errno));
+	     file, strerror(errno));
       memset(passphrase1, 0, strlen(passphrase1));
       xfree(passphrase1);
       RSA_free(private_key);
@@ -177,51 +240,33 @@
 void
 do_change_comment(struct passwd *pw)
 {
-  char buf[1024], new_comment[1024], *comment;
+  char new_comment[1024], *file, *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);
-    }
-
+  file = get_filename(pw, "Enter file in which the key is");
   /* Check if the file exists. */
-  if (stat(buf, &st) < 0)
+  if (stat(file, &st) < 0)
     {
-      perror(buf);
+      perror(file);
       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))
+  if (!load_public_key(file, public_key, NULL))
     {
-      printf("%s is not a valid key file.\n", buf);
+      printf("%s is not a valid key file.\n", file);
       exit(1);
     }
 
   private_key = RSA_new();
   /* Try to load the file with empty passphrase. */
-  if (load_private_key(buf, "", private_key, &comment))
+  if (load_private_key(file, "", private_key, &comment))
     passphrase = xstrdup("");
   else
     {
@@ -234,7 +279,7 @@
 	else
 	  passphrase = read_passphrase("Enter passphrase: ", 1);
       /* Try to load using the passphrase. */
-      if (!load_private_key(buf, passphrase, private_key, &comment))
+      if (!load_private_key(file, passphrase, private_key, &comment))
 	{
 	  memset(passphrase, 0, strlen(passphrase));
 	  xfree(passphrase);
@@ -246,8 +291,7 @@
 
   if (identity_comment)
     {
-      strncpy(new_comment, identity_comment, sizeof(new_comment));
-      new_comment[sizeof(new_comment) - 1] = '\0';
+      strlcpy(new_comment, identity_comment, sizeof(new_comment));
     }
   else
     {
@@ -266,10 +310,10 @@
     }
       
   /* Save the file using the new passphrase. */
-  if (!save_private_key(buf, passphrase, private_key, new_comment))
+  if (!save_private_key(file, passphrase, private_key, new_comment))
     {
       printf("Saving the key failed: %s: %s.\n",
-	     buf, strerror(errno));
+	     file, strerror(errno));
       memset(passphrase, 0, strlen(passphrase));
       xfree(passphrase);
       RSA_free(private_key);
@@ -284,11 +328,11 @@
 
   /* Save the public key in text format in a file with the same name but
      .pub appended. */
-  strcat(buf, ".pub");
-  f = fopen(buf, "w");
+  strcat(file, ".pub");
+  f = fopen(file, "w");
   if (!f)
     {
-      printf("Could not save your public key in %s\n", buf);
+      printf("Could not save your public key in %s\n", file);
       exit(1);
     }
   fprintf(f, "%d ", BN_num_bits(public_key->n));
@@ -313,7 +357,7 @@
 {
   char buf[16384], buf2[1024], *passphrase1, *passphrase2;
   struct passwd *pw;
-  char *tmpbuf;
+  char *file, *tmpbuf;
   int opt;
   struct stat st;
   FILE *f;
@@ -323,6 +367,7 @@
 
   /* 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",
@@ -345,7 +390,7 @@
       error("Could not create directory '%s'.", buf);
 
   /* Parse command line arguments. */
-  while ((opt = getopt(ac, av, "qpcb:f:P:N:C:")) != EOF)
+  while ((opt = getopt(ac, av, "qpclb:f:P:N:C:")) != EOF)
     {
       switch (opt)
 	{
@@ -358,6 +403,10 @@
 	    }
 	  break;
 
+	case 'l':
+	  print_fingerprint = 1;
+	  break;
+
 	case 'p':
 	  change_passphrase = 1;
 	  break;
@@ -404,6 +453,9 @@
       exit(1);
     }
 
+  if (print_fingerprint)
+    do_fingerprint(pw);
+
   /* If the user requested to change the passphrase, do it now.  This
      function never returns. */
   if (change_passphrase)
@@ -426,29 +478,12 @@
 
  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);
-    }
+  file = get_filename(pw, "Enter file in which to save the key");
 
   /* If the file aready exists, ask the user to confirm. */
-  if (stat(buf, &st) >= 0)
+  if (stat(file, &st) >= 0)
     {
-      printf("%s already exists.\n", buf);
+      printf("%s already exists.\n", file);
       printf("Overwrite (y/n)? ");
       fflush(stdout);
       if (fgets(buf2, sizeof(buf2), stdin) == NULL)
@@ -501,12 +536,13 @@
     }
 
   /* Save the key with the given passphrase and comment. */
-  if (!save_private_key(buf, passphrase1, private_key, buf2))
+  if (!save_private_key(file, passphrase1, private_key, buf2))
     {
       printf("Saving the key failed: %s: %s.\n",
-	     buf, strerror(errno));
+	     file, strerror(errno));
       memset(passphrase1, 0, strlen(passphrase1));
       xfree(passphrase1);
+      xfree(file);
       goto ask_file_again;
     }
   /* Clear the passphrase. */
@@ -518,7 +554,7 @@
   arc4random_stir();
 
   if (!quiet)
-    printf("Your identification has been saved in %s.\n", buf);
+    printf("Your identification has been saved in %s.\n", file);
 
   /* Display the public key on the screen. */
   if (!quiet) {
@@ -534,11 +570,11 @@
 
   /* Save the public key in text format in a file with the same name but
      .pub appended. */
-  strcat(buf, ".pub");
-  f = fopen(buf, "w");
+  strcat(file, ".pub");
+  f = fopen(file, "w");
   if (!f)
     {
-      printf("Could not save your public key in %s\n", buf);
+      printf("Could not save your public key in %s\n", file);
       exit(1);
     }
   fprintf(f, "%d ", BN_num_bits(public_key->n));
@@ -551,7 +587,7 @@
   fclose(f);
 
   if (!quiet)
-    printf("Your public key has been saved in %s\n", buf);
+    printf("Your public key has been saved in %s\n", file);
   
   exit(0);
 }