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