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