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