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