Include <mmsystem.h> and winmm.lib for PlaySound.
[sgt/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;
d2371c81 227 int i;
3c0b3d06 228
229 if (keylist) {
230 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
d2371c81 231 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
45cebe79 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 }
d2371c81 245 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
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 {
5c58ad2d 441 struct RSAKey *key;
442 int len, nkeys;
d2371c81 443 int i;
5c58ad2d 444
445 /*
446 * Count up the number and length of keys we hold.
447 */
448 len = nkeys = 0;
d2371c81 449 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
5c58ad2d 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;
d2371c81 468 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
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 {
45cebe79 484 struct ssh2_userkey *key;
485 int len, nkeys;
486 unsigned char *blob;
487 int bloblen;
d2371c81 488 int i;
45cebe79 489
490 /*
491 * Count up the number and length of keys we hold.
492 */
493 len = nkeys = 0;
d2371c81 494 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
45cebe79 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;
d2371c81 514 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
45cebe79 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;
45cebe79 746
d2371c81 747 while ( (rkey = index234(rsakeys, 0)) != NULL ) {
45cebe79 748 del234(rsakeys, rkey);
749 freersakey(rkey);
750 sfree(rkey);
751 }
752 keylist_update();
753
754 PUT_32BIT(ret, 1);
755 ret[4] = SSH_AGENT_SUCCESS;
756 }
757 break;
758 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
759 /*
760 * Remove all SSH2 keys. Always returns success.
761 */
762 {
763 struct ssh2_userkey *skey;
45cebe79 764
d2371c81 765 while ( (skey = index234(ssh2keys, 0)) != NULL ) {
45cebe79 766 del234(ssh2keys, skey);
767 skey->alg->freekey(skey->data);
768 sfree(skey);
769 }
770 keylist_update();
771
772 PUT_32BIT(ret, 1);
773 ret[4] = SSH_AGENT_SUCCESS;
774 }
775 break;
5c58ad2d 776 default:
777 failure:
778 /*
779 * Unrecognised message. Return SSH_AGENT_FAILURE.
780 */
d70f60ae 781 PUT_32BIT(ret, 1);
782 ret[4] = SSH_AGENT_FAILURE;
5c58ad2d 783 break;
784 }
5c58ad2d 785}
786
787/*
788 * Key comparison function for the 2-3-4 tree of RSA keys.
789 */
45cebe79 790static int cmpkeys_rsa(void *av, void *bv) {
5c58ad2d 791 struct RSAKey *a = (struct RSAKey *)av;
792 struct RSAKey *b = (struct RSAKey *)bv;
793 Bignum am, bm;
794 int alen, blen;
795
796 am = a->modulus;
797 bm = b->modulus;
798 /*
799 * Compare by length of moduli.
800 */
ddecd643 801 alen = bignum_bitcount(am);
802 blen = bignum_bitcount(bm);
5c58ad2d 803 if (alen > blen) return +1; else if (alen < blen) return -1;
804 /*
805 * Now compare by moduli themselves.
806 */
807 alen = (alen + 7) / 8; /* byte count */
808 while (alen-- > 0) {
809 int abyte, bbyte;
810 abyte = bignum_byte(am, alen);
811 bbyte = bignum_byte(bm, alen);
812 if (abyte > bbyte) return +1; else if (abyte < bbyte) return -1;
813 }
814 /*
815 * Give up.
816 */
817 return 0;
818}
819
45cebe79 820/*
821 * Key comparison function for the 2-3-4 tree of SSH2 keys.
822 */
823static int cmpkeys_ssh2(void *av, void *bv) {
824 struct ssh2_userkey *a = (struct ssh2_userkey *)av;
825 struct ssh2_userkey *b = (struct ssh2_userkey *)bv;
826 int i;
827 int alen, blen;
828 unsigned char *ablob, *bblob;
829 int c;
830
831 /*
832 * Compare purely by public blob.
833 */
834 ablob = a->alg->public_blob(a->data, &alen);
835 bblob = b->alg->public_blob(b->data, &blen);
836
837 c = 0;
838 for (i = 0; i < alen && i < blen; i++) {
839 if (ablob[i] < bblob[i]) {
840 c = -1; break;
841 } else if (ablob[i] > bblob[i]) {
842 c = +1; break;
843 }
844 }
845 if (c == 0 && i < alen) c = +1; /* a is longer */
846 if (c == 0 && i < blen) c = -1; /* a is longer */
847
848 sfree(ablob);
849 sfree(bblob);
850
851 return c;
852}
853
854/*
855 * Key comparison function for looking up a blob in the 2-3-4 tree
856 * of SSH2 keys.
857 */
858static int cmpkeys_ssh2_asymm(void *av, void *bv) {
859 struct blob *a = (struct blob *)av;
860 struct ssh2_userkey *b = (struct ssh2_userkey *)bv;
861 int i;
862 int alen, blen;
863 unsigned char *ablob, *bblob;
864 int c;
865
866 /*
867 * Compare purely by public blob.
868 */
869 ablob = a->blob;
870 alen = a->len;
871 bblob = b->alg->public_blob(b->data, &blen);
872
873 c = 0;
874 for (i = 0; i < alen && i < blen; i++) {
875 if (ablob[i] < bblob[i]) {
876 c = -1; break;
877 } else if (ablob[i] > bblob[i]) {
878 c = +1; break;
879 }
880 }
881 if (c == 0 && i < alen) c = +1; /* a is longer */
882 if (c == 0 && i < blen) c = -1; /* a is longer */
883
884 sfree(bblob);
885
886 return c;
887}
888
5c58ad2d 889static void error(char *s) {
890 MessageBox(hwnd, s, APPNAME, MB_OK | MB_ICONERROR);
891}
892
893/*
ab162329 894 * Prompt for a key file to add, and add it.
895 */
896static void prompt_add_keyfile(void) {
897 OPENFILENAME of;
898 char filename[FILENAME_MAX];
899 memset(&of, 0, sizeof(of));
900#ifdef OPENFILENAME_SIZE_VERSION_400
901 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
902#else
903 of.lStructSize = sizeof(of);
904#endif
905 of.hwndOwner = hwnd;
906 of.lpstrFilter = "All Files\0*\0\0\0";
907 of.lpstrCustomFilter = NULL;
908 of.nFilterIndex = 1;
909 of.lpstrFile = filename; *filename = '\0';
910 of.nMaxFile = sizeof(filename);
911 of.lpstrFileTitle = NULL;
912 of.lpstrInitialDir = NULL;
913 of.lpstrTitle = "Select Private Key File";
914 of.Flags = 0;
915 if (GetOpenFileName(&of)) {
916 add_keyfile(filename);
917 keylist_update();
918 }
919}
920
921/*
5c58ad2d 922 * Dialog-box function for the key list box.
923 */
924static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
925 WPARAM wParam, LPARAM lParam) {
45cebe79 926 struct RSAKey *rkey;
927 struct ssh2_userkey *skey;
5c58ad2d 928
929 switch (msg) {
930 case WM_INITDIALOG:
45cebe79 931 /*
932 * Centre the window.
933 */
934 { /* centre the window */
935 RECT rs, rd;
936 HWND hw;
937
938 hw = GetDesktopWindow();
939 if (GetWindowRect (hw, &rs) && GetWindowRect (hwnd, &rd))
940 MoveWindow (hwnd, (rs.right + rs.left + rd.left - rd.right)/2,
941 (rs.bottom + rs.top + rd.top - rd.bottom)/2,
942 rd.right-rd.left, rd.bottom-rd.top, TRUE);
943 }
944
1c2a93c4 945 keylist = hwnd;
946 {
45cebe79 947 static int tabs[] = {35, 60, 210};
948 SendDlgItemMessage (hwnd, 100, LB_SETTABSTOPS,
949 sizeof(tabs)/sizeof(*tabs), (LPARAM) tabs);
1c2a93c4 950 }
951 keylist_update();
5c58ad2d 952 return 0;
953 case WM_COMMAND:
954 switch (LOWORD(wParam)) {
955 case IDOK:
956 case IDCANCEL:
957 keylist = NULL;
958 DestroyWindow(hwnd);
959 return 0;
960 case 101: /* add key */
961 if (HIWORD(wParam) == BN_CLICKED ||
962 HIWORD(wParam) == BN_DOUBLECLICKED) {
ab162329 963 prompt_add_keyfile();
5c58ad2d 964 }
965 return 0;
966 case 102: /* remove key */
967 if (HIWORD(wParam) == BN_CLICKED ||
968 HIWORD(wParam) == BN_DOUBLECLICKED) {
969 int n = SendDlgItemMessage (hwnd, 100, LB_GETCURSEL, 0, 0);
d2371c81 970 int i;
f70a606e 971 if (n == LB_ERR) {
5c58ad2d 972 MessageBeep(0);
973 break;
974 }
d2371c81 975 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++)
5c58ad2d 976 if (n-- == 0)
977 break;
45cebe79 978 if (rkey) {
979 del234(rsakeys, rkey);
980 freersakey(rkey);
981 sfree(rkey);
982 } else {
d2371c81 983 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++)
45cebe79 984 if (n-- == 0)
985 break;
986 if (skey) {
987 del234(ssh2keys, skey);
988 skey->alg->freekey(skey->data);
989 sfree(skey);
990 }
991 }
1c2a93c4 992 keylist_update();
5c58ad2d 993 }
994 return 0;
995 }
996 return 0;
997 case WM_CLOSE:
998 keylist = NULL;
999 DestroyWindow(hwnd);
1000 return 0;
1001 }
1002 return 0;
1003}
1004
1005static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
1006 WPARAM wParam, LPARAM lParam) {
1007 int ret;
1008 static int menuinprogress;
1009
1010 switch (message) {
1011 case WM_SYSTRAY:
1012 if (lParam == WM_RBUTTONUP) {
1013 POINT cursorpos;
1014 GetCursorPos(&cursorpos);
1015 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
905beba5 1016 } else if (lParam == WM_LBUTTONDBLCLK) {
1017 /* Equivalent to IDM_VIEWKEYS. */
1018 PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
5c58ad2d 1019 }
1020 break;
1021 case WM_SYSTRAY2:
1022 if (!menuinprogress) {
1023 menuinprogress = 1;
1024 SetForegroundWindow(hwnd);
1025 ret = TrackPopupMenu(systray_menu,
1026 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1027 TPM_RIGHTBUTTON,
1028 wParam, lParam, 0, hwnd, NULL);
1029 menuinprogress = 0;
1030 }
1031 break;
1032 case WM_COMMAND:
1033 case WM_SYSCOMMAND:
1034 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1035 case IDM_CLOSE:
1036 SendMessage(hwnd, WM_CLOSE, 0, 0);
1037 break;
1038 case IDM_VIEWKEYS:
1039 if (!keylist) {
1040 keylist = CreateDialog (instance, MAKEINTRESOURCE(211),
1041 NULL, KeyListProc);
1042 ShowWindow (keylist, SW_SHOWNORMAL);
905beba5 1043 /*
1044 * Sometimes the window comes up minimised / hidden
1045 * for no obvious reason. Prevent this.
1046 */
1047 SetForegroundWindow(keylist);
1048 SetWindowPos (keylist, HWND_TOP, 0, 0, 0, 0,
1049 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
5c58ad2d 1050 }
1051 break;
ab162329 1052 case IDM_ADDKEY:
1053 prompt_add_keyfile();
1054 break;
5af3a3b6 1055 case IDM_ABOUT:
1056 if (!aboutbox) {
1057 aboutbox = CreateDialog (instance, MAKEINTRESOURCE(213),
1058 NULL, AboutProc);
1059 ShowWindow (aboutbox, SW_SHOWNORMAL);
1060 /*
1061 * Sometimes the window comes up minimised / hidden
1062 * for no obvious reason. Prevent this.
1063 */
1064 SetForegroundWindow(aboutbox);
1065 SetWindowPos (aboutbox, HWND_TOP, 0, 0, 0, 0,
1066 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1067 }
1068 break;
5c58ad2d 1069 }
1070 break;
1071 case WM_DESTROY:
1072 PostQuitMessage (0);
1073 return 0;
1074 case WM_COPYDATA:
1075 {
1076 COPYDATASTRUCT *cds;
d70f60ae 1077 char *mapname;
1078 void *p;
1079 HANDLE filemap, proc;
1080 PSID mapowner, procowner;
1081 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1082 int ret = 0;
5c58ad2d 1083
1084 cds = (COPYDATASTRUCT *)lParam;
d70f60ae 1085 if (cds->dwData != AGENT_COPYDATA_ID)
1086 return 0; /* not our message, mate */
1087 mapname = (char *)cds->lpData;
1088 if (mapname[cds->cbData - 1] != '\0')
1089 return 0; /* failure to be ASCIZ! */
1090#ifdef DEBUG_IPC
1091 debug(("mapname is :%s:\r\n", mapname));
1092#endif
1093 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1094#ifdef DEBUG_IPC
1095 debug(("filemap is %p\r\n", filemap));
1096#endif
1097 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1098 int rc;
123bc6ea 1099#ifndef NO_SECURITY
016ef8ab 1100 if (has_security) {
1101 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1102 GetCurrentProcessId())) == NULL) {
d70f60ae 1103#ifdef DEBUG_IPC
016ef8ab 1104 debug(("couldn't get handle for process\r\n"));
d70f60ae 1105#endif
016ef8ab 1106 return 0;
1107 }
1108 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1109 OWNER_SECURITY_INFORMATION,
1110 &procowner, NULL, NULL, NULL,
1111 &psd2) != ERROR_SUCCESS) {
d70f60ae 1112#ifdef DEBUG_IPC
016ef8ab 1113 debug(("couldn't get owner info for process\r\n"));
d70f60ae 1114#endif
016ef8ab 1115 CloseHandle(proc);
1116 return 0; /* unable to get security info */
1117 }
d70f60ae 1118 CloseHandle(proc);
016ef8ab 1119 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1120 OWNER_SECURITY_INFORMATION,
1121 &mapowner, NULL, NULL, NULL,
1122 &psd1) != ERROR_SUCCESS)) {
d70f60ae 1123#ifdef DEBUG_IPC
016ef8ab 1124 debug(("couldn't get owner info for filemap: %d\r\n", rc));
d70f60ae 1125#endif
016ef8ab 1126 return 0;
1127 }
1128#ifdef DEBUG_IPC
1129 debug(("got security stuff\r\n"));
1130#endif
1131 if (!EqualSid(mapowner, procowner))
1132 return 0; /* security ID mismatch! */
d70f60ae 1133#ifdef DEBUG_IPC
016ef8ab 1134 debug(("security stuff matched\r\n"));
d70f60ae 1135#endif
016ef8ab 1136 LocalFree(psd1);
1137 LocalFree(psd2);
1138 } else {
d70f60ae 1139#ifdef DEBUG_IPC
016ef8ab 1140 debug(("security APIs not present\r\n"));
d70f60ae 1141#endif
016ef8ab 1142 }
123bc6ea 1143#endif
d70f60ae 1144 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1145#ifdef DEBUG_IPC
1146 debug(("p is %p\r\n", p));
1147 {int i; for(i=0;i<5;i++)debug(("p[%d]=%02x\r\n", i, ((unsigned char *)p)[i]));}
1148#endif
1149 answer_msg(p);
1150 ret = 1;
1151 UnmapViewOfFile(p);
1152 }
1153 CloseHandle(filemap);
1154 return ret;
5c58ad2d 1155 }
5c58ad2d 1156 }
1157
1158 return DefWindowProc (hwnd, message, wParam, lParam);
1159}
1160
ddecd643 1161/*
1162 * Fork and Exec the command in cmdline. [DBW]
1163 */
1164void spawn_cmd(char *cmdline, int show) {
1165 if (ShellExecute(NULL, _T("open"), cmdline,
1166 NULL, NULL, show) <= (HINSTANCE) 32) {
1167 TCHAR sMsg[140];
1168 sprintf(sMsg, _T("Failed to run \"%.100s\", Error: %d"), cmdline,
1169 GetLastError());
1170 MessageBox(NULL, sMsg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1171 }
1172}
1173
5c58ad2d 1174int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
1175 WNDCLASS wndclass;
5c58ad2d 1176 MSG msg;
016ef8ab 1177 OSVERSIONINFO osi;
1178 HMODULE advapi;
ddecd643 1179 char *command = NULL;
1180 int added_keys = 0;
016ef8ab 1181
1182 /*
1183 * Determine whether we're an NT system (should have security
1184 * APIs) or a non-NT system (don't do security).
1185 */
1186 memset(&osi, 0, sizeof(OSVERSIONINFO));
1187 osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1188 if (GetVersionEx(&osi) && osi.dwPlatformId==VER_PLATFORM_WIN32_NT) {
1189 has_security = TRUE;
1190 } else
1191 has_security = FALSE;
1192
1193 if (has_security) {
123bc6ea 1194#ifndef NO_SECURITY
016ef8ab 1195 /*
ddecd643 1196 * Attempt to get the security API we need.
016ef8ab 1197 */
1198 advapi = LoadLibrary("ADVAPI32.DLL");
1199 getsecurityinfo = (gsi_fn_t)GetProcAddress(advapi, "GetSecurityInfo");
1200 if (!getsecurityinfo) {
1201 MessageBox(NULL,
1202 "Unable to access security APIs. Pageant will\n"
1203 "not run, in case it causes a security breach.",
1204 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1205 return 1;
1206 }
123bc6ea 1207#else
1208 MessageBox(NULL,
1209 "This program has been compiled for Win9X and will\n"
1210 "not run on NT, in case it causes a security breach.",
1211 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1212 return 1;
1213#endif
016ef8ab 1214 } else
1215 advapi = NULL;
5c58ad2d 1216
1217 instance = inst;
1218
ddecd643 1219 /*
1220 * Find out if Pageant is already running.
1221 */
1222 already_running = FALSE;
1223 if (FindWindow("Pageant", "Pageant"))
1224 already_running = TRUE;
1225 else {
1226
1227 if (!prev) {
1228 wndclass.style = 0;
1229 wndclass.lpfnWndProc = WndProc;
1230 wndclass.cbClsExtra = 0;
1231 wndclass.cbWndExtra = 0;
1232 wndclass.hInstance = inst;
1233 wndclass.hIcon = LoadIcon (inst,
1234 MAKEINTRESOURCE(IDI_MAINICON));
1235 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
1236 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
1237 wndclass.lpszMenuName = NULL;
1238 wndclass.lpszClassName = APPNAME;
1239
1240 RegisterClass (&wndclass);
1241 }
5c58ad2d 1242
ddecd643 1243 hwnd = keylist = NULL;
5c58ad2d 1244
ddecd643 1245 hwnd = CreateWindow (APPNAME, APPNAME,
1246 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1247 CW_USEDEFAULT, CW_USEDEFAULT,
1248 100, 100, NULL, NULL, inst, NULL);
5c58ad2d 1249
ddecd643 1250 /* Set up a system tray icon */
1251 {
1252 BOOL res;
1253 NOTIFYICONDATA tnid;
1254 HICON hicon;
5c58ad2d 1255
1256#ifdef NIM_SETVERSION
ddecd643 1257 tnid.uVersion = 0;
1258 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
5c58ad2d 1259#endif
1260
ddecd643 1261 tnid.cbSize = sizeof(NOTIFYICONDATA);
1262 tnid.hWnd = hwnd;
1263 tnid.uID = 1; /* unique within this systray use */
1264 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1265 tnid.uCallbackMessage = WM_SYSTRAY;
1266 tnid.hIcon = hicon = LoadIcon (instance, MAKEINTRESOURCE(201));
1267 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1268
1269 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1270
1271 if (hicon)
1272 DestroyIcon(hicon);
1273
1274 systray_menu = CreatePopupMenu();
1275 /* accelerators used: vkxa */
1276 AppendMenu (systray_menu, MF_ENABLED, IDM_VIEWKEYS, "&View Keys");
1277 AppendMenu (systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1278 AppendMenu (systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1279 AppendMenu (systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1280 }
5c58ad2d 1281
ddecd643 1282 ShowWindow (hwnd, SW_HIDE);
5c58ad2d 1283
ddecd643 1284 /*
1285 * Initialise storage for RSA keys.
1286 */
1287 rsakeys = newtree234(cmpkeys_rsa);
1288 ssh2keys = newtree234(cmpkeys_ssh2);
1289
1290 }
dacbd0e8 1291
1292 /*
45cebe79 1293 * Process the command line and add keys as listed on it.
ddecd643 1294 * If we already determined that we need to spawn a program from above we
1295 * need to ignore the first two arguments. [DBW]
5c58ad2d 1296 */
1297 {
8c42f28c 1298 char *p;
1299 int inquotes = 0;
ddecd643 1300 int ignorearg = 0;
8c42f28c 1301 p = cmdline;
dacbd0e8 1302 while (*p) {
1303 while (*p && isspace(*p)) p++;
1304 if (*p && !isspace(*p)) {
8c42f28c 1305 char *q = p, *pp = p;
1306 while (*p && (inquotes || !isspace(*p)))
1307 {
1308 if (*p == '"') {
1309 inquotes = !inquotes;
1310 p++;
1311 continue;
1312 }
1313 *pp++ = *p++;
1314 }
1315 if (*pp) {
1316 if (*p) p++;
1317 *pp++ = '\0';
1318 }
ddecd643 1319 if (!strcmp(q, "-c")) {
1320 /*
1321 * If we see `-c', then the rest of the
1322 * command line should be treated as a
1323 * command to be spawned.
1324 */
1325 while (*p && isspace(*p)) p++;
1326 command = p;
1327 break;
1328 } else {
1329 add_keyfile(q);
1330 added_keys = TRUE;
1331 }
dacbd0e8 1332 }
1333 }
5c58ad2d 1334 }
1335
ddecd643 1336 if (command) spawn_cmd (command, show);
1337
1338 /*
1339 * If Pageant was already running, we leave now. If we haven't
1340 * even taken any auxiliary action (spawned a command or added
1341 * keys), complain.
1342 */
1343 if (already_running) {
1344 if (!command && !added_keys) {
1345 MessageBox(NULL, "Pageant is already running", "Pageant Error",
1346 MB_ICONERROR | MB_OK);
1347 }
1348 if (advapi) FreeLibrary(advapi);
1349 return 0;
1350 }
1351
5c58ad2d 1352 /*
dacbd0e8 1353 * Main message loop.
5c58ad2d 1354 */
5c58ad2d 1355 while (GetMessage(&msg, NULL, 0, 0) == 1) {
1356 TranslateMessage(&msg);
1357 DispatchMessage(&msg);
1358 }
1359
1360 /* Clean up the system tray icon */
1361 {
1362 NOTIFYICONDATA tnid;
1363
1364 tnid.cbSize = sizeof(NOTIFYICONDATA);
1365 tnid.hWnd = hwnd;
1366 tnid.uID = 1;
1367
1368 Shell_NotifyIcon(NIM_DELETE, &tnid);
1369
1370 DestroyMenu(systray_menu);
1371 }
1372
016ef8ab 1373 if (advapi) FreeLibrary(advapi);
5c58ad2d 1374 exit(msg.wParam);
1375}