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