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