| /* |
| * 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); |
| 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; |
| } |
| |