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