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