From 2069de8c8f9b54fcdc9af71fefc55ada725ebc49 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 11 May 2015 15:06:25 +0100 Subject: [PATCH] Pageant: factor out cross-platform parts of add_keyfile(). I've now centralised into pageant.c all the logic about trying to load keys of any type, with no passphrase or with the passphrases used in previous key-loading actions or with a new user-supplied passphrase, whether we're the main Pageant process ourself or are talking to another one as a client. The only part of that code remaining in winpgnt.c is the user interaction via dialog boxes, which of course is the part that will need to be done differently on other platforms. --- pageant.c | 409 ++++++++++++++++++++++++++++++++++++++ pageant.h | 35 ++++ windows/winpgnt.c | 491 +++++++--------------------------------------- 3 files changed, 512 insertions(+), 423 deletions(-) diff --git a/pageant.c b/pageant.c index a97110d0..76e5911a 100644 --- a/pageant.c +++ b/pageant.c @@ -27,6 +27,8 @@ int random_byte(void) return 0; /* unreachable, but placate optimiser */ } +static int pageant_local = FALSE; + /* * rsakeys stores SSH-1 RSA keys. ssh2keys stores all SSH-2 keys. */ @@ -918,6 +920,7 @@ void *pageant_failure_msg(int *outlen) void pageant_init(void) { + pageant_local = TRUE; rsakeys = newtree234(cmpkeys_rsa); ssh2keys = newtree234(cmpkeys_ssh2); } @@ -1173,3 +1176,409 @@ void pageant_listener_free(struct pageant_listen_state *pl) sk_close(pl->listensock); sfree(pl); } + +/* ---------------------------------------------------------------------- + * Code to perform agent operations either as a client, or within the + * same process as the running agent. + */ + +static tree234 *passphrases = NULL; + +/* + * After processing a list of filenames, we want to forget the + * passphrases. + */ +void pageant_forget_passphrases(void) +{ + while (count234(passphrases) > 0) { + char *pp = index234(passphrases, 0); + smemclr(pp, strlen(pp)); + delpos234(passphrases, 0); + free(pp); + } +} + +void *pageant_get_keylist1(int *length) +{ + void *ret; + + if (!pageant_local) { + unsigned char request[5], *response; + void *vresponse; + int resplen, retval; + request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES; + PUT_32BIT(request, 4); + + retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL); + assert(retval == 1); + response = vresponse; + if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) { + sfree(response); + return NULL; + } + + ret = snewn(resplen-5, unsigned char); + memcpy(ret, response+5, resplen-5); + sfree(response); + + if (length) + *length = resplen-5; + } else { + ret = pageant_make_keylist1(length); + } + return ret; +} + +void *pageant_get_keylist2(int *length) +{ + void *ret; + + if (!pageant_local) { + unsigned char request[5], *response; + void *vresponse; + int resplen, retval; + + request[4] = SSH2_AGENTC_REQUEST_IDENTITIES; + PUT_32BIT(request, 4); + + retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL); + assert(retval == 1); + response = vresponse; + if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) { + sfree(response); + return NULL; + } + + ret = snewn(resplen-5, unsigned char); + memcpy(ret, response+5, resplen-5); + sfree(response); + + if (length) + *length = resplen-5; + } else { + ret = pageant_make_keylist2(length); + } + return ret; +} + +int pageant_add_keyfile(Filename *filename, const char *passphrase, + char **retstr) +{ + struct RSAKey *rkey = NULL; + struct ssh2_userkey *skey = NULL; + int needs_pass; + int ret; + int attempts; + char *comment; + const char *this_passphrase; + const char *error = NULL; + int type; + + if (!passphrases) { + passphrases = newtree234(NULL); + } + + *retstr = NULL; + + type = key_type(filename); + if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) { + *retstr = dupprintf("Couldn't load this key (%s)", + key_type_to_str(type)); + return PAGEANT_ACTION_FAILURE; + } + + /* + * See if the key is already loaded (in the primary Pageant, + * which may or may not be us). + */ + { + void *blob; + unsigned char *keylist, *p; + int i, nkeys, bloblen, keylistlen; + + if (type == SSH_KEYTYPE_SSH1) { + if (!rsakey_pubblob(filename, &blob, &bloblen, NULL, &error)) { + *retstr = dupprintf("Couldn't load private key (%s)", error); + return PAGEANT_ACTION_FAILURE; + } + keylist = pageant_get_keylist1(&keylistlen); + } else { + unsigned char *blob2; + blob = ssh2_userkey_loadpub(filename, NULL, &bloblen, + NULL, &error); + if (!blob) { + *retstr = dupprintf("Couldn't load private key (%s)", error); + return PAGEANT_ACTION_FAILURE; + } + /* For our purposes we want the blob prefixed with its length */ + blob2 = snewn(bloblen+4, unsigned char); + PUT_32BIT(blob2, bloblen); + memcpy(blob2 + 4, blob, bloblen); + sfree(blob); + blob = blob2; + + keylist = pageant_get_keylist2(&keylistlen); + } + if (keylist) { + if (keylistlen < 4) { + *retstr = dupstr("Received broken key list from agent"); + return PAGEANT_ACTION_FAILURE; + } + nkeys = toint(GET_32BIT(keylist)); + if (nkeys < 0) { + *retstr = dupstr("Received broken key list from agent"); + return PAGEANT_ACTION_FAILURE; + } + p = keylist + 4; + keylistlen -= 4; + + for (i = 0; i < nkeys; i++) { + if (!memcmp(blob, p, bloblen)) { + /* Key is already present; we can now leave. */ + sfree(keylist); + sfree(blob); + return PAGEANT_ACTION_OK; + } + /* Now skip over public blob */ + if (type == SSH_KEYTYPE_SSH1) { + int n = rsa_public_blob_len(p, keylistlen); + if (n < 0) { + *retstr = dupstr("Received broken key list from agent"); + return PAGEANT_ACTION_FAILURE; + } + p += n; + keylistlen -= n; + } else { + int n; + if (keylistlen < 4) { + *retstr = dupstr("Received broken key list from agent"); + return PAGEANT_ACTION_FAILURE; + } + n = toint(4 + GET_32BIT(p)); + if (n < 0 || keylistlen < n) { + *retstr = dupstr("Received broken key list from agent"); + return PAGEANT_ACTION_FAILURE; + } + p += n; + keylistlen -= n; + } + /* Now skip over comment field */ + { + int n; + if (keylistlen < 4) { + *retstr = dupstr("Received broken key list from agent"); + return PAGEANT_ACTION_FAILURE; + } + n = toint(4 + GET_32BIT(p)); + if (n < 0 || keylistlen < n) { + *retstr = dupstr("Received broken key list from agent"); + return PAGEANT_ACTION_FAILURE; + } + p += n; + keylistlen -= n; + } + } + + sfree(keylist); + } + + sfree(blob); + } + + error = NULL; + if (type == SSH_KEYTYPE_SSH1) + needs_pass = rsakey_encrypted(filename, &comment); + else + needs_pass = ssh2_userkey_encrypted(filename, &comment); + attempts = 0; + if (type == SSH_KEYTYPE_SSH1) + rkey = snew(struct RSAKey); + + /* + * Loop round repeatedly trying to load the key, until we either + * succeed, fail for some serious reason, or run out of + * passphrases to try. + */ + while (1) { + if (needs_pass) { + + /* + * If we've been given a passphrase on input, try using + * it. Otherwise, try one from our tree234 of previously + * useful passphrases. + */ + if (passphrase) { + this_passphrase = (attempts == 0 ? passphrase : NULL); + } else { + this_passphrase = (const char *)index234(passphrases, attempts); + } + + if (!this_passphrase) { + /* + * Run out of passphrases to try. + */ + *retstr = comment; + return PAGEANT_ACTION_NEED_PP; + } + } else + this_passphrase = ""; + + if (type == SSH_KEYTYPE_SSH1) + ret = loadrsakey(filename, rkey, this_passphrase, &error); + else { + skey = ssh2_load_userkey(filename, this_passphrase, &error); + if (skey == SSH2_WRONG_PASSPHRASE) + ret = -1; + else if (!skey) + ret = 0; + else + ret = 1; + } + + if (ret == 0) { + /* + * Failed to load the key file, for some reason other than + * a bad passphrase. + */ + *retstr = dupstr(error); + return PAGEANT_ACTION_FAILURE; + } else if (ret == 1) { + /* + * Successfully loaded the key file. + */ + break; + } else { + /* + * Passphrase wasn't right; go round again. + */ + attempts++; + } + } + + /* + * If we get here, we've succesfully loaded the key into + * rkey/skey, but not yet added it to the agent. + */ + + /* + * If the key was successfully decrypted, save the passphrase for + * use with other keys we try to load. + */ + { + char *pp_copy = dupstr(this_passphrase); + if (addpos234(passphrases, pp_copy, 0) != pp_copy) { + /* No need; it was already there. */ + smemclr(pp_copy, strlen(pp_copy)); + sfree(pp_copy); + } + } + + if (comment) + sfree(comment); + + if (type == SSH_KEYTYPE_SSH1) { + if (!pageant_local) { + unsigned char *request, *response; + void *vresponse; + int reqlen, clen, resplen, ret; + + clen = strlen(rkey->comment); + + reqlen = 4 + 1 + /* length, message type */ + 4 + /* bit count */ + ssh1_bignum_length(rkey->modulus) + + ssh1_bignum_length(rkey->exponent) + + ssh1_bignum_length(rkey->private_exponent) + + ssh1_bignum_length(rkey->iqmp) + + ssh1_bignum_length(rkey->p) + + ssh1_bignum_length(rkey->q) + 4 + clen /* comment */ + ; + + request = snewn(reqlen, unsigned char); + + request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY; + reqlen = 5; + PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus)); + reqlen += 4; + reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus); + reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent); + reqlen += + ssh1_write_bignum(request + reqlen, + rkey->private_exponent); + reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp); + reqlen += ssh1_write_bignum(request + reqlen, rkey->p); + reqlen += ssh1_write_bignum(request + reqlen, rkey->q); + PUT_32BIT(request + reqlen, clen); + memcpy(request + reqlen + 4, rkey->comment, clen); + reqlen += 4 + clen; + PUT_32BIT(request, reqlen - 4); + + ret = agent_query(request, reqlen, &vresponse, &resplen, + NULL, NULL); + assert(ret == 1); + response = vresponse; + if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) { + *retstr = dupstr("The already running Pageant " + "refused to add the key."); + return PAGEANT_ACTION_FAILURE; + } + sfree(request); + sfree(response); + } else { + if (!pageant_add_ssh1_key(rkey)) { + sfree(rkey); /* already present, don't waste RAM */ + } + } + } else { + if (!pageant_local) { + unsigned char *request, *response; + void *vresponse; + int reqlen, alglen, clen, keybloblen, resplen, ret; + alglen = strlen(skey->alg->name); + clen = strlen(skey->comment); + + keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0); + + reqlen = 4 + 1 + /* length, message type */ + 4 + alglen + /* algorithm name */ + keybloblen + /* key data */ + 4 + clen /* comment */ + ; + + request = snewn(reqlen, unsigned char); + + request[4] = SSH2_AGENTC_ADD_IDENTITY; + reqlen = 5; + PUT_32BIT(request + reqlen, alglen); + reqlen += 4; + memcpy(request + reqlen, skey->alg->name, alglen); + reqlen += alglen; + reqlen += skey->alg->openssh_fmtkey(skey->data, + request + reqlen, + keybloblen); + PUT_32BIT(request + reqlen, clen); + memcpy(request + reqlen + 4, skey->comment, clen); + reqlen += clen + 4; + PUT_32BIT(request, reqlen - 4); + + ret = agent_query(request, reqlen, &vresponse, &resplen, + NULL, NULL); + assert(ret == 1); + response = vresponse; + if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) { + *retstr = dupstr("The already running Pageant " + "refused to add the key."); + return PAGEANT_ACTION_FAILURE; + } + + sfree(request); + sfree(response); + } else { + if (!pageant_add_ssh2_key(skey)) { + skey->alg->freekey(skey->data); + sfree(skey); /* already present, don't waste RAM */ + } + } + } + return PAGEANT_ACTION_OK; +} diff --git a/pageant.h b/pageant.h index 06dd033a..c1bc854e 100644 --- a/pageant.h +++ b/pageant.h @@ -85,3 +85,38 @@ void pageant_listener_got_socket(struct pageant_listen_state *pl, Socket sock); void pageant_listener_set_logfn(struct pageant_listen_state *pl, void *logctx, pageant_logfn_t logfn); void pageant_listener_free(struct pageant_listen_state *pl); + +/* + * Functions to perform specific key actions, either as a client of an + * ssh-agent running elsewhere, or directly on the agent state in this + * process. (On at least one platform we want to do this in an + * agnostic way between the two situations.) + * + * pageant_get_keylist{1,2} work just like pageant_make_keylist{1,2} + * above, except that they can also cope if they have to contact an + * external agent. + * + * pageant_add_keyfile() is used to load a private key from a file and + * add it to the agent. Initially, you should call it with passphrase + * NULL, and it will check if the key is already in the agent, and + * whether a passphrase is required. Return values are given in the + * enum below. On return, *retstr will either be NULL, or a + * dynamically allocated string containing a key comment or an error + * message. + * + * pageant_add_keyfile() also remembers passphrases with which it's + * successfully decrypted keys (because if you try to add multiple + * keys in one go, you might very well have used the same passphrase + * for keys that have the same trust properties). Call + * pageant_forget_passphrases() to get rid of them all. + */ +void *pageant_get_keylist1(int *length); +void *pageant_get_keylist2(int *length); +enum { + PAGEANT_ACTION_OK, /* success; no further action needed */ + PAGEANT_ACTION_FAILURE, /* failure; *retstr is error message */ + PAGEANT_ACTION_NEED_PP /* need passphrase: *retstr is key comment */ +}; +int pageant_add_keyfile(Filename *filename, const char *passphrase, + char **retstr); +void pageant_forget_passphrases(void); diff --git a/windows/winpgnt.c b/windows/winpgnt.c index 2aa98860..a82888f6 100644 --- a/windows/winpgnt.c +++ b/windows/winpgnt.c @@ -112,33 +112,11 @@ static void unmungestr(char *in, char *out, int outlen) static int has_security; -/* - * Forward references - */ -static void *get_keylist1(int *length); -static void *get_keylist2(int *length); - struct PassphraseProcStruct { char **passphrase; char *comment; }; -static tree234 *passphrases = NULL; - -/* - * After processing a list of filenames, we want to forget the - * passphrases. - */ -static void forget_passphrases(void) -{ - while (count234(passphrases) > 0) { - char *pp = index234(passphrases, 0); - smemclr(pp, strlen(pp)); - delpos234(passphrases, 0); - free(pp); - } -} - /* * Dialog-box function for the Licence box. */ @@ -340,397 +318,6 @@ void keylist_update(void) } } -/* - * This function loads a key from a file and adds it. - */ -static void add_keyfile(Filename *filename) -{ - char *passphrase; - struct RSAKey *rkey = NULL; - struct ssh2_userkey *skey = NULL; - int needs_pass; - int ret; - int attempts; - char *comment; - const char *error = NULL; - int type; - int original_pass; - - type = key_type(filename); - if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) { - char *msg = dupprintf("Couldn't load this key (%s)", - key_type_to_str(type)); - message_box(msg, APPNAME, MB_OK | MB_ICONERROR, - HELPCTXID(errors_cantloadkey)); - sfree(msg); - return; - } - - /* - * See if the key is already loaded (in the primary Pageant, - * which may or may not be us). - */ - { - void *blob; - unsigned char *keylist, *p; - int i, nkeys, bloblen, keylistlen; - - if (type == SSH_KEYTYPE_SSH1) { - if (!rsakey_pubblob(filename, &blob, &bloblen, NULL, &error)) { - char *msg = dupprintf("Couldn't load private key (%s)", error); - message_box(msg, APPNAME, MB_OK | MB_ICONERROR, - HELPCTXID(errors_cantloadkey)); - sfree(msg); - return; - } - keylist = get_keylist1(&keylistlen); - } else { - unsigned char *blob2; - blob = ssh2_userkey_loadpub(filename, NULL, &bloblen, - NULL, &error); - if (!blob) { - char *msg = dupprintf("Couldn't load private key (%s)", error); - message_box(msg, APPNAME, MB_OK | MB_ICONERROR, - HELPCTXID(errors_cantloadkey)); - sfree(msg); - return; - } - /* For our purposes we want the blob prefixed with its length */ - blob2 = snewn(bloblen+4, unsigned char); - PUT_32BIT(blob2, bloblen); - memcpy(blob2 + 4, blob, bloblen); - sfree(blob); - blob = blob2; - - keylist = get_keylist2(&keylistlen); - } - if (keylist) { - if (keylistlen < 4) { - MessageBox(NULL, "Received broken key list?!", APPNAME, - MB_OK | MB_ICONERROR); - return; - } - nkeys = toint(GET_32BIT(keylist)); - if (nkeys < 0) { - MessageBox(NULL, "Received broken key list?!", APPNAME, - MB_OK | MB_ICONERROR); - return; - } - p = keylist + 4; - keylistlen -= 4; - - for (i = 0; i < nkeys; i++) { - if (!memcmp(blob, p, bloblen)) { - /* Key is already present; we can now leave. */ - sfree(keylist); - sfree(blob); - return; - } - /* Now skip over public blob */ - if (type == SSH_KEYTYPE_SSH1) { - int n = rsa_public_blob_len(p, keylistlen); - if (n < 0) { - MessageBox(NULL, "Received broken key list?!", APPNAME, - MB_OK | MB_ICONERROR); - return; - } - p += n; - keylistlen -= n; - } else { - int n; - if (keylistlen < 4) { - MessageBox(NULL, "Received broken key list?!", APPNAME, - MB_OK | MB_ICONERROR); - return; - } - n = toint(4 + GET_32BIT(p)); - if (n < 0 || keylistlen < n) { - MessageBox(NULL, "Received broken key list?!", APPNAME, - MB_OK | MB_ICONERROR); - return; - } - p += n; - keylistlen -= n; - } - /* Now skip over comment field */ - { - int n; - if (keylistlen < 4) { - MessageBox(NULL, "Received broken key list?!", APPNAME, - MB_OK | MB_ICONERROR); - return; - } - n = toint(4 + GET_32BIT(p)); - if (n < 0 || keylistlen < n) { - MessageBox(NULL, "Received broken key list?!", APPNAME, - MB_OK | MB_ICONERROR); - return; - } - p += n; - keylistlen -= n; - } - } - - sfree(keylist); - } - - sfree(blob); - } - - error = NULL; - if (type == SSH_KEYTYPE_SSH1) - needs_pass = rsakey_encrypted(filename, &comment); - else - needs_pass = ssh2_userkey_encrypted(filename, &comment); - attempts = 0; - if (type == SSH_KEYTYPE_SSH1) - rkey = snew(struct RSAKey); - passphrase = NULL; - original_pass = 0; - do { - burnstr(passphrase); - passphrase = NULL; - - if (needs_pass) { - /* try all the remembered passphrases first */ - char *pp = index234(passphrases, attempts); - if(pp) { - passphrase = dupstr(pp); - } else { - int dlgret; - struct PassphraseProcStruct pps; - - pps.passphrase = &passphrase; - pps.comment = comment; - - original_pass = 1; - dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210), - NULL, PassphraseProc, (LPARAM) &pps); - passphrase_box = NULL; - if (!dlgret) { - if (comment) - sfree(comment); - if (type == SSH_KEYTYPE_SSH1) - sfree(rkey); - return; /* operation cancelled */ - } - - assert(passphrase != NULL); - } - } else - passphrase = dupstr(""); - - if (type == SSH_KEYTYPE_SSH1) - ret = loadrsakey(filename, rkey, passphrase, &error); - else { - skey = ssh2_load_userkey(filename, passphrase, &error); - if (skey == SSH2_WRONG_PASSPHRASE) - ret = -1; - else if (!skey) - ret = 0; - else - ret = 1; - } - attempts++; - } while (ret == -1); - - if(original_pass && ret) { - /* If they typed in an ok passphrase, remember it */ - addpos234(passphrases, passphrase, 0); - } else { - /* Otherwise, destroy it */ - burnstr(passphrase); - } - passphrase = NULL; - - if (comment) - sfree(comment); - if (ret == 0) { - char *msg = dupprintf("Couldn't load private key (%s)", error); - message_box(msg, APPNAME, MB_OK | MB_ICONERROR, - HELPCTXID(errors_cantloadkey)); - sfree(msg); - if (type == SSH_KEYTYPE_SSH1) - sfree(rkey); - return; - } - if (type == SSH_KEYTYPE_SSH1) { - if (already_running) { - unsigned char *request, *response; - void *vresponse; - int reqlen, clen, resplen, ret; - - clen = strlen(rkey->comment); - - reqlen = 4 + 1 + /* length, message type */ - 4 + /* bit count */ - ssh1_bignum_length(rkey->modulus) + - ssh1_bignum_length(rkey->exponent) + - ssh1_bignum_length(rkey->private_exponent) + - ssh1_bignum_length(rkey->iqmp) + - ssh1_bignum_length(rkey->p) + - ssh1_bignum_length(rkey->q) + 4 + clen /* comment */ - ; - - request = snewn(reqlen, unsigned char); - - request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY; - reqlen = 5; - PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus)); - reqlen += 4; - reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus); - reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent); - reqlen += - ssh1_write_bignum(request + reqlen, - rkey->private_exponent); - reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp); - reqlen += ssh1_write_bignum(request + reqlen, rkey->p); - reqlen += ssh1_write_bignum(request + reqlen, rkey->q); - PUT_32BIT(request + reqlen, clen); - memcpy(request + reqlen + 4, rkey->comment, clen); - reqlen += 4 + clen; - PUT_32BIT(request, reqlen - 4); - - ret = agent_query(request, reqlen, &vresponse, &resplen, - NULL, NULL); - assert(ret == 1); - response = vresponse; - if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) - MessageBox(NULL, "The already running Pageant " - "refused to add the key.", APPNAME, - MB_OK | MB_ICONERROR); - - sfree(request); - sfree(response); - } else { - if (!pageant_add_ssh1_key(rkey)) - sfree(rkey); /* already present, don't waste RAM */ - } - } else { - if (already_running) { - unsigned char *request, *response; - void *vresponse; - int reqlen, alglen, clen, keybloblen, resplen, ret; - alglen = strlen(skey->alg->name); - clen = strlen(skey->comment); - - keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0); - - reqlen = 4 + 1 + /* length, message type */ - 4 + alglen + /* algorithm name */ - keybloblen + /* key data */ - 4 + clen /* comment */ - ; - - request = snewn(reqlen, unsigned char); - - request[4] = SSH2_AGENTC_ADD_IDENTITY; - reqlen = 5; - PUT_32BIT(request + reqlen, alglen); - reqlen += 4; - memcpy(request + reqlen, skey->alg->name, alglen); - reqlen += alglen; - reqlen += skey->alg->openssh_fmtkey(skey->data, - request + reqlen, - keybloblen); - PUT_32BIT(request + reqlen, clen); - memcpy(request + reqlen + 4, skey->comment, clen); - reqlen += clen + 4; - PUT_32BIT(request, reqlen - 4); - - ret = agent_query(request, reqlen, &vresponse, &resplen, - NULL, NULL); - assert(ret == 1); - response = vresponse; - if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) - MessageBox(NULL, "The already running Pageant " - "refused to add the key.", APPNAME, - MB_OK | MB_ICONERROR); - - sfree(request); - sfree(response); - } else { - if (!pageant_add_ssh2_key(skey)) { - skey->alg->freekey(skey->data); - sfree(skey); /* already present, don't waste RAM */ - } - } - } -} - -/* - * Acquire a keylist1 from the primary Pageant; this means either - * calling pageant_make_keylist1 (if that's us) or sending a message - * to the primary Pageant (if it's not). - */ -static void *get_keylist1(int *length) -{ - void *ret; - - if (already_running) { - unsigned char request[5], *response; - void *vresponse; - int resplen, retval; - request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES; - PUT_32BIT(request, 4); - - retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL); - assert(retval == 1); - response = vresponse; - if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) { - sfree(response); - return NULL; - } - - ret = snewn(resplen-5, unsigned char); - memcpy(ret, response+5, resplen-5); - sfree(response); - - if (length) - *length = resplen-5; - } else { - ret = pageant_make_keylist1(length); - } - return ret; -} - -/* - * Acquire a keylist2 from the primary Pageant; this means either - * calling pageant_make_keylist2 (if that's us) or sending a message - * to the primary Pageant (if it's not). - */ -static void *get_keylist2(int *length) -{ - void *ret; - - if (already_running) { - unsigned char request[5], *response; - void *vresponse; - int resplen, retval; - - request[4] = SSH2_AGENTC_REQUEST_IDENTITIES; - PUT_32BIT(request, 4); - - retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL); - assert(retval == 1); - response = vresponse; - if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) { - sfree(response); - return NULL; - } - - ret = snewn(resplen-5, unsigned char); - memcpy(ret, response+5, resplen-5); - sfree(response); - - if (length) - *length = resplen-5; - } else { - ret = pageant_make_keylist2(length); - } - return ret; -} - static void answer_msg(void *msgv) { unsigned char *msg = (unsigned char *)msgv; @@ -759,6 +346,69 @@ static void answer_msg(void *msgv) sfree(reply); } +static void win_add_keyfile(Filename *filename) +{ + char *err; + int ret; + char *passphrase = NULL; + + /* + * Try loading the key without a passphrase. (Or rather, without a + * _new_ passphrase; pageant_add_keyfile will take care of trying + * all the passphrases we've already stored.) + */ + ret = pageant_add_keyfile(filename, NULL, &err); + if (ret == PAGEANT_ACTION_OK) { + goto done; + } else if (ret == PAGEANT_ACTION_FAILURE) { + goto error; + } + + /* + * OK, a passphrase is needed, and we've been given the key + * comment to use in the passphrase prompt. + */ + while (1) { + int dlgret; + struct PassphraseProcStruct pps; + + pps.passphrase = &passphrase; + pps.comment = err; + dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210), + NULL, PassphraseProc, (LPARAM) &pps); + passphrase_box = NULL; + + sfree(err); + + if (!dlgret) + goto done; /* operation cancelled */ + + assert(passphrase != NULL); + + ret = pageant_add_keyfile(filename, passphrase, &err); + if (ret == PAGEANT_ACTION_OK) { + goto done; + } else if (ret == PAGEANT_ACTION_FAILURE) { + goto error; + } + + smemclr(passphrase, strlen(passphrase)); + sfree(passphrase); + passphrase = NULL; + } + + error: + message_box(err, APPNAME, MB_OK | MB_ICONERROR, + HELPCTXID(errors_cantloadkey)); + done: + if (passphrase) { + smemclr(passphrase, strlen(passphrase)); + sfree(passphrase); + } + sfree(err); + return; +} + /* * Prompt for a key file to add, and add it. */ @@ -783,7 +433,7 @@ static void prompt_add_keyfile(void) if(strlen(filelist) > of.nFileOffset) { /* Only one filename returned? */ Filename *fn = filename_from_str(filelist); - add_keyfile(fn); + win_add_keyfile(fn); filename_free(fn); } else { /* we are returned a bunch of strings, end to @@ -796,7 +446,7 @@ static void prompt_add_keyfile(void) while (*filewalker != '\0') { char *filename = dupcat(dir, "\\", filewalker, NULL); Filename *fn = filename_from_str(filename); - add_keyfile(fn); + win_add_keyfile(fn); filename_free(fn); sfree(filename); filewalker += strlen(filewalker) + 1; @@ -804,7 +454,7 @@ static void prompt_add_keyfile(void) } keylist_update(); - forget_passphrases(); + pageant_forget_passphrases(); } sfree(filelist); } @@ -1453,11 +1103,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) pageant_init(); } - /* - * Initialise storage for short-term passphrase cache. - */ - passphrases = newtree234(NULL); - /* * Process the command line and add keys as listed on it. */ @@ -1479,7 +1124,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) break; } else { Filename *fn = filename_from_str(argv[i]); - add_keyfile(fn); + win_add_keyfile(fn); filename_free(fn); added_keys = TRUE; } @@ -1489,7 +1134,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) * Forget any passphrase that we retained while going over * command line keyfiles. */ - forget_passphrases(); + pageant_forget_passphrases(); if (command) { char *args; -- 2.45.2