X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/2285d016cf0103f03e05e53e22025282c104a164..HEAD:/cmdgen.c diff --git a/cmdgen.c b/cmdgen.c index c4fcc9e1..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,32 +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")) { - /* support "-pgpfp" for consistency with others */ - pgp_fingerprints(); - nogo = TRUE; + 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; @@ -470,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; } @@ -482,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. @@ -494,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) { /* @@ -620,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) { @@ -660,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; @@ -687,11 +749,12 @@ 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 */ @@ -708,10 +771,13 @@ int main(int argc, char **argv) } 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; @@ -721,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; @@ -743,7 +811,7 @@ int main(int argc, char **argv) case SSH_KEYTYPE_OPENSSH: case SSH_KEYTYPE_SSHCOM: - ssh2key = import_ssh2(&infilename, intype, passphrase, &error); + ssh2key = import_ssh2(infilename, intype, passphrase, &error); if (ssh2key) { if (ssh2key != SSH2_WRONG_PASSPHRASE) error = NULL; @@ -784,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; + } } } @@ -831,14 +903,14 @@ 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 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 SSH-2 private key\n"); return 1; @@ -859,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); @@ -917,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); @@ -947,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); @@ -962,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; @@ -975,7 +1047,7 @@ int main(int argc, char **argv) } if (passphrase) { - memset(passphrase, 0, strlen(passphrase)); + smemclr(passphrase, strlen(passphrase)); sfree(passphrase); }