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