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