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