diff --git a/pageant.c b/pageant.c index 08811d27..97f9c588 100644 --- a/pageant.c +++ b/pageant.c @@ -1604,7 +1604,7 @@ void *pageant_get_keylist2(int *length) } int pageant_add_keyfile(Filename *filename, const char *passphrase, - char **retstr) + char **retstr, bool add_encrypted) { RSAKey *rkey = NULL; ssh2_userkey *skey = NULL; @@ -1629,6 +1629,11 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase, return PAGEANT_ACTION_FAILURE; } + if (add_encrypted && type == SSH_KEYTYPE_SSH1) { + *retstr = dupprintf("Can't add SSH-1 keys in encrypted form"); + return PAGEANT_ACTION_FAILURE; + } + /* * See if the key is already loaded (in the primary Pageant, * which may or may not be us). @@ -1747,6 +1752,38 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase, strbuf_free(blob); } + if (add_encrypted) { + const char *load_error; + LoadedFile *lf = lf_load_keyfile(filename, &load_error); + if (!lf) { + *retstr = dupstr(load_error); + return PAGEANT_ACTION_FAILURE; + } + + strbuf *request = strbuf_new_for_agent_query(); + put_byte(request, SSH2_AGENTC_EXTENSION); + put_stringpl(request, PUTTYEXT("add-ppk")); + put_string(request, lf->data, lf->len); + + lf_free(lf); + + void *vresponse; + int resplen; + pageant_client_query(request, &vresponse, &resplen); + strbuf_free(request); + + unsigned char *response = vresponse; + if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) { + *retstr = dupstr("The already running Pageant " + "refused to add the key."); + sfree(response); + return PAGEANT_ACTION_FAILURE; + } + + sfree(response); + return PAGEANT_ACTION_OK; + } + error = NULL; if (type == SSH_KEYTYPE_SSH1) needs_pass = rsa1_encrypted_f(filename, &comment); diff --git a/pageant.h b/pageant.h index 3d58df33..a34d0094 100644 --- a/pageant.h +++ b/pageant.h @@ -220,7 +220,7 @@ enum { PAGEANT_ACTION_NEED_PP /* need passphrase: *retstr is key comment */ }; int pageant_add_keyfile(Filename *filename, const char *passphrase, - char **retstr); + char **retstr, bool add_encrypted); void pageant_forget_passphrases(void); struct pageant_pubkey { diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c index 18fb5968..642b6db4 100644 --- a/unix/uxpgnt.c +++ b/unix/uxpgnt.c @@ -312,7 +312,10 @@ static void tty_life_timer(void *ctx, unsigned long now) typedef enum { KEYACT_AGENT_LOAD, - KEYACT_CLIENT_ADD, + KEYACT_AGENT_LOAD_ENCRYPTED, + KEYACT_CLIENT_BASE, + KEYACT_CLIENT_ADD = KEYACT_CLIENT_BASE, + KEYACT_CLIENT_ADD_ENCRYPTED, KEYACT_CLIENT_DEL, KEYACT_CLIENT_DEL_ALL, KEYACT_CLIENT_LIST, @@ -327,7 +330,7 @@ struct cmdline_key_action { bool is_agent_action(keyact action) { - return action == KEYACT_AGENT_LOAD; + return action < KEYACT_CLIENT_BASE; } static struct cmdline_key_action *keyact_head = NULL, *keyact_tail = NULL; @@ -438,7 +441,7 @@ static char *askpass(const char *prompt) } } -static bool unix_add_keyfile(const char *filename_str) +static bool unix_add_keyfile(const char *filename_str, bool add_encrypted) { Filename *filename = filename_from_str(filename_str); int status; @@ -450,7 +453,7 @@ static bool unix_add_keyfile(const char *filename_str) /* * Try without a passphrase. */ - status = pageant_add_keyfile(filename, NULL, &err); + status = pageant_add_keyfile(filename, NULL, &err, add_encrypted); if (status == PAGEANT_ACTION_OK) { goto cleanup; } else if (status == PAGEANT_ACTION_FAILURE) { @@ -472,7 +475,8 @@ static bool unix_add_keyfile(const char *filename_str) if (!passphrase) break; - status = pageant_add_keyfile(filename, passphrase, &err); + status = pageant_add_keyfile(filename, passphrase, &err, + add_encrypted); smemclr(passphrase, strlen(passphrase)); sfree(passphrase); @@ -692,7 +696,9 @@ void run_client(void) for (act = keyact_head; act; act = act->next) { switch (act->action) { case KEYACT_CLIENT_ADD: - if (!unix_add_keyfile(act->filename)) + case KEYACT_CLIENT_ADD_ENCRYPTED: + if (!unix_add_keyfile(act->filename, + act->action == KEYACT_CLIENT_ADD_ENCRYPTED)) errors = true; break; case KEYACT_CLIENT_LIST: @@ -875,8 +881,10 @@ void run_agent(FILE *logfp, const char *symlink_path) * Start by loading any keys provided on the command line. */ for (act = keyact_head; act; act = act->next) { - assert(act->action == KEYACT_AGENT_LOAD); - if (!unix_add_keyfile(act->filename)) + assert(act->action == KEYACT_AGENT_LOAD || + act->action == KEYACT_AGENT_LOAD_ENCRYPTED); + if (!unix_add_keyfile(act->filename, + act->action == KEYACT_AGENT_LOAD_ENCRYPTED)) errors = true; } if (errors) @@ -1097,6 +1105,16 @@ int main(int argc, char **argv) life = LIFE_X11; } else if (!strcmp(p, "-T")) { life = LIFE_TTY; + } else if (!strcmp(p, "-E")) { + if (curr_keyact == KEYACT_AGENT_LOAD) + curr_keyact = KEYACT_AGENT_LOAD_ENCRYPTED; + else if (curr_keyact == KEYACT_CLIENT_ADD) + curr_keyact = KEYACT_CLIENT_ADD_ENCRYPTED; + else { + fprintf(stderr, "pageant: unexpected -E while not adding " + "keys\n"); + exit(1); + } } else if (!strcmp(p, "--debug")) { life = LIFE_DEBUG; } else if (!strcmp(p, "--permanent")) { diff --git a/windows/winpgnt.c b/windows/winpgnt.c index 345cfbe3..149e1da1 100644 --- a/windows/winpgnt.c +++ b/windows/winpgnt.c @@ -373,7 +373,7 @@ static void win_add_keyfile(Filename *filename) * _new_ passphrase; pageant_add_keyfile will take care of trying * all the passphrases we've already stored.) */ - ret = pageant_add_keyfile(filename, NULL, &err); + ret = pageant_add_keyfile(filename, NULL, &err, false); if (ret == PAGEANT_ACTION_OK) { goto done; } else if (ret == PAGEANT_ACTION_FAILURE) { @@ -401,7 +401,7 @@ static void win_add_keyfile(Filename *filename) assert(passphrase != NULL); - ret = pageant_add_keyfile(filename, passphrase, &err); + ret = pageant_add_keyfile(filename, passphrase, &err, false); if (ret == PAGEANT_ACTION_OK) { goto done; } else if (ret == PAGEANT_ACTION_FAILURE) {