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