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