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