Rework keylist_update() to fix both a buffer-size limitation and a
[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;
814 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)
815 return NULL;
816
3d88e64d 817 ret = snewn(resplen-5, unsigned char);
3f2d010c 818 memcpy(ret, response+5, resplen-5);
819 sfree(response);
0016d70b 820
821 if (length)
822 *length = resplen-5;
3f2d010c 823 } else {
0016d70b 824 ret = make_keylist1(length);
3f2d010c 825 }
826 return ret;
827}
828
829/*
830 * Acquire a keylist2 from the primary Pageant; this means either
831 * calling make_keylist2 (if that's us) or sending a message to the
832 * primary Pageant (if it's not).
833 */
0016d70b 834static void *get_keylist2(int *length)
3f2d010c 835{
836 void *ret;
837
838 if (already_running) {
839 unsigned char request[5], *response;
840 void *vresponse;
839f10db 841 int resplen, retval;
3f2d010c 842
843 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
844 PUT_32BIT(request, 4);
845
839f10db 846 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
847 assert(retval == 1);
3f2d010c 848 response = vresponse;
849 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)
850 return NULL;
851
3d88e64d 852 ret = snewn(resplen-5, unsigned char);
3f2d010c 853 memcpy(ret, response+5, resplen-5);
854 sfree(response);
0016d70b 855
856 if (length)
857 *length = resplen-5;
3f2d010c 858 } else {
0016d70b 859 ret = make_keylist2(length);
3f2d010c 860 }
861 return ret;
862}
863
864/*
5c58ad2d 865 * This is the main agent function that answers messages.
866 */
32874aea 867static void answer_msg(void *msg)
868{
d70f60ae 869 unsigned char *p = msg;
870 unsigned char *ret = msg;
0016d70b 871 unsigned char *msgend;
5c58ad2d 872 int type;
873
5c58ad2d 874 /*
0016d70b 875 * Get the message length.
876 */
877 msgend = p + 4 + GET_32BIT(p);
878
879 /*
5c58ad2d 880 * Get the message type.
881 */
0016d70b 882 if (msgend < p+5)
883 goto failure;
5c58ad2d 884 type = p[4];
885
886 p += 5;
5c58ad2d 887 switch (type) {
45cebe79 888 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
32874aea 889 /*
890 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
891 */
892 {
3f2d010c 893 int len;
894 void *keylist;
32874aea 895
32874aea 896 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
3f2d010c 897 keylist = make_keylist1(&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 SSH2_AGENTC_REQUEST_IDENTITIES:
32874aea 908 /*
909 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
910 */
911 {
3f2d010c 912 int len;
913 void *keylist;
32874aea 914
32874aea 915 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
3f2d010c 916 keylist = make_keylist2(&len);
917 if (len + 5 > AGENT_MAX_MSGLEN) {
918 sfree(keylist);
919 goto failure;
32874aea 920 }
3f2d010c 921 PUT_32BIT(ret, len + 1);
922 memcpy(ret + 5, keylist, len);
923 sfree(keylist);
32874aea 924 }
925 break;
45cebe79 926 case SSH1_AGENTC_RSA_CHALLENGE:
32874aea 927 /*
928 * Reply with either SSH1_AGENT_RSA_RESPONSE or
929 * SSH_AGENT_FAILURE, depending on whether we have that key
930 * or not.
931 */
932 {
933 struct RSAKey reqkey, *key;
934 Bignum challenge, response;
935 unsigned char response_source[48], response_md5[16];
936 struct MD5Context md5c;
937 int i, len;
938
939 p += 4;
0016d70b 940 i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
941 if (i < 0)
942 goto failure;
943 p += i;
944 i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
945 if (i < 0)
946 goto failure;
947 p += i;
948 i = ssh1_read_bignum(p, msgend - p, &challenge);
949 if (i < 0)
950 goto failure;
951 p += i;
952 if (msgend < p+16) {
953 freebn(reqkey.exponent);
954 freebn(reqkey.modulus);
955 freebn(challenge);
956 goto failure;
957 }
32874aea 958 memcpy(response_source + 32, p, 16);
959 p += 16;
0016d70b 960 if (msgend < p+4 ||
961 GET_32BIT(p) != 1 ||
32874aea 962 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
963 freebn(reqkey.exponent);
964 freebn(reqkey.modulus);
965 freebn(challenge);
966 goto failure;
967 }
968 response = rsadecrypt(challenge, key);
969 for (i = 0; i < 32; i++)
970 response_source[i] = bignum_byte(response, 31 - i);
971
972 MD5Init(&md5c);
973 MD5Update(&md5c, response_source, 48);
974 MD5Final(response_md5, &md5c);
dfb88efd 975 smemclr(response_source, 48); /* burn the evidence */
32874aea 976 freebn(response); /* and that evidence */
977 freebn(challenge); /* yes, and that evidence */
978 freebn(reqkey.exponent); /* and free some memory ... */
979 freebn(reqkey.modulus); /* ... while we're at it. */
980
981 /*
982 * Packet is the obvious five byte header, plus sixteen
983 * bytes of MD5.
984 */
985 len = 5 + 16;
986 PUT_32BIT(ret, len - 4);
987 ret[4] = SSH1_AGENT_RSA_RESPONSE;
988 memcpy(ret + 5, response_md5, 16);
989 }
990 break;
45cebe79 991 case SSH2_AGENTC_SIGN_REQUEST:
32874aea 992 /*
5c72ca61 993 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
32874aea 994 * SSH_AGENT_FAILURE, depending on whether we have that key
995 * or not.
996 */
45cebe79 997 {
998 struct ssh2_userkey *key;
999 struct blob b;
1000 unsigned char *data, *signature;
1001 int datalen, siglen, len;
1002
0016d70b 1003 if (msgend < p+4)
1004 goto failure;
b1650067 1005 b.len = toint(GET_32BIT(p));
1006 if (b.len < 0 || b.len > msgend - (p+4))
1007 goto failure;
45cebe79 1008 p += 4;
1009 b.blob = p;
1010 p += b.len;
0016d70b 1011 if (msgend < p+4)
1012 goto failure;
b1650067 1013 datalen = toint(GET_32BIT(p));
45cebe79 1014 p += 4;
b1650067 1015 if (datalen < 0 || datalen > msgend - p)
0016d70b 1016 goto failure;
45cebe79 1017 data = p;
1018 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1019 if (!key)
1020 goto failure;
1021 signature = key->alg->sign(key->data, data, datalen, &siglen);
32874aea 1022 len = 5 + 4 + siglen;
1023 PUT_32BIT(ret, len - 4);
1024 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
1025 PUT_32BIT(ret + 5, siglen);
1026 memcpy(ret + 5 + 4, signature, siglen);
45cebe79 1027 sfree(signature);
1028 }
1029 break;
1030 case SSH1_AGENTC_ADD_RSA_IDENTITY:
32874aea 1031 /*
1032 * Add to the list and return SSH_AGENT_SUCCESS, or
1033 * SSH_AGENT_FAILURE if the key was malformed.
1034 */
1035 {
1036 struct RSAKey *key;
1037 char *comment;
0016d70b 1038 int n, commentlen;
1039
3d88e64d 1040 key = snew(struct RSAKey);
28edeffe 1041 memset(key, 0, sizeof(struct RSAKey));
0016d70b 1042
1043 n = makekey(p, msgend - p, key, NULL, 1);
1044 if (n < 0) {
1045 freersakey(key);
1046 sfree(key);
1047 goto failure;
1048 }
1049 p += n;
1050
1051 n = makeprivate(p, msgend - p, key);
1052 if (n < 0) {
1053 freersakey(key);
1054 sfree(key);
1055 goto failure;
1056 }
1057 p += n;
1058
1059 n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */
1060 if (n < 0) {
1061 freersakey(key);
1062 sfree(key);
1063 goto failure;
1064 }
1065 p += n;
1066
1067 n = ssh1_read_bignum(p, msgend - p, &key->p); /* p */
1068 if (n < 0) {
1069 freersakey(key);
1070 sfree(key);
1071 goto failure;
1072 }
1073 p += n;
1074
1075 n = ssh1_read_bignum(p, msgend - p, &key->q); /* q */
1076 if (n < 0) {
1077 freersakey(key);
1078 sfree(key);
1079 goto failure;
1080 }
1081 p += n;
1082
1083 if (msgend < p+4) {
1084 freersakey(key);
1085 sfree(key);
1086 goto failure;
1087 }
b1650067 1088 commentlen = toint(GET_32BIT(p));
0016d70b 1089
b1650067 1090 if (commentlen < 0 || commentlen > msgend - p) {
0016d70b 1091 freersakey(key);
1092 sfree(key);
1093 goto failure;
1094 }
1095
3d88e64d 1096 comment = snewn(commentlen+1, char);
32874aea 1097 if (comment) {
080954b8 1098 memcpy(comment, p + 4, commentlen);
1099 comment[commentlen] = '\0';
32874aea 1100 key->comment = comment;
1101 }
1102 PUT_32BIT(ret, 1);
1103 ret[4] = SSH_AGENT_FAILURE;
1104 if (add234(rsakeys, key) == key) {
1105 keylist_update();
1106 ret[4] = SSH_AGENT_SUCCESS;
1107 } else {
1108 freersakey(key);
1109 sfree(key);
1110 }
1111 }
1112 break;
45cebe79 1113 case SSH2_AGENTC_ADD_IDENTITY:
32874aea 1114 /*
1115 * Add to the list and return SSH_AGENT_SUCCESS, or
1116 * SSH_AGENT_FAILURE if the key was malformed.
1117 */
1118 {
1119 struct ssh2_userkey *key;
1120 char *comment, *alg;
45cebe79 1121 int alglen, commlen;
1122 int bloblen;
1123
45cebe79 1124
0016d70b 1125 if (msgend < p+4)
1126 goto failure;
b1650067 1127 alglen = toint(GET_32BIT(p));
32874aea 1128 p += 4;
b1650067 1129 if (alglen < 0 || alglen > msgend - p)
0016d70b 1130 goto failure;
32874aea 1131 alg = p;
1132 p += alglen;
0016d70b 1133
1134 key = snew(struct ssh2_userkey);
45cebe79 1135 /* Add further algorithm names here. */
1136 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
1137 key->alg = &ssh_rsa;
5c72ca61 1138 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
1139 key->alg = &ssh_dss;
45cebe79 1140 else {
1141 sfree(key);
1142 goto failure;
1143 }
1144
0016d70b 1145 bloblen = msgend - p;
45cebe79 1146 key->data = key->alg->openssh_createkey(&p, &bloblen);
1147 if (!key->data) {
1148 sfree(key);
1149 goto failure;
1150 }
0016d70b 1151
1152 /*
1153 * p has been advanced by openssh_createkey, but
1154 * certainly not _beyond_ the end of the buffer.
1155 */
1156 assert(p <= msgend);
1157
1158 if (msgend < p+4) {
1159 key->alg->freekey(key->data);
1160 sfree(key);
1161 goto failure;
1162 }
b1650067 1163 commlen = toint(GET_32BIT(p));
32874aea 1164 p += 4;
45cebe79 1165
b1650067 1166 if (commlen < 0 || commlen > msgend - p) {
0016d70b 1167 key->alg->freekey(key->data);
1168 sfree(key);
1169 goto failure;
1170 }
3d88e64d 1171 comment = snewn(commlen + 1, char);
32874aea 1172 if (comment) {
1173 memcpy(comment, p, commlen);
45cebe79 1174 comment[commlen] = '\0';
32874aea 1175 }
45cebe79 1176 key->comment = comment;
1177
32874aea 1178 PUT_32BIT(ret, 1);
1179 ret[4] = SSH_AGENT_FAILURE;
1180 if (add234(ssh2keys, key) == key) {
1181 keylist_update();
1182 ret[4] = SSH_AGENT_SUCCESS;
1183 } else {
45cebe79 1184 key->alg->freekey(key->data);
1185 sfree(key->comment);
32874aea 1186 sfree(key);
1187 }
1188 }
1189 break;
45cebe79 1190 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
32874aea 1191 /*
1192 * Remove from the list and return SSH_AGENT_SUCCESS, or
1193 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1194 * start with.
1195 */
1196 {
1197 struct RSAKey reqkey, *key;
0016d70b 1198 int n;
1199
1200 n = makekey(p, msgend - p, &reqkey, NULL, 0);
1201 if (n < 0)
1202 goto failure;
32874aea 1203
32874aea 1204 key = find234(rsakeys, &reqkey, NULL);
1205 freebn(reqkey.exponent);
1206 freebn(reqkey.modulus);
1207 PUT_32BIT(ret, 1);
1208 ret[4] = SSH_AGENT_FAILURE;
1209 if (key) {
1210 del234(rsakeys, key);
1211 keylist_update();
1212 freersakey(key);
45cebe79 1213 sfree(key);
32874aea 1214 ret[4] = SSH_AGENT_SUCCESS;
1215 }
1216 }
1217 break;
45cebe79 1218 case SSH2_AGENTC_REMOVE_IDENTITY:
32874aea 1219 /*
1220 * Remove from the list and return SSH_AGENT_SUCCESS, or
1221 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1222 * start with.
1223 */
1224 {
1225 struct ssh2_userkey *key;
45cebe79 1226 struct blob b;
1227
0016d70b 1228 if (msgend < p+4)
1229 goto failure;
b1650067 1230 b.len = toint(GET_32BIT(p));
45cebe79 1231 p += 4;
0016d70b 1232
b1650067 1233 if (b.len < 0 || b.len > msgend - p)
0016d70b 1234 goto failure;
45cebe79 1235 b.blob = p;
1236 p += b.len;
0016d70b 1237
45cebe79 1238 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1239 if (!key)
1240 goto failure;
1241
32874aea 1242 PUT_32BIT(ret, 1);
1243 ret[4] = SSH_AGENT_FAILURE;
1244 if (key) {
1245 del234(ssh2keys, key);
1246 keylist_update();
45cebe79 1247 key->alg->freekey(key->data);
1248 sfree(key);
32874aea 1249 ret[4] = SSH_AGENT_SUCCESS;
1250 }
1251 }
1252 break;
45cebe79 1253 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
32874aea 1254 /*
2e85c969 1255 * Remove all SSH-1 keys. Always returns success.
32874aea 1256 */
1257 {
45cebe79 1258 struct RSAKey *rkey;
45cebe79 1259
32874aea 1260 while ((rkey = index234(rsakeys, 0)) != NULL) {
1261 del234(rsakeys, rkey);
45cebe79 1262 freersakey(rkey);
1263 sfree(rkey);
32874aea 1264 }
45cebe79 1265 keylist_update();
1266
1267 PUT_32BIT(ret, 1);
1268 ret[4] = SSH_AGENT_SUCCESS;
32874aea 1269 }
1270 break;
45cebe79 1271 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
32874aea 1272 /*
2e85c969 1273 * Remove all SSH-2 keys. Always returns success.
32874aea 1274 */
1275 {
1276 struct ssh2_userkey *skey;
1277
1278 while ((skey = index234(ssh2keys, 0)) != NULL) {
1279 del234(ssh2keys, skey);
45cebe79 1280 skey->alg->freekey(skey->data);
1281 sfree(skey);
32874aea 1282 }
45cebe79 1283 keylist_update();
1284
1285 PUT_32BIT(ret, 1);
1286 ret[4] = SSH_AGENT_SUCCESS;
32874aea 1287 }
1288 break;
5c58ad2d 1289 default:
32874aea 1290 failure:
1291 /*
1292 * Unrecognised message. Return SSH_AGENT_FAILURE.
1293 */
1294 PUT_32BIT(ret, 1);
1295 ret[4] = SSH_AGENT_FAILURE;
1296 break;
5c58ad2d 1297 }
5c58ad2d 1298}
1299
1300/*
1301 * Key comparison function for the 2-3-4 tree of RSA keys.
1302 */
32874aea 1303static int cmpkeys_rsa(void *av, void *bv)
1304{
1305 struct RSAKey *a = (struct RSAKey *) av;
1306 struct RSAKey *b = (struct RSAKey *) bv;
5c58ad2d 1307 Bignum am, bm;
1308 int alen, blen;
1309
1310 am = a->modulus;
1311 bm = b->modulus;
1312 /*
1313 * Compare by length of moduli.
1314 */
ddecd643 1315 alen = bignum_bitcount(am);
1316 blen = bignum_bitcount(bm);
32874aea 1317 if (alen > blen)
1318 return +1;
1319 else if (alen < blen)
1320 return -1;
5c58ad2d 1321 /*
1322 * Now compare by moduli themselves.
1323 */
32874aea 1324 alen = (alen + 7) / 8; /* byte count */
5c58ad2d 1325 while (alen-- > 0) {
32874aea 1326 int abyte, bbyte;
1327 abyte = bignum_byte(am, alen);
1328 bbyte = bignum_byte(bm, alen);
1329 if (abyte > bbyte)
1330 return +1;
1331 else if (abyte < bbyte)
1332 return -1;
5c58ad2d 1333 }
1334 /*
1335 * Give up.
1336 */
1337 return 0;
1338}
1339
45cebe79 1340/*
2e85c969 1341 * Key comparison function for the 2-3-4 tree of SSH-2 keys.
45cebe79 1342 */
32874aea 1343static int cmpkeys_ssh2(void *av, void *bv)
1344{
1345 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1346 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
45cebe79 1347 int i;
1348 int alen, blen;
1349 unsigned char *ablob, *bblob;
1350 int c;
32874aea 1351
45cebe79 1352 /*
1353 * Compare purely by public blob.
1354 */
1355 ablob = a->alg->public_blob(a->data, &alen);
1356 bblob = b->alg->public_blob(b->data, &blen);
1357
1358 c = 0;
1359 for (i = 0; i < alen && i < blen; i++) {
1360 if (ablob[i] < bblob[i]) {
32874aea 1361 c = -1;
1362 break;
45cebe79 1363 } else if (ablob[i] > bblob[i]) {
32874aea 1364 c = +1;
1365 break;
45cebe79 1366 }
1367 }
32874aea 1368 if (c == 0 && i < alen)
1369 c = +1; /* a is longer */
1370 if (c == 0 && i < blen)
1371 c = -1; /* a is longer */
45cebe79 1372
1373 sfree(ablob);
1374 sfree(bblob);
1375
1376 return c;
1377}
1378
1379/*
1380 * Key comparison function for looking up a blob in the 2-3-4 tree
2e85c969 1381 * of SSH-2 keys.
45cebe79 1382 */
32874aea 1383static int cmpkeys_ssh2_asymm(void *av, void *bv)
1384{
1385 struct blob *a = (struct blob *) av;
1386 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
45cebe79 1387 int i;
1388 int alen, blen;
1389 unsigned char *ablob, *bblob;
1390 int c;
32874aea 1391
45cebe79 1392 /*
1393 * Compare purely by public blob.
1394 */
1395 ablob = a->blob;
1396 alen = a->len;
1397 bblob = b->alg->public_blob(b->data, &blen);
1398
1399 c = 0;
1400 for (i = 0; i < alen && i < blen; i++) {
1401 if (ablob[i] < bblob[i]) {
32874aea 1402 c = -1;
1403 break;
45cebe79 1404 } else if (ablob[i] > bblob[i]) {
32874aea 1405 c = +1;
1406 break;
45cebe79 1407 }
1408 }
32874aea 1409 if (c == 0 && i < alen)
1410 c = +1; /* a is longer */
1411 if (c == 0 && i < blen)
1412 c = -1; /* a is longer */
45cebe79 1413
1414 sfree(bblob);
1415
1416 return c;
1417}
1418
5c58ad2d 1419/*
ab162329 1420 * Prompt for a key file to add, and add it.
1421 */
32874aea 1422static void prompt_add_keyfile(void)
1423{
ab162329 1424 OPENFILENAME of;
3d88e64d 1425 char *filelist = snewn(8192, char);
0959acf7 1426
9754e5ca 1427 if (!keypath) keypath = filereq_new();
ab162329 1428 memset(&of, 0, sizeof(of));
28339579 1429 of.hwndOwner = hwnd;
9754e5ca 1430 of.lpstrFilter = FILTER_KEY_FILES;
ab162329 1431 of.lpstrCustomFilter = NULL;
1432 of.nFilterIndex = 1;
0959acf7 1433 of.lpstrFile = filelist;
1434 *filelist = '\0';
9754e5ca 1435 of.nMaxFile = 8192;
ab162329 1436 of.lpstrFileTitle = NULL;
ab162329 1437 of.lpstrTitle = "Select Private Key File";
0959acf7 1438 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
9754e5ca 1439 if (request_file(keypath, &of, TRUE, FALSE)) {
0959acf7 1440 if(strlen(filelist) > of.nFileOffset)
1441 /* Only one filename returned? */
9a30e26b 1442 add_keyfile(filename_from_str(filelist));
0959acf7 1443 else {
1444 /* we are returned a bunch of strings, end to
1445 * end. first string is the directory, the
1446 * rest the filenames. terminated with an
1447 * empty string.
1448 */
9754e5ca 1449 char *dir = filelist;
1450 char *filewalker = filelist + strlen(dir) + 1;
1451 while (*filewalker != '\0') {
1452 char *filename = dupcat(dir, "\\", filewalker, NULL);
9a30e26b 1453 add_keyfile(filename_from_str(filename));
9754e5ca 1454 sfree(filename);
1455 filewalker += strlen(filewalker) + 1;
0959acf7 1456 }
1457 }
1458
32874aea 1459 keylist_update();
0959acf7 1460 forget_passphrases();
ab162329 1461 }
0959acf7 1462 sfree(filelist);
ab162329 1463}
1464
1465/*
5c58ad2d 1466 * Dialog-box function for the key list box.
1467 */
1468static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
32874aea 1469 WPARAM wParam, LPARAM lParam)
1470{
45cebe79 1471 struct RSAKey *rkey;
1472 struct ssh2_userkey *skey;
5c58ad2d 1473
1474 switch (msg) {
1475 case WM_INITDIALOG:
45cebe79 1476 /*
1477 * Centre the window.
1478 */
1479 { /* centre the window */
1480 RECT rs, rd;
1481 HWND hw;
1482
1483 hw = GetDesktopWindow();
32874aea 1484 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1485 MoveWindow(hwnd,
1486 (rs.right + rs.left + rd.left - rd.right) / 2,
1487 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1488 rd.right - rd.left, rd.bottom - rd.top, TRUE);
45cebe79 1489 }
1490
cb2708d3 1491 if (has_help())
1e5eefb6 1492 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
1493 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
1494 WS_EX_CONTEXTHELP);
ecea795f 1495 else {
1496 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1497 if (item)
1498 DestroyWindow(item);
1499 }
ecea795f 1500
32874aea 1501 keylist = hwnd;
1c2a93c4 1502 {
32874aea 1503 static int tabs[] = { 35, 60, 210 };
1504 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1505 sizeof(tabs) / sizeof(*tabs),
1506 (LPARAM) tabs);
1c2a93c4 1507 }
32874aea 1508 keylist_update();
1509 return 0;
5c58ad2d 1510 case WM_COMMAND:
1511 switch (LOWORD(wParam)) {
1512 case IDOK:
1513 case IDCANCEL:
32874aea 1514 keylist = NULL;
1515 DestroyWindow(hwnd);
1516 return 0;
1517 case 101: /* add key */
5c58ad2d 1518 if (HIWORD(wParam) == BN_CLICKED ||
1519 HIWORD(wParam) == BN_DOUBLECLICKED) {
cb5ca813 1520 if (passphrase_box) {
1521 MessageBeep(MB_ICONERROR);
1522 SetForegroundWindow(passphrase_box);
1523 break;
1524 }
32874aea 1525 prompt_add_keyfile();
1526 }
1527 return 0;
1528 case 102: /* remove key */
5c58ad2d 1529 if (HIWORD(wParam) == BN_CLICKED ||
1530 HIWORD(wParam) == BN_DOUBLECLICKED) {
d2371c81 1531 int i;
0959acf7 1532 int rCount, sCount;
1533 int *selectedArray;
1534
1535 /* our counter within the array of selected items */
1536 int itemNum;
1537
1538 /* get the number of items selected in the list */
1539 int numSelected =
1540 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1541
1542 /* none selected? that was silly */
1543 if (numSelected == 0) {
5c58ad2d 1544 MessageBeep(0);
1545 break;
1546 }
0959acf7 1547
1548 /* get item indices in an array */
3d88e64d 1549 selectedArray = snewn(numSelected, int);
0959acf7 1550 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1551 numSelected, (WPARAM)selectedArray);
1552
1553 itemNum = numSelected - 1;
1554 rCount = count234(rsakeys);
1555 sCount = count234(ssh2keys);
1556
1557 /* go through the non-rsakeys until we've covered them all,
1558 * and/or we're out of selected items to check. note that
1559 * we go *backwards*, to avoid complications from deleting
1560 * things hence altering the offset of subsequent items
1561 */
1562 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1563 skey = index234(ssh2keys, i);
1564
1565 if (selectedArray[itemNum] == rCount + i) {
1566 del234(ssh2keys, skey);
1567 skey->alg->freekey(skey->data);
1568 sfree(skey);
1569 itemNum--;
1570 }
45cebe79 1571 }
0959acf7 1572
1573 /* do the same for the rsa keys */
1574 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1575 rkey = index234(rsakeys, i);
1576
1577 if(selectedArray[itemNum] == i) {
1578 del234(rsakeys, rkey);
1579 freersakey(rkey);
1580 sfree(rkey);
1581 itemNum--;
1582 }
1583 }
1584
1585 sfree(selectedArray);
32874aea 1586 keylist_update();
1587 }
1588 return 0;
ecea795f 1589 case 103: /* help */
1590 if (HIWORD(wParam) == BN_CLICKED ||
1591 HIWORD(wParam) == BN_DOUBLECLICKED) {
cb2708d3 1592 launch_help(hwnd, WINHELP_CTX_pageant_general);
ecea795f 1593 }
1594 return 0;
5c58ad2d 1595 }
1596 return 0;
ecea795f 1597 case WM_HELP:
cb2708d3 1598 {
ecea795f 1599 int id = ((LPHELPINFO)lParam)->iCtrlId;
a9dc49af 1600 char *topic = NULL;
ecea795f 1601 switch (id) {
cb2708d3 1602 case 100: topic = WINHELP_CTX_pageant_keylist; break;
1603 case 101: topic = WINHELP_CTX_pageant_addkey; break;
1604 case 102: topic = WINHELP_CTX_pageant_remkey; break;
ecea795f 1605 }
a9dc49af 1606 if (topic) {
cb2708d3 1607 launch_help(hwnd, topic);
ecea795f 1608 } else {
1609 MessageBeep(0);
1610 }
1611 }
1612 break;
5c58ad2d 1613 case WM_CLOSE:
32874aea 1614 keylist = NULL;
1615 DestroyWindow(hwnd);
5c58ad2d 1616 return 0;
1617 }
1618 return 0;
1619}
1620
15ad2e8a 1621/* Set up a system tray icon */
1622static BOOL AddTrayIcon(HWND hwnd)
1623{
1624 BOOL res;
1625 NOTIFYICONDATA tnid;
1626 HICON hicon;
1627
1628#ifdef NIM_SETVERSION
1629 tnid.uVersion = 0;
1630 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1631#endif
1632
1633 tnid.cbSize = sizeof(NOTIFYICONDATA);
1634 tnid.hWnd = hwnd;
1635 tnid.uID = 1; /* unique within this systray use */
1636 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1637 tnid.uCallbackMessage = WM_SYSTRAY;
28339579 1638 tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
15ad2e8a 1639 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1640
1641 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1642
1643 if (hicon) DestroyIcon(hicon);
1644
1645 return res;
1646}
1647
76b51e35 1648/* Update the saved-sessions menu. */
1649static void update_sessions(void)
1650{
1651 int num_entries;
1652 HKEY hkey;
1653 TCHAR buf[MAX_PATH + 1];
1654 MENUITEMINFO mii;
1655
1656 int index_key, index_menu;
1657
1658 if (!putty_path)
1659 return;
1660
1661 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1662 return;
1663
1664 for(num_entries = GetMenuItemCount(session_menu);
1665 num_entries > initial_menuitems_count;
1666 num_entries--)
1667 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1668
1669 index_key = 0;
1670 index_menu = 0;
1671
1672 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1673 TCHAR session_name[MAX_PATH + 1];
1674 unmungestr(buf, session_name, MAX_PATH);
1675 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1676 memset(&mii, 0, sizeof(mii));
1677 mii.cbSize = sizeof(mii);
1678 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1679 mii.fType = MFT_STRING;
1680 mii.fState = MFS_ENABLED;
1681 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1682 mii.dwTypeData = session_name;
1683 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1684 index_menu++;
1685 }
1686 index_key++;
1687 }
1688
1689 RegCloseKey(hkey);
1690
1691 if(index_menu == 0) {
1692 mii.cbSize = sizeof(mii);
1693 mii.fMask = MIIM_TYPE | MIIM_STATE;
1694 mii.fType = MFT_STRING;
1695 mii.fState = MFS_GRAYED;
1696 mii.dwTypeData = _T("(No sessions)");
1697 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1698 }
1699}
1700
13f302dc 1701#ifndef NO_SECURITY
1702/*
1703 * Versions of Pageant prior to 0.61 expected this SID on incoming
1704 * communications. For backwards compatibility, and more particularly
1705 * for compatibility with derived works of PuTTY still using the old
1706 * Pageant client code, we accept it as an alternative to the one
1707 * returned from get_user_sid() in winpgntc.c.
1708 */
1709PSID get_default_sid(void)
1710{
1711 HANDLE proc = NULL;
1712 DWORD sidlen;
1713 PSECURITY_DESCRIPTOR psd = NULL;
1714 PSID sid = NULL, copy = NULL, ret = NULL;
1715
1716 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1717 GetCurrentProcessId())) == NULL)
1718 goto cleanup;
1719
1720 if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
1721 &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)
1722 goto cleanup;
1723
1724 sidlen = GetLengthSid(sid);
1725
1726 copy = (PSID)smalloc(sidlen);
1727
1728 if (!CopySid(sidlen, copy, sid))
1729 goto cleanup;
1730
1731 /* Success. Move sid into the return value slot, and null it out
1732 * to stop the cleanup code freeing it. */
1733 ret = copy;
1734 copy = NULL;
1735
1736 cleanup:
1737 if (proc != NULL)
1738 CloseHandle(proc);
1739 if (psd != NULL)
1740 LocalFree(psd);
1741 if (copy != NULL)
1742 sfree(copy);
1743
1744 return ret;
1745}
1746#endif
1747
32874aea 1748static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1749 WPARAM wParam, LPARAM lParam)
1750{
5c58ad2d 1751 int ret;
1752 static int menuinprogress;
15ad2e8a 1753 static UINT msgTaskbarCreated = 0;
5c58ad2d 1754
1755 switch (message) {
15ad2e8a 1756 case WM_CREATE:
1757 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1758 break;
1759 default:
1760 if (message==msgTaskbarCreated) {
8e3a513c 1761 /*
1762 * Explorer has been restarted, so the tray icon will
1763 * have been lost.
1764 */
1765 AddTrayIcon(hwnd);
15ad2e8a 1766 }
1767 break;
1768
5c58ad2d 1769 case WM_SYSTRAY:
32874aea 1770 if (lParam == WM_RBUTTONUP) {
1771 POINT cursorpos;
1772 GetCursorPos(&cursorpos);
1773 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1774 } else if (lParam == WM_LBUTTONDBLCLK) {
45f23490 1775 /* Run the default menu item. */
1776 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
1777 if (menuitem != -1)
1778 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
32874aea 1779 }
1780 break;
5c58ad2d 1781 case WM_SYSTRAY2:
32874aea 1782 if (!menuinprogress) {
1783 menuinprogress = 1;
76b51e35 1784 update_sessions();
32874aea 1785 SetForegroundWindow(hwnd);
1786 ret = TrackPopupMenu(systray_menu,
1787 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1788 TPM_RIGHTBUTTON,
1789 wParam, lParam, 0, hwnd, NULL);
1790 menuinprogress = 0;
1791 }
1792 break;
5c58ad2d 1793 case WM_COMMAND:
1794 case WM_SYSCOMMAND:
1795 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
76b51e35 1796 case IDM_PUTTY:
1797 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1798 SW_SHOW) <= 32) {
1799 MessageBox(NULL, "Unable to execute PuTTY!",
1800 "Error", MB_OK | MB_ICONERROR);
1801 }
1802 break;
32874aea 1803 case IDM_CLOSE:
cb5ca813 1804 if (passphrase_box)
1805 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
32874aea 1806 SendMessage(hwnd, WM_CLOSE, 0, 0);
1807 break;
1808 case IDM_VIEWKEYS:
1809 if (!keylist) {
28339579 1810 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
32874aea 1811 NULL, KeyListProc);
1812 ShowWindow(keylist, SW_SHOWNORMAL);
32874aea 1813 }
afb2076d 1814 /*
1815 * Sometimes the window comes up minimised / hidden for
1816 * no obvious reason. Prevent this. This also brings it
1817 * to the front if it's already present (the user
1818 * selected View Keys because they wanted to _see_ the
1819 * thing).
1820 */
1821 SetForegroundWindow(keylist);
1822 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1823 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
32874aea 1824 break;
1825 case IDM_ADDKEY:
cb5ca813 1826 if (passphrase_box) {
1827 MessageBeep(MB_ICONERROR);
1828 SetForegroundWindow(passphrase_box);
1829 break;
1830 }
32874aea 1831 prompt_add_keyfile();
1832 break;
1833 case IDM_ABOUT:
1834 if (!aboutbox) {
28339579 1835 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
32874aea 1836 NULL, AboutProc);
1837 ShowWindow(aboutbox, SW_SHOWNORMAL);
1838 /*
1839 * Sometimes the window comes up minimised / hidden
1840 * for no obvious reason. Prevent this.
1841 */
1842 SetForegroundWindow(aboutbox);
1843 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1844 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1845 }
1846 break;
ecea795f 1847 case IDM_HELP:
cb2708d3 1848 launch_help(hwnd, WINHELP_CTX_pageant_general);
ecea795f 1849 break;
76b51e35 1850 default:
1851 {
1852 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1853 MENUITEMINFO mii;
1854 TCHAR buf[MAX_PATH + 1];
1855 TCHAR param[MAX_PATH + 1];
1856 memset(&mii, 0, sizeof(mii));
1857 mii.cbSize = sizeof(mii);
1858 mii.fMask = MIIM_TYPE;
1859 mii.cch = MAX_PATH;
1860 mii.dwTypeData = buf;
1861 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1862 strcpy(param, "@");
1863 strcat(param, mii.dwTypeData);
1864 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1865 _T(""), SW_SHOW) <= 32) {
1866 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1867 MB_OK | MB_ICONERROR);
1868 }
1869 }
1870 }
1871 break;
32874aea 1872 }
1873 break;
5c58ad2d 1874 case WM_DESTROY:
cb2708d3 1875 quit_help(hwnd);
32874aea 1876 PostQuitMessage(0);
5c58ad2d 1877 return 0;
1878 case WM_COPYDATA:
32874aea 1879 {
1880 COPYDATASTRUCT *cds;
1881 char *mapname;
1882 void *p;
2d466ffd 1883 HANDLE filemap;
1884#ifndef NO_SECURITY
13f302dc 1885 PSID mapowner, ourself, ourself2;
2d466ffd 1886#endif
13f302dc 1887 PSECURITY_DESCRIPTOR psd = NULL;
32874aea 1888 int ret = 0;
1889
1890 cds = (COPYDATASTRUCT *) lParam;
1891 if (cds->dwData != AGENT_COPYDATA_ID)
1892 return 0; /* not our message, mate */
1893 mapname = (char *) cds->lpData;
1894 if (mapname[cds->cbData - 1] != '\0')
1895 return 0; /* failure to be ASCIZ! */
d70f60ae 1896#ifdef DEBUG_IPC
32874aea 1897 debug(("mapname is :%s:\n", mapname));
d70f60ae 1898#endif
32874aea 1899 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
d70f60ae 1900#ifdef DEBUG_IPC
32874aea 1901 debug(("filemap is %p\n", filemap));
d70f60ae 1902#endif
32874aea 1903 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
123bc6ea 1904#ifndef NO_SECURITY
2d466ffd 1905 int rc;
32874aea 1906 if (has_security) {
ac3337f6 1907 if ((ourself = get_user_sid()) == NULL) {
d70f60ae 1908#ifdef DEBUG_IPC
ac3337f6 1909 debug(("couldn't get user SID\n"));
d70f60ae 1910#endif
32874aea 1911 return 0;
ac3337f6 1912 }
1913
13f302dc 1914 if ((ourself2 = get_default_sid()) == NULL) {
1915#ifdef DEBUG_IPC
1916 debug(("couldn't get default SID\n"));
1917#endif
1918 return 0;
1919 }
1920
9099600a 1921 if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
1922 OWNER_SECURITY_INFORMATION,
1923 &mapowner, NULL, NULL, NULL,
13f302dc 1924 &psd) != ERROR_SUCCESS)) {
d70f60ae 1925#ifdef DEBUG_IPC
ac3337f6 1926 debug(("couldn't get owner info for filemap: %d\n",
1927 rc));
d70f60ae 1928#endif
32874aea 1929 return 0;
1930 }
016ef8ab 1931#ifdef DEBUG_IPC
ac3337f6 1932 {
13f302dc 1933 LPTSTR ours, ours2, theirs;
ac3337f6 1934 ConvertSidToStringSid(mapowner, &theirs);
1935 ConvertSidToStringSid(ourself, &ours);
13f302dc 1936 ConvertSidToStringSid(ourself2, &ours2);
1937 debug(("got sids:\n oursnew=%s\n oursold=%s\n"
1938 " theirs=%s\n", ours, ours2, theirs));
ac3337f6 1939 LocalFree(ours);
13f302dc 1940 LocalFree(ours2);
ac3337f6 1941 LocalFree(theirs);
1942 }
016ef8ab 1943#endif
13f302dc 1944 if (!EqualSid(mapowner, ourself) &&
98e5846b 1945 !EqualSid(mapowner, ourself2)) {
1946 CloseHandle(filemap);
32874aea 1947 return 0; /* security ID mismatch! */
98e5846b 1948 }
d70f60ae 1949#ifdef DEBUG_IPC
32874aea 1950 debug(("security stuff matched\n"));
d70f60ae 1951#endif
13f302dc 1952 LocalFree(psd);
1953 sfree(ourself);
1954 sfree(ourself2);
32874aea 1955 } else {
d70f60ae 1956#ifdef DEBUG_IPC
32874aea 1957 debug(("security APIs not present\n"));
d70f60ae 1958#endif
32874aea 1959 }
123bc6ea 1960#endif
32874aea 1961 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
d70f60ae 1962#ifdef DEBUG_IPC
32874aea 1963 debug(("p is %p\n", p));
1964 {
1965 int i;
1966 for (i = 0; i < 5; i++)
ac3337f6 1967 debug(("p[%d]=%02x\n", i,
1968 ((unsigned char *) p)[i]));
1969 }
d70f60ae 1970#endif
32874aea 1971 answer_msg(p);
1972 ret = 1;
1973 UnmapViewOfFile(p);
1974 }
1975 CloseHandle(filemap);
1976 return ret;
1977 }
5c58ad2d 1978 }
1979
32874aea 1980 return DefWindowProc(hwnd, message, wParam, lParam);
5c58ad2d 1981}
1982
ddecd643 1983/*
1984 * Fork and Exec the command in cmdline. [DBW]
1985 */
2c975497 1986void spawn_cmd(char *cmdline, char * args, int show)
32874aea 1987{
ddecd643 1988 if (ShellExecute(NULL, _T("open"), cmdline,
2c975497 1989 args, NULL, show) <= (HINSTANCE) 32) {
57356d63 1990 char *msg;
1991 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1992 (int)GetLastError());
1993 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1994 sfree(msg);
ddecd643 1995 }
1996}
1997
c44bf5bd 1998/*
1999 * This is a can't-happen stub, since Pageant never makes
2000 * asynchronous agent requests.
2001 */
2002void agent_schedule_callback(void (*callback)(void *, void *, int),
2003 void *callback_ctx, void *data, int len)
2004{
2005 assert(!"We shouldn't get here");
2006}
2007
cb2708d3 2008void cleanup_exit(int code)
2009{
2010 shutdown_help();
2011 exit(code);
2012}
93b581bd 2013
c44bf5bd 2014int flags = FLAG_SYNCAGENT;
2015
32874aea 2016int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
2017{
5c58ad2d 2018 WNDCLASS wndclass;
5c58ad2d 2019 MSG msg;
016ef8ab 2020 HMODULE advapi;
ddecd643 2021 char *command = NULL;
2022 int added_keys = 0;
d3a1a808 2023 int argc, i;
2024 char **argv, **argstart;
016ef8ab 2025
28339579 2026 hinst = inst;
2027 hwnd = NULL;
2028
016ef8ab 2029 /*
2030 * Determine whether we're an NT system (should have security
2031 * APIs) or a non-NT system (don't do security).
2032 */
4c48c989 2033 if (!init_winver())
2034 {
2035 modalfatalbox("Windows refuses to report a version");
2036 }
2037 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
32874aea 2038 has_security = TRUE;
016ef8ab 2039 } else
32874aea 2040 has_security = FALSE;
016ef8ab 2041
2042 if (has_security) {
123bc6ea 2043#ifndef NO_SECURITY
32874aea 2044 /*
2045 * Attempt to get the security API we need.
2046 */
ac3337f6 2047 if (!init_advapi()) {
32874aea 2048 MessageBox(NULL,
2049 "Unable to access security APIs. Pageant will\n"
2050 "not run, in case it causes a security breach.",
2051 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2052 return 1;
2053 }
123bc6ea 2054#else
2055 MessageBox(NULL,
2056 "This program has been compiled for Win9X and will\n"
2057 "not run on NT, in case it causes a security breach.",
2058 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2059 return 1;
2060#endif
016ef8ab 2061 } else
32874aea 2062 advapi = NULL;
5c58ad2d 2063
ddecd643 2064 /*
ecea795f 2065 * See if we can find our Help file.
2066 */
cb2708d3 2067 init_help();
ecea795f 2068
2069 /*
76b51e35 2070 * Look for the PuTTY binary (we will enable the saved session
2071 * submenu if we find it).
2072 */
2073 {
2074 char b[2048], *p, *q, *r;
2075 FILE *fp;
ee2b7cd8 2076 GetModuleFileName(NULL, b, sizeof(b) - 16);
76b51e35 2077 r = b;
2078 p = strrchr(b, '\\');
2079 if (p && p >= r) r = p+1;
2080 q = strrchr(b, ':');
2081 if (q && q >= r) r = q+1;
2082 strcpy(r, "putty.exe");
2083 if ( (fp = fopen(b, "r")) != NULL) {
2084 putty_path = dupstr(b);
2085 fclose(fp);
2086 } else
2087 putty_path = NULL;
2088 }
2089
2090 /*
ddecd643 2091 * Find out if Pageant is already running.
2092 */
2285d016 2093 already_running = agent_exists();
45f23490 2094
2285d016 2095 /*
2096 * Initialise storage for RSA keys.
2097 */
2098 if (!already_running) {
ddecd643 2099 rsakeys = newtree234(cmpkeys_rsa);
2100 ssh2keys = newtree234(cmpkeys_ssh2);
ddecd643 2101 }
dacbd0e8 2102
2103 /*
0959acf7 2104 * Initialise storage for short-term passphrase cache.
2105 */
2106 passphrases = newtree234(NULL);
2107
2108 /*
45cebe79 2109 * Process the command line and add keys as listed on it.
5c58ad2d 2110 */
d3a1a808 2111 split_into_argv(cmdline, &argc, &argv, &argstart);
2112 for (i = 0; i < argc; i++) {
2285d016 2113 if (!strcmp(argv[i], "-pgpfp")) {
2114 pgp_fingerprints();
2115 if (advapi)
2116 FreeLibrary(advapi);
2117 return 1;
2118 } else if (!strcmp(argv[i], "-c")) {
d3a1a808 2119 /*
2120 * If we see `-c', then the rest of the
2121 * command line should be treated as a
2122 * command to be spawned.
2123 */
2124 if (i < argc-1)
2125 command = argstart[i+1];
2126 else
2127 command = "";
2128 break;
2129 } else {
9a30e26b 2130 add_keyfile(filename_from_str(argv[i]));
d3a1a808 2131 added_keys = TRUE;
32874aea 2132 }
5c58ad2d 2133 }
2134
0959acf7 2135 /*
2136 * Forget any passphrase that we retained while going over
2137 * command line keyfiles.
2138 */
2139 forget_passphrases();
2140
2c975497 2141 if (command) {
2142 char *args;
2143 if (command[0] == '"')
2144 args = strchr(++command, '"');
2145 else
2146 args = strchr(command, ' ');
2147 if (args) {
2148 *args++ = 0;
2149 while(*args && isspace(*args)) args++;
2150 }
2151 spawn_cmd(command, args, show);
2152 }
ddecd643 2153
2154 /*
2155 * If Pageant was already running, we leave now. If we haven't
2156 * even taken any auxiliary action (spawned a command or added
2157 * keys), complain.
2158 */
2159 if (already_running) {
2160 if (!command && !added_keys) {
2161 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2162 MB_ICONERROR | MB_OK);
2163 }
32874aea 2164 if (advapi)
2165 FreeLibrary(advapi);
2166 return 0;
ddecd643 2167 }
2168
2285d016 2169 if (!prev) {
2170 wndclass.style = 0;
2171 wndclass.lpfnWndProc = WndProc;
2172 wndclass.cbClsExtra = 0;
2173 wndclass.cbWndExtra = 0;
2174 wndclass.hInstance = inst;
2175 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
2176 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
2177 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
2178 wndclass.lpszMenuName = NULL;
2179 wndclass.lpszClassName = APPNAME;
2180
2181 RegisterClass(&wndclass);
2182 }
2183
2184 keylist = NULL;
2185
2186 hwnd = CreateWindow(APPNAME, APPNAME,
2187 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
2188 CW_USEDEFAULT, CW_USEDEFAULT,
2189 100, 100, NULL, NULL, inst, NULL);
2190
2191 /* Set up a system tray icon */
2192 AddTrayIcon(hwnd);
2193
2194 /* Accelerators used: nsvkxa */
2195 systray_menu = CreatePopupMenu();
2196 if (putty_path) {
2197 session_menu = CreateMenu();
2198 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
2199 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
2200 (UINT) session_menu, "&Saved Sessions");
2201 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2202 }
2203 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
2204 "&View Keys");
2205 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
2206 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
cb2708d3 2207 if (has_help())
2285d016 2208 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
2209 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
2210 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2211 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
2212 initial_menuitems_count = GetMenuItemCount(session_menu);
2213
2214 /* Set the default menu item. */
2215 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
2216
2217 ShowWindow(hwnd, SW_HIDE);
2218
5c58ad2d 2219 /*
dacbd0e8 2220 * Main message loop.
5c58ad2d 2221 */
5c58ad2d 2222 while (GetMessage(&msg, NULL, 0, 0) == 1) {
6de8ca59 2223 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2224 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2225 TranslateMessage(&msg);
2226 DispatchMessage(&msg);
2227 }
5c58ad2d 2228 }
2229
2230 /* Clean up the system tray icon */
2231 {
32874aea 2232 NOTIFYICONDATA tnid;
5c58ad2d 2233
32874aea 2234 tnid.cbSize = sizeof(NOTIFYICONDATA);
28339579 2235 tnid.hWnd = hwnd;
32874aea 2236 tnid.uID = 1;
5c58ad2d 2237
32874aea 2238 Shell_NotifyIcon(NIM_DELETE, &tnid);
5c58ad2d 2239
32874aea 2240 DestroyMenu(systray_menu);
5c58ad2d 2241 }
2242
9754e5ca 2243 if (keypath) filereq_free(keypath);
2244
32874aea 2245 if (advapi)
2246 FreeLibrary(advapi);
cb2708d3 2247
2248 cleanup_exit(msg.wParam);
2249 return msg.wParam; /* just in case optimiser complains */
5c58ad2d 2250}