|  | /* | 
|  | * 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; | 
|  | int bpalloc; | 
|  | }; | 
|  |  | 
|  | 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], *ns; | 
|  | char sock[200], addr[200]; | 
|  | uvlong x; | 
|  |  | 
|  | export = 0; | 
|  | dotextlist = 0; | 
|  | pid = getpid(); | 
|  | fmtinstall('B', mpfmt); | 
|  | fmtinstall('H', encodefmt); | 
|  | fmtinstall('[', encodefmt); | 
|  |  | 
|  | ARGBEGIN{ | 
|  | case '9': | 
|  | chatty9pclient++; | 
|  | break; | 
|  | 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(); | 
|  |  | 
|  | ns = getns(); | 
|  | snprint(sock, sizeof sock, "%s/ssh-agent.socket", ns); | 
|  | if(0){ | 
|  | 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); | 
|  |  | 
|  | 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); | 
|  | rfork(RFNOTEG); | 
|  | proccreate(listenproc, nil, STACK); | 
|  | 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 (char*)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); | 
|  | } | 
|  |  | 
|  | void | 
|  | newmsg(Msg *m) | 
|  | { | 
|  | memset(m, 0, sizeof *m); | 
|  | } | 
|  |  | 
|  | void | 
|  | mreset(Msg *m) | 
|  | { | 
|  | if(m->bpalloc){ | 
|  | memset(m->bp, 0, m->ep-m->bp); | 
|  | free(m->bp); | 
|  | } | 
|  | memset(m, 0, sizeof *m); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | mm->bpalloc = 0; | 
|  | return mm; | 
|  | } | 
|  |  | 
|  | uchar* | 
|  | ensure(Msg *m, int n) | 
|  | { | 
|  | int len; | 
|  | uchar *p; | 
|  | uchar *obp; | 
|  |  | 
|  | if(m->bp == nil) | 
|  | m->bpalloc = 1; | 
|  | if(!m->bpalloc){ | 
|  | p = emalloc(m->ep - m->bp); | 
|  | memmove(p, m->bp, m->ep - m->bp); | 
|  | obp = m->bp; | 
|  | m->bp = p; | 
|  | m->ep += m->bp - obp; | 
|  | m->p += m->bp - obp; | 
|  | m->bpalloc = 1; | 
|  | } | 
|  | len = m->ep - m->bp; | 
|  | if(m->p+n > m->ep){ | 
|  | obp = m->bp; | 
|  | m->bp = erealloc(m->bp, len+n+1024); | 
|  | m->p += m->bp - obp; | 
|  | m->ep += m->bp - obp; | 
|  | m->ep += 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 | 
|  | 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 t=%d: %.*H\n", n, p[4], n, m->bp+4); | 
|  | write(a->fd, p, n+4); | 
|  | mreset(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); | 
|  | mreset(&mm); | 
|  | 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); | 
|  | mreset(&m); | 
|  | 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; | 
|  | int pnk; | 
|  | int i, n, nl, nf, nk; | 
|  | CFid *fid; | 
|  |  | 
|  | nk = 0; | 
|  | pnk = m->p - m->bp; | 
|  | 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++; | 
|  | } | 
|  | } | 
|  | if(chatty) | 
|  | fprint(2, "sending %d keys\n", nk); | 
|  | fsclose(fid); | 
|  | m->bp[pnk+0] = (nk>>24)&0xFF; | 
|  | m->bp[pnk+1] = (nk>>16)&0xFF; | 
|  | m->bp[pnk+2] = (nk>>8)&0xFF; | 
|  | m->bp[pnk+3] = nk&0xFF; | 
|  | return nk; | 
|  | } | 
|  |  | 
|  | void | 
|  | listkeystext(void) | 
|  | { | 
|  | char buf[8192+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; | 
|  | 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=decrypt 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; | 
|  | } | 
|  |  | 
|  | 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, "writehex", p, strlen(p)) != ARok){ | 
|  | fprint(2, "ssh-agent: dorsa: auth 'write': %r\n"); | 
|  | free(p); | 
|  | goto Die; | 
|  | } | 
|  | free(p); | 
|  | if(auth_rpc(rpc, "readhex", 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){ | 
|  | mreset(&m); | 
|  | goto Failure; | 
|  | } | 
|  | if(chatty) | 
|  | fprint(2, "request identities\n", nk); | 
|  | reply(a, &m); | 
|  | break; | 
|  |  | 
|  | case SSH_AGENTC_RSA_CHALLENGE: | 
|  | n = get4(&m); | 
|  | USED(n); | 
|  | ek = getmp(&m); | 
|  | mod = getmp(&m); | 
|  | chal = getmp(&m); | 
|  | if((p = (char*)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); | 
|  | print("md5 %.*H %.*H => %.*H\n", 32, chalbuf, 16, sessid, MD5dlen, digest); | 
|  |  | 
|  | 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) == nil | 
|  | || getm(&m, &mdata) == nil) | 
|  | 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); | 
|  | mreset(&msig); | 
|  | 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; | 
|  | } | 
|  |  |