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