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