Finish replacing `Network error' with `Proxy error' throughout proxy
[u/mdw/putty] / pageant.c
CommitLineData
5c58ad2d 1/*
2 * Pageant: the PuTTY Authentication Agent.
3 */
4
5#include <windows.h>
123bc6ea 6#ifndef NO_SECURITY
d70f60ae 7#include <aclapi.h>
123bc6ea 8#endif
9b581c37 9#include <stdio.h>
49bad831 10#include <stdlib.h>
11#include <ctype.h>
3f2d010c 12#include <assert.h>
ddecd643 13#include <tchar.h>
14
5c58ad2d 15#include "ssh.h"
ecea795f 16#include "misc.h"
5c58ad2d 17#include "tree234.h"
d3a1a808 18#include "winstuff.h"
5c58ad2d 19
20#define IDI_MAINICON 200
21#define IDI_TRAYICON 201
22
23#define WM_XUSER (WM_USER + 0x2000)
24#define WM_SYSTRAY (WM_XUSER + 6)
25#define WM_SYSTRAY2 (WM_XUSER + 7)
d70f60ae 26
27#define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
28
29/*
30 * FIXME: maybe some day we can sort this out ...
31 */
32#define AGENT_MAX_MSGLEN 8192
5c58ad2d 33
34#define IDM_CLOSE 0x0010
35#define IDM_VIEWKEYS 0x0020
ab162329 36#define IDM_ADDKEY 0x0030
ecea795f 37#define IDM_HELP 0x0040
38#define IDM_ABOUT 0x0050
5c58ad2d 39
40#define APPNAME "Pageant"
41
5af3a3b6 42extern char ver[];
43
75cab814 44static HINSTANCE instance;
ecea795f 45static HWND main_hwnd;
75cab814 46static HWND keylist;
47static HWND aboutbox;
76b51e35 48static HMENU systray_menu, session_menu;
ddecd643 49static int already_running;
ecea795f 50static int requested_help;
51
52static char *help_path;
76b51e35 53static char *putty_path;
54
55#define IDM_PUTTY 0x0060
56#define IDM_SESSIONS_BASE 0x1000
57#define IDM_SESSIONS_MAX 0x2000
58#define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
59#define PUTTY_DEFAULT "Default%20Settings"
60static int initial_menuitems_count;
61
62/* Un-munge session names out of the registry. */
63static void unmungestr(char *in, char *out, int outlen)
64{
65 while (*in) {
66 if (*in == '%' && in[1] && in[2]) {
67 int i, j;
68
69 i = in[1] - '0';
70 i -= (i > 9 ? 7 : 0);
71 j = in[2] - '0';
72 j -= (j > 9 ? 7 : 0);
73
74 *out++ = (i << 4) + j;
75 if (!--outlen)
76 return;
77 in += 3;
78 } else {
79 *out++ = *in++;
80 if (!--outlen)
81 return;
82 }
83 }
84 *out = '\0';
85 return;
86}
5c58ad2d 87
45cebe79 88static tree234 *rsakeys, *ssh2keys;
5c58ad2d 89
75cab814 90static int has_security;
123bc6ea 91#ifndef NO_SECURITY
32874aea 92typedef DWORD(WINAPI * gsi_fn_t)
93 (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
94 PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *);
75cab814 95static gsi_fn_t getsecurityinfo;
123bc6ea 96#endif
016ef8ab 97
5c58ad2d 98/*
260f3dec 99 * Exports from pageantc.c
100 */
101void agent_query(void *in, int inlen, void **out, int *outlen);
102int agent_exists(void);
103
104/*
3f2d010c 105 * Forward references
106 */
107static void *make_keylist1(int *length);
108static void *make_keylist2(int *length);
109static void *get_keylist1(void);
110static void *get_keylist2(void);
111
112/*
5c58ad2d 113 * We need this to link with the RSA code, because rsaencrypt()
45cebe79 114 * pads its data with random bytes. Since we only use rsadecrypt()
115 * and the signing functions, which are deterministic, this should
116 * never be called.
5c72ca61 117 *
5c58ad2d 118 * If it _is_ called, there is a _serious_ problem, because it
119 * won't generate true random numbers. So we must scream, panic,
120 * and exit immediately if that should happen.
121 */
32874aea 122int random_byte(void)
123{
ecea795f 124 MessageBox(main_hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
5c58ad2d 125 exit(0);
cdcbdf3b 126 /* this line can't be reached but it placates MSVC's warnings :-) */
127 return 0;
5c58ad2d 128}
129
130/*
45cebe79 131 * Blob structure for passing to the asymmetric SSH2 key compare
132 * function, prototyped here.
133 */
134struct blob {
135 unsigned char *blob;
136 int len;
137};
138static int cmpkeys_ssh2_asymm(void *av, void *bv);
139
140/*
5c58ad2d 141 * This function is needed to link with the DES code. We need not
142 * have it do anything at all.
143 */
32874aea 144void logevent(char *msg)
145{
5c58ad2d 146}
147
148#define GET_32BIT(cp) \
149 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
150 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
151 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
152 ((unsigned long)(unsigned char)(cp)[3]))
153
154#define PUT_32BIT(cp, value) { \
155 (cp)[0] = (unsigned char)((value) >> 24); \
156 (cp)[1] = (unsigned char)((value) >> 16); \
157 (cp)[2] = (unsigned char)((value) >> 8); \
158 (cp)[3] = (unsigned char)(value); }
159
160#define PASSPHRASE_MAXLEN 512
161
d4de2d2a 162struct PassphraseProcStruct {
163 char *passphrase;
164 char *comment;
165};
166
0959acf7 167static tree234 *passphrases = NULL;
168
169/*
170 * After processing a list of filenames, we want to forget the
171 * passphrases.
172 */
173static void forget_passphrases(void)
174{
0959acf7 175 while (count234(passphrases) > 0) {
176 char *pp = index234(passphrases, 0);
177 memset(pp, 0, strlen(pp));
178 delpos234(passphrases, 0);
179 free(pp);
180 }
181}
182
5c58ad2d 183/*
5af3a3b6 184 * Dialog-box function for the Licence box.
185 */
32874aea 186static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
187 WPARAM wParam, LPARAM lParam)
188{
5af3a3b6 189 switch (msg) {
190 case WM_INITDIALOG:
191 return 1;
192 case WM_COMMAND:
193 switch (LOWORD(wParam)) {
194 case IDOK:
32874aea 195 EndDialog(hwnd, 1);
5af3a3b6 196 return 0;
197 }
198 return 0;
199 case WM_CLOSE:
200 EndDialog(hwnd, 1);
201 return 0;
202 }
203 return 0;
204}
205
206/*
207 * Dialog-box function for the About box.
208 */
32874aea 209static int CALLBACK AboutProc(HWND hwnd, UINT msg,
210 WPARAM wParam, LPARAM lParam)
211{
5af3a3b6 212 switch (msg) {
213 case WM_INITDIALOG:
32874aea 214 SetDlgItemText(hwnd, 100, ver);
5af3a3b6 215 return 1;
216 case WM_COMMAND:
217 switch (LOWORD(wParam)) {
218 case IDOK:
219 aboutbox = NULL;
32874aea 220 DestroyWindow(hwnd);
5af3a3b6 221 return 0;
222 case 101:
223 EnableWindow(hwnd, 0);
32874aea 224 DialogBox(instance, MAKEINTRESOURCE(214), NULL, LicenceProc);
5af3a3b6 225 EnableWindow(hwnd, 1);
32874aea 226 SetActiveWindow(hwnd);
5af3a3b6 227 return 0;
228 }
229 return 0;
230 case WM_CLOSE:
231 aboutbox = NULL;
32874aea 232 DestroyWindow(hwnd);
5af3a3b6 233 return 0;
234 }
235 return 0;
236}
237
cb5ca813 238static HWND passphrase_box;
239
5af3a3b6 240/*
5c58ad2d 241 * Dialog-box function for the passphrase box.
242 */
243static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
32874aea 244 WPARAM wParam, LPARAM lParam)
245{
8e1feb27 246 static char *passphrase = NULL;
d4de2d2a 247 struct PassphraseProcStruct *p;
5c58ad2d 248
249 switch (msg) {
250 case WM_INITDIALOG:
cb5ca813 251 passphrase_box = hwnd;
45cebe79 252 /*
253 * Centre the window.
254 */
255 { /* centre the window */
256 RECT rs, rd;
257 HWND hw;
258
259 hw = GetDesktopWindow();
32874aea 260 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
261 MoveWindow(hwnd,
262 (rs.right + rs.left + rd.left - rd.right) / 2,
263 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
264 rd.right - rd.left, rd.bottom - rd.top, TRUE);
45cebe79 265 }
266
32874aea 267 SetForegroundWindow(hwnd);
268 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
269 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
270 p = (struct PassphraseProcStruct *) lParam;
271 passphrase = p->passphrase;
272 if (p->comment)
273 SetDlgItemText(hwnd, 101, p->comment);
274 *passphrase = 0;
275 SetDlgItemText(hwnd, 102, passphrase);
276 return 0;
5c58ad2d 277 case WM_COMMAND:
278 switch (LOWORD(wParam)) {
279 case IDOK:
280 if (*passphrase)
32874aea 281 EndDialog(hwnd, 1);
5c58ad2d 282 else
32874aea 283 MessageBeep(0);
5c58ad2d 284 return 0;
285 case IDCANCEL:
32874aea 286 EndDialog(hwnd, 0);
5c58ad2d 287 return 0;
32874aea 288 case 102: /* edit box */
8e1feb27 289 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
32874aea 290 GetDlgItemText(hwnd, 102, passphrase,
291 PASSPHRASE_MAXLEN - 1);
292 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
293 }
294 return 0;
5c58ad2d 295 }
296 return 0;
297 case WM_CLOSE:
32874aea 298 EndDialog(hwnd, 0);
5c58ad2d 299 return 0;
300 }
301 return 0;
302}
303
304/*
7bedb13c 305 * Warn about the obsolescent key file format.
306 */
307void old_keyfile_warning(void)
308{
309 static const char mbtitle[] = "PuTTY Key File Warning";
310 static const char message[] =
311 "You are loading an SSH 2 private key which has an\n"
312 "old version of the file format. This means your key\n"
313 "file is not fully tamperproof. Future versions of\n"
314 "PuTTY may stop supporting this private key format,\n"
315 "so we recommend you convert your key to the new\n"
316 "format.\n"
317 "\n"
318 "You can perform this conversion by loading the key\n"
319 "into PuTTYgen and then saving it again.";
320
321 MessageBox(NULL, message, mbtitle, MB_OK);
322}
323
324/*
3c0b3d06 325 * Update the visible key list.
326 */
32874aea 327static void keylist_update(void)
328{
45cebe79 329 struct RSAKey *rkey;
330 struct ssh2_userkey *skey;
d2371c81 331 int i;
3c0b3d06 332
333 if (keylist) {
32874aea 334 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
335 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
336 char listentry[512], *p;
337 /*
338 * Replace two spaces in the fingerprint with tabs, for
339 * nice alignment in the box.
340 */
45cebe79 341 strcpy(listentry, "ssh1\t");
32874aea 342 p = listentry + strlen(listentry);
343 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
344 p = strchr(listentry, ' ');
345 if (p)
346 *p = '\t';
347 p = strchr(listentry, ' ');
348 if (p)
349 *p = '\t';
350 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
351 0, (LPARAM) listentry);
352 }
353 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
354 char listentry[512], *p;
45cebe79 355 int len;
32874aea 356 /*
357 * Replace two spaces in the fingerprint with tabs, for
358 * nice alignment in the box.
359 */
45cebe79 360 p = skey->alg->fingerprint(skey->data);
361 strncpy(listentry, p, sizeof(listentry));
32874aea 362 p = strchr(listentry, ' ');
363 if (p)
364 *p = '\t';
365 p = strchr(listentry, ' ');
366 if (p)
367 *p = '\t';
45cebe79 368 len = strlen(listentry);
32874aea 369 if (len < sizeof(listentry) - 2) {
45cebe79 370 listentry[len] = '\t';
32874aea 371 strncpy(listentry + len + 1, skey->comment,
372 sizeof(listentry) - len - 1);
45cebe79 373 }
32874aea 374 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
375 (LPARAM) listentry);
376 }
377 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
3c0b3d06 378 }
379}
380
381/*
5c58ad2d 382 * This function loads a key from a file and adds it.
383 */
32874aea 384static void add_keyfile(char *filename)
385{
5c58ad2d 386 char passphrase[PASSPHRASE_MAXLEN];
2d466ffd 387 struct RSAKey *rkey = NULL;
388 struct ssh2_userkey *skey = NULL;
5c58ad2d 389 int needs_pass;
390 int ret;
391 int attempts;
d4de2d2a 392 char *comment;
393 struct PassphraseProcStruct pps;
231ee168 394 int type;
0959acf7 395 int original_pass;
396
231ee168 397 type = key_type(filename);
398 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
399 char msg[256];
400 sprintf(msg, "Couldn't load this key (%s)", key_type_to_str(type));
401 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONERROR);
32874aea 402 return;
45cebe79 403 }
5c58ad2d 404
3f2d010c 405 /*
406 * See if the key is already loaded (in the primary Pageant,
407 * which may or may not be us).
408 */
409 {
410 void *blob;
411 unsigned char *keylist, *p;
412 int i, nkeys, bloblen;
413
231ee168 414 if (type == SSH_KEYTYPE_SSH1) {
3f2d010c 415 if (!rsakey_pubblob(filename, &blob, &bloblen)) {
416 MessageBox(NULL, "Couldn't load private key.", APPNAME,
417 MB_OK | MB_ICONERROR);
418 return;
419 }
420 keylist = get_keylist1();
421 } else {
422 unsigned char *blob2;
423 blob = ssh2_userkey_loadpub(filename, NULL, &bloblen);
424 if (!blob) {
425 MessageBox(NULL, "Couldn't load private key.", APPNAME,
426 MB_OK | MB_ICONERROR);
427 return;
428 }
429 /* For our purposes we want the blob prefixed with its length */
430 blob2 = smalloc(bloblen+4);
431 PUT_32BIT(blob2, bloblen);
432 memcpy(blob2 + 4, blob, bloblen);
433 sfree(blob);
434 blob = blob2;
435
436 keylist = get_keylist2();
437 }
438 if (keylist) {
439 nkeys = GET_32BIT(keylist);
440 p = keylist + 4;
441
442 for (i = 0; i < nkeys; i++) {
443 if (!memcmp(blob, p, bloblen)) {
444 /* Key is already present; we can now leave. */
445 sfree(keylist);
446 sfree(blob);
447 return;
448 }
449 /* Now skip over public blob */
231ee168 450 if (type == SSH_KEYTYPE_SSH1)
3f2d010c 451 p += rsa_public_blob_len(p);
452 else
453 p += 4 + GET_32BIT(p);
454 /* Now skip over comment field */
455 p += 4 + GET_32BIT(p);
456 }
457
458 sfree(keylist);
459 }
460
461 sfree(blob);
462 }
463
231ee168 464 if (type == SSH_KEYTYPE_SSH1)
45cebe79 465 needs_pass = rsakey_encrypted(filename, &comment);
466 else
467 needs_pass = ssh2_userkey_encrypted(filename, &comment);
5c58ad2d 468 attempts = 0;
231ee168 469 if (type == SSH_KEYTYPE_SSH1)
45cebe79 470 rkey = smalloc(sizeof(*rkey));
d4de2d2a 471 pps.passphrase = passphrase;
472 pps.comment = comment;
0959acf7 473 original_pass = 0;
5c58ad2d 474 do {
32874aea 475 if (needs_pass) {
0959acf7 476 /* try all the remembered passphrases first */
477 char *pp = index234(passphrases, attempts);
478 if(pp) {
479 strcpy(passphrase, pp);
480 } else {
481 int dlgret;
482 original_pass = 1;
483 dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
484 NULL, PassphraseProc, (LPARAM) & pps);
485 passphrase_box = NULL;
486 if (!dlgret) {
487 if (comment)
488 sfree(comment);
231ee168 489 if (type == SSH_KEYTYPE_SSH1)
0959acf7 490 sfree(rkey);
491 return; /* operation cancelled */
492 }
32874aea 493 }
494 } else
495 *passphrase = '\0';
231ee168 496 if (type == SSH_KEYTYPE_SSH1)
45cebe79 497 ret = loadrsakey(filename, rkey, passphrase);
498 else {
499 skey = ssh2_load_userkey(filename, passphrase);
500 if (skey == SSH2_WRONG_PASSPHRASE)
501 ret = -1;
502 else if (!skey)
503 ret = 0;
504 else
505 ret = 1;
506 }
32874aea 507 attempts++;
5c58ad2d 508 } while (ret == -1);
0959acf7 509
510 /* if they typed in an ok passphrase, remember it */
511 if(original_pass && ret) {
512 char *pp = dupstr(passphrase);
513 addpos234(passphrases, pp, 0);
514 }
515
32874aea 516 if (comment)
517 sfree(comment);
5c58ad2d 518 if (ret == 0) {
32874aea 519 MessageBox(NULL, "Couldn't load private key.", APPNAME,
520 MB_OK | MB_ICONERROR);
231ee168 521 if (type == SSH_KEYTYPE_SSH1)
45cebe79 522 sfree(rkey);
32874aea 523 return;
5c58ad2d 524 }
231ee168 525 if (type == SSH_KEYTYPE_SSH1) {
ddecd643 526 if (already_running) {
527 unsigned char *request, *response;
2d466ffd 528 void *vresponse;
ddecd643 529 int reqlen, clen, resplen;
530
ddecd643 531 clen = strlen(rkey->comment);
ddecd643 532
533 reqlen = 4 + 1 + /* length, message type */
534 4 + /* bit count */
535 ssh1_bignum_length(rkey->modulus) +
536 ssh1_bignum_length(rkey->exponent) +
537 ssh1_bignum_length(rkey->private_exponent) +
538 ssh1_bignum_length(rkey->iqmp) +
539 ssh1_bignum_length(rkey->p) +
32874aea 540 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
ddecd643 541 ;
ddecd643 542
543 request = smalloc(reqlen);
ddecd643 544
ddecd643 545 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
ddecd643 546 reqlen = 5;
32874aea 547 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
ddecd643 548 reqlen += 4;
32874aea 549 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
550 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
551 reqlen +=
552 ssh1_write_bignum(request + reqlen,
553 rkey->private_exponent);
554 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
555 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
556 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
557 PUT_32BIT(request + reqlen, clen);
558 memcpy(request + reqlen + 4, rkey->comment, clen);
559 reqlen += 4 + clen;
560 PUT_32BIT(request, reqlen - 4);
ddecd643 561
2d466ffd 562 agent_query(request, reqlen, &vresponse, &resplen);
563 response = vresponse;
ddecd643 564 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
565 MessageBox(NULL, "The already running Pageant "
566 "refused to add the key.", APPNAME,
567 MB_OK | MB_ICONERROR);
3f2d010c 568
569 sfree(request);
570 sfree(response);
ddecd643 571 } else {
572 if (add234(rsakeys, rkey) != rkey)
573 sfree(rkey); /* already present, don't waste RAM */
574 }
45cebe79 575 } else {
ddecd643 576 if (already_running) {
577 unsigned char *request, *response;
2d466ffd 578 void *vresponse;
ddecd643 579 int reqlen, alglen, clen, keybloblen, resplen;
ddecd643 580 alglen = strlen(skey->alg->name);
ddecd643 581 clen = strlen(skey->comment);
ddecd643 582
ddecd643 583 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
ddecd643 584
ddecd643 585 reqlen = 4 + 1 + /* length, message type */
586 4 + alglen + /* algorithm name */
587 keybloblen + /* key data */
588 4 + clen /* comment */
589 ;
ddecd643 590
ddecd643 591 request = smalloc(reqlen);
ddecd643 592
593 request[4] = SSH2_AGENTC_ADD_IDENTITY;
ddecd643 594 reqlen = 5;
32874aea 595 PUT_32BIT(request + reqlen, alglen);
ddecd643 596 reqlen += 4;
32874aea 597 memcpy(request + reqlen, skey->alg->name, alglen);
ddecd643 598 reqlen += alglen;
ddecd643 599 reqlen += skey->alg->openssh_fmtkey(skey->data,
32874aea 600 request + reqlen,
601 keybloblen);
602 PUT_32BIT(request + reqlen, clen);
603 memcpy(request + reqlen + 4, skey->comment, clen);
604 PUT_32BIT(request, reqlen - 4);
605 reqlen += clen + 4;
ddecd643 606
2d466ffd 607 agent_query(request, reqlen, &vresponse, &resplen);
608 response = vresponse;
ddecd643 609 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
cc93d5e6 610 MessageBox(NULL, "The already running Pageant "
ddecd643 611 "refused to add the key.", APPNAME,
612 MB_OK | MB_ICONERROR);
3f2d010c 613
614 sfree(request);
615 sfree(response);
ddecd643 616 } else {
617 if (add234(ssh2keys, skey) != skey) {
618 skey->alg->freekey(skey->data);
619 sfree(skey); /* already present, don't waste RAM */
620 }
45cebe79 621 }
622 }
5c58ad2d 623}
624
625/*
3f2d010c 626 * Create an SSH1 key list in a malloc'ed buffer; return its
627 * length.
628 */
629static void *make_keylist1(int *length)
630{
631 int i, nkeys, len;
632 struct RSAKey *key;
633 unsigned char *blob, *p, *ret;
634 int bloblen;
635
636 /*
637 * Count up the number and length of keys we hold.
638 */
639 len = 4;
640 nkeys = 0;
641 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
642 nkeys++;
643 blob = rsa_public_blob(key, &bloblen);
644 len += bloblen;
645 sfree(blob);
646 len += 4 + strlen(key->comment);
647 }
648
649 /* Allocate the buffer. */
650 p = ret = smalloc(len);
651 if (length) *length = len;
652
653 PUT_32BIT(p, nkeys);
654 p += 4;
655 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
656 blob = rsa_public_blob(key, &bloblen);
657 memcpy(p, blob, bloblen);
658 p += bloblen;
659 sfree(blob);
660 PUT_32BIT(p, strlen(key->comment));
661 memcpy(p + 4, key->comment, strlen(key->comment));
662 p += 4 + strlen(key->comment);
663 }
664
665 assert(p - ret == len);
666 return ret;
667}
668
669/*
670 * Create an SSH2 key list in a malloc'ed buffer; return its
671 * length.
672 */
673static void *make_keylist2(int *length)
674{
675 struct ssh2_userkey *key;
676 int i, len, nkeys;
677 unsigned char *blob, *p, *ret;
678 int bloblen;
679
680 /*
681 * Count up the number and length of keys we hold.
682 */
683 len = 4;
684 nkeys = 0;
685 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
686 nkeys++;
687 len += 4; /* length field */
688 blob = key->alg->public_blob(key->data, &bloblen);
689 len += bloblen;
690 sfree(blob);
691 len += 4 + strlen(key->comment);
692 }
693
694 /* Allocate the buffer. */
695 p = ret = smalloc(len);
696 if (length) *length = len;
697
698 /*
699 * Packet header is the obvious five bytes, plus four
700 * bytes for the key count.
701 */
702 PUT_32BIT(p, nkeys);
703 p += 4;
704 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
705 blob = key->alg->public_blob(key->data, &bloblen);
706 PUT_32BIT(p, bloblen);
707 p += 4;
708 memcpy(p, blob, bloblen);
709 p += bloblen;
710 sfree(blob);
711 PUT_32BIT(p, strlen(key->comment));
712 memcpy(p + 4, key->comment, strlen(key->comment));
713 p += 4 + strlen(key->comment);
714 }
715
716 assert(p - ret == len);
717 return ret;
718}
719
720/*
721 * Acquire a keylist1 from the primary Pageant; this means either
722 * calling make_keylist1 (if that's us) or sending a message to the
723 * primary Pageant (if it's not).
724 */
725static void *get_keylist1(void)
726{
727 void *ret;
728
729 if (already_running) {
730 unsigned char request[5], *response;
731 void *vresponse;
732 int resplen;
733 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
734 PUT_32BIT(request, 4);
735
736 agent_query(request, 5, &vresponse, &resplen);
737 response = vresponse;
738 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)
739 return NULL;
740
741 ret = smalloc(resplen-5);
742 memcpy(ret, response+5, resplen-5);
743 sfree(response);
744 } else {
745 ret = make_keylist1(NULL);
746 }
747 return ret;
748}
749
750/*
751 * Acquire a keylist2 from the primary Pageant; this means either
752 * calling make_keylist2 (if that's us) or sending a message to the
753 * primary Pageant (if it's not).
754 */
755static void *get_keylist2(void)
756{
757 void *ret;
758
759 if (already_running) {
760 unsigned char request[5], *response;
761 void *vresponse;
762 int resplen;
763
764 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
765 PUT_32BIT(request, 4);
766
767 agent_query(request, 5, &vresponse, &resplen);
768 response = vresponse;
769 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)
770 return NULL;
771
772 ret = smalloc(resplen-5);
773 memcpy(ret, response+5, resplen-5);
774 sfree(response);
775 } else {
776 ret = make_keylist2(NULL);
777 }
778 return ret;
779}
780
781/*
5c58ad2d 782 * This is the main agent function that answers messages.
783 */
32874aea 784static void answer_msg(void *msg)
785{
d70f60ae 786 unsigned char *p = msg;
787 unsigned char *ret = msg;
5c58ad2d 788 int type;
789
5c58ad2d 790 /*
791 * Get the message type.
792 */
793 type = p[4];
794
795 p += 5;
5c58ad2d 796 switch (type) {
45cebe79 797 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
32874aea 798 /*
799 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
800 */
801 {
3f2d010c 802 int len;
803 void *keylist;
32874aea 804
32874aea 805 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
3f2d010c 806 keylist = make_keylist1(&len);
807 if (len + 5 > AGENT_MAX_MSGLEN) {
808 sfree(keylist);
809 goto failure;
32874aea 810 }
3f2d010c 811 PUT_32BIT(ret, len + 1);
812 memcpy(ret + 5, keylist, len);
813 sfree(keylist);
32874aea 814 }
815 break;
45cebe79 816 case SSH2_AGENTC_REQUEST_IDENTITIES:
32874aea 817 /*
818 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
819 */
820 {
3f2d010c 821 int len;
822 void *keylist;
32874aea 823
32874aea 824 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
3f2d010c 825 keylist = make_keylist2(&len);
826 if (len + 5 > AGENT_MAX_MSGLEN) {
827 sfree(keylist);
828 goto failure;
32874aea 829 }
3f2d010c 830 PUT_32BIT(ret, len + 1);
831 memcpy(ret + 5, keylist, len);
832 sfree(keylist);
32874aea 833 }
834 break;
45cebe79 835 case SSH1_AGENTC_RSA_CHALLENGE:
32874aea 836 /*
837 * Reply with either SSH1_AGENT_RSA_RESPONSE or
838 * SSH_AGENT_FAILURE, depending on whether we have that key
839 * or not.
840 */
841 {
842 struct RSAKey reqkey, *key;
843 Bignum challenge, response;
844 unsigned char response_source[48], response_md5[16];
845 struct MD5Context md5c;
846 int i, len;
847
848 p += 4;
849 p += ssh1_read_bignum(p, &reqkey.exponent);
850 p += ssh1_read_bignum(p, &reqkey.modulus);
851 p += ssh1_read_bignum(p, &challenge);
852 memcpy(response_source + 32, p, 16);
853 p += 16;
854 if (GET_32BIT(p) != 1 ||
855 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
856 freebn(reqkey.exponent);
857 freebn(reqkey.modulus);
858 freebn(challenge);
859 goto failure;
860 }
861 response = rsadecrypt(challenge, key);
862 for (i = 0; i < 32; i++)
863 response_source[i] = bignum_byte(response, 31 - i);
864
865 MD5Init(&md5c);
866 MD5Update(&md5c, response_source, 48);
867 MD5Final(response_md5, &md5c);
868 memset(response_source, 0, 48); /* burn the evidence */
869 freebn(response); /* and that evidence */
870 freebn(challenge); /* yes, and that evidence */
871 freebn(reqkey.exponent); /* and free some memory ... */
872 freebn(reqkey.modulus); /* ... while we're at it. */
873
874 /*
875 * Packet is the obvious five byte header, plus sixteen
876 * bytes of MD5.
877 */
878 len = 5 + 16;
879 PUT_32BIT(ret, len - 4);
880 ret[4] = SSH1_AGENT_RSA_RESPONSE;
881 memcpy(ret + 5, response_md5, 16);
882 }
883 break;
45cebe79 884 case SSH2_AGENTC_SIGN_REQUEST:
32874aea 885 /*
5c72ca61 886 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
32874aea 887 * SSH_AGENT_FAILURE, depending on whether we have that key
888 * or not.
889 */
45cebe79 890 {
891 struct ssh2_userkey *key;
892 struct blob b;
893 unsigned char *data, *signature;
894 int datalen, siglen, len;
895
896 b.len = GET_32BIT(p);
897 p += 4;
898 b.blob = p;
899 p += b.len;
900 datalen = GET_32BIT(p);
901 p += 4;
902 data = p;
903 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
904 if (!key)
905 goto failure;
906 signature = key->alg->sign(key->data, data, datalen, &siglen);
32874aea 907 len = 5 + 4 + siglen;
908 PUT_32BIT(ret, len - 4);
909 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
910 PUT_32BIT(ret + 5, siglen);
911 memcpy(ret + 5 + 4, signature, siglen);
45cebe79 912 sfree(signature);
913 }
914 break;
915 case SSH1_AGENTC_ADD_RSA_IDENTITY:
32874aea 916 /*
917 * Add to the list and return SSH_AGENT_SUCCESS, or
918 * SSH_AGENT_FAILURE if the key was malformed.
919 */
920 {
921 struct RSAKey *key;
922 char *comment;
080954b8 923 int commentlen;
32874aea 924 key = smalloc(sizeof(struct RSAKey));
28edeffe 925 memset(key, 0, sizeof(struct RSAKey));
32874aea 926 p += makekey(p, key, NULL, 1);
927 p += makeprivate(p, key);
080954b8 928 p += ssh1_read_bignum(p, &key->iqmp); /* p^-1 mod q */
929 p += ssh1_read_bignum(p, &key->p); /* p */
930 p += ssh1_read_bignum(p, &key->q); /* q */
931 commentlen = GET_32BIT(p);
932 comment = smalloc(commentlen+1);
32874aea 933 if (comment) {
080954b8 934 memcpy(comment, p + 4, commentlen);
935 comment[commentlen] = '\0';
32874aea 936 key->comment = comment;
937 }
938 PUT_32BIT(ret, 1);
939 ret[4] = SSH_AGENT_FAILURE;
940 if (add234(rsakeys, key) == key) {
941 keylist_update();
942 ret[4] = SSH_AGENT_SUCCESS;
943 } else {
944 freersakey(key);
945 sfree(key);
946 }
947 }
948 break;
45cebe79 949 case SSH2_AGENTC_ADD_IDENTITY:
32874aea 950 /*
951 * Add to the list and return SSH_AGENT_SUCCESS, or
952 * SSH_AGENT_FAILURE if the key was malformed.
953 */
954 {
955 struct ssh2_userkey *key;
956 char *comment, *alg;
45cebe79 957 int alglen, commlen;
958 int bloblen;
959
960 key = smalloc(sizeof(struct ssh2_userkey));
961
32874aea 962 alglen = GET_32BIT(p);
963 p += 4;
964 alg = p;
965 p += alglen;
45cebe79 966 /* Add further algorithm names here. */
967 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
968 key->alg = &ssh_rsa;
5c72ca61 969 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
970 key->alg = &ssh_dss;
45cebe79 971 else {
972 sfree(key);
973 goto failure;
974 }
975
32874aea 976 bloblen =
977 GET_32BIT((unsigned char *) msg) - (p -
978 (unsigned char *) msg -
979 4);
45cebe79 980 key->data = key->alg->openssh_createkey(&p, &bloblen);
981 if (!key->data) {
982 sfree(key);
983 goto failure;
984 }
32874aea 985 commlen = GET_32BIT(p);
986 p += 4;
45cebe79 987
32874aea 988 comment = smalloc(commlen + 1);
989 if (comment) {
990 memcpy(comment, p, commlen);
45cebe79 991 comment[commlen] = '\0';
32874aea 992 }
45cebe79 993 key->comment = comment;
994
32874aea 995 PUT_32BIT(ret, 1);
996 ret[4] = SSH_AGENT_FAILURE;
997 if (add234(ssh2keys, key) == key) {
998 keylist_update();
999 ret[4] = SSH_AGENT_SUCCESS;
1000 } else {
45cebe79 1001 key->alg->freekey(key->data);
1002 sfree(key->comment);
32874aea 1003 sfree(key);
1004 }
1005 }
1006 break;
45cebe79 1007 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
32874aea 1008 /*
1009 * Remove from the list and return SSH_AGENT_SUCCESS, or
1010 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1011 * start with.
1012 */
1013 {
1014 struct RSAKey reqkey, *key;
1015
1016 p += makekey(p, &reqkey, NULL, 0);
1017 key = find234(rsakeys, &reqkey, NULL);
1018 freebn(reqkey.exponent);
1019 freebn(reqkey.modulus);
1020 PUT_32BIT(ret, 1);
1021 ret[4] = SSH_AGENT_FAILURE;
1022 if (key) {
1023 del234(rsakeys, key);
1024 keylist_update();
1025 freersakey(key);
45cebe79 1026 sfree(key);
32874aea 1027 ret[4] = SSH_AGENT_SUCCESS;
1028 }
1029 }
1030 break;
45cebe79 1031 case SSH2_AGENTC_REMOVE_IDENTITY:
32874aea 1032 /*
1033 * Remove from the list and return SSH_AGENT_SUCCESS, or
1034 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1035 * start with.
1036 */
1037 {
1038 struct ssh2_userkey *key;
45cebe79 1039 struct blob b;
1040
1041 b.len = GET_32BIT(p);
1042 p += 4;
1043 b.blob = p;
1044 p += b.len;
1045 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1046 if (!key)
1047 goto failure;
1048
32874aea 1049 PUT_32BIT(ret, 1);
1050 ret[4] = SSH_AGENT_FAILURE;
1051 if (key) {
1052 del234(ssh2keys, key);
1053 keylist_update();
45cebe79 1054 key->alg->freekey(key->data);
1055 sfree(key);
32874aea 1056 ret[4] = SSH_AGENT_SUCCESS;
1057 }
1058 }
1059 break;
45cebe79 1060 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
32874aea 1061 /*
1062 * Remove all SSH1 keys. Always returns success.
1063 */
1064 {
45cebe79 1065 struct RSAKey *rkey;
45cebe79 1066
32874aea 1067 while ((rkey = index234(rsakeys, 0)) != NULL) {
1068 del234(rsakeys, rkey);
45cebe79 1069 freersakey(rkey);
1070 sfree(rkey);
32874aea 1071 }
45cebe79 1072 keylist_update();
1073
1074 PUT_32BIT(ret, 1);
1075 ret[4] = SSH_AGENT_SUCCESS;
32874aea 1076 }
1077 break;
45cebe79 1078 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
32874aea 1079 /*
1080 * Remove all SSH2 keys. Always returns success.
1081 */
1082 {
1083 struct ssh2_userkey *skey;
1084
1085 while ((skey = index234(ssh2keys, 0)) != NULL) {
1086 del234(ssh2keys, skey);
45cebe79 1087 skey->alg->freekey(skey->data);
1088 sfree(skey);
32874aea 1089 }
45cebe79 1090 keylist_update();
1091
1092 PUT_32BIT(ret, 1);
1093 ret[4] = SSH_AGENT_SUCCESS;
32874aea 1094 }
1095 break;
5c58ad2d 1096 default:
32874aea 1097 failure:
1098 /*
1099 * Unrecognised message. Return SSH_AGENT_FAILURE.
1100 */
1101 PUT_32BIT(ret, 1);
1102 ret[4] = SSH_AGENT_FAILURE;
1103 break;
5c58ad2d 1104 }
5c58ad2d 1105}
1106
1107/*
1108 * Key comparison function for the 2-3-4 tree of RSA keys.
1109 */
32874aea 1110static int cmpkeys_rsa(void *av, void *bv)
1111{
1112 struct RSAKey *a = (struct RSAKey *) av;
1113 struct RSAKey *b = (struct RSAKey *) bv;
5c58ad2d 1114 Bignum am, bm;
1115 int alen, blen;
1116
1117 am = a->modulus;
1118 bm = b->modulus;
1119 /*
1120 * Compare by length of moduli.
1121 */
ddecd643 1122 alen = bignum_bitcount(am);
1123 blen = bignum_bitcount(bm);
32874aea 1124 if (alen > blen)
1125 return +1;
1126 else if (alen < blen)
1127 return -1;
5c58ad2d 1128 /*
1129 * Now compare by moduli themselves.
1130 */
32874aea 1131 alen = (alen + 7) / 8; /* byte count */
5c58ad2d 1132 while (alen-- > 0) {
32874aea 1133 int abyte, bbyte;
1134 abyte = bignum_byte(am, alen);
1135 bbyte = bignum_byte(bm, alen);
1136 if (abyte > bbyte)
1137 return +1;
1138 else if (abyte < bbyte)
1139 return -1;
5c58ad2d 1140 }
1141 /*
1142 * Give up.
1143 */
1144 return 0;
1145}
1146
45cebe79 1147/*
1148 * Key comparison function for the 2-3-4 tree of SSH2 keys.
1149 */
32874aea 1150static int cmpkeys_ssh2(void *av, void *bv)
1151{
1152 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1153 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
45cebe79 1154 int i;
1155 int alen, blen;
1156 unsigned char *ablob, *bblob;
1157 int c;
32874aea 1158
45cebe79 1159 /*
1160 * Compare purely by public blob.
1161 */
1162 ablob = a->alg->public_blob(a->data, &alen);
1163 bblob = b->alg->public_blob(b->data, &blen);
1164
1165 c = 0;
1166 for (i = 0; i < alen && i < blen; i++) {
1167 if (ablob[i] < bblob[i]) {
32874aea 1168 c = -1;
1169 break;
45cebe79 1170 } else if (ablob[i] > bblob[i]) {
32874aea 1171 c = +1;
1172 break;
45cebe79 1173 }
1174 }
32874aea 1175 if (c == 0 && i < alen)
1176 c = +1; /* a is longer */
1177 if (c == 0 && i < blen)
1178 c = -1; /* a is longer */
45cebe79 1179
1180 sfree(ablob);
1181 sfree(bblob);
1182
1183 return c;
1184}
1185
1186/*
1187 * Key comparison function for looking up a blob in the 2-3-4 tree
1188 * of SSH2 keys.
1189 */
32874aea 1190static int cmpkeys_ssh2_asymm(void *av, void *bv)
1191{
1192 struct blob *a = (struct blob *) av;
1193 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
45cebe79 1194 int i;
1195 int alen, blen;
1196 unsigned char *ablob, *bblob;
1197 int c;
32874aea 1198
45cebe79 1199 /*
1200 * Compare purely by public blob.
1201 */
1202 ablob = a->blob;
1203 alen = a->len;
1204 bblob = b->alg->public_blob(b->data, &blen);
1205
1206 c = 0;
1207 for (i = 0; i < alen && i < blen; i++) {
1208 if (ablob[i] < bblob[i]) {
32874aea 1209 c = -1;
1210 break;
45cebe79 1211 } else if (ablob[i] > bblob[i]) {
32874aea 1212 c = +1;
1213 break;
45cebe79 1214 }
1215 }
32874aea 1216 if (c == 0 && i < alen)
1217 c = +1; /* a is longer */
1218 if (c == 0 && i < blen)
1219 c = -1; /* a is longer */
45cebe79 1220
1221 sfree(bblob);
1222
1223 return c;
1224}
1225
5c58ad2d 1226/*
ab162329 1227 * Prompt for a key file to add, and add it.
1228 */
32874aea 1229static void prompt_add_keyfile(void)
1230{
ab162329 1231 OPENFILENAME of;
1232 char filename[FILENAME_MAX];
0959acf7 1233 char *filelist = smalloc(8192);
1234 char *filewalker;
1235 int n, dirlen;
1236
ab162329 1237 memset(&of, 0, sizeof(of));
1238#ifdef OPENFILENAME_SIZE_VERSION_400
1239 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
1240#else
1241 of.lStructSize = sizeof(of);
1242#endif
ecea795f 1243 of.hwndOwner = main_hwnd;
e2210ffc 1244 of.lpstrFilter = "PuTTY Private Key Files\0*.PPK\0AllFiles\0*\0\0\0";
ab162329 1245 of.lpstrCustomFilter = NULL;
1246 of.nFilterIndex = 1;
0959acf7 1247 of.lpstrFile = filelist;
1248 *filelist = '\0';
1249 of.nMaxFile = FILENAME_MAX;
ab162329 1250 of.lpstrFileTitle = NULL;
1251 of.lpstrInitialDir = NULL;
1252 of.lpstrTitle = "Select Private Key File";
0959acf7 1253 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
ab162329 1254 if (GetOpenFileName(&of)) {
0959acf7 1255 if(strlen(filelist) > of.nFileOffset)
1256 /* Only one filename returned? */
1257 add_keyfile(filelist);
1258 else {
1259 /* we are returned a bunch of strings, end to
1260 * end. first string is the directory, the
1261 * rest the filenames. terminated with an
1262 * empty string.
1263 */
1264 filewalker = filelist;
1265 dirlen = strlen(filewalker);
1266 if(dirlen > FILENAME_MAX - 8) return;
1267 memcpy(filename, filewalker, dirlen);
1268
1269 filewalker += dirlen + 1;
1270 filename[dirlen++] = '\\';
1271
1272 /* then go over names one by one */
1273 for(;;) {
1274 n = strlen(filewalker) + 1;
1275 /* end of the list */
1276 if(n == 1)
1277 break;
1278 /* too big, shouldn't happen */
1279 if(n + dirlen > FILENAME_MAX)
1280 break;
1281
1282 memcpy(filename + dirlen, filewalker, n);
1283 filewalker += n;
1284
1285 add_keyfile(filename);
1286 }
1287 }
1288
32874aea 1289 keylist_update();
0959acf7 1290 forget_passphrases();
ab162329 1291 }
0959acf7 1292 sfree(filelist);
ab162329 1293}
1294
1295/*
5c58ad2d 1296 * Dialog-box function for the key list box.
1297 */
1298static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
32874aea 1299 WPARAM wParam, LPARAM lParam)
1300{
45cebe79 1301 struct RSAKey *rkey;
1302 struct ssh2_userkey *skey;
5c58ad2d 1303
1304 switch (msg) {
1305 case WM_INITDIALOG:
45cebe79 1306 /*
1307 * Centre the window.
1308 */
1309 { /* centre the window */
1310 RECT rs, rd;
1311 HWND hw;
1312
1313 hw = GetDesktopWindow();
32874aea 1314 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1315 MoveWindow(hwnd,
1316 (rs.right + rs.left + rd.left - rd.right) / 2,
1317 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1318 rd.right - rd.left, rd.bottom - rd.top, TRUE);
45cebe79 1319 }
1320
ecea795f 1321 if (help_path)
1322 SetWindowLong(hwnd, GWL_EXSTYLE,
1323 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
1324 else {
1325 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1326 if (item)
1327 DestroyWindow(item);
1328 }
1329 requested_help = FALSE;
1330
32874aea 1331 keylist = hwnd;
1c2a93c4 1332 {
32874aea 1333 static int tabs[] = { 35, 60, 210 };
1334 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1335 sizeof(tabs) / sizeof(*tabs),
1336 (LPARAM) tabs);
1c2a93c4 1337 }
32874aea 1338 keylist_update();
1339 return 0;
5c58ad2d 1340 case WM_COMMAND:
1341 switch (LOWORD(wParam)) {
1342 case IDOK:
1343 case IDCANCEL:
32874aea 1344 keylist = NULL;
1345 DestroyWindow(hwnd);
1346 return 0;
1347 case 101: /* add key */
5c58ad2d 1348 if (HIWORD(wParam) == BN_CLICKED ||
1349 HIWORD(wParam) == BN_DOUBLECLICKED) {
cb5ca813 1350 if (passphrase_box) {
1351 MessageBeep(MB_ICONERROR);
1352 SetForegroundWindow(passphrase_box);
1353 break;
1354 }
32874aea 1355 prompt_add_keyfile();
1356 }
1357 return 0;
1358 case 102: /* remove key */
5c58ad2d 1359 if (HIWORD(wParam) == BN_CLICKED ||
1360 HIWORD(wParam) == BN_DOUBLECLICKED) {
d2371c81 1361 int i;
0959acf7 1362 int rCount, sCount;
1363 int *selectedArray;
1364
1365 /* our counter within the array of selected items */
1366 int itemNum;
1367
1368 /* get the number of items selected in the list */
1369 int numSelected =
1370 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1371
1372 /* none selected? that was silly */
1373 if (numSelected == 0) {
5c58ad2d 1374 MessageBeep(0);
1375 break;
1376 }
0959acf7 1377
1378 /* get item indices in an array */
1379 selectedArray = smalloc(numSelected * sizeof(int));
1380 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1381 numSelected, (WPARAM)selectedArray);
1382
1383 itemNum = numSelected - 1;
1384 rCount = count234(rsakeys);
1385 sCount = count234(ssh2keys);
1386
1387 /* go through the non-rsakeys until we've covered them all,
1388 * and/or we're out of selected items to check. note that
1389 * we go *backwards*, to avoid complications from deleting
1390 * things hence altering the offset of subsequent items
1391 */
1392 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1393 skey = index234(ssh2keys, i);
1394
1395 if (selectedArray[itemNum] == rCount + i) {
1396 del234(ssh2keys, skey);
1397 skey->alg->freekey(skey->data);
1398 sfree(skey);
1399 itemNum--;
1400 }
45cebe79 1401 }
0959acf7 1402
1403 /* do the same for the rsa keys */
1404 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1405 rkey = index234(rsakeys, i);
1406
1407 if(selectedArray[itemNum] == i) {
1408 del234(rsakeys, rkey);
1409 freersakey(rkey);
1410 sfree(rkey);
1411 itemNum--;
1412 }
1413 }
1414
1415 sfree(selectedArray);
32874aea 1416 keylist_update();
1417 }
1418 return 0;
ecea795f 1419 case 103: /* help */
1420 if (HIWORD(wParam) == BN_CLICKED ||
1421 HIWORD(wParam) == BN_DOUBLECLICKED) {
1422 if (help_path) {
1423 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1424 (DWORD)"JI(`',`pageant.general')");
1425 requested_help = TRUE;
1426 }
1427 }
1428 return 0;
5c58ad2d 1429 }
1430 return 0;
ecea795f 1431 case WM_HELP:
1432 if (help_path) {
1433 int id = ((LPHELPINFO)lParam)->iCtrlId;
1434 char *cmd = NULL;
1435 switch (id) {
1436 case 100: cmd = "JI(`',`pageant.keylist')"; break;
1437 case 101: cmd = "JI(`',`pageant.addkey')"; break;
1438 case 102: cmd = "JI(`',`pageant.remkey')"; break;
1439 }
1440 if (cmd) {
1441 WinHelp(main_hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1442 requested_help = TRUE;
1443 } else {
1444 MessageBeep(0);
1445 }
1446 }
1447 break;
5c58ad2d 1448 case WM_CLOSE:
32874aea 1449 keylist = NULL;
1450 DestroyWindow(hwnd);
5c58ad2d 1451 return 0;
1452 }
1453 return 0;
1454}
1455
15ad2e8a 1456/* Set up a system tray icon */
1457static BOOL AddTrayIcon(HWND hwnd)
1458{
1459 BOOL res;
1460 NOTIFYICONDATA tnid;
1461 HICON hicon;
1462
1463#ifdef NIM_SETVERSION
1464 tnid.uVersion = 0;
1465 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1466#endif
1467
1468 tnid.cbSize = sizeof(NOTIFYICONDATA);
1469 tnid.hWnd = hwnd;
1470 tnid.uID = 1; /* unique within this systray use */
1471 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1472 tnid.uCallbackMessage = WM_SYSTRAY;
1473 tnid.hIcon = hicon = LoadIcon(instance, MAKEINTRESOURCE(201));
1474 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1475
1476 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1477
1478 if (hicon) DestroyIcon(hicon);
1479
1480 return res;
1481}
1482
76b51e35 1483/* Update the saved-sessions menu. */
1484static void update_sessions(void)
1485{
1486 int num_entries;
1487 HKEY hkey;
1488 TCHAR buf[MAX_PATH + 1];
1489 MENUITEMINFO mii;
1490
1491 int index_key, index_menu;
1492
1493 if (!putty_path)
1494 return;
1495
1496 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1497 return;
1498
1499 for(num_entries = GetMenuItemCount(session_menu);
1500 num_entries > initial_menuitems_count;
1501 num_entries--)
1502 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1503
1504 index_key = 0;
1505 index_menu = 0;
1506
1507 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1508 TCHAR session_name[MAX_PATH + 1];
1509 unmungestr(buf, session_name, MAX_PATH);
1510 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1511 memset(&mii, 0, sizeof(mii));
1512 mii.cbSize = sizeof(mii);
1513 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1514 mii.fType = MFT_STRING;
1515 mii.fState = MFS_ENABLED;
1516 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1517 mii.dwTypeData = session_name;
1518 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1519 index_menu++;
1520 }
1521 index_key++;
1522 }
1523
1524 RegCloseKey(hkey);
1525
1526 if(index_menu == 0) {
1527 mii.cbSize = sizeof(mii);
1528 mii.fMask = MIIM_TYPE | MIIM_STATE;
1529 mii.fType = MFT_STRING;
1530 mii.fState = MFS_GRAYED;
1531 mii.dwTypeData = _T("(No sessions)");
1532 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1533 }
1534}
1535
32874aea 1536static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1537 WPARAM wParam, LPARAM lParam)
1538{
5c58ad2d 1539 int ret;
1540 static int menuinprogress;
15ad2e8a 1541 static UINT msgTaskbarCreated = 0;
5c58ad2d 1542
1543 switch (message) {
15ad2e8a 1544 case WM_CREATE:
1545 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1546 break;
1547 default:
1548 if (message==msgTaskbarCreated) {
8e3a513c 1549 /*
1550 * Explorer has been restarted, so the tray icon will
1551 * have been lost.
1552 */
1553 AddTrayIcon(hwnd);
15ad2e8a 1554 }
1555 break;
1556
5c58ad2d 1557 case WM_SYSTRAY:
32874aea 1558 if (lParam == WM_RBUTTONUP) {
1559 POINT cursorpos;
1560 GetCursorPos(&cursorpos);
1561 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1562 } else if (lParam == WM_LBUTTONDBLCLK) {
1563 /* Equivalent to IDM_VIEWKEYS. */
1564 PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
1565 }
1566 break;
5c58ad2d 1567 case WM_SYSTRAY2:
32874aea 1568 if (!menuinprogress) {
1569 menuinprogress = 1;
76b51e35 1570 update_sessions();
32874aea 1571 SetForegroundWindow(hwnd);
1572 ret = TrackPopupMenu(systray_menu,
1573 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1574 TPM_RIGHTBUTTON,
1575 wParam, lParam, 0, hwnd, NULL);
1576 menuinprogress = 0;
1577 }
1578 break;
5c58ad2d 1579 case WM_COMMAND:
1580 case WM_SYSCOMMAND:
1581 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
76b51e35 1582 case IDM_PUTTY:
1583 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1584 SW_SHOW) <= 32) {
1585 MessageBox(NULL, "Unable to execute PuTTY!",
1586 "Error", MB_OK | MB_ICONERROR);
1587 }
1588 break;
32874aea 1589 case IDM_CLOSE:
cb5ca813 1590 if (passphrase_box)
1591 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
32874aea 1592 SendMessage(hwnd, WM_CLOSE, 0, 0);
1593 break;
1594 case IDM_VIEWKEYS:
1595 if (!keylist) {
1596 keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
1597 NULL, KeyListProc);
1598 ShowWindow(keylist, SW_SHOWNORMAL);
1599 /*
1600 * Sometimes the window comes up minimised / hidden
1601 * for no obvious reason. Prevent this.
1602 */
1603 SetForegroundWindow(keylist);
1604 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1605 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1606 }
1607 break;
1608 case IDM_ADDKEY:
cb5ca813 1609 if (passphrase_box) {
1610 MessageBeep(MB_ICONERROR);
1611 SetForegroundWindow(passphrase_box);
1612 break;
1613 }
32874aea 1614 prompt_add_keyfile();
1615 break;
1616 case IDM_ABOUT:
1617 if (!aboutbox) {
1618 aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
1619 NULL, AboutProc);
1620 ShowWindow(aboutbox, SW_SHOWNORMAL);
1621 /*
1622 * Sometimes the window comes up minimised / hidden
1623 * for no obvious reason. Prevent this.
1624 */
1625 SetForegroundWindow(aboutbox);
1626 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1627 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1628 }
1629 break;
ecea795f 1630 case IDM_HELP:
1631 if (help_path) {
1632 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1633 (DWORD)"JI(`',`pageant.general')");
1634 requested_help = TRUE;
1635 }
1636 break;
76b51e35 1637 default:
1638 {
1639 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1640 MENUITEMINFO mii;
1641 TCHAR buf[MAX_PATH + 1];
1642 TCHAR param[MAX_PATH + 1];
1643 memset(&mii, 0, sizeof(mii));
1644 mii.cbSize = sizeof(mii);
1645 mii.fMask = MIIM_TYPE;
1646 mii.cch = MAX_PATH;
1647 mii.dwTypeData = buf;
1648 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1649 strcpy(param, "@");
1650 strcat(param, mii.dwTypeData);
1651 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1652 _T(""), SW_SHOW) <= 32) {
1653 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1654 MB_OK | MB_ICONERROR);
1655 }
1656 }
1657 }
1658 break;
32874aea 1659 }
1660 break;
5c58ad2d 1661 case WM_DESTROY:
ecea795f 1662 if (requested_help) {
1663 WinHelp(main_hwnd, help_path, HELP_QUIT, 0);
1664 requested_help = FALSE;
1665 }
32874aea 1666 PostQuitMessage(0);
5c58ad2d 1667 return 0;
1668 case WM_COPYDATA:
32874aea 1669 {
1670 COPYDATASTRUCT *cds;
1671 char *mapname;
1672 void *p;
2d466ffd 1673 HANDLE filemap;
1674#ifndef NO_SECURITY
1675 HANDLE proc;
32874aea 1676 PSID mapowner, procowner;
1677 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
2d466ffd 1678#endif
32874aea 1679 int ret = 0;
1680
1681 cds = (COPYDATASTRUCT *) lParam;
1682 if (cds->dwData != AGENT_COPYDATA_ID)
1683 return 0; /* not our message, mate */
1684 mapname = (char *) cds->lpData;
1685 if (mapname[cds->cbData - 1] != '\0')
1686 return 0; /* failure to be ASCIZ! */
d70f60ae 1687#ifdef DEBUG_IPC
32874aea 1688 debug(("mapname is :%s:\n", mapname));
d70f60ae 1689#endif
32874aea 1690 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
d70f60ae 1691#ifdef DEBUG_IPC
32874aea 1692 debug(("filemap is %p\n", filemap));
d70f60ae 1693#endif
32874aea 1694 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
123bc6ea 1695#ifndef NO_SECURITY
2d466ffd 1696 int rc;
32874aea 1697 if (has_security) {
1698 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1699 GetCurrentProcessId())) ==
1700 NULL) {
d70f60ae 1701#ifdef DEBUG_IPC
32874aea 1702 debug(("couldn't get handle for process\n"));
d70f60ae 1703#endif
32874aea 1704 return 0;
1705 }
1706 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1707 OWNER_SECURITY_INFORMATION,
1708 &procowner, NULL, NULL, NULL,
1709 &psd2) != ERROR_SUCCESS) {
d70f60ae 1710#ifdef DEBUG_IPC
32874aea 1711 debug(("couldn't get owner info for process\n"));
d70f60ae 1712#endif
32874aea 1713 CloseHandle(proc);
1714 return 0; /* unable to get security info */
1715 }
1716 CloseHandle(proc);
1717 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1718 OWNER_SECURITY_INFORMATION,
1719 &mapowner, NULL, NULL, NULL,
1720 &psd1) != ERROR_SUCCESS)) {
d70f60ae 1721#ifdef DEBUG_IPC
32874aea 1722 debug(
1723 ("couldn't get owner info for filemap: %d\n",
1724 rc));
d70f60ae 1725#endif
32874aea 1726 return 0;
1727 }
016ef8ab 1728#ifdef DEBUG_IPC
32874aea 1729 debug(("got security stuff\n"));
016ef8ab 1730#endif
32874aea 1731 if (!EqualSid(mapowner, procowner))
1732 return 0; /* security ID mismatch! */
d70f60ae 1733#ifdef DEBUG_IPC
32874aea 1734 debug(("security stuff matched\n"));
d70f60ae 1735#endif
32874aea 1736 LocalFree(psd1);
1737 LocalFree(psd2);
1738 } else {
d70f60ae 1739#ifdef DEBUG_IPC
32874aea 1740 debug(("security APIs not present\n"));
d70f60ae 1741#endif
32874aea 1742 }
123bc6ea 1743#endif
32874aea 1744 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
d70f60ae 1745#ifdef DEBUG_IPC
32874aea 1746 debug(("p is %p\n", p));
1747 {
1748 int i;
1749 for (i = 0; i < 5; i++)
1750 debug(
1751 ("p[%d]=%02x\n", i,
1752 ((unsigned char *) p)[i]));}
d70f60ae 1753#endif
32874aea 1754 answer_msg(p);
1755 ret = 1;
1756 UnmapViewOfFile(p);
1757 }
1758 CloseHandle(filemap);
1759 return ret;
1760 }
5c58ad2d 1761 }
1762
32874aea 1763 return DefWindowProc(hwnd, message, wParam, lParam);
5c58ad2d 1764}
1765
ddecd643 1766/*
1767 * Fork and Exec the command in cmdline. [DBW]
1768 */
2c975497 1769void spawn_cmd(char *cmdline, char * args, int show)
32874aea 1770{
ddecd643 1771 if (ShellExecute(NULL, _T("open"), cmdline,
2c975497 1772 args, NULL, show) <= (HINSTANCE) 32) {
32874aea 1773 TCHAR sMsg[140];
1774 sprintf(sMsg, _T("Failed to run \"%.100s\", Error: %d"), cmdline,
2d466ffd 1775 (int)GetLastError());
32874aea 1776 MessageBox(NULL, sMsg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
ddecd643 1777 }
1778}
1779
93b581bd 1780void cleanup_exit(int code) { exit(code); }
1781
32874aea 1782int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1783{
5c58ad2d 1784 WNDCLASS wndclass;
5c58ad2d 1785 MSG msg;
016ef8ab 1786 OSVERSIONINFO osi;
1787 HMODULE advapi;
ddecd643 1788 char *command = NULL;
1789 int added_keys = 0;
d3a1a808 1790 int argc, i;
1791 char **argv, **argstart;
016ef8ab 1792
1793 /*
1794 * Determine whether we're an NT system (should have security
1795 * APIs) or a non-NT system (don't do security).
1796 */
1797 memset(&osi, 0, sizeof(OSVERSIONINFO));
1798 osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
32874aea 1799 if (GetVersionEx(&osi) && osi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1800 has_security = TRUE;
016ef8ab 1801 } else
32874aea 1802 has_security = FALSE;
016ef8ab 1803
1804 if (has_security) {
123bc6ea 1805#ifndef NO_SECURITY
32874aea 1806 /*
1807 * Attempt to get the security API we need.
1808 */
1809 advapi = LoadLibrary("ADVAPI32.DLL");
1810 getsecurityinfo =
1811 (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
1812 if (!getsecurityinfo) {
1813 MessageBox(NULL,
1814 "Unable to access security APIs. Pageant will\n"
1815 "not run, in case it causes a security breach.",
1816 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1817 return 1;
1818 }
123bc6ea 1819#else
1820 MessageBox(NULL,
1821 "This program has been compiled for Win9X and will\n"
1822 "not run on NT, in case it causes a security breach.",
1823 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1824 return 1;
1825#endif
016ef8ab 1826 } else
32874aea 1827 advapi = NULL;
5c58ad2d 1828
1829 instance = inst;
1830
ddecd643 1831 /*
ecea795f 1832 * See if we can find our Help file.
1833 */
1834 {
1835 char b[2048], *p, *q, *r;
1836 FILE *fp;
1837 GetModuleFileName(NULL, b, sizeof(b) - 1);
1838 r = b;
1839 p = strrchr(b, '\\');
1840 if (p && p >= r) r = p+1;
1841 q = strrchr(b, ':');
1842 if (q && q >= r) r = q+1;
1843 strcpy(r, "putty.hlp");
1844 if ( (fp = fopen(b, "r")) != NULL) {
1845 help_path = dupstr(b);
1846 fclose(fp);
1847 } else
1848 help_path = NULL;
1849 }
1850
1851 /*
76b51e35 1852 * Look for the PuTTY binary (we will enable the saved session
1853 * submenu if we find it).
1854 */
1855 {
1856 char b[2048], *p, *q, *r;
1857 FILE *fp;
1858 GetModuleFileName(NULL, b, sizeof(b) - 1);
1859 r = b;
1860 p = strrchr(b, '\\');
1861 if (p && p >= r) r = p+1;
1862 q = strrchr(b, ':');
1863 if (q && q >= r) r = q+1;
1864 strcpy(r, "putty.exe");
1865 if ( (fp = fopen(b, "r")) != NULL) {
1866 putty_path = dupstr(b);
1867 fclose(fp);
1868 } else
1869 putty_path = NULL;
1870 }
1871
1872 /*
ddecd643 1873 * Find out if Pageant is already running.
1874 */
1875 already_running = FALSE;
260f3dec 1876 if (agent_exists())
ddecd643 1877 already_running = TRUE;
1878 else {
1879
1880 if (!prev) {
32874aea 1881 wndclass.style = 0;
1882 wndclass.lpfnWndProc = WndProc;
1883 wndclass.cbClsExtra = 0;
1884 wndclass.cbWndExtra = 0;
1885 wndclass.hInstance = inst;
1886 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1887 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1888 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1889 wndclass.lpszMenuName = NULL;
ddecd643 1890 wndclass.lpszClassName = APPNAME;
1891
32874aea 1892 RegisterClass(&wndclass);
ddecd643 1893 }
5c58ad2d 1894
ecea795f 1895 main_hwnd = keylist = NULL;
5c58ad2d 1896
ecea795f 1897 main_hwnd = CreateWindow(APPNAME, APPNAME,
1898 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1899 CW_USEDEFAULT, CW_USEDEFAULT,
1900 100, 100, NULL, NULL, inst, NULL);
5c58ad2d 1901
ddecd643 1902 /* Set up a system tray icon */
ecea795f 1903 AddTrayIcon(main_hwnd);
15ad2e8a 1904
76b51e35 1905 /* Accelerators used: nsvkxa */
15ad2e8a 1906 systray_menu = CreatePopupMenu();
76b51e35 1907 if (putty_path) {
1908 session_menu = CreateMenu();
1909 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1910 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1911 (UINT) session_menu, "&Saved Sessions");
1912 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1913 }
15ad2e8a 1914 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1915 "&View Keys");
1916 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
ecea795f 1917 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1918 if (help_path)
1919 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
15ad2e8a 1920 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
ecea795f 1921 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
15ad2e8a 1922 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
76b51e35 1923 initial_menuitems_count = GetMenuItemCount(session_menu);
5c58ad2d 1924
ecea795f 1925 ShowWindow(main_hwnd, SW_HIDE);
5c58ad2d 1926
ddecd643 1927 /*
1928 * Initialise storage for RSA keys.
1929 */
1930 rsakeys = newtree234(cmpkeys_rsa);
1931 ssh2keys = newtree234(cmpkeys_ssh2);
1932
1933 }
dacbd0e8 1934
1935 /*
0959acf7 1936 * Initialise storage for short-term passphrase cache.
1937 */
1938 passphrases = newtree234(NULL);
1939
1940 /*
45cebe79 1941 * Process the command line and add keys as listed on it.
5c58ad2d 1942 */
d3a1a808 1943 split_into_argv(cmdline, &argc, &argv, &argstart);
1944 for (i = 0; i < argc; i++) {
1945 if (!strcmp(argv[i], "-c")) {
1946 /*
1947 * If we see `-c', then the rest of the
1948 * command line should be treated as a
1949 * command to be spawned.
1950 */
1951 if (i < argc-1)
1952 command = argstart[i+1];
1953 else
1954 command = "";
1955 break;
1956 } else {
1957 add_keyfile(argv[i]);
1958 added_keys = TRUE;
32874aea 1959 }
5c58ad2d 1960 }
1961
0959acf7 1962 /*
1963 * Forget any passphrase that we retained while going over
1964 * command line keyfiles.
1965 */
1966 forget_passphrases();
1967
2c975497 1968 if (command) {
1969 char *args;
1970 if (command[0] == '"')
1971 args = strchr(++command, '"');
1972 else
1973 args = strchr(command, ' ');
1974 if (args) {
1975 *args++ = 0;
1976 while(*args && isspace(*args)) args++;
1977 }
1978 spawn_cmd(command, args, show);
1979 }
ddecd643 1980
1981 /*
1982 * If Pageant was already running, we leave now. If we haven't
1983 * even taken any auxiliary action (spawned a command or added
1984 * keys), complain.
1985 */
1986 if (already_running) {
1987 if (!command && !added_keys) {
1988 MessageBox(NULL, "Pageant is already running", "Pageant Error",
1989 MB_ICONERROR | MB_OK);
1990 }
32874aea 1991 if (advapi)
1992 FreeLibrary(advapi);
1993 return 0;
ddecd643 1994 }
1995
5c58ad2d 1996 /*
dacbd0e8 1997 * Main message loop.
5c58ad2d 1998 */
5c58ad2d 1999 while (GetMessage(&msg, NULL, 0, 0) == 1) {
32874aea 2000 TranslateMessage(&msg);
2001 DispatchMessage(&msg);
5c58ad2d 2002 }
2003
2004 /* Clean up the system tray icon */
2005 {
32874aea 2006 NOTIFYICONDATA tnid;
5c58ad2d 2007
32874aea 2008 tnid.cbSize = sizeof(NOTIFYICONDATA);
ecea795f 2009 tnid.hWnd = main_hwnd;
32874aea 2010 tnid.uID = 1;
5c58ad2d 2011
32874aea 2012 Shell_NotifyIcon(NIM_DELETE, &tnid);
5c58ad2d 2013
32874aea 2014 DestroyMenu(systray_menu);
5c58ad2d 2015 }
2016
32874aea 2017 if (advapi)
2018 FreeLibrary(advapi);
76b51e35 2019 return msg.wParam;
5c58ad2d 2020}