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