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