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