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