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