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