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