Tweak to make it more obvious that the System menu is only available from
[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);
122static void *get_keylist1(void);
123static void *get_keylist2(void);
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;
417 int i, nkeys, bloblen;
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 }
425 keylist = get_keylist1();
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
441 keylist = get_keylist2();
442 }
443 if (keylist) {
444 nkeys = GET_32BIT(keylist);
445 p = keylist + 4;
446
447 for (i = 0; i < nkeys; i++) {
448 if (!memcmp(blob, p, bloblen)) {
449 /* Key is already present; we can now leave. */
450 sfree(keylist);
451 sfree(blob);
452 return;
453 }
454 /* Now skip over public blob */
231ee168 455 if (type == SSH_KEYTYPE_SSH1)
3f2d010c 456 p += rsa_public_blob_len(p);
457 else
458 p += 4 + GET_32BIT(p);
459 /* Now skip over comment field */
460 p += 4 + GET_32BIT(p);
461 }
462
463 sfree(keylist);
464 }
465
466 sfree(blob);
467 }
468
231ee168 469 if (type == SSH_KEYTYPE_SSH1)
9a30e26b 470 needs_pass = rsakey_encrypted(&filename, &comment);
45cebe79 471 else
9a30e26b 472 needs_pass = ssh2_userkey_encrypted(&filename, &comment);
5c58ad2d 473 attempts = 0;
231ee168 474 if (type == SSH_KEYTYPE_SSH1)
3d88e64d 475 rkey = snew(struct RSAKey);
d4de2d2a 476 pps.passphrase = passphrase;
477 pps.comment = comment;
0959acf7 478 original_pass = 0;
5c58ad2d 479 do {
32874aea 480 if (needs_pass) {
0959acf7 481 /* try all the remembered passphrases first */
482 char *pp = index234(passphrases, attempts);
483 if(pp) {
484 strcpy(passphrase, pp);
485 } else {
486 int dlgret;
487 original_pass = 1;
488 dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
489 NULL, PassphraseProc, (LPARAM) & pps);
490 passphrase_box = NULL;
491 if (!dlgret) {
492 if (comment)
493 sfree(comment);
231ee168 494 if (type == SSH_KEYTYPE_SSH1)
0959acf7 495 sfree(rkey);
496 return; /* operation cancelled */
497 }
32874aea 498 }
499 } else
500 *passphrase = '\0';
231ee168 501 if (type == SSH_KEYTYPE_SSH1)
222d54dc 502 ret = loadrsakey(&filename, rkey, passphrase, NULL);
45cebe79 503 else {
222d54dc 504 skey = ssh2_load_userkey(&filename, passphrase, NULL);
45cebe79 505 if (skey == SSH2_WRONG_PASSPHRASE)
506 ret = -1;
507 else if (!skey)
508 ret = 0;
509 else
510 ret = 1;
511 }
32874aea 512 attempts++;
5c58ad2d 513 } while (ret == -1);
0959acf7 514
515 /* if they typed in an ok passphrase, remember it */
516 if(original_pass && ret) {
517 char *pp = dupstr(passphrase);
518 addpos234(passphrases, pp, 0);
519 }
520
32874aea 521 if (comment)
522 sfree(comment);
5c58ad2d 523 if (ret == 0) {
32874aea 524 MessageBox(NULL, "Couldn't load private key.", APPNAME,
525 MB_OK | MB_ICONERROR);
231ee168 526 if (type == SSH_KEYTYPE_SSH1)
45cebe79 527 sfree(rkey);
32874aea 528 return;
5c58ad2d 529 }
231ee168 530 if (type == SSH_KEYTYPE_SSH1) {
ddecd643 531 if (already_running) {
532 unsigned char *request, *response;
2d466ffd 533 void *vresponse;
839f10db 534 int reqlen, clen, resplen, ret;
ddecd643 535
ddecd643 536 clen = strlen(rkey->comment);
ddecd643 537
538 reqlen = 4 + 1 + /* length, message type */
539 4 + /* bit count */
540 ssh1_bignum_length(rkey->modulus) +
541 ssh1_bignum_length(rkey->exponent) +
542 ssh1_bignum_length(rkey->private_exponent) +
543 ssh1_bignum_length(rkey->iqmp) +
544 ssh1_bignum_length(rkey->p) +
32874aea 545 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
ddecd643 546 ;
ddecd643 547
3d88e64d 548 request = snewn(reqlen, unsigned char);
ddecd643 549
ddecd643 550 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
ddecd643 551 reqlen = 5;
32874aea 552 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
ddecd643 553 reqlen += 4;
32874aea 554 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
555 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
556 reqlen +=
557 ssh1_write_bignum(request + reqlen,
558 rkey->private_exponent);
559 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
560 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
561 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
562 PUT_32BIT(request + reqlen, clen);
563 memcpy(request + reqlen + 4, rkey->comment, clen);
564 reqlen += 4 + clen;
565 PUT_32BIT(request, reqlen - 4);
ddecd643 566
839f10db 567 ret = agent_query(request, reqlen, &vresponse, &resplen,
568 NULL, NULL);
569 assert(ret == 1);
2d466ffd 570 response = vresponse;
ddecd643 571 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
572 MessageBox(NULL, "The already running Pageant "
573 "refused to add the key.", APPNAME,
574 MB_OK | MB_ICONERROR);
3f2d010c 575
576 sfree(request);
577 sfree(response);
ddecd643 578 } else {
579 if (add234(rsakeys, rkey) != rkey)
580 sfree(rkey); /* already present, don't waste RAM */
581 }
45cebe79 582 } else {
ddecd643 583 if (already_running) {
584 unsigned char *request, *response;
2d466ffd 585 void *vresponse;
839f10db 586 int reqlen, alglen, clen, keybloblen, resplen, ret;
ddecd643 587 alglen = strlen(skey->alg->name);
ddecd643 588 clen = strlen(skey->comment);
ddecd643 589
ddecd643 590 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
ddecd643 591
ddecd643 592 reqlen = 4 + 1 + /* length, message type */
593 4 + alglen + /* algorithm name */
594 keybloblen + /* key data */
595 4 + clen /* comment */
596 ;
ddecd643 597
3d88e64d 598 request = snewn(reqlen, unsigned char);
ddecd643 599
600 request[4] = SSH2_AGENTC_ADD_IDENTITY;
ddecd643 601 reqlen = 5;
32874aea 602 PUT_32BIT(request + reqlen, alglen);
ddecd643 603 reqlen += 4;
32874aea 604 memcpy(request + reqlen, skey->alg->name, alglen);
ddecd643 605 reqlen += alglen;
ddecd643 606 reqlen += skey->alg->openssh_fmtkey(skey->data,
32874aea 607 request + reqlen,
608 keybloblen);
609 PUT_32BIT(request + reqlen, clen);
610 memcpy(request + reqlen + 4, skey->comment, clen);
611 PUT_32BIT(request, reqlen - 4);
612 reqlen += clen + 4;
ddecd643 613
839f10db 614 ret = agent_query(request, reqlen, &vresponse, &resplen,
615 NULL, NULL);
616 assert(ret == 1);
2d466ffd 617 response = vresponse;
ddecd643 618 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
cc93d5e6 619 MessageBox(NULL, "The already running Pageant "
ddecd643 620 "refused to add the key.", APPNAME,
621 MB_OK | MB_ICONERROR);
3f2d010c 622
623 sfree(request);
624 sfree(response);
ddecd643 625 } else {
626 if (add234(ssh2keys, skey) != skey) {
627 skey->alg->freekey(skey->data);
628 sfree(skey); /* already present, don't waste RAM */
629 }
45cebe79 630 }
631 }
5c58ad2d 632}
633
634/*
3f2d010c 635 * Create an SSH1 key list in a malloc'ed buffer; return its
636 * length.
637 */
638static void *make_keylist1(int *length)
639{
640 int i, nkeys, len;
641 struct RSAKey *key;
642 unsigned char *blob, *p, *ret;
643 int bloblen;
644
645 /*
646 * Count up the number and length of keys we hold.
647 */
648 len = 4;
649 nkeys = 0;
650 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
651 nkeys++;
652 blob = rsa_public_blob(key, &bloblen);
653 len += bloblen;
654 sfree(blob);
655 len += 4 + strlen(key->comment);
656 }
657
658 /* Allocate the buffer. */
3d88e64d 659 p = ret = snewn(len, unsigned char);
3f2d010c 660 if (length) *length = len;
661
662 PUT_32BIT(p, nkeys);
663 p += 4;
664 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
665 blob = rsa_public_blob(key, &bloblen);
666 memcpy(p, blob, bloblen);
667 p += bloblen;
668 sfree(blob);
669 PUT_32BIT(p, strlen(key->comment));
670 memcpy(p + 4, key->comment, strlen(key->comment));
671 p += 4 + strlen(key->comment);
672 }
673
674 assert(p - ret == len);
675 return ret;
676}
677
678/*
679 * Create an SSH2 key list in a malloc'ed buffer; return its
680 * length.
681 */
682static void *make_keylist2(int *length)
683{
684 struct ssh2_userkey *key;
685 int i, len, nkeys;
686 unsigned char *blob, *p, *ret;
687 int bloblen;
688
689 /*
690 * Count up the number and length of keys we hold.
691 */
692 len = 4;
693 nkeys = 0;
694 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
695 nkeys++;
696 len += 4; /* length field */
697 blob = key->alg->public_blob(key->data, &bloblen);
698 len += bloblen;
699 sfree(blob);
700 len += 4 + strlen(key->comment);
701 }
702
703 /* Allocate the buffer. */
3d88e64d 704 p = ret = snewn(len, unsigned char);
3f2d010c 705 if (length) *length = len;
706
707 /*
708 * Packet header is the obvious five bytes, plus four
709 * bytes for the key count.
710 */
711 PUT_32BIT(p, nkeys);
712 p += 4;
713 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
714 blob = key->alg->public_blob(key->data, &bloblen);
715 PUT_32BIT(p, bloblen);
716 p += 4;
717 memcpy(p, blob, bloblen);
718 p += bloblen;
719 sfree(blob);
720 PUT_32BIT(p, strlen(key->comment));
721 memcpy(p + 4, key->comment, strlen(key->comment));
722 p += 4 + strlen(key->comment);
723 }
724
725 assert(p - ret == len);
726 return ret;
727}
728
729/*
730 * Acquire a keylist1 from the primary Pageant; this means either
731 * calling make_keylist1 (if that's us) or sending a message to the
732 * primary Pageant (if it's not).
733 */
734static void *get_keylist1(void)
735{
736 void *ret;
737
738 if (already_running) {
739 unsigned char request[5], *response;
740 void *vresponse;
839f10db 741 int resplen, retval;
3f2d010c 742 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
743 PUT_32BIT(request, 4);
744
839f10db 745 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
746 assert(retval == 1);
3f2d010c 747 response = vresponse;
748 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)
749 return NULL;
750
3d88e64d 751 ret = snewn(resplen-5, unsigned char);
3f2d010c 752 memcpy(ret, response+5, resplen-5);
753 sfree(response);
754 } else {
755 ret = make_keylist1(NULL);
756 }
757 return ret;
758}
759
760/*
761 * Acquire a keylist2 from the primary Pageant; this means either
762 * calling make_keylist2 (if that's us) or sending a message to the
763 * primary Pageant (if it's not).
764 */
765static void *get_keylist2(void)
766{
767 void *ret;
768
769 if (already_running) {
770 unsigned char request[5], *response;
771 void *vresponse;
839f10db 772 int resplen, retval;
3f2d010c 773
774 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
775 PUT_32BIT(request, 4);
776
839f10db 777 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
778 assert(retval == 1);
3f2d010c 779 response = vresponse;
780 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)
781 return NULL;
782
3d88e64d 783 ret = snewn(resplen-5, unsigned char);
3f2d010c 784 memcpy(ret, response+5, resplen-5);
785 sfree(response);
786 } else {
787 ret = make_keylist2(NULL);
788 }
789 return ret;
790}
791
792/*
5c58ad2d 793 * This is the main agent function that answers messages.
794 */
32874aea 795static void answer_msg(void *msg)
796{
d70f60ae 797 unsigned char *p = msg;
798 unsigned char *ret = msg;
5c58ad2d 799 int type;
800
5c58ad2d 801 /*
802 * Get the message type.
803 */
804 type = p[4];
805
806 p += 5;
5c58ad2d 807 switch (type) {
45cebe79 808 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
32874aea 809 /*
810 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
811 */
812 {
3f2d010c 813 int len;
814 void *keylist;
32874aea 815
32874aea 816 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
3f2d010c 817 keylist = make_keylist1(&len);
818 if (len + 5 > AGENT_MAX_MSGLEN) {
819 sfree(keylist);
820 goto failure;
32874aea 821 }
3f2d010c 822 PUT_32BIT(ret, len + 1);
823 memcpy(ret + 5, keylist, len);
824 sfree(keylist);
32874aea 825 }
826 break;
45cebe79 827 case SSH2_AGENTC_REQUEST_IDENTITIES:
32874aea 828 /*
829 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
830 */
831 {
3f2d010c 832 int len;
833 void *keylist;
32874aea 834
32874aea 835 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
3f2d010c 836 keylist = make_keylist2(&len);
837 if (len + 5 > AGENT_MAX_MSGLEN) {
838 sfree(keylist);
839 goto failure;
32874aea 840 }
3f2d010c 841 PUT_32BIT(ret, len + 1);
842 memcpy(ret + 5, keylist, len);
843 sfree(keylist);
32874aea 844 }
845 break;
45cebe79 846 case SSH1_AGENTC_RSA_CHALLENGE:
32874aea 847 /*
848 * Reply with either SSH1_AGENT_RSA_RESPONSE or
849 * SSH_AGENT_FAILURE, depending on whether we have that key
850 * or not.
851 */
852 {
853 struct RSAKey reqkey, *key;
854 Bignum challenge, response;
855 unsigned char response_source[48], response_md5[16];
856 struct MD5Context md5c;
857 int i, len;
858
859 p += 4;
860 p += ssh1_read_bignum(p, &reqkey.exponent);
861 p += ssh1_read_bignum(p, &reqkey.modulus);
862 p += ssh1_read_bignum(p, &challenge);
863 memcpy(response_source + 32, p, 16);
864 p += 16;
865 if (GET_32BIT(p) != 1 ||
866 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
867 freebn(reqkey.exponent);
868 freebn(reqkey.modulus);
869 freebn(challenge);
870 goto failure;
871 }
872 response = rsadecrypt(challenge, key);
873 for (i = 0; i < 32; i++)
874 response_source[i] = bignum_byte(response, 31 - i);
875
876 MD5Init(&md5c);
877 MD5Update(&md5c, response_source, 48);
878 MD5Final(response_md5, &md5c);
879 memset(response_source, 0, 48); /* burn the evidence */
880 freebn(response); /* and that evidence */
881 freebn(challenge); /* yes, and that evidence */
882 freebn(reqkey.exponent); /* and free some memory ... */
883 freebn(reqkey.modulus); /* ... while we're at it. */
884
885 /*
886 * Packet is the obvious five byte header, plus sixteen
887 * bytes of MD5.
888 */
889 len = 5 + 16;
890 PUT_32BIT(ret, len - 4);
891 ret[4] = SSH1_AGENT_RSA_RESPONSE;
892 memcpy(ret + 5, response_md5, 16);
893 }
894 break;
45cebe79 895 case SSH2_AGENTC_SIGN_REQUEST:
32874aea 896 /*
5c72ca61 897 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
32874aea 898 * SSH_AGENT_FAILURE, depending on whether we have that key
899 * or not.
900 */
45cebe79 901 {
902 struct ssh2_userkey *key;
903 struct blob b;
904 unsigned char *data, *signature;
905 int datalen, siglen, len;
906
907 b.len = GET_32BIT(p);
908 p += 4;
909 b.blob = p;
910 p += b.len;
911 datalen = GET_32BIT(p);
912 p += 4;
913 data = p;
914 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
915 if (!key)
916 goto failure;
917 signature = key->alg->sign(key->data, data, datalen, &siglen);
32874aea 918 len = 5 + 4 + siglen;
919 PUT_32BIT(ret, len - 4);
920 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
921 PUT_32BIT(ret + 5, siglen);
922 memcpy(ret + 5 + 4, signature, siglen);
45cebe79 923 sfree(signature);
924 }
925 break;
926 case SSH1_AGENTC_ADD_RSA_IDENTITY:
32874aea 927 /*
928 * Add to the list and return SSH_AGENT_SUCCESS, or
929 * SSH_AGENT_FAILURE if the key was malformed.
930 */
931 {
932 struct RSAKey *key;
933 char *comment;
080954b8 934 int commentlen;
3d88e64d 935 key = snew(struct RSAKey);
28edeffe 936 memset(key, 0, sizeof(struct RSAKey));
32874aea 937 p += makekey(p, key, NULL, 1);
938 p += makeprivate(p, key);
080954b8 939 p += ssh1_read_bignum(p, &key->iqmp); /* p^-1 mod q */
940 p += ssh1_read_bignum(p, &key->p); /* p */
941 p += ssh1_read_bignum(p, &key->q); /* q */
942 commentlen = GET_32BIT(p);
3d88e64d 943 comment = snewn(commentlen+1, char);
32874aea 944 if (comment) {
080954b8 945 memcpy(comment, p + 4, commentlen);
946 comment[commentlen] = '\0';
32874aea 947 key->comment = comment;
948 }
949 PUT_32BIT(ret, 1);
950 ret[4] = SSH_AGENT_FAILURE;
951 if (add234(rsakeys, key) == key) {
952 keylist_update();
953 ret[4] = SSH_AGENT_SUCCESS;
954 } else {
955 freersakey(key);
956 sfree(key);
957 }
958 }
959 break;
45cebe79 960 case SSH2_AGENTC_ADD_IDENTITY:
32874aea 961 /*
962 * Add to the list and return SSH_AGENT_SUCCESS, or
963 * SSH_AGENT_FAILURE if the key was malformed.
964 */
965 {
966 struct ssh2_userkey *key;
967 char *comment, *alg;
45cebe79 968 int alglen, commlen;
969 int bloblen;
970
3d88e64d 971 key = snew(struct ssh2_userkey);
45cebe79 972
32874aea 973 alglen = GET_32BIT(p);
974 p += 4;
975 alg = p;
976 p += alglen;
45cebe79 977 /* Add further algorithm names here. */
978 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
979 key->alg = &ssh_rsa;
5c72ca61 980 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
981 key->alg = &ssh_dss;
45cebe79 982 else {
983 sfree(key);
984 goto failure;
985 }
986
32874aea 987 bloblen =
988 GET_32BIT((unsigned char *) msg) - (p -
989 (unsigned char *) msg -
990 4);
45cebe79 991 key->data = key->alg->openssh_createkey(&p, &bloblen);
992 if (!key->data) {
993 sfree(key);
994 goto failure;
995 }
32874aea 996 commlen = GET_32BIT(p);
997 p += 4;
45cebe79 998
3d88e64d 999 comment = snewn(commlen + 1, char);
32874aea 1000 if (comment) {
1001 memcpy(comment, p, commlen);
45cebe79 1002 comment[commlen] = '\0';
32874aea 1003 }
45cebe79 1004 key->comment = comment;
1005
32874aea 1006 PUT_32BIT(ret, 1);
1007 ret[4] = SSH_AGENT_FAILURE;
1008 if (add234(ssh2keys, key) == key) {
1009 keylist_update();
1010 ret[4] = SSH_AGENT_SUCCESS;
1011 } else {
45cebe79 1012 key->alg->freekey(key->data);
1013 sfree(key->comment);
32874aea 1014 sfree(key);
1015 }
1016 }
1017 break;
45cebe79 1018 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
32874aea 1019 /*
1020 * Remove from the list and return SSH_AGENT_SUCCESS, or
1021 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1022 * start with.
1023 */
1024 {
1025 struct RSAKey reqkey, *key;
1026
1027 p += makekey(p, &reqkey, NULL, 0);
1028 key = find234(rsakeys, &reqkey, NULL);
1029 freebn(reqkey.exponent);
1030 freebn(reqkey.modulus);
1031 PUT_32BIT(ret, 1);
1032 ret[4] = SSH_AGENT_FAILURE;
1033 if (key) {
1034 del234(rsakeys, key);
1035 keylist_update();
1036 freersakey(key);
45cebe79 1037 sfree(key);
32874aea 1038 ret[4] = SSH_AGENT_SUCCESS;
1039 }
1040 }
1041 break;
45cebe79 1042 case SSH2_AGENTC_REMOVE_IDENTITY:
32874aea 1043 /*
1044 * Remove from the list and return SSH_AGENT_SUCCESS, or
1045 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1046 * start with.
1047 */
1048 {
1049 struct ssh2_userkey *key;
45cebe79 1050 struct blob b;
1051
1052 b.len = GET_32BIT(p);
1053 p += 4;
1054 b.blob = p;
1055 p += b.len;
1056 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1057 if (!key)
1058 goto failure;
1059
32874aea 1060 PUT_32BIT(ret, 1);
1061 ret[4] = SSH_AGENT_FAILURE;
1062 if (key) {
1063 del234(ssh2keys, key);
1064 keylist_update();
45cebe79 1065 key->alg->freekey(key->data);
1066 sfree(key);
32874aea 1067 ret[4] = SSH_AGENT_SUCCESS;
1068 }
1069 }
1070 break;
45cebe79 1071 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
32874aea 1072 /*
1073 * Remove all SSH1 keys. Always returns success.
1074 */
1075 {
45cebe79 1076 struct RSAKey *rkey;
45cebe79 1077
32874aea 1078 while ((rkey = index234(rsakeys, 0)) != NULL) {
1079 del234(rsakeys, rkey);
45cebe79 1080 freersakey(rkey);
1081 sfree(rkey);
32874aea 1082 }
45cebe79 1083 keylist_update();
1084
1085 PUT_32BIT(ret, 1);
1086 ret[4] = SSH_AGENT_SUCCESS;
32874aea 1087 }
1088 break;
45cebe79 1089 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
32874aea 1090 /*
1091 * Remove all SSH2 keys. Always returns success.
1092 */
1093 {
1094 struct ssh2_userkey *skey;
1095
1096 while ((skey = index234(ssh2keys, 0)) != NULL) {
1097 del234(ssh2keys, skey);
45cebe79 1098 skey->alg->freekey(skey->data);
1099 sfree(skey);
32874aea 1100 }
45cebe79 1101 keylist_update();
1102
1103 PUT_32BIT(ret, 1);
1104 ret[4] = SSH_AGENT_SUCCESS;
32874aea 1105 }
1106 break;
5c58ad2d 1107 default:
32874aea 1108 failure:
1109 /*
1110 * Unrecognised message. Return SSH_AGENT_FAILURE.
1111 */
1112 PUT_32BIT(ret, 1);
1113 ret[4] = SSH_AGENT_FAILURE;
1114 break;
5c58ad2d 1115 }
5c58ad2d 1116}
1117
1118/*
1119 * Key comparison function for the 2-3-4 tree of RSA keys.
1120 */
32874aea 1121static int cmpkeys_rsa(void *av, void *bv)
1122{
1123 struct RSAKey *a = (struct RSAKey *) av;
1124 struct RSAKey *b = (struct RSAKey *) bv;
5c58ad2d 1125 Bignum am, bm;
1126 int alen, blen;
1127
1128 am = a->modulus;
1129 bm = b->modulus;
1130 /*
1131 * Compare by length of moduli.
1132 */
ddecd643 1133 alen = bignum_bitcount(am);
1134 blen = bignum_bitcount(bm);
32874aea 1135 if (alen > blen)
1136 return +1;
1137 else if (alen < blen)
1138 return -1;
5c58ad2d 1139 /*
1140 * Now compare by moduli themselves.
1141 */
32874aea 1142 alen = (alen + 7) / 8; /* byte count */
5c58ad2d 1143 while (alen-- > 0) {
32874aea 1144 int abyte, bbyte;
1145 abyte = bignum_byte(am, alen);
1146 bbyte = bignum_byte(bm, alen);
1147 if (abyte > bbyte)
1148 return +1;
1149 else if (abyte < bbyte)
1150 return -1;
5c58ad2d 1151 }
1152 /*
1153 * Give up.
1154 */
1155 return 0;
1156}
1157
45cebe79 1158/*
1159 * Key comparison function for the 2-3-4 tree of SSH2 keys.
1160 */
32874aea 1161static int cmpkeys_ssh2(void *av, void *bv)
1162{
1163 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1164 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
45cebe79 1165 int i;
1166 int alen, blen;
1167 unsigned char *ablob, *bblob;
1168 int c;
32874aea 1169
45cebe79 1170 /*
1171 * Compare purely by public blob.
1172 */
1173 ablob = a->alg->public_blob(a->data, &alen);
1174 bblob = b->alg->public_blob(b->data, &blen);
1175
1176 c = 0;
1177 for (i = 0; i < alen && i < blen; i++) {
1178 if (ablob[i] < bblob[i]) {
32874aea 1179 c = -1;
1180 break;
45cebe79 1181 } else if (ablob[i] > bblob[i]) {
32874aea 1182 c = +1;
1183 break;
45cebe79 1184 }
1185 }
32874aea 1186 if (c == 0 && i < alen)
1187 c = +1; /* a is longer */
1188 if (c == 0 && i < blen)
1189 c = -1; /* a is longer */
45cebe79 1190
1191 sfree(ablob);
1192 sfree(bblob);
1193
1194 return c;
1195}
1196
1197/*
1198 * Key comparison function for looking up a blob in the 2-3-4 tree
1199 * of SSH2 keys.
1200 */
32874aea 1201static int cmpkeys_ssh2_asymm(void *av, void *bv)
1202{
1203 struct blob *a = (struct blob *) av;
1204 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
45cebe79 1205 int i;
1206 int alen, blen;
1207 unsigned char *ablob, *bblob;
1208 int c;
32874aea 1209
45cebe79 1210 /*
1211 * Compare purely by public blob.
1212 */
1213 ablob = a->blob;
1214 alen = a->len;
1215 bblob = b->alg->public_blob(b->data, &blen);
1216
1217 c = 0;
1218 for (i = 0; i < alen && i < blen; i++) {
1219 if (ablob[i] < bblob[i]) {
32874aea 1220 c = -1;
1221 break;
45cebe79 1222 } else if (ablob[i] > bblob[i]) {
32874aea 1223 c = +1;
1224 break;
45cebe79 1225 }
1226 }
32874aea 1227 if (c == 0 && i < alen)
1228 c = +1; /* a is longer */
1229 if (c == 0 && i < blen)
1230 c = -1; /* a is longer */
45cebe79 1231
1232 sfree(bblob);
1233
1234 return c;
1235}
1236
5c58ad2d 1237/*
ab162329 1238 * Prompt for a key file to add, and add it.
1239 */
32874aea 1240static void prompt_add_keyfile(void)
1241{
ab162329 1242 OPENFILENAME of;
1243 char filename[FILENAME_MAX];
3d88e64d 1244 char *filelist = snewn(8192, char);
0959acf7 1245 char *filewalker;
1246 int n, dirlen;
1247
ab162329 1248 memset(&of, 0, sizeof(of));
1249#ifdef OPENFILENAME_SIZE_VERSION_400
1250 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
1251#else
1252 of.lStructSize = sizeof(of);
1253#endif
ecea795f 1254 of.hwndOwner = main_hwnd;
b1ccd98e 1255 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
1256 "All Files (*.*)\0*\0\0\0";
ab162329 1257 of.lpstrCustomFilter = NULL;
1258 of.nFilterIndex = 1;
0959acf7 1259 of.lpstrFile = filelist;
1260 *filelist = '\0';
1261 of.nMaxFile = FILENAME_MAX;
ab162329 1262 of.lpstrFileTitle = NULL;
1263 of.lpstrInitialDir = NULL;
1264 of.lpstrTitle = "Select Private Key File";
0959acf7 1265 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
ab162329 1266 if (GetOpenFileName(&of)) {
0959acf7 1267 if(strlen(filelist) > of.nFileOffset)
1268 /* Only one filename returned? */
9a30e26b 1269 add_keyfile(filename_from_str(filelist));
0959acf7 1270 else {
1271 /* we are returned a bunch of strings, end to
1272 * end. first string is the directory, the
1273 * rest the filenames. terminated with an
1274 * empty string.
1275 */
1276 filewalker = filelist;
1277 dirlen = strlen(filewalker);
1278 if(dirlen > FILENAME_MAX - 8) return;
1279 memcpy(filename, filewalker, dirlen);
1280
1281 filewalker += dirlen + 1;
1282 filename[dirlen++] = '\\';
1283
1284 /* then go over names one by one */
1285 for(;;) {
1286 n = strlen(filewalker) + 1;
1287 /* end of the list */
1288 if(n == 1)
1289 break;
1290 /* too big, shouldn't happen */
1291 if(n + dirlen > FILENAME_MAX)
1292 break;
1293
1294 memcpy(filename + dirlen, filewalker, n);
1295 filewalker += n;
1296
9a30e26b 1297 add_keyfile(filename_from_str(filename));
0959acf7 1298 }
1299 }
1300
32874aea 1301 keylist_update();
0959acf7 1302 forget_passphrases();
ab162329 1303 }
0959acf7 1304 sfree(filelist);
ab162329 1305}
1306
1307/*
5c58ad2d 1308 * Dialog-box function for the key list box.
1309 */
1310static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
32874aea 1311 WPARAM wParam, LPARAM lParam)
1312{
45cebe79 1313 struct RSAKey *rkey;
1314 struct ssh2_userkey *skey;
5c58ad2d 1315
1316 switch (msg) {
1317 case WM_INITDIALOG:
45cebe79 1318 /*
1319 * Centre the window.
1320 */
1321 { /* centre the window */
1322 RECT rs, rd;
1323 HWND hw;
1324
1325 hw = GetDesktopWindow();
32874aea 1326 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1327 MoveWindow(hwnd,
1328 (rs.right + rs.left + rd.left - rd.right) / 2,
1329 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1330 rd.right - rd.left, rd.bottom - rd.top, TRUE);
45cebe79 1331 }
1332
ecea795f 1333 if (help_path)
1334 SetWindowLong(hwnd, GWL_EXSTYLE,
1335 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
1336 else {
1337 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1338 if (item)
1339 DestroyWindow(item);
1340 }
1341 requested_help = FALSE;
1342
32874aea 1343 keylist = hwnd;
1c2a93c4 1344 {
32874aea 1345 static int tabs[] = { 35, 60, 210 };
1346 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1347 sizeof(tabs) / sizeof(*tabs),
1348 (LPARAM) tabs);
1c2a93c4 1349 }
32874aea 1350 keylist_update();
1351 return 0;
5c58ad2d 1352 case WM_COMMAND:
1353 switch (LOWORD(wParam)) {
1354 case IDOK:
1355 case IDCANCEL:
32874aea 1356 keylist = NULL;
1357 DestroyWindow(hwnd);
1358 return 0;
1359 case 101: /* add key */
5c58ad2d 1360 if (HIWORD(wParam) == BN_CLICKED ||
1361 HIWORD(wParam) == BN_DOUBLECLICKED) {
cb5ca813 1362 if (passphrase_box) {
1363 MessageBeep(MB_ICONERROR);
1364 SetForegroundWindow(passphrase_box);
1365 break;
1366 }
32874aea 1367 prompt_add_keyfile();
1368 }
1369 return 0;
1370 case 102: /* remove key */
5c58ad2d 1371 if (HIWORD(wParam) == BN_CLICKED ||
1372 HIWORD(wParam) == BN_DOUBLECLICKED) {
d2371c81 1373 int i;
0959acf7 1374 int rCount, sCount;
1375 int *selectedArray;
1376
1377 /* our counter within the array of selected items */
1378 int itemNum;
1379
1380 /* get the number of items selected in the list */
1381 int numSelected =
1382 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1383
1384 /* none selected? that was silly */
1385 if (numSelected == 0) {
5c58ad2d 1386 MessageBeep(0);
1387 break;
1388 }
0959acf7 1389
1390 /* get item indices in an array */
3d88e64d 1391 selectedArray = snewn(numSelected, int);
0959acf7 1392 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1393 numSelected, (WPARAM)selectedArray);
1394
1395 itemNum = numSelected - 1;
1396 rCount = count234(rsakeys);
1397 sCount = count234(ssh2keys);
1398
1399 /* go through the non-rsakeys until we've covered them all,
1400 * and/or we're out of selected items to check. note that
1401 * we go *backwards*, to avoid complications from deleting
1402 * things hence altering the offset of subsequent items
1403 */
1404 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1405 skey = index234(ssh2keys, i);
1406
1407 if (selectedArray[itemNum] == rCount + i) {
1408 del234(ssh2keys, skey);
1409 skey->alg->freekey(skey->data);
1410 sfree(skey);
1411 itemNum--;
1412 }
45cebe79 1413 }
0959acf7 1414
1415 /* do the same for the rsa keys */
1416 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1417 rkey = index234(rsakeys, i);
1418
1419 if(selectedArray[itemNum] == i) {
1420 del234(rsakeys, rkey);
1421 freersakey(rkey);
1422 sfree(rkey);
1423 itemNum--;
1424 }
1425 }
1426
1427 sfree(selectedArray);
32874aea 1428 keylist_update();
1429 }
1430 return 0;
ecea795f 1431 case 103: /* help */
1432 if (HIWORD(wParam) == BN_CLICKED ||
1433 HIWORD(wParam) == BN_DOUBLECLICKED) {
1434 if (help_path) {
1435 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1436 (DWORD)"JI(`',`pageant.general')");
1437 requested_help = TRUE;
1438 }
1439 }
1440 return 0;
5c58ad2d 1441 }
1442 return 0;
ecea795f 1443 case WM_HELP:
1444 if (help_path) {
1445 int id = ((LPHELPINFO)lParam)->iCtrlId;
1446 char *cmd = NULL;
1447 switch (id) {
1448 case 100: cmd = "JI(`',`pageant.keylist')"; break;
1449 case 101: cmd = "JI(`',`pageant.addkey')"; break;
1450 case 102: cmd = "JI(`',`pageant.remkey')"; break;
1451 }
1452 if (cmd) {
1453 WinHelp(main_hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1454 requested_help = TRUE;
1455 } else {
1456 MessageBeep(0);
1457 }
1458 }
1459 break;
5c58ad2d 1460 case WM_CLOSE:
32874aea 1461 keylist = NULL;
1462 DestroyWindow(hwnd);
5c58ad2d 1463 return 0;
1464 }
1465 return 0;
1466}
1467
15ad2e8a 1468/* Set up a system tray icon */
1469static BOOL AddTrayIcon(HWND hwnd)
1470{
1471 BOOL res;
1472 NOTIFYICONDATA tnid;
1473 HICON hicon;
1474
1475#ifdef NIM_SETVERSION
1476 tnid.uVersion = 0;
1477 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1478#endif
1479
1480 tnid.cbSize = sizeof(NOTIFYICONDATA);
1481 tnid.hWnd = hwnd;
1482 tnid.uID = 1; /* unique within this systray use */
1483 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1484 tnid.uCallbackMessage = WM_SYSTRAY;
1485 tnid.hIcon = hicon = LoadIcon(instance, MAKEINTRESOURCE(201));
1486 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1487
1488 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1489
1490 if (hicon) DestroyIcon(hicon);
1491
1492 return res;
1493}
1494
76b51e35 1495/* Update the saved-sessions menu. */
1496static void update_sessions(void)
1497{
1498 int num_entries;
1499 HKEY hkey;
1500 TCHAR buf[MAX_PATH + 1];
1501 MENUITEMINFO mii;
1502
1503 int index_key, index_menu;
1504
1505 if (!putty_path)
1506 return;
1507
1508 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1509 return;
1510
1511 for(num_entries = GetMenuItemCount(session_menu);
1512 num_entries > initial_menuitems_count;
1513 num_entries--)
1514 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1515
1516 index_key = 0;
1517 index_menu = 0;
1518
1519 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1520 TCHAR session_name[MAX_PATH + 1];
1521 unmungestr(buf, session_name, MAX_PATH);
1522 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1523 memset(&mii, 0, sizeof(mii));
1524 mii.cbSize = sizeof(mii);
1525 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1526 mii.fType = MFT_STRING;
1527 mii.fState = MFS_ENABLED;
1528 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1529 mii.dwTypeData = session_name;
1530 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1531 index_menu++;
1532 }
1533 index_key++;
1534 }
1535
1536 RegCloseKey(hkey);
1537
1538 if(index_menu == 0) {
1539 mii.cbSize = sizeof(mii);
1540 mii.fMask = MIIM_TYPE | MIIM_STATE;
1541 mii.fType = MFT_STRING;
1542 mii.fState = MFS_GRAYED;
1543 mii.dwTypeData = _T("(No sessions)");
1544 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1545 }
1546}
1547
32874aea 1548static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1549 WPARAM wParam, LPARAM lParam)
1550{
5c58ad2d 1551 int ret;
1552 static int menuinprogress;
15ad2e8a 1553 static UINT msgTaskbarCreated = 0;
5c58ad2d 1554
1555 switch (message) {
15ad2e8a 1556 case WM_CREATE:
1557 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1558 break;
1559 default:
1560 if (message==msgTaskbarCreated) {
8e3a513c 1561 /*
1562 * Explorer has been restarted, so the tray icon will
1563 * have been lost.
1564 */
1565 AddTrayIcon(hwnd);
15ad2e8a 1566 }
1567 break;
1568
5c58ad2d 1569 case WM_SYSTRAY:
32874aea 1570 if (lParam == WM_RBUTTONUP) {
1571 POINT cursorpos;
1572 GetCursorPos(&cursorpos);
1573 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1574 } else if (lParam == WM_LBUTTONDBLCLK) {
1575 /* Equivalent to IDM_VIEWKEYS. */
1576 PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
1577 }
1578 break;
5c58ad2d 1579 case WM_SYSTRAY2:
32874aea 1580 if (!menuinprogress) {
1581 menuinprogress = 1;
76b51e35 1582 update_sessions();
32874aea 1583 SetForegroundWindow(hwnd);
1584 ret = TrackPopupMenu(systray_menu,
1585 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1586 TPM_RIGHTBUTTON,
1587 wParam, lParam, 0, hwnd, NULL);
1588 menuinprogress = 0;
1589 }
1590 break;
5c58ad2d 1591 case WM_COMMAND:
1592 case WM_SYSCOMMAND:
1593 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
76b51e35 1594 case IDM_PUTTY:
1595 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1596 SW_SHOW) <= 32) {
1597 MessageBox(NULL, "Unable to execute PuTTY!",
1598 "Error", MB_OK | MB_ICONERROR);
1599 }
1600 break;
32874aea 1601 case IDM_CLOSE:
cb5ca813 1602 if (passphrase_box)
1603 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
32874aea 1604 SendMessage(hwnd, WM_CLOSE, 0, 0);
1605 break;
1606 case IDM_VIEWKEYS:
1607 if (!keylist) {
1608 keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
1609 NULL, KeyListProc);
1610 ShowWindow(keylist, SW_SHOWNORMAL);
32874aea 1611 }
afb2076d 1612 /*
1613 * Sometimes the window comes up minimised / hidden for
1614 * no obvious reason. Prevent this. This also brings it
1615 * to the front if it's already present (the user
1616 * selected View Keys because they wanted to _see_ the
1617 * thing).
1618 */
1619 SetForegroundWindow(keylist);
1620 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1621 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
32874aea 1622 break;
1623 case IDM_ADDKEY:
cb5ca813 1624 if (passphrase_box) {
1625 MessageBeep(MB_ICONERROR);
1626 SetForegroundWindow(passphrase_box);
1627 break;
1628 }
32874aea 1629 prompt_add_keyfile();
1630 break;
1631 case IDM_ABOUT:
1632 if (!aboutbox) {
1633 aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
1634 NULL, AboutProc);
1635 ShowWindow(aboutbox, SW_SHOWNORMAL);
1636 /*
1637 * Sometimes the window comes up minimised / hidden
1638 * for no obvious reason. Prevent this.
1639 */
1640 SetForegroundWindow(aboutbox);
1641 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1642 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1643 }
1644 break;
ecea795f 1645 case IDM_HELP:
1646 if (help_path) {
1647 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1648 (DWORD)"JI(`',`pageant.general')");
1649 requested_help = TRUE;
1650 }
1651 break;
76b51e35 1652 default:
1653 {
1654 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1655 MENUITEMINFO mii;
1656 TCHAR buf[MAX_PATH + 1];
1657 TCHAR param[MAX_PATH + 1];
1658 memset(&mii, 0, sizeof(mii));
1659 mii.cbSize = sizeof(mii);
1660 mii.fMask = MIIM_TYPE;
1661 mii.cch = MAX_PATH;
1662 mii.dwTypeData = buf;
1663 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1664 strcpy(param, "@");
1665 strcat(param, mii.dwTypeData);
1666 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1667 _T(""), SW_SHOW) <= 32) {
1668 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1669 MB_OK | MB_ICONERROR);
1670 }
1671 }
1672 }
1673 break;
32874aea 1674 }
1675 break;
5c58ad2d 1676 case WM_DESTROY:
ecea795f 1677 if (requested_help) {
1678 WinHelp(main_hwnd, help_path, HELP_QUIT, 0);
1679 requested_help = FALSE;
1680 }
32874aea 1681 PostQuitMessage(0);
5c58ad2d 1682 return 0;
1683 case WM_COPYDATA:
32874aea 1684 {
1685 COPYDATASTRUCT *cds;
1686 char *mapname;
1687 void *p;
2d466ffd 1688 HANDLE filemap;
1689#ifndef NO_SECURITY
1690 HANDLE proc;
32874aea 1691 PSID mapowner, procowner;
1692 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
2d466ffd 1693#endif
32874aea 1694 int ret = 0;
1695
1696 cds = (COPYDATASTRUCT *) lParam;
1697 if (cds->dwData != AGENT_COPYDATA_ID)
1698 return 0; /* not our message, mate */
1699 mapname = (char *) cds->lpData;
1700 if (mapname[cds->cbData - 1] != '\0')
1701 return 0; /* failure to be ASCIZ! */
d70f60ae 1702#ifdef DEBUG_IPC
32874aea 1703 debug(("mapname is :%s:\n", mapname));
d70f60ae 1704#endif
32874aea 1705 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
d70f60ae 1706#ifdef DEBUG_IPC
32874aea 1707 debug(("filemap is %p\n", filemap));
d70f60ae 1708#endif
32874aea 1709 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
123bc6ea 1710#ifndef NO_SECURITY
2d466ffd 1711 int rc;
32874aea 1712 if (has_security) {
1713 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1714 GetCurrentProcessId())) ==
1715 NULL) {
d70f60ae 1716#ifdef DEBUG_IPC
32874aea 1717 debug(("couldn't get handle for process\n"));
d70f60ae 1718#endif
32874aea 1719 return 0;
1720 }
1721 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1722 OWNER_SECURITY_INFORMATION,
1723 &procowner, NULL, NULL, NULL,
1724 &psd2) != ERROR_SUCCESS) {
d70f60ae 1725#ifdef DEBUG_IPC
32874aea 1726 debug(("couldn't get owner info for process\n"));
d70f60ae 1727#endif
32874aea 1728 CloseHandle(proc);
1729 return 0; /* unable to get security info */
1730 }
1731 CloseHandle(proc);
1732 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1733 OWNER_SECURITY_INFORMATION,
1734 &mapowner, NULL, NULL, NULL,
1735 &psd1) != ERROR_SUCCESS)) {
d70f60ae 1736#ifdef DEBUG_IPC
32874aea 1737 debug(
1738 ("couldn't get owner info for filemap: %d\n",
1739 rc));
d70f60ae 1740#endif
32874aea 1741 return 0;
1742 }
016ef8ab 1743#ifdef DEBUG_IPC
32874aea 1744 debug(("got security stuff\n"));
016ef8ab 1745#endif
32874aea 1746 if (!EqualSid(mapowner, procowner))
1747 return 0; /* security ID mismatch! */
d70f60ae 1748#ifdef DEBUG_IPC
32874aea 1749 debug(("security stuff matched\n"));
d70f60ae 1750#endif
32874aea 1751 LocalFree(psd1);
1752 LocalFree(psd2);
1753 } else {
d70f60ae 1754#ifdef DEBUG_IPC
32874aea 1755 debug(("security APIs not present\n"));
d70f60ae 1756#endif
32874aea 1757 }
123bc6ea 1758#endif
32874aea 1759 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
d70f60ae 1760#ifdef DEBUG_IPC
32874aea 1761 debug(("p is %p\n", p));
1762 {
1763 int i;
1764 for (i = 0; i < 5; i++)
1765 debug(
1766 ("p[%d]=%02x\n", i,
1767 ((unsigned char *) p)[i]));}
d70f60ae 1768#endif
32874aea 1769 answer_msg(p);
1770 ret = 1;
1771 UnmapViewOfFile(p);
1772 }
1773 CloseHandle(filemap);
1774 return ret;
1775 }
5c58ad2d 1776 }
1777
32874aea 1778 return DefWindowProc(hwnd, message, wParam, lParam);
5c58ad2d 1779}
1780
ddecd643 1781/*
1782 * Fork and Exec the command in cmdline. [DBW]
1783 */
2c975497 1784void spawn_cmd(char *cmdline, char * args, int show)
32874aea 1785{
ddecd643 1786 if (ShellExecute(NULL, _T("open"), cmdline,
2c975497 1787 args, NULL, show) <= (HINSTANCE) 32) {
57356d63 1788 char *msg;
1789 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1790 (int)GetLastError());
1791 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1792 sfree(msg);
ddecd643 1793 }
1794}
1795
c44bf5bd 1796/*
1797 * This is a can't-happen stub, since Pageant never makes
1798 * asynchronous agent requests.
1799 */
1800void agent_schedule_callback(void (*callback)(void *, void *, int),
1801 void *callback_ctx, void *data, int len)
1802{
1803 assert(!"We shouldn't get here");
1804}
1805
93b581bd 1806void cleanup_exit(int code) { exit(code); }
1807
c44bf5bd 1808int flags = FLAG_SYNCAGENT;
1809
32874aea 1810int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1811{
5c58ad2d 1812 WNDCLASS wndclass;
5c58ad2d 1813 MSG msg;
016ef8ab 1814 HMODULE advapi;
ddecd643 1815 char *command = NULL;
1816 int added_keys = 0;
d3a1a808 1817 int argc, i;
1818 char **argv, **argstart;
016ef8ab 1819
1820 /*
1821 * Determine whether we're an NT system (should have security
1822 * APIs) or a non-NT system (don't do security).
1823 */
4c48c989 1824 if (!init_winver())
1825 {
1826 modalfatalbox("Windows refuses to report a version");
1827 }
1828 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
32874aea 1829 has_security = TRUE;
016ef8ab 1830 } else
32874aea 1831 has_security = FALSE;
016ef8ab 1832
1833 if (has_security) {
123bc6ea 1834#ifndef NO_SECURITY
32874aea 1835 /*
1836 * Attempt to get the security API we need.
1837 */
1838 advapi = LoadLibrary("ADVAPI32.DLL");
1839 getsecurityinfo =
1840 (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
1841 if (!getsecurityinfo) {
1842 MessageBox(NULL,
1843 "Unable to access security APIs. Pageant will\n"
1844 "not run, in case it causes a security breach.",
1845 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1846 return 1;
1847 }
123bc6ea 1848#else
1849 MessageBox(NULL,
1850 "This program has been compiled for Win9X and will\n"
1851 "not run on NT, in case it causes a security breach.",
1852 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1853 return 1;
1854#endif
016ef8ab 1855 } else
32874aea 1856 advapi = NULL;
5c58ad2d 1857
1858 instance = inst;
1859
ddecd643 1860 /*
ecea795f 1861 * See if we can find our Help file.
1862 */
1863 {
1864 char b[2048], *p, *q, *r;
1865 FILE *fp;
1866 GetModuleFileName(NULL, b, sizeof(b) - 1);
1867 r = b;
1868 p = strrchr(b, '\\');
1869 if (p && p >= r) r = p+1;
1870 q = strrchr(b, ':');
1871 if (q && q >= r) r = q+1;
1872 strcpy(r, "putty.hlp");
1873 if ( (fp = fopen(b, "r")) != NULL) {
1874 help_path = dupstr(b);
1875 fclose(fp);
1876 } else
1877 help_path = NULL;
1878 }
1879
1880 /*
76b51e35 1881 * Look for the PuTTY binary (we will enable the saved session
1882 * submenu if we find it).
1883 */
1884 {
1885 char b[2048], *p, *q, *r;
1886 FILE *fp;
1887 GetModuleFileName(NULL, b, sizeof(b) - 1);
1888 r = b;
1889 p = strrchr(b, '\\');
1890 if (p && p >= r) r = p+1;
1891 q = strrchr(b, ':');
1892 if (q && q >= r) r = q+1;
1893 strcpy(r, "putty.exe");
1894 if ( (fp = fopen(b, "r")) != NULL) {
1895 putty_path = dupstr(b);
1896 fclose(fp);
1897 } else
1898 putty_path = NULL;
1899 }
1900
1901 /*
ddecd643 1902 * Find out if Pageant is already running.
1903 */
1904 already_running = FALSE;
260f3dec 1905 if (agent_exists())
ddecd643 1906 already_running = TRUE;
1907 else {
1908
1909 if (!prev) {
32874aea 1910 wndclass.style = 0;
1911 wndclass.lpfnWndProc = WndProc;
1912 wndclass.cbClsExtra = 0;
1913 wndclass.cbWndExtra = 0;
1914 wndclass.hInstance = inst;
1915 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1916 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1917 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1918 wndclass.lpszMenuName = NULL;
ddecd643 1919 wndclass.lpszClassName = APPNAME;
1920
32874aea 1921 RegisterClass(&wndclass);
ddecd643 1922 }
5c58ad2d 1923
ecea795f 1924 main_hwnd = keylist = NULL;
5c58ad2d 1925
ecea795f 1926 main_hwnd = CreateWindow(APPNAME, APPNAME,
1927 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1928 CW_USEDEFAULT, CW_USEDEFAULT,
1929 100, 100, NULL, NULL, inst, NULL);
5c58ad2d 1930
ddecd643 1931 /* Set up a system tray icon */
ecea795f 1932 AddTrayIcon(main_hwnd);
15ad2e8a 1933
76b51e35 1934 /* Accelerators used: nsvkxa */
15ad2e8a 1935 systray_menu = CreatePopupMenu();
76b51e35 1936 if (putty_path) {
1937 session_menu = CreateMenu();
1938 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1939 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1940 (UINT) session_menu, "&Saved Sessions");
1941 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1942 }
15ad2e8a 1943 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1944 "&View Keys");
1945 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
ecea795f 1946 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1947 if (help_path)
1948 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
15ad2e8a 1949 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
ecea795f 1950 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
15ad2e8a 1951 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
76b51e35 1952 initial_menuitems_count = GetMenuItemCount(session_menu);
5c58ad2d 1953
ecea795f 1954 ShowWindow(main_hwnd, SW_HIDE);
5c58ad2d 1955
ddecd643 1956 /*
1957 * Initialise storage for RSA keys.
1958 */
1959 rsakeys = newtree234(cmpkeys_rsa);
1960 ssh2keys = newtree234(cmpkeys_ssh2);
1961
1962 }
dacbd0e8 1963
1964 /*
0959acf7 1965 * Initialise storage for short-term passphrase cache.
1966 */
1967 passphrases = newtree234(NULL);
1968
1969 /*
45cebe79 1970 * Process the command line and add keys as listed on it.
5c58ad2d 1971 */
d3a1a808 1972 split_into_argv(cmdline, &argc, &argv, &argstart);
1973 for (i = 0; i < argc; i++) {
1974 if (!strcmp(argv[i], "-c")) {
1975 /*
1976 * If we see `-c', then the rest of the
1977 * command line should be treated as a
1978 * command to be spawned.
1979 */
1980 if (i < argc-1)
1981 command = argstart[i+1];
1982 else
1983 command = "";
1984 break;
1985 } else {
9a30e26b 1986 add_keyfile(filename_from_str(argv[i]));
d3a1a808 1987 added_keys = TRUE;
32874aea 1988 }
5c58ad2d 1989 }
1990
0959acf7 1991 /*
1992 * Forget any passphrase that we retained while going over
1993 * command line keyfiles.
1994 */
1995 forget_passphrases();
1996
2c975497 1997 if (command) {
1998 char *args;
1999 if (command[0] == '"')
2000 args = strchr(++command, '"');
2001 else
2002 args = strchr(command, ' ');
2003 if (args) {
2004 *args++ = 0;
2005 while(*args && isspace(*args)) args++;
2006 }
2007 spawn_cmd(command, args, show);
2008 }
ddecd643 2009
2010 /*
2011 * If Pageant was already running, we leave now. If we haven't
2012 * even taken any auxiliary action (spawned a command or added
2013 * keys), complain.
2014 */
2015 if (already_running) {
2016 if (!command && !added_keys) {
2017 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2018 MB_ICONERROR | MB_OK);
2019 }
32874aea 2020 if (advapi)
2021 FreeLibrary(advapi);
2022 return 0;
ddecd643 2023 }
2024
5c58ad2d 2025 /*
dacbd0e8 2026 * Main message loop.
5c58ad2d 2027 */
5c58ad2d 2028 while (GetMessage(&msg, NULL, 0, 0) == 1) {
6de8ca59 2029 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2030 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2031 TranslateMessage(&msg);
2032 DispatchMessage(&msg);
2033 }
5c58ad2d 2034 }
2035
2036 /* Clean up the system tray icon */
2037 {
32874aea 2038 NOTIFYICONDATA tnid;
5c58ad2d 2039
32874aea 2040 tnid.cbSize = sizeof(NOTIFYICONDATA);
ecea795f 2041 tnid.hWnd = main_hwnd;
32874aea 2042 tnid.uID = 1;
5c58ad2d 2043
32874aea 2044 Shell_NotifyIcon(NIM_DELETE, &tnid);
5c58ad2d 2045
32874aea 2046 DestroyMenu(systray_menu);
5c58ad2d 2047 }
2048
32874aea 2049 if (advapi)
2050 FreeLibrary(advapi);
76b51e35 2051 return msg.wParam;
5c58ad2d 2052}