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