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