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