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