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