]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Add command-line passphrase-file options to command-line PuTTYgen.
authorSimon Tatham <anakin@pobox.com>
Thu, 17 Mar 2016 18:42:46 +0000 (18:42 +0000)
committerSimon Tatham <anakin@pobox.com>
Thu, 17 Mar 2016 18:55:57 +0000 (18:55 +0000)
Patch due to Colin Watson.

Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).

Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as

  puttygen --new-passphrase <(echo -n "my passphrase") [options]

Failing that, try a secure file-wipe utility, as the man page change
mentions.

(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)

cmdgen.c
doc/man-pg.but

index 12e1ac9e19e31822e5d16f8095e982c21f37990b..4722a8ad7af73b271012d967f810bc0fd224463b 100644 (file)
--- a/cmdgen.c
+++ b/cmdgen.c
@@ -10,6 +10,8 @@
 #include <limits.h>
 #include <assert.h>
 #include <time.h>
+#include <errno.h>
+#include <string.h>
 
 #include "putty.h"
 #include "ssh.h"
@@ -171,6 +173,10 @@ void help(void)
            "  -l    equivalent to `-O fingerprint'\n"
            "  -L    equivalent to `-O public-openssh'\n"
            "  -p    equivalent to `-O public'\n"
+           "  --old-passphrase file\n"
+           "        specify file containing old key passphrase\n"
+           "  --new-passphrase file\n"
+           "        specify file containing new key passphrase\n"
            );
 }
 
@@ -193,6 +199,29 @@ static int move(char *from, char *to)
     return TRUE;
 }
 
+static char *readpassphrase(const char *filename)
+{
+    FILE *fp;
+    char *line;
+
+    fp = fopen(filename, "r");
+    if (!fp) {
+       fprintf(stderr, "puttygen: cannot open %s: %s\n",
+               filename, strerror(errno));
+       return NULL;
+    }
+    line = fgetline(fp);
+    if (line)
+       line[strcspn(line, "\r\n")] = '\0';
+    else if (ferror(fp))
+       fprintf(stderr, "puttygen: error reading from %s: %s\n",
+               filename, strerror(errno));
+    else       /* empty file */
+       line = dupstr("");
+    fclose(fp);
+    return line;
+}
+
 int main(int argc, char **argv)
 {
     char *infile = NULL;
@@ -213,7 +242,7 @@ int main(int argc, char **argv)
     char *ssh2alg = NULL;
     const struct ssh_signkey *ssh2algf = NULL;
     int ssh2bloblen;
-    char *passphrase = NULL;
+    char *old_passphrase = NULL, *new_passphrase = NULL;
     int load_encrypted;
     progfn_t progressfn = is_interactive() ? progress_update : no_progress;
 
@@ -285,21 +314,31 @@ int main(int argc, char **argv)
                                 pgp_fingerprints();
                                 nogo = TRUE;
                             }
