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