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