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