]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winpgnt.c
Move the dynamic loading of advapi into its own module.
[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 #ifndef NO_SECURITY
121 DECL_WINDOWS_FUNCTION(extern, DWORD, GetSecurityInfo,
122                       (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
123                        PSID *, PSID *, PACL *, PACL *,
124                        PSECURITY_DESCRIPTOR *));
125 #endif
126
127 /*
128  * Forward references
129  */
130 static void *make_keylist1(int *length);
131 static void *make_keylist2(int *length);
132 static void *get_keylist1(int *length);
133 static void *get_keylist2(int *length);
134
135 /*
136  * We need this to link with the RSA code, because rsaencrypt()
137  * pads its data with random bytes. Since we only use rsadecrypt()
138  * and the signing functions, which are deterministic, this should
139  * never be called.
140  *
141  * If it _is_ called, there is a _serious_ problem, because it
142  * won't generate true random numbers. So we must scream, panic,
143  * and exit immediately if that should happen.
144  */
145 int random_byte(void)
146 {
147     MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
148     exit(0);
149     /* this line can't be reached but it placates MSVC's warnings :-) */
150     return 0;
151 }
152
153 /*
154  * Blob structure for passing to the asymmetric SSH-2 key compare
155  * function, prototyped here.
156  */
157 struct blob {
158     unsigned char *blob;
159     int len;
160 };
161 static int cmpkeys_ssh2_asymm(void *av, void *bv);
162
163 struct PassphraseProcStruct {
164     char **passphrase;
165     char *comment;
166 };
167
168 static tree234 *passphrases = NULL;
169
170 /* 
171  * After processing a list of filenames, we want to forget the
172  * passphrases.
173  */
174 static void forget_passphrases(void)
175 {
176     while (count234(passphrases) > 0) {
177         char *pp = index234(passphrases, 0);
178         smemclr(pp, strlen(pp));
179         delpos234(passphrases, 0);
180         free(pp);
181     }
182 }
183
184 /*
185  * Dialog-box function for the Licence box.
186  */
187 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
188                                 WPARAM wParam, LPARAM lParam)
189 {
190     switch (msg) {
191       case WM_INITDIALOG:
192         return 1;
193       case WM_COMMAND:
194         switch (LOWORD(wParam)) {
195           case IDOK:
196           case IDCANCEL:
197             EndDialog(hwnd, 1);
198             return 0;
199         }
200         return 0;
201       case WM_CLOSE:
202         EndDialog(hwnd, 1);
203         return 0;
204     }
205     return 0;
206 }
207
208 /*
209  * Dialog-box function for the About box.
210  */
211 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
212                               WPARAM wParam, LPARAM lParam)
213 {
214     switch (msg) {
215       case WM_INITDIALOG:
216         SetDlgItemText(hwnd, 100, ver);
217         return 1;
218       case WM_COMMAND:
219         switch (LOWORD(wParam)) {
220           case IDOK:
221           case IDCANCEL:
222             aboutbox = NULL;
223             DestroyWindow(hwnd);
224             return 0;
225           case 101:
226             EnableWindow(hwnd, 0);
227             DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
228             EnableWindow(hwnd, 1);
229             SetActiveWindow(hwnd);
230             return 0;
231         }
232         return 0;
233       case WM_CLOSE:
234         aboutbox = NULL;
235         DestroyWindow(hwnd);
236         return 0;
237     }
238     return 0;
239 }
240
241 static HWND passphrase_box;
242
243 /*
244  * Dialog-box function for the passphrase box.
245  */
246 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
247                                    WPARAM wParam, LPARAM lParam)
248 {
249     static char **passphrase = NULL;
250     struct PassphraseProcStruct *p;
251
252     switch (msg) {
253       case WM_INITDIALOG:
254         passphrase_box = hwnd;
255         /*
256          * Centre the window.
257          */
258         {                              /* centre the window */
259             RECT rs, rd;
260             HWND hw;
261
262             hw = GetDesktopWindow();
263             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
264                 MoveWindow(hwnd,
265                            (rs.right + rs.left + rd.left - rd.right) / 2,
266                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
267                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
268         }
269
270         SetForegroundWindow(hwnd);
271         SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
272                      SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
273         p = (struct PassphraseProcStruct *) lParam;
274         passphrase = p->passphrase;
275         if (p->comment)
276             SetDlgItemText(hwnd, 101, p->comment);
277         burnstr(*passphrase);
278         *passphrase = dupstr("");
279         SetDlgItemText(hwnd, 102, *passphrase);
280         return 0;
281       case WM_COMMAND:
282         switch (LOWORD(wParam)) {
283           case IDOK:
284             if (*passphrase)
285                 EndDialog(hwnd, 1);
286             else
287                 MessageBeep(0);
288             return 0;
289           case IDCANCEL:
290             EndDialog(hwnd, 0);
291             return 0;
292           case 102:                    /* edit box */
293             if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
294                 burnstr(*passphrase);
295                 *passphrase = GetDlgItemText_alloc(hwnd, 102);
296             }
297             return 0;
298         }
299         return 0;
300       case WM_CLOSE:
301         EndDialog(hwnd, 0);
302         return 0;
303     }
304     return 0;
305 }
306
307 /*
308  * Warn about the obsolescent key file format.
309  */
310 void old_keyfile_warning(void)
311 {
312     static const char mbtitle[] = "PuTTY Key File Warning";
313     static const char message[] =
314         "You are loading an SSH-2 private key which has an\n"
315         "old version of the file format. This means your key\n"
316         "file is not fully tamperproof. Future versions of\n"
317         "PuTTY may stop supporting this private key format,\n"
318         "so we recommend you convert your key to the new\n"
319         "format.\n"
320         "\n"
321         "You can perform this conversion by loading the key\n"
322         "into PuTTYgen and then saving it again.";
323
324     MessageBox(NULL, message, mbtitle, MB_OK);
325 }
326
327 /*
328  * Update the visible key list.
329  */
330 static void keylist_update(void)
331 {
332     struct RSAKey *rkey;
333     struct ssh2_userkey *skey;
334     int i;
335
336     if (keylist) {
337         SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
338         for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
339             char listentry[512], *p;
340             /*
341              * Replace two spaces in the fingerprint with tabs, for
342              * nice alignment in the box.
343              */
344             strcpy(listentry, "ssh1\t");
345             p = listentry + strlen(listentry);
346             rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
347             p = strchr(listentry, ' ');
348             if (p)
349                 *p = '\t';
350             p = strchr(listentry, ' ');
351             if (p)
352                 *p = '\t';
353             SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
354                                0, (LPARAM) listentry);
355         }
356         for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
357             char *listentry, *p;
358             int fp_len;
359             /*
360              * Replace two spaces in the fingerprint with tabs, for
361              * nice alignment in the box.
362              */
363             p = skey->alg->fingerprint(skey->data);
364             listentry = dupprintf("%s\t%s", p, skey->comment);
365             fp_len = strlen(listentry);
366             sfree(p);
367
368             p = strchr(listentry, ' ');
369             if (p && p < listentry + fp_len)
370                 *p = '\t';
371             p = strchr(listentry, ' ');
372             if (p && p < listentry + fp_len)
373                 *p = '\t';
374
375             SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
376                                (LPARAM) listentry);
377             sfree(listentry);
378         }
379         SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
380     }
381 }
382
383 /*
384  * This function loads a key from a file and adds it.
385  */
386 static void add_keyfile(Filename *filename)
387 {
388     char *passphrase;
389     struct RSAKey *rkey = NULL;
390     struct ssh2_userkey *skey = NULL;
391     int needs_pass;
392     int ret;
393     int attempts;
394     char *comment;
395     const char *error = NULL;
396     int type;
397     int original_pass;
398         
399     type = key_type(filename);
400     if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
401         char *msg = dupprintf("Couldn't load this key (%s)",
402                               key_type_to_str(type));
403         message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
404                     HELPCTXID(errors_cantloadkey));
405         sfree(msg);
406         return;
407     }
408
409     /*
410      * See if the key is already loaded (in the primary Pageant,
411      * which may or may not be us).
412      */
413     {
414         void *blob;
415         unsigned char *keylist, *p;
416         int i, nkeys, bloblen, keylistlen;
417
418         if (type == SSH_KEYTYPE_SSH1) {
419             if (!rsakey_pubblob(filename, &blob, &bloblen, NULL, &error)) {
420                 char *msg = dupprintf("Couldn't load private key (%s)", error);
421                 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
422                             HELPCTXID(errors_cantloadkey));
423                 sfree(msg);
424                 return;
425             }
426             keylist = get_keylist1(&keylistlen);
427         } else {
428             unsigned char *blob2;
429             blob = ssh2_userkey_loadpub(filename, NULL, &bloblen,
430                                         NULL, &error);
431             if (!blob) {
432                 char *msg = dupprintf("Couldn't load private key (%s)", error);
433                 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
434                             HELPCTXID(errors_cantloadkey));
435                 sfree(msg);
436                 return;
437             }
438             /* For our purposes we want the blob prefixed with its length */
439             blob2 = snewn(bloblen+4, unsigned char);
440             PUT_32BIT(blob2, bloblen);
441             memcpy(blob2 + 4, blob, bloblen);
442             sfree(blob);
443             blob = blob2;
444
445             keylist = get_keylist2(&keylistlen);
446         }
447         if (keylist) {
448             if (keylistlen < 4) {
449                 MessageBox(NULL, "Received broken key list?!", APPNAME,
450                            MB_OK | MB_ICONERROR);
451                 return;
452             }
453             nkeys = toint(GET_32BIT(keylist));
454             if (nkeys < 0) {
455                 MessageBox(NULL, "Received broken key list?!", APPNAME,
456                            MB_OK | MB_ICONERROR);
457                 return;
458             }
459             p = keylist + 4;
460             keylistlen -= 4;
461
462             for (i = 0; i < nkeys; i++) {
463                 if (!memcmp(blob, p, bloblen)) {
464                     /* Key is already present; we can now leave. */
465                     sfree(keylist);
466                     sfree(blob);
467                     return;
468                 }
469                 /* Now skip over public blob */
470                 if (type == SSH_KEYTYPE_SSH1) {
471                     int n = rsa_public_blob_len(p, keylistlen);
472                     if (n < 0) {
473                         MessageBox(NULL, "Received broken key list?!", APPNAME,
474                                    MB_OK | MB_ICONERROR);
475                         return;
476                     }
477                     p += n;
478                     keylistlen -= n;
479                 } else {
480                     int n;
481                     if (keylistlen < 4) {
482                         MessageBox(NULL, "Received broken key list?!", APPNAME,
483                                    MB_OK | MB_ICONERROR);
484                         return;
485                     }
486                     n = toint(4 + GET_32BIT(p));
487                     if (n < 0 || keylistlen < n) {
488                         MessageBox(NULL, "Received broken key list?!", APPNAME,
489                                    MB_OK | MB_ICONERROR);
490                         return;
491                     }
492                     p += n;
493                     keylistlen -= n;
494                 }
495                 /* Now skip over comment field */
496                 {
497                     int n;
498                     if (keylistlen < 4) {
499                         MessageBox(NULL, "Received broken key list?!", APPNAME,
500                                    MB_OK | MB_ICONERROR);
501                         return;
502                     }
503                     n = toint(4 + GET_32BIT(p));
504                     if (n < 0 || keylistlen < n) {
505                         MessageBox(NULL, "Received broken key list?!", APPNAME,
506                                    MB_OK | MB_ICONERROR);
507                         return;
508                     }
509                     p += n;
510                     keylistlen -= n;
511                 }
512             }
513
514             sfree(keylist);
515         }
516
517         sfree(blob);
518     }
519
520     error = NULL;
521     if (type == SSH_KEYTYPE_SSH1)
522         needs_pass = rsakey_encrypted(filename, &comment);
523     else
524         needs_pass = ssh2_userkey_encrypted(filename, &comment);
525     attempts = 0;
526     if (type == SSH_KEYTYPE_SSH1)
527         rkey = snew(struct RSAKey);
528     passphrase = NULL;
529     original_pass = 0;
530     do {
531         burnstr(passphrase);
532         passphrase = NULL;
533
534         if (needs_pass) {
535             /* try all the remembered passphrases first */
536             char *pp = index234(passphrases, attempts);
537             if(pp) {
538                 passphrase = dupstr(pp);
539             } else {
540                 int dlgret;
541                 struct PassphraseProcStruct pps;
542
543                 pps.passphrase = &passphrase;
544                 pps.comment = comment;
545
546                 original_pass = 1;
547                 dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
548                                         NULL, PassphraseProc, (LPARAM) &pps);
549                 passphrase_box = NULL;
550                 if (!dlgret) {
551                     if (comment)
552                         sfree(comment);
553                     if (type == SSH_KEYTYPE_SSH1)
554                         sfree(rkey);
555                     return;                    /* operation cancelled */
556                 }
557
558                 assert(passphrase != NULL);
559             }
560         } else
561             passphrase = dupstr("");
562
563         if (type == SSH_KEYTYPE_SSH1)
564             ret = loadrsakey(filename, rkey, passphrase, &error);
565         else {
566             skey = ssh2_load_userkey(filename, passphrase, &error);
567             if (skey == SSH2_WRONG_PASSPHRASE)
568                 ret = -1;
569             else if (!skey)
570                 ret = 0;
571             else
572                 ret = 1;
573         }
574         attempts++;
575     } while (ret == -1);
576
577     if(original_pass && ret) {
578         /* If they typed in an ok passphrase, remember it */
579         addpos234(passphrases, passphrase, 0);
580     } else {
581         /* Otherwise, destroy it */
582         burnstr(passphrase);
583     }
584     passphrase = NULL;
585
586     if (comment)
587         sfree(comment);
588     if (ret == 0) {
589         char *msg = dupprintf("Couldn't load private key (%s)", error);
590         message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
591                     HELPCTXID(errors_cantloadkey));
592         sfree(msg);
593         if (type == SSH_KEYTYPE_SSH1)
594             sfree(rkey);
595         return;
596     }
597     if (type == SSH_KEYTYPE_SSH1) {
598         if (already_running) {
599             unsigned char *request, *response;
600             void *vresponse;
601             int reqlen, clen, resplen, ret;
602
603             clen = strlen(rkey->comment);
604
605             reqlen = 4 + 1 +           /* length, message type */
606                 4 +                    /* bit count */
607                 ssh1_bignum_length(rkey->modulus) +
608                 ssh1_bignum_length(rkey->exponent) +
609                 ssh1_bignum_length(rkey->private_exponent) +
610                 ssh1_bignum_length(rkey->iqmp) +
611                 ssh1_bignum_length(rkey->p) +
612                 ssh1_bignum_length(rkey->q) + 4 + clen  /* comment */
613                 ;
614
615             request = snewn(reqlen, unsigned char);
616
617             request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
618             reqlen = 5;
619             PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
620             reqlen += 4;
621             reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
622             reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
623             reqlen +=
624                 ssh1_write_bignum(request + reqlen,
625                                   rkey->private_exponent);
626             reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
627             reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
628             reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
629             PUT_32BIT(request + reqlen, clen);
630             memcpy(request + reqlen + 4, rkey->comment, clen);
631             reqlen += 4 + clen;
632             PUT_32BIT(request, reqlen - 4);
633
634             ret = agent_query(request, reqlen, &vresponse, &resplen,
635                               NULL, NULL);
636             assert(ret == 1);
637             response = vresponse;
638             if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
639                 MessageBox(NULL, "The already running Pageant "
640                            "refused to add the key.", APPNAME,
641                            MB_OK | MB_ICONERROR);
642
643             sfree(request);
644             sfree(response);
645         } else {
646             if (add234(rsakeys, rkey) != rkey)
647                 sfree(rkey);           /* already present, don't waste RAM */
648         }
649     } else {
650         if (already_running) {
651             unsigned char *request, *response;
652             void *vresponse;
653             int reqlen, alglen, clen, keybloblen, resplen, ret;
654             alglen = strlen(skey->alg->name);
655             clen = strlen(skey->comment);
656
657             keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
658
659             reqlen = 4 + 1 +           /* length, message type */
660                 4 + alglen +           /* algorithm name */
661                 keybloblen +           /* key data */
662                 4 + clen               /* comment */
663                 ;
664
665             request = snewn(reqlen, unsigned char);
666
667             request[4] = SSH2_AGENTC_ADD_IDENTITY;
668             reqlen = 5;
669             PUT_32BIT(request + reqlen, alglen);
670             reqlen += 4;
671             memcpy(request + reqlen, skey->alg->name, alglen);
672             reqlen += alglen;
673             reqlen += skey->alg->openssh_fmtkey(skey->data,
674                                                 request + reqlen,
675                                                 keybloblen);
676             PUT_32BIT(request + reqlen, clen);
677             memcpy(request + reqlen + 4, skey->comment, clen);
678             reqlen += clen + 4;
679             PUT_32BIT(request, reqlen - 4);
680
681             ret = agent_query(request, reqlen, &vresponse, &resplen,
682                               NULL, NULL);
683             assert(ret == 1);
684             response = vresponse;
685             if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
686                 MessageBox(NULL, "The already running Pageant "
687                            "refused to add the key.", APPNAME,
688                            MB_OK | MB_ICONERROR);
689
690             sfree(request);
691             sfree(response);
692         } else {
693             if (add234(ssh2keys, skey) != skey) {
694                 skey->alg->freekey(skey->data);
695                 sfree(skey);           /* already present, don't waste RAM */
696             }
697         }
698     }
699 }
700
701 /*
702  * Create an SSH-1 key list in a malloc'ed buffer; return its
703  * length.
704  */
705 static void *make_keylist1(int *length)
706 {
707     int i, nkeys, len;
708     struct RSAKey *key;
709     unsigned char *blob, *p, *ret;
710     int bloblen;
711
712     /*
713      * Count up the number and length of keys we hold.
714      */
715     len = 4;
716     nkeys = 0;
717     for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
718         nkeys++;
719         blob = rsa_public_blob(key, &bloblen);
720         len += bloblen;
721         sfree(blob);
722         len += 4 + strlen(key->comment);
723     }
724
725     /* Allocate the buffer. */
726     p = ret = snewn(len, unsigned char);
727     if (length) *length = len;
728
729     PUT_32BIT(p, nkeys);
730     p += 4;
731     for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
732         blob = rsa_public_blob(key, &bloblen);
733         memcpy(p, blob, bloblen);
734         p += bloblen;
735         sfree(blob);
736         PUT_32BIT(p, strlen(key->comment));
737         memcpy(p + 4, key->comment, strlen(key->comment));
738         p += 4 + strlen(key->comment);
739     }
740
741     assert(p - ret == len);
742     return ret;
743 }
744
745 /*
746  * Create an SSH-2 key list in a malloc'ed buffer; return its
747  * length.
748  */
749 static void *make_keylist2(int *length)
750 {
751     struct ssh2_userkey *key;
752     int i, len, nkeys;
753     unsigned char *blob, *p, *ret;
754     int bloblen;
755
756     /*
757      * Count up the number and length of keys we hold.
758      */
759     len = 4;
760     nkeys = 0;
761     for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
762         nkeys++;
763         len += 4;              /* length field */
764         blob = key->alg->public_blob(key->data, &bloblen);
765         len += bloblen;
766         sfree(blob);
767         len += 4 + strlen(key->comment);
768     }
769
770     /* Allocate the buffer. */
771     p = ret = snewn(len, unsigned char);
772     if (length) *length = len;
773
774     /*
775      * Packet header is the obvious five bytes, plus four
776      * bytes for the key count.
777      */
778     PUT_32BIT(p, nkeys);
779     p += 4;
780     for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
781         blob = key->alg->public_blob(key->data, &bloblen);
782         PUT_32BIT(p, bloblen);
783         p += 4;
784         memcpy(p, blob, bloblen);
785         p += bloblen;
786         sfree(blob);
787         PUT_32BIT(p, strlen(key->comment));
788         memcpy(p + 4, key->comment, strlen(key->comment));
789         p += 4 + strlen(key->comment);
790     }
791
792     assert(p - ret == len);
793     return ret;
794 }
795
796 /*
797  * Acquire a keylist1 from the primary Pageant; this means either
798  * calling make_keylist1 (if that's us) or sending a message to the
799  * primary Pageant (if it's not).
800  */
801 static void *get_keylist1(int *length)
802 {
803     void *ret;
804
805     if (already_running) {
806         unsigned char request[5], *response;
807         void *vresponse;
808         int resplen, retval;
809         request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
810         PUT_32BIT(request, 4);
811
812         retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
813         assert(retval == 1);
814         response = vresponse;
815         if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
816             sfree(response);
817             return NULL;
818         }
819
820         ret = snewn(resplen-5, unsigned char);
821         memcpy(ret, response+5, resplen-5);
822         sfree(response);
823
824         if (length)
825             *length = resplen-5;
826     } else {
827         ret = make_keylist1(length);
828     }
829     return ret;
830 }
831
832 /*
833  * Acquire a keylist2 from the primary Pageant; this means either
834  * calling make_keylist2 (if that's us) or sending a message to the
835  * primary Pageant (if it's not).
836  */
837 static void *get_keylist2(int *length)
838 {
839     void *ret;
840
841     if (already_running) {
842         unsigned char request[5], *response;
843         void *vresponse;
844         int resplen, retval;
845
846         request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
847         PUT_32BIT(request, 4);
848
849         retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
850         assert(retval == 1);
851         response = vresponse;
852         if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) {
853             sfree(response);
854             return NULL;
855         }
856
857         ret = snewn(resplen-5, unsigned char);
858         memcpy(ret, response+5, resplen-5);
859         sfree(response);
860
861         if (length)
862             *length = resplen-5;
863     } else {
864         ret = make_keylist2(length);
865     }
866     return ret;
867 }
868
869 /*
870  * This is the main agent function that answers messages.
871  */
872 static void answer_msg(void *msg)
873 {
874     unsigned char *p = msg;
875     unsigned char *ret = msg;
876     unsigned char *msgend;
877     int type;
878
879     /*
880      * Get the message length.
881      */
882     msgend = p + 4 + GET_32BIT(p);
883
884     /*
885      * Get the message type.
886      */
887     if (msgend < p+5)
888         goto failure;
889     type = p[4];
890
891     p += 5;
892     switch (type) {
893       case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
894         /*
895          * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
896          */
897         {
898             int len;
899             void *keylist;
900
901             ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
902             keylist = make_keylist1(&len);
903             if (len + 5 > AGENT_MAX_MSGLEN) {
904                 sfree(keylist);
905                 goto failure;
906             }
907             PUT_32BIT(ret, len + 1);
908             memcpy(ret + 5, keylist, len);
909             sfree(keylist);
910         }
911         break;
912       case SSH2_AGENTC_REQUEST_IDENTITIES:
913         /*
914          * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
915          */
916         {
917             int len;
918             void *keylist;
919
920             ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
921             keylist = make_keylist2(&len);
922             if (len + 5 > AGENT_MAX_MSGLEN) {
923                 sfree(keylist);
924                 goto failure;
925             }
926             PUT_32BIT(ret, len + 1);
927             memcpy(ret + 5, keylist, len);
928             sfree(keylist);
929         }
930         break;
931       case SSH1_AGENTC_RSA_CHALLENGE:
932         /*
933          * Reply with either SSH1_AGENT_RSA_RESPONSE or
934          * SSH_AGENT_FAILURE, depending on whether we have that key
935          * or not.
936          */
937         {
938             struct RSAKey reqkey, *key;
939             Bignum challenge, response;
940             unsigned char response_source[48], response_md5[16];
941             struct MD5Context md5c;
942             int i, len;
943
944             p += 4;
945             i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
946             if (i < 0)
947                 goto failure;
948             p += i;
949             i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
950             if (i < 0) {
951                 freebn(reqkey.exponent);
952                 goto failure;
953             }
954             p += i;
955             i = ssh1_read_bignum(p, msgend - p, &challenge);
956             if (i < 0) {
957                 freebn(reqkey.exponent);
958                 freebn(reqkey.modulus);
959                 goto failure;
960             }
961             p += i;
962             if (msgend < p+16) {
963                 freebn(reqkey.exponent);
964                 freebn(reqkey.modulus);
965                 freebn(challenge);
966                 goto failure;
967             }
968             memcpy(response_source + 32, p, 16);
969             p += 16;
970             if (msgend < p+4 ||
971                 GET_32BIT(p) != 1 ||
972                 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
973                 freebn(reqkey.exponent);
974                 freebn(reqkey.modulus);
975                 freebn(challenge);
976                 goto failure;
977             }
978             response = rsadecrypt(challenge, key);
979             for (i = 0; i < 32; i++)
980                 response_source[i] = bignum_byte(response, 31 - i);
981
982             MD5Init(&md5c);
983             MD5Update(&md5c, response_source, 48);
984             MD5Final(response_md5, &md5c);
985             smemclr(response_source, 48);       /* burn the evidence */
986             freebn(response);          /* and that evidence */
987             freebn(challenge);         /* yes, and that evidence */
988             freebn(reqkey.exponent);   /* and free some memory ... */
989             freebn(reqkey.modulus);    /* ... while we're at it. */
990
991             /*
992              * Packet is the obvious five byte header, plus sixteen
993              * bytes of MD5.
994              */
995             len = 5 + 16;
996             PUT_32BIT(ret, len - 4);
997             ret[4] = SSH1_AGENT_RSA_RESPONSE;
998             memcpy(ret + 5, response_md5, 16);
999         }
1000         break;
1001       case SSH2_AGENTC_SIGN_REQUEST:
1002         /*
1003          * Reply with either SSH2_AGENT_SIGN_RESPONSE or
1004          * SSH_AGENT_FAILURE, depending on whether we have that key
1005          * or not.
1006          */
1007         {
1008             struct ssh2_userkey *key;
1009             struct blob b;
1010             unsigned char *data, *signature;
1011             int datalen, siglen, len;
1012
1013             if (msgend < p+4)
1014                 goto failure;
1015             b.len = toint(GET_32BIT(p));
1016             if (b.len < 0 || b.len > msgend - (p+4))
1017                 goto failure;
1018             p += 4;
1019             b.blob = p;
1020             p += b.len;
1021             if (msgend < p+4)
1022                 goto failure;
1023             datalen = toint(GET_32BIT(p));
1024             p += 4;
1025             if (datalen < 0 || datalen > msgend - p)
1026                 goto failure;
1027             data = p;
1028             key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1029             if (!key)
1030                 goto failure;
1031             signature = key->alg->sign(key->data, data, datalen, &siglen);
1032             len = 5 + 4 + siglen;
1033             PUT_32BIT(ret, len - 4);
1034             ret[4] = SSH2_AGENT_SIGN_RESPONSE;
1035             PUT_32BIT(ret + 5, siglen);
1036             memcpy(ret + 5 + 4, signature, siglen);
1037             sfree(signature);
1038         }
1039         break;
1040       case SSH1_AGENTC_ADD_RSA_IDENTITY:
1041         /*
1042          * Add to the list and return SSH_AGENT_SUCCESS, or
1043          * SSH_AGENT_FAILURE if the key was malformed.
1044          */
1045         {
1046             struct RSAKey *key;
1047             char *comment;
1048             int n, commentlen;
1049
1050             key = snew(struct RSAKey);
1051             memset(key, 0, sizeof(struct RSAKey));
1052
1053             n = makekey(p, msgend - p, key, NULL, 1);
1054             if (n < 0) {
1055                 freersakey(key);
1056                 sfree(key);
1057                 goto failure;
1058             }
1059             p += n;
1060
1061             n = makeprivate(p, msgend - p, key);
1062             if (n < 0) {
1063                 freersakey(key);
1064                 sfree(key);
1065                 goto failure;
1066             }
1067             p += n;
1068
1069             n = ssh1_read_bignum(p, msgend - p, &key->iqmp);  /* p^-1 mod q */
1070             if (n < 0) {
1071                 freersakey(key);
1072                 sfree(key);
1073                 goto failure;
1074             }
1075             p += n;
1076
1077             n = ssh1_read_bignum(p, msgend - p, &key->p);  /* p */
1078             if (n < 0) {
1079                 freersakey(key);
1080                 sfree(key);
1081                 goto failure;
1082             }
1083             p += n;
1084
1085             n = ssh1_read_bignum(p, msgend - p, &key->q);  /* q */
1086             if (n < 0) {
1087                 freersakey(key);
1088                 sfree(key);
1089                 goto failure;
1090             }
1091             p += n;
1092
1093             if (msgend < p+4) {
1094                 freersakey(key);
1095                 sfree(key);
1096                 goto failure;
1097             }
1098             commentlen = toint(GET_32BIT(p));
1099
1100             if (commentlen < 0 || commentlen > msgend - p) {
1101                 freersakey(key);
1102                 sfree(key);
1103                 goto failure;
1104             }
1105
1106             comment = snewn(commentlen+1, char);
1107             if (comment) {
1108                 memcpy(comment, p + 4, commentlen);
1109                 comment[commentlen] = '\0';
1110                 key->comment = comment;
1111             }
1112             PUT_32BIT(ret, 1);
1113             ret[4] = SSH_AGENT_FAILURE;
1114             if (add234(rsakeys, key) == key) {
1115                 keylist_update();
1116                 ret[4] = SSH_AGENT_SUCCESS;
1117             } else {
1118                 freersakey(key);
1119                 sfree(key);
1120             }
1121         }
1122         break;
1123       case SSH2_AGENTC_ADD_IDENTITY:
1124         /*
1125          * Add to the list and return SSH_AGENT_SUCCESS, or
1126          * SSH_AGENT_FAILURE if the key was malformed.
1127          */
1128         {
1129             struct ssh2_userkey *key;
1130             char *comment, *alg;
1131             int alglen, commlen;
1132             int bloblen;
1133
1134
1135             if (msgend < p+4)
1136                 goto failure;
1137             alglen = toint(GET_32BIT(p));
1138             p += 4;
1139             if (alglen < 0 || alglen > msgend - p)
1140                 goto failure;
1141             alg = p;
1142             p += alglen;
1143
1144             key = snew(struct ssh2_userkey);
1145             /* Add further algorithm names here. */
1146             if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
1147                 key->alg = &ssh_rsa;
1148             else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
1149                 key->alg = &ssh_dss;
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 }