OSDN Git Service

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