]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - pageant.c
Add a missing space in an error message.
[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                  * Sometimes the window comes up minimised / hidden
1601                  * for no obvious reason. Prevent this.
1602                  */
1603                 SetForegroundWindow(keylist);
1604                 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1605                              SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1606             }
1607             break;
1608           case IDM_ADDKEY:
1609             if (passphrase_box) {
1610                 MessageBeep(MB_ICONERROR);
1611                 SetForegroundWindow(passphrase_box);
1612                 break;
1613             }
1614             prompt_add_keyfile();
1615             break;
1616           case IDM_ABOUT:
1617             if (!aboutbox) {
1618                 aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
1619                                         NULL, AboutProc);
1620                 ShowWindow(aboutbox, SW_SHOWNORMAL);
1621                 /* 
1622                  * Sometimes the window comes up minimised / hidden
1623                  * for no obvious reason. Prevent this.
1624                  */
1625                 SetForegroundWindow(aboutbox);
1626                 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1627                              SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1628             }
1629             break;
1630           case IDM_HELP:
1631             if (help_path) {
1632                 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1633                         (DWORD)"JI(`',`pageant.general')");
1634                 requested_help = TRUE;
1635             }
1636             break;
1637           default:
1638             {
1639                 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1640                     MENUITEMINFO mii;
1641                     TCHAR buf[MAX_PATH + 1];
1642                     TCHAR param[MAX_PATH + 1];
1643                     memset(&mii, 0, sizeof(mii));
1644                     mii.cbSize = sizeof(mii);
1645                     mii.fMask = MIIM_TYPE;
1646                     mii.cch = MAX_PATH;
1647                     mii.dwTypeData = buf;
1648                     GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1649                     strcpy(param, "@");
1650                     strcat(param, mii.dwTypeData);
1651                     if((int)ShellExecute(hwnd, NULL, putty_path, param,
1652                                          _T(""), SW_SHOW) <= 32) {
1653                         MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1654                                    MB_OK | MB_ICONERROR);
1655                     }
1656                 }
1657             }
1658             break;
1659         }
1660         break;
1661       case WM_DESTROY:
1662         if (requested_help) {
1663             WinHelp(main_hwnd, help_path, HELP_QUIT, 0);
1664             requested_help = FALSE;
1665         }
1666         PostQuitMessage(0);
1667         return 0;
1668       case WM_COPYDATA:
1669         {
1670             COPYDATASTRUCT *cds;
1671             char *mapname;
1672             void *p;
1673             HANDLE filemap;
1674 #ifndef NO_SECURITY
1675             HANDLE proc;
1676             PSID mapowner, procowner;
1677             PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1678 #endif
1679             int ret = 0;
1680
1681             cds = (COPYDATASTRUCT *) lParam;
1682             if (cds->dwData != AGENT_COPYDATA_ID)
1683                 return 0;              /* not our message, mate */
1684             mapname = (char *) cds->lpData;
1685             if (mapname[cds->cbData - 1] != '\0')
1686                 return 0;              /* failure to be ASCIZ! */
1687 #ifdef DEBUG_IPC
1688             debug(("mapname is :%s:\n", mapname));
1689 #endif
1690             filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1691 #ifdef DEBUG_IPC
1692             debug(("filemap is %p\n", filemap));
1693 #endif
1694             if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1695 #ifndef NO_SECURITY
1696                 int rc;
1697                 if (has_security) {
1698                     if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1699                                             GetCurrentProcessId())) ==
1700                         NULL) {
1701 #ifdef DEBUG_IPC
1702                         debug(("couldn't get handle for process\n"));
1703 #endif
1704                         return 0;
1705                     }
1706                     if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1707                                         OWNER_SECURITY_INFORMATION,
1708                                         &procowner, NULL, NULL, NULL,
1709                                         &psd2) != ERROR_SUCCESS) {
1710 #ifdef DEBUG_IPC
1711                         debug(("couldn't get owner info for process\n"));
1712 #endif
1713                         CloseHandle(proc);
1714                         return 0;      /* unable to get security info */
1715                     }
1716                     CloseHandle(proc);
1717                     if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1718                                               OWNER_SECURITY_INFORMATION,
1719                                               &mapowner, NULL, NULL, NULL,
1720                                               &psd1) != ERROR_SUCCESS)) {
1721 #ifdef DEBUG_IPC
1722                         debug(
1723                               ("couldn't get owner info for filemap: %d\n",
1724                                rc));
1725 #endif
1726                         return 0;
1727                     }
1728 #ifdef DEBUG_IPC
1729                     debug(("got security stuff\n"));
1730 #endif
1731                     if (!EqualSid(mapowner, procowner))
1732                         return 0;      /* security ID mismatch! */
1733 #ifdef DEBUG_IPC
1734                     debug(("security stuff matched\n"));
1735 #endif
1736                     LocalFree(psd1);
1737                     LocalFree(psd2);
1738                 } else {
1739 #ifdef DEBUG_IPC
1740                     debug(("security APIs not present\n"));
1741 #endif
1742                 }
1743 #endif
1744                 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1745 #ifdef DEBUG_IPC
1746                 debug(("p is %p\n", p));
1747                 {
1748                     int i;
1749                     for (i = 0; i < 5; i++)
1750                         debug(
1751                               ("p[%d]=%02x\n", i,
1752                                ((unsigned char *) p)[i]));}
1753 #endif
1754                 answer_msg(p);
1755                 ret = 1;
1756                 UnmapViewOfFile(p);
1757             }
1758             CloseHandle(filemap);
1759             return ret;
1760         }
1761     }
1762
1763     return DefWindowProc(hwnd, message, wParam, lParam);
1764 }
1765
1766 /*
1767  * Fork and Exec the command in cmdline. [DBW]
1768  */
1769 void spawn_cmd(char *cmdline, char * args, int show)
1770 {
1771     if (ShellExecute(NULL, _T("open"), cmdline,
1772                      args, NULL, show) <= (HINSTANCE) 32) {
1773         TCHAR sMsg[140];
1774         sprintf(sMsg, _T("Failed to run \"%.100s\", Error: %d"), cmdline,
1775                 (int)GetLastError());
1776         MessageBox(NULL, sMsg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1777     }
1778 }
1779
1780 void cleanup_exit(int code) { exit(code); }
1781
1782 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1783 {
1784     WNDCLASS wndclass;
1785     MSG msg;
1786     OSVERSIONINFO osi;
1787     HMODULE advapi;
1788     char *command = NULL;
1789     int added_keys = 0;
1790     int argc, i;
1791     char **argv, **argstart;
1792
1793     /*
1794      * Determine whether we're an NT system (should have security
1795      * APIs) or a non-NT system (don't do security).
1796      */
1797     memset(&osi, 0, sizeof(OSVERSIONINFO));
1798     osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1799     if (GetVersionEx(&osi) && osi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1800         has_security = TRUE;
1801     } else
1802         has_security = FALSE;
1803
1804     if (has_security) {
1805 #ifndef NO_SECURITY
1806         /*
1807          * Attempt to get the security API we need.
1808          */
1809         advapi = LoadLibrary("ADVAPI32.DLL");
1810         getsecurityinfo =
1811             (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
1812         if (!getsecurityinfo) {
1813             MessageBox(NULL,
1814                        "Unable to access security APIs. Pageant will\n"
1815                        "not run, in case it causes a security breach.",
1816                        "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1817             return 1;
1818         }
1819 #else
1820         MessageBox(NULL,
1821                    "This program has been compiled for Win9X and will\n"
1822                    "not run on NT, in case it causes a security breach.",
1823                    "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1824         return 1;
1825 #endif
1826     } else
1827         advapi = NULL;
1828
1829     instance = inst;
1830
1831     /*
1832      * See if we can find our Help file.
1833      */
1834     {
1835         char b[2048], *p, *q, *r;
1836         FILE *fp;
1837         GetModuleFileName(NULL, b, sizeof(b) - 1);
1838         r = b;
1839         p = strrchr(b, '\\');
1840         if (p && p >= r) r = p+1;
1841         q = strrchr(b, ':');
1842         if (q && q >= r) r = q+1;
1843         strcpy(r, "putty.hlp");
1844         if ( (fp = fopen(b, "r")) != NULL) {
1845             help_path = dupstr(b);
1846             fclose(fp);
1847         } else
1848             help_path = NULL;
1849     }
1850
1851     /*
1852      * Look for the PuTTY binary (we will enable the saved session
1853      * submenu if we find it).
1854      */
1855     {
1856         char b[2048], *p, *q, *r;
1857         FILE *fp;
1858         GetModuleFileName(NULL, b, sizeof(b) - 1);
1859         r = b;
1860         p = strrchr(b, '\\');
1861         if (p && p >= r) r = p+1;
1862         q = strrchr(b, ':');
1863         if (q && q >= r) r = q+1;
1864         strcpy(r, "putty.exe");
1865         if ( (fp = fopen(b, "r")) != NULL) {
1866             putty_path = dupstr(b);
1867             fclose(fp);
1868         } else
1869             putty_path = NULL;
1870     }
1871
1872     /*
1873      * Find out if Pageant is already running.
1874      */
1875     already_running = FALSE;
1876     if (agent_exists())
1877         already_running = TRUE;
1878     else {
1879
1880         if (!prev) {
1881             wndclass.style = 0;
1882             wndclass.lpfnWndProc = WndProc;
1883             wndclass.cbClsExtra = 0;
1884             wndclass.cbWndExtra = 0;
1885             wndclass.hInstance = inst;
1886             wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1887             wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1888             wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1889             wndclass.lpszMenuName = NULL;
1890             wndclass.lpszClassName = APPNAME;
1891
1892             RegisterClass(&wndclass);
1893         }
1894
1895         main_hwnd = keylist = NULL;
1896
1897         main_hwnd = CreateWindow(APPNAME, APPNAME,
1898                                  WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1899                                  CW_USEDEFAULT, CW_USEDEFAULT,
1900                                  100, 100, NULL, NULL, inst, NULL);
1901
1902         /* Set up a system tray icon */
1903         AddTrayIcon(main_hwnd);
1904
1905         /* Accelerators used: nsvkxa */
1906         systray_menu = CreatePopupMenu();
1907         if (putty_path) {
1908             session_menu = CreateMenu();
1909             AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1910             AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1911                        (UINT) session_menu, "&Saved Sessions");
1912             AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1913         }
1914         AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1915                "&View Keys");
1916         AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1917         AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1918         if (help_path)
1919             AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1920         AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1921         AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1922         AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1923         initial_menuitems_count = GetMenuItemCount(session_menu);
1924
1925         ShowWindow(main_hwnd, SW_HIDE);
1926
1927         /*
1928          * Initialise storage for RSA keys.
1929          */
1930         rsakeys = newtree234(cmpkeys_rsa);
1931         ssh2keys = newtree234(cmpkeys_ssh2);
1932
1933     }
1934
1935     /*
1936      * Initialise storage for short-term passphrase cache.
1937      */
1938     passphrases = newtree234(NULL);
1939
1940     /*
1941      * Process the command line and add keys as listed on it.
1942      */
1943     split_into_argv(cmdline, &argc, &argv, &argstart);
1944     for (i = 0; i < argc; i++) {
1945         if (!strcmp(argv[i], "-c")) {
1946             /*
1947              * If we see `-c', then the rest of the
1948              * command line should be treated as a
1949              * command to be spawned.
1950              */
1951             if (i < argc-1)
1952                 command = argstart[i+1];
1953             else
1954                 command = "";
1955             break;
1956         } else {
1957             add_keyfile(argv[i]);
1958             added_keys = TRUE;
1959         }
1960     }
1961
1962     /*
1963      * Forget any passphrase that we retained while going over
1964      * command line keyfiles.
1965      */
1966     forget_passphrases();
1967
1968     if (command) {
1969         char *args;
1970         if (command[0] == '"')
1971             args = strchr(++command, '"');
1972         else
1973             args = strchr(command, ' ');
1974         if (args) {
1975             *args++ = 0;
1976             while(*args && isspace(*args)) args++;
1977         }
1978         spawn_cmd(command, args, show);
1979     }
1980
1981     /*
1982      * If Pageant was already running, we leave now. If we haven't
1983      * even taken any auxiliary action (spawned a command or added
1984      * keys), complain.
1985      */
1986     if (already_running) {
1987         if (!command && !added_keys) {
1988             MessageBox(NULL, "Pageant is already running", "Pageant Error",
1989                        MB_ICONERROR | MB_OK);
1990         }
1991         if (advapi)
1992             FreeLibrary(advapi);
1993         return 0;
1994     }
1995
1996     /*
1997      * Main message loop.
1998      */
1999     while (GetMessage(&msg, NULL, 0, 0) == 1) {
2000         TranslateMessage(&msg);
2001         DispatchMessage(&msg);
2002     }
2003
2004     /* Clean up the system tray icon */
2005     {
2006         NOTIFYICONDATA tnid;
2007
2008         tnid.cbSize = sizeof(NOTIFYICONDATA);
2009         tnid.hWnd = main_hwnd;
2010         tnid.uID = 1;
2011
2012         Shell_NotifyIcon(NIM_DELETE, &tnid);
2013
2014         DestroyMenu(systray_menu);
2015     }
2016
2017     if (advapi)
2018         FreeLibrary(advapi);
2019     return msg.wParam;
2020 }