From 7599a57a33ab8bc3ad94ccc175732fbf05eef8a6 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 2 Feb 2020 11:41:06 +0000 Subject: [PATCH] Allow cmdgen to read keys from standard input. This reworks the cmdgen main program so that it loads the input file into a LoadedFile right at the start, and then every time it needs to do something with the contents, it calls one of the API functions taking a BinarySource instead of one taking a Filename. The usefulness of this is that now we can read from things that aren't regular files, and can't be rewound or reopened. In particular, the filename "-" is now taken (per the usual convention) to mean standard input. So now you can pipe a public or private key file into cmdgen's standard input and have it do something useful. For example, I was recently experimenting with the SFTP-only SSH server that comes with 'proftpd', which keeps its authorized_keys file in RFC 4716 format instead of the OpenSSH one-liner format, and I found I wanted to do grep 'my-key-comment' ~/.ssh/authorized_keys | puttygen -p - to quickly get hold of my existing public key to put in that file. But I had to go via a temporary file to make that work, because puttygen couldn't read from standard input. Next time, it will be able to! --- cmdgen.c | 44 +++++++++++++++++++++++++++++++++----------- ssh.h | 1 + sshpubk.c | 4 +--- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/cmdgen.c b/cmdgen.c index ba1cffc6..d83c980d 100644 --- a/cmdgen.c +++ b/cmdgen.c @@ -215,6 +215,8 @@ int main(int argc, char **argv) { char *infile = NULL; Filename *infilename = NULL, *outfilename = NULL; + LoadedFile *infile_lf = NULL; + BinarySource *infile_bs = NULL; enum { NOKEYGEN, RSA1, RSA2, DSA, ECDSA, ED25519 } keytype = NOKEYGEN; char *outfile = NULL, *outfiletmp = NULL; enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH_AUTO, @@ -256,7 +258,7 @@ int main(int argc, char **argv) */ while (--argc) { char *p = *++argv; - if (*p == '-') { + if (p[0] == '-' && p[1]) { /* * An option. */ @@ -562,9 +564,23 @@ int main(int argc, char **argv) * course of action. */ if (infile) { - infilename = filename_from_str(infile); + const char *load_error; - intype = key_type(infilename); + infilename = filename_from_str(infile); + if (!strcmp(infile, "-")) + infile_lf = lf_load_keyfile_fp(stdin, &load_error); + else + infile_lf = lf_load_keyfile(infilename, &load_error); + + if (!infile_lf) { + fprintf(stderr, "puttygen: unable to load file `%s': %s\n", + infile, load_error); + RETURN(1); + } + + infile_bs = BinarySource_UPCAST(infile_lf); + intype = key_type_s(infile_bs); + BinarySource_REWIND(infile_bs); switch (intype) { case SSH_KEYTYPE_UNOPENABLE: @@ -751,11 +767,13 @@ int main(int argc, char **argv) * Find out whether the input key is encrypted. */ if (intype == SSH_KEYTYPE_SSH1) - encrypted = rsa1_encrypted_f(infilename, &origcomment); + encrypted = rsa1_encrypted_s(infile_bs, &origcomment); else if (intype == SSH_KEYTYPE_SSH2) - encrypted = ppk_encrypted_f(infilename, &origcomment); + encrypted = ppk_encrypted_s(infile_bs, &origcomment); else - encrypted = import_encrypted(infilename, intype, &origcomment); + encrypted = import_encrypted_s(infilename, infile_bs, + intype, &origcomment); + BinarySource_REWIND(infile_bs); /* * If so, ask for a passphrase. @@ -799,7 +817,7 @@ int main(int argc, char **argv) blob = strbuf_new(); - ret = rsa1_loadpub_f(infilename, BinarySink_UPCAST(blob), + ret = rsa1_loadpub_s(infile_bs, BinarySink_UPCAST(blob), &origcomment, &error); BinarySource_BARE_INIT(src, blob->u, blob->len); get_rsa_ssh1_pub(src, ssh1key, RSA_SSH1_EXPONENT_FIRST); @@ -811,8 +829,9 @@ int main(int argc, char **argv) ssh1key->q = NULL; ssh1key->iqmp = NULL; } else { - ret = rsa1_load_f(infilename, ssh1key, old_passphrase, &error); + ret = rsa1_load_s(infile_bs, ssh1key, old_passphrase, &error); } + BinarySource_REWIND(infile_bs); if (ret > 0) error = NULL; else if (!error) @@ -826,7 +845,7 @@ int main(int argc, char **argv) sfree(origcomment); origcomment = NULL; ssh2blob = strbuf_new(); - if (ppk_loadpub_f(infilename, &ssh2alg, + if (ppk_loadpub_s(infile_bs, &ssh2alg, BinarySink_UPCAST(ssh2blob), &origcomment, &error)) { const ssh_keyalg *alg = find_pubkey_alg(ssh2alg); @@ -841,8 +860,9 @@ int main(int argc, char **argv) } sfree(ssh2alg); } else { - ssh2key = ppk_load_f(infilename, old_passphrase, &error); + ssh2key = ppk_load_s(infile_bs, old_passphrase, &error); } + BinarySource_REWIND(infile_bs); if ((ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) || ssh2blob) error = NULL; else if (!error) { @@ -856,7 +876,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, old_passphrase, &error); + ssh2key = import_ssh2_s(infile_bs, intype, old_passphrase, &error); if (ssh2key) { if (ssh2key != SSH2_WRONG_PASSPHRASE) error = NULL; @@ -1105,6 +1125,8 @@ int main(int argc, char **argv) sfree(origcomment); if (infilename) filename_free(infilename); + if (infile_lf) + lf_free(infile_lf); if (outfilename) filename_free(outfilename); sfree(outfiletmp); diff --git a/ssh.h b/ssh.h index d3b946c0..70ec64de 100644 --- a/ssh.h +++ b/ssh.h @@ -1260,6 +1260,7 @@ void ssh2_write_pubkey(FILE *fp, const char *comment, char *ssh2_fingerprint_blob(ptrlen); char *ssh2_fingerprint(ssh_key *key); int key_type(const Filename *filename); +int key_type_s(BinarySource *src); const char *key_type_to_str(int type); bool import_possible(int type); diff --git a/sshpubk.c b/sshpubk.c index 4a0e6729..a06d0c01 100644 --- a/sshpubk.c +++ b/sshpubk.c @@ -146,8 +146,6 @@ LoadedFile *lf_load_keyfile_fp(FILE *fp, const char **errptr) return lf; } -static int key_type_s(BinarySource *src); - static bool expect_signature(BinarySource *src, ptrlen realsig) { ptrlen thissig = get_data(src, realsig.len); @@ -1678,7 +1676,7 @@ static int key_type_s_internal(BinarySource *src) return SSH_KEYTYPE_UNKNOWN; /* unrecognised or EOF */ } -static int key_type_s(BinarySource *src) +int key_type_s(BinarySource *src) { int toret = key_type_s_internal(src); BinarySource_REWIND(src);