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