tadd ssh-agent via factotum - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit ce94dbe662155bd60d6839b5e8c82ad708667bcd
 (DIR) parent ea77b9ce7c579a2e625806dc01104d5f6929cc43
 (HTM) Author: rsc <devnull@localhost>
       Date:   Sun, 13 Feb 2005 18:04:00 +0000
       
       add ssh-agent via factotum
       
       Diffstat:
         M src/cmd/auth/factotum/ctl.c         |       5 ++++-
         M src/cmd/auth/factotum/dat.h         |       6 +++++-
         M src/cmd/auth/factotum/dsa.c         |      14 ++++++++++----
         M src/cmd/auth/factotum/fs.c          |       2 +-
         M src/cmd/auth/factotum/pkcs1.c       |      57 ++++++++++++++++++++++++++++---
         M src/cmd/auth/factotum/rpc.c         |      35 ++++++++++++++++++++++++++++---
         M src/cmd/auth/factotum/rsa.c         |     148 +++++++++++++++++++++++--------
         M src/cmd/auth/secstore/secstore.c    |      12 ++++--------
         A src/cmd/auth/ssh-agent.c            |    1047 +++++++++++++++++++++++++++++++
       
       9 files changed, 1267 insertions(+), 59 deletions(-)
       ---
 (DIR) diff --git a/src/cmd/auth/factotum/ctl.c b/src/cmd/auth/factotum/ctl.c
       t@@ -40,6 +40,9 @@ ctlwrite(char *a)
                Key *k;
                Proto *proto;
        
       +        while(*a == ' ' || *a == '\t' || *a == '\n')
       +                a++;
       +
                if(a[0] == '#' || a[0] == '\0')
                        return 0;
        
       t@@ -63,7 +66,7 @@ ctlwrite(char *a)
                        *p++ = '\0';
                switch(classify(a)){
                default:
       -                werrstr("unknown verb");
       +                werrstr("unknown verb %s", a);
                        return -1;
                case 0:        /* key */
                        attr = parseattr(p);
 (DIR) diff --git a/src/cmd/auth/factotum/dat.h b/src/cmd/auth/factotum/dat.h
       t@@ -9,6 +9,8 @@ enum
                RpcRead,
                RpcStart,
                RpcWrite,
       +        RpcReadHex,
       +        RpcWriteHex,
        
                /* thread stack size - big buffers for printing */
                STACK = 65536,
       t@@ -27,6 +29,7 @@ struct Rpc
                int op;
                void *data;
                int count;
       +        int hex;        /* should result of read be turned into hex? */
        };
        
        struct Conv
       t@@ -214,9 +217,10 @@ extern int                xiowrite(int, void*, int);
        extern int                xioasrdresp(int, void*, int);
        extern int                xioasgetticket(int, char*, char*);
        
       -/* pkcs1.c */
       +/* pkcs1.c - maybe should be in libsec */
        typedef DigestState *DigestAlg(uchar*, ulong, uchar*, DigestState*);
        int        rsasign(RSApriv*, DigestAlg*, uchar*, uint, uchar*, uint);
       +int        rsaverify(RSApub*, DigestAlg*, uchar*, uint, uchar*, uint);
        void        mptoberjust(mpint*, uchar*, uint);
        
        
 (DIR) diff --git a/src/cmd/auth/factotum/dsa.c b/src/cmd/auth/factotum/dsa.c
       t@@ -23,7 +23,7 @@ xdsasign(Conv *c)
        {
                int n;
                mpint *m;
       -        uchar digest[SHA1dlen];
       +        uchar digest[SHA1dlen], sigblob[20+20];
                DSAsig *sig;
                Key *k;
        
       t@@ -46,7 +46,13 @@ xdsasign(Conv *c)
                mpfree(m);
                if(sig == nil)
                        return -1;
       -        convprint(c, "%B %B", sig->r, sig->s);
       +        if(mpsignif(sig->r) > 20*8 || mpsignif(sig->s) > 20*8){
       +                werrstr("signature too long");
       +                return -1;
       +        }
       +        mptoberjust(sig->r, sigblob, 20);
       +        mptoberjust(sig->s, sigblob+20, 20);
       +        convwrite(c, sigblob, sizeof sigblob);
                dsasigfree(sig);
                return 0;
        }
       t@@ -80,11 +86,11 @@ readdsapriv(Key *k)
                || (priv->pub.q=strtomp(a, nil, 16, nil))==nil)
                        goto Error;
                strlwr(a);
       -        if((a=strfindattr(k->privattr, "alpha"))==nil 
       +        if((a=strfindattr(k->attr, "alpha"))==nil 
                || (priv->pub.alpha=strtomp(a, nil, 16, nil))==nil)
                        goto Error;
                strlwr(a);
       -        if((a=strfindattr(k->privattr, "key"))==nil 
       +        if((a=strfindattr(k->attr, "key"))==nil 
                || (priv->pub.key=strtomp(a, nil, 16, nil))==nil)
                        goto Error;
                strlwr(a);
 (DIR) diff --git a/src/cmd/auth/factotum/fs.c b/src/cmd/auth/factotum/fs.c
       t@@ -166,7 +166,7 @@ static int
        keylist(int i, char *a, uint nn)
        {
                int n;
       -        char buf[512];
       +        char buf[4096];
                Key *k;
        
                if(i >= ring.nkey)
 (DIR) diff --git a/src/cmd/auth/factotum/pkcs1.c b/src/cmd/auth/factotum/pkcs1.c
       t@@ -36,7 +36,7 @@ rsasign(RSApriv *key, DigestAlg *hash, uchar *digest, uint dlen,
                /*
                 * Create number to sign.
                 */
       -        len = (mpsignif(key->pub.n)+7)/8;
       +        len = (mpsignif(key->pub.n)+7)/8 - 1;
                if(len < n+2){
                        werrstr("rsa key too short");
                        return -1;
       t@@ -65,9 +65,41 @@ rsasign(RSApriv *key, DigestAlg *hash, uchar *digest, uint dlen,
                mpfree(m);
                if(s == nil)
                        return -1;
       -        mptoberjust(s, sig, len);
       +        mptoberjust(s, sig, len+1);
                mpfree(s);
       -        return len;
       +        return len+1;
       +}
       +
       +int
       +rsaverify(RSApub *key, DigestAlg *hash, uchar *digest, uint dlen,
       +        uchar *sig, uint siglen)
       +{
       +        uchar asn1[64], xasn1[64];
       +        int n, nn;
       +        mpint *m, *s;
       +
       +        /*
       +         * Create ASN.1
       +         */
       +        n = mkasn1(asn1, hash, digest, dlen);
       +
       +        /*
       +         * Extract plaintext of signature.
       +         */
       +        s = betomp(sig, siglen, nil);
       +        if(s == nil)
       +                return -1;
       +        m = rsaencrypt(key, s, nil);
       +        mpfree(s);
       +        if(m == nil)
       +                return -1;
       +        nn = mptobe(m, xasn1, sizeof xasn1, nil);
       +        mpfree(m);
       +        if(n != nn || memcmp(asn1, xasn1, n) != 0){
       +                werrstr("signature did not verify");
       +                return -1;
       +        }
       +        return 0;
        }
        
        /*
       t@@ -116,6 +148,15 @@ uchar oidmd5[] = { O0(1, 2), O2(840), O3(113549), 2, 5 };
         *                digestAlgorithm AlgorithmIdentifier,
         *                digest OCTET STRING
         *        }
       + *
       + * except that OpenSSL seems to sign
       + *
       + *        DigestInfo ::= SEQUENCE {
       + *                SEQUENCE{ digestAlgorithm AlgorithmIdentifier, NULL }
       + *                digest OCTET STRING
       + *        }
       + *
       + * instead.  Sigh.
         */
        static int
        mkasn1(uchar *asn1, DigestAlg *alg, uchar *d, uint dlen)
       t@@ -138,17 +179,25 @@ mkasn1(uchar *asn1, DigestAlg *alg, uchar *d, uint dlen)
                *p++ = 0x30;        /* sequence */
                p++;
                
       +        *p++ = 0x30;        /* another sequence */
       +        p++;
       +
                *p++ = 0x06;        /* object id */
                *p++ = olen;
                memmove(p, obj, olen);
                p += olen;
                
       +        *p++ = 0x05;        /* null */
       +        *p++ = 0;
       +        
       +        asn1[3] = p - (asn1+4);        /* end of inner sequence */
       +        
                *p++ = 0x04;        /* octet string */
                *p++ = dlen;
                memmove(p, d, dlen);
                p += dlen;
        
       -        asn1[1] = p - (asn1+2);
       +        asn1[1] = p - (asn1+2);        /* end of outer sequence */
                return p-asn1;
        }
        
 (DIR) diff --git a/src/cmd/auth/factotum/rpc.c b/src/cmd/auth/factotum/rpc.c
       t@@ -40,6 +40,8 @@ char *rpcname[] =
                "read",
                "start",
                "write",
       +        "readhex",
       +        "writehex",
        };
        
        static int
       t@@ -153,7 +155,20 @@ rpcexec(Conv *c)
        {
                uchar *p;
        
       +        c->rpc.hex = 0;
                switch(c->rpc.op){
       +        case RpcWriteHex:
       +                c->rpc.op = RpcWrite;
       +                if(dec16(c->rpc.data, c->rpc.count, c->rpc.data, c->rpc.count) != c->rpc.count/2){
       +                        rpcrespond(c, "bad hex");
       +                        break;
       +                }
       +                c->rpc.count /= 2;
       +                goto Default;
       +        case RpcReadHex:
       +                c->rpc.hex = 1;
       +                c->rpc.op = RpcRead;
       +                /* fall through */
                case RpcRead:
                        if(c->rpc.count > 0){
                                rpcrespond(c, "error read takes no parameters");
       t@@ -161,6 +176,7 @@ rpcexec(Conv *c)
                        }
                        /* fall through */
                default:
       +        Default:
                        if(!c->active){
                                if(c->done)
                                        rpcrespond(c, "done");
       t@@ -224,11 +240,18 @@ void
        rpcrespondn(Conv *c, char *verb, void *data, int count)
        {
                char *p;
       +        int need, hex;
        
                if(c->hangup)
                        return;
        
       -        if(strlen(verb)+1+count > sizeof c->reply){
       +        need = strlen(verb)+1+count;
       +        hex = 0;
       +        if(c->rpc.hex && strcmp(verb, "ok") == 0){
       +                need += count;
       +                hex = 1;
       +        }
       +        if(need > sizeof c->reply){
                        print("RPC response too large; caller %#lux", getcallerpc(&c));
                        return;
                }
       t@@ -236,8 +259,14 @@ rpcrespondn(Conv *c, char *verb, void *data, int count)
                strcpy(c->reply, verb);
                p = c->reply + strlen(c->reply);
                *p++ = ' ';
       -        memmove(p, data, count);
       -        c->nreply = count + (p - c->reply);
       +        if(hex){
       +                enc16(p, 2*count, data, count);
       +                p += 2*count;
       +        }else{
       +                memmove(p, data, count);
       +                p += count;
       +        }
       +        c->nreply = p - c->reply;
                (*c->kickreply)(c);
                c->rpc.op = RpcUnknown;
        }
 (DIR) diff --git a/src/cmd/auth/factotum/rsa.c b/src/cmd/auth/factotum/rsa.c
       t@@ -4,65 +4,116 @@
        /*
         * RSA authentication.
         * 
       - * Client:
       + * Encrypt/Decrypt:
         *        start n=xxx ek=xxx
         *        write msg
       - *        read decrypt(msg)
       + *        read encrypt/decrypt(msg)
         *
         * Sign (PKCS #1 using hash=sha1 or hash=md5)
         *        start n=xxx ek=xxx
         *        write hash(msg)
         *        read signature(hash(msg))
         * 
       + * Verify:
       + *        start n=xxx ek=xxx
       + *        write hash(msg)
       + *        write signature(hash(msg))
       + *        read ok or fail
       + *
         * all numbers are hexadecimal biginits parsable with strtomp.
         * must be lower case for attribute matching in start.
         */
        
        static int
       -rsaclient(Conv *c)
       +xrsadecrypt(Conv *c)
        {
       -        char *chal;
       -        mpint *m;
       +        char *txt, buf[4096], *role;
       +        int n, ret;
       +        mpint *m, *mm;
                Key *k;
       +        RSApriv *key;
       +
       +        ret = -1;
       +        txt = nil;
       +        m = nil;
       +        mm = nil;
        
       +        /* fetch key */
       +        c->state = "keylookup";
                k = keylookup("%A", c->attr);
                if(k == nil)
       -                return -1;
       -        c->state = "read challenge";
       -        if(convreadm(c, &chal) < 0){
       -                keyclose(k);
       -                return -1;
       +                goto out;
       +        key = k->priv;
       +        
       +        /* make sure have private half if needed */
       +        role = strfindattr(c->attr, "role");
       +        if(strcmp(role, "decrypt") == 0 && !key->c2){
       +                werrstr("missing private half of key -- cannot decrypt");
       +                goto out;
                }
       -        if(strlen(chal) < 32){
       -        badchal:
       -                free(chal);
       -                convprint(c, "bad challenge");
       -                keyclose(k);
       -                return -1;
       +        
       +        /* read text */
       +        c->state = "read";
       +        if((n=convreadm(c, &txt)) < 0)
       +                goto out;
       +        if(n < 32){
       +                convprint(c, "data too short");
       +                goto out;
                }
       -        m = strtomp(chal, nil, 16, nil);
       +        
       +        /* encrypt/decrypt */
       +        m = betomp(txt, n, nil);
                if(m == nil)
       -                goto badchal;
       -        free(chal);
       -        m = rsadecrypt(k->priv, m, m);
       -        convprint(c, "%B", m);
       +                goto out;
       +        if(strcmp(role, "decrypt") == 0)
       +                mm = rsadecrypt(key, m, m);
       +        else
       +                mm = rsaencrypt(&key->pub, m, nil);
       +        if(mm == nil)
       +                goto out;
       +        n = mptobe(m, buf, sizeof buf, nil);
       +        
       +        /* send response */
       +        c->state = "write";
       +        convwrite(c, buf, n);
       +        ret = 0;
       +
       +out:
                mpfree(m);
       +        mpfree(mm);
                keyclose(k);
       -        return 0;
       +        free(txt);
       +        return ret;
        }
        
        static int
        xrsasign(Conv *c)
        {
       -        char *hash;
       -        int dlen, n;
       +        char *hash, *role;
       +        int dlen, n, ret;
                DigestAlg *hashfn;
                Key *k;
       +        RSApriv *key;
                uchar sig[1024], digest[64];
       +        char *sig2;
        
       +        ret = -1;
       +        
       +        /* fetch key */
       +        c->state = "keylookup";
                k = keylookup("%A", c->attr);
                if(k == nil)
       -                return -1;
       +                goto out;
       +
       +        /* make sure have private half if needed */
       +        key = k->priv;
       +        role = strfindattr(c->attr, "role");
       +        if(strcmp(role, "sign") == 0 && !key->c2){
       +                werrstr("missing private half of key -- cannot sign");
       +                goto out;
       +        }
       +        
       +        /* get hash type from key */
                hash = strfindattr(k->attr, "hash");
                if(hash == nil)
                        hash = "sha1";
       t@@ -74,20 +125,38 @@ xrsasign(Conv *c)
                        dlen = MD5dlen;
                }else{
                        werrstr("unknown hash function %s", hash);
       -                return -1;
       +                goto out;
                }
       -        c->state = "read data";
       -        if((n=convread(c, digest, dlen)) < 0){
       -                keyclose(k);
       -                return -1;
       +
       +        /* read hash */
       +        c->state = "read hash";
       +        if((n=convread(c, digest, dlen)) < 0)
       +                goto out;
       +
       +        if(strcmp(role, "sign") == 0){
       +                /* sign */
       +                if((n=rsasign(key, hashfn, digest, dlen, sig, sizeof sig)) < 0)
       +                        goto out;
       +
       +                /* write */
       +                convwrite(c, sig, n);
       +        }else{
       +                /* read signature */
       +                if((n = convreadm(c, &sig2)) < 0)
       +                        goto out;
       +                        
       +                /* verify */
       +                if(rsaverify(&key->pub, hashfn, digest, dlen, (uchar*)sig2, n) == 0)
       +                        convprint(c, "ok");
       +                else
       +                        convprint(c, "signature does not verify");
       +                free(sig2);
                }
       -        memset(sig, 0xAA, sizeof sig);
       -        n = rsasign(k->priv, hashfn, digest, dlen, sig, sizeof sig);
       +        ret = 0;
       +
       +out:
                keyclose(k);
       -        if(n < 0)
       -                return -1;        
       -        convwrite(c, sig, n);
       -        return 0;
       +        return ret;
        }
        
        /*
       t@@ -119,6 +188,9 @@ readrsapriv(Key *k)
                || (priv->pub.n=strtomp(a, nil, 16, nil))==nil)
                        goto Error;
                strlwr(a);
       +        if(k->privattr == nil)        /* only public half */
       +                return priv;
       +
                if((a=strfindattr(k->privattr, "!p"))==nil 
                || (priv->p=strtomp(a, nil, 16, nil))==nil)
                        goto Error;
       t@@ -177,8 +249,10 @@ rsaclose(Key *k)
        static Role
        rsaroles[] = 
        {
       -        "client",        rsaclient,
                "sign",        xrsasign,
       +        "verify",        xrsasign,        /* public operation */
       +        "decrypt",        xrsadecrypt,
       +        "encrypt",        xrsadecrypt,        /* public operation */
                0
        };
        
 (DIR) diff --git a/src/cmd/auth/secstore/secstore.c b/src/cmd/auth/secstore/secstore.c
       t@@ -494,7 +494,9 @@ main(int argc, char **argv)
                char *serve, *tcpserve, *user;
                AuthConn *c;
        
       -        serve = "$auth";
       +        serve = getenv("secstore");
       +        if(serve == nil)
       +                serve = "secstore";
                user = getuser();
                memset(Gflag, 0, sizeof Gflag);
                fmtinstall('B', mpfmt);
       t@@ -559,14 +561,8 @@ main(int argc, char **argv)
                        exits("usage");
                }
        
       -        rc = strlen(serve)+sizeof("tcp!!99990");
       -        tcpserve = emalloc(rc);
       -        if(strchr(serve,'!'))
       -                strcpy(tcpserve, serve);
       -        else
       -                snprint(tcpserve, rc, "tcp!%s!5356", serve);
       +        tcpserve = netmkaddr(serve, "tcp", "secstore");
                c = login(user, tcpserve, pass_stdin, pass_nvram);
       -        free(tcpserve);
                if(c == nil){
                        fprint(2, "secstore authentication failed\n");
                        exits("secstore authentication failed");
 (DIR) diff --git a/src/cmd/auth/ssh-agent.c b/src/cmd/auth/ssh-agent.c
       t@@ -0,0 +1,1047 @@
       +/*
       + * Present factotum in ssh agent clothing.
       + */
       +#include <u.h>
       +#include <libc.h>
       +#include <mp.h>
       +#include <libsec.h>
       +#include <auth.h>
       +#include <thread.h>
       +#include <9pclient.h>
       +
       +enum
       +{
       +        STACK = 65536
       +};
       +enum                /* agent protocol packet types */
       +{
       +        SSH_AGENTC_NONE = 0,
       +        SSH_AGENTC_REQUEST_RSA_IDENTITIES,
       +        SSH_AGENT_RSA_IDENTITIES_ANSWER,
       +        SSH_AGENTC_RSA_CHALLENGE,
       +        SSH_AGENT_RSA_RESPONSE,
       +        SSH_AGENT_FAILURE,
       +        SSH_AGENT_SUCCESS,
       +        SSH_AGENTC_ADD_RSA_IDENTITY,
       +        SSH_AGENTC_REMOVE_RSA_IDENTITY,
       +        SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES,
       +        
       +        SSH2_AGENTC_REQUEST_IDENTITIES = 11,
       +        SSH2_AGENT_IDENTITIES_ANSWER,
       +        SSH2_AGENTC_SIGN_REQUEST,
       +        SSH2_AGENT_SIGN_RESPONSE,
       +
       +        SSH2_AGENTC_ADD_IDENTITY = 17,
       +        SSH2_AGENTC_REMOVE_IDENTITY,
       +        SSH2_AGENTC_REMOVE_ALL_IDENTITIES,
       +        SSH2_AGENTC_ADD_SMARTCARD_KEY,
       +        SSH2_AGENTC_REMOVE_SMARTCARD_KEY,
       +
       +        SSH_AGENTC_LOCK,
       +        SSH_AGENTC_UNLOCK,
       +        SSH_AGENTC_ADD_RSA_ID_CONSTRAINED,
       +        SSH2_AGENTC_ADD_ID_CONSTRAINED,
       +        SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED,
       +        
       +        SSH_AGENT_CONSTRAIN_LIFETIME = 1,
       +        SSH_AGENT_CONSTRAIN_CONFIRM = 2,
       +
       +        SSH2_AGENT_FAILURE = 30,
       +        
       +        SSH_COM_AGENT2_FAILURE = 102,
       +        SSH_AGENT_OLD_SIGNATURE = 0x01,
       +};
       +
       +typedef struct Aconn Aconn;
       +struct Aconn
       +{
       +        uchar *data;
       +        uint ndata;
       +        int ctl;
       +        int fd;
       +        char dir[40];
       +};
       +
       +typedef struct Msg Msg;
       +struct Msg
       +{
       +        uchar *bp;
       +        uchar *p;
       +        uchar *ep;
       +};
       +
       +char adir[40];
       +int afd;
       +int chatty;
       +char *factotum = "factotum";
       +
       +void                agentproc(void *v);
       +void*        emalloc(int n);
       +void*        erealloc(void *v, int n);
       +void                listenproc(void *v);
       +int                runmsg(Aconn *a);
       +void                listkeystext(void);
       +
       +void
       +usage(void)
       +{
       +        fprint(2, "usage: 9 ssh-agent [-D] [factotum]\n");
       +        threadexitsall("usage");
       +}
       +
       +void
       +threadmain(int argc, char **argv)
       +{
       +        int fd, pid, export, dotextlist;
       +        char dir[100];
       +        char sock[200], addr[200];
       +        uvlong x;
       +
       +        export = 0;
       +        dotextlist = 0;
       +        pid = getpid();
       +        fmtinstall('B', mpfmt);
       +        fmtinstall('H', encodefmt);
       +        fmtinstall('[', encodefmt);
       +        
       +        ARGBEGIN{
       +        case 'D':
       +                chatty++;
       +                break;
       +        case 'e':
       +                export = 1;
       +                break;
       +        case 'l':
       +                dotextlist = 1;
       +                break;
       +        default:
       +                usage();
       +        }ARGEND
       +        
       +        if(argc > 1)
       +                usage();
       +        if(argc == 1)
       +                factotum = argv[0];
       +                
       +        if(dotextlist)
       +                listkeystext();
       +
       +        x = ((uvlong)fastrand()<<32) | fastrand();
       +        x ^= ((uvlong)fastrand()<<32) | fastrand();
       +        snprint(dir, sizeof dir, "/tmp/ssh-%llux", x);
       +        if((fd = create(dir, OREAD, DMDIR|0700)) < 0)
       +                sysfatal("mkdir %s: %r", dir);
       +        close(fd);
       +        snprint(sock, sizeof sock, "%s/agent.%d", dir, pid);
       +        snprint(addr, sizeof addr, "unix!%s", sock);
       +
       +        if((afd = announce(addr, adir)) < 0)
       +                sysfatal("announce %s: %r", addr);
       +        
       +        proccreate(listenproc, nil, STACK);
       +        
       +        print("SSH_AUTH_SOCK=%s;\n", sock);
       +        if(export)
       +                print("export SSH_AUTH_SOCK;\n");
       +        print("SSH_AGENT_PID=%d;\n", pid);
       +        if(export)
       +                print("export SSH_AGENT_PID;\n");
       +        close(1);
       +        threadexits(0);
       +}
       +
       +void
       +listenproc(void *v)
       +{
       +        Aconn *a;
       +
       +        USED(v);
       +        for(;;){
       +                a = emalloc(sizeof *a);
       +                a->ctl = listen(adir, a->dir);
       +                if(a->ctl < 0)
       +                        sysfatal("listen: %r");
       +                proccreate(agentproc, a, STACK);
       +        }
       +}
       +
       +void
       +agentproc(void *v)
       +{
       +        Aconn *a;
       +        int n;
       +        
       +        a = v;
       +        a->fd = accept(a->ctl, a->dir);
       +        close(a->ctl);
       +        a->ctl = -1;
       +        for(;;){
       +                a->data = erealloc(a->data, a->ndata+1024);
       +                n = read(a->fd, a->data+a->ndata, 1024);
       +                if(n <= 0)
       +                        break;
       +                a->ndata += n;
       +                while(runmsg(a))
       +                        ;
       +        }
       +        close(a->fd);
       +        free(a);
       +        threadexits(nil);
       +}
       +
       +int
       +get1(Msg *m)
       +{
       +        if(m->p >= m->ep)
       +                return 0;
       +        return *m->p++;
       +}
       +
       +int
       +get2(Msg *m)
       +{
       +        uint x;
       +        
       +        if(m->p+2 > m->ep)
       +                return 0;
       +        x = (m->p[0]<<8)|m->p[1];
       +        m->p += 2;
       +        return x;
       +}
       +
       +int
       +get4(Msg *m)
       +{
       +        uint x;
       +        if(m->p+4 > m->ep)
       +                return 0;
       +        x = (m->p[0]<<24)|(m->p[1]<<16)|(m->p[2]<<8)|m->p[3];
       +        m->p += 4;
       +        return x;
       +}
       +
       +uchar*
       +getn(Msg *m, uint n)
       +{
       +        uchar *p;
       +        
       +        if(m->p+n > m->ep)
       +                return nil;
       +        p = m->p;
       +        m->p += n;
       +        return p;
       +}
       +
       +char*
       +getstr(Msg *m)
       +{
       +        uint n;
       +        uchar *p;
       +
       +        n = get4(m);
       +        p = getn(m, n);
       +        if(p == nil)
       +                return nil;
       +        p--;
       +        memmove(p, p+1, n);
       +        p[n] = 0;
       +        return p;
       +}
       +
       +mpint*
       +getmp(Msg *m)
       +{
       +        int n;
       +        uchar *p;
       +        
       +        n = (get2(m)+7)/8;
       +        if((p=getn(m, n)) == nil)
       +                return nil;
       +        return betomp(p, n, nil);
       +}
       +
       +mpint*
       +getmp2(Msg *m)
       +{
       +        int n;
       +        uchar *p;
       +        
       +        n = get4(m);
       +        if((p = getn(m, n)) == nil)
       +                return nil;
       +        return betomp(p, n, nil);
       +}
       +
       +Msg*
       +getm(Msg *m, Msg *mm)
       +{
       +        uint n;
       +        uchar *p;
       +        
       +        n = get4(m);
       +        if((p = getn(m, n)) == nil)
       +                return nil;
       +        mm->bp = p;
       +        mm->p = p;
       +        mm->ep = p+n;
       +        return mm;
       +}
       +
       +uchar*
       +ensure(Msg *m, int n)
       +{
       +        int len, plen;
       +        uchar *p;
       +        
       +        len = m->ep - m->bp;
       +        if(m->p+n > m->ep){
       +                plen = m->p - m->bp;
       +                m->bp = erealloc(m->bp, len+n+1024);
       +                m->p = m->bp+plen;
       +                m->ep = m->bp+len+n+1024;
       +        }
       +        p = m->p;
       +        m->p += n;
       +        return p;
       +}
       +
       +void
       +put4(Msg *m, uint n)
       +{
       +        uchar *p;
       +        
       +        p = ensure(m, 4);
       +        p[0] = (n>>24)&0xFF;
       +        p[1] = (n>>16)&0xFF;
       +        p[2] = (n>>8)&0xFF;
       +        p[3] = n&0xFF;
       +}
       +
       +void
       +put2(Msg *m, uint n)
       +{
       +        uchar *p;
       +        
       +        p = ensure(m, 2);
       +        p[0] = (n>>8)&0xFF;
       +        p[1] = n&0xFF;
       +}
       +
       +void
       +put1(Msg *m, uint n)
       +{
       +        uchar *p;
       +        
       +        p = ensure(m, 1);
       +        p[0] = n&0xFF;
       +}
       +
       +void
       +putn(Msg *m, void *a, uint n)
       +{
       +        uchar *p;
       +        
       +        p = ensure(m, n);
       +        memmove(p, a, n);
       +}
       +
       +void
       +putmp(Msg *m, mpint *b)
       +{
       +        int bits, n;
       +        uchar *p;
       +        
       +        bits = mpsignif(b);
       +        put2(m, bits);
       +        n = (bits+7)/8;
       +        p = ensure(m, n);
       +        mptobe(b, p, n, nil);
       +}
       +
       +void
       +putmp2(Msg *m, mpint *b)
       +{
       +        int bits, n;
       +        uchar *p;
       +        
       +        if(mpcmp(b, mpzero) == 0){
       +                put4(m, 0);
       +                return;
       +        }
       +        bits = mpsignif(b);
       +        n = (bits+7)/8;
       +        if(bits%8 == 0){
       +                put4(m, n+1);
       +                put1(m, 0);
       +        }else
       +                put4(m, n);
       +        p = ensure(m, n);
       +        mptobe(b, p, n, nil);
       +}
       +
       +void
       +putstr(Msg *m, char *s)
       +{
       +        int n;
       +        
       +        n = strlen(s);
       +        put4(m, n);
       +        putn(m, s, n);
       +}
       +
       +void
       +putm(Msg *m, Msg *mm)
       +{
       +        uint n;
       +        
       +        n = mm->p - mm->bp;
       +        put4(m, n);
       +        putn(m, mm->bp, n);
       +}
       +
       +void
       +newmsg(Msg *m)
       +{
       +        memset(m, 0, sizeof *m);
       +}
       +
       +void
       +newreply(Msg *m, int type)
       +{
       +        memset(m, 0, sizeof *m);
       +        put4(m, 0);
       +        put1(m, type);
       +}
       +
       +void
       +reply(Aconn *a, Msg *m)
       +{
       +        uint n;
       +        uchar *p;
       +        
       +        n = (m->p - m->bp) - 4;
       +        p = m->bp;
       +        p[0] = (n>>24)&0xFF;
       +        p[1] = (n>>16)&0xFF;
       +        p[2] = (n>>8)&0xFF;
       +        p[3] = n&0xFF;
       +        if(chatty)
       +                fprint(2, "respond %d: %.*H\n", p[4], n, m->bp+4);
       +        write(a->fd, p, n+4);
       +        free(p);
       +        memset(m, 0, sizeof *m);
       +}
       +
       +typedef struct Key Key;
       +struct Key
       +{
       +        mpint *mod;
       +        mpint *ek;
       +        char *comment;
       +};
       +
       +static char*
       +find(char **f, int nf, char *k)
       +{
       +        int i, len;
       +
       +        len = strlen(k);
       +        for(i=1; i<nf; i++)        /* i=1: f[0] is "key" */
       +                if(strncmp(f[i], k, len) == 0 && f[i][len] == '=')
       +                        return f[i]+len+1;
       +        return nil;
       +}
       +
       +static int
       +putrsa1(Msg *m, char **f, int nf)
       +{
       +        char *p;
       +        mpint *mod, *ek;
       +
       +        p = find(f, nf, "n");
       +        if(p == nil || (mod = strtomp(p, nil, 16, nil)) == nil)
       +                return -1;
       +        p = find(f, nf, "ek");
       +        if(p == nil || (ek = strtomp(p, nil, 16, nil)) == nil){
       +                mpfree(mod);
       +                return -1;
       +        }
       +        p = find(f, nf, "comment");
       +        if(p == nil)
       +                p = "";
       +        put4(m, mpsignif(mod));
       +        putmp(m, ek);
       +        putmp(m, mod);
       +        putstr(m, p);
       +        mpfree(mod);
       +        mpfree(ek);
       +        return 0;
       +}
       +
       +void
       +printattr(char **f, int nf)
       +{
       +        int i;
       +        
       +        print("#");
       +        for(i=0; i<nf; i++)
       +                print(" %s", f[i]);
       +        print("\n");
       +}
       +
       +void
       +printrsa1(char **f, int nf)
       +{
       +        char *p;
       +        mpint *mod, *ek;
       +
       +        p = find(f, nf, "n");
       +        if(p == nil || (mod = strtomp(p, nil, 16, nil)) == nil)
       +                return;
       +        p = find(f, nf, "ek");
       +        if(p == nil || (ek = strtomp(p, nil, 16, nil)) == nil){
       +                mpfree(mod);
       +                return;
       +        }
       +        p = find(f, nf, "comment");
       +        if(p == nil)
       +                p = "";
       +
       +        if(chatty)
       +                printattr(f, nf);
       +        print("%d %.10B %.10B %s\n", mpsignif(mod), ek, mod, p);
       +        mpfree(ek);
       +        mpfree(mod);
       +}
       +
       +static int
       +putrsa(Msg *m, char **f, int nf)
       +{
       +        char *p;
       +        mpint *mod, *ek;
       +
       +        p = find(f, nf, "n");
       +        if(p == nil || (mod = strtomp(p, nil, 16, nil)) == nil)
       +                return -1;
       +        p = find(f, nf, "ek");
       +        if(p == nil || (ek = strtomp(p, nil, 16, nil)) == nil){
       +                mpfree(mod);
       +                return -1;
       +        }
       +        putstr(m, "ssh-rsa");
       +        putmp2(m, ek);
       +        putmp2(m, mod);
       +        mpfree(ek);
       +        mpfree(mod);
       +        return 0;
       +}
       +
       +RSApub*
       +getrsapub(Msg *m)
       +{
       +        RSApub *k;
       +        
       +        k = rsapuballoc();
       +        if(k == nil)
       +                return nil;
       +        k->ek = getmp2(m);
       +        k->n = getmp2(m);
       +        if(k->ek == nil || k->n == nil){
       +                rsapubfree(k);
       +                return nil;
       +        }
       +        return k;
       +}
       +
       +static int
       +putdsa(Msg *m, char **f, int nf)
       +{
       +        char *p;
       +        int ret;
       +        mpint *dp, *dq, *dalpha, *dkey;
       +
       +        ret = -1;
       +        dp = dq = dalpha = dkey = nil;
       +        p = find(f, nf, "p");
       +        if(p == nil || (dp = strtomp(p, nil, 16, nil)) == nil)
       +                goto out;
       +        p = find(f, nf, "q");
       +        if(p == nil || (dq = strtomp(p, nil, 16, nil)) == nil)
       +                goto out;
       +        p = find(f, nf, "alpha");
       +        if(p == nil || (dalpha = strtomp(p, nil, 16, nil)) == nil)
       +                goto out;
       +        p = find(f, nf, "key");
       +        if(p == nil || (dkey = strtomp(p, nil, 16, nil)) == nil)
       +                goto out;
       +        putstr(m, "ssh-dss");
       +        putmp2(m, dp);
       +        putmp2(m, dq);
       +        putmp2(m, dalpha);
       +        putmp2(m, dkey);
       +        ret = 0;
       +out:
       +        mpfree(dp);
       +        mpfree(dq);
       +        mpfree(dalpha);
       +        mpfree(dkey);
       +        return ret;
       +}
       +
       +static int
       +putkey2(Msg *m, int (*put)(Msg*,char**,int), char **f, int nf)
       +{
       +        char *p;
       +        Msg mm;
       +        
       +        newmsg(&mm);
       +        if(put(&mm, f, nf) < 0)
       +                return -1;
       +        putm(m, &mm);
       +        free(mm.bp);
       +        p = find(f, nf, "comment");
       +        if(p == nil)
       +                p = "";
       +        putstr(m, p);
       +        return 0;
       +}
       +
       +static int
       +printkey(char *type, int (*put)(Msg*,char**,int), char **f, int nf)
       +{
       +        Msg m;
       +        char *p;
       +        
       +        newmsg(&m);
       +        if(put(&m, f, nf) < 0)
       +                return -1;
       +        p = find(f, nf, "comment");
       +        if(p == nil)
       +                p = "";
       +        if(chatty)
       +                printattr(f, nf);
       +        print("%s %.*[ %s\n", type, m.p-m.bp, m.bp, p);
       +        free(m.bp);
       +        return 0;
       +}
       +
       +DSApub*
       +getdsapub(Msg *m)
       +{
       +        DSApub *k;
       +        
       +        k = dsapuballoc();
       +        if(k == nil)
       +                return nil;
       +        k->p = getmp2(m);
       +        k->q = getmp2(m);
       +        k->alpha = getmp2(m);
       +        k->key = getmp2(m);
       +        if(!k->p || !k->q || !k->alpha || !k->key){
       +                dsapubfree(k);
       +                return nil;
       +        }
       +        return k;
       +}
       +
       +static int
       +listkeys(Msg *m, int version)
       +{
       +        char buf[8192+1], *line[100], *f[20], *p, *s;
       +        uchar *pnk;
       +        int i, n, nl, nf, nk;
       +        CFid *fid;
       +
       +        nk = 0;
       +        pnk = m->p;
       +        put4(m, 0);
       +        if((fid = nsopen("factotum", nil, "ctl", OREAD)) == nil){
       +                fprint(2, "ssh-agent: open factotum: %r\n");
       +                return -1;
       +        }
       +        for(;;){
       +                if((n = fsread(fid, buf, sizeof buf-1)) <= 0)
       +                        break;
       +                buf[n] = 0;
       +                nl = getfields(buf, line, nelem(line), 1, "\n");
       +                for(i=0; i<nl; i++){
       +                        nf = tokenize(line[i], f, nelem(f));
       +                        if(nf == 0 || strcmp(f[0], "key") != 0)
       +                                continue;
       +                        p = find(f, nf, "proto");
       +                        if(p == nil)
       +                                continue;
       +                        s = find(f, nf, "service");
       +                        if(s == nil)
       +                                continue;
       +
       +                        if(version == 1 && strcmp(p, "rsa") == 0 && strcmp(s, "ssh") == 0)
       +                                if(putrsa1(m, f, nf) >= 0)
       +                                        nk++;
       +                        if(version == 2 && strcmp(p, "rsa") == 0 && strcmp(s, "ssh-rsa") == 0)
       +                                if(putkey2(m, putrsa, f, nf) >= 0)
       +                                        nk++;
       +                        if(version == 2 && strcmp(p, "dsa") == 0 && strcmp(s, "ssh-dss") == 0)
       +                                if(putkey2(m, putdsa, f, nf) >= 0)
       +                                        nk++;
       +                }
       +        }
       +        fsclose(fid);
       +        pnk[0] = (nk>>24)&0xFF;
       +        pnk[1] = (nk>>16)&0xFF;
       +        pnk[2] = (nk>>8)&0xFF;
       +        pnk[3] = nk&0xFF;
       +        return nk;
       +}
       +
       +void
       +listkeystext(void)
       +{
       +        char buf[4096+1], *line[100], *f[20], *p, *s;
       +        int i, n, nl, nf;
       +        CFid *fid;
       +
       +        if((fid = nsopen(factotum, nil, "ctl", OREAD)) == nil){
       +                fprint(2, "ssh-agent: open factotum: %r\n");
       +                return;
       +        }
       +        for(;;){
       +                if((n = fsread(fid, buf, sizeof buf-1)) <= 0)
       +                        break;
       +                buf[n] = 0;
       +                nl = getfields(buf, line, nelem(line), 1, "\n");
       +                for(i=0; i<nl; i++){
       +                        nf = tokenize(line[i], f, nelem(f));
       +                        if(nf == 0 || strcmp(f[0], "key") != 0)
       +                                continue;
       +                        p = find(f, nf, "proto");
       +                        if(p == nil)
       +                                continue;
       +                        s = find(f, nf, "service");
       +                        if(s == nil)
       +                                continue;
       +
       +                        if(strcmp(p, "rsa") == 0 && strcmp(s, "ssh") == 0)
       +                                printrsa1(f, nf);
       +                        if(strcmp(p, "rsa") == 0 && strcmp(s, "ssh-rsa") == 0)
       +                                printkey("ssh-rsa", putrsa, f, nf);
       +                        if(strcmp(p, "dsa") == 0 && strcmp(s, "ssh-dss") == 0)
       +                                printkey("ssh-dss", putdsa, f, nf);
       +                }
       +        }
       +        fsclose(fid);
       +        threadexitsall(nil);
       +}
       +
       +mpint*
       +rsaunpad(mpint *b)
       +{
       +        int i, n;
       +        uchar buf[2560];
       +
       +        n = (mpsignif(b)+7)/8;
       +        if(n > sizeof buf){
       +                werrstr("rsaunpad: too big");
       +                return nil;
       +        }
       +        mptobe(b, buf, n, nil);
       +
       +        /* the initial zero has been eaten by the betomp -> mptobe sequence */
       +        if(buf[0] != 2){
       +                werrstr("rsaunpad: expected leading 2");
       +                return nil;
       +        }
       +        for(i=1; i<n; i++)
       +                if(buf[i]==0)
       +                        break;
       +        return betomp(buf+i, n-i, nil);
       +}
       +
       +void
       +mptoberjust(mpint *b, uchar *buf, int len)
       +{
       +        int n;
       +
       +        n = mptobe(b, buf, len, nil);
       +        assert(n >= 0);
       +        if(n < len){
       +                len -= n;
       +                memmove(buf+len, buf, n);
       +                memset(buf, 0, len);
       +        }
       +}
       +
       +static int
       +dorsa(Aconn *a, mpint *mod, mpint *exp, mpint *chal, uchar chalbuf[32])
       +{
       +        AuthRpc *rpc;
       +        mpint *m;
       +        char buf[4096], *p;
       +        mpint *decr, *unpad;
       +
       +        USED(exp);
       +        if((rpc = auth_allocrpc()) == nil){
       +                fprint(2, "ssh-agent: auth_allocrpc: %r\n");
       +                return -1;
       +        }
       +        snprint(buf, sizeof buf, "proto=rsa service=ssh role=client n=%lB ek=%lB", mod, exp);
       +        if(chatty)
       +                fprint(2, "ssh-agent: start %s\n", buf);
       +        if(auth_rpc(rpc, "start", buf, strlen(buf)) != ARok){
       +                fprint(2, "ssh-agent: auth 'start' failed: %r\n");
       +        Die:
       +                auth_freerpc(rpc);
       +                return -1;
       +        }
       +        m = nil;
       +        if(auth_rpc(rpc, "read", nil, 0) != ARok){
       +                fprint(2, "ssh-agent: did not find negotiated key\n");
       +                goto Die;
       +        }
       +        if(chatty)
       +                fprint(2, "read key %s\n", (char*)rpc->arg);
       +        m = strtomp(rpc->arg, nil, 16, nil);
       +        if(mpcmp(m, mod) != 0){
       +                fprint(2, "ssh-agent: found wrong key\n");
       +                mpfree(m);
       +                goto Die;
       +        }
       +        mpfree(m);
       +        
       +        p = mptoa(chal, 16, nil, 0);
       +        if(p == nil){
       +                fprint(2, "ssh-agent: dorsa: mptoa: %r\n");
       +                goto Die;
       +        }
       +        if(chatty)
       +                fprint(2, "ssh-agent: challenge %B => %s\n", chal, p);
       +        if(auth_rpc(rpc, "write", p, strlen(p)) != ARok){
       +                fprint(2, "ssh-agent: dorsa: auth 'write': %r\n");
       +                free(p);
       +                goto Die;
       +        }
       +        free(p);
       +        if(auth_rpc(rpc, "read", nil, 0) != ARok){
       +                fprint(2, "ssh-agent: dorsa: auth 'read': %r\n");
       +                goto Die;
       +        }
       +        decr = strtomp(rpc->arg, nil, 16, nil);
       +        if(chatty)
       +                fprint(2, "ssh-agent: response %s => %B\n", rpc->arg, decr);
       +        if(decr == nil){
       +                fprint(2, "ssh-agent: dorsa: strtomp: %r\n");
       +                goto Die;
       +        }
       +        unpad = rsaunpad(decr);
       +        if(chatty)
       +                fprint(2, "ssh-agent: unpad %B => %B\n", decr, unpad);
       +        if(unpad == nil){
       +                fprint(2, "ssh-agent: dorsa: rsaunpad: %r\n");
       +                mpfree(decr);
       +                goto Die;
       +        }
       +        mpfree(decr);
       +        mptoberjust(unpad, chalbuf, 32);
       +        mpfree(unpad);
       +        auth_freerpc(rpc);
       +        return 0;
       +}
       +
       +int
       +keysign(Msg *mkey, Msg *mdata, Msg *msig)
       +{
       +        char *s;
       +        AuthRpc *rpc;
       +        RSApub *rsa;
       +        DSApub *dsa;
       +        char buf[4096];
       +        uchar digest[SHA1dlen];
       +        
       +        s = getstr(mkey);
       +        if(strcmp(s, "ssh-rsa") == 0){
       +                rsa = getrsapub(mkey);
       +                if(rsa == nil)
       +                        return -1;
       +                snprint(buf, sizeof buf, "proto=rsa service=ssh-rsa role=sign n=%lB ek=%lB",
       +                        rsa->n, rsa->ek);
       +                rsapubfree(rsa);
       +        }else if(strcmp(s, "ssh-dss") == 0){
       +                dsa = getdsapub(mkey);
       +                if(dsa == nil)
       +                        return -1;
       +                snprint(buf, sizeof buf, "proto=dsa service=ssh-dss role=sign p=%lB q=%lB alpha=%lB key=%lB",
       +                        dsa->p, dsa->q, dsa->alpha, dsa->key);
       +                dsapubfree(dsa);
       +        }else{
       +                fprint(2, "ssh-agent: cannot sign key type %s\n", s);
       +                werrstr("unknown key type %s", s);
       +                return -1;
       +        }
       +
       +        if((rpc = auth_allocrpc()) == nil){
       +                fprint(2, "ssh-agent: auth_allocrpc: %r\n");
       +                return -1;
       +        }
       +        if(chatty)
       +                fprint(2, "ssh-agent: start %s\n", buf);
       +        if(auth_rpc(rpc, "start", buf, strlen(buf)) != ARok){
       +                fprint(2, "ssh-agent: auth 'start' failed: %r\n");
       +        Die:
       +                auth_freerpc(rpc);
       +                return -1;
       +        }
       +        sha1(mdata->bp, mdata->ep-mdata->bp, digest, nil);
       +        if(auth_rpc(rpc, "write", digest, SHA1dlen) != ARok){
       +                fprint(2, "ssh-agent: auth 'write in sign failed: %r\n");
       +                goto Die;
       +        }
       +        if(auth_rpc(rpc, "read", nil, 0) != ARok){
       +                fprint(2, "ssh-agent: auth 'read' failed: %r\n");
       +                goto Die;
       +        }
       +        newmsg(msig);
       +        putstr(msig, s);
       +        put4(msig, rpc->narg);
       +        putn(msig, rpc->arg, rpc->narg);
       +        auth_freerpc(rpc);
       +        return 0;
       +}
       +
       +int
       +runmsg(Aconn *a)
       +{
       +        char *p;
       +        int n, nk, type, rt, vers;
       +        mpint *ek, *mod, *chal;
       +        uchar sessid[16], chalbuf[32], digest[MD5dlen];
       +        uint len, flags;
       +        DigestState *s;
       +        Msg m, mkey, mdata, msig;
       +        
       +        if(a->ndata < 4)
       +                return 0;
       +        len = (a->data[0]<<24)|(a->data[1]<<16)|(a->data[2]<<8)|a->data[3];
       +        if(a->ndata < 4+len)
       +                return 0;
       +        m.p = a->data+4;
       +        m.ep = m.p+len;
       +        type = get1(&m);
       +        if(chatty)
       +                fprint(2, "msg %d: %.*H\n", type, len, m.p);
       +        switch(type){
       +        default:
       +        Failure:
       +                newreply(&m, SSH_AGENT_FAILURE);
       +                reply(a, &m);
       +                break;
       +
       +        case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
       +                vers = 1;
       +                newreply(&m, SSH_AGENT_RSA_IDENTITIES_ANSWER);
       +                goto Identities;
       +        case SSH2_AGENTC_REQUEST_IDENTITIES:
       +                vers = 2;
       +                newreply(&m, SSH2_AGENT_IDENTITIES_ANSWER);
       +        Identities:
       +                nk = listkeys(&m, vers);
       +                if(nk < 0){
       +                        free(m.bp);
       +                        goto Failure;
       +                }
       +                if(chatty)
       +                        fprint(2, "request identities\n", nk);
       +                reply(a, &m);
       +                break;
       +
       +        case SSH_AGENTC_RSA_CHALLENGE:
       +                n = get4(&m);
       +                ek = getmp(&m);
       +                mod = getmp(&m);
       +                chal = getmp(&m);
       +                if((p = getn(&m, 16)) == nil){
       +                Failchal:
       +                        mpfree(ek);
       +                        mpfree(mod);
       +                        mpfree(chal);
       +                        goto Failure;
       +                }
       +                memmove(sessid, p, 16);
       +                rt = get4(&m);
       +                if(rt != 1 || dorsa(a, mod, ek, chal, chalbuf) < 0)
       +                        goto Failchal;
       +                s = md5(chalbuf, 32, nil, nil);
       +                if(s == nil)
       +                        goto Failchal;
       +                md5(sessid, 16, digest, s);
       +                
       +                newreply(&m, SSH_AGENT_RSA_RESPONSE);
       +                putn(&m, digest, 16);
       +                reply(a, &m);
       +
       +                mpfree(ek);
       +                mpfree(mod);
       +                mpfree(chal);
       +                break;
       +
       +        case SSH2_AGENTC_SIGN_REQUEST:
       +                if(getm(&m, &mkey) < 0
       +                || getm(&m, &mdata) < 0)
       +                        goto Failure;
       +                flags = get4(&m);
       +                if(flags & SSH_AGENT_OLD_SIGNATURE)
       +                        goto Failure;
       +                if(keysign(&mkey, &mdata, &msig) < 0)
       +                        goto Failure;
       +                if(chatty)
       +                        fprint(2, "signature: %.*H\n",
       +                                msig.p-msig.bp, msig.bp);
       +                newreply(&m, SSH2_AGENT_SIGN_RESPONSE);
       +                putm(&m, &msig);
       +                free(msig.bp);
       +                reply(a, &m);
       +                break;
       +                
       +        case SSH_AGENTC_ADD_RSA_IDENTITY:
       +                /*
       +                        msg: n[4] mod[mp] pubexp[exp] privexp[mp]
       +                                p^-1 mod q[mp] p[mp] q[mp] comment[str]
       +                 */
       +                goto Failure;
       +                
       +        case SSH_AGENTC_REMOVE_RSA_IDENTITY:
       +                /*
       +                        msg: n[4] mod[mp] pubexp[mp]
       +                 */
       +                goto Failure;
       +                
       +        }
       +        
       +        a->ndata -= 4+len;
       +        memmove(a->data, a->data+4+len, a->ndata);
       +        return 1;
       +}
       +
       +void*
       +emalloc(int n)
       +{
       +        void *v;
       +
       +        v = mallocz(n, 1);
       +        if(v == nil){
       +                abort();
       +                sysfatal("out of memory allocating %d", n);
       +        }
       +        return v;
       +}
       +
       +void*
       +erealloc(void *v, int n)
       +{
       +        v = realloc(v, n);
       +        if(v == nil){
       +                abort();
       +                sysfatal("out of memory reallocating %d", n);
       +        }
       +        return v;
       +}
       +