aboutsummaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2005-02-13 18:04:00 +0000
committerrsc <devnull@localhost>2005-02-13 18:04:00 +0000
commitce94dbe662155bd60d6839b5e8c82ad708667bcd (patch)
tree0c405ef046615640061cc6080b9fee516c5c80f8 /src/cmd
parentea77b9ce7c579a2e625806dc01104d5f6929cc43 (diff)
downloadplan9port-ce94dbe662155bd60d6839b5e8c82ad708667bcd.tar.gz
plan9port-ce94dbe662155bd60d6839b5e8c82ad708667bcd.tar.bz2
plan9port-ce94dbe662155bd60d6839b5e8c82ad708667bcd.zip
add ssh-agent via factotum
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/auth/factotum/ctl.c5
-rw-r--r--src/cmd/auth/factotum/dat.h6
-rw-r--r--src/cmd/auth/factotum/dsa.c14
-rw-r--r--src/cmd/auth/factotum/fs.c2
-rw-r--r--src/cmd/auth/factotum/pkcs1.c57
-rw-r--r--src/cmd/auth/factotum/rpc.c35
-rw-r--r--src/cmd/auth/factotum/rsa.c148
-rw-r--r--src/cmd/auth/secstore/secstore.c12
-rw-r--r--src/cmd/auth/ssh-agent.c1047
9 files changed, 1267 insertions, 59 deletions
diff --git a/src/cmd/auth/factotum/ctl.c b/src/cmd/auth/factotum/ctl.c
index df44b97d..85fbe5d8 100644
--- a/src/cmd/auth/factotum/ctl.c
+++ b/src/cmd/auth/factotum/ctl.c
@@ -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;
@@ -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);
diff --git a/src/cmd/auth/factotum/dat.h b/src/cmd/auth/factotum/dat.h
index 11648328..678594a2 100644
--- a/src/cmd/auth/factotum/dat.h
+++ b/src/cmd/auth/factotum/dat.h
@@ -9,6 +9,8 @@ enum
RpcRead,
RpcStart,
RpcWrite,
+ RpcReadHex,
+ RpcWriteHex,
/* thread stack size - big buffers for printing */
STACK = 65536,
@@ -27,6 +29,7 @@ struct Rpc
int op;
void *data;
int count;
+ int hex; /* should result of read be turned into hex? */
};
struct Conv
@@ -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);
diff --git a/src/cmd/auth/factotum/dsa.c b/src/cmd/auth/factotum/dsa.c
index 73f8d296..c0d199e4 100644
--- a/src/cmd/auth/factotum/dsa.c
+++ b/src/cmd/auth/factotum/dsa.c
@@ -23,7 +23,7 @@ xdsasign(Conv *c)
{
int n;
mpint *m;
- uchar digest[SHA1dlen];
+ uchar digest[SHA1dlen], sigblob[20+20];
DSAsig *sig;
Key *k;
@@ -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;
}
@@ -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);
diff --git a/src/cmd/auth/factotum/fs.c b/src/cmd/auth/factotum/fs.c
index f9ad785b..88bbfd8b 100644
--- a/src/cmd/auth/factotum/fs.c
+++ b/src/cmd/auth/factotum/fs.c
@@ -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)
diff --git a/src/cmd/auth/factotum/pkcs1.c b/src/cmd/auth/factotum/pkcs1.c
index fb35ce83..f3278454 100644
--- a/src/cmd/auth/factotum/pkcs1.c
+++ b/src/cmd/auth/factotum/pkcs1.c
@@ -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;
@@ -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;
}
/*
@@ -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)
@@ -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;
}
diff --git a/src/cmd/auth/factotum/rpc.c b/src/cmd/auth/factotum/rpc.c
index e9c163aa..8e2b17a0 100644
--- a/src/cmd/auth/factotum/rpc.c
+++ b/src/cmd/auth/factotum/rpc.c
@@ -40,6 +40,8 @@ char *rpcname[] =
"read",
"start",
"write",
+ "readhex",
+ "writehex",
};
static int
@@ -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");
@@ -161,6 +176,7 @@ rpcexec(Conv *c)
}
/* fall through */
default:
+ Default:
if(!c->active){
if(c->done)
rpcrespond(c, "done");
@@ -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;
}
@@ -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;
}
diff --git a/src/cmd/auth/factotum/rsa.c b/src/cmd/auth/factotum/rsa.c
index 327dbc5b..34ddb784 100644
--- a/src/cmd/auth/factotum/rsa.c
+++ b/src/cmd/auth/factotum/rsa.c
@@ -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";
@@ -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;
}
/*
@@ -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;
@@ -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
};
diff --git a/src/cmd/auth/secstore/secstore.c b/src/cmd/auth/secstore/secstore.c
index 864aa88d..cb6e585e 100644
--- a/src/cmd/auth/secstore/secstore.c
+++ b/src/cmd/auth/secstore/secstore.c
@@ -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);
@@ -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");
diff --git a/src/cmd/auth/ssh-agent.c b/src/cmd/auth/ssh-agent.c
new file mode 100644
index 00000000..f711ff06
--- /dev/null
+++ b/src/cmd/auth/ssh-agent.c
@@ -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;
+}
+