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