-                        }
-                       /*
-                        * For long options requiring an argument, add
-                        * code along the lines of
-                        * 
-                        * else if (!strcmp(opt, "-output")) {
-                        *     if (!val) {
-                        *         errs = TRUE;
-                         *         fprintf(stderr, "puttygen: option `-%s'"
-                         *                 " expects an argument\n", opt);
-                        *     } else
-                        *         ofile = val;
-                        * }
-                        */
-                       else {
+                       } else if (!strcmp(opt, "-old-passphrase")) {
+                           if (!val && argc > 1)
+                               --argc, val = *++argv;
+                           if (!val) {
+                               errs = TRUE;
+                               fprintf(stderr, "puttygen: option `-%s'"
+                                       " expects an argument\n", opt);
+                           } else {
+                               old_passphrase = readpassphrase(val);
+                               if (!old_passphrase)
+                                   errs = TRUE;
+                           }
+                       } else if (!strcmp(opt, "-new-passphrase")) {
+                           if (!val && argc > 1)
+                               --argc, val = *++argv;
+                           if (!val) {
+                               errs = TRUE;
+                               fprintf(stderr, "puttygen: option `-%s'"
+                                       " expects an argument\n", opt);
+                           } else {
+                               new_passphrase = readpassphrase(val);
+                               if (!new_passphrase)
+                                   errs = TRUE;
+                           }
+                       } else {
                            errs = TRUE;
                            fprintf(stderr,
                                    "puttygen: no such option `-%s'\n", opt);
@@ -708,23 +747,25 @@ int main(int argc, char **argv)
         * If so, ask for a passphrase.
         */
        if (encrypted && load_encrypted) {
-           prompts_t *p = new_prompts(NULL);
-           int ret;
-           p->to_server = FALSE;
-           p->name = dupstr("SSH key passphrase");
-           add_prompt(p, dupstr("Enter passphrase to load key: "), FALSE);
-           ret = console_get_userpass_input(p, NULL, 0);
-           assert(ret >= 0);
-           if (!ret) {
-               free_prompts(p);
-               perror("puttygen: unable to read passphrase");
-               return 1;
-           } else {
-               passphrase = dupstr(p->prompts[0]->result);
-               free_prompts(p);
+           if (!old_passphrase) {
+               prompts_t *p = new_prompts(NULL);
+               int ret;
+               p->to_server = FALSE;
+               p->name = dupstr("SSH key passphrase");
+               add_prompt(p, dupstr("Enter passphrase to load key: "), FALSE);
+               ret = console_get_userpass_input(p, NULL, 0);
+               assert(ret >= 0);
+               if (!ret) {
+                   free_prompts(p);
+                   perror("puttygen: unable to read passphrase");
+                   return 1;
+               } else {
+                   old_passphrase = dupstr(p->prompts[0]->result);
+                   free_prompts(p);
+               }
            }
        } else {
-           passphrase = NULL;
+           old_passphrase = NULL;
        }
 
        switch (intype) {
@@ -763,7 +804,7 @@ int main(int argc, char **argv)
                ssh1key->q = NULL;
                ssh1key->iqmp = NULL;
            } else {
-               ret = loadrsakey(infilename, ssh1key, passphrase, &error);
+               ret = loadrsakey(infilename, ssh1key, old_passphrase, &error);
            }
            if (ret > 0)
                error = NULL;
@@ -788,7 +829,8 @@ int main(int argc, char **argv)
                 }
                 sfree(ssh2alg);
            } else {
-               ssh2key = ssh2_load_userkey(infilename, passphrase, &error);
+               ssh2key = ssh2_load_userkey(infilename, old_passphrase,
+                                           &error);
            }
            if ((ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) || ssh2blob)
                error = NULL;
@@ -803,7 +845,7 @@ int main(int argc, char **argv)
          case SSH_KEYTYPE_OPENSSH_PEM:
          case SSH_KEYTYPE_OPENSSH_NEW:
          case SSH_KEYTYPE_SSHCOM:
-           ssh2key = import_ssh2(infilename, intype, passphrase, &error);
+           ssh2key = import_ssh2(infilename, intype, old_passphrase, &error);
            if (ssh2key) {
                if (ssh2key != SSH2_WRONG_PASSPHRASE)
                    error = NULL;
@@ -839,11 +881,18 @@ int main(int argc, char **argv)
        }
     }
 
+    /*
+     * Unless we're changing the passphrase, the old one (if any) is a
+     * reasonable default.
+     */
+    if (!change_passphrase && old_passphrase && !new_passphrase)
+       new_passphrase = dupstr(old_passphrase);
+
     /*
      * Prompt for a new passphrase if we have been asked to, or if
      * we have just generated a key.
      */
