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