X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=cmdgen.c;h=4722a8ad7af73b271012d967f810bc0fd224463b;hb=15386cbe927fc85ac2fed0bb47704645c4b67dad;hp=7809c89186b489ecfa30f549e3888b62fb273774;hpb=90af5bed04f8e4dcf7e42c464b1be6cc7056f768;p=PuTTY.git diff --git a/cmdgen.c b/cmdgen.c index 7809c891..4722a8ad 100644 --- a/cmdgen.c +++ b/cmdgen.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include "putty.h" #include "ssh.h" @@ -91,7 +93,7 @@ static void no_progress(void *param, int action, int phase, int iprogress) { } -void modalfatalbox(char *p, ...) +void modalfatalbox(const char *p, ...) { va_list ap; fprintf(stderr, "FATAL ERROR: "); @@ -102,7 +104,7 @@ void modalfatalbox(char *p, ...) cleanup_exit(1); } -void nonfatal(char *p, ...) +void nonfatal(const char *p, ...) { va_list ap; fprintf(stderr, "ERROR: "); @@ -152,7 +154,8 @@ void help(void) showversion(); usage(FALSE); fprintf(stderr, - " -t specify key type when generating (rsa, dsa, rsa1)\n" + " -t specify key type when generating (ed25519, ecdsa, rsa, " + "dsa, rsa1)\n" " -b specify number of bits when generating key\n" " -C change or specify key comment\n" " -P change key passphrase\n" @@ -163,66 +166,20 @@ void help(void) " private-openssh-new export OpenSSH private key " "(force new file format)\n" " private-sshcom export ssh.com private key\n" - " public standard / ssh.com public key\n" + " public RFC 4716 / ssh.com public key\n" " public-openssh OpenSSH public key\n" " fingerprint output the key fingerprint\n" " -o specify output file\n" " -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" ); } -static int save_ssh2_pubkey(char *filename, char *comment, - void *v_pub_blob, int pub_len) -{ - unsigned char *pub_blob = (unsigned char *)v_pub_blob; - char *p; - int i, column; - FILE *fp; - - if (filename) { - fp = fopen(filename, "wb"); - if (!fp) - return 0; - } else - fp = stdout; - - fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n"); - - if (comment) { - fprintf(fp, "Comment: \""); - for (p = comment; *p; p++) { - if (*p == '\\' || *p == '\"') - fputc('\\', fp); - fputc(*p, fp); - } - fprintf(fp, "\"\n"); - } - - i = 0; - column = 0; - while (i < pub_len) { - char buf[5]; - int n = (pub_len - i < 3 ? pub_len - i : 3); - base64_encode_atom(pub_blob + i, n, buf); - i += n; - buf[4] = '\0'; - fputs(buf, fp); - if (++column >= 16) { - fputc('\n', fp); - column = 0; - } - } - if (column > 0) - fputc('\n', fp); - - fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n"); - if (filename) - fclose(fp); - return 1; -} - static int move(char *from, char *to) { int ret; @@ -242,25 +199,27 @@ static int move(char *from, char *to) return TRUE; } -static char *blobfp(char *alg, int bits, unsigned char *blob, int bloblen) +static char *readpassphrase(const char *filename) { - char buffer[128]; - unsigned char digest[16]; - struct MD5Context md5c; - int i; - - MD5Init(&md5c); - MD5Update(&md5c, blob, bloblen); - MD5Final(digest, &md5c); - - sprintf(buffer, "%s ", alg); - if (bits > 0) - sprintf(buffer + strlen(buffer), "%d ", bits); - for (i = 0; i < 16; i++) - sprintf(buffer + strlen(buffer), "%s%02x", i ? ":" : "", - digest[i]); + FILE *fp; + char *line; - return dupstr(buffer); + 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) @@ -283,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; @@ -355,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); @@ -583,28 +552,6 @@ int main(int argc, char **argv) intype = key_type(infilename); switch (intype) { - /* - * It would be nice here to be able to load _public_ - * key files, in any of a number of forms, and (a) - * convert them to other public key types, (b) print - * out their fingerprints. Or, I suppose, for real - * orthogonality, (c) change their comment! - * - * In fact this opens some interesting possibilities. - * Suppose ssh2_userkey_loadpub() were able to load - * public key files as well as extracting the public - * key from private ones. And suppose I did the thing - * I've been wanting to do, where specifying a - * particular private key file for authentication - * causes any _other_ key in the agent to be discarded. - * Then, if you had an agent forwarded to the machine - * you were running Unix PuTTY or Plink on, and you - * needed to specify which of the keys in the agent it - * should use, you could do that by supplying a - * _public_ key file, thus not needing to trust even - * your encrypted private key file to the network. Ooh! - */ - case SSH_KEYTYPE_UNOPENABLE: case SSH_KEYTYPE_UNKNOWN: fprintf(stderr, "puttygen: unable to load file `%s': %s\n", @@ -612,6 +559,7 @@ int main(int argc, char **argv) return 1; case SSH_KEYTYPE_SSH1: + case SSH_KEYTYPE_SSH1_PUBLIC: if (sshver == 2) { fprintf(stderr, "puttygen: conversion from SSH-1 to SSH-2 keys" " not supported\n"); @@ -621,6 +569,8 @@ int main(int argc, char **argv) break; case SSH_KEYTYPE_SSH2: + case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716: + case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH: case SSH_KEYTYPE_OPENSSH_PEM: case SSH_KEYTYPE_OPENSSH_NEW: case SSH_KEYTYPE_SSHCOM: @@ -692,6 +642,14 @@ int main(int argc, char **argv) else load_encrypted = FALSE; + if (load_encrypted && (intype == SSH_KEYTYPE_SSH1_PUBLIC || + intype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 || + intype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH)) { + fprintf(stderr, "puttygen: cannot perform this action on a " + "public-key-only input file\n"); + return 1; + } + /* ------------------------------------------------------------------ * Now we're ready to actually do some stuff. */ @@ -741,13 +699,7 @@ int main(int argc, char **argv) ec_generate(ec, bits, progressfn, &prog); ssh2key = snew(struct ssh2_userkey); ssh2key->data = ec; - if (bits == 256) { - ssh2key->alg = &ssh_ecdsa_nistp256; - } else if (bits == 384) { - ssh2key->alg = &ssh_ecdsa_nistp384; - } else { - ssh2key->alg = &ssh_ecdsa_nistp521; - } + ssh2key->alg = ec->signalg; ssh1key = NULL; } else if (keytype == ED25519) { struct ec_key *ec = snew(struct ec_key); @@ -795,29 +747,32 @@ 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) { int ret; case SSH_KEYTYPE_SSH1: + case SSH_KEYTYPE_SSH1_PUBLIC: ssh1key = snew(struct RSAKey); if (!load_encrypted) { void *vblob; @@ -849,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; @@ -858,18 +813,24 @@ int main(int argc, char **argv) break; case SSH_KEYTYPE_SSH2: + case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716: + case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH: if (!load_encrypted) { ssh2blob = ssh2_userkey_loadpub(infilename, &ssh2alg, - &ssh2bloblen, NULL, &error); + &ssh2bloblen, &origcomment, + &error); if (ssh2blob) { ssh2algf = find_pubkey_alg(ssh2alg); if (ssh2algf) - bits = ssh2algf->pubkey_bits(ssh2blob, ssh2bloblen); + bits = ssh2algf->pubkey_bits(ssh2algf, + ssh2blob, ssh2bloblen); else bits = -1; } + 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; @@ -884,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; @@ -920,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; @@ -944,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. @@ -976,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; @@ -997,80 +961,33 @@ int main(int argc, char **argv) case PUBLIC: case PUBLICO: - if (sshver == 1) { - FILE *fp; - char *dec1, *dec2; + { + FILE *fp; - assert(ssh1key); + if (outfile) + fp = f_open(outfilename, "w", FALSE); + else + fp = stdout; - if (outfile) - fp = f_open(outfilename, "w", FALSE); - else - fp = stdout; - dec1 = bignum_decimal(ssh1key->exponent); - dec2 = bignum_decimal(ssh1key->modulus); - fprintf(fp, "%d %s %s %s\n", bignum_bitcount(ssh1key->modulus), - dec1, dec2, ssh1key->comment); - sfree(dec1); - sfree(dec2); - if (outfile) - fclose(fp); - } else if (outtype == PUBLIC) { - if (!ssh2blob) { - assert(ssh2key); - ssh2blob = ssh2key->alg->public_blob(ssh2key->data, - &ssh2bloblen); - } - save_ssh2_pubkey(outfile, ssh2key ? ssh2key->comment : origcomment, - ssh2blob, ssh2bloblen); - } else if (outtype == PUBLICO) { - char *buffer, *p; - int i; - FILE *fp; + if (sshver == 1) { + ssh1_write_pubkey(fp, ssh1key); + } else { + if (!ssh2blob) { + assert(ssh2key); + ssh2blob = ssh2key->alg->public_blob(ssh2key->data, + &ssh2bloblen); + } - if (!ssh2blob) { - assert(ssh2key); - ssh2blob = ssh2key->alg->public_blob(ssh2key->data, - &ssh2bloblen); - } - if (!ssh2alg) { - assert(ssh2key); - ssh2alg = ssh2key->alg->name; - } - if (ssh2key) - comment = ssh2key->comment; - else - comment = origcomment; - - buffer = snewn(strlen(ssh2alg) + - 4 * ((ssh2bloblen+2) / 3) + - strlen(comment) + 3, char); - strcpy(buffer, ssh2alg); - p = buffer + strlen(buffer); - *p++ = ' '; - i = 0; - while (i < ssh2bloblen) { - int n = (ssh2bloblen - i < 3 ? ssh2bloblen - i : 3); - base64_encode_atom(ssh2blob + i, n, p); - i += n; - p += 4; - } - if (*comment) { - *p++ = ' '; - strcpy(p, comment); - } else - *p++ = '\0'; + ssh2_write_pubkey(fp, ssh2key ? ssh2key->comment : origcomment, + ssh2blob, ssh2bloblen, + (outtype == PUBLIC ? + SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 : + SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH)); + } - if (outfile) - fp = f_open(outfilename, "w", FALSE); - else - fp = stdout; - fprintf(fp, "%s\n", buffer); if (outfile) fclose(fp); - - sfree(buffer); - } + } break; case FP: @@ -1084,10 +1001,11 @@ int main(int argc, char **argv) rsa_fingerprint(fingerprint, 128, ssh1key); } else { if (ssh2key) { - fingerprint = ssh2key->alg->fingerprint(ssh2key->data); + fingerprint = ssh2_fingerprint(ssh2key->alg, + ssh2key->data); } else { assert(ssh2blob); - fingerprint = blobfp(ssh2alg, bits, ssh2blob, ssh2bloblen); + fingerprint = ssh2_fingerprint_blob(ssh2blob, ssh2bloblen); } } @@ -1123,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; @@ -1135,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)