]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - pageant.c
Pageant is now able to avoid asking for the passphrase when asked to
[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 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1662 {
1663     WNDCLASS wndclass;
1664     MSG msg;
1665     OSVERSIONINFO osi;
1666     HMODULE advapi;
1667     char *command = NULL;
1668     int added_keys = 0;
1669
1670     /*
1671      * Determine whether we're an NT system (should have security
1672      * APIs) or a non-NT system (don't do security).
1673      */
1674     memset(&osi, 0, sizeof(OSVERSIONINFO));
1675     osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1676     if (GetVersionEx(&osi) && osi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1677         has_security = TRUE;
1678     } else
1679         has_security = FALSE;
1680
1681     if (has_security) {
1682 #ifndef NO_SECURITY
1683         /*
1684          * Attempt to get the security API we need.
1685          */
1686         advapi = LoadLibrary("ADVAPI32.DLL");
1687         getsecurityinfo =
1688             (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
1689         if (!getsecurityinfo) {
1690             MessageBox(NULL,
1691                        "Unable to access security APIs. Pageant will\n"
1692                        "not run, in case it causes a security breach.",
1693                        "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1694             return 1;
1695         }
1696 #else
1697         MessageBox(NULL,
1698                    "This program has been compiled for Win9X and will\n"
1699                    "not run on NT, in case it causes a security breach.",
1700                    "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1701         return 1;
1702 #endif
1703     } else
1704         advapi = NULL;
1705
1706     instance = inst;
1707
1708     /*
1709      * See if we can find our Help file.
1710      */
1711     {
1712         char b[2048], *p, *q, *r;
1713         FILE *fp;
1714         GetModuleFileName(NULL, b, sizeof(b) - 1);
1715         r = b;
1716         p = strrchr(b, '\\');
1717         if (p && p >= r) r = p+1;
1718         q = strrchr(b, ':');
1719         if (q && q >= r) r = q+1;
1720         strcpy(r, "putty.hlp");
1721         if ( (fp = fopen(b, "r")) != NULL) {
1722             help_path = dupstr(b);
1723             fclose(fp);
1724         } else
1725             help_path = NULL;
1726     }
1727
1728     /*
1729      * Find out if Pageant is already running.
1730      */
1731     already_running = FALSE;
1732     if (agent_exists())
1733         already_running = TRUE;
1734     else {
1735
1736         if (!prev) {
1737             wndclass.style = 0;
1738             wndclass.lpfnWndProc = WndProc;
1739             wndclass.cbClsExtra = 0;
1740             wndclass.cbWndExtra = 0;
1741             wndclass.hInstance = inst;
1742             wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1743             wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1744             wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1745             wndclass.lpszMenuName = NULL;
1746             wndclass.lpszClassName = APPNAME;
1747
1748             RegisterClass(&wndclass);
1749         }
1750
1751         main_hwnd = keylist = NULL;
1752
1753         main_hwnd = CreateWindow(APPNAME, APPNAME,
1754                                  WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1755                                  CW_USEDEFAULT, CW_USEDEFAULT,
1756                                  100, 100, NULL, NULL, inst, NULL);
1757
1758         /* Set up a system tray icon */
1759         AddTrayIcon(main_hwnd);
1760
1761         systray_menu = CreatePopupMenu();
1762         /* accelerators used: vkxa */
1763         AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1764                "&View Keys");
1765         AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1766         AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1767         if (help_path)
1768             AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1769         AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1770         AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1771         AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1772
1773         ShowWindow(main_hwnd, SW_HIDE);
1774
1775         /*
1776          * Initialise storage for RSA keys.
1777          */
1778         rsakeys = newtree234(cmpkeys_rsa);
1779         ssh2keys = newtree234(cmpkeys_ssh2);
1780
1781     }
1782
1783     /*
1784      * Initialise storage for short-term passphrase cache.
1785      */
1786     passphrases = newtree234(NULL);
1787
1788     /*
1789      * Process the command line and add keys as listed on it.
1790      * If we already determined that we need to spawn a program from above we
1791      * need to ignore the first two arguments. [DBW]
1792      */
1793     {
1794         char *p;
1795         int inquotes = 0;
1796         p = cmdline;
1797         while (*p) {
1798             while (*p && isspace(*p))
1799                 p++;
1800                 if (*p && !isspace(*p)) {
1801                 char *q = p, *pp = p;
1802                 while (*p && (inquotes || !isspace(*p))) {
1803                         if (*p == '"') {
1804                         inquotes = !inquotes;
1805                         p++;
1806                         continue;
1807                     }
1808                     *pp++ = *p++;
1809                 }
1810                 if (*pp) {
1811                     if (*p)
1812                         p++;
1813                     *pp++ = '\0';
1814                 }
1815                 if (!strcmp(q, "-c")) {
1816                     /*
1817                      * If we see `-c', then the rest of the
1818                      * command line should be treated as a
1819                      * command to be spawned.
1820                      */
1821                     while (*p && isspace(*p))
1822                         p++;
1823                     command = p;
1824                     break;
1825                 } else {
1826                     add_keyfile(q);
1827                     added_keys = TRUE;
1828                 }
1829             }
1830         }
1831     }
1832
1833     /*
1834      * Forget any passphrase that we retained while going over
1835      * command line keyfiles.
1836      */
1837     forget_passphrases();
1838
1839     if (command) {
1840         char *args;
1841         if (command[0] == '"')
1842             args = strchr(++command, '"');
1843         else
1844             args = strchr(command, ' ');
1845         if (args) {
1846             *args++ = 0;
1847             while(*args && isspace(*args)) args++;
1848         }
1849         spawn_cmd(command, args, show);
1850     }
1851
1852     /*
1853      * If Pageant was already running, we leave now. If we haven't
1854      * even taken any auxiliary action (spawned a command or added
1855      * keys), complain.
1856      */
1857     if (already_running) {
1858         if (!command && !added_keys) {
1859             MessageBox(NULL, "Pageant is already running", "Pageant Error",
1860                        MB_ICONERROR | MB_OK);
1861         }
1862         if (advapi)
1863             FreeLibrary(advapi);
1864         return 0;
1865     }
1866
1867     /*
1868      * Main message loop.
1869      */
1870     while (GetMessage(&msg, NULL, 0, 0) == 1) {
1871         TranslateMessage(&msg);
1872         DispatchMessage(&msg);
1873     }
1874
1875     /* Clean up the system tray icon */
1876     {
1877         NOTIFYICONDATA tnid;
1878
1879         tnid.cbSize = sizeof(NOTIFYICONDATA);
1880         tnid.hWnd = main_hwnd;
1881         tnid.uID = 1;
1882
1883         Shell_NotifyIcon(NIM_DELETE, &tnid);
1884
1885         DestroyMenu(systray_menu);
1886     }
1887
1888     if (advapi)
1889         FreeLibrary(advapi);
1890     exit(msg.wParam);
1891 }