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