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