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