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