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