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