]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winpgnt.c
3451fecabbd279213fc83ef188e15d011b9da731
[PuTTY.git] / windows / winpgnt.c
1 /*
2  * Pageant: the PuTTY Authentication Agent.
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <ctype.h>
8 #include <assert.h>
9 #include <tchar.h>
10
11 #define PUTTY_DO_GLOBALS
12
13 #include "putty.h"
14 #include "ssh.h"
15 #include "misc.h"
16 #include "tree234.h"
17
18 #include <shellapi.h>
19
20 #ifndef NO_SECURITY
21 #include <aclapi.h>
22 #endif
23
24 #define IDI_MAINICON 200
25 #define IDI_TRAYICON 201
26
27 #define WM_SYSTRAY   (WM_APP + 6)
28 #define WM_SYSTRAY2  (WM_APP + 7)
29
30 #define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */
31
32 /*
33  * FIXME: maybe some day we can sort this out ...
34  */
35 #define AGENT_MAX_MSGLEN  8192
36
37 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
38  * wParam are used by Windows, and should be masked off, so we shouldn't
39  * attempt to store information in them. Hence all these identifiers have
40  * the low 4 bits clear. Also, identifiers should < 0xF000. */
41
42 #define IDM_CLOSE    0x0010
43 #define IDM_VIEWKEYS 0x0020
44 #define IDM_ADDKEY   0x0030
45 #define IDM_HELP     0x0040
46 #define IDM_ABOUT    0x0050
47
48 #define APPNAME "Pageant"
49
50 extern char ver[];
51
52 static HWND keylist;
53 static HWND aboutbox;
54 static HMENU systray_menu, session_menu;
55 static int already_running;
56
57 static char *putty_path;
58
59 /* CWD for "add key" file requester. */
60 static filereq *keypath = NULL;
61
62 #define IDM_PUTTY         0x0060
63 #define IDM_SESSIONS_BASE 0x1000
64 #define IDM_SESSIONS_MAX  0x2000
65 #define PUTTY_REGKEY      "Software\\SimonTatham\\PuTTY\\Sessions"
66 #define PUTTY_DEFAULT     "Default%20Settings"
67 static int initial_menuitems_count;
68
69 /*
70  * Print a modal (Really Bad) message box and perform a fatal exit.
71  */
72 void modalfatalbox(char *fmt, ...)
73 {
74     va_list ap;
75     char *buf;
76
77     va_start(ap, fmt);
78     buf = dupvprintf(fmt, ap);
79     va_end(ap);
80     MessageBox(hwnd, buf, "Pageant Fatal Error",
81                MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
82     sfree(buf);
83     exit(1);
84 }
85
86 /* Un-munge session names out of the registry. */
87 static void unmungestr(char *in, char *out, int outlen)
88 {
89     while (*in) {
90         if (*in == '%' && in[1] && in[2]) {
91             int i, j;
92
93             i = in[1] - '0';
94             i -= (i > 9 ? 7 : 0);
95             j = in[2] - '0';
96             j -= (j > 9 ? 7 : 0);
97
98             *out++ = (i << 4) + j;
99             if (!--outlen)
100                 return;
101             in += 3;
102         } else {
103             *out++ = *in++;
104             if (!--outlen)
105                 return;
106         }
107     }
108     *out = '\0';
109     return;
110 }
111
112 static tree234 *rsakeys, *ssh2keys;
113
114 static int has_security;
115 #ifndef NO_SECURITY
116 DECL_WINDOWS_FUNCTION(static, DWORD, GetSecurityInfo,
117                       (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
118                        PSID *, PSID *, PACL *, PACL *,
119                        PSECURITY_DESCRIPTOR *));
120 #endif
121
122 /*
123  * Forward references
124  */
125 static void *make_keylist1(int *length);
126 static void *make_keylist2(int *length);
127 static void *get_keylist1(int *length);
128 static void *get_keylist2(int *length);
129
130 /*
131  * We need this to link with the RSA code, because rsaencrypt()
132  * pads its data with random bytes. Since we only use rsadecrypt()
133  * and the signing functions, which are deterministic, this should
134  * never be called.
135  *
136  * If it _is_ called, there is a _serious_ problem, because it
137  * won't generate true random numbers. So we must scream, panic,
138  * and exit immediately if that should happen.
139  */
140 int random_byte(void)
141 {
142     MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
143     exit(0);
144     /* this line can't be reached but it placates MSVC's warnings :-) */
145     return 0;
146 }
147
148 /*
149  * Blob structure for passing to the asymmetric SSH-2 key compare
150  * function, prototyped here.
151  */
152 struct blob {
153     unsigned char *blob;
154     int len;
155 };
156 static int cmpkeys_ssh2_asymm(void *av, void *bv);
157
158 #define PASSPHRASE_MAXLEN 512
159
160 struct PassphraseProcStruct {
161     char *passphrase;
162     char *comment;
163 };
164
165 static tree234 *passphrases = NULL;
166
167 /* 
168  * After processing a list of filenames, we want to forget the
169  * passphrases.
170  */
171 static void forget_passphrases(void)
172 {
173     while (count234(passphrases) > 0) {
174         char *pp = index234(passphrases, 0);
175         memset(pp, 0, strlen(pp));
176         delpos234(passphrases, 0);
177         free(pp);
178     }
179 }
180
181 /*
182  * Dialog-box function for the Licence box.
183  */
184 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
185                                 WPARAM wParam, LPARAM lParam)
186 {
187     switch (msg) {
188       case WM_INITDIALOG:
189         return 1;
190       case WM_COMMAND:
191         switch (LOWORD(wParam)) {
192           case IDOK:
193           case IDCANCEL:
194             EndDialog(hwnd, 1);
195             return 0;
196         }
197         return 0;
198       case WM_CLOSE:
199         EndDialog(hwnd, 1);
200         return 0;
201     }
202     return 0;
203 }
204
205 /*
206  * Dialog-box function for the About box.
207  */
208 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
209                               WPARAM wParam, LPARAM lParam)
210 {
211     switch (msg) {
212       case WM_INITDIALOG:
213         SetDlgItemText(hwnd, 100, ver);
214         return 1;
215       case WM_COMMAND:
216         switch (LOWORD(wParam)) {
217           case IDOK:
218           case IDCANCEL:
219             aboutbox = NULL;
220             DestroyWindow(hwnd);
221             return 0;
222           case 101:
223             EnableWindow(hwnd, 0);
224             DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
225             EnableWindow(hwnd, 1);
226             SetActiveWindow(hwnd);
227             return 0;
228         }
229         return 0;
230       case WM_CLOSE:
231         aboutbox = NULL;
232         DestroyWindow(hwnd);
233         return 0;
234     }
235     return 0;
236 }
237
238 static HWND passphrase_box;
239
240 /*
241  * Dialog-box function for the passphrase box.
242  */
243 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
244                                    WPARAM wParam, LPARAM lParam)
245 {
246     static char *passphrase = NULL;
247     struct PassphraseProcStruct *p;
248
249     switch (msg) {
250       case WM_INITDIALOG:
251         passphrase_box = hwnd;
252         /*
253          * Centre the window.
254          */
255         {                              /* centre the window */
256             RECT rs, rd;
257             HWND hw;
258
259             hw = GetDesktopWindow();
260             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
261                 MoveWindow(hwnd,
262                            (rs.right + rs.left + rd.left - rd.right) / 2,
263                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
264                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
265         }
266
267         SetForegroundWindow(hwnd);
268         SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
269                      SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
270         p = (struct PassphraseProcStruct *) lParam;
271         passphrase = p->passphrase;
272         if (p->comment)
273             SetDlgItemText(hwnd, 101, p->comment);
274         *passphrase = 0;
275         SetDlgItemText(hwnd, 102, passphrase);
276         return 0;
277       case WM_COMMAND:
278         switch (LOWORD(wParam)) {
279           case IDOK:
280             if (*passphrase)
281                 EndDialog(hwnd, 1);
282             else
283                 MessageBeep(0);
284             return 0;
285           case IDCANCEL:
286             EndDialog(hwnd, 0);
287             return 0;
288           case 102:                    /* edit box */
289             if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
290                 GetDlgItemText(hwnd, 102, passphrase,
291                                PASSPHRASE_MAXLEN - 1);
292                 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
293             }
294             return 0;
295         }
296         return 0;
297       case WM_CLOSE:
298         EndDialog(hwnd, 0);
299         return 0;
300     }
301     return 0;
302 }
303
304 /*
305  * Warn about the obsolescent key file format.
306  */
307 void old_keyfile_warning(void)
308 {
309     static const char mbtitle[] = "PuTTY Key File Warning";
310     static const char message[] =
311         "You are loading an SSH-2 private key which has an\n"
312         "old version of the file format. This means your key\n"
313         "file is not fully tamperproof. Future versions of\n"
314         "PuTTY may stop supporting this private key format,\n"
315         "so we recommend you convert your key to the new\n"
316         "format.\n"
317         "\n"
318         "You can perform this conversion by loading the key\n"
319         "into PuTTYgen and then saving it again.";
320
321     MessageBox(NULL, message, mbtitle, MB_OK);
322 }
323
324 /*
325  * Update the visible key list.
326  */
327 static void keylist_update(void)
328 {
329     struct RSAKey *rkey;
330     struct ssh2_userkey *skey;
331     int i;
332
333     if (keylist) {
334         SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
335         for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
336             char listentry[512], *p;
337             /*
338              * Replace two spaces in the fingerprint with tabs, for
339              * nice alignment in the box.
340              */
341             strcpy(listentry, "ssh1\t");
342             p = listentry + strlen(listentry);
343             rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
344             p = strchr(listentry, ' ');
345             if (p)
346                 *p = '\t';
347             p = strchr(listentry, ' ');
348             if (p)
349                 *p = '\t';
350             SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
351                                0, (LPARAM) listentry);
352         }
353         for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
354             char listentry[512], *p;
355             int len;
356             /*
357              * Replace two spaces in the fingerprint with tabs, for
358              * nice alignment in the box.
359              */
360             p = skey->alg->fingerprint(skey->data);
361             strncpy(listentry, p, sizeof(listentry));
362             p = strchr(listentry, ' ');
363             if (p)
364                 *p = '\t';
365             p = strchr(listentry, ' ');
366             if (p)
367                 *p = '\t';
368             len = strlen(listentry);
369             if (len < sizeof(listentry) - 2) {
370                 listentry[len] = '\t';
371                 strncpy(listentry + len + 1, skey->comment,
372                         sizeof(listentry) - len - 1);
373             }
374             SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
375                                (LPARAM) listentry);
376         }
377         SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
378     }
379 }
380
381 /*
382  * This function loads a key from a file and adds it.
383  */
384 static void add_keyfile(Filename filename)
385 {
386     char passphrase[PASSPHRASE_MAXLEN];
387     struct RSAKey *rkey = NULL;
388     struct ssh2_userkey *skey = NULL;
389     int needs_pass;
390     int ret;
391     int attempts;
392     char *comment;
393     const char *error = NULL;
394     struct PassphraseProcStruct pps;
395     int type;
396     int original_pass;
397         
398     type = key_type(&filename);
399     if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
400         char *msg = dupprintf("Couldn't load this key (%s)",
401                               key_type_to_str(type));
402         message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
403                     HELPCTXID(errors_cantloadkey));
404         sfree(msg);
405         return;
406     }
407
408     /*
409      * See if the key is already loaded (in the primary Pageant,
410      * which may or may not be us).
411      */
412     {
413         void *blob;
414         unsigned char *keylist, *p;
415         int i, nkeys, bloblen, keylistlen;
416
417         if (type == SSH_KEYTYPE_SSH1) {
418             if (!rsakey_pubblob(&filename, &blob, &bloblen, NULL, &error)) {
419                 char *msg = dupprintf("Couldn't load private key (%s)", error);
420                 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
421                             HELPCTXID(errors_cantloadkey));
422                 sfree(msg);
423                 return;
424             }
425             keylist = get_keylist1(&keylistlen);
426         } else {
427             unsigned char *blob2;
428             blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen,
429                                         NULL, &error);
430             if (!blob) {
431                 char *msg = dupprintf("Couldn't load private key (%s)", error);
432                 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
433                             HELPCTXID(errors_cantloadkey));
434                 sfree(msg);
435                 return;
436             }
437             /* For our purposes we want the blob prefixed with its length */
438             blob2 = snewn(bloblen+4, unsigned char);
439             PUT_32BIT(blob2, bloblen);
440             memcpy(blob2 + 4, blob, bloblen);
441             sfree(blob);
442             blob = blob2;
443
444             keylist = get_keylist2(&keylistlen);
445         }
446         if (keylist) {
447             if (keylistlen < 4) {
448                 MessageBox(NULL, "Received broken key list?!", APPNAME,
449                            MB_OK | MB_ICONERROR);
450                 return;
451             }
452             nkeys = GET_32BIT(keylist);
453             p = keylist + 4;
454             keylistlen -= 4;
455
456             for (i = 0; i < nkeys; i++) {
457                 if (!memcmp(blob, p, bloblen)) {
458                     /* Key is already present; we can now leave. */
459                     sfree(keylist);
460                     sfree(blob);
461                     return;
462                 }
463                 /* Now skip over public blob */
464                 if (type == SSH_KEYTYPE_SSH1) {
465                     int n = rsa_public_blob_len(p, keylistlen);
466                     if (n < 0) {
467                         MessageBox(NULL, "Received broken key list?!", APPNAME,
468                                    MB_OK | MB_ICONERROR);
469                         return;
470                     }
471                     p += n;
472                     keylistlen -= n;
473                 } else {
474                     int n;
475                     if (keylistlen < 4) {
476                         MessageBox(NULL, "Received broken key list?!", APPNAME,
477                                    MB_OK | MB_ICONERROR);
478                         return;
479                     }
480                     n = 4 + GET_32BIT(p);
481                     if (keylistlen < n) {
482                         MessageBox(NULL, "Received broken key list?!", APPNAME,
483                                    MB_OK | MB_ICONERROR);
484                         return;
485                     }
486                     p += n;
487                     keylistlen -= n;
488                 }
489                 /* Now skip over comment field */
490                 {
491                     int n;
492                     if (keylistlen < 4) {
493                         MessageBox(NULL, "Received broken key list?!", APPNAME,
494                                    MB_OK | MB_ICONERROR);
495                         return;
496                     }
497                     n = 4 + GET_32BIT(p);
498                     if (keylistlen < n) {
499                         MessageBox(NULL, "Received broken key list?!", APPNAME,
500                                    MB_OK | MB_ICONERROR);
501                         return;
502                     }
503                     p += n;
504                     keylistlen -= n;
505                 }
506             }
507
508             sfree(keylist);
509         }
510
511         sfree(blob);
512     }
513
514     error = NULL;
515     if (type == SSH_KEYTYPE_SSH1)
516         needs_pass = rsakey_encrypted(&filename, &comment);
517     else
518         needs_pass = ssh2_userkey_encrypted(&filename, &comment);
519     attempts = 0;
520     if (type == SSH_KEYTYPE_SSH1)
521         rkey = snew(struct RSAKey);
522     pps.passphrase = passphrase;
523     pps.comment = comment;
524     original_pass = 0;
525     do {
526         if (needs_pass) {
527             /* try all the remembered passphrases first */
528             char *pp = index234(passphrases, attempts);
529             if(pp) {
530                 strcpy(passphrase, pp);
531             } else {
532                 int dlgret;
533                 original_pass = 1;
534                 dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
535                                         NULL, PassphraseProc, (LPARAM) &pps);
536                 passphrase_box = NULL;
537                 if (!dlgret) {
538                     if (comment)
539                         sfree(comment);
540                     if (type == SSH_KEYTYPE_SSH1)
541                         sfree(rkey);
542                     return;                    /* operation cancelled */
543                 }
544             }
545         } else
546             *passphrase = '\0';
547         if (type == SSH_KEYTYPE_SSH1)
548             ret = loadrsakey(&filename, rkey, passphrase, &error);
549         else {
550             skey = ssh2_load_userkey(&filename, passphrase, &error);
551             if (skey == SSH2_WRONG_PASSPHRASE)
552                 ret = -1;
553             else if (!skey)
554                 ret = 0;
555             else
556                 ret = 1;
557         }
558         attempts++;
559     } while (ret == -1);
560
561     /* if they typed in an ok passphrase, remember it */
562     if(original_pass && ret) {
563         char *pp = dupstr(passphrase);
564         addpos234(passphrases, pp, 0);
565     }
566
567     if (comment)
568         sfree(comment);
569     if (ret == 0) {
570         char *msg = dupprintf("Couldn't load private key (%s)", error);
571         message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
572                     HELPCTXID(errors_cantloadkey));
573         sfree(msg);
574         if (type == SSH_KEYTYPE_SSH1)
575             sfree(rkey);
576         return;
577     }
578     if (type == SSH_KEYTYPE_SSH1) {
579         if (already_running) {
580             unsigned char *request, *response;
581             void *vresponse;
582             int reqlen, clen, resplen, ret;
583
584             clen = strlen(rkey->comment);
585
586             reqlen = 4 + 1 +           /* length, message type */
587                 4 +                    /* bit count */
588                 ssh1_bignum_length(rkey->modulus) +
589                 ssh1_bignum_length(rkey->exponent) +
590                 ssh1_bignum_length(rkey->private_exponent) +
591                 ssh1_bignum_length(rkey->iqmp) +
592                 ssh1_bignum_length(rkey->p) +
593                 ssh1_bignum_length(rkey->q) + 4 + clen  /* comment */
594                 ;
595
596             request = snewn(reqlen, unsigned char);
597
598             request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
599             reqlen = 5;
600             PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
601             reqlen += 4;
602             reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
603             reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
604             reqlen +=
605                 ssh1_write_bignum(request + reqlen,
606                                   rkey->private_exponent);
607             reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
608             reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
609             reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
610             PUT_32BIT(request + reqlen, clen);
611             memcpy(request + reqlen + 4, rkey->comment, clen);
612             reqlen += 4 + clen;
613             PUT_32BIT(request, reqlen - 4);
614
615             ret = agent_query(request, reqlen, &vresponse, &resplen,
616                               NULL, NULL);
617             assert(ret == 1);
618             response = vresponse;
619             if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
620                 MessageBox(NULL, "The already running Pageant "
621                            "refused to add the key.", APPNAME,
622                            MB_OK | MB_ICONERROR);
623
624             sfree(request);
625             sfree(response);
626         } else {
627             if (add234(rsakeys, rkey) != rkey)
628                 sfree(rkey);           /* already present, don't waste RAM */
629         }
630     } else {
631         if (already_running) {
632             unsigned char *request, *response;
633             void *vresponse;
634             int reqlen, alglen, clen, keybloblen, resplen, ret;
635             alglen = strlen(skey->alg->name);
636             clen = strlen(skey->comment);
637
638             keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
639
640             reqlen = 4 + 1 +           /* length, message type */
641                 4 + alglen +           /* algorithm name */
642                 keybloblen +           /* key data */
643                 4 + clen               /* comment */
644                 ;
645
646             request = snewn(reqlen, unsigned char);
647
648             request[4] = SSH2_AGENTC_ADD_IDENTITY;
649             reqlen = 5;
650             PUT_32BIT(request + reqlen, alglen);
651             reqlen += 4;
652             memcpy(request + reqlen, skey->alg->name, alglen);
653             reqlen += alglen;
654             reqlen += skey->alg->openssh_fmtkey(skey->data,
655                                                 request + reqlen,
656                                                 keybloblen);
657             PUT_32BIT(request + reqlen, clen);
658             memcpy(request + reqlen + 4, skey->comment, clen);
659             reqlen += clen + 4;
660             PUT_32BIT(request, reqlen - 4);
661
662             ret = agent_query(request, reqlen, &vresponse, &resplen,
663                               NULL, NULL);
664             assert(ret == 1);
665             response = vresponse;
666             if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
667                 MessageBox(NULL, "The already running Pageant "
668                            "refused to add the key.", APPNAME,
669                            MB_OK | MB_ICONERROR);
670
671             sfree(request);
672             sfree(response);
673         } else {
674             if (add234(ssh2keys, skey) != skey) {
675                 skey->alg->freekey(skey->data);
676                 sfree(skey);           /* already present, don't waste RAM */
677             }
678         }
679     }
680 }
681
682 /*
683  * Create an SSH-1 key list in a malloc'ed buffer; return its
684  * length.
685  */
686 static void *make_keylist1(int *length)
687 {
688     int i, nkeys, len;
689     struct RSAKey *key;
690     unsigned char *blob, *p, *ret;
691     int bloblen;
692
693     /*
694      * Count up the number and length of keys we hold.
695      */
696     len = 4;
697     nkeys = 0;
698     for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
699         nkeys++;
700         blob = rsa_public_blob(key, &bloblen);
701         len += bloblen;
702         sfree(blob);
703         len += 4 + strlen(key->comment);
704     }
705
706     /* Allocate the buffer. */
707     p = ret = snewn(len, unsigned char);
708     if (length) *length = len;
709
710     PUT_32BIT(p, nkeys);
711     p += 4;
712     for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
713         blob = rsa_public_blob(key, &bloblen);
714         memcpy(p, blob, bloblen);
715         p += bloblen;
716         sfree(blob);
717         PUT_32BIT(p, strlen(key->comment));
718         memcpy(p + 4, key->comment, strlen(key->comment));
719         p += 4 + strlen(key->comment);
720     }
721
722     assert(p - ret == len);
723     return ret;
724 }
725
726 /*
727  * Create an SSH-2 key list in a malloc'ed buffer; return its
728  * length.
729  */
730 static void *make_keylist2(int *length)
731 {
732     struct ssh2_userkey *key;
733     int i, len, nkeys;
734     unsigned char *blob, *p, *ret;
735     int bloblen;
736
737     /*
738      * Count up the number and length of keys we hold.
739      */
740     len = 4;
741     nkeys = 0;
742     for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
743         nkeys++;
744         len += 4;              /* length field */
745         blob = key->alg->public_blob(key->data, &bloblen);
746         len += bloblen;
747         sfree(blob);
748         len += 4 + strlen(key->comment);
749     }
750
751     /* Allocate the buffer. */
752     p = ret = snewn(len, unsigned char);
753     if (length) *length = len;
754
755     /*
756      * Packet header is the obvious five bytes, plus four
757      * bytes for the key count.
758      */
759     PUT_32BIT(p, nkeys);
760     p += 4;
761     for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
762         blob = key->alg->public_blob(key->data, &bloblen);
763         PUT_32BIT(p, bloblen);
764         p += 4;
765         memcpy(p, blob, bloblen);
766         p += bloblen;
767         sfree(blob);
768         PUT_32BIT(p, strlen(key->comment));
769         memcpy(p + 4, key->comment, strlen(key->comment));
770         p += 4 + strlen(key->comment);
771     }
772
773     assert(p - ret == len);
774     return ret;
775 }
776
777 /*
778  * Acquire a keylist1 from the primary Pageant; this means either
779  * calling make_keylist1 (if that's us) or sending a message to the
780  * primary Pageant (if it's not).
781  */
782 static void *get_keylist1(int *length)
783 {
784     void *ret;
785
786     if (already_running) {
787         unsigned char request[5], *response;
788         void *vresponse;
789         int resplen, retval;
790         request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
791         PUT_32BIT(request, 4);
792
793         retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
794         assert(retval == 1);
795         response = vresponse;
796         if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)
797             return NULL;
798
799         ret = snewn(resplen-5, unsigned char);
800         memcpy(ret, response+5, resplen-5);
801         sfree(response);
802
803         if (length)
804             *length = resplen-5;
805     } else {
806         ret = make_keylist1(length);
807     }
808     return ret;
809 }
810
811 /*
812  * Acquire a keylist2 from the primary Pageant; this means either
813  * calling make_keylist2 (if that's us) or sending a message to the
814  * primary Pageant (if it's not).
815  */
816 static void *get_keylist2(int *length)
817 {
818     void *ret;
819
820     if (already_running) {
821         unsigned char request[5], *response;
822         void *vresponse;
823         int resplen, retval;
824
825         request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
826         PUT_32BIT(request, 4);
827
828         retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
829         assert(retval == 1);
830         response = vresponse;
831         if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)
832             return NULL;
833
834         ret = snewn(resplen-5, unsigned char);
835         memcpy(ret, response+5, resplen-5);
836         sfree(response);
837
838         if (length)
839             *length = resplen-5;
840     } else {
841         ret = make_keylist2(length);
842     }
843     return ret;
844 }
845
846 /*
847  * This is the main agent function that answers messages.
848  */
849 static void answer_msg(void *msg)
850 {
851     unsigned char *p = msg;
852     unsigned char *ret = msg;
853     unsigned char *msgend;
854     int type;
855
856     /*
857      * Get the message length.
858      */
859     msgend = p + 4 + GET_32BIT(p);
860
861     /*
862      * Get the message type.
863      */
864     if (msgend < p+5)
865         goto failure;
866     type = p[4];
867
868     p += 5;
869     switch (type) {
870       case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
871         /*
872          * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
873          */
874         {
875             int len;
876             void *keylist;
877
878             ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
879             keylist = make_keylist1(&len);
880             if (len + 5 > AGENT_MAX_MSGLEN) {
881                 sfree(keylist);
882                 goto failure;
883             }
884             PUT_32BIT(ret, len + 1);
885             memcpy(ret + 5, keylist, len);
886             sfree(keylist);
887         }
888         break;
889       case SSH2_AGENTC_REQUEST_IDENTITIES:
890         /*
891          * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
892          */
893         {
894             int len;
895             void *keylist;
896
897             ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
898             keylist = make_keylist2(&len);
899             if (len + 5 > AGENT_MAX_MSGLEN) {
900                 sfree(keylist);
901                 goto failure;
902             }
903             PUT_32BIT(ret, len + 1);
904             memcpy(ret + 5, keylist, len);
905             sfree(keylist);
906         }
907         break;
908       case SSH1_AGENTC_RSA_CHALLENGE:
909         /*
910          * Reply with either SSH1_AGENT_RSA_RESPONSE or
911          * SSH_AGENT_FAILURE, depending on whether we have that key
912          * or not.
913          */
914         {
915             struct RSAKey reqkey, *key;
916             Bignum challenge, response;
917             unsigned char response_source[48], response_md5[16];
918             struct MD5Context md5c;
919             int i, len;
920
921             p += 4;
922             i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
923             if (i < 0)
924                 goto failure;
925             p += i;
926             i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
927             if (i < 0)
928                 goto failure;
929             p += i;
930             i = ssh1_read_bignum(p, msgend - p, &challenge);
931             if (i < 0)
932                 goto failure;
933             p += i;
934             if (msgend < p+16) {
935                 freebn(reqkey.exponent);
936                 freebn(reqkey.modulus);
937                 freebn(challenge);
938                 goto failure;
939             }
940             memcpy(response_source + 32, p, 16);
941             p += 16;
942             if (msgend < p+4 ||
943                 GET_32BIT(p) != 1 ||
944                 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
945                 freebn(reqkey.exponent);
946                 freebn(reqkey.modulus);
947                 freebn(challenge);
948                 goto failure;
949             }
950             response = rsadecrypt(challenge, key);
951             for (i = 0; i < 32; i++)
952                 response_source[i] = bignum_byte(response, 31 - i);
953
954             MD5Init(&md5c);
955             MD5Update(&md5c, response_source, 48);
956             MD5Final(response_md5, &md5c);
957             memset(response_source, 0, 48);     /* burn the evidence */
958             freebn(response);          /* and that evidence */
959             freebn(challenge);         /* yes, and that evidence */
960             freebn(reqkey.exponent);   /* and free some memory ... */
961             freebn(reqkey.modulus);    /* ... while we're at it. */
962
963             /*
964              * Packet is the obvious five byte header, plus sixteen
965              * bytes of MD5.
966              */
967             len = 5 + 16;
968             PUT_32BIT(ret, len - 4);
969             ret[4] = SSH1_AGENT_RSA_RESPONSE;
970             memcpy(ret + 5, response_md5, 16);
971         }
972         break;
973       case SSH2_AGENTC_SIGN_REQUEST:
974         /*
975          * Reply with either SSH2_AGENT_SIGN_RESPONSE or
976          * SSH_AGENT_FAILURE, depending on whether we have that key
977          * or not.
978          */
979         {
980             struct ssh2_userkey *key;
981             struct blob b;
982             unsigned char *data, *signature;
983             int datalen, siglen, len;
984
985             if (msgend < p+4)
986                 goto failure;
987             b.len = GET_32BIT(p);
988             p += 4;
989             if (msgend < p+b.len)
990                 goto failure;
991             b.blob = p;
992             p += b.len;
993             if (msgend < p+4)
994                 goto failure;
995             datalen = GET_32BIT(p);
996             p += 4;
997             if (msgend < p+datalen)
998                 goto failure;
999             data = p;
1000             key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1001             if (!key)
1002                 goto failure;
1003             signature = key->alg->sign(key->data, data, datalen, &siglen);
1004             len = 5 + 4 + siglen;
1005             PUT_32BIT(ret, len - 4);
1006             ret[4] = SSH2_AGENT_SIGN_RESPONSE;
1007             PUT_32BIT(ret + 5, siglen);
1008             memcpy(ret + 5 + 4, signature, siglen);
1009             sfree(signature);
1010         }
1011         break;
1012       case SSH1_AGENTC_ADD_RSA_IDENTITY:
1013         /*
1014          * Add to the list and return SSH_AGENT_SUCCESS, or
1015          * SSH_AGENT_FAILURE if the key was malformed.
1016          */
1017         {
1018             struct RSAKey *key;
1019             char *comment;
1020             int n, commentlen;
1021
1022             key = snew(struct RSAKey);
1023             memset(key, 0, sizeof(struct RSAKey));
1024
1025             n = makekey(p, msgend - p, key, NULL, 1);
1026             if (n < 0) {
1027                 freersakey(key);
1028                 sfree(key);
1029                 goto failure;
1030             }
1031             p += n;
1032
1033             n = makeprivate(p, msgend - p, key);
1034             if (n < 0) {
1035                 freersakey(key);
1036                 sfree(key);
1037                 goto failure;
1038             }
1039             p += n;
1040
1041             n = ssh1_read_bignum(p, msgend - p, &key->iqmp);  /* p^-1 mod q */
1042             if (n < 0) {
1043                 freersakey(key);
1044                 sfree(key);
1045                 goto failure;
1046             }
1047             p += n;
1048
1049             n = ssh1_read_bignum(p, msgend - p, &key->p);  /* p */
1050             if (n < 0) {
1051                 freersakey(key);
1052                 sfree(key);
1053                 goto failure;
1054             }
1055             p += n;
1056
1057             n = ssh1_read_bignum(p, msgend - p, &key->q);  /* q */
1058             if (n < 0) {
1059                 freersakey(key);
1060                 sfree(key);
1061                 goto failure;
1062             }
1063             p += n;
1064
1065             if (msgend < p+4) {
1066                 freersakey(key);
1067                 sfree(key);
1068                 goto failure;
1069             }
1070             commentlen = GET_32BIT(p);
1071
1072             if (msgend < p+commentlen) {
1073                 freersakey(key);
1074                 sfree(key);
1075                 goto failure;
1076             }
1077
1078             comment = snewn(commentlen+1, char);
1079             if (comment) {
1080                 memcpy(comment, p + 4, commentlen);
1081                 comment[commentlen] = '\0';
1082                 key->comment = comment;
1083             }
1084             PUT_32BIT(ret, 1);
1085             ret[4] = SSH_AGENT_FAILURE;
1086             if (add234(rsakeys, key) == key) {
1087                 keylist_update();
1088                 ret[4] = SSH_AGENT_SUCCESS;
1089             } else {
1090                 freersakey(key);
1091                 sfree(key);
1092             }
1093         }
1094         break;
1095       case SSH2_AGENTC_ADD_IDENTITY:
1096         /*
1097          * Add to the list and return SSH_AGENT_SUCCESS, or
1098          * SSH_AGENT_FAILURE if the key was malformed.
1099          */
1100         {
1101             struct ssh2_userkey *key;
1102             char *comment, *alg;
1103             int alglen, commlen;
1104             int bloblen;
1105
1106
1107             if (msgend < p+4)
1108                 goto failure;
1109             alglen = GET_32BIT(p);
1110             p += 4;
1111             if (msgend < p+alglen)
1112                 goto failure;
1113             alg = p;
1114             p += alglen;
1115
1116             key = snew(struct ssh2_userkey);
1117             /* Add further algorithm names here. */
1118             if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
1119                 key->alg = &ssh_rsa;
1120             else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
1121                 key->alg = &ssh_dss;
1122             else {
1123                 sfree(key);
1124                 goto failure;
1125             }
1126
1127             bloblen = msgend - p;
1128             key->data = key->alg->openssh_createkey(&p, &bloblen);
1129             if (!key->data) {
1130                 sfree(key);
1131                 goto failure;
1132             }
1133
1134             /*
1135              * p has been advanced by openssh_createkey, but
1136              * certainly not _beyond_ the end of the buffer.
1137              */
1138             assert(p <= msgend);
1139
1140             if (msgend < p+4) {
1141                 key->alg->freekey(key->data);
1142                 sfree(key);
1143                 goto failure;
1144             }
1145             commlen = GET_32BIT(p);
1146             p += 4;
1147
1148             if (msgend < p+commlen) {
1149                 key->alg->freekey(key->data);
1150                 sfree(key);
1151                 goto failure;
1152             }
1153             comment = snewn(commlen + 1, char);
1154             if (comment) {
1155                 memcpy(comment, p, commlen);
1156                 comment[commlen] = '\0';
1157             }
1158             key->comment = comment;
1159
1160             PUT_32BIT(ret, 1);
1161             ret[4] = SSH_AGENT_FAILURE;
1162             if (add234(ssh2keys, key) == key) {
1163                 keylist_update();
1164                 ret[4] = SSH_AGENT_SUCCESS;
1165             } else {
1166                 key->alg->freekey(key->data);
1167                 sfree(key->comment);
1168                 sfree(key);
1169             }
1170         }
1171         break;
1172       case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
1173         /*
1174          * Remove from the list and return SSH_AGENT_SUCCESS, or
1175          * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1176          * start with.
1177          */
1178         {
1179             struct RSAKey reqkey, *key;
1180             int n;
1181
1182             n = makekey(p, msgend - p, &reqkey, NULL, 0);
1183             if (n < 0)
1184                 goto failure;
1185
1186             key = find234(rsakeys, &reqkey, NULL);
1187             freebn(reqkey.exponent);
1188             freebn(reqkey.modulus);
1189             PUT_32BIT(ret, 1);
1190             ret[4] = SSH_AGENT_FAILURE;
1191             if (key) {
1192                 del234(rsakeys, key);
1193                 keylist_update();
1194                 freersakey(key);
1195                 sfree(key);
1196                 ret[4] = SSH_AGENT_SUCCESS;
1197             }
1198         }
1199         break;
1200       case SSH2_AGENTC_REMOVE_IDENTITY:
1201         /*
1202          * Remove from the list and return SSH_AGENT_SUCCESS, or
1203          * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1204          * start with.
1205          */
1206         {
1207             struct ssh2_userkey *key;
1208             struct blob b;
1209
1210             if (msgend < p+4)
1211                 goto failure;
1212             b.len = GET_32BIT(p);
1213             p += 4;
1214
1215             if (msgend < p+b.len)
1216                 goto failure;
1217             b.blob = p;
1218             p += b.len;
1219
1220             key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1221             if (!key)
1222                 goto failure;
1223
1224             PUT_32BIT(ret, 1);
1225             ret[4] = SSH_AGENT_FAILURE;
1226             if (key) {
1227                 del234(ssh2keys, key);
1228                 keylist_update();
1229                 key->alg->freekey(key->data);
1230                 sfree(key);
1231                 ret[4] = SSH_AGENT_SUCCESS;
1232             }
1233         }
1234         break;
1235       case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
1236         /*
1237          * Remove all SSH-1 keys. Always returns success.
1238          */
1239         {
1240             struct RSAKey *rkey;
1241
1242             while ((rkey = index234(rsakeys, 0)) != NULL) {
1243                 del234(rsakeys, rkey);
1244                 freersakey(rkey);
1245                 sfree(rkey);
1246             }
1247             keylist_update();
1248
1249             PUT_32BIT(ret, 1);
1250             ret[4] = SSH_AGENT_SUCCESS;
1251         }
1252         break;
1253       case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
1254         /*
1255          * Remove all SSH-2 keys. Always returns success.
1256          */
1257         {
1258             struct ssh2_userkey *skey;
1259
1260             while ((skey = index234(ssh2keys, 0)) != NULL) {
1261                 del234(ssh2keys, skey);
1262                 skey->alg->freekey(skey->data);
1263                 sfree(skey);
1264             }
1265             keylist_update();
1266
1267             PUT_32BIT(ret, 1);
1268             ret[4] = SSH_AGENT_SUCCESS;
1269         }
1270         break;
1271       default:
1272       failure:
1273         /*
1274          * Unrecognised message. Return SSH_AGENT_FAILURE.
1275          */
1276         PUT_32BIT(ret, 1);
1277         ret[4] = SSH_AGENT_FAILURE;
1278         break;
1279     }
1280 }
1281
1282 /*
1283  * Key comparison function for the 2-3-4 tree of RSA keys.
1284  */
1285 static int cmpkeys_rsa(void *av, void *bv)
1286 {
1287     struct RSAKey *a = (struct RSAKey *) av;
1288     struct RSAKey *b = (struct RSAKey *) bv;
1289     Bignum am, bm;
1290     int alen, blen;
1291
1292     am = a->modulus;
1293     bm = b->modulus;
1294     /*
1295      * Compare by length of moduli.
1296      */
1297     alen = bignum_bitcount(am);
1298     blen = bignum_bitcount(bm);
1299     if (alen > blen)
1300         return +1;
1301     else if (alen < blen)
1302         return -1;
1303     /*
1304      * Now compare by moduli themselves.
1305      */
1306     alen = (alen + 7) / 8;             /* byte count */
1307     while (alen-- > 0) {
1308         int abyte, bbyte;
1309         abyte = bignum_byte(am, alen);
1310         bbyte = bignum_byte(bm, alen);
1311         if (abyte > bbyte)
1312             return +1;
1313         else if (abyte < bbyte)
1314             return -1;
1315     }
1316     /*
1317      * Give up.
1318      */
1319     return 0;
1320 }
1321
1322 /*
1323  * Key comparison function for the 2-3-4 tree of SSH-2 keys.
1324  */
1325 static int cmpkeys_ssh2(void *av, void *bv)
1326 {
1327     struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1328     struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1329     int i;
1330     int alen, blen;
1331     unsigned char *ablob, *bblob;
1332     int c;
1333
1334     /*
1335      * Compare purely by public blob.
1336      */
1337     ablob = a->alg->public_blob(a->data, &alen);
1338     bblob = b->alg->public_blob(b->data, &blen);
1339
1340     c = 0;
1341     for (i = 0; i < alen && i < blen; i++) {
1342         if (ablob[i] < bblob[i]) {
1343             c = -1;
1344             break;
1345         } else if (ablob[i] > bblob[i]) {
1346             c = +1;
1347             break;
1348         }
1349     }
1350     if (c == 0 && i < alen)
1351         c = +1;                        /* a is longer */
1352     if (c == 0 && i < blen)
1353         c = -1;                        /* a is longer */
1354
1355     sfree(ablob);
1356     sfree(bblob);
1357
1358     return c;
1359 }
1360
1361 /*
1362  * Key comparison function for looking up a blob in the 2-3-4 tree
1363  * of SSH-2 keys.
1364  */
1365 static int cmpkeys_ssh2_asymm(void *av, void *bv)
1366 {
1367     struct blob *a = (struct blob *) av;
1368     struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1369     int i;
1370     int alen, blen;
1371     unsigned char *ablob, *bblob;
1372     int c;
1373
1374     /*
1375      * Compare purely by public blob.
1376      */
1377     ablob = a->blob;
1378     alen = a->len;
1379     bblob = b->alg->public_blob(b->data, &blen);
1380
1381     c = 0;
1382     for (i = 0; i < alen && i < blen; i++) {
1383         if (ablob[i] < bblob[i]) {
1384             c = -1;
1385             break;
1386         } else if (ablob[i] > bblob[i]) {
1387             c = +1;
1388             break;
1389         }
1390     }
1391     if (c == 0 && i < alen)
1392         c = +1;                        /* a is longer */
1393     if (c == 0 && i < blen)
1394         c = -1;                        /* a is longer */
1395
1396     sfree(bblob);
1397
1398     return c;
1399 }
1400
1401 /*
1402  * Prompt for a key file to add, and add it.
1403  */
1404 static void prompt_add_keyfile(void)
1405 {
1406     OPENFILENAME of;
1407     char *filelist = snewn(8192, char);
1408         
1409     if (!keypath) keypath = filereq_new();
1410     memset(&of, 0, sizeof(of));
1411     of.hwndOwner = hwnd;
1412     of.lpstrFilter = FILTER_KEY_FILES;
1413     of.lpstrCustomFilter = NULL;
1414     of.nFilterIndex = 1;
1415     of.lpstrFile = filelist;
1416     *filelist = '\0';
1417     of.nMaxFile = 8192;
1418     of.lpstrFileTitle = NULL;
1419     of.lpstrTitle = "Select Private Key File";
1420     of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
1421     if (request_file(keypath, &of, TRUE, FALSE)) {
1422         if(strlen(filelist) > of.nFileOffset)
1423             /* Only one filename returned? */
1424             add_keyfile(filename_from_str(filelist));
1425         else {
1426             /* we are returned a bunch of strings, end to
1427              * end. first string is the directory, the
1428              * rest the filenames. terminated with an
1429              * empty string.
1430              */
1431             char *dir = filelist;
1432             char *filewalker = filelist + strlen(dir) + 1;
1433             while (*filewalker != '\0') {
1434                 char *filename = dupcat(dir, "\\", filewalker, NULL);
1435                 add_keyfile(filename_from_str(filename));
1436                 sfree(filename);
1437                 filewalker += strlen(filewalker) + 1;
1438             }
1439         }
1440
1441         keylist_update();
1442         forget_passphrases();
1443     }
1444     sfree(filelist);
1445 }
1446
1447 /*
1448  * Dialog-box function for the key list box.
1449  */
1450 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1451                                 WPARAM wParam, LPARAM lParam)
1452 {
1453     struct RSAKey *rkey;
1454     struct ssh2_userkey *skey;
1455
1456     switch (msg) {
1457       case WM_INITDIALOG:
1458         /*
1459          * Centre the window.
1460          */
1461         {                              /* centre the window */
1462             RECT rs, rd;
1463             HWND hw;
1464
1465             hw = GetDesktopWindow();
1466             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1467                 MoveWindow(hwnd,
1468                            (rs.right + rs.left + rd.left - rd.right) / 2,
1469                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1470                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
1471         }
1472
1473         if (has_help())
1474             SetWindowLongPtr(hwnd, GWL_EXSTYLE,
1475                              GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
1476                              WS_EX_CONTEXTHELP);
1477         else {
1478             HWND item = GetDlgItem(hwnd, 103);   /* the Help button */
1479             if (item)
1480                 DestroyWindow(item);
1481         }
1482
1483         keylist = hwnd;
1484         {
1485             static int tabs[] = { 35, 60, 210 };
1486             SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1487                                sizeof(tabs) / sizeof(*tabs),
1488                                (LPARAM) tabs);
1489         }
1490         keylist_update();
1491         return 0;
1492       case WM_COMMAND:
1493         switch (LOWORD(wParam)) {
1494           case IDOK:
1495           case IDCANCEL:
1496             keylist = NULL;
1497             DestroyWindow(hwnd);
1498             return 0;
1499           case 101:                    /* add key */
1500             if (HIWORD(wParam) == BN_CLICKED ||
1501                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1502                 if (passphrase_box) {
1503                     MessageBeep(MB_ICONERROR);
1504                     SetForegroundWindow(passphrase_box);
1505                     break;
1506                 }
1507                 prompt_add_keyfile();
1508             }
1509             return 0;
1510           case 102:                    /* remove key */
1511             if (HIWORD(wParam) == BN_CLICKED ||
1512                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1513                 int i;
1514                 int rCount, sCount;
1515                 int *selectedArray;
1516                 
1517                 /* our counter within the array of selected items */
1518                 int itemNum;
1519                 
1520                 /* get the number of items selected in the list */
1521                 int numSelected = 
1522                         SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1523                 
1524                 /* none selected? that was silly */
1525                 if (numSelected == 0) {
1526                     MessageBeep(0);
1527                     break;
1528                 }
1529
1530                 /* get item indices in an array */
1531                 selectedArray = snewn(numSelected, int);
1532                 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1533                                 numSelected, (WPARAM)selectedArray);
1534                 
1535                 itemNum = numSelected - 1;
1536                 rCount = count234(rsakeys);
1537                 sCount = count234(ssh2keys);
1538                 
1539                 /* go through the non-rsakeys until we've covered them all, 
1540                  * and/or we're out of selected items to check. note that
1541                  * we go *backwards*, to avoid complications from deleting
1542                  * things hence altering the offset of subsequent items
1543                  */
1544             for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1545                         skey = index234(ssh2keys, i);
1546                         
1547                         if (selectedArray[itemNum] == rCount + i) {
1548                                 del234(ssh2keys, skey);
1549                                 skey->alg->freekey(skey->data);
1550                                 sfree(skey);
1551                                 itemNum--; 
1552                         }
1553                 }
1554                 
1555                 /* do the same for the rsa keys */
1556                 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1557                         rkey = index234(rsakeys, i);
1558
1559                         if(selectedArray[itemNum] == i) {
1560                                 del234(rsakeys, rkey);
1561                                 freersakey(rkey);
1562                                 sfree(rkey);
1563                                 itemNum--;
1564                         }
1565                 }
1566
1567                 sfree(selectedArray); 
1568                 keylist_update();
1569             }
1570             return 0;
1571           case 103:                    /* help */
1572             if (HIWORD(wParam) == BN_CLICKED ||
1573                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1574                 launch_help(hwnd, WINHELP_CTX_pageant_general);
1575             }
1576             return 0;
1577         }
1578         return 0;
1579       case WM_HELP:
1580         {
1581             int id = ((LPHELPINFO)lParam)->iCtrlId;
1582             char *topic = NULL;
1583             switch (id) {
1584               case 100: topic = WINHELP_CTX_pageant_keylist; break;
1585               case 101: topic = WINHELP_CTX_pageant_addkey; break;
1586               case 102: topic = WINHELP_CTX_pageant_remkey; break;
1587             }
1588             if (topic) {
1589                 launch_help(hwnd, topic);
1590             } else {
1591                 MessageBeep(0);
1592             }
1593         }
1594         break;
1595       case WM_CLOSE:
1596         keylist = NULL;
1597         DestroyWindow(hwnd);
1598         return 0;
1599     }
1600     return 0;
1601 }
1602
1603 /* Set up a system tray icon */
1604 static BOOL AddTrayIcon(HWND hwnd)
1605 {
1606     BOOL res;
1607     NOTIFYICONDATA tnid;
1608     HICON hicon;
1609
1610 #ifdef NIM_SETVERSION
1611     tnid.uVersion = 0;
1612     res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1613 #endif
1614
1615     tnid.cbSize = sizeof(NOTIFYICONDATA);
1616     tnid.hWnd = hwnd;
1617     tnid.uID = 1;              /* unique within this systray use */
1618     tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1619     tnid.uCallbackMessage = WM_SYSTRAY;
1620     tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
1621     strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1622
1623     res = Shell_NotifyIcon(NIM_ADD, &tnid);
1624
1625     if (hicon) DestroyIcon(hicon);
1626     
1627     return res;
1628 }
1629
1630 /* Update the saved-sessions menu. */
1631 static void update_sessions(void)
1632 {
1633     int num_entries;
1634     HKEY hkey;
1635     TCHAR buf[MAX_PATH + 1];
1636     MENUITEMINFO mii;
1637
1638     int index_key, index_menu;
1639
1640     if (!putty_path)
1641         return;
1642
1643     if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1644         return;
1645
1646     for(num_entries = GetMenuItemCount(session_menu);
1647         num_entries > initial_menuitems_count;
1648         num_entries--)
1649         RemoveMenu(session_menu, 0, MF_BYPOSITION);
1650
1651     index_key = 0;
1652     index_menu = 0;
1653
1654     while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1655         TCHAR session_name[MAX_PATH + 1];
1656         unmungestr(buf, session_name, MAX_PATH);
1657         if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1658             memset(&mii, 0, sizeof(mii));
1659             mii.cbSize = sizeof(mii);
1660             mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1661             mii.fType = MFT_STRING;
1662             mii.fState = MFS_ENABLED;
1663             mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1664             mii.dwTypeData = session_name;
1665             InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1666             index_menu++;
1667         }
1668         index_key++;
1669     }
1670
1671     RegCloseKey(hkey);
1672
1673     if(index_menu == 0) {
1674         mii.cbSize = sizeof(mii);
1675         mii.fMask = MIIM_TYPE | MIIM_STATE;
1676         mii.fType = MFT_STRING;
1677         mii.fState = MFS_GRAYED;
1678         mii.dwTypeData = _T("(No sessions)");
1679         InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1680     }
1681 }
1682
1683 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1684                                 WPARAM wParam, LPARAM lParam)
1685 {
1686     int ret;
1687     static int menuinprogress;
1688     static UINT msgTaskbarCreated = 0;
1689
1690     switch (message) {
1691       case WM_CREATE:
1692         msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1693         break;
1694       default:
1695         if (message==msgTaskbarCreated) {
1696             /*
1697              * Explorer has been restarted, so the tray icon will
1698              * have been lost.
1699              */
1700             AddTrayIcon(hwnd);
1701         }
1702         break;
1703         
1704       case WM_SYSTRAY:
1705         if (lParam == WM_RBUTTONUP) {
1706             POINT cursorpos;
1707             GetCursorPos(&cursorpos);
1708             PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1709         } else if (lParam == WM_LBUTTONDBLCLK) {
1710             /* Run the default menu item. */
1711             UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
1712             if (menuitem != -1)
1713                 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
1714         }
1715         break;
1716       case WM_SYSTRAY2:
1717         if (!menuinprogress) {
1718             menuinprogress = 1;
1719             update_sessions();
1720             SetForegroundWindow(hwnd);
1721             ret = TrackPopupMenu(systray_menu,
1722                                  TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1723                                  TPM_RIGHTBUTTON,
1724                                  wParam, lParam, 0, hwnd, NULL);
1725             menuinprogress = 0;
1726         }
1727         break;
1728       case WM_COMMAND:
1729       case WM_SYSCOMMAND:
1730         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
1731           case IDM_PUTTY:
1732             if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1733                                  SW_SHOW) <= 32) {
1734                 MessageBox(NULL, "Unable to execute PuTTY!",
1735                            "Error", MB_OK | MB_ICONERROR);
1736             }
1737             break;
1738           case IDM_CLOSE:
1739             if (passphrase_box)
1740                 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1741             SendMessage(hwnd, WM_CLOSE, 0, 0);
1742             break;
1743           case IDM_VIEWKEYS:
1744             if (!keylist) {
1745                 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
1746                                        NULL, KeyListProc);
1747                 ShowWindow(keylist, SW_SHOWNORMAL);
1748             }
1749             /* 
1750              * Sometimes the window comes up minimised / hidden for
1751              * no obvious reason. Prevent this. This also brings it
1752              * to the front if it's already present (the user
1753              * selected View Keys because they wanted to _see_ the
1754              * thing).
1755              */
1756             SetForegroundWindow(keylist);
1757             SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1758                          SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1759             break;
1760           case IDM_ADDKEY:
1761             if (passphrase_box) {
1762                 MessageBeep(MB_ICONERROR);
1763                 SetForegroundWindow(passphrase_box);
1764                 break;
1765             }
1766             prompt_add_keyfile();
1767             break;
1768           case IDM_ABOUT:
1769             if (!aboutbox) {
1770                 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
1771                                         NULL, AboutProc);
1772                 ShowWindow(aboutbox, SW_SHOWNORMAL);
1773                 /* 
1774                  * Sometimes the window comes up minimised / hidden
1775                  * for no obvious reason. Prevent this.
1776                  */
1777                 SetForegroundWindow(aboutbox);
1778                 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1779                              SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1780             }
1781             break;
1782           case IDM_HELP:
1783             launch_help(hwnd, WINHELP_CTX_pageant_general);
1784             break;
1785           default:
1786             {
1787                 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1788                     MENUITEMINFO mii;
1789                     TCHAR buf[MAX_PATH + 1];
1790                     TCHAR param[MAX_PATH + 1];
1791                     memset(&mii, 0, sizeof(mii));
1792                     mii.cbSize = sizeof(mii);
1793                     mii.fMask = MIIM_TYPE;
1794                     mii.cch = MAX_PATH;
1795                     mii.dwTypeData = buf;
1796                     GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1797                     strcpy(param, "@");
1798                     strcat(param, mii.dwTypeData);
1799                     if((int)ShellExecute(hwnd, NULL, putty_path, param,
1800                                          _T(""), SW_SHOW) <= 32) {
1801                         MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1802                                    MB_OK | MB_ICONERROR);
1803                     }
1804                 }
1805             }
1806             break;
1807         }
1808         break;
1809       case WM_DESTROY:
1810         quit_help(hwnd);
1811         PostQuitMessage(0);
1812         return 0;
1813       case WM_COPYDATA:
1814         {
1815             COPYDATASTRUCT *cds;
1816             char *mapname;
1817             void *p;
1818             HANDLE filemap;
1819 #ifndef NO_SECURITY
1820             HANDLE proc;
1821             PSID mapowner, procowner;
1822             PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1823 #endif
1824             int ret = 0;
1825
1826             cds = (COPYDATASTRUCT *) lParam;
1827             if (cds->dwData != AGENT_COPYDATA_ID)
1828                 return 0;              /* not our message, mate */
1829             mapname = (char *) cds->lpData;
1830             if (mapname[cds->cbData - 1] != '\0')
1831                 return 0;              /* failure to be ASCIZ! */
1832 #ifdef DEBUG_IPC
1833             debug(("mapname is :%s:\n", mapname));
1834 #endif
1835             filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1836 #ifdef DEBUG_IPC
1837             debug(("filemap is %p\n", filemap));
1838 #endif
1839             if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1840 #ifndef NO_SECURITY
1841                 int rc;
1842                 if (has_security) {
1843                     if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1844                                             GetCurrentProcessId())) ==
1845                         NULL) {
1846 #ifdef DEBUG_IPC
1847                         debug(("couldn't get handle for process\n"));
1848 #endif
1849                         return 0;
1850                     }
1851                     if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT,
1852                                           OWNER_SECURITY_INFORMATION,
1853                                           &procowner, NULL, NULL, NULL,
1854                                           &psd2) != ERROR_SUCCESS) {
1855 #ifdef DEBUG_IPC
1856                         debug(("couldn't get owner info for process\n"));
1857 #endif
1858                         CloseHandle(proc);
1859                         return 0;      /* unable to get security info */
1860                     }
1861                     CloseHandle(proc);
1862                     if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
1863                                                 OWNER_SECURITY_INFORMATION,
1864                                                 &mapowner, NULL, NULL, NULL,
1865                                                 &psd1) != ERROR_SUCCESS)) {
1866 #ifdef DEBUG_IPC
1867                         debug(
1868                               ("couldn't get owner info for filemap: %d\n",
1869                                rc));
1870 #endif
1871                         return 0;
1872                     }
1873 #ifdef DEBUG_IPC
1874                     debug(("got security stuff\n"));
1875 #endif
1876                     if (!EqualSid(mapowner, procowner))
1877                         return 0;      /* security ID mismatch! */
1878 #ifdef DEBUG_IPC
1879                     debug(("security stuff matched\n"));
1880 #endif
1881                     LocalFree(psd1);
1882                     LocalFree(psd2);
1883                 } else {
1884 #ifdef DEBUG_IPC
1885                     debug(("security APIs not present\n"));
1886 #endif
1887                 }
1888 #endif
1889                 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1890 #ifdef DEBUG_IPC
1891                 debug(("p is %p\n", p));
1892                 {
1893                     int i;
1894                     for (i = 0; i < 5; i++)
1895                         debug(
1896                               ("p[%d]=%02x\n", i,
1897                                ((unsigned char *) p)[i]));}
1898 #endif
1899                 answer_msg(p);
1900                 ret = 1;
1901                 UnmapViewOfFile(p);
1902             }
1903             CloseHandle(filemap);
1904             return ret;
1905         }
1906     }
1907
1908     return DefWindowProc(hwnd, message, wParam, lParam);
1909 }
1910
1911 /*
1912  * Fork and Exec the command in cmdline. [DBW]
1913  */
1914 void spawn_cmd(char *cmdline, char * args, int show)
1915 {
1916     if (ShellExecute(NULL, _T("open"), cmdline,
1917                      args, NULL, show) <= (HINSTANCE) 32) {
1918         char *msg;
1919         msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1920                         (int)GetLastError());
1921         MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1922         sfree(msg);
1923     }
1924 }
1925
1926 /*
1927  * This is a can't-happen stub, since Pageant never makes
1928  * asynchronous agent requests.
1929  */
1930 void agent_schedule_callback(void (*callback)(void *, void *, int),
1931                              void *callback_ctx, void *data, int len)
1932 {
1933     assert(!"We shouldn't get here");
1934 }
1935
1936 void cleanup_exit(int code)
1937 {
1938     shutdown_help();
1939     exit(code);
1940 }
1941
1942 int flags = FLAG_SYNCAGENT;
1943
1944 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1945 {
1946     WNDCLASS wndclass;
1947     MSG msg;
1948     HMODULE advapi;
1949     char *command = NULL;
1950     int added_keys = 0;
1951     int argc, i;
1952     char **argv, **argstart;
1953
1954     hinst = inst;
1955     hwnd = NULL;
1956
1957     /*
1958      * Determine whether we're an NT system (should have security
1959      * APIs) or a non-NT system (don't do security).
1960      */
1961     if (!init_winver())
1962     {
1963         modalfatalbox("Windows refuses to report a version");
1964     }
1965     if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1966         has_security = TRUE;
1967     } else
1968         has_security = FALSE;
1969
1970     if (has_security) {
1971 #ifndef NO_SECURITY
1972         /*
1973          * Attempt to get the security API we need.
1974          */
1975         advapi = load_system32_dll("advapi32.dll");
1976         GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo);
1977         if (!p_GetSecurityInfo) {
1978             MessageBox(NULL,
1979                        "Unable to access security APIs. Pageant will\n"
1980                        "not run, in case it causes a security breach.",
1981                        "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1982             return 1;
1983         }
1984 #else
1985         MessageBox(NULL,
1986                    "This program has been compiled for Win9X and will\n"
1987                    "not run on NT, in case it causes a security breach.",
1988                    "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1989         return 1;
1990 #endif
1991     } else
1992         advapi = NULL;
1993
1994     /*
1995      * See if we can find our Help file.
1996      */
1997     init_help();
1998
1999     /*
2000      * Look for the PuTTY binary (we will enable the saved session
2001      * submenu if we find it).
2002      */
2003     {
2004         char b[2048], *p, *q, *r;
2005         FILE *fp;
2006         GetModuleFileName(NULL, b, sizeof(b) - 16);
2007         r = b;
2008         p = strrchr(b, '\\');
2009         if (p && p >= r) r = p+1;
2010         q = strrchr(b, ':');
2011         if (q && q >= r) r = q+1;
2012         strcpy(r, "putty.exe");
2013         if ( (fp = fopen(b, "r")) != NULL) {
2014             putty_path = dupstr(b);
2015             fclose(fp);
2016         } else
2017             putty_path = NULL;
2018     }
2019
2020     /*
2021      * Find out if Pageant is already running.
2022      */
2023     already_running = agent_exists();
2024
2025     /*
2026      * Initialise storage for RSA keys.
2027      */
2028     if (!already_running) {
2029         rsakeys = newtree234(cmpkeys_rsa);
2030         ssh2keys = newtree234(cmpkeys_ssh2);
2031     }
2032
2033     /*
2034      * Initialise storage for short-term passphrase cache.
2035      */
2036     passphrases = newtree234(NULL);
2037
2038     /*
2039      * Process the command line and add keys as listed on it.
2040      */
2041     split_into_argv(cmdline, &argc, &argv, &argstart);
2042     for (i = 0; i < argc; i++) {
2043         if (!strcmp(argv[i], "-pgpfp")) {
2044             pgp_fingerprints();
2045             if (advapi)
2046                 FreeLibrary(advapi);
2047             return 1;
2048         } else if (!strcmp(argv[i], "-c")) {
2049             /*
2050              * If we see `-c', then the rest of the
2051              * command line should be treated as a
2052              * command to be spawned.
2053              */
2054             if (i < argc-1)
2055                 command = argstart[i+1];
2056             else
2057                 command = "";
2058             break;
2059         } else {
2060             add_keyfile(filename_from_str(argv[i]));
2061             added_keys = TRUE;
2062         }
2063     }
2064
2065     /*
2066      * Forget any passphrase that we retained while going over
2067      * command line keyfiles.
2068      */
2069     forget_passphrases();
2070
2071     if (command) {
2072         char *args;
2073         if (command[0] == '"')
2074             args = strchr(++command, '"');
2075         else
2076             args = strchr(command, ' ');
2077         if (args) {
2078             *args++ = 0;
2079             while(*args && isspace(*args)) args++;
2080         }
2081         spawn_cmd(command, args, show);
2082     }
2083
2084     /*
2085      * If Pageant was already running, we leave now. If we haven't
2086      * even taken any auxiliary action (spawned a command or added
2087      * keys), complain.
2088      */
2089     if (already_running) {
2090         if (!command && !added_keys) {
2091             MessageBox(NULL, "Pageant is already running", "Pageant Error",
2092                        MB_ICONERROR | MB_OK);
2093         }
2094         if (advapi)
2095             FreeLibrary(advapi);
2096         return 0;
2097     }
2098
2099     if (!prev) {
2100         wndclass.style = 0;
2101         wndclass.lpfnWndProc = WndProc;
2102         wndclass.cbClsExtra = 0;
2103         wndclass.cbWndExtra = 0;
2104         wndclass.hInstance = inst;
2105         wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
2106         wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
2107         wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
2108         wndclass.lpszMenuName = NULL;
2109         wndclass.lpszClassName = APPNAME;
2110
2111         RegisterClass(&wndclass);
2112     }
2113
2114     keylist = NULL;
2115
2116     hwnd = CreateWindow(APPNAME, APPNAME,
2117                         WS_OVERLAPPEDWINDOW | WS_VSCROLL,
2118                         CW_USEDEFAULT, CW_USEDEFAULT,
2119                         100, 100, NULL, NULL, inst, NULL);
2120
2121     /* Set up a system tray icon */
2122     AddTrayIcon(hwnd);
2123
2124     /* Accelerators used: nsvkxa */
2125     systray_menu = CreatePopupMenu();
2126     if (putty_path) {
2127         session_menu = CreateMenu();
2128         AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
2129         AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
2130                    (UINT) session_menu, "&Saved Sessions");
2131         AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2132     }
2133     AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
2134            "&View Keys");
2135     AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
2136     AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2137     if (has_help())
2138         AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
2139     AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
2140     AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2141     AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
2142     initial_menuitems_count = GetMenuItemCount(session_menu);
2143
2144     /* Set the default menu item. */
2145     SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
2146
2147     ShowWindow(hwnd, SW_HIDE);
2148
2149     /*
2150      * Main message loop.
2151      */
2152     while (GetMessage(&msg, NULL, 0, 0) == 1) {
2153         if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2154             !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2155             TranslateMessage(&msg);
2156             DispatchMessage(&msg);
2157         }
2158     }
2159
2160     /* Clean up the system tray icon */
2161     {
2162         NOTIFYICONDATA tnid;
2163
2164         tnid.cbSize = sizeof(NOTIFYICONDATA);
2165         tnid.hWnd = hwnd;
2166         tnid.uID = 1;
2167
2168         Shell_NotifyIcon(NIM_DELETE, &tnid);
2169
2170         DestroyMenu(systray_menu);
2171     }
2172
2173     if (keypath) filereq_free(keypath);
2174
2175     if (advapi)
2176         FreeLibrary(advapi);
2177
2178     cleanup_exit(msg.wParam);
2179     return msg.wParam;                 /* just in case optimiser complains */
2180 }