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