2 * Present factotum in ssh agent clothing.
16 enum /* agent protocol packet types */
19 SSH_AGENTC_REQUEST_RSA_IDENTITIES,
20 SSH_AGENT_RSA_IDENTITIES_ANSWER,
21 SSH_AGENTC_RSA_CHALLENGE,
22 SSH_AGENT_RSA_RESPONSE,
25 SSH_AGENTC_ADD_RSA_IDENTITY,
26 SSH_AGENTC_REMOVE_RSA_IDENTITY,
27 SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES,
29 SSH2_AGENTC_REQUEST_IDENTITIES = 11,
30 SSH2_AGENT_IDENTITIES_ANSWER,
31 SSH2_AGENTC_SIGN_REQUEST,
32 SSH2_AGENT_SIGN_RESPONSE,
34 SSH2_AGENTC_ADD_IDENTITY = 17,
35 SSH2_AGENTC_REMOVE_IDENTITY,
36 SSH2_AGENTC_REMOVE_ALL_IDENTITIES,
37 SSH2_AGENTC_ADD_SMARTCARD_KEY,
38 SSH2_AGENTC_REMOVE_SMARTCARD_KEY,
42 SSH_AGENTC_ADD_RSA_ID_CONSTRAINED,
43 SSH2_AGENTC_ADD_ID_CONSTRAINED,
44 SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED,
46 SSH_AGENT_CONSTRAIN_LIFETIME = 1,
47 SSH_AGENT_CONSTRAIN_CONFIRM = 2,
49 SSH2_AGENT_FAILURE = 30,
51 SSH_COM_AGENT2_FAILURE = 102,
52 SSH_AGENT_OLD_SIGNATURE = 0x01
55 typedef struct Aconn Aconn;
65 typedef struct Msg Msg;
77 char *factotum = "factotum";
79 void agentproc(void *v);
81 void* erealloc(void *v, int n);
82 void listenproc(void *v);
84 void listkeystext(void);
89 fprint(2, "usage: 9 ssh-agent [-D] [factotum]\n");
90 threadexitsall("usage");
94 threadmaybackground(void)
100 threadmain(int argc, char **argv)
102 int fd, pid, export, dotextlist;
104 char sock[200], addr[200];
110 fmtinstall('B', mpfmt);
111 fmtinstall('H', encodefmt);
112 fmtinstall('[', encodefmt);
140 snprint(sock, sizeof sock, "%s/ssh-agent.socket", ns);
142 x = ((uvlong)fastrand()<<32) | fastrand();
143 x ^= ((uvlong)fastrand()<<32) | fastrand();
144 snprint(dir, sizeof dir, "/tmp/ssh-%llux", x);
145 if((fd = create(dir, OREAD, DMDIR|0700)) < 0)
146 sysfatal("mkdir %s: %r", dir);
148 snprint(sock, sizeof sock, "%s/agent.%d", dir, pid);
150 snprint(addr, sizeof addr, "unix!%s", sock);
152 if((afd = announce(addr, adir)) < 0)
153 sysfatal("announce %s: %r", addr);
155 print("SSH_AUTH_SOCK=%s;\n", sock);
157 print("export SSH_AUTH_SOCK;\n");
158 print("SSH_AGENT_PID=%d;\n", pid);
160 print("export SSH_AGENT_PID;\n");
163 proccreate(listenproc, nil, STACK);
174 a = emalloc(sizeof *a);
175 a->ctl = listen(adir, a->dir);
177 sysfatal("listen: %r");
178 proccreate(agentproc, a, STACK);
189 a->fd = accept(a->ctl, a->dir);
193 a->data = erealloc(a->data, a->ndata+1024);
194 n = read(a->fd, a->data+a->ndata, 1024);
221 x = (m->p[0]<<8)|m->p[1];
232 x = (m->p[0]<<24)|(m->p[1]<<16)|(m->p[2]<<8)|m->p[3];
272 if((p=getn(m, n)) == nil)
274 return betomp(p, n, nil);
284 if((p = getn(m, n)) == nil)
286 return betomp(p, n, nil);
292 memset(m, 0, sizeof *m);
299 memset(m->bp, 0, m->ep-m->bp);
302 memset(m, 0, sizeof *m);
306 getm(Msg *m, Msg *mm)
312 if((p = getn(m, n)) == nil)
322 ensure(Msg *m, int n)
331 p = emalloc(m->ep - m->bp);
332 memmove(p, m->bp, m->ep - m->bp);
335 m->ep += m->bp - obp;
342 m->bp = erealloc(m->bp, len+n+1024);
344 m->ep += m->bp - obp;
384 putn(Msg *m, void *a, uint n)
393 putmp(Msg *m, mpint *b)
402 mptobe(b, p, n, nil);
406 putmp2(Msg *m, mpint *b)
411 if(mpcmp(b, mpzero) == 0){
423 mptobe(b, p, n, nil);
427 putstr(Msg *m, char *s)
437 putm(Msg *m, Msg *mm)
447 newreply(Msg *m, int type)
449 memset(m, 0, sizeof *m);
455 reply(Aconn *a, Msg *m)
460 n = (m->p - m->bp) - 4;
467 fprint(2, "respond %d t=%d: %.*H\n", n, p[4], n, m->bp+4);
468 write(a->fd, p, n+4);
472 typedef struct Key Key;
481 find(char **f, int nf, char *k)
486 for(i=1; i<nf; i++) /* i=1: f[0] is "key" */
487 if(strncmp(f[i], k, len) == 0 && f[i][len] == '=')
493 putrsa1(Msg *m, char **f, int nf)
498 p = find(f, nf, "n");
499 if(p == nil || (mod = strtomp(p, nil, 16, nil)) == nil)
501 p = find(f, nf, "ek");
502 if(p == nil || (ek = strtomp(p, nil, 16, nil)) == nil){
506 p = find(f, nf, "comment");
509 put4(m, mpsignif(mod));
519 printattr(char **f, int nf)
530 printrsa1(char **f, int nf)
535 p = find(f, nf, "n");
536 if(p == nil || (mod = strtomp(p, nil, 16, nil)) == nil)
538 p = find(f, nf, "ek");
539 if(p == nil || (ek = strtomp(p, nil, 16, nil)) == nil){
543 p = find(f, nf, "comment");
549 print("%d %.10B %.10B %s\n", mpsignif(mod), ek, mod, p);
555 putrsa(Msg *m, char **f, int nf)
560 p = find(f, nf, "n");
561 if(p == nil || (mod = strtomp(p, nil, 16, nil)) == nil)
563 p = find(f, nf, "ek");
564 if(p == nil || (ek = strtomp(p, nil, 16, nil)) == nil){
568 putstr(m, "ssh-rsa");
586 if(k->ek == nil || k->n == nil){
594 putdsa(Msg *m, char **f, int nf)
598 mpint *dp, *dq, *dalpha, *dkey;
601 dp = dq = dalpha = dkey = nil;
602 p = find(f, nf, "p");
603 if(p == nil || (dp = strtomp(p, nil, 16, nil)) == nil)
605 p = find(f, nf, "q");
606 if(p == nil || (dq = strtomp(p, nil, 16, nil)) == nil)
608 p = find(f, nf, "alpha");
609 if(p == nil || (dalpha = strtomp(p, nil, 16, nil)) == nil)
611 p = find(f, nf, "key");
612 if(p == nil || (dkey = strtomp(p, nil, 16, nil)) == nil)
614 putstr(m, "ssh-dss");
629 putkey2(Msg *m, int (*put)(Msg*,char**,int), char **f, int nf)
635 if(put(&mm, f, nf) < 0)
639 p = find(f, nf, "comment");
647 printkey(char *type, int (*put)(Msg*,char**,int), char **f, int nf)
653 if(put(&m, f, nf) < 0)
655 p = find(f, nf, "comment");
660 print("%s %.*[ %s\n", type, m.p-m.bp, m.bp, p);
675 k->alpha = getmp2(m);
677 if(!k->p || !k->q || !k->alpha || !k->key){
685 listkeys(Msg *m, int version)
687 char buf[8192+1], *line[100], *f[20], *p, *s;
689 int i, n, nl, nf, nk;
695 if((fid = nsopen(factotum, nil, "ctl", OREAD)) == nil){
696 fprint(2, "ssh-agent: open factotum: %r\n");
700 if((n = fsread(fid, buf, sizeof buf-1)) <= 0)
703 nl = getfields(buf, line, nelem(line), 1, "\n");
705 nf = tokenize(line[i], f, nelem(f));
706 if(nf == 0 || strcmp(f[0], "key") != 0)
708 p = find(f, nf, "proto");
711 s = find(f, nf, "service");
715 if(version == 1 && strcmp(p, "rsa") == 0 && strcmp(s, "ssh") == 0)
716 if(putrsa1(m, f, nf) >= 0)
718 if(version == 2 && strcmp(p, "rsa") == 0 && strcmp(s, "ssh-rsa") == 0)
719 if(putkey2(m, putrsa, f, nf) >= 0)
721 if(version == 2 && strcmp(p, "dsa") == 0 && strcmp(s, "ssh-dss") == 0)
722 if(putkey2(m, putdsa, f, nf) >= 0)
727 fprint(2, "sending %d keys\n", nk);
729 m->bp[pnk+0] = (nk>>24)&0xFF;
730 m->bp[pnk+1] = (nk>>16)&0xFF;
731 m->bp[pnk+2] = (nk>>8)&0xFF;
732 m->bp[pnk+3] = nk&0xFF;
739 char buf[8192+1], *line[100], *f[20], *p, *s;
743 if((fid = nsopen(factotum, nil, "ctl", OREAD)) == nil){
744 fprint(2, "ssh-agent: open factotum: %r\n");
748 if((n = fsread(fid, buf, sizeof buf-1)) <= 0)
751 nl = getfields(buf, line, nelem(line), 1, "\n");
753 nf = tokenize(line[i], f, nelem(f));
754 if(nf == 0 || strcmp(f[0], "key") != 0)
756 p = find(f, nf, "proto");
759 s = find(f, nf, "service");
763 if(strcmp(p, "rsa") == 0 && strcmp(s, "ssh") == 0)
765 if(strcmp(p, "rsa") == 0 && strcmp(s, "ssh-rsa") == 0)
766 printkey("ssh-rsa", putrsa, f, nf);
767 if(strcmp(p, "dsa") == 0 && strcmp(s, "ssh-dss") == 0)
768 printkey("ssh-dss", putdsa, f, nf);
781 n = (mpsignif(b)+7)/8;
783 werrstr("rsaunpad: too big");
786 mptobe(b, buf, n, nil);
788 /* the initial zero has been eaten by the betomp -> mptobe sequence */
790 werrstr("rsaunpad: expected leading 2");
796 return betomp(buf+i, n-i, nil);
800 mptoberjust(mpint *b, uchar *buf, int len)
804 n = mptobe(b, buf, len, nil);
808 memmove(buf+len, buf, n);
814 dorsa(Aconn *a, mpint *mod, mpint *exp, mpint *chal, uchar chalbuf[32])
821 if((rpc = auth_allocrpc()) == nil){
822 fprint(2, "ssh-agent: auth_allocrpc: %r\n");
825 snprint(buf, sizeof buf, "proto=rsa service=ssh role=decrypt n=%lB ek=%lB", mod, exp);
827 fprint(2, "ssh-agent: start %s\n", buf);
828 if(auth_rpc(rpc, "start", buf, strlen(buf)) != ARok){
829 fprint(2, "ssh-agent: auth 'start' failed: %r\n");
835 p = mptoa(chal, 16, nil, 0);
837 fprint(2, "ssh-agent: dorsa: mptoa: %r\n");
841 fprint(2, "ssh-agent: challenge %B => %s\n", chal, p);
842 if(auth_rpc(rpc, "writehex", p, strlen(p)) != ARok){
843 fprint(2, "ssh-agent: dorsa: auth 'write': %r\n");
848 if(auth_rpc(rpc, "readhex", nil, 0) != ARok){
849 fprint(2, "ssh-agent: dorsa: auth 'read': %r\n");
852 decr = strtomp(rpc->arg, nil, 16, nil);
854 fprint(2, "ssh-agent: response %s => %B\n", rpc->arg, decr);
856 fprint(2, "ssh-agent: dorsa: strtomp: %r\n");
859 unpad = rsaunpad(decr);
861 fprint(2, "ssh-agent: unpad %B => %B\n", decr, unpad);
863 fprint(2, "ssh-agent: dorsa: rsaunpad: %r\n");
868 mptoberjust(unpad, chalbuf, 32);
875 keysign(Msg *mkey, Msg *mdata, Msg *msig)
882 uchar digest[SHA1dlen];
885 if(strcmp(s, "ssh-rsa") == 0){
886 rsa = getrsapub(mkey);
889 snprint(buf, sizeof buf, "proto=rsa service=ssh-rsa role=sign n=%lB ek=%lB",
892 }else if(strcmp(s, "ssh-dss") == 0){
893 dsa = getdsapub(mkey);
896 snprint(buf, sizeof buf, "proto=dsa service=ssh-dss role=sign p=%lB q=%lB alpha=%lB key=%lB",
897 dsa->p, dsa->q, dsa->alpha, dsa->key);
900 fprint(2, "ssh-agent: cannot sign key type %s\n", s);
901 werrstr("unknown key type %s", s);
905 if((rpc = auth_allocrpc()) == nil){
906 fprint(2, "ssh-agent: auth_allocrpc: %r\n");
910 fprint(2, "ssh-agent: start %s\n", buf);
911 if(auth_rpc(rpc, "start", buf, strlen(buf)) != ARok){
912 fprint(2, "ssh-agent: auth 'start' failed: %r\n");
917 sha1(mdata->bp, mdata->ep-mdata->bp, digest, nil);
918 if(auth_rpc(rpc, "write", digest, SHA1dlen) != ARok){
919 fprint(2, "ssh-agent: auth 'write in sign failed: %r\n");
922 if(auth_rpc(rpc, "read", nil, 0) != ARok){
923 fprint(2, "ssh-agent: auth 'read' failed: %r\n");
928 put4(msig, rpc->narg);
929 putn(msig, rpc->arg, rpc->narg);
938 int n, nk, type, rt, vers;
939 mpint *ek, *mod, *chal;
940 uchar sessid[16], chalbuf[32], digest[MD5dlen];
943 Msg m, mkey, mdata, msig;
947 len = (a->data[0]<<24)|(a->data[1]<<16)|(a->data[2]<<8)|a->data[3];
954 fprint(2, "msg %d: %.*H\n", type, len, m.p);
958 newreply(&m, SSH_AGENT_FAILURE);
962 case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
964 newreply(&m, SSH_AGENT_RSA_IDENTITIES_ANSWER);
966 case SSH2_AGENTC_REQUEST_IDENTITIES:
968 newreply(&m, SSH2_AGENT_IDENTITIES_ANSWER);
970 nk = listkeys(&m, vers);
976 fprint(2, "request identities\n", nk);
980 case SSH_AGENTC_RSA_CHALLENGE:
986 if((p = (char*)getn(&m, 16)) == nil){
993 memmove(sessid, p, 16);
995 if(rt != 1 || dorsa(a, mod, ek, chal, chalbuf) < 0)
997 s = md5(chalbuf, 32, nil, nil);
1000 md5(sessid, 16, digest, s);
1001 print("md5 %.*H %.*H => %.*H\n", 32, chalbuf, 16, sessid, MD5dlen, digest);
1003 newreply(&m, SSH_AGENT_RSA_RESPONSE);
1004 putn(&m, digest, 16);
1012 case SSH2_AGENTC_SIGN_REQUEST:
1013 if(getm(&m, &mkey) == nil
1014 || getm(&m, &mdata) == nil)
1017 if(flags & SSH_AGENT_OLD_SIGNATURE)
1019 if(keysign(&mkey, &mdata, &msig) < 0)
1022 fprint(2, "signature: %.*H\n",
1023 msig.p-msig.bp, msig.bp);
1024 newreply(&m, SSH2_AGENT_SIGN_RESPONSE);
1030 case SSH_AGENTC_ADD_RSA_IDENTITY:
1032 msg: n[4] mod[mp] pubexp[exp] privexp[mp]
1033 p^-1 mod q[mp] p[mp] q[mp] comment[str]
1037 case SSH_AGENTC_REMOVE_RSA_IDENTITY:
1039 msg: n[4] mod[mp] pubexp[mp]
1046 memmove(a->data, a->data+4+len, a->ndata);
1058 sysfatal("out of memory allocating %d", n);
1064 erealloc(void *v, int n)
1069 sysfatal("out of memory reallocating %d", n);