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