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