]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - pageant.c
Clean up Unix Pageant's setup and teardown.
[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 char *fingerprint_ssh2_blob(const void *blob, int bloblen)
261 {
262     unsigned char digest[16];
263     char fingerprint_str[16*3];
264     unsigned stringlen;
265     int i;
266
267     MD5Simple(blob, bloblen, digest);
268     for (i = 0; i < 16; i++)
269         sprintf(fingerprint_str + i*3, "%02x%s", digest[i], i==15 ? "" : ":");
270
271     stringlen = GET_32BIT((const unsigned char *)blob);
272     if (stringlen < bloblen-4)
273         return dupprintf("%.*s %s", (int)stringlen, (const char *)blob + 4,
274                          fingerprint_str);
275     else
276         return dupstr(fingerprint_str);        
277 }
278
279 static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...)
280 #ifdef __GNUC__
281 __attribute__ ((format (printf, 3, 4)))
282 #endif
283     ;
284
285 static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...)
286 {
287     /*
288      * This is the wrapper that takes a variadic argument list and
289      * turns it into the va_list that the log function really expects.
290      * It's safe to call this with logfn==NULL, because we
291      * double-check that below; but if you're going to do lots of work
292      * before getting here (such as looping, or hashing things) then
293      * you should probably check logfn manually before doing that.
294      */
295     if (logfn) {
296         va_list ap;
297         va_start(ap, fmt);
298         logfn(logctx, fmt, ap);
299         va_end(ap);
300     }
301 }
302
303 void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
304                          void *logctx, pageant_logfn_t logfn)
305 {
306     const unsigned char *p = msg;
307     const unsigned char *msgend;
308     unsigned char *ret = snewn(AGENT_MAX_MSGLEN, unsigned char);
309     int type;
310     const char *fail_reason;
311
312     msgend = p + msglen;
313
314     /*
315      * Get the message type.
316      */
317     if (msgend < p+1) {
318         fail_reason = "message contained no type code";
319         goto failure;
320     }
321     type = *p++;
322
323     switch (type) {
324       case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
325         /*
326          * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
327          */
328         {
329             int len;
330             void *keylist;
331
332             plog(logctx, logfn, "request: SSH1_AGENTC_REQUEST_RSA_IDENTITIES");
333
334             ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
335             keylist = pageant_make_keylist1(&len);
336             if (len + 5 > AGENT_MAX_MSGLEN) {
337                 sfree(keylist);
338                 fail_reason = "output would exceed max msglen";
339                 goto failure;
340             }
341             PUT_32BIT(ret, len + 1);
342             memcpy(ret + 5, keylist, len);
343
344             plog(logctx, logfn, "reply: SSH1_AGENT_RSA_IDENTITIES_ANSWER");
345             if (logfn) {               /* skip this loop if not logging */
346                 int i;
347                 struct RSAKey *rkey;
348                 for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) {
349                     char fingerprint[128];
350                     rsa_fingerprint(fingerprint, sizeof(fingerprint), rkey);
351                     plog(logctx, logfn, "returned key: %s", fingerprint);
352                 }
353             }
354             sfree(keylist);
355         }
356         break;
357       case SSH2_AGENTC_REQUEST_IDENTITIES:
358         /*
359          * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
360          */
361         {
362             int len;
363             void *keylist;
364
365             plog(logctx, logfn, "request: SSH2_AGENTC_REQUEST_IDENTITIES");
366
367             ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
368             keylist = pageant_make_keylist2(&len);
369             if (len + 5 > AGENT_MAX_MSGLEN) {
370                 sfree(keylist);
371                 fail_reason = "output would exceed max msglen";
372                 goto failure;
373             }
374             PUT_32BIT(ret, len + 1);
375             memcpy(ret + 5, keylist, len);
376
377             plog(logctx, logfn, "reply: SSH2_AGENT_IDENTITIES_ANSWER");
378             if (logfn) {               /* skip this loop if not logging */
379                 int i;
380                 struct ssh2_userkey *skey;
381                 for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) {
382                     char *fingerprint = skey->alg->fingerprint(skey->data);
383                     plog(logctx, logfn, "returned key: %s %s",
384                          fingerprint, skey->comment);
385                     sfree(fingerprint);
386                 }
387             }
388
389             sfree(keylist);
390         }
391         break;
392       case SSH1_AGENTC_RSA_CHALLENGE:
393         /*
394          * Reply with either SSH1_AGENT_RSA_RESPONSE or
395          * SSH_AGENT_FAILURE, depending on whether we have that key
396          * or not.
397          */
398         {
399             struct RSAKey reqkey, *key;
400             Bignum challenge, response;
401             unsigned char response_source[48], response_md5[16];
402             struct MD5Context md5c;
403             int i, len;
404
405             plog(logctx, logfn, "request: SSH1_AGENTC_RSA_CHALLENGE");
406
407             p += 4;
408             i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
409             if (i < 0) {
410                 fail_reason = "request truncated before key exponent";
411                 goto failure;
412             }
413             p += i;
414             i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
415             if (i < 0) {
416                 freebn(reqkey.exponent);
417                 fail_reason = "request truncated before key modulus";
418                 goto failure;
419             }
420             p += i;
421             i = ssh1_read_bignum(p, msgend - p, &challenge);
422             if (i < 0) {
423                 freebn(reqkey.exponent);
424                 freebn(reqkey.modulus);
425                 fail_reason = "request truncated before challenge";
426                 goto failure;
427             }
428             p += i;
429             if (msgend < p+16) {
430                 freebn(reqkey.exponent);
431                 freebn(reqkey.modulus);
432                 freebn(challenge);
433                 fail_reason = "request truncated before session id";
434                 goto failure;
435             }
436             memcpy(response_source + 32, p, 16);
437             p += 16;
438             if (msgend < p+4) {
439                 freebn(reqkey.exponent);
440                 freebn(reqkey.modulus);
441                 freebn(challenge);
442                 fail_reason = "request truncated before response type";
443                 goto failure;
444             }
445             if (GET_32BIT(p) != 1) {
446                 freebn(reqkey.exponent);
447                 freebn(reqkey.modulus);
448                 freebn(challenge);
449                 fail_reason = "response type other than 1 not supported";
450                 goto failure;
451             }
452             if (logfn) {
453                 char fingerprint[128];
454                 reqkey.comment = NULL;
455                 rsa_fingerprint(fingerprint, sizeof(fingerprint), &reqkey);
456                 plog(logctx, logfn, "requested key: %s", fingerprint);
457             }
458             if ((key = find234(rsakeys, &reqkey, NULL)) == NULL) {
459                 freebn(reqkey.exponent);
460                 freebn(reqkey.modulus);
461                 freebn(challenge);
462                 fail_reason = "key not found";
463                 goto failure;
464             }
465             response = rsadecrypt(challenge, key);
466             for (i = 0; i < 32; i++)
467                 response_source[i] = bignum_byte(response, 31 - i);
468
469             MD5Init(&md5c);
470             MD5Update(&md5c, response_source, 48);
471             MD5Final(response_md5, &md5c);
472             smemclr(response_source, 48);       /* burn the evidence */
473             freebn(response);          /* and that evidence */
474             freebn(challenge);         /* yes, and that evidence */
475             freebn(reqkey.exponent);   /* and free some memory ... */
476             freebn(reqkey.modulus);    /* ... while we're at it. */
477
478             /*
479              * Packet is the obvious five byte header, plus sixteen
480              * bytes of MD5.
481              */
482             len = 5 + 16;
483             PUT_32BIT(ret, len - 4);
484             ret[4] = SSH1_AGENT_RSA_RESPONSE;
485             memcpy(ret + 5, response_md5, 16);
486
487             plog(logctx, logfn, "reply: SSH1_AGENT_RSA_RESPONSE");
488         }
489         break;
490       case SSH2_AGENTC_SIGN_REQUEST:
491         /*
492          * Reply with either SSH2_AGENT_SIGN_RESPONSE or
493          * SSH_AGENT_FAILURE, depending on whether we have that key
494          * or not.
495          */
496         {
497             struct ssh2_userkey *key;
498             struct blob b;
499             const unsigned char *data;
500             unsigned char *signature;
501             int datalen, siglen, len;
502
503             plog(logctx, logfn, "request: SSH2_AGENTC_SIGN_REQUEST");
504
505             if (msgend < p+4) {
506                 fail_reason = "request truncated before public key";
507                 goto failure;
508             }
509             b.len = toint(GET_32BIT(p));
510             if (b.len < 0 || b.len > msgend - (p+4)) {
511                 fail_reason = "request truncated before public key";
512                 goto failure;
513             }
514             p += 4;
515             b.blob = p;
516             p += b.len;
517             if (msgend < p+4) {
518                 fail_reason = "request truncated before string to sign";
519                 goto failure;
520             }
521             datalen = toint(GET_32BIT(p));
522             p += 4;
523             if (datalen < 0 || datalen > msgend - p) {
524                 fail_reason = "request truncated before string to sign";
525                 goto failure;
526             }
527             data = p;
528             if (logfn) {
529                 char *fingerprint = fingerprint_ssh2_blob(b.blob, b.len);
530                 plog(logctx, logfn, "requested key: %s", fingerprint);
531                 sfree(fingerprint);
532             }
533             key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
534             if (!key) {
535                 fail_reason = "key not found";
536                 goto failure;
537             }
538             signature = key->alg->sign(key->data, (const char *)data,
539                                        datalen, &siglen);
540             len = 5 + 4 + siglen;
541             PUT_32BIT(ret, len - 4);
542             ret[4] = SSH2_AGENT_SIGN_RESPONSE;
543             PUT_32BIT(ret + 5, siglen);
544             memcpy(ret + 5 + 4, signature, siglen);
545             sfree(signature);
546
547             plog(logctx, logfn, "reply: SSH2_AGENT_SIGN_RESPONSE");
548         }
549         break;
550       case SSH1_AGENTC_ADD_RSA_IDENTITY:
551         /*
552          * Add to the list and return SSH_AGENT_SUCCESS, or
553          * SSH_AGENT_FAILURE if the key was malformed.
554          */
555         {
556             struct RSAKey *key;
557             char *comment;
558             int n, commentlen;
559
560             plog(logctx, logfn, "request: SSH1_AGENTC_ADD_RSA_IDENTITY");
561
562             key = snew(struct RSAKey);
563             memset(key, 0, sizeof(struct RSAKey));
564
565             n = makekey(p, msgend - p, key, NULL, 1);
566             if (n < 0) {
567                 freersakey(key);
568                 sfree(key);
569                 fail_reason = "request truncated before public key";
570                 goto failure;
571             }
572             p += n;
573
574             n = makeprivate(p, msgend - p, key);
575             if (n < 0) {
576                 freersakey(key);
577                 sfree(key);
578                 fail_reason = "request truncated before private key";
579                 goto failure;
580             }
581             p += n;
582
583             /* SSH-1 names p and q the other way round, i.e. we have
584              * the inverse of p mod q and not of q mod p. We swap the
585              * names, because our internal RSA wants iqmp. */
586
587             n = ssh1_read_bignum(p, msgend - p, &key->iqmp);  /* p^-1 mod q */
588             if (n < 0) {
589                 freersakey(key);
590                 sfree(key);
591                 fail_reason = "request truncated before iqmp";
592                 goto failure;
593             }
594             p += n;
595
596             n = ssh1_read_bignum(p, msgend - p, &key->q);  /* p */
597             if (n < 0) {
598                 freersakey(key);
599                 sfree(key);
600                 fail_reason = "request truncated before p";
601                 goto failure;
602             }
603             p += n;
604
605             n = ssh1_read_bignum(p, msgend - p, &key->p);  /* q */
606             if (n < 0) {
607                 freersakey(key);
608                 sfree(key);
609                 fail_reason = "request truncated before q";
610                 goto failure;
611             }
612             p += n;
613
614             if (msgend < p+4) {
615                 freersakey(key);
616                 sfree(key);
617                 fail_reason = "request truncated before key comment";
618                 goto failure;
619             }
620             commentlen = toint(GET_32BIT(p));
621
622             if (commentlen < 0 || commentlen > msgend - p) {
623                 freersakey(key);
624                 sfree(key);
625                 fail_reason = "request truncated before key comment";
626                 goto failure;
627             }
628
629             comment = snewn(commentlen+1, char);
630             if (comment) {
631                 memcpy(comment, p + 4, commentlen);
632                 comment[commentlen] = '\0';
633                 key->comment = comment;
634             }
635
636             if (logfn) {
637                 char fingerprint[128];
638                 rsa_fingerprint(fingerprint, sizeof(fingerprint), key);
639                 plog(logctx, logfn, "submitted key: %s", fingerprint);
640             }
641
642             if (add234(rsakeys, key) == key) {
643                 keylist_update();
644                 PUT_32BIT(ret, 1);
645                 ret[4] = SSH_AGENT_SUCCESS;
646
647                 plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
648             } else {
649                 freersakey(key);
650                 sfree(key);
651
652                 fail_reason = "key already present";
653                 goto failure;
654             }
655         }
656         break;
657       case SSH2_AGENTC_ADD_IDENTITY:
658         /*
659          * Add to the list and return SSH_AGENT_SUCCESS, or
660          * SSH_AGENT_FAILURE if the key was malformed.
661          */
662         {
663             struct ssh2_userkey *key;
664             char *comment;
665             const char *alg;
666             int alglen, commlen;
667             int bloblen;
668
669             plog(logctx, logfn, "request: SSH2_AGENTC_ADD_IDENTITY");
670
671             if (msgend < p+4) {
672                 fail_reason = "request truncated before key algorithm";
673                 goto failure;
674             }
675             alglen = toint(GET_32BIT(p));
676             p += 4;
677             if (alglen < 0 || alglen > msgend - p) {
678                 fail_reason = "request truncated before key algorithm";
679                 goto failure;
680             }
681             alg = (const char *)p;
682             p += alglen;
683
684             key = snew(struct ssh2_userkey);
685             /* Add further algorithm names here. */
686             if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
687                 key->alg = &ssh_rsa;
688             else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
689                 key->alg = &ssh_dss;
690             else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp256", 19))
691                 key->alg = &ssh_ecdsa_nistp256;
692             else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp384", 19))
693                 key->alg = &ssh_ecdsa_nistp384;
694             else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp521", 19))
695                 key->alg = &ssh_ecdsa_nistp521;
696             else {
697                 sfree(key);
698                 fail_reason = "algorithm unknown";
699                 goto failure;
700             }
701
702             bloblen = msgend - p;
703             key->data = key->alg->openssh_createkey(&p, &bloblen);
704             if (!key->data) {
705                 sfree(key);
706                 fail_reason = "key setup failed";
707                 goto failure;
708             }
709
710             /*
711              * p has been advanced by openssh_createkey, but
712              * certainly not _beyond_ the end of the buffer.
713              */
714             assert(p <= msgend);
715
716             if (msgend < p+4) {
717                 key->alg->freekey(key->data);
718                 sfree(key);
719                 fail_reason = "request truncated before key comment";
720                 goto failure;
721             }
722             commlen = toint(GET_32BIT(p));
723             p += 4;
724
725             if (commlen < 0 || commlen > msgend - p) {
726                 key->alg->freekey(key->data);
727                 sfree(key);
728                 fail_reason = "request truncated before key comment";
729                 goto failure;
730             }
731             comment = snewn(commlen + 1, char);
732             if (comment) {
733                 memcpy(comment, p, commlen);
734                 comment[commlen] = '\0';
735             }
736             key->comment = comment;
737
738             if (logfn) {
739                 char *fingerprint = key->alg->fingerprint(key->data);
740                 plog(logctx, logfn, "submitted key: %s %s",
741                      fingerprint, key->comment);
742                 sfree(fingerprint);
743             }
744
745             if (add234(ssh2keys, key) == key) {
746                 keylist_update();
747                 PUT_32BIT(ret, 1);
748                 ret[4] = SSH_AGENT_SUCCESS;
749
750                 plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
751             } else {
752                 key->alg->freekey(key->data);
753                 sfree(key->comment);
754                 sfree(key);
755
756                 fail_reason = "key already present";
757                 goto failure;
758             }
759         }
760         break;
761       case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
762         /*
763          * Remove from the list and return SSH_AGENT_SUCCESS, or
764          * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
765          * start with.
766          */
767         {
768             struct RSAKey reqkey, *key;
769             int n;
770
771             plog(logctx, logfn, "request: SSH1_AGENTC_REMOVE_RSA_IDENTITY");
772
773             n = makekey(p, msgend - p, &reqkey, NULL, 0);
774             if (n < 0) {
775                 fail_reason = "request truncated before public key";
776                 goto failure;
777             }
778
779             if (logfn) {
780                 char fingerprint[128];
781                 reqkey.comment = NULL;
782                 rsa_fingerprint(fingerprint, sizeof(fingerprint), &reqkey);
783                 plog(logctx, logfn, "unwanted key: %s", fingerprint);
784             }
785
786             key = find234(rsakeys, &reqkey, NULL);
787             freebn(reqkey.exponent);
788             freebn(reqkey.modulus);
789             PUT_32BIT(ret, 1);
790             if (key) {
791                 plog(logctx, logfn, "found with comment: %s", key->comment);
792
793                 del234(rsakeys, key);
794                 keylist_update();
795                 freersakey(key);
796                 sfree(key);
797                 ret[4] = SSH_AGENT_SUCCESS;
798
799                 plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
800             } else {
801                 fail_reason = "key not found";
802                 goto failure;
803             }
804         }
805         break;
806       case SSH2_AGENTC_REMOVE_IDENTITY:
807         /*
808          * Remove from the list and return SSH_AGENT_SUCCESS, or
809          * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
810          * start with.
811          */
812         {
813             struct ssh2_userkey *key;
814             struct blob b;
815
816             plog(logctx, logfn, "request: SSH2_AGENTC_REMOVE_IDENTITY");
817
818             if (msgend < p+4) {
819                 fail_reason = "request truncated before public key";
820                 goto failure;
821             }
822             b.len = toint(GET_32BIT(p));
823             p += 4;
824
825             if (b.len < 0 || b.len > msgend - p) {
826                 fail_reason = "request truncated before public key";
827                 goto failure;
828             }
829             b.blob = p;
830             p += b.len;
831
832             if (logfn) {
833                 char *fingerprint = fingerprint_ssh2_blob(b.blob, b.len);
834                 plog(logctx, logfn, "unwanted key: %s", fingerprint);
835                 sfree(fingerprint);
836             }
837
838             key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
839             if (!key) {
840                 fail_reason = "key not found";
841                 goto failure;
842             }
843
844             plog(logctx, logfn, "found with comment: %s", key->comment);
845
846             del234(ssh2keys, key);
847             keylist_update();
848             key->alg->freekey(key->data);
849             sfree(key);
850             PUT_32BIT(ret, 1);
851             ret[4] = SSH_AGENT_SUCCESS;
852
853             plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
854         }
855         break;
856       case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
857         /*
858          * Remove all SSH-1 keys. Always returns success.
859          */
860         {
861             struct RSAKey *rkey;
862
863             plog(logctx, logfn, "request:"
864                 " SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES");
865
866             while ((rkey = index234(rsakeys, 0)) != NULL) {
867                 del234(rsakeys, rkey);
868                 freersakey(rkey);
869                 sfree(rkey);
870             }
871             keylist_update();
872
873             PUT_32BIT(ret, 1);
874             ret[4] = SSH_AGENT_SUCCESS;
875
876             plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
877         }
878         break;
879       case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
880         /*
881          * Remove all SSH-2 keys. Always returns success.
882          */
883         {
884             struct ssh2_userkey *skey;
885
886             plog(logctx, logfn, "request: SSH2_AGENTC_REMOVE_ALL_IDENTITIES");
887
888             while ((skey = index234(ssh2keys, 0)) != NULL) {
889                 del234(ssh2keys, skey);
890                 skey->alg->freekey(skey->data);
891                 sfree(skey);
892             }
893             keylist_update();
894
895             PUT_32BIT(ret, 1);
896             ret[4] = SSH_AGENT_SUCCESS;
897
898             plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
899         }
900         break;
901       default:
902         plog(logctx, logfn, "request: unknown message type %d", type);
903
904         fail_reason = "unrecognised message";
905         /* fall through */
906       failure:
907         /*
908          * Unrecognised message. Return SSH_AGENT_FAILURE.
909          */
910         PUT_32BIT(ret, 1);
911         ret[4] = SSH_AGENT_FAILURE;
912         plog(logctx, logfn, "reply: SSH_AGENT_FAILURE (%s)", fail_reason);
913         break;
914     }
915
916     *outlen = 4 + GET_32BIT(ret);
917     return ret;
918 }
919
920 void *pageant_failure_msg(int *outlen)
921 {
922     unsigned char *ret = snewn(5, unsigned char);
923     PUT_32BIT(ret, 1);
924     ret[4] = SSH_AGENT_FAILURE;
925     *outlen = 5;
926     return ret;
927 }
928
929 void pageant_init(void)
930 {
931     rsakeys = newtree234(cmpkeys_rsa);
932     ssh2keys = newtree234(cmpkeys_ssh2);
933 }
934
935 struct RSAKey *pageant_nth_ssh1_key(int i)
936 {
937     return index234(rsakeys, i);
938 }
939
940 struct ssh2_userkey *pageant_nth_ssh2_key(int i)
941 {
942     return index234(ssh2keys, i);
943 }
944
945 int pageant_count_ssh1_keys(void)
946 {
947     return count234(rsakeys);
948 }
949
950 int pageant_count_ssh2_keys(void)
951 {
952     return count234(ssh2keys);
953 }
954
955 int pageant_add_ssh1_key(struct RSAKey *rkey)
956 {
957     return add234(rsakeys, rkey) == rkey;
958 }
959
960 int pageant_add_ssh2_key(struct ssh2_userkey *skey)
961 {
962     return add234(ssh2keys, skey) == skey;
963 }
964
965 int pageant_delete_ssh1_key(struct RSAKey *rkey)
966 {
967     struct RSAKey *deleted = del234(rsakeys, rkey);
968     if (!deleted)
969         return FALSE;
970     assert(deleted == rkey);
971     return TRUE;
972 }
973
974 int pageant_delete_ssh2_key(struct ssh2_userkey *skey)
975 {
976     struct ssh2_userkey *deleted = del234(ssh2keys, skey);
977     if (!deleted)
978         return FALSE;
979     assert(deleted == skey);
980     return TRUE;
981 }
982
983 /* ----------------------------------------------------------------------
984  * The agent plug.
985  */
986
987 /*
988  * Coroutine macros similar to, but simplified from, those in ssh.c.
989  */
990 #define crBegin(v)      { int *crLine = &v; switch(v) { case 0:;
991 #define crFinish(z)     } *crLine = 0; return (z); }
992 #define crGetChar(c) do                                         \
993     {                                                           \
994         while (len == 0) {                                      \
995             *crLine =__LINE__; return 1; case __LINE__:;        \
996         }                                                       \
997         len--;                                                  \
998         (c) = (unsigned char)*data++;                           \
999     } while (0)
1000
1001 struct pageant_conn_state {
1002     const struct plug_function_table *fn;
1003     /* the above variable absolutely *must* be the first in this structure */
1004
1005     Socket connsock;
1006     void *logctx;
1007     pageant_logfn_t logfn;
1008     unsigned char lenbuf[4], pktbuf[AGENT_MAX_MSGLEN];
1009     unsigned len, got;
1010     int real_packet;
1011     int crLine;            /* for coroutine in pageant_conn_receive */
1012 };
1013
1014 static int pageant_conn_closing(Plug plug, const char *error_msg,
1015                                 int error_code, int calling_back)
1016 {
1017     struct pageant_conn_state *pc = (struct pageant_conn_state *)plug;
1018     if (error_msg)
1019         plog(pc->logctx, pc->logfn, "%p: error: %s", pc, error_msg);
1020     else
1021         plog(pc->logctx, pc->logfn, "%p: connection closed", pc);
1022     sk_close(pc->connsock);
1023     sfree(pc);
1024     return 1;
1025 }
1026
1027 static void pageant_conn_sent(Plug plug, int bufsize)
1028 {
1029     /* struct pageant_conn_state *pc = (struct pageant_conn_state *)plug; */
1030
1031     /*
1032      * We do nothing here, because we expect that there won't be a
1033      * need to throttle and unthrottle the connection to an agent -
1034      * clients will typically not send many requests, and will wait
1035      * until they receive each reply before sending a new request.
1036      */
1037 }
1038
1039 static void pageant_conn_log(void *logctx, const char *fmt, va_list ap)
1040 {
1041     /* Wrapper on pc->logfn that prefixes the connection identifier */
1042     struct pageant_conn_state *pc = (struct pageant_conn_state *)logctx;
1043     char *formatted = dupvprintf(fmt, ap);
1044     plog(pc->logctx, pc->logfn, "%p: %s", pc, formatted);
1045     sfree(formatted);
1046 }
1047
1048 static int pageant_conn_receive(Plug plug, int urgent, char *data, int len)
1049 {
1050     struct pageant_conn_state *pc = (struct pageant_conn_state *)plug;
1051     char c;
1052
1053     crBegin(pc->crLine);
1054
1055     while (len > 0) {
1056         pc->got = 0;
1057         while (pc->got < 4) {
1058             crGetChar(c);
1059             pc->lenbuf[pc->got++] = c;
1060         }
1061
1062         pc->len = GET_32BIT(pc->lenbuf);
1063         pc->got = 0;
1064         pc->real_packet = (pc->len < AGENT_MAX_MSGLEN-4);
1065
1066         while (pc->got < pc->len) {
1067             crGetChar(c);
1068             if (pc->real_packet)
1069                 pc->pktbuf[pc->got] = c;
1070             pc->got++;
1071         }
1072
1073         {
1074             void *reply;
1075             int replylen;
1076
1077             if (pc->real_packet) {
1078                 reply = pageant_handle_msg(pc->pktbuf, pc->len, &replylen, pc,
1079                                            pc->logfn?pageant_conn_log:NULL);
1080             } else {
1081                 plog(pc->logctx, pc->logfn, "%p: overlong message (%u)",
1082                      pc, pc->len);
1083                 plog(pc->logctx, pc->logfn, "%p: reply: SSH_AGENT_FAILURE "
1084                      "(message too long)", pc);
1085                 reply = pageant_failure_msg(&replylen);
1086             }
1087             sk_write(pc->connsock, reply, replylen);
1088             smemclr(reply, replylen);
1089         }
1090     }
1091
1092     crFinish(1);
1093 }
1094
1095 struct pageant_listen_state {
1096     const struct plug_function_table *fn;
1097     /* the above variable absolutely *must* be the first in this structure */
1098
1099     Socket listensock;
1100     void *logctx;
1101     pageant_logfn_t logfn;
1102 };
1103
1104 static int pageant_listen_closing(Plug plug, const char *error_msg,
1105                                   int error_code, int calling_back)
1106 {
1107     struct pageant_listen_state *pl = (struct pageant_listen_state *)plug;
1108     if (error_msg)
1109         plog(pl->logctx, pl->logfn, "listening socket: error: %s", error_msg);
1110     sk_close(pl->listensock);
1111     pl->listensock = NULL;
1112     return 1;
1113 }
1114
1115 static int pageant_listen_accepting(Plug plug,
1116                                     accept_fn_t constructor, accept_ctx_t ctx)
1117 {
1118     static const struct plug_function_table connection_fn_table = {
1119         NULL, /* no log function, because that's for outgoing connections */
1120         pageant_conn_closing,
1121         pageant_conn_receive,
1122         pageant_conn_sent,
1123         NULL /* no accepting function, because we've already done it */
1124     };
1125     struct pageant_listen_state *pl = (struct pageant_listen_state *)plug;
1126     struct pageant_conn_state *pc;
1127     const char *err;
1128
1129     pc = snew(struct pageant_conn_state);
1130     pc->fn = &connection_fn_table;
1131     pc->logfn = pl->logfn;
1132     pc->logctx = pl->logctx;
1133     pc->crLine = 0;
1134
1135     pc->connsock = constructor(ctx, (Plug) pc);
1136     if ((err = sk_socket_error(pc->connsock)) != NULL) {
1137         sk_close(pc->connsock);
1138         sfree(pc);
1139         return TRUE;
1140     }
1141
1142     sk_set_frozen(pc->connsock, 0);
1143
1144     /* FIXME: can we get any useful peer id info? */
1145     plog(pl->logctx, pl->logfn, "%p: new connection", pc);
1146
1147     return 0;
1148 }
1149
1150 struct pageant_listen_state *pageant_listener_new(void)
1151 {
1152     static const struct plug_function_table listener_fn_table = {
1153         NULL, /* no log function, because that's for outgoing connections */
1154         pageant_listen_closing,
1155         NULL, /* no receive function on a listening socket */
1156         NULL, /* no sent function on a listening socket */
1157         pageant_listen_accepting
1158     };
1159
1160     struct pageant_listen_state *pl = snew(struct pageant_listen_state);
1161     pl->fn = &listener_fn_table;
1162     pl->logctx = NULL;
1163     pl->logfn = NULL;
1164     pl->listensock = NULL;
1165     return pl;
1166 }
1167
1168 void pageant_listener_got_socket(struct pageant_listen_state *pl, Socket sock)
1169 {
1170     pl->listensock = sock;
1171 }
1172
1173 void pageant_listener_set_logfn(struct pageant_listen_state *pl,
1174                                 void *logctx, pageant_logfn_t logfn)
1175 {
1176     pl->logctx = logctx;
1177     pl->logfn = logfn;
1178 }
1179
1180 void pageant_listener_free(struct pageant_listen_state *pl)
1181 {
1182     if (pl->listensock)
1183         sk_close(pl->listensock);
1184     sfree(pl);
1185 }