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