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