mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
Pageant: fix concurrent attempts to use an encrypted key.
If you had a key stored encrypted in Pageant, and you launched two PuTTY sessions both trying to generate a signature with the key, and then didn't type the passphrase into the async passphrase prompt until after both sessions had made requests, then only one of the requests would be replied to once you decrypted the key. The other would sit there waiting for a response that Pageant never got round to sending. This would also happen in the case where you launched two PuTTY sessions each trying to use a _different_ encrypted key in Pageant, in which case Pageant should have presented the next GUI passphrase box after the first one was dismissed. That didn't happen either. This was the result of two separate bugs in the code. Firstly, when signop_coroutine() noticed that a GUI request was already in progress, it would crReturn without making any arrangement to be called back. Now there's a queue of 'requests blocked on waiting for some GUI prompt to finish'. Secondly, if multiple requests were blocked on the same key, the code that went over the linked list of them had a bug in which it would unblock the _first_ list item in every iteration, instead of unblocking all the items once each.
This commit is contained in:
parent
e0959d4647
commit
0d4af8e1c4
39
pageant.c
39
pageant.c
@ -124,6 +124,8 @@ struct PageantSignOp {
|
||||
/* Master lock that indicates whether a GUI request is currently in
|
||||
* progress */
|
||||
static bool gui_request_in_progress = false;
|
||||
static PageantKeyRequestNode requests_blocked_on_gui =
|
||||
{ &requests_blocked_on_gui, &requests_blocked_on_gui };
|
||||
|
||||
static void failure(PageantClient *pc, PageantClientRequestId *reqid,
|
||||
strbuf *sb, unsigned char type, const char *fmt, ...);
|
||||
@ -359,7 +361,7 @@ static PRINTF_LIKE(5, 6) void failure(
|
||||
}
|
||||
}
|
||||
|
||||
static void signop_link(PageantSignOp *so)
|
||||
static void signop_link_to_key(PageantSignOp *so)
|
||||
{
|
||||
assert(!so->pkr.prev);
|
||||
assert(!so->pkr.next);
|
||||
@ -370,12 +372,24 @@ static void signop_link(PageantSignOp *so)
|
||||
so->pkr.next->prev = &so->pkr;
|
||||
}
|
||||
|
||||
static void signop_link_to_pending_gui_request(PageantSignOp *so)
|
||||
{
|
||||
assert(!so->pkr.prev);
|
||||
assert(!so->pkr.next);
|
||||
|
||||
so->pkr.prev = requests_blocked_on_gui.prev;
|
||||
so->pkr.next = &requests_blocked_on_gui;
|
||||
so->pkr.prev->next = &so->pkr;
|
||||
so->pkr.next->prev = &so->pkr;
|
||||
}
|
||||
|
||||
static void signop_unlink(PageantSignOp *so)
|
||||
{
|
||||
if (so->pkr.next) {
|
||||
assert(so->pkr.prev);
|
||||
so->pkr.next->prev = so->pkr.prev;
|
||||
so->pkr.prev->next = so->pkr.next;
|
||||
so->pkr.prev = so->pkr.next = NULL;
|
||||
} else {
|
||||
assert(!so->pkr.prev);
|
||||
}
|
||||
@ -413,8 +427,11 @@ static void signop_coroutine(PageantAsyncOp *pao)
|
||||
|
||||
crBegin(so->crLine);
|
||||
|
||||
while (!so->pk->skey && gui_request_in_progress)
|
||||
while (!so->pk->skey && gui_request_in_progress) {
|
||||
signop_link_to_pending_gui_request(so);
|
||||
crReturnV;
|
||||
signop_unlink(so);
|
||||
}
|
||||
|
||||
if (!so->pk->skey) {
|
||||
assert(so->pk->encrypted_key_file);
|
||||
@ -427,7 +444,7 @@ static void signop_coroutine(PageantAsyncOp *pao)
|
||||
goto respond;
|
||||
}
|
||||
|
||||
signop_link(so);
|
||||
signop_link_to_key(so);
|
||||
crReturnV;
|
||||
signop_unlink(so);
|
||||
}
|
||||
@ -496,8 +513,16 @@ static void unblock_requests_for_key(PageantKey *pk)
|
||||
{
|
||||
for (PageantKeyRequestNode *pkr = pk->blocked_requests.next;
|
||||
pkr != &pk->blocked_requests; pkr = pkr->next) {
|
||||
PageantSignOp *so = container_of(pk->blocked_requests.next,
|
||||
PageantSignOp, pkr);
|
||||
PageantSignOp *so = container_of(pkr, PageantSignOp, pkr);
|
||||
queue_toplevel_callback(pageant_async_op_callback, &so->pao);
|
||||
}
|
||||
}
|
||||
|
||||
static void unblock_pending_gui_requests(void)
|
||||
{
|
||||
for (PageantKeyRequestNode *pkr = requests_blocked_on_gui.next;
|
||||
pkr != &requests_blocked_on_gui; pkr = pkr->next) {
|
||||
PageantSignOp *so = container_of(pkr, PageantSignOp, pkr);
|
||||
queue_toplevel_callback(pageant_async_op_callback, &so->pao);
|
||||
}
|
||||
}
|
||||
@ -557,6 +582,8 @@ void pageant_passphrase_request_success(PageantClientDialogId *dlgid,
|
||||
}
|
||||
|
||||
unblock_requests_for_key(pk);
|
||||
|
||||
unblock_pending_gui_requests();
|
||||
}
|
||||
|
||||
void pageant_passphrase_request_refused(PageantClientDialogId *dlgid)
|
||||
@ -568,6 +595,8 @@ void pageant_passphrase_request_refused(PageantClientDialogId *dlgid)
|
||||
pk->decryption_prompt_active = false;
|
||||
|
||||
fail_requests_for_key(pk, "user refused to supply passphrase");
|
||||
|
||||
unblock_pending_gui_requests();
|
||||
}
|
||||
|
||||
typedef struct PageantImmOp PageantImmOp;
|
||||
|
Loading…
Reference in New Issue
Block a user