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