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