Pageant core: extension requests to re-encrypt keys.

These requests parallel 'delete key' and 'delete all keys', but they
work on keys which you originally uploaded in encrypted form: they
cause Pageant to delete only the _decrypted_ form of the key, so that
the next attempt to use the key will need to re-prompt for its
passphrase.
This commit is contained in:
Simon Tatham 2020-02-15 16:37:10 +00:00
Родитель 1ae8850d93
Коммит 9f15ab4cac
1 изменённых файлов: 121 добавлений и 3 удалений

124
pageant.c
Просмотреть файл

@ -577,11 +577,45 @@ static struct PageantAsyncOpVtable immop_vtable = {
immop_free,
};
static bool reencrypt_key(PageantKey *pk)
{
if (pk->sort.ssh_version != 2) {
/*
* We don't support storing SSH-1 keys in encrypted form at
* all.
*/
return false;
}
if (!pk->encrypted_key_file) {
/*
* We can't re-encrypt a key if it doesn't have an encrypted
* form. (We could make one up, of course - but with what
* passphrase that we could expect the user to know later?)
*/
return false;
}
/* Only actually free pk->skey if it exists. But we return success
* regardless, so that 'please ensure this key isn't stored
* decrypted' is idempotent. */
if (pk->skey) {
sfree(pk->skey->comment);
ssh_key_free(pk->skey->key);
sfree(pk->skey);
pk->skey = NULL;
}
return true;
}
#define PUTTYEXT(base) base "@putty.projects.tartarus.org"
#define KNOWN_EXTENSIONS(X) \
X(EXT_QUERY, "query") \
X(EXT_ADD_PPK, PUTTYEXT("add-ppk")) \
#define KNOWN_EXTENSIONS(X) \
X(EXT_QUERY, "query") \
X(EXT_ADD_PPK, PUTTYEXT("add-ppk")) \
X(EXT_REENCRYPT, PUTTYEXT("reencrypt")) \
X(EXT_REENCRYPT_ALL, PUTTYEXT("reencrypt-all")) \
/* end of list */
#define DECL_EXT_ENUM(id, name) id,
@ -1035,6 +1069,10 @@ static PageantAsyncOp *pageant_make_op(
{
enum Extension exttype = EXT_UNKNOWN;
ptrlen extname = get_string(msg);
pageant_client_log(pc, reqid,
"request: SSH2_AGENTC_EXTENSION \"%.*s\"",
PTRLEN_PRINTF(extname));
for (size_t i = 0; i < lenof(extension_names); i++)
if (ptrlen_eq_ptrlen(extname, extension_names[i])) {
exttype = i;
@ -1064,6 +1102,8 @@ static PageantAsyncOp *pageant_make_op(
put_byte(sb, SSH_AGENT_SUCCESS);
for (size_t i = 0; i < lenof(extension_names); i++)
put_stringpl(sb, extension_names[i]);
pageant_client_log(pc, reqid,
"reply: SSH_AGENT_SUCCESS + names");
break;
case EXT_ADD_PPK: {
@ -1178,6 +1218,84 @@ static PageantAsyncOp *pageant_make_op(
sfree(comment);
break;
}
case EXT_REENCRYPT: {
/*
* Re-encrypt a single key, in the sense of deleting
* its unencrypted copy, returning it to the state of
* only having the encrypted PPK form stored, so that
* the next attempt to use it will have to re-prompt
* for the passphrase.
*/
ptrlen blob = get_string(msg);
if (get_err(msg)) {
fail("unable to decode request");
goto responded;
}
if (!pc->suppress_logging) {
char *fingerprint = ssh2_fingerprint_blob(blob);
pageant_client_log(pc, reqid, "key to re-encrypt: %s",
fingerprint);
sfree(fingerprint);
}
PageantKey *pk = findkey2(blob);
if (!pk) {
fail("key not found");
goto responded;
}
pageant_client_log(pc, reqid,
"found with comment: %s", pk->comment);
if (!reencrypt_key(pk)) {
fail("this key couldn't be re-encrypted");
goto responded;
}
put_byte(sb, SSH_AGENT_SUCCESS);
pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS");
break;
}
case EXT_REENCRYPT_ALL: {
/*
* Re-encrypt all keys that have an encrypted form
* stored. Usually, returns success, but with a uint32
* appended indicating how many keys remain
* unencrypted. The exception is if there is at least
* one key in the agent and _no_ key was successfully
* re-encrypted; in that situation we've done nothing,
* and the client didn't _want_ us to do nothing, so
* we return failure.
*
* (Rationale: the 'failure' message ought to be
* atomic, that is, you shouldn't return failure
* having made a state change.)
*/
unsigned nfailures = 0, nsuccesses = 0;
PageantKey *pk;
for (int i = 0; (pk = index234(keytree, i)) != NULL; i++) {
if (reencrypt_key(pk))
nsuccesses++;
else
nfailures++;
}
if (nsuccesses == 0 && nfailures > 0) {
fail("no key could be re-encrypted");
} else {
put_byte(sb, SSH_AGENT_SUCCESS);
put_uint32(sb, nfailures);
pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS "
"(%u keys re-encrypted, %u failures)",
nsuccesses, nfailures);
}
break;
}
}
}
break;