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