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