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