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