X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/aca589d972c5c12706d57322e89f9dec2b2a3a00..HEAD:/cmdgen.c diff --git a/cmdgen.c b/cmdgen.c index 5042aa4e..69c0177c 100644 --- a/cmdgen.c +++ b/cmdgen.c @@ -25,9 +25,10 @@ * order to avoid depleting the test system's /dev/random * unnecessarily. * - * - Calls to console_get_line() are replaced with the diagnostic - * function below, so that I can run tests in an automated - * manner and provide their interactive passphrase inputs. + * - Calls to console_get_userpass_input() are replaced with the + * diagnostic function below, so that I can run tests in an + * automated manner and provide their interactive passphrase + * inputs. * * - main() is renamed to cmdgen_main(); at the bottom of the file * I define another main() which calls the former repeatedly to @@ -40,19 +41,23 @@ char *get_random_data(int len) memset(buf, 'x', len); return buf; } -#define console_get_line console_get_line_diagnostic +#define console_get_userpass_input console_get_userpass_input_diagnostic int nprompts, promptsgot; const char *prompts[3]; -int console_get_line(const char *prompt, char *str, int maxlen, int is_pw) +int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { - if (promptsgot < nprompts) { - assert(strlen(prompts[promptsgot]) < maxlen); - strcpy(str, prompts[promptsgot++]); - return TRUE; - } else { - promptsgot++; /* track number of requests anyway */ - return FALSE; + size_t i; + int ret = 1; + for (i = 0; i < p->n_prompts; i++) { + if (promptsgot < nprompts) { + assert(strlen(prompts[promptsgot]) < p->prompts[i]->result_len); + strcpy(p->prompts[i]->result, prompts[promptsgot++]); + } else { + promptsgot++; /* track number of requests anyway */ + ret = 0; + } } + return ret; } #define main cmdgen_main #endif @@ -97,6 +102,16 @@ void modalfatalbox(char *p, ...) cleanup_exit(1); } +void nonfatal(char *p, ...) +{ + va_list ap; + fprintf(stderr, "ERROR: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); +} + /* * Stubs to let everything else link sensibly. */ @@ -113,19 +128,19 @@ void sk_cleanup(void) void showversion(void) { - char *verstr = dupstr(ver); - verstr[0] = tolower(verstr[0]); - printf("PuTTYgen %s\n", verstr); - sfree(verstr); + printf("puttygen: %s\n", ver); } -void usage(void) +void usage(int standalone) { fprintf(stderr, "Usage: puttygen ( keyfile | -t type [ -b bits ] )\n" - " [ -C comment ] [ -P ]\n" + " [ -C comment ] [ -P ] [ -q ]\n" " [ -o output-keyfile ] [ -O type | -l | -L" " | -p ]\n"); + if (standalone) + fprintf(stderr, + "Use \"puttygen --help\" for more detail.\n"); } void help(void) @@ -135,12 +150,13 @@ void help(void) * start with that, plus a version heading. */ showversion(); - usage(); + usage(FALSE); fprintf(stderr, " -t specify key type when generating (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" + " -q quiet: do not display progress bar\n" " -O specify output type:\n" " private output PuTTY private key format\n" " private-openssh export OpenSSH private key\n" @@ -224,7 +240,7 @@ static int move(char *from, char *to) return TRUE; } -static char *blobfp(char *alg, int bits, char *blob, int bloblen) +static char *blobfp(char *alg, int bits, unsigned char *blob, int bloblen) { char buffer[128]; unsigned char digest[16]; @@ -248,12 +264,11 @@ static char *blobfp(char *alg, int bits, char *blob, int bloblen) int main(int argc, char **argv) { char *infile = NULL; - Filename infilename; + Filename *infilename = NULL, *outfilename = NULL; enum { NOKEYGEN, RSA1, RSA2, DSA } keytype = NOKEYGEN; char *outfile = NULL, *outfiletmp = NULL; - Filename outfilename; enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH, SSHCOM } outtype = PRIVATE; - int bits = 1024; + int bits = 2048; char *comment = NULL, *origcomment = NULL; int change_passphrase = FALSE; int errs = FALSE, nogo = FALSE; @@ -261,7 +276,8 @@ int main(int argc, char **argv) int sshver = 0; struct ssh2_userkey *ssh2key = NULL; struct RSAKey *ssh1key = NULL; - char *ssh2blob = NULL, *ssh2alg = NULL; + unsigned char *ssh2blob = NULL; + char *ssh2alg = NULL; const struct ssh_signkey *ssh2algf = NULL; int ssh2bloblen; char *passphrase = NULL; @@ -277,7 +293,7 @@ int main(int argc, char **argv) * return success. */ if (argc <= 1) { - usage(); + usage(TRUE); return 0; } @@ -306,28 +322,54 @@ int main(int argc, char **argv) *p++ = '\0'; val = p; } else - val = NULL; + val = NULL; + if (!strcmp(opt, "-help")) { - help(); - nogo = TRUE; + if (val) { + errs = TRUE; + fprintf(stderr, "puttygen: option `-%s'" + " expects no argument\n", opt); + } else { + help(); + nogo = TRUE; + } } else if (!strcmp(opt, "-version")) { - showversion(); - nogo = TRUE; - } + if (val) { + errs = TRUE; + fprintf(stderr, "puttygen: option `-%s'" + " expects no argument\n", opt); + } else { + showversion(); + nogo = TRUE; + } + } else if (!strcmp(opt, "-pgpfp")) { + if (val) { + errs = TRUE; + fprintf(stderr, "puttygen: option `-%s'" + " expects no argument\n", opt); + } else { + /* support --pgpfp for consistency */ + pgp_fingerprints(); + nogo = TRUE; + } + } /* - * A sample option requiring an argument: + * For long options requiring an argument, add + * code along the lines of * * else if (!strcmp(opt, "-output")) { - * if (!val) - * errs = TRUE, error(err_optnoarg, opt); - * else + * if (!val) { + * errs = TRUE; + * fprintf(stderr, "puttygen: option `-%s'" + * " expects an argument\n", opt); + * } else * ofile = val; * } */ else { errs = TRUE; fprintf(stderr, - "puttygen: no such option `--%s'\n", opt); + "puttygen: no such option `-%s'\n", opt); } } p = NULL; @@ -466,7 +508,7 @@ int main(int argc, char **argv) * ones, print the usage message and return failure. */ if (!infile && keytype == NOKEYGEN) { - usage(); + usage(TRUE); return 1; } @@ -478,11 +520,21 @@ int main(int argc, char **argv) * Bomb out if we've been asked to both load and generate a * key. */ - if (keytype != NOKEYGEN && intype) { + if (keytype != NOKEYGEN && infile) { fprintf(stderr, "puttygen: cannot both load and generate a key\n"); return 1; } + /* + * We must save the private part when generating a new key. + */ + if (keytype != NOKEYGEN && + (outtype != PRIVATE && outtype != OPENSSH && outtype != SSHCOM)) { + fprintf(stderr, "puttygen: this would generate a new key but " + "discard the private part\n"); + return 1; + } + /* * Analyse the type of the input file, in case this affects our * course of action. @@ -490,7 +542,7 @@ int main(int argc, char **argv) if (infile) { infilename = filename_from_str(infile); - intype = key_type(&infilename); + intype = key_type(infilename); switch (intype) { /* @@ -523,7 +575,7 @@ int main(int argc, char **argv) case SSH_KEYTYPE_SSH1: if (sshver == 2) { - fprintf(stderr, "puttygen: conversion from SSH1 to SSH2 keys" + fprintf(stderr, "puttygen: conversion from SSH-1 to SSH-2 keys" " not supported\n"); return 1; } @@ -534,7 +586,7 @@ int main(int argc, char **argv) case SSH_KEYTYPE_OPENSSH: case SSH_KEYTYPE_SSHCOM: if (sshver == 1) { - fprintf(stderr, "puttygen: conversion from SSH2 to SSH1 keys" + fprintf(stderr, "puttygen: conversion from SSH-2 to SSH-1 keys" " not supported\n"); return 1; } @@ -616,8 +668,13 @@ int main(int argc, char **argv) random_ref(); entropy = get_random_data(bits / 8); + if (!entropy) { + fprintf(stderr, "puttygen: failed to collect entropy, " + "could not generate key\n"); + return 1; + } random_add_heavynoise(entropy, bits / 8); - memset(entropy, 0, bits/8); + smemclr(entropy, bits/8); sfree(entropy); if (keytype == DSA) { @@ -656,21 +713,30 @@ int main(int argc, char **argv) * Find out whether the input key is encrypted. */ if (intype == SSH_KEYTYPE_SSH1) - encrypted = rsakey_encrypted(&infilename, &origcomment); + encrypted = rsakey_encrypted(infilename, &origcomment); else if (intype == SSH_KEYTYPE_SSH2) - encrypted = ssh2_userkey_encrypted(&infilename, &origcomment); + encrypted = ssh2_userkey_encrypted(infilename, &origcomment); else - encrypted = import_encrypted(&infilename, intype, &origcomment); + encrypted = import_encrypted(infilename, intype, &origcomment); /* * If so, ask for a passphrase. */ if (encrypted && load_encrypted) { - passphrase = snewn(512, char); - if (!console_get_line("Enter passphrase to load key: ", - passphrase, 512, TRUE)) { + 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); } } else { passphrase = NULL; @@ -683,31 +749,35 @@ int main(int argc, char **argv) ssh1key = snew(struct RSAKey); if (!load_encrypted) { void *vblob; - char *blob; + unsigned char *blob; int n, l, bloblen; - ret = rsakey_pubblob(&infilename, &vblob, &bloblen, &error); - blob = (char *)vblob; + ret = rsakey_pubblob(infilename, &vblob, &bloblen, + &origcomment, &error); + blob = (unsigned char *)vblob; n = 4; /* skip modulus bits */ l = ssh1_read_bignum(blob + n, bloblen - n, &ssh1key->exponent); if (l < 0) { - error = "SSH1 public key blob was too short"; + error = "SSH-1 public key blob was too short"; } else { n += l; l = ssh1_read_bignum(blob + n, bloblen - n, &ssh1key->modulus); if (l < 0) { - error = "SSH1 public key blob was too short"; + error = "SSH-1 public key blob was too short"; } else n += l; } - ssh1key->comment = NULL; + ssh1key->comment = dupstr(origcomment); ssh1key->private_exponent = NULL; + ssh1key->p = NULL; + ssh1key->q = NULL; + ssh1key->iqmp = NULL; } else { - ret = loadrsakey(&infilename, ssh1key, passphrase, &error); + ret = loadrsakey(infilename, ssh1key, passphrase, &error); } if (ret > 0) error = NULL; @@ -717,15 +787,17 @@ int main(int argc, char **argv) case SSH_KEYTYPE_SSH2: if (!load_encrypted) { - ssh2blob = ssh2_userkey_loadpub(&infilename, &ssh2alg, - &ssh2bloblen, &error); - ssh2algf = find_pubkey_alg(ssh2alg); - if (ssh2algf) - bits = ssh2algf->pubkey_bits(ssh2blob, ssh2bloblen); - else - bits = -1; + ssh2blob = ssh2_userkey_loadpub(infilename, &ssh2alg, + &ssh2bloblen, NULL, &error); + if (ssh2blob) { + ssh2algf = find_pubkey_alg(ssh2alg); + if (ssh2algf) + bits = ssh2algf->pubkey_bits(ssh2blob, ssh2bloblen); + else + bits = -1; + } } else { - ssh2key = ssh2_load_userkey(&infilename, passphrase, &error); + ssh2key = ssh2_load_userkey(infilename, passphrase, &error); } if ((ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) || ssh2blob) error = NULL; @@ -739,15 +811,14 @@ int main(int argc, char **argv) case SSH_KEYTYPE_OPENSSH: case SSH_KEYTYPE_SSHCOM: - ssh2key = import_ssh2(&infilename, intype, passphrase); - if (ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) - error = NULL; - else if (!error) { - if (ssh2key == SSH2_WRONG_PASSPHRASE) - error = "wrong passphrase"; + ssh2key = import_ssh2(infilename, intype, passphrase, &error); + if (ssh2key) { + if (ssh2key != SSH2_WRONG_PASSPHRASE) + error = NULL; else - error = "unknown error"; - } + error = "wrong passphrase"; + } else if (!error) + error = "unknown error"; break; default: @@ -781,31 +852,35 @@ int main(int argc, char **argv) * we have just generated a key. */ if (change_passphrase || keytype != NOKEYGEN) { - char *passphrase2; - - if (passphrase) { - memset(passphrase, 0, strlen(passphrase)); - sfree(passphrase); - } + prompts_t *p = new_prompts(NULL); + int ret; - passphrase = snewn(512, char); - passphrase2 = snewn(512, char); - if (!console_get_line("Enter passphrase to save key: ", - passphrase, 512, TRUE) || - !console_get_line("Re-enter passphrase to verify: ", - passphrase2, 512, TRUE)) { + p->to_server = FALSE; + p->name = dupstr("New SSH key passphrase"); + add_prompt(p, dupstr("Enter passphrase to save key: "), FALSE); + add_prompt(p, dupstr("Re-enter passphrase to verify: "), FALSE); + ret = console_get_userpass_input(p, NULL, 0); + assert(ret >= 0); + if (!ret) { + free_prompts(p); perror("puttygen: unable to read new passphrase"); return 1; - } - if (strcmp(passphrase, passphrase2)) { - fprintf(stderr, "puttygen: passphrases do not match\n"); - return 1; - } - memset(passphrase2, 0, strlen(passphrase2)); - sfree(passphrase2); - if (!*passphrase) { - sfree(passphrase); - passphrase = NULL; + } else { + if (strcmp(p->prompts[0]->result, p->prompts[1]->result)) { + free_prompts(p); + fprintf(stderr, "puttygen: passphrases do not match\n"); + return 1; + } + if (passphrase) { + smemclr(passphrase, strlen(passphrase)); + sfree(passphrase); + } + passphrase = dupstr(p->prompts[0]->result); + free_prompts(p); + if (!*passphrase) { + sfree(passphrase); + passphrase = NULL; + } } } @@ -828,16 +903,16 @@ int main(int argc, char **argv) case PRIVATE: if (sshver == 1) { assert(ssh1key); - ret = saversakey(&outfilename, ssh1key, passphrase); + ret = saversakey(outfilename, ssh1key, passphrase); if (!ret) { - fprintf(stderr, "puttygen: unable to save SSH1 private key\n"); + 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, passphrase); if (!ret) { - fprintf(stderr, "puttygen: unable to save SSH2 private key\n"); + fprintf(stderr, "puttygen: unable to save SSH-2 private key\n"); return 1; } } @@ -856,7 +931,7 @@ int main(int argc, char **argv) assert(ssh1key); if (outfile) - fp = f_open(outfilename, "w"); + fp = f_open(outfilename, "w", FALSE); else fp = stdout; dec1 = bignum_decimal(ssh1key->exponent); @@ -914,7 +989,7 @@ int main(int argc, char **argv) *p++ = '\0'; if (outfile) - fp = f_open(outfilename, "w"); + fp = f_open(outfilename, "w", FALSE); else fp = stdout; fprintf(fp, "%s\n", buffer); @@ -944,7 +1019,7 @@ int main(int argc, char **argv) } if (outfile) - fp = f_open(outfilename, "w"); + fp = f_open(outfilename, "w", FALSE); else fp = stdout; fprintf(fp, "%s\n", fingerprint); @@ -959,7 +1034,7 @@ int main(int argc, char **argv) case SSHCOM: assert(sshver == 2); assert(ssh2key); - ret = export_ssh2(&outfilename, outtype, ssh2key, passphrase); + ret = export_ssh2(outfilename, outtype, ssh2key, passphrase); if (!ret) { fprintf(stderr, "puttygen: unable to export key\n"); return 1; @@ -972,7 +1047,7 @@ int main(int argc, char **argv) } if (passphrase) { - memset(passphrase, 0, strlen(passphrase)); + smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } @@ -1216,10 +1291,10 @@ int main(int argc, char **argv) * Change the comment of the key; this _does_ require a * passphrase owing to the tamperproofing. * - * NOTE: In SSH1, this only requires a passphrase because + * NOTE: In SSH-1, this only requires a passphrase because * of inadequacies of the loading and saving mechanisms. In * _principle_, it should be perfectly possible to modify - * the comment on an SSH1 key without requiring a + * the comment on an SSH-1 key without requiring a * passphrase; the only reason I can't do it is because my * loading and saving mechanisms don't include a method of * loading all the key data without also trying to decrypt @@ -1227,7 +1302,7 @@ int main(int argc, char **argv) * * I don't consider this to be a problem worth solving, * because (a) to fix it would probably end up bloating - * PuTTY proper, and (b) SSH1 is on the way out anyway so + * PuTTY proper, and (b) SSH-1 is on the way out anyway so * it shouldn't be highly significant. If it seriously * bothers anyone then perhaps I _might_ be persuadable. */