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