The WinSock library is now loaded at run-time, which means we can
[u/mdw/putty] / pageant.c
1 /*
2 * Pageant: the PuTTY Authentication Agent.
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <ctype.h>
8 #include <assert.h>
9 #include <tchar.h>
10
11 #include "putty.h"
12 #include "ssh.h"
13 #include "misc.h"
14 #include "tree234.h"
15
16 #ifndef NO_SECURITY
17 #include <aclapi.h>
18 #endif
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 * Forward references
117 */
118 static void *make_keylist1(int *length);
119 static void *make_keylist2(int *length);
120 static void *get_keylist1(void);
121 static void *get_keylist2(void);
122
123 /*
124 * We need this to link with the RSA code, because rsaencrypt()
125 * pads its data with random bytes. Since we only use rsadecrypt()
126 * and the signing functions, which are deterministic, this should
127 * never be called.
128 *
129 * If it _is_ called, there is a _serious_ problem, because it
130 * won't generate true random numbers. So we must scream, panic,
131 * and exit immediately if that should happen.
132 */
133 int random_byte(void)
134 {
135 MessageBox(main_hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
136 exit(0);
137 /* this line can't be reached but it placates MSVC's warnings :-) */
138 return 0;
139 }
140
141 /*
142 * Blob structure for passing to the asymmetric SSH2 key compare
143 * function, prototyped here.
144 */
145 struct blob {
146 unsigned char *blob;
147 int len;
148 };
149 static int cmpkeys_ssh2_asymm(void *av, void *bv);
150
151 #define GET_32BIT(cp) \
152 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
153 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
154 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
155 ((unsigned long)(unsigned char)(cp)[3]))
156
157 #define PUT_32BIT(cp, value) { \
158 (cp)[0] = (unsigned char)((value) >> 24); \
159 (cp)[1] = (unsigned char)((value) >> 16); \
160 (cp)[2] = (unsigned char)((value) >> 8); \
161 (cp)[3] = (unsigned char)(value); }
162
163 #define PASSPHRASE_MAXLEN 512
164
165 struct PassphraseProcStruct {
166 char *passphrase;
167 char *comment;
168 };
169
170 static tree234 *passphrases = NULL;
171
172 /*
173 * After processing a list of filenames, we want to forget the
174 * passphrases.
175 */
176 static void forget_passphrases(void)
177 {
178 while (count234(passphrases) > 0) {
179 char *pp = index234(passphrases, 0);
180 memset(pp, 0, strlen(pp));
181 delpos234(passphrases, 0);
182 free(pp);
183 }
184 }
185
186 /*
187 * Dialog-box function for the Licence box.
188 */
189 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
190 WPARAM wParam, LPARAM lParam)
191 {
192 switch (msg) {
193 case WM_INITDIALOG:
194 return 1;
195 case WM_COMMAND:
196 switch (LOWORD(wParam)) {
197 case IDOK:
198 EndDialog(hwnd, 1);
199 return 0;
200 }
201 return 0;
202 case WM_CLOSE:
203 EndDialog(hwnd, 1);
204 return 0;
205 }
206 return 0;
207 }
208
209 /*
210 * Dialog-box function for the About box.
211 */
212 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
213 WPARAM wParam, LPARAM lParam)
214 {
215 switch (msg) {
216 case WM_INITDIALOG:
217 SetDlgItemText(hwnd, 100, ver);
218 return 1;
219 case WM_COMMAND:
220 switch (LOWORD(wParam)) {
221 case IDOK:
222 aboutbox = NULL;
223 DestroyWindow(hwnd);
224 return 0;
225 case 101:
226 EnableWindow(hwnd, 0);
227 DialogBox(instance, MAKEINTRESOURCE(214), hwnd, LicenceProc);
228 EnableWindow(hwnd, 1);
229 SetActiveWindow(hwnd);
230 return 0;
231 }
232 return 0;
233 case WM_CLOSE:
234 aboutbox = NULL;
235 DestroyWindow(hwnd);
236 return 0;
237 }
238 return 0;
239 }
240
241 static HWND passphrase_box;
242
243 /*
244 * Dialog-box function for the passphrase box.
245 */
246 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
247 WPARAM wParam, LPARAM lParam)
248 {
249 static char *passphrase = NULL;
250 struct PassphraseProcStruct *p;
251
252 switch (msg) {
253 case WM_INITDIALOG:
254 passphrase_box = hwnd;
255 /*
256 * Centre the window.
257 */
258 { /* centre the window */
259 RECT rs, rd;
260 HWND hw;
261
262 hw = GetDesktopWindow();
263 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
264 MoveWindow(hwnd,
265 (rs.right + rs.left + rd.left - rd.right) / 2,
266 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
267 rd.right - rd.left, rd.bottom - rd.top, TRUE);
268 }
269
270 SetForegroundWindow(hwnd);
271 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
272 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
273 p = (struct PassphraseProcStruct *) lParam;
274 passphrase = p->passphrase;
275 if (p->comment)
276 SetDlgItemText(hwnd, 101, p->comment);
277 *passphrase = 0;
278 SetDlgItemText(hwnd, 102, passphrase);
279 return 0;
280 case WM_COMMAND:
281 switch (LOWORD(wParam)) {
282 case IDOK:
283 if (*passphrase)
284 EndDialog(hwnd, 1);
285 else
286 MessageBeep(0);
287 return 0;
288 case IDCANCEL:
289 EndDialog(hwnd, 0);
290 return 0;
291 case 102: /* edit box */
292 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
293 GetDlgItemText(hwnd, 102, passphrase,
294 PASSPHRASE_MAXLEN - 1);
295 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
296 }
297 return 0;
298 }
299 return 0;
300 case WM_CLOSE:
301 EndDialog(hwnd, 0);
302 return 0;
303 }
304 return 0;
305 }
306
307 /*
308 * Warn about the obsolescent key file format.
309 */
310 void old_keyfile_warning(void)
311 {
312 static const char mbtitle[] = "PuTTY Key File Warning";
313 static const char message[] =
314 "You are loading an SSH 2 private key which has an\n"
315 "old version of the file format. This means your key\n"
316 "file is not fully tamperproof. Future versions of\n"
317 "PuTTY may stop supporting this private key format,\n"
318 "so we recommend you convert your key to the new\n"
319 "format.\n"
320 "\n"
321 "You can perform this conversion by loading the key\n"
322 "into PuTTYgen and then saving it again.";
323
324 MessageBox(NULL, message, mbtitle, MB_OK);
325 }
326
327 /*
328 * Update the visible key list.
329 */
330 static void keylist_update(void)
331 {
332 struct RSAKey *rkey;
333 struct ssh2_userkey *skey;
334 int i;
335
336 if (keylist) {
337 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
338 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
339 char listentry[512], *p;
340 /*
341 * Replace two spaces in the fingerprint with tabs, for
342 * nice alignment in the box.
343 */
344 strcpy(listentry, "ssh1\t");
345 p = listentry + strlen(listentry);
346 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
347 p = strchr(listentry, ' ');
348 if (p)
349 *p = '\t';
350 p = strchr(listentry, ' ');
351 if (p)
352 *p = '\t';
353 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
354 0, (LPARAM) listentry);
355 }
356 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
357 char listentry[512], *p;
358 int len;
359 /*
360 * Replace two spaces in the fingerprint with tabs, for
361 * nice alignment in the box.
362 */
363 p = skey->alg->fingerprint(skey->data);
364 strncpy(listentry, p, sizeof(listentry));
365 p = strchr(listentry, ' ');
366 if (p)
367 *p = '\t';
368 p = strchr(listentry, ' ');
369 if (p)
370 *p = '\t';
371 len = strlen(listentry);
372 if (len < sizeof(listentry) - 2) {
373 listentry[len] = '\t';
374 strncpy(listentry + len + 1, skey->comment,
375 sizeof(listentry) - len - 1);
376 }
377 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
378 (LPARAM) listentry);
379 }
380 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
381 }
382 }
383
384 /*
385 * This function loads a key from a file and adds it.
386 */
387 static void add_keyfile(Filename filename)
388 {
389 char passphrase[PASSPHRASE_MAXLEN];
390 struct RSAKey *rkey = NULL;
391 struct ssh2_userkey *skey = NULL;
392 int needs_pass;
393 int ret;
394 int attempts;
395 char *comment;
396 struct PassphraseProcStruct pps;
397 int type;
398 int original_pass;
399
400 type = key_type(&filename);
401 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
402 char msg[256];
403 sprintf(msg, "Couldn't load this key (%s)", key_type_to_str(type));
404 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONERROR);
405 return;
406 }
407
408 /*
409 * See if the key is already loaded (in the primary Pageant,
410 * which may or may not be us).
411 */
412 {
413 void *blob;
414 unsigned char *keylist, *p;
415 int i, nkeys, bloblen;
416
417 if (type == SSH_KEYTYPE_SSH1) {
418 if (!rsakey_pubblob(&filename, &blob, &bloblen, NULL)) {
419 MessageBox(NULL, "Couldn't load private key.", APPNAME,
420 MB_OK | MB_ICONERROR);
421 return;
422 }
423 keylist = get_keylist1();
424 } else {
425 unsigned char *blob2;
426 blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen, NULL);
427 if (!blob) {
428 MessageBox(NULL, "Couldn't load private key.", APPNAME,
429 MB_OK | MB_ICONERROR);
430 return;
431 }
432 /* For our purposes we want the blob prefixed with its length */
433 blob2 = snewn(bloblen+4, unsigned char);
434 PUT_32BIT(blob2, bloblen);
435 memcpy(blob2 + 4, blob, bloblen);
436 sfree(blob);
437 blob = blob2;
438
439 keylist = get_keylist2();
440 }
441 if (keylist) {
442 nkeys = GET_32BIT(keylist);
443 p = keylist + 4;
444
445 for (i = 0; i < nkeys; i++) {
446 if (!memcmp(blob, p, bloblen)) {
447 /* Key is already present; we can now leave. */
448 sfree(keylist);
449 sfree(blob);
450 return;
451 }
452 /* Now skip over public blob */
453 if (type == SSH_KEYTYPE_SSH1)
454 p += rsa_public_blob_len(p);
455 else
456 p += 4 + GET_32BIT(p);
457 /* Now skip over comment field */
458 p += 4 + GET_32BIT(p);
459 }
460
461 sfree(keylist);
462 }
463
464 sfree(blob);
465 }
466
467 if (type == SSH_KEYTYPE_SSH1)
468 needs_pass = rsakey_encrypted(&filename, &comment);
469 else
470 needs_pass = ssh2_userkey_encrypted(&filename, &comment);
471 attempts = 0;
472 if (type == SSH_KEYTYPE_SSH1)
473 rkey = snew(struct RSAKey);
474 pps.passphrase = passphrase;
475 pps.comment = comment;
476 original_pass = 0;
477 do {
478 if (needs_pass) {
479 /* try all the remembered passphrases first */
480 char *pp = index234(passphrases, attempts);
481 if(pp) {
482 strcpy(passphrase, pp);
483 } else {
484 int dlgret;
485 original_pass = 1;
486 dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
487 NULL, PassphraseProc, (LPARAM) & pps);
488 passphrase_box = NULL;
489 if (!dlgret) {
490 if (comment)
491 sfree(comment);
492 if (type == SSH_KEYTYPE_SSH1)
493 sfree(rkey);
494 return; /* operation cancelled */
495 }
496 }
497 } else
498 *passphrase = '\0';
499 if (type == SSH_KEYTYPE_SSH1)
500 ret = loadrsakey(&filename, rkey, passphrase, NULL);
501 else {
502 skey = ssh2_load_userkey(&filename, passphrase, NULL);
503 if (skey == SSH2_WRONG_PASSPHRASE)
504 ret = -1;
505 else if (!skey)
506 ret = 0;
507 else
508 ret = 1;
509 }
510 attempts++;
511 } while (ret == -1);
512
513 /* if they typed in an ok passphrase, remember it */
514 if(original_pass && ret) {
515 char *pp = dupstr(passphrase);
516 addpos234(passphrases, pp, 0);
517 }
518
519 if (comment)
520 sfree(comment);
521 if (ret == 0) {
522 MessageBox(NULL, "Couldn't load private key.", APPNAME,
523 MB_OK | MB_ICONERROR);
524 if (type == SSH_KEYTYPE_SSH1)
525 sfree(rkey);
526 return;
527 }
528 if (type == SSH_KEYTYPE_SSH1) {
529 if (already_running) {
530 unsigned char *request, *response;
531 void *vresponse;
532 int reqlen, clen, resplen, ret;
533
534 clen = strlen(rkey->comment);
535
536 reqlen = 4 + 1 + /* length, message type */
537 4 + /* bit count */
538 ssh1_bignum_length(rkey->modulus) +
539 ssh1_bignum_length(rkey->exponent) +
540 ssh1_bignum_length(rkey->private_exponent) +
541 ssh1_bignum_length(rkey->iqmp) +
542 ssh1_bignum_length(rkey->p) +
543 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
544 ;
545
546 request = snewn(reqlen, unsigned char);
547
548 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
549 reqlen = 5;
550 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
551 reqlen += 4;
552 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
553 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
554 reqlen +=
555 ssh1_write_bignum(request + reqlen,
556 rkey->private_exponent);
557 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
558 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
559 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
560 PUT_32BIT(request + reqlen, clen);
561 memcpy(request + reqlen + 4, rkey->comment, clen);
562 reqlen += 4 + clen;
563 PUT_32BIT(request, reqlen - 4);
564
565 ret = agent_query(request, reqlen, &vresponse, &resplen,
566 NULL, NULL);
567 assert(ret == 1);
568 response = vresponse;
569 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
570 MessageBox(NULL, "The already running Pageant "
571 "refused to add the key.", APPNAME,
572 MB_OK | MB_ICONERROR);
573
574 sfree(request);
575 sfree(response);
576 } else {
577 if (add234(rsakeys, rkey) != rkey)
578 sfree(rkey); /* already present, don't waste RAM */
579 }
580 } else {
581 if (already_running) {
582 unsigned char *request, *response;
583 void *vresponse;
584 int reqlen, alglen, clen, keybloblen, resplen, ret;
585 alglen = strlen(skey->alg->name);
586 clen = strlen(skey->comment);
587
588 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
589
590 reqlen = 4 + 1 + /* length, message type */
591 4 + alglen + /* algorithm name */
592 keybloblen + /* key data */
593 4 + clen /* comment */
594 ;
595
596 request = snewn(reqlen, unsigned char);
597
598 request[4] = SSH2_AGENTC_ADD_IDENTITY;
599 reqlen = 5;
600 PUT_32BIT(request + reqlen, alglen);
601 reqlen += 4;
602 memcpy(request + reqlen, skey->alg->name, alglen);
603 reqlen += alglen;
604 reqlen += skey->alg->openssh_fmtkey(skey->data,
605 request + reqlen,
606 keybloblen);
607 PUT_32BIT(request + reqlen, clen);
608 memcpy(request + reqlen + 4, skey->comment, clen);
609 PUT_32BIT(request, reqlen - 4);
610 reqlen += clen + 4;
611
612 ret = agent_query(request, reqlen, &vresponse, &resplen,
613 NULL, NULL);
614 assert(ret == 1);
615 response = vresponse;
616 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
617 MessageBox(NULL, "The already running Pageant "
618 "refused to add the key.", APPNAME,
619 MB_OK | MB_ICONERROR);
620
621 sfree(request);
622 sfree(response);
623 } else {
624 if (add234(ssh2keys, skey) != skey) {
625 skey->alg->freekey(skey->data);
626 sfree(skey); /* already present, don't waste RAM */
627 }
628 }
629 }
630 }
631
632 /*
633 * Create an SSH1 key list in a malloc'ed buffer; return its
634 * length.
635 */
636 static void *make_keylist1(int *length)
637 {
638 int i, nkeys, len;
639 struct RSAKey *key;
640 unsigned char *blob, *p, *ret;
641 int bloblen;
642
643 /*
644 * Count up the number and length of keys we hold.
645 */
646 len = 4;
647 nkeys = 0;
648 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
649 nkeys++;
650 blob = rsa_public_blob(key, &bloblen);
651 len += bloblen;
652 sfree(blob);
653 len += 4 + strlen(key->comment);
654 }
655
656 /* Allocate the buffer. */
657 p = ret = snewn(len, unsigned char);
658 if (length) *length = len;
659
660 PUT_32BIT(p, nkeys);
661 p += 4;
662 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
663 blob = rsa_public_blob(key, &bloblen);
664 memcpy(p, blob, bloblen);
665 p += bloblen;
666 sfree(blob);
667 PUT_32BIT(p, strlen(key->comment));
668 memcpy(p + 4, key->comment, strlen(key->comment));
669 p += 4 + strlen(key->comment);
670 }
671
672 assert(p - ret == len);
673 return ret;
674 }
675
676 /*
677 * Create an SSH2 key list in a malloc'ed buffer; return its
678 * length.
679 */
680 static void *make_keylist2(int *length)
681 {
682 struct ssh2_userkey *key;
683 int i, len, nkeys;
684 unsigned char *blob, *p, *ret;
685 int bloblen;
686
687 /*
688 * Count up the number and length of keys we hold.
689 */
690 len = 4;
691 nkeys = 0;
692 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
693 nkeys++;
694 len += 4; /* length field */
695 blob = key->alg->public_blob(key->data, &bloblen);
696 len += bloblen;
697 sfree(blob);
698 len += 4 + strlen(key->comment);
699 }
700
701 /* Allocate the buffer. */
702 p = ret = snewn(len, unsigned char);
703 if (length) *length = len;
704
705 /*
706 * Packet header is the obvious five bytes, plus four
707 * bytes for the key count.
708 */
709 PUT_32BIT(p, nkeys);
710 p += 4;
711 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
712 blob = key->alg->public_blob(key->data, &bloblen);
713 PUT_32BIT(p, bloblen);
714 p += 4;
715 memcpy(p, blob, bloblen);
716 p += bloblen;
717 sfree(blob);
718 PUT_32BIT(p, strlen(key->comment));
719 memcpy(p + 4, key->comment, strlen(key->comment));
720 p += 4 + strlen(key->comment);
721 }
722
723 assert(p - ret == len);
724 return ret;
725 }
726
727 /*
728 * Acquire a keylist1 from the primary Pageant; this means either
729 * calling make_keylist1 (if that's us) or sending a message to the
730 * primary Pageant (if it's not).
731 */
732 static void *get_keylist1(void)
733 {
734 void *ret;
735
736 if (already_running) {
737 unsigned char request[5], *response;
738 void *vresponse;
739 int resplen, retval;
740 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
741 PUT_32BIT(request, 4);
742
743 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
744 assert(retval == 1);
745 response = vresponse;
746 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)
747 return NULL;
748
749 ret = snewn(resplen-5, unsigned char);
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, retval;
771
772 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
773 PUT_32BIT(request, 4);
774
775 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
776 assert(retval == 1);
777 response = vresponse;
778 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)
779 return NULL;
780
781 ret = snewn(resplen-5, unsigned char);
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 = snew(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 = snewn(commentlen+1, char);
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 = snew(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 = snewn(commlen + 1, char);
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 = snewn(8192, char);
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(filename_from_str(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_from_str(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 = snewn(numSelected, 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 /*
1795 * This is a can't-happen stub, since Pageant never makes
1796 * asynchronous agent requests.
1797 */
1798 void agent_schedule_callback(void (*callback)(void *, void *, int),
1799 void *callback_ctx, void *data, int len)
1800 {
1801 assert(!"We shouldn't get here");
1802 }
1803
1804 void cleanup_exit(int code) { exit(code); }
1805
1806 int flags = FLAG_SYNCAGENT;
1807
1808 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1809 {
1810 WNDCLASS wndclass;
1811 MSG msg;
1812 HMODULE advapi;
1813 char *command = NULL;
1814 int added_keys = 0;
1815 int argc, i;
1816 char **argv, **argstart;
1817
1818 /*
1819 * Determine whether we're an NT system (should have security
1820 * APIs) or a non-NT system (don't do security).
1821 */
1822 if (!init_winver())
1823 {
1824 modalfatalbox("Windows refuses to report a version");
1825 }
1826 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1827 has_security = TRUE;
1828 } else
1829 has_security = FALSE;
1830
1831 if (has_security) {
1832 #ifndef NO_SECURITY
1833 /*
1834 * Attempt to get the security API we need.
1835 */
1836 advapi = LoadLibrary("ADVAPI32.DLL");
1837 getsecurityinfo =
1838 (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
1839 if (!getsecurityinfo) {
1840 MessageBox(NULL,
1841 "Unable to access security APIs. Pageant will\n"
1842 "not run, in case it causes a security breach.",
1843 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1844 return 1;
1845 }
1846 #else
1847 MessageBox(NULL,
1848 "This program has been compiled for Win9X and will\n"
1849 "not run on NT, in case it causes a security breach.",
1850 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1851 return 1;
1852 #endif
1853 } else
1854 advapi = NULL;
1855
1856 instance = inst;
1857
1858 /*
1859 * See if we can find our Help file.
1860 */
1861 {
1862 char b[2048], *p, *q, *r;
1863 FILE *fp;
1864 GetModuleFileName(NULL, b, sizeof(b) - 1);
1865 r = b;
1866 p = strrchr(b, '\\');
1867 if (p && p >= r) r = p+1;
1868 q = strrchr(b, ':');
1869 if (q && q >= r) r = q+1;
1870 strcpy(r, "putty.hlp");
1871 if ( (fp = fopen(b, "r")) != NULL) {
1872 help_path = dupstr(b);
1873 fclose(fp);
1874 } else
1875 help_path = NULL;
1876 }
1877
1878 /*
1879 * Look for the PuTTY binary (we will enable the saved session
1880 * submenu if we find it).
1881 */
1882 {
1883 char b[2048], *p, *q, *r;
1884 FILE *fp;
1885 GetModuleFileName(NULL, b, sizeof(b) - 1);
1886 r = b;
1887 p = strrchr(b, '\\');
1888 if (p && p >= r) r = p+1;
1889 q = strrchr(b, ':');
1890 if (q && q >= r) r = q+1;
1891 strcpy(r, "putty.exe");
1892 if ( (fp = fopen(b, "r")) != NULL) {
1893 putty_path = dupstr(b);
1894 fclose(fp);
1895 } else
1896 putty_path = NULL;
1897 }
1898
1899 /*
1900 * Find out if Pageant is already running.
1901 */
1902 already_running = FALSE;
1903 if (agent_exists())
1904 already_running = TRUE;
1905 else {
1906
1907 if (!prev) {
1908 wndclass.style = 0;
1909 wndclass.lpfnWndProc = WndProc;
1910 wndclass.cbClsExtra = 0;
1911 wndclass.cbWndExtra = 0;
1912 wndclass.hInstance = inst;
1913 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1914 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1915 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1916 wndclass.lpszMenuName = NULL;
1917 wndclass.lpszClassName = APPNAME;
1918
1919 RegisterClass(&wndclass);
1920 }
1921
1922 main_hwnd = keylist = NULL;
1923
1924 main_hwnd = CreateWindow(APPNAME, APPNAME,
1925 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1926 CW_USEDEFAULT, CW_USEDEFAULT,
1927 100, 100, NULL, NULL, inst, NULL);
1928
1929 /* Set up a system tray icon */
1930 AddTrayIcon(main_hwnd);
1931
1932 /* Accelerators used: nsvkxa */
1933 systray_menu = CreatePopupMenu();
1934 if (putty_path) {
1935 session_menu = CreateMenu();
1936 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1937 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1938 (UINT) session_menu, "&Saved Sessions");
1939 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1940 }
1941 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1942 "&View Keys");
1943 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1944 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1945 if (help_path)
1946 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1947 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1948 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1949 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1950 initial_menuitems_count = GetMenuItemCount(session_menu);
1951
1952 ShowWindow(main_hwnd, SW_HIDE);
1953
1954 /*
1955 * Initialise storage for RSA keys.
1956 */
1957 rsakeys = newtree234(cmpkeys_rsa);
1958 ssh2keys = newtree234(cmpkeys_ssh2);
1959
1960 }
1961
1962 /*
1963 * Initialise storage for short-term passphrase cache.
1964 */
1965 passphrases = newtree234(NULL);
1966
1967 /*
1968 * Process the command line and add keys as listed on it.
1969 */
1970 split_into_argv(cmdline, &argc, &argv, &argstart);
1971 for (i = 0; i < argc; i++) {
1972 if (!strcmp(argv[i], "-c")) {
1973 /*
1974 * If we see `-c', then the rest of the
1975 * command line should be treated as a
1976 * command to be spawned.
1977 */
1978 if (i < argc-1)
1979 command = argstart[i+1];
1980 else
1981 command = "";
1982 break;
1983 } else {
1984 add_keyfile(filename_from_str(argv[i]));
1985 added_keys = TRUE;
1986 }
1987 }
1988
1989 /*
1990 * Forget any passphrase that we retained while going over
1991 * command line keyfiles.
1992 */
1993 forget_passphrases();
1994
1995 if (command) {
1996 char *args;
1997 if (command[0] == '"')
1998 args = strchr(++command, '"');
1999 else
2000 args = strchr(command, ' ');
2001 if (args) {
2002 *args++ = 0;
2003 while(*args && isspace(*args)) args++;
2004 }
2005 spawn_cmd(command, args, show);
2006 }
2007
2008 /*
2009 * If Pageant was already running, we leave now. If we haven't
2010 * even taken any auxiliary action (spawned a command or added
2011 * keys), complain.
2012 */
2013 if (already_running) {
2014 if (!command && !added_keys) {
2015 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2016 MB_ICONERROR | MB_OK);
2017 }
2018 if (advapi)
2019 FreeLibrary(advapi);
2020 return 0;
2021 }
2022
2023 /*
2024 * Main message loop.
2025 */
2026 while (GetMessage(&msg, NULL, 0, 0) == 1) {
2027 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2028 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2029 TranslateMessage(&msg);
2030 DispatchMessage(&msg);
2031 }
2032 }
2033
2034 /* Clean up the system tray icon */
2035 {
2036 NOTIFYICONDATA tnid;
2037
2038 tnid.cbSize = sizeof(NOTIFYICONDATA);
2039 tnid.hWnd = main_hwnd;
2040 tnid.uID = 1;
2041
2042 Shell_NotifyIcon(NIM_DELETE, &tnid);
2043
2044 DestroyMenu(systray_menu);
2045 }
2046
2047 if (advapi)
2048 FreeLibrary(advapi);
2049 return msg.wParam;
2050 }