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