]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - pageant.c
SSH2 can now use Pageant to obtain keys from
[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 "putty.h" // FIXME
13 #include "ssh.h"
14 #include "tree234.h"
15
16 #define IDI_MAINICON 200
17 #define IDI_TRAYICON 201
18
19 #define WM_XUSER     (WM_USER + 0x2000)
20 #define WM_SYSTRAY   (WM_XUSER + 6)
21 #define WM_SYSTRAY2  (WM_XUSER + 7)
22
23 #define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */
24
25 /*
26  * FIXME: maybe some day we can sort this out ...
27  */
28 #define AGENT_MAX_MSGLEN  8192
29
30 #define IDM_CLOSE    0x0010
31 #define IDM_VIEWKEYS 0x0020
32 #define IDM_ADDKEY   0x0030
33 #define IDM_ABOUT    0x0040
34
35 #define APPNAME "Pageant"
36
37 extern char ver[];
38
39 static HINSTANCE instance;
40 static HWND hwnd;
41 static HWND keylist;
42 static HWND aboutbox;
43 static HMENU systray_menu;
44
45 static tree234 *rsakeys, *ssh2keys;
46
47 static int has_security;
48 #ifndef NO_SECURITY
49 typedef DWORD (WINAPI *gsi_fn_t)
50     (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
51                                  PSID *, PSID *, PACL *, PACL *,
52                                  PSECURITY_DESCRIPTOR *);
53 static gsi_fn_t getsecurityinfo;
54 #endif
55
56 /*
57  * We need this to link with the RSA code, because rsaencrypt()
58  * pads its data with random bytes. Since we only use rsadecrypt()
59  * and the signing functions, which are deterministic, this should
60  * never be called.
61  * 
62  * If it _is_ called, there is a _serious_ problem, because it
63  * won't generate true random numbers. So we must scream, panic,
64  * and exit immediately if that should happen.
65  */
66 int random_byte(void) {
67     MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
68     exit(0);
69 }
70
71 /*
72  * Blob structure for passing to the asymmetric SSH2 key compare
73  * function, prototyped here.
74  */
75 struct blob {
76     unsigned char *blob;
77     int len;
78 };
79 static int cmpkeys_ssh2_asymm(void *av, void *bv);
80
81 /*
82  * This function is needed to link with the DES code. We need not
83  * have it do anything at all.
84  */
85 void logevent(char *msg) {
86 }
87
88 #define GET_32BIT(cp) \
89     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
90     ((unsigned long)(unsigned char)(cp)[1] << 16) | \
91     ((unsigned long)(unsigned char)(cp)[2] << 8) | \
92     ((unsigned long)(unsigned char)(cp)[3]))
93
94 #define PUT_32BIT(cp, value) { \
95     (cp)[0] = (unsigned char)((value) >> 24); \
96     (cp)[1] = (unsigned char)((value) >> 16); \
97     (cp)[2] = (unsigned char)((value) >> 8); \
98     (cp)[3] = (unsigned char)(value); }
99
100 #define PASSPHRASE_MAXLEN 512
101
102 struct PassphraseProcStruct {
103     char *passphrase;
104     char *comment;
105 };
106
107 /*
108  * Dialog-box function for the Licence box.
109  */
110 static int CALLBACK LicenceProc (HWND hwnd, UINT msg,
111                                  WPARAM wParam, LPARAM lParam) {
112     switch (msg) {
113       case WM_INITDIALOG:
114         return 1;
115       case WM_COMMAND:
116         switch (LOWORD(wParam)) {
117           case IDOK:
118             EndDialog(hwnd, 1);
119             return 0;
120         }
121         return 0;
122       case WM_CLOSE:
123         EndDialog(hwnd, 1);
124         return 0;
125     }
126     return 0;
127 }
128
129 /*
130  * Dialog-box function for the About box.
131  */
132 static int CALLBACK AboutProc (HWND hwnd, UINT msg,
133                                WPARAM wParam, LPARAM lParam) {
134     switch (msg) {
135       case WM_INITDIALOG:
136         SetDlgItemText (hwnd, 100, ver);
137         return 1;
138       case WM_COMMAND:
139         switch (LOWORD(wParam)) {
140           case IDOK:
141             aboutbox = NULL;
142             DestroyWindow (hwnd);
143             return 0;
144           case 101:
145             EnableWindow(hwnd, 0);
146             DialogBox (instance, MAKEINTRESOURCE(214), NULL, LicenceProc);
147             EnableWindow(hwnd, 1);
148             SetActiveWindow(hwnd);
149             return 0;
150         }
151         return 0;
152       case WM_CLOSE:
153         aboutbox = NULL;
154         DestroyWindow (hwnd);
155         return 0;
156     }
157     return 0;
158 }
159
160 /*
161  * Dialog-box function for the passphrase box.
162  */
163 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
164                                    WPARAM wParam, LPARAM lParam) {
165     static char *passphrase;
166     struct PassphraseProcStruct *p;
167
168     switch (msg) {
169       case WM_INITDIALOG:
170         /*
171          * Centre the window.
172          */
173         {                              /* centre the window */
174             RECT rs, rd;
175             HWND hw;
176
177             hw = GetDesktopWindow();
178             if (GetWindowRect (hw, &rs) && GetWindowRect (hwnd, &rd))
179                 MoveWindow (hwnd, (rs.right + rs.left + rd.left - rd.right)/2,
180                             (rs.bottom + rs.top + rd.top - rd.bottom)/2,
181                             rd.right-rd.left, rd.bottom-rd.top, TRUE);
182         }
183
184         SetForegroundWindow(hwnd);
185         SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0,
186                       SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
187         p = (struct PassphraseProcStruct *)lParam;
188         passphrase = p->passphrase;
189         if (p->comment)
190             SetDlgItemText(hwnd, 101, p->comment);
191         *passphrase = 0;
192         return 0;
193       case WM_COMMAND:
194         switch (LOWORD(wParam)) {
195           case IDOK:
196             if (*passphrase)
197                 EndDialog (hwnd, 1);
198             else
199                 MessageBeep (0);
200             return 0;
201           case IDCANCEL:
202             EndDialog (hwnd, 0);
203             return 0;
204           case 102:                    /* edit box */
205             if (HIWORD(wParam) == EN_CHANGE) {
206                 GetDlgItemText (hwnd, 102, passphrase, PASSPHRASE_MAXLEN-1);
207                 passphrase[PASSPHRASE_MAXLEN-1] = '\0';
208             }
209             return 0;
210         }
211         return 0;
212       case WM_CLOSE:
213         EndDialog (hwnd, 0);
214         return 0;
215     }
216     return 0;
217 }
218
219 /*
220  * Update the visible key list.
221  */
222 static void keylist_update(void) {
223     struct RSAKey *rkey;
224     struct ssh2_userkey *skey;
225     enum234 e;
226
227     if (keylist) {
228         SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
229         for (rkey = first234(rsakeys, &e); rkey; rkey = next234(&e)) {
230             char listentry[512], *p;
231             /*
232              * Replace two spaces in the fingerprint with tabs, for
233              * nice alignment in the box.
234              */
235             strcpy(listentry, "ssh1\t");
236             p = listentry+strlen(listentry);
237             rsa_fingerprint(p, sizeof(listentry)-(p-listentry), rkey);
238             p = strchr(listentry, ' '); if (p) *p = '\t';
239             p = strchr(listentry, ' '); if (p) *p = '\t';
240             SendDlgItemMessage (keylist, 100, LB_ADDSTRING,
241                                 0, (LPARAM)listentry);
242         }
243         for (skey = first234(ssh2keys, &e); skey; skey = next234(&e)) {
244             char listentry[512], *p;
245             int len;
246             /*
247              * Replace two spaces in the fingerprint with tabs, for
248              * nice alignment in the box.
249              */
250             p = skey->alg->fingerprint(skey->data);
251             strncpy(listentry, p, sizeof(listentry));
252             p = strchr(listentry, ' '); if (p) *p = '\t';
253             p = strchr(listentry, ' '); if (p) *p = '\t';
254             len = strlen(listentry);
255             if (len < sizeof(listentry)-2) {
256                 listentry[len] = '\t';
257                 strncpy(listentry+len+1, skey->comment, sizeof(listentry)-len-1);
258             }
259             SendDlgItemMessage (keylist, 100, LB_ADDSTRING,
260                                 0, (LPARAM)listentry);
261         }
262         SendDlgItemMessage (keylist, 100, LB_SETCURSEL, (WPARAM) -1, 0);
263     }
264 }
265
266 /*
267  * This function loads a key from a file and adds it.
268  */
269 static void add_keyfile(char *filename) {
270     char passphrase[PASSPHRASE_MAXLEN];
271     struct RSAKey *rkey;
272     struct ssh2_userkey *skey;
273     int needs_pass;
274     int ret;
275     int attempts;
276     char *comment;
277     struct PassphraseProcStruct pps;
278     int ver;
279
280     ver = keyfile_version(filename);
281     if (ver == 0) {
282         MessageBox(NULL, "Couldn't load private key.", APPNAME,
283                    MB_OK | MB_ICONERROR);
284         return;
285     }
286
287     if (ver == 1)
288         needs_pass = rsakey_encrypted(filename, &comment);
289     else
290         needs_pass = ssh2_userkey_encrypted(filename, &comment);
291     attempts = 0;
292     if (ver == 1)
293         rkey = smalloc(sizeof(*rkey));
294     pps.passphrase = passphrase;
295     pps.comment = comment;
296     do {
297         if (needs_pass) {
298             int dlgret;
299             dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
300                                     NULL, PassphraseProc,
301                                     (LPARAM)&pps);
302             if (!dlgret) {
303                 if (comment) sfree(comment);
304                 if (ver == 1)
305                     sfree(rkey);
306                 return;                /* operation cancelled */
307             }
308         } else
309             *passphrase = '\0';
310         if (ver == 1)
311             ret = loadrsakey(filename, rkey, passphrase);
312         else {
313             skey = ssh2_load_userkey(filename, passphrase);
314             if (skey == SSH2_WRONG_PASSPHRASE)
315                 ret = -1;
316             else if (!skey)
317                 ret = 0;
318             else
319                 ret = 1;
320         }
321         attempts++;
322     } while (ret == -1);
323     if (comment) sfree(comment);
324     if (ret == 0) {
325         MessageBox(NULL, "Couldn't load private key.", APPNAME,
326                    MB_OK | MB_ICONERROR);
327         if (ver == 1)
328             sfree(rkey);
329         return;
330     }
331     if (ver == 1) {
332         if (add234(rsakeys, rkey) != rkey)
333             sfree(rkey);               /* already present, don't waste RAM */
334     } else {
335         if (add234(ssh2keys, skey) != skey) {
336             skey->alg->freekey(skey->data);
337             sfree(skey);               /* already present, don't waste RAM */
338         }
339     }
340 }
341
342 /*
343  * This is the main agent function that answers messages.
344  */
345 static void answer_msg(void *msg) {
346     unsigned char *p = msg;
347     unsigned char *ret = msg;
348     int type;
349
350     /*
351      * Get the message type.
352      */
353     type = p[4];
354
355     p += 5;
356     switch (type) {
357       case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
358         /*
359          * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
360          */
361         {
362             enum234 e;
363             struct RSAKey *key;
364             int len, nkeys;
365
366             /*
367              * Count up the number and length of keys we hold.
368              */
369             len = nkeys = 0;
370             for (key = first234(rsakeys, &e); key; key = next234(&e)) {
371                 nkeys++;
372                 len += 4;              /* length field */
373                 len += ssh1_bignum_length(key->exponent);
374                 len += ssh1_bignum_length(key->modulus);
375                 len += 4 + strlen(key->comment);
376             }
377
378             /*
379              * Packet header is the obvious five bytes, plus four
380              * bytes for the key count.
381              */
382             len += 5 + 4;
383             if (len > AGENT_MAX_MSGLEN)
384                 goto failure;          /* aaargh! too much stuff! */
385             PUT_32BIT(ret, len-4);
386             ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
387             PUT_32BIT(ret+5, nkeys);
388             p = ret + 5 + 4;
389             for (key = first234(rsakeys, &e); key; key = next234(&e)) {
390                 PUT_32BIT(p, ssh1_bignum_bitcount(key->modulus));
391                 p += 4;
392                 p += ssh1_write_bignum(p, key->exponent);
393                 p += ssh1_write_bignum(p, key->modulus);
394                 PUT_32BIT(p, strlen(key->comment));
395                 memcpy(p+4, key->comment, strlen(key->comment));
396                 p += 4 + strlen(key->comment);
397             }
398         }
399         break;
400       case SSH2_AGENTC_REQUEST_IDENTITIES:
401         /*
402          * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
403          */
404         {
405             enum234 e;
406             struct ssh2_userkey *key;
407             int len, nkeys;
408             unsigned char *blob;
409             int bloblen;
410
411             /*
412              * Count up the number and length of keys we hold.
413              */
414             len = nkeys = 0;
415             for (key = first234(ssh2keys, &e); key; key = next234(&e)) {
416                 nkeys++;
417                 len += 4;              /* length field */
418                 blob = key->alg->public_blob(key->data, &bloblen);
419                 len += bloblen;
420                 sfree(blob);
421                 len += 4 + strlen(key->comment);
422             }
423
424             /*
425              * Packet header is the obvious five bytes, plus four
426              * bytes for the key count.
427              */
428             len += 5 + 4;
429             if (len > AGENT_MAX_MSGLEN)
430                 goto failure;          /* aaargh! too much stuff! */
431             PUT_32BIT(ret, len-4);
432             ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
433             PUT_32BIT(ret+5, nkeys);
434             p = ret + 5 + 4;
435             for (key = first234(ssh2keys, &e); key; key = next234(&e)) {
436                 blob = key->alg->public_blob(key->data, &bloblen);
437                 PUT_32BIT(p, bloblen);
438                 p += 4;
439                 memcpy(p, blob, bloblen);
440                 p += bloblen;
441                 sfree(blob);
442                 PUT_32BIT(p, strlen(key->comment));
443                 memcpy(p+4, key->comment, strlen(key->comment));
444                 p += 4 + strlen(key->comment);
445             }
446         }
447         break;
448       case SSH1_AGENTC_RSA_CHALLENGE:
449         /*
450          * Reply with either SSH1_AGENT_RSA_RESPONSE or
451          * SSH_AGENT_FAILURE, depending on whether we have that key
452          * or not.
453          */
454         {
455             struct RSAKey reqkey, *key;
456             Bignum challenge, response;
457             unsigned char response_source[48], response_md5[16];
458             struct MD5Context md5c;
459             int i, len;
460
461             p += 4;
462             p += ssh1_read_bignum(p, &reqkey.exponent);
463             p += ssh1_read_bignum(p, &reqkey.modulus);
464             p += ssh1_read_bignum(p, &challenge);
465             memcpy(response_source+32, p, 16); p += 16;
466             if (GET_32BIT(p) != 1 ||
467                 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
468                 freebn(reqkey.exponent);
469                 freebn(reqkey.modulus);
470                 freebn(challenge);
471                 goto failure;
472             }
473             response = rsadecrypt(challenge, key);
474             for (i = 0; i < 32; i++)
475                 response_source[i] = bignum_byte(response, 31-i);
476
477             MD5Init(&md5c);
478             MD5Update(&md5c, response_source, 48);
479             MD5Final(response_md5, &md5c);
480             memset(response_source, 0, 48);   /* burn the evidence */
481             freebn(response);          /* and that evidence */
482             freebn(challenge);         /* yes, and that evidence */
483             freebn(reqkey.exponent);   /* and free some memory ... */
484             freebn(reqkey.modulus);    /* ... while we're at it. */
485
486             /*
487              * Packet is the obvious five byte header, plus sixteen
488              * bytes of MD5.
489              */
490             len = 5 + 16;
491             PUT_32BIT(ret, len-4);
492             ret[4] = SSH1_AGENT_RSA_RESPONSE;
493             memcpy(ret+5, response_md5, 16);
494         }
495         break;
496       case SSH2_AGENTC_SIGN_REQUEST:
497         /*
498          * Reply with either SSH2_AGENT_RSA_RESPONSE or
499          * SSH_AGENT_FAILURE, depending on whether we have that key
500          * or not.
501          */
502         {
503             struct ssh2_userkey *key;
504             struct blob b;
505             unsigned char *data, *signature;
506             int datalen, siglen, len;
507
508             b.len = GET_32BIT(p);
509             p += 4;
510             b.blob = p;
511             p += b.len;
512             datalen = GET_32BIT(p);
513             p += 4;
514             data = p;
515             key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
516             if (!key)
517                 goto failure;
518             signature = key->alg->sign(key->data, data, datalen, &siglen);
519             len = 5+4+siglen;
520             PUT_32BIT(ret, len-4);
521             ret[4] = SSH2_AGENT_SIGN_RESPONSE;
522             PUT_32BIT(ret+5, siglen);
523             memcpy(ret+5+4, signature, siglen);
524             sfree(signature);
525         }
526         break;
527       case SSH1_AGENTC_ADD_RSA_IDENTITY:
528         /*
529          * Add to the list and return SSH_AGENT_SUCCESS, or
530          * SSH_AGENT_FAILURE if the key was malformed.
531          */
532         {
533             struct RSAKey *key;
534             char *comment;
535             key = smalloc(sizeof(struct RSAKey));
536             memset(key, 0, sizeof(key));
537             p += makekey(p, key, NULL, 1);
538             p += makeprivate(p, key);
539             p += ssh1_read_bignum(p, key->iqmp); /* p^-1 mod q */
540             p += ssh1_read_bignum(p, key->p);    /* p */
541             p += ssh1_read_bignum(p, key->q);    /* q */
542             comment = smalloc(GET_32BIT(p));
543             if (comment) {
544                 memcpy(comment, p+4, GET_32BIT(p));
545                 key->comment = comment;
546             }
547             PUT_32BIT(ret, 1);
548             ret[4] = SSH_AGENT_FAILURE;
549             if (add234(rsakeys, key) == key) {
550                 keylist_update();
551                 ret[4] = SSH_AGENT_SUCCESS;
552             } else {
553                 freersakey(key);
554                 sfree(key);
555             }
556         }
557         break;
558       case SSH2_AGENTC_ADD_IDENTITY:
559         /*
560          * Add to the list and return SSH_AGENT_SUCCESS, or
561          * SSH_AGENT_FAILURE if the key was malformed.
562          */
563         {
564             struct ssh2_userkey *key;
565             char *comment, *alg;
566             int alglen, commlen;
567             int bloblen;
568
569             key = smalloc(sizeof(struct ssh2_userkey));
570
571             alglen = GET_32BIT(p); p += 4;
572             alg = p; p += alglen;
573             /* Add further algorithm names here. */
574             if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
575                 key->alg = &ssh_rsa;
576             else {
577                 sfree(key);
578                 goto failure;
579             }
580
581             bloblen = GET_32BIT((unsigned char *)msg) - (p-(unsigned char *)msg-4);
582             key->data = key->alg->openssh_createkey(&p, &bloblen);
583             if (!key->data) {
584                 sfree(key);
585                 goto failure;
586             }
587             commlen = GET_32BIT(p); p += 4;
588
589             comment = smalloc(commlen+1);
590             if (comment) {
591                 memcpy(comment, p, commlen);
592                 comment[commlen] = '\0';
593             }
594             key->comment = comment;
595
596             PUT_32BIT(ret, 1);
597             ret[4] = SSH_AGENT_FAILURE;
598             if (add234(ssh2keys, key) == key) {
599                 keylist_update();
600                 ret[4] = SSH_AGENT_SUCCESS;
601             } else {
602                 key->alg->freekey(key->data);
603                 sfree(key->comment);
604                 sfree(key);
605             }
606         }
607         break;
608       case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
609         /*
610          * Remove from the list and return SSH_AGENT_SUCCESS, or
611          * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
612          * start with.
613          */
614         {
615             struct RSAKey reqkey, *key;
616
617             p += makekey(p, &reqkey, NULL, 0);
618             key = find234(rsakeys, &reqkey, NULL);
619             freebn(reqkey.exponent);
620             freebn(reqkey.modulus);
621             PUT_32BIT(ret, 1);
622             ret[4] = SSH_AGENT_FAILURE;
623             if (key) {
624                 del234(rsakeys, key);
625                 keylist_update();
626                 freersakey(key);
627                 sfree(key);
628                 ret[4] = SSH_AGENT_SUCCESS;
629             }
630         }
631         break;
632       case SSH2_AGENTC_REMOVE_IDENTITY:
633         /*
634          * Remove from the list and return SSH_AGENT_SUCCESS, or
635          * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
636          * start with.
637          */
638         {
639             struct ssh2_userkey *key;
640             struct blob b;
641
642             b.len = GET_32BIT(p);
643             p += 4;
644             b.blob = p;
645             p += b.len;
646             key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
647             if (!key)
648                 goto failure;
649
650             PUT_32BIT(ret, 1);
651             ret[4] = SSH_AGENT_FAILURE;
652             if (key) {
653                 del234(ssh2keys, key);
654                 keylist_update();
655                 key->alg->freekey(key->data);
656                 sfree(key);
657                 ret[4] = SSH_AGENT_SUCCESS;
658             }
659         }
660         break;
661       case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
662         /*
663          * Remove all SSH1 keys. Always returns success.
664          */
665         {
666             struct RSAKey *rkey;
667             enum234 e;
668
669             while ( (rkey = first234(rsakeys, &e)) != NULL ) {
670                 del234(rsakeys, rkey);
671                 freersakey(rkey);
672                 sfree(rkey);
673             }
674             keylist_update();
675
676             PUT_32BIT(ret, 1);
677             ret[4] = SSH_AGENT_SUCCESS;
678         }
679         break;
680       case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
681         /*
682          * Remove all SSH2 keys. Always returns success.
683          */
684         {
685             struct ssh2_userkey *skey;
686             enum234 e;
687
688             while ( (skey = first234(ssh2keys, &e)) != NULL ) {
689                 del234(ssh2keys, skey);
690                 skey->alg->freekey(skey->data);
691                 sfree(skey);
692             }
693             keylist_update();
694
695             PUT_32BIT(ret, 1);
696             ret[4] = SSH_AGENT_SUCCESS;
697         }
698         break;
699       default:
700         failure:
701         /*
702          * Unrecognised message. Return SSH_AGENT_FAILURE.
703          */
704         PUT_32BIT(ret, 1);
705         ret[4] = SSH_AGENT_FAILURE;
706         break;
707     }
708 }
709
710 /*
711  * Key comparison function for the 2-3-4 tree of RSA keys.
712  */
713 static int cmpkeys_rsa(void *av, void *bv) {
714     struct RSAKey *a = (struct RSAKey *)av;
715     struct RSAKey *b = (struct RSAKey *)bv;
716     Bignum am, bm;
717     int alen, blen;
718
719     am = a->modulus;
720     bm = b->modulus;
721     /*
722      * Compare by length of moduli.
723      */
724     alen = ssh1_bignum_bitcount(am);
725     blen = ssh1_bignum_bitcount(bm);
726     if (alen > blen) return +1; else if (alen < blen) return -1;
727     /*
728      * Now compare by moduli themselves.
729      */
730     alen = (alen + 7) / 8;             /* byte count */
731     while (alen-- > 0) {
732         int abyte, bbyte;
733         abyte = bignum_byte(am, alen);
734         bbyte = bignum_byte(bm, alen);
735         if (abyte > bbyte) return +1; else if (abyte < bbyte) return -1;
736     }
737     /*
738      * Give up.
739      */
740     return 0;
741 }
742
743 /*
744  * Key comparison function for the 2-3-4 tree of SSH2 keys.
745  */
746 static int cmpkeys_ssh2(void *av, void *bv) {
747     struct ssh2_userkey *a = (struct ssh2_userkey *)av;
748     struct ssh2_userkey *b = (struct ssh2_userkey *)bv;
749     int i;
750     int alen, blen;
751     unsigned char *ablob, *bblob;
752     int c;
753     
754     /*
755      * Compare purely by public blob.
756      */
757     ablob = a->alg->public_blob(a->data, &alen);
758     bblob = b->alg->public_blob(b->data, &blen);
759
760     c = 0;
761     for (i = 0; i < alen && i < blen; i++) {
762         if (ablob[i] < bblob[i]) {
763             c = -1; break;
764         } else if (ablob[i] > bblob[i]) {
765             c = +1; break;
766         }
767     }
768     if (c == 0 && i < alen) c = +1;    /* a is longer */
769     if (c == 0 && i < blen) c = -1;    /* a is longer */
770
771     sfree(ablob);
772     sfree(bblob);
773
774     return c;
775 }
776
777 /*
778  * Key comparison function for looking up a blob in the 2-3-4 tree
779  * of SSH2 keys.
780  */
781 static int cmpkeys_ssh2_asymm(void *av, void *bv) {
782     struct blob *a = (struct blob *)av;
783     struct ssh2_userkey *b = (struct ssh2_userkey *)bv;
784     int i;
785     int alen, blen;
786     unsigned char *ablob, *bblob;
787     int c;
788     
789     /*
790      * Compare purely by public blob.
791      */
792     ablob = a->blob;
793     alen = a->len;
794     bblob = b->alg->public_blob(b->data, &blen);
795
796     c = 0;
797     for (i = 0; i < alen && i < blen; i++) {
798         if (ablob[i] < bblob[i]) {
799             c = -1; break;
800         } else if (ablob[i] > bblob[i]) {
801             c = +1; break;
802         }
803     }
804     if (c == 0 && i < alen) c = +1;    /* a is longer */
805     if (c == 0 && i < blen) c = -1;    /* a is longer */
806
807     sfree(bblob);
808
809     return c;
810 }
811
812 static void error(char *s) {
813     MessageBox(hwnd, s, APPNAME, MB_OK | MB_ICONERROR);
814 }
815
816 /*
817  * Prompt for a key file to add, and add it.
818  */
819 static void prompt_add_keyfile(void) {
820     OPENFILENAME of;
821     char filename[FILENAME_MAX];
822     memset(&of, 0, sizeof(of));
823 #ifdef OPENFILENAME_SIZE_VERSION_400
824     of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
825 #else
826     of.lStructSize = sizeof(of);
827 #endif
828     of.hwndOwner = hwnd;
829     of.lpstrFilter = "All Files\0*\0\0\0";
830     of.lpstrCustomFilter = NULL;
831     of.nFilterIndex = 1;
832     of.lpstrFile = filename; *filename = '\0';
833     of.nMaxFile = sizeof(filename);
834     of.lpstrFileTitle = NULL;
835     of.lpstrInitialDir = NULL;
836     of.lpstrTitle = "Select Private Key File";
837     of.Flags = 0;
838     if (GetOpenFileName(&of)) {
839         add_keyfile(filename);
840         keylist_update();
841     }
842 }
843
844 /*
845  * Dialog-box function for the key list box.
846  */
847 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
848                                 WPARAM wParam, LPARAM lParam) {
849     enum234 e;
850     struct RSAKey *rkey;
851     struct ssh2_userkey *skey;
852
853     switch (msg) {
854       case WM_INITDIALOG:
855         /*
856          * Centre the window.
857          */
858         {                              /* centre the window */
859             RECT rs, rd;
860             HWND hw;
861
862             hw = GetDesktopWindow();
863             if (GetWindowRect (hw, &rs) && GetWindowRect (hwnd, &rd))
864                 MoveWindow (hwnd, (rs.right + rs.left + rd.left - rd.right)/2,
865                             (rs.bottom + rs.top + rd.top - rd.bottom)/2,
866                             rd.right-rd.left, rd.bottom-rd.top, TRUE);
867         }
868
869         keylist = hwnd;
870         {
871             static int tabs[] = {35, 60, 210};
872             SendDlgItemMessage (hwnd, 100, LB_SETTABSTOPS,
873                                 sizeof(tabs)/sizeof(*tabs), (LPARAM) tabs);
874         }
875         keylist_update();
876         return 0;
877       case WM_COMMAND:
878         switch (LOWORD(wParam)) {
879           case IDOK:
880           case IDCANCEL:
881             keylist = NULL;
882             DestroyWindow(hwnd);
883             return 0;
884           case 101:                    /* add key */
885             if (HIWORD(wParam) == BN_CLICKED ||
886                 HIWORD(wParam) == BN_DOUBLECLICKED) {
887                 prompt_add_keyfile();
888             }
889             return 0;
890           case 102:                    /* remove key */
891             if (HIWORD(wParam) == BN_CLICKED ||
892                 HIWORD(wParam) == BN_DOUBLECLICKED) {
893                 int n = SendDlgItemMessage (hwnd, 100, LB_GETCURSEL, 0, 0);
894                 if (n == LB_ERR) {
895                     MessageBeep(0);
896                     break;
897                 }
898                 for (rkey = first234(rsakeys, &e); rkey; rkey = next234(&e))
899                     if (n-- == 0)
900                         break;
901                 if (rkey) {
902                     del234(rsakeys, rkey);
903                     freersakey(rkey);
904                     sfree(rkey);
905                 } else {
906                     for (skey = first234(ssh2keys, &e); skey; skey = next234(&e))
907                         if (n-- == 0)
908                             break;
909                     if (skey) {
910                         del234(ssh2keys, skey);
911                         skey->alg->freekey(skey->data);
912                         sfree(skey);
913                     }
914                 }
915                 keylist_update();
916             }
917             return 0;
918         }
919         return 0;
920       case WM_CLOSE:
921         keylist = NULL;
922         DestroyWindow(hwnd);
923         return 0;
924     }
925     return 0;
926 }
927
928 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
929                                  WPARAM wParam, LPARAM lParam) {
930     int ret;
931     static int menuinprogress;
932
933     switch (message) {
934       case WM_SYSTRAY:
935         if (lParam == WM_RBUTTONUP) {
936             POINT cursorpos;
937             GetCursorPos(&cursorpos);
938             PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
939         } else if (lParam == WM_LBUTTONDBLCLK) {
940             /* Equivalent to IDM_VIEWKEYS. */
941             PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
942         }
943         break;
944       case WM_SYSTRAY2:
945         if (!menuinprogress) {
946             menuinprogress = 1;
947             SetForegroundWindow(hwnd);
948             ret = TrackPopupMenu(systray_menu,
949                                  TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
950                                  TPM_RIGHTBUTTON,
951                                  wParam, lParam, 0, hwnd, NULL);
952             menuinprogress = 0;
953         }
954         break;
955       case WM_COMMAND:
956       case WM_SYSCOMMAND:
957         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
958           case IDM_CLOSE:
959             SendMessage(hwnd, WM_CLOSE, 0, 0);
960             break;
961           case IDM_VIEWKEYS:
962             if (!keylist) {
963                 keylist = CreateDialog (instance, MAKEINTRESOURCE(211),
964                                         NULL, KeyListProc);
965                 ShowWindow (keylist, SW_SHOWNORMAL);
966                 /* 
967                  * Sometimes the window comes up minimised / hidden
968                  * for no obvious reason. Prevent this.
969                  */
970                 SetForegroundWindow(keylist);
971                 SetWindowPos (keylist, HWND_TOP, 0, 0, 0, 0,
972                               SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
973             }
974             break;
975           case IDM_ADDKEY:
976             prompt_add_keyfile();
977             break;
978           case IDM_ABOUT:
979             if (!aboutbox) {
980                 aboutbox = CreateDialog (instance, MAKEINTRESOURCE(213),
981                                          NULL, AboutProc);
982                 ShowWindow (aboutbox, SW_SHOWNORMAL);
983                 /* 
984                  * Sometimes the window comes up minimised / hidden
985                  * for no obvious reason. Prevent this.
986                  */
987                 SetForegroundWindow(aboutbox);
988                 SetWindowPos (aboutbox, HWND_TOP, 0, 0, 0, 0,
989                               SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
990             }
991             break;
992         }
993         break;
994       case WM_DESTROY:
995         PostQuitMessage (0);
996         return 0;
997       case WM_COPYDATA:
998         {
999             COPYDATASTRUCT *cds;
1000             char *mapname;
1001             void *p;
1002             HANDLE filemap, proc;
1003             PSID mapowner, procowner;
1004             PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1005             int ret = 0;
1006
1007             cds = (COPYDATASTRUCT *)lParam;
1008             if (cds->dwData != AGENT_COPYDATA_ID)
1009                 return 0;              /* not our message, mate */
1010             mapname = (char *)cds->lpData;
1011             if (mapname[cds->cbData - 1] != '\0')
1012                 return 0;              /* failure to be ASCIZ! */
1013 #ifdef DEBUG_IPC
1014             debug(("mapname is :%s:\r\n", mapname));
1015 #endif
1016             filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1017 #ifdef DEBUG_IPC
1018             debug(("filemap is %p\r\n", filemap));
1019 #endif
1020             if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1021                 int rc;
1022 #ifndef NO_SECURITY
1023                 if (has_security) {
1024                     if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1025                                             GetCurrentProcessId())) == NULL) {
1026 #ifdef DEBUG_IPC
1027                         debug(("couldn't get handle for process\r\n"));
1028 #endif
1029                         return 0;
1030                     }
1031                     if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1032                                         OWNER_SECURITY_INFORMATION,
1033                                         &procowner, NULL, NULL, NULL,
1034                                         &psd2) != ERROR_SUCCESS) {
1035 #ifdef DEBUG_IPC
1036                         debug(("couldn't get owner info for process\r\n"));
1037 #endif
1038                         CloseHandle(proc);
1039                         return 0;          /* unable to get security info */
1040                     }
1041                     CloseHandle(proc);
1042                     if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1043                                               OWNER_SECURITY_INFORMATION,
1044                                               &mapowner, NULL, NULL, NULL,
1045                                               &psd1) != ERROR_SUCCESS)) {
1046 #ifdef DEBUG_IPC
1047                         debug(("couldn't get owner info for filemap: %d\r\n", rc));
1048 #endif
1049                         return 0;
1050                     }
1051 #ifdef DEBUG_IPC
1052                     debug(("got security stuff\r\n"));
1053 #endif
1054                     if (!EqualSid(mapowner, procowner))
1055                         return 0;          /* security ID mismatch! */
1056 #ifdef DEBUG_IPC
1057                     debug(("security stuff matched\r\n"));
1058 #endif
1059                     LocalFree(psd1);
1060                     LocalFree(psd2);
1061                 } else {
1062 #ifdef DEBUG_IPC
1063                     debug(("security APIs not present\r\n"));
1064 #endif
1065                 }
1066 #endif
1067                 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1068 #ifdef DEBUG_IPC
1069                 debug(("p is %p\r\n", p));
1070                 {int i; for(i=0;i<5;i++)debug(("p[%d]=%02x\r\n", i, ((unsigned char *)p)[i]));}
1071 #endif
1072                 answer_msg(p);
1073                 ret = 1;
1074                 UnmapViewOfFile(p);
1075             }
1076             CloseHandle(filemap);
1077             return ret;
1078         }
1079     }
1080
1081     return DefWindowProc (hwnd, message, wParam, lParam);
1082 }
1083
1084 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
1085     WNDCLASS wndclass;
1086     MSG msg;
1087     OSVERSIONINFO osi;
1088     HMODULE advapi;
1089
1090     /*
1091      * Determine whether we're an NT system (should have security
1092      * APIs) or a non-NT system (don't do security).
1093      */
1094     memset(&osi, 0, sizeof(OSVERSIONINFO));
1095     osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1096     if (GetVersionEx(&osi) && osi.dwPlatformId==VER_PLATFORM_WIN32_NT) {
1097         has_security = TRUE;
1098     } else
1099         has_security = FALSE;
1100
1101     if (has_security) {
1102 #ifndef NO_SECURITY
1103         /*
1104          * Attempt to ge the security API we need.
1105          */
1106         advapi = LoadLibrary("ADVAPI32.DLL");
1107         getsecurityinfo = (gsi_fn_t)GetProcAddress(advapi, "GetSecurityInfo");
1108         if (!getsecurityinfo) {
1109             MessageBox(NULL,
1110                        "Unable to access security APIs. Pageant will\n"
1111                        "not run, in case it causes a security breach.",
1112                        "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1113             return 1;
1114         }
1115 #else
1116         MessageBox(NULL,
1117                    "This program has been compiled for Win9X and will\n"
1118                    "not run on NT, in case it causes a security breach.",
1119                    "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1120         return 1;
1121 #endif
1122     } else
1123         advapi = NULL;
1124
1125     /*
1126      * First bomb out totally if we are already running.
1127      */
1128     if (FindWindow("Pageant", "Pageant")) {
1129         MessageBox(NULL, "Pageant is already running", "Pageant Error",
1130                    MB_ICONERROR | MB_OK);
1131         if (advapi) FreeLibrary(advapi);
1132         return 0;
1133     }
1134
1135     instance = inst;
1136
1137     if (!prev) {
1138         wndclass.style         = 0;
1139         wndclass.lpfnWndProc   = WndProc;
1140         wndclass.cbClsExtra    = 0;
1141         wndclass.cbWndExtra    = 0;
1142         wndclass.hInstance     = inst;
1143         wndclass.hIcon         = LoadIcon (inst,
1144                                            MAKEINTRESOURCE(IDI_MAINICON));
1145         wndclass.hCursor       = LoadCursor (NULL, IDC_IBEAM);
1146         wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
1147         wndclass.lpszMenuName  = NULL;
1148         wndclass.lpszClassName = APPNAME;
1149
1150         RegisterClass (&wndclass);
1151     }
1152
1153     hwnd = keylist = NULL;
1154
1155     hwnd = CreateWindow (APPNAME, APPNAME,
1156                          WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1157                          CW_USEDEFAULT, CW_USEDEFAULT,
1158                          100, 100, NULL, NULL, inst, NULL);
1159
1160     /* Set up a system tray icon */
1161     {
1162         BOOL res;
1163         NOTIFYICONDATA tnid;
1164         HICON hicon;
1165
1166 #ifdef NIM_SETVERSION
1167         tnid.uVersion = 0;
1168         res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1169 #endif
1170
1171         tnid.cbSize = sizeof(NOTIFYICONDATA); 
1172         tnid.hWnd = hwnd; 
1173         tnid.uID = 1;                  /* unique within this systray use */
1174         tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; 
1175         tnid.uCallbackMessage = WM_SYSTRAY;
1176         tnid.hIcon = hicon = LoadIcon (instance, MAKEINTRESOURCE(201));
1177         strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1178
1179         res = Shell_NotifyIcon(NIM_ADD, &tnid); 
1180
1181         if (hicon) 
1182             DestroyIcon(hicon); 
1183
1184         systray_menu = CreatePopupMenu();
1185         /* accelerators used: vkxa */
1186         AppendMenu (systray_menu, MF_ENABLED, IDM_VIEWKEYS, "&View Keys");
1187         AppendMenu (systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1188         AppendMenu (systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1189         AppendMenu (systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1190     }
1191
1192     ShowWindow (hwnd, SW_HIDE);
1193
1194     /*
1195      * Initialise storage for RSA keys.
1196      */
1197     rsakeys = newtree234(cmpkeys_rsa);
1198     ssh2keys = newtree234(cmpkeys_ssh2);
1199
1200     /*
1201      * Process the command line and add keys as listed on it.
1202      */
1203     {
1204         char *p;
1205         int inquotes = 0;
1206         p = cmdline;
1207         while (*p) {
1208             while (*p && isspace(*p)) p++;
1209             if (*p && !isspace(*p)) {
1210                 char *q = p, *pp = p;
1211                 while (*p && (inquotes || !isspace(*p)))
1212                 {
1213                     if (*p == '"') {
1214                         inquotes = !inquotes;
1215                         p++;
1216                         continue;
1217                     }
1218                     *pp++ = *p++;
1219                 }
1220                 if (*pp) {
1221                     if (*p) p++;
1222                     *pp++ = '\0';
1223                 }
1224                 add_keyfile(q);
1225             }
1226         }
1227     }
1228
1229     /*
1230      * Main message loop.
1231      */
1232     while (GetMessage(&msg, NULL, 0, 0) == 1) {
1233         TranslateMessage(&msg);
1234         DispatchMessage(&msg);
1235     }
1236
1237     /* Clean up the system tray icon */
1238     {
1239         NOTIFYICONDATA tnid;
1240
1241         tnid.cbSize = sizeof(NOTIFYICONDATA); 
1242         tnid.hWnd = hwnd;
1243         tnid.uID = 1;
1244
1245         Shell_NotifyIcon(NIM_DELETE, &tnid); 
1246
1247         DestroyMenu(systray_menu);
1248     }
1249
1250     if (advapi) FreeLibrary(advapi);
1251     exit(msg.wParam);
1252 }