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