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