1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Add command-line passphrase-file options to command-line PuTTYgen.

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.)
This commit is contained in:
Simon Tatham 2016-03-17 18:42:46 +00:00
parent 5c5879b99d
commit 0fadffe0cb
2 changed files with 114 additions and 55 deletions

149
cmdgen.c
View File

@ -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)

View 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.