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