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