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