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