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