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