-    if (change_passphrase || keytype != NOKEYGEN) {
+    if (!new_passphrase && (change_passphrase || keytype != NOKEYGEN)) {
        prompts_t *p = new_prompts(NULL);
        int ret;
 
@@ -863,18 +912,14 @@ int main(int argc, char **argv)
                fprintf(stderr, "puttygen: passphrases do not match\n");
                return 1;
            }
-           if (passphrase) {
-               smemclr(passphrase, strlen(passphrase));
-               sfree(passphrase);
-           }
-           passphrase = dupstr(p->prompts[0]->result);
+           new_passphrase = dupstr(p->prompts[0]->result);
            free_prompts(p);
-           if (!*passphrase) {
-               sfree(passphrase);
-               passphrase = NULL;
-           }
        }
     }
+    if (new_passphrase && !*new_passphrase) {
+       sfree(new_passphrase);
+       new_passphrase = NULL;
+    }
 
     /*
      * Write output.
@@ -895,14 +940,14 @@ int main(int argc, char **argv)
       case PRIVATE:
        if (sshver == 1) {
            assert(ssh1key);
-           ret = saversakey(outfilename, ssh1key, passphrase);
+           ret = saversakey(outfilename, ssh1key, new_passphrase);
            if (!ret) {
                fprintf(stderr, "puttygen: unable to save SSH-1 private key\n");
                return 1;
            }
        } else {
            assert(ssh2key);
-           ret = ssh2_save_userkey(outfilename, ssh2key, passphrase);
+           ret = ssh2_save_userkey(outfilename, ssh2key, new_passphrase);
            if (!ret) {
                fprintf(stderr, "puttygen: unable to save SSH-2 private key\n");
                return 1;
@@ -996,7 +1041,7 @@ int main(int argc, char **argv)
           default:
             assert(0 && "control flow goof");
         }
-       ret = export_ssh2(outfilename, real_outtype, ssh2key, passphrase);
+       ret = export_ssh2(outfilename, real_outtype, ssh2key, new_passphrase);
        if (!ret) {
            fprintf(stderr, "puttygen: unable to export key\n");
            return 1;
@@ -1008,9 +1053,13 @@ int main(int argc, char **argv)
        break;
     }
 
-    if (passphrase) {
-       smemclr(passphrase, strlen(passphrase));
-       sfree(passphrase);
+    if (old_passphrase) {
+       smemclr(old_passphrase, strlen(old_passphrase));
+       sfree(old_passphrase);
+    }
+    if (new_passphrase) {
+       smemclr(new_passphrase, strlen(new_passphrase));
+       sfree(new_passphrase);
     }
 
     if (ssh1key)
index 6ee37c9932ac80b491d87296a03146344adc5774..b6e4ef2c11bc1ea560a24491454b12d60998ae5e 100644 (file)
@@ -64,6 +64,13 @@ and \c{rsa1} (to generate SSH-1 keys).
 
 \dd Suppress the progress display when generating a new key.
 
+\dt \cw{\-\-old\-passphrase} \e{file}
+
+\dd Specify a file name; the first line will be read from this file
+(removing any trailing newline) and used as the old passphrase.
+\s{CAUTION:} If the passphrase is important, the file should be stored
+on a temporary filesystem or else securely erased after use.
+
 In the second phase, \c{puttygen} optionally alters properties of
 the key it has loaded or generated. The options to control this are:
 
@@ -156,6 +163,14 @@ fingerprint. Otherwise, the \c{\-o} option is required.
 
 \dd Synonym for \q{\cw{-O public}}.
 
+\dt \cw{\-\-new\-passphrase} \e{file}
+
+\dd Specify a file name; the first line will be read from this file
+(removing any trailing newline) and used as the new passphrase. If the
+file is empty then the saved key will be unencrypted. \s{CAUTION:} If
+the passphrase is important, the file should be stored on a temporary
+filesystem or else securely erased after use.
+
 The following options do not run PuTTYgen as normal, but print
 informational messages and then quit:
 
@@ -210,8 +225,3 @@ To add the OpenSSH-format public half of a key to your authorised
 keys file:
 
 \c puttygen -L mykey.ppk >> $HOME/.ssh/authorized_keys
-
-\S{puttygen-manpage-bugs} BUGS
-
-There's currently no way to supply passphrases in batch mode, or
-even just to specify that you don't want a passphrase at all.