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