]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - pageant.c
Cross-platform support for speaking SSH agent protocol on a Socket.
[PuTTY.git] / pageant.c
1 /*
2  * pageant.c: cross-platform code to implement Pageant.
3  */
4
5 #include <stddef.h>
6 #include <stdlib.h>
7 #include <assert.h>
8
9 #include "putty.h"
10 #include "ssh.h"
11 #include "pageant.h"
12
13 /*
14  * We need this to link with the RSA code, because rsaencrypt()
15  * pads its data with random bytes. Since we only use rsadecrypt()
16  * and the signing functions, which are deterministic, this should
17  * never be called.
18  *
19  * If it _is_ called, there is a _serious_ problem, because it
20  * won't generate true random numbers. So we must scream, panic,
21  * and exit immediately if that should happen.
22  */
23 int random_byte(void)
24 {
25     modalfatalbox("Internal error: attempt to use random numbers in Pageant");
26     exit(0);
27     return 0;                 /* unreachable, but placate optimiser */
28 }
29
30 /*
31  * rsakeys stores SSH-1 RSA keys. ssh2keys stores all SSH-2 keys.
32  */
33 static tree234 *rsakeys, *ssh2keys;
34
35 /*
36  * Blob structure for passing to the asymmetric SSH-2 key compare
37  * function, prototyped here.
38  */
39 struct blob {
40     const unsigned char *blob;
41     int len;
42 };
43 static int cmpkeys_ssh2_asymm(void *av, void *bv);
44
45 /*
46  * Key comparison function for the 2-3-4 tree of RSA keys.
47  */
48 static int cmpkeys_rsa(void *av, void *bv)
49 {
50     struct RSAKey *a = (struct RSAKey *) av;
51     struct RSAKey *b = (struct RSAKey *) bv;
52     Bignum am, bm;
53     int alen, blen;
54
55     am = a->modulus;
56     bm = b->modulus;
57     /*
58      * Compare by length of moduli.
59      */
60     alen = bignum_bitcount(am);
61     blen = bignum_bitcount(bm);
62     if (alen > blen)
63         return +1;
64     else if (alen < blen)
65         return -1;
66     /*
67      * Now compare by moduli themselves.
68      */
69     alen = (alen + 7) / 8;             /* byte count */
70     while (alen-- > 0) {
71         int abyte, bbyte;
72         abyte = bignum_byte(am, alen);
73         bbyte = bignum_byte(bm, alen);
74         if (abyte > bbyte)
75             return +1;
76         else if (abyte < bbyte)
77             return -1;
78     }
79     /*
80      * Give up.
81      */
82     return 0;
83 }
84
85 /*
86  * Key comparison function for the 2-3-4 tree of SSH-2 keys.
87  */
88 static int cmpkeys_ssh2(void *av, void *bv)
89 {
90     struct ssh2_userkey *a = (struct ssh2_userkey *) av;
91     struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
92     int i;
93     int alen, blen;
94     unsigned char *ablob, *bblob;
95     int c;
96
97     /*
98      * Compare purely by public blob.
99      */
100     ablob = a->alg->public_blob(a->data, &alen);
101     bblob = b->alg->public_blob(b->data, &blen);
102
103     c = 0;
104     for (i = 0; i < alen && i < blen; i++) {
105         if (ablob[i] < bblob[i]) {
106             c = -1;
107             break;
108         } else if (ablob[i] > bblob[i]) {
109             c = +1;
110             break;
111         }
112     }
113     if (c == 0 && i < alen)
114         c = +1;                        /* a is longer */
115     if (c == 0 && i < blen)
116         c = -1;                        /* a is longer */
117
118     sfree(ablob);
119     sfree(bblob);
120
121     return c;
122 }
123
124 /*
125  * Key comparison function for looking up a blob in the 2-3-4 tree
126  * of SSH-2 keys.
127  */
128 static int cmpkeys_ssh2_asymm(void *av, void *bv)
129 {
130     struct blob *a = (struct blob *) av;
131     struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
132     int i;
133     int alen, blen;
134     const unsigned char *ablob;
135     unsigned char *bblob;
136     int c;
137
138     /*
139      * Compare purely by public blob.
140      */
141     ablob = a->blob;
142     alen = a->len;
143     bblob = b->alg->public_blob(b->data, &blen);
144
145     c = 0;
146     for (i = 0; i < alen && i < blen; i++) {
147         if (ablob[i] < bblob[i]) {
148             c = -1;
149             break;
150         } else if (ablob[i] > bblob[i]) {
151             c = +1;
152             break;
153         }
154     }
155     if (c == 0 && i < alen)
156         c = +1;                        /* a is longer */
157     if (c == 0 && i < blen)
158         c = -1;                        /* a is longer */
159
160     sfree(bblob);
161
162     return c;
163 }
164
165 /*
166  * Create an SSH-1 key list in a malloc'ed buffer; return its
167  * length.
168  */
169 void *pageant_make_keylist1(int *length)
170 {
171     int i, nkeys, len;
172     struct RSAKey *key;
173     unsigned char *blob, *p, *ret;
174     int bloblen;
175
176     /*
177      * Count up the number and length of keys we hold.
178      */
179     len = 4;
180     nkeys = 0;
181     for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
182         nkeys++;
183         blob = rsa_public_blob(key, &bloblen);
184         len += bloblen;
185         sfree(blob);
186         len += 4 + strlen(key->comment);
187     }
188
189     /* Allocate the buffer. */
190     p = ret = snewn(len, unsigned char);
191     if (length) *length = len;
192
193     PUT_32BIT(p, nkeys);
194     p += 4;
195     for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
196         blob = rsa_public_blob(key, &bloblen);
197         memcpy(p, blob, bloblen);
198         p += bloblen;
199         sfree(blob);
200         PUT_32BIT(p, strlen(key->comment));
201         memcpy(p + 4, key->comment, strlen(key->comment));
202         p += 4 + strlen(key->comment);
203     }
204
205     assert(p - ret == len);
206     return ret;
207 }
208
209 /*
210  * Create an SSH-2 key list in a malloc'ed buffer; return its
211  * length.
212  */
213 void *pageant_make_keylist2(int *length)
214 {
215     struct ssh2_userkey *key;
216     int i, len, nkeys;
217     unsigned char *blob, *p, *ret;
218     int bloblen;
219
220     /*
221      * Count up the number and length of keys we hold.
222      */
223     len = 4;
224     nkeys = 0;
225     for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
226         nkeys++;
227         len += 4;              /* length field */
228         blob = key->alg->public_blob(key->data, &bloblen);
229         len += bloblen;
230         sfree(blob);
231         len += 4 + strlen(key->comment);
232     }
233
234     /* Allocate the buffer. */
235     p = ret = snewn(len, unsigned char);
236     if (length) *length = len;
237
238     /*
239      * Packet header is the obvious five bytes, plus four
240      * bytes for the key count.
241      */
242     PUT_32BIT(p, nkeys);
243     p += 4;
244     for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
245         blob = key->alg->public_blob(key->data, &bloblen);
246         PUT_32BIT(p, bloblen);
247         p += 4;
248         memcpy(p, blob, bloblen);
249         p += bloblen;
250         sfree(blob);
251         PUT_32BIT(p, strlen(key->comment));
252         memcpy(p + 4, key->comment, strlen(key->comment));
253         p += 4 + strlen(key->comment);
254     }
255
256     assert(p - ret == len);
257     return ret;
258 }
259
260 void *pageant_handle_msg(const void *msg, int msglen, int *outlen)
261 {
262     const unsigned char *p = msg;
263     const unsigned char *msgend;
264     unsigned char *ret = snewn(AGENT_MAX_MSGLEN, unsigned char);
265     int type;
266
267     msgend = p + msglen;
268
269     /*
270      * Get the message type.
271      */
272     if (msgend < p+1)
273         goto failure;
274     type = *p++;
275
276     switch (type) {
277       case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
278         /*
279          * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
280          */
281         {
282             int len;
283             void *keylist;
284
285             ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
286             keylist = pageant_make_keylist1(&len);
287             if (len + 5 > AGENT_MAX_MSGLEN) {
288                 sfree(keylist);
289                 goto failure;
290             }
291             PUT_32BIT(ret, len + 1);
292             memcpy(ret + 5, keylist, len);
293             sfree(keylist);
294         }
295         break;
296       case SSH2_AGENTC_REQUEST_IDENTITIES:
297         /*
298          * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
299          */
300         {
301             int len;
302             void *keylist;
303
304             ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
305             keylist = pageant_make_keylist2(&len);
306             if (len + 5 > AGENT_MAX_MSGLEN) {
307                 sfree(keylist);
308                 goto failure;
309             }
310             PUT_32BIT(ret, len + 1);
311             memcpy(ret + 5, keylist, len);
312             sfree(keylist);
313         }
314         break;
315       case SSH1_AGENTC_RSA_CHALLENGE:
316         /*
317          * Reply with either SSH1_AGENT_RSA_RESPONSE or
318          * SSH_AGENT_FAILURE, depending on whether we have that key
319          * or not.
320          */
321         {
322             struct RSAKey reqkey, *key;
323             Bignum challenge, response;
324             unsigned char response_source[48], response_md5[16];
325             struct MD5Context md5c;
326             int i, len;
327
328             p += 4;
329             i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
330             if (i < 0)
331                 goto failure;
332             p += i;
333             i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
334             if (i < 0) {
335                 freebn(reqkey.exponent);
336                 goto failure;
337             }
338             p += i;
339             i = ssh1_read_bignum(p, msgend - p, &challenge);
340             if (i < 0) {
341                 freebn(reqkey.exponent);
342                 freebn(reqkey.modulus);
343                 goto failure;
344             }
345             p += i;
346             if (msgend < p+16) {
347                 freebn(reqkey.exponent);
348                 freebn(reqkey.modulus);
349                 freebn(challenge);
350                 goto failure;
351             }
352             memcpy(response_source + 32, p, 16);
353             p += 16;
354             if (msgend < p+4 ||
355                 GET_32BIT(p) != 1 ||
356                 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
357                 freebn(reqkey.exponent);
358                 freebn(reqkey.modulus);
359                 freebn(challenge);
360                 goto failure;
361             }
362             response = rsadecrypt(challenge, key);
363             for (i = 0; i < 32; i++)
364                 response_source[i] = bignum_byte(response, 31 - i);
365
366             MD5Init(&md5c);
367             MD5Update(&md5c, response_source, 48);
368             MD5Final(response_md5, &md5c);
369             smemclr(response_source, 48);       /* burn the evidence */
370             freebn(response);          /* and that evidence */
371             freebn(challenge);         /* yes, and that evidence */
372             freebn(reqkey.exponent);   /* and free some memory ... */
373             freebn(reqkey.modulus);    /* ... while we're at it. */
374
375             /*
376              * Packet is the obvious five byte header, plus sixteen
377              * bytes of MD5.
378              */
379             len = 5 + 16;
380             PUT_32BIT(ret, len - 4);
381             ret[4] = SSH1_AGENT_RSA_RESPONSE;
382             memcpy(ret + 5, response_md5, 16);
383         }
384         break;
385       case SSH2_AGENTC_SIGN_REQUEST:
386         /*
387          * Reply with either SSH2_AGENT_SIGN_RESPONSE or
388          * SSH_AGENT_FAILURE, depending on whether we have that key
389          * or not.
390          */
391         {
392             struct ssh2_userkey *key;
393             struct blob b;
394             const unsigned char *data;
395             unsigned char *signature;
396             int datalen, siglen, len;
397
398             if (msgend < p+4)
399                 goto failure;
400             b.len = toint(GET_32BIT(p));
401             if (b.len < 0 || b.len > msgend - (p+4))
402                 goto failure;
403             p += 4;
404             b.blob = p;
405             p += b.len;
406             if (msgend < p+4)
407                 goto failure;
408             datalen = toint(GET_32BIT(p));
409             p += 4;
410             if (datalen < 0 || datalen > msgend - p)
411                 goto failure;
412             data = p;
413             key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
414             if (!key)
415                 goto failure;
416             signature = key->alg->sign(key->data, (const char *)data,
417                                        datalen, &siglen);
418             len = 5 + 4 + siglen;
419             PUT_32BIT(ret, len - 4);
420             ret[4] = SSH2_AGENT_SIGN_RESPONSE;
421             PUT_32BIT(ret + 5, siglen);
422             memcpy(ret + 5 + 4, signature, siglen);
423             sfree(signature);
424         }
425         break;
426       case SSH1_AGENTC_ADD_RSA_IDENTITY:
427         /*
428          * Add to the list and return SSH_AGENT_SUCCESS, or
429          * SSH_AGENT_FAILURE if the key was malformed.
430          */
431         {
432             struct RSAKey *key;
433             char *comment;
434             int n, commentlen;
435
436             key = snew(struct RSAKey);
437             memset(key, 0, sizeof(struct RSAKey));
438
439             n = makekey(p, msgend - p, key, NULL, 1);
440             if (n < 0) {
441                 freersakey(key);
442                 sfree(key);
443                 goto failure;
444             }
445             p += n;
446
447             n = makeprivate(p, msgend - p, key);
448             if (n < 0) {
449                 freersakey(key);
450                 sfree(key);
451                 goto failure;
452             }
453             p += n;
454
455             n = ssh1_read_bignum(p, msgend - p, &key->iqmp);  /* p^-1 mod q */
456             if (n < 0) {
457                 freersakey(key);
458                 sfree(key);
459                 goto failure;
460             }
461             p += n;
462
463             n = ssh1_read_bignum(p, msgend - p, &key->p);  /* p */
464             if (n < 0) {
465                 freersakey(key);
466                 sfree(key);
467                 goto failure;
468             }
469             p += n;
470
471             n = ssh1_read_bignum(p, msgend - p, &key->q);  /* q */
472             if (n < 0) {
473                 freersakey(key);
474                 sfree(key);
475                 goto failure;
476             }
477             p += n;
478
479             if (msgend < p+4) {
480                 freersakey(key);
481                 sfree(key);
482                 goto failure;
483             }
484             commentlen = toint(GET_32BIT(p));
485
486             if (commentlen < 0 || commentlen > msgend - p) {
487                 freersakey(key);
488                 sfree(key);
489                 goto failure;
490             }
491
492             comment = snewn(commentlen+1, char);
493             if (comment) {
494                 memcpy(comment, p + 4, commentlen);
495                 comment[commentlen] = '\0';
496                 key->comment = comment;
497             }
498             PUT_32BIT(ret, 1);
499             ret[4] = SSH_AGENT_FAILURE;
500             if (add234(rsakeys, key) == key) {
501                 keylist_update();
502                 ret[4] = SSH_AGENT_SUCCESS;
503             } else {
504                 freersakey(key);
505                 sfree(key);
506             }
507         }
508         break;
509       case SSH2_AGENTC_ADD_IDENTITY:
510         /*
511          * Add to the list and return SSH_AGENT_SUCCESS, or
512          * SSH_AGENT_FAILURE if the key was malformed.
513          */
514         {
515             struct ssh2_userkey *key;
516             char *comment;
517             const char *alg;
518             int alglen, commlen;
519             int bloblen;
520
521
522             if (msgend < p+4)
523                 goto failure;
524             alglen = toint(GET_32BIT(p));
525             p += 4;
526             if (alglen < 0 || alglen > msgend - p)
527                 goto failure;
528             alg = (const char *)p;
529             p += alglen;
530
531             key = snew(struct ssh2_userkey);
532             /* Add further algorithm names here. */
533             if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
534                 key->alg = &ssh_rsa;
535             else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
536                 key->alg = &ssh_dss;
537             else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp256", 19))
538                 key->alg = &ssh_ecdsa_nistp256;
539             else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp384", 19))
540                 key->alg = &ssh_ecdsa_nistp384;
541             else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp521", 19))
542                 key->alg = &ssh_ecdsa_nistp521;
543             else {
544                 sfree(key);
545                 goto failure;
546             }
547
548             bloblen = msgend - p;
549             key->data = key->alg->openssh_createkey(&p, &bloblen);
550             if (!key->data) {
551                 sfree(key);
552                 goto failure;
553             }
554
555             /*
556              * p has been advanced by openssh_createkey, but
557              * certainly not _beyond_ the end of the buffer.
558              */
559             assert(p <= msgend);
560
561             if (msgend < p+4) {
562                 key->alg->freekey(key->data);
563                 sfree(key);
564                 goto failure;
565             }
566             commlen = toint(GET_32BIT(p));
567             p += 4;
568
569             if (commlen < 0 || commlen > msgend - p) {
570                 key->alg->freekey(key->data);
571                 sfree(key);
572                 goto failure;
573             }
574             comment = snewn(commlen + 1, char);
575             if (comment) {
576                 memcpy(comment, p, commlen);
577                 comment[commlen] = '\0';
578             }
579             key->comment = comment;
580
581             PUT_32BIT(ret, 1);
582             ret[4] = SSH_AGENT_FAILURE;
583             if (add234(ssh2keys, key) == key) {
584                 keylist_update();
585                 ret[4] = SSH_AGENT_SUCCESS;
586             } else {
587                 key->alg->freekey(key->data);
588                 sfree(key->comment);
589                 sfree(key);
590             }
591         }
592         break;
593       case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
594         /*
595          * Remove from the list and return SSH_AGENT_SUCCESS, or
596          * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
597          * start with.
598          */
599         {
600             struct RSAKey reqkey, *key;
601             int n;
602
603             n = makekey(p, msgend - p, &reqkey, NULL, 0);
604             if (n < 0)
605                 goto failure;
606
607             key = find234(rsakeys, &reqkey, NULL);
608             freebn(reqkey.exponent);
609             freebn(reqkey.modulus);
610             PUT_32BIT(ret, 1);
611             ret[4] = SSH_AGENT_FAILURE;
612             if (key) {
613                 del234(rsakeys, key);
614                 keylist_update();
615                 freersakey(key);
616                 sfree(key);
617                 ret[4] = SSH_AGENT_SUCCESS;
618             }
619         }
620         break;
621       case SSH2_AGENTC_REMOVE_IDENTITY:
622         /*
623          * Remove from the list and return SSH_AGENT_SUCCESS, or
624          * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
625          * start with.
626          */
627         {
628             struct ssh2_userkey *key;
629             struct blob b;
630
631             if (msgend < p+4)
632                 goto failure;
633             b.len = toint(GET_32BIT(p));
634             p += 4;
635
636             if (b.len < 0 || b.len > msgend - p)
637                 goto failure;
638             b.blob = p;
639             p += b.len;
640
641             key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
642             if (!key)
643                 goto failure;
644
645             PUT_32BIT(ret, 1);
646             ret[4] = SSH_AGENT_FAILURE;
647             if (key) {
648                 del234(ssh2keys, key);
649                 keylist_update();
650                 key->alg->freekey(key->data);
651                 sfree(key);
652                 ret[4] = SSH_AGENT_SUCCESS;
653             }
654         }
655         break;
656       case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
657         /*
658          * Remove all SSH-1 keys. Always returns success.
659          */
660         {
661             struct RSAKey *rkey;
662
663             while ((rkey = index234(rsakeys, 0)) != NULL) {
664                 del234(rsakeys, rkey);
665                 freersakey(rkey);
666                 sfree(rkey);
667             }
668             keylist_update();
669
670             PUT_32BIT(ret, 1);
671             ret[4] = SSH_AGENT_SUCCESS;
672         }
673         break;
674       case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
675         /*
676          * Remove all SSH-2 keys. Always returns success.
677          */
678         {
679             struct ssh2_userkey *skey;
680
681             while ((skey = index234(ssh2keys, 0)) != NULL) {
682                 del234(ssh2keys, skey);
683                 skey->alg->freekey(skey->data);
684                 sfree(skey);
685             }
686             keylist_update();
687
688             PUT_32BIT(ret, 1);
689             ret[4] = SSH_AGENT_SUCCESS;
690         }
691         break;
692       default:
693       failure:
694         /*
695          * Unrecognised message. Return SSH_AGENT_FAILURE.
696          */
697         PUT_32BIT(ret, 1);
698         ret[4] = SSH_AGENT_FAILURE;
699         break;
700     }
701
702     *outlen = 4 + GET_32BIT(ret);
703     return ret;
704 }
705
706 void *pageant_failure_msg(int *outlen)
707 {
708     unsigned char *ret = snewn(5, unsigned char);
709     PUT_32BIT(ret, 1);
710     ret[4] = SSH_AGENT_FAILURE;
711     *outlen = 5;
712     return ret;
713 }
714
715 void pageant_init(void)
716 {
717     rsakeys = newtree234(cmpkeys_rsa);
718     ssh2keys = newtree234(cmpkeys_ssh2);
719 }
720
721 struct RSAKey *pageant_nth_ssh1_key(int i)
722 {
723     return index234(rsakeys, i);
724 }
725
726 struct ssh2_userkey *pageant_nth_ssh2_key(int i)
727 {
728     return index234(ssh2keys, i);
729 }
730
731 int pageant_count_ssh1_keys(void)
732 {
733     return count234(rsakeys);
734 }
735
736 int pageant_count_ssh2_keys(void)
737 {
738     return count234(ssh2keys);
739 }
740
741 int pageant_add_ssh1_key(struct RSAKey *rkey)
742 {
743     return add234(rsakeys, rkey) != rkey;
744 }
745
746 int pageant_add_ssh2_key(struct ssh2_userkey *skey)
747 {
748     return add234(ssh2keys, skey) != skey;
749 }
750
751 int pageant_delete_ssh1_key(struct RSAKey *rkey)
752 {
753     struct RSAKey *deleted = del234(rsakeys, rkey);
754     if (!deleted)
755         return FALSE;
756     assert(deleted == rkey);
757     return TRUE;
758 }
759
760 int pageant_delete_ssh2_key(struct ssh2_userkey *skey)
761 {
762     struct ssh2_userkey *deleted = del234(ssh2keys, skey);
763     if (!deleted)
764         return FALSE;
765     assert(deleted == skey);
766     return TRUE;
767 }
768
769 /* ----------------------------------------------------------------------
770  * The agent plug.
771  */
772
773 /*
774  * Coroutine macros similar to, but simplified from, those in ssh.c.
775  */
776 #define crBegin(v)      { int *crLine = &v; switch(v) { case 0:;
777 #define crFinish(z)     } *crLine = 0; return (z); }
778 #define crGetChar(c) do                                         \
779     {                                                           \
780         while (len == 0) {                                      \
781             *crLine =__LINE__; return 1; case __LINE__:;        \
782         }                                                       \
783         len--;                                                  \
784         (c) = (unsigned char)*data++;                           \
785     } while (0)
786
787 struct pageant_conn_state {
788     const struct plug_function_table *fn;
789     /* the above variable absolutely *must* be the first in this structure */
790
791     Socket connsock;
792     void *logctx;
793     void (*logfn)(void *logctx, const char *fmt, ...);
794     unsigned char lenbuf[4], pktbuf[AGENT_MAX_MSGLEN];
795     unsigned len, got;
796     int real_packet;
797     int crLine;            /* for coroutine in pageant_conn_receive */
798 };
799
800 static int pageant_conn_closing(Plug plug, const char *error_msg,
801                                 int error_code, int calling_back)
802 {
803     struct pageant_conn_state *pc = (struct pageant_conn_state *)plug;
804     if (error_msg && pc->logfn)
805         pc->logfn(pc->logctx, "Pageant connection socket: %s", error_msg);
806     sk_close(pc->connsock);
807     sfree(pc);
808     return 1;
809 }
810
811 static void pageant_conn_sent(Plug plug, int bufsize)
812 {
813     /* struct pageant_conn_state *pc = (struct pageant_conn_state *)plug; */
814
815     /*
816      * We do nothing here, because we expect that there won't be a
817      * need to throttle and unthrottle the connection to an agent -
818      * clients will typically not send many requests, and will wait
819      * until they receive each reply before sending a new request.
820      */
821 }
822
823 static int pageant_conn_receive(Plug plug, int urgent, char *data, int len)
824 {
825     struct pageant_conn_state *pc = (struct pageant_conn_state *)plug;
826     char c;
827
828     crBegin(pc->crLine);
829
830     while (len > 0) {
831         pc->got = 0;
832         while (pc->got < 4) {
833             crGetChar(c);
834             pc->lenbuf[pc->got++] = c;
835         }
836
837         pc->len = GET_32BIT(pc->lenbuf);
838         pc->got = 0;
839         pc->real_packet = (pc->len < AGENT_MAX_MSGLEN-4);
840
841         while (pc->got < pc->len) {
842             crGetChar(c);
843             if (pc->real_packet)
844                 pc->pktbuf[pc->got] = c;
845             pc->got++;
846         }
847
848         {
849             void *reply;
850             int replylen;
851
852             if (pc->real_packet) {
853                 reply = pageant_handle_msg(pc->pktbuf, pc->len, &replylen);
854             } else {
855                 reply = pageant_failure_msg(&replylen);
856             }
857             sk_write(pc->connsock, reply, replylen);
858             smemclr(reply, replylen);
859         }
860     }
861
862     crFinish(1);
863 }
864
865 struct pageant_listen_state {
866     const struct plug_function_table *fn;
867     /* the above variable absolutely *must* be the first in this structure */
868
869     Socket listensock;
870     void *logctx;
871     void (*logfn)(void *logctx, const char *fmt, ...);
872 };
873
874 static int pageant_listen_closing(Plug plug, const char *error_msg,
875                                   int error_code, int calling_back)
876 {
877     struct pageant_listen_state *pl = (struct pageant_listen_state *)plug;
878     if (error_msg && pl->logfn)
879         pl->logfn(pl->logctx, "Pageant listening socket: %s", error_msg);
880     sk_close(pl->listensock);
881     pl->listensock = NULL;
882     return 1;
883 }
884
885 static int pageant_listen_accepting(Plug plug,
886                                     accept_fn_t constructor, accept_ctx_t ctx)
887 {
888     static const struct plug_function_table connection_fn_table = {
889         NULL, /* no log function, because that's for outgoing connections */
890         pageant_conn_closing,
891         pageant_conn_receive,
892         pageant_conn_sent,
893         NULL /* no accepting function, because we've already done it */
894     };
895     struct pageant_listen_state *pl = (struct pageant_listen_state *)plug;
896     struct pageant_conn_state *pc;
897     const char *err;
898
899     pc = snew(struct pageant_conn_state);
900     pc->fn = &connection_fn_table;
901     pc->logfn = pl->logfn;
902     pc->logctx = pl->logctx;
903     pc->crLine = 0;
904
905     pc->connsock = constructor(ctx, (Plug) pc);
906     if ((err = sk_socket_error(pc->connsock)) != NULL) {
907         sk_close(pc->connsock);
908         sfree(pc);
909         return TRUE;
910     }
911
912     sk_set_frozen(pc->connsock, 0);
913
914     /* FIXME: can we get any useful peer id info? */
915     if (pl->logfn)
916         pl->logfn(pl->logctx, "Pageant socket connected");
917
918     return 0;
919 }
920
921 struct pageant_listen_state *pageant_listener_new
922 (void *logctx, void (*logfn)(void *logctx, const char *fmt, ...))
923 {
924     static const struct plug_function_table listener_fn_table = {
925         NULL, /* no log function, because that's for outgoing connections */
926         pageant_listen_closing,
927         NULL, /* no receive function on a listening socket */
928         NULL, /* no sent function on a listening socket */
929         pageant_listen_accepting
930     };
931
932     struct pageant_listen_state *pl = snew(struct pageant_listen_state);
933     pl->fn = &listener_fn_table;
934     pl->logctx = logctx;
935     pl->logfn = logfn;
936     pl->listensock = NULL;
937     return pl;
938 }
939
940 void pageant_listener_got_socket(struct pageant_listen_state *pl, Socket sock)
941 {
942     pl->listensock = sock;
943 }
944
945 void pageant_listener_free(struct pageant_listen_state *pl)
946 {
947     if (pl->listensock)
948         sk_close(pl->listensock);
949     sfree(pl);
950 }