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