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