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