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