Blob


1 /*
2 * Present factotum in ssh agent clothing.
3 */
4 #include <u.h>
5 #include <libc.h>
6 #include <mp.h>
7 #include <libsec.h>
8 #include <auth.h>
9 #include <thread.h>
10 #include <9pclient.h>
12 enum
13 {
14 STACK = 65536
15 };
16 enum /* agent protocol packet types */
17 {
18 SSH_AGENTC_NONE = 0,
19 SSH_AGENTC_REQUEST_RSA_IDENTITIES,
20 SSH_AGENT_RSA_IDENTITIES_ANSWER,
21 SSH_AGENTC_RSA_CHALLENGE,
22 SSH_AGENT_RSA_RESPONSE,
23 SSH_AGENT_FAILURE,
24 SSH_AGENT_SUCCESS,
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,
40 SSH_AGENTC_LOCK,
41 SSH_AGENTC_UNLOCK,
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,
53 };
55 typedef struct Aconn Aconn;
56 struct Aconn
57 {
58 uchar *data;
59 uint ndata;
60 int ctl;
61 int fd;
62 char dir[40];
63 };
65 typedef struct Msg Msg;
66 struct Msg
67 {
68 uchar *bp;
69 uchar *p;
70 uchar *ep;
71 };
73 char adir[40];
74 int afd;
75 int chatty;
76 char *factotum = "factotum";
78 void agentproc(void *v);
79 void* emalloc(int n);
80 void* erealloc(void *v, int n);
81 void listenproc(void *v);
82 int runmsg(Aconn *a);
83 void listkeystext(void);
85 void
86 usage(void)
87 {
88 fprint(2, "usage: 9 ssh-agent [-D] [factotum]\n");
89 threadexitsall("usage");
90 }
92 void
93 threadmain(int argc, char **argv)
94 {
95 int fd, pid, export, dotextlist;
96 char dir[100], *ns;
97 char sock[200], addr[200];
98 uvlong x;
100 export = 0;
101 dotextlist = 0;
102 pid = getpid();
103 fmtinstall('B', mpfmt);
104 fmtinstall('H', encodefmt);
105 fmtinstall('[', encodefmt);
107 ARGBEGIN{
108 case '9':
109 chatty9pclient++;
110 break;
111 case 'D':
112 chatty++;
113 break;
114 case 'e':
115 export = 1;
116 break;
117 case 'l':
118 dotextlist = 1;
119 break;
120 default:
121 usage();
122 }ARGEND
124 if(argc > 1)
125 usage();
126 if(argc == 1)
127 factotum = argv[0];
129 if(dotextlist)
130 listkeystext();
132 ns = getns();
133 snprint(sock, sizeof sock, "%s/ssh-agent.socket", ns);
134 if(0){
135 x = ((uvlong)fastrand()<<32) | fastrand();
136 x ^= ((uvlong)fastrand()<<32) | fastrand();
137 snprint(dir, sizeof dir, "/tmp/ssh-%llux", x);
138 if((fd = create(dir, OREAD, DMDIR|0700)) < 0)
139 sysfatal("mkdir %s: %r", dir);
140 close(fd);
141 snprint(sock, sizeof sock, "%s/agent.%d", dir, pid);
143 snprint(addr, sizeof addr, "unix!%s", sock);
145 if((afd = announce(addr, adir)) < 0)
146 sysfatal("announce %s: %r", addr);
148 print("SSH_AUTH_SOCK=%s;\n", sock);
149 if(export)
150 print("export SSH_AUTH_SOCK;\n");
151 print("SSH_AGENT_PID=%d;\n", pid);
152 if(export)
153 print("export SSH_AGENT_PID;\n");
154 close(1);
155 rfork(RFNOTEG);
156 proccreate(listenproc, nil, STACK);
157 threadexits(0);
160 void
161 listenproc(void *v)
163 Aconn *a;
165 USED(v);
166 for(;;){
167 a = emalloc(sizeof *a);
168 a->ctl = listen(adir, a->dir);
169 if(a->ctl < 0)
170 sysfatal("listen: %r");
171 proccreate(agentproc, a, STACK);
175 void
176 agentproc(void *v)
178 Aconn *a;
179 int n;
181 a = v;
182 a->fd = accept(a->ctl, a->dir);
183 close(a->ctl);
184 a->ctl = -1;
185 for(;;){
186 a->data = erealloc(a->data, a->ndata+1024);
187 n = read(a->fd, a->data+a->ndata, 1024);
188 if(n <= 0)
189 break;
190 a->ndata += n;
191 while(runmsg(a))
194 close(a->fd);
195 free(a);
196 threadexits(nil);
199 int
200 get1(Msg *m)
202 if(m->p >= m->ep)
203 return 0;
204 return *m->p++;
207 int
208 get2(Msg *m)
210 uint x;
212 if(m->p+2 > m->ep)
213 return 0;
214 x = (m->p[0]<<8)|m->p[1];
215 m->p += 2;
216 return x;
219 int
220 get4(Msg *m)
222 uint x;
223 if(m->p+4 > m->ep)
224 return 0;
225 x = (m->p[0]<<24)|(m->p[1]<<16)|(m->p[2]<<8)|m->p[3];
226 m->p += 4;
227 return x;
230 uchar*
231 getn(Msg *m, uint n)
233 uchar *p;
235 if(m->p+n > m->ep)
236 return nil;
237 p = m->p;
238 m->p += n;
239 return p;
242 char*
243 getstr(Msg *m)
245 uint n;
246 uchar *p;
248 n = get4(m);
249 p = getn(m, n);
250 if(p == nil)
251 return nil;
252 p--;
253 memmove(p, p+1, n);
254 p[n] = 0;
255 return (char*)p;
258 mpint*
259 getmp(Msg *m)
261 int n;
262 uchar *p;
264 n = (get2(m)+7)/8;
265 if((p=getn(m, n)) == nil)
266 return nil;
267 return betomp(p, n, nil);
270 mpint*
271 getmp2(Msg *m)
273 int n;
274 uchar *p;
276 n = get4(m);
277 if((p = getn(m, n)) == nil)
278 return nil;
279 return betomp(p, n, nil);
282 Msg*
283 getm(Msg *m, Msg *mm)
285 uint n;
286 uchar *p;
288 n = get4(m);
289 if((p = getn(m, n)) == nil)
290 return nil;
291 mm->bp = p;
292 mm->p = p;
293 mm->ep = p+n;
294 return mm;
297 uchar*
298 ensure(Msg *m, int n)
300 int len, plen;
301 uchar *p;
303 len = m->ep - m->bp;
304 if(m->p+n > m->ep){
305 plen = m->p - m->bp;
306 m->bp = erealloc(m->bp, len+n+1024);
307 m->p = m->bp+plen;
308 m->ep = m->bp+len+n+1024;
310 p = m->p;
311 m->p += n;
312 return p;
315 void
316 put4(Msg *m, uint n)
318 uchar *p;
320 p = ensure(m, 4);
321 p[0] = (n>>24)&0xFF;
322 p[1] = (n>>16)&0xFF;
323 p[2] = (n>>8)&0xFF;
324 p[3] = n&0xFF;
327 void
328 put2(Msg *m, uint n)
330 uchar *p;
332 p = ensure(m, 2);
333 p[0] = (n>>8)&0xFF;
334 p[1] = n&0xFF;
337 void
338 put1(Msg *m, uint n)
340 uchar *p;
342 p = ensure(m, 1);
343 p[0] = n&0xFF;
346 void
347 putn(Msg *m, void *a, uint n)
349 uchar *p;
351 p = ensure(m, n);
352 memmove(p, a, n);
355 void
356 putmp(Msg *m, mpint *b)
358 int bits, n;
359 uchar *p;
361 bits = mpsignif(b);
362 put2(m, bits);
363 n = (bits+7)/8;
364 p = ensure(m, n);
365 mptobe(b, p, n, nil);
368 void
369 putmp2(Msg *m, mpint *b)
371 int bits, n;
372 uchar *p;
374 if(mpcmp(b, mpzero) == 0){
375 put4(m, 0);
376 return;
378 bits = mpsignif(b);
379 n = (bits+7)/8;
380 if(bits%8 == 0){
381 put4(m, n+1);
382 put1(m, 0);
383 }else
384 put4(m, n);
385 p = ensure(m, n);
386 mptobe(b, p, n, nil);
389 void
390 putstr(Msg *m, char *s)
392 int n;
394 n = strlen(s);
395 put4(m, n);
396 putn(m, s, n);
399 void
400 putm(Msg *m, Msg *mm)
402 uint n;
404 n = mm->p - mm->bp;
405 put4(m, n);
406 putn(m, mm->bp, n);
409 void
410 newmsg(Msg *m)
412 memset(m, 0, sizeof *m);
415 void
416 newreply(Msg *m, int type)
418 memset(m, 0, sizeof *m);
419 put4(m, 0);
420 put1(m, type);
423 void
424 reply(Aconn *a, Msg *m)
426 uint n;
427 uchar *p;
429 n = (m->p - m->bp) - 4;
430 p = m->bp;
431 p[0] = (n>>24)&0xFF;
432 p[1] = (n>>16)&0xFF;
433 p[2] = (n>>8)&0xFF;
434 p[3] = n&0xFF;
435 if(chatty)
436 fprint(2, "respond %d: %.*H\n", p[4], n, m->bp+4);
437 write(a->fd, p, n+4);
438 free(p);
439 memset(m, 0, sizeof *m);
442 typedef struct Key Key;
443 struct Key
445 mpint *mod;
446 mpint *ek;
447 char *comment;
448 };
450 static char*
451 find(char **f, int nf, char *k)
453 int i, len;
455 len = strlen(k);
456 for(i=1; i<nf; i++) /* i=1: f[0] is "key" */
457 if(strncmp(f[i], k, len) == 0 && f[i][len] == '=')
458 return f[i]+len+1;
459 return nil;
462 static int
463 putrsa1(Msg *m, char **f, int nf)
465 char *p;
466 mpint *mod, *ek;
468 p = find(f, nf, "n");
469 if(p == nil || (mod = strtomp(p, nil, 16, nil)) == nil)
470 return -1;
471 p = find(f, nf, "ek");
472 if(p == nil || (ek = strtomp(p, nil, 16, nil)) == nil){
473 mpfree(mod);
474 return -1;
476 p = find(f, nf, "comment");
477 if(p == nil)
478 p = "";
479 put4(m, mpsignif(mod));
480 putmp(m, ek);
481 putmp(m, mod);
482 putstr(m, p);
483 mpfree(mod);
484 mpfree(ek);
485 return 0;
488 void
489 printattr(char **f, int nf)
491 int i;
493 print("#");
494 for(i=0; i<nf; i++)
495 print(" %s", f[i]);
496 print("\n");
499 void
500 printrsa1(char **f, int nf)
502 char *p;
503 mpint *mod, *ek;
505 p = find(f, nf, "n");
506 if(p == nil || (mod = strtomp(p, nil, 16, nil)) == nil)
507 return;
508 p = find(f, nf, "ek");
509 if(p == nil || (ek = strtomp(p, nil, 16, nil)) == nil){
510 mpfree(mod);
511 return;
513 p = find(f, nf, "comment");
514 if(p == nil)
515 p = "";
517 if(chatty)
518 printattr(f, nf);
519 print("%d %.10B %.10B %s\n", mpsignif(mod), ek, mod, p);
520 mpfree(ek);
521 mpfree(mod);
524 static int
525 putrsa(Msg *m, char **f, int nf)
527 char *p;
528 mpint *mod, *ek;
530 p = find(f, nf, "n");
531 if(p == nil || (mod = strtomp(p, nil, 16, nil)) == nil)
532 return -1;
533 p = find(f, nf, "ek");
534 if(p == nil || (ek = strtomp(p, nil, 16, nil)) == nil){
535 mpfree(mod);
536 return -1;
538 putstr(m, "ssh-rsa");
539 putmp2(m, ek);
540 putmp2(m, mod);
541 mpfree(ek);
542 mpfree(mod);
543 return 0;
546 RSApub*
547 getrsapub(Msg *m)
549 RSApub *k;
551 k = rsapuballoc();
552 if(k == nil)
553 return nil;
554 k->ek = getmp2(m);
555 k->n = getmp2(m);
556 if(k->ek == nil || k->n == nil){
557 rsapubfree(k);
558 return nil;
560 return k;
563 static int
564 putdsa(Msg *m, char **f, int nf)
566 char *p;
567 int ret;
568 mpint *dp, *dq, *dalpha, *dkey;
570 ret = -1;
571 dp = dq = dalpha = dkey = nil;
572 p = find(f, nf, "p");
573 if(p == nil || (dp = strtomp(p, nil, 16, nil)) == nil)
574 goto out;
575 p = find(f, nf, "q");
576 if(p == nil || (dq = strtomp(p, nil, 16, nil)) == nil)
577 goto out;
578 p = find(f, nf, "alpha");
579 if(p == nil || (dalpha = strtomp(p, nil, 16, nil)) == nil)
580 goto out;
581 p = find(f, nf, "key");
582 if(p == nil || (dkey = strtomp(p, nil, 16, nil)) == nil)
583 goto out;
584 putstr(m, "ssh-dss");
585 putmp2(m, dp);
586 putmp2(m, dq);
587 putmp2(m, dalpha);
588 putmp2(m, dkey);
589 ret = 0;
590 out:
591 mpfree(dp);
592 mpfree(dq);
593 mpfree(dalpha);
594 mpfree(dkey);
595 return ret;
598 static int
599 putkey2(Msg *m, int (*put)(Msg*,char**,int), char **f, int nf)
601 char *p;
602 Msg mm;
604 newmsg(&mm);
605 if(put(&mm, f, nf) < 0)
606 return -1;
607 putm(m, &mm);
608 free(mm.bp);
609 p = find(f, nf, "comment");
610 if(p == nil)
611 p = "";
612 putstr(m, p);
613 return 0;
616 static int
617 printkey(char *type, int (*put)(Msg*,char**,int), char **f, int nf)
619 Msg m;
620 char *p;
622 newmsg(&m);
623 if(put(&m, f, nf) < 0)
624 return -1;
625 p = find(f, nf, "comment");
626 if(p == nil)
627 p = "";
628 if(chatty)
629 printattr(f, nf);
630 print("%s %.*[ %s\n", type, m.p-m.bp, m.bp, p);
631 free(m.bp);
632 return 0;
635 DSApub*
636 getdsapub(Msg *m)
638 DSApub *k;
640 k = dsapuballoc();
641 if(k == nil)
642 return nil;
643 k->p = getmp2(m);
644 k->q = getmp2(m);
645 k->alpha = getmp2(m);
646 k->key = getmp2(m);
647 if(!k->p || !k->q || !k->alpha || !k->key){
648 dsapubfree(k);
649 return nil;
651 return k;
654 static int
655 listkeys(Msg *m, int version)
657 char buf[8192+1], *line[100], *f[20], *p, *s;
658 uchar *pnk;
659 int i, n, nl, nf, nk;
660 CFid *fid;
662 nk = 0;
663 pnk = m->p;
664 put4(m, 0);
665 if((fid = nsopen(factotum, nil, "ctl", OREAD)) == nil){
666 fprint(2, "ssh-agent: open factotum: %r\n");
667 return -1;
669 for(;;){
670 if((n = fsread(fid, buf, sizeof buf-1)) <= 0)
671 break;
672 buf[n] = 0;
673 nl = getfields(buf, line, nelem(line), 1, "\n");
674 for(i=0; i<nl; i++){
675 nf = tokenize(line[i], f, nelem(f));
676 if(nf == 0 || strcmp(f[0], "key") != 0)
677 continue;
678 p = find(f, nf, "proto");
679 if(p == nil)
680 continue;
681 s = find(f, nf, "service");
682 if(s == nil)
683 continue;
685 if(version == 1 && strcmp(p, "rsa") == 0 && strcmp(s, "ssh") == 0)
686 if(putrsa1(m, f, nf) >= 0)
687 nk++;
688 if(version == 2 && strcmp(p, "rsa") == 0 && strcmp(s, "ssh-rsa") == 0)
689 if(putkey2(m, putrsa, f, nf) >= 0)
690 nk++;
691 if(version == 2 && strcmp(p, "dsa") == 0 && strcmp(s, "ssh-dss") == 0)
692 if(putkey2(m, putdsa, f, nf) >= 0)
693 nk++;
696 fsclose(fid);
697 pnk[0] = (nk>>24)&0xFF;
698 pnk[1] = (nk>>16)&0xFF;
699 pnk[2] = (nk>>8)&0xFF;
700 pnk[3] = nk&0xFF;
701 return nk;
704 void
705 listkeystext(void)
707 char buf[4096+1], *line[100], *f[20], *p, *s;
708 int i, n, nl, nf;
709 CFid *fid;
711 if((fid = nsopen(factotum, nil, "ctl", OREAD)) == nil){
712 fprint(2, "ssh-agent: open factotum: %r\n");
713 return;
715 for(;;){
716 if((n = fsread(fid, buf, sizeof buf-1)) <= 0)
717 break;
718 buf[n] = 0;
719 nl = getfields(buf, line, nelem(line), 1, "\n");
720 for(i=0; i<nl; i++){
721 nf = tokenize(line[i], f, nelem(f));
722 if(nf == 0 || strcmp(f[0], "key") != 0)
723 continue;
724 p = find(f, nf, "proto");
725 if(p == nil)
726 continue;
727 s = find(f, nf, "service");
728 if(s == nil)
729 continue;
731 if(strcmp(p, "rsa") == 0 && strcmp(s, "ssh") == 0)
732 printrsa1(f, nf);
733 if(strcmp(p, "rsa") == 0 && strcmp(s, "ssh-rsa") == 0)
734 printkey("ssh-rsa", putrsa, f, nf);
735 if(strcmp(p, "dsa") == 0 && strcmp(s, "ssh-dss") == 0)
736 printkey("ssh-dss", putdsa, f, nf);
739 fsclose(fid);
740 threadexitsall(nil);
743 mpint*
744 rsaunpad(mpint *b)
746 int i, n;
747 uchar buf[2560];
749 n = (mpsignif(b)+7)/8;
750 if(n > sizeof buf){
751 werrstr("rsaunpad: too big");
752 return nil;
754 mptobe(b, buf, n, nil);
756 /* the initial zero has been eaten by the betomp -> mptobe sequence */
757 if(buf[0] != 2){
758 werrstr("rsaunpad: expected leading 2");
759 return nil;
761 for(i=1; i<n; i++)
762 if(buf[i]==0)
763 break;
764 return betomp(buf+i, n-i, nil);
767 void
768 mptoberjust(mpint *b, uchar *buf, int len)
770 int n;
772 n = mptobe(b, buf, len, nil);
773 assert(n >= 0);
774 if(n < len){
775 len -= n;
776 memmove(buf+len, buf, n);
777 memset(buf, 0, len);
781 static int
782 dorsa(Aconn *a, mpint *mod, mpint *exp, mpint *chal, uchar chalbuf[32])
784 AuthRpc *rpc;
785 char buf[4096], *p;
786 mpint *decr, *unpad;
788 USED(exp);
789 if((rpc = auth_allocrpc()) == nil){
790 fprint(2, "ssh-agent: auth_allocrpc: %r\n");
791 return -1;
793 snprint(buf, sizeof buf, "proto=rsa service=ssh role=decrypt n=%lB ek=%lB", mod, exp);
794 if(chatty)
795 fprint(2, "ssh-agent: start %s\n", buf);
796 if(auth_rpc(rpc, "start", buf, strlen(buf)) != ARok){
797 fprint(2, "ssh-agent: auth 'start' failed: %r\n");
798 Die:
799 auth_freerpc(rpc);
800 return -1;
803 p = mptoa(chal, 16, nil, 0);
804 if(p == nil){
805 fprint(2, "ssh-agent: dorsa: mptoa: %r\n");
806 goto Die;
808 if(chatty)
809 fprint(2, "ssh-agent: challenge %B => %s\n", chal, p);
810 if(auth_rpc(rpc, "writehex", p, strlen(p)) != ARok){
811 fprint(2, "ssh-agent: dorsa: auth 'write': %r\n");
812 free(p);
813 goto Die;
815 free(p);
816 if(auth_rpc(rpc, "readhex", nil, 0) != ARok){
817 fprint(2, "ssh-agent: dorsa: auth 'read': %r\n");
818 goto Die;
820 decr = strtomp(rpc->arg, nil, 16, nil);
821 if(chatty)
822 fprint(2, "ssh-agent: response %s => %B\n", rpc->arg, decr);
823 if(decr == nil){
824 fprint(2, "ssh-agent: dorsa: strtomp: %r\n");
825 goto Die;
827 unpad = rsaunpad(decr);
828 if(chatty)
829 fprint(2, "ssh-agent: unpad %B => %B\n", decr, unpad);
830 if(unpad == nil){
831 fprint(2, "ssh-agent: dorsa: rsaunpad: %r\n");
832 mpfree(decr);
833 goto Die;
835 mpfree(decr);
836 mptoberjust(unpad, chalbuf, 32);
837 mpfree(unpad);
838 auth_freerpc(rpc);
839 return 0;
842 int
843 keysign(Msg *mkey, Msg *mdata, Msg *msig)
845 char *s;
846 AuthRpc *rpc;
847 RSApub *rsa;
848 DSApub *dsa;
849 char buf[4096];
850 uchar digest[SHA1dlen];
852 s = getstr(mkey);
853 if(strcmp(s, "ssh-rsa") == 0){
854 rsa = getrsapub(mkey);
855 if(rsa == nil)
856 return -1;
857 snprint(buf, sizeof buf, "proto=rsa service=ssh-rsa role=sign n=%lB ek=%lB",
858 rsa->n, rsa->ek);
859 rsapubfree(rsa);
860 }else if(strcmp(s, "ssh-dss") == 0){
861 dsa = getdsapub(mkey);
862 if(dsa == nil)
863 return -1;
864 snprint(buf, sizeof buf, "proto=dsa service=ssh-dss role=sign p=%lB q=%lB alpha=%lB key=%lB",
865 dsa->p, dsa->q, dsa->alpha, dsa->key);
866 dsapubfree(dsa);
867 }else{
868 fprint(2, "ssh-agent: cannot sign key type %s\n", s);
869 werrstr("unknown key type %s", s);
870 return -1;
873 if((rpc = auth_allocrpc()) == nil){
874 fprint(2, "ssh-agent: auth_allocrpc: %r\n");
875 return -1;
877 if(chatty)
878 fprint(2, "ssh-agent: start %s\n", buf);
879 if(auth_rpc(rpc, "start", buf, strlen(buf)) != ARok){
880 fprint(2, "ssh-agent: auth 'start' failed: %r\n");
881 Die:
882 auth_freerpc(rpc);
883 return -1;
885 sha1(mdata->bp, mdata->ep-mdata->bp, digest, nil);
886 if(auth_rpc(rpc, "write", digest, SHA1dlen) != ARok){
887 fprint(2, "ssh-agent: auth 'write in sign failed: %r\n");
888 goto Die;
890 if(auth_rpc(rpc, "read", nil, 0) != ARok){
891 fprint(2, "ssh-agent: auth 'read' failed: %r\n");
892 goto Die;
894 newmsg(msig);
895 putstr(msig, s);
896 put4(msig, rpc->narg);
897 putn(msig, rpc->arg, rpc->narg);
898 auth_freerpc(rpc);
899 return 0;
902 int
903 runmsg(Aconn *a)
905 char *p;
906 int n, nk, type, rt, vers;
907 mpint *ek, *mod, *chal;
908 uchar sessid[16], chalbuf[32], digest[MD5dlen];
909 uint len, flags;
910 DigestState *s;
911 Msg m, mkey, mdata, msig;
913 if(a->ndata < 4)
914 return 0;
915 len = (a->data[0]<<24)|(a->data[1]<<16)|(a->data[2]<<8)|a->data[3];
916 if(a->ndata < 4+len)
917 return 0;
918 m.p = a->data+4;
919 m.ep = m.p+len;
920 type = get1(&m);
921 if(chatty)
922 fprint(2, "msg %d: %.*H\n", type, len, m.p);
923 switch(type){
924 default:
925 Failure:
926 newreply(&m, SSH_AGENT_FAILURE);
927 reply(a, &m);
928 break;
930 case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
931 vers = 1;
932 newreply(&m, SSH_AGENT_RSA_IDENTITIES_ANSWER);
933 goto Identities;
934 case SSH2_AGENTC_REQUEST_IDENTITIES:
935 vers = 2;
936 newreply(&m, SSH2_AGENT_IDENTITIES_ANSWER);
937 Identities:
938 nk = listkeys(&m, vers);
939 if(nk < 0){
940 free(m.bp);
941 goto Failure;
943 if(chatty)
944 fprint(2, "request identities\n", nk);
945 reply(a, &m);
946 break;
948 case SSH_AGENTC_RSA_CHALLENGE:
949 n = get4(&m);
950 ek = getmp(&m);
951 mod = getmp(&m);
952 chal = getmp(&m);
953 if((p = (char*)getn(&m, 16)) == nil){
954 Failchal:
955 mpfree(ek);
956 mpfree(mod);
957 mpfree(chal);
958 goto Failure;
960 memmove(sessid, p, 16);
961 rt = get4(&m);
962 if(rt != 1 || dorsa(a, mod, ek, chal, chalbuf) < 0)
963 goto Failchal;
964 s = md5(chalbuf, 32, nil, nil);
965 if(s == nil)
966 goto Failchal;
967 md5(sessid, 16, digest, s);
968 print("md5 %.*H %.*H => %.*H\n", 32, chalbuf, 16, sessid, MD5dlen, digest);
970 newreply(&m, SSH_AGENT_RSA_RESPONSE);
971 putn(&m, digest, 16);
972 reply(a, &m);
974 mpfree(ek);
975 mpfree(mod);
976 mpfree(chal);
977 break;
979 case SSH2_AGENTC_SIGN_REQUEST:
980 if(getm(&m, &mkey) == nil
981 || getm(&m, &mdata) == nil)
982 goto Failure;
983 flags = get4(&m);
984 if(flags & SSH_AGENT_OLD_SIGNATURE)
985 goto Failure;
986 if(keysign(&mkey, &mdata, &msig) < 0)
987 goto Failure;
988 if(chatty)
989 fprint(2, "signature: %.*H\n",
990 msig.p-msig.bp, msig.bp);
991 newreply(&m, SSH2_AGENT_SIGN_RESPONSE);
992 putm(&m, &msig);
993 free(msig.bp);
994 reply(a, &m);
995 break;
997 case SSH_AGENTC_ADD_RSA_IDENTITY:
998 /*
999 msg: n[4] mod[mp] pubexp[exp] privexp[mp]
1000 p^-1 mod q[mp] p[mp] q[mp] comment[str]
1002 goto Failure;
1004 case SSH_AGENTC_REMOVE_RSA_IDENTITY:
1006 msg: n[4] mod[mp] pubexp[mp]
1008 goto Failure;
1012 a->ndata -= 4+len;
1013 memmove(a->data, a->data+4+len, a->ndata);
1014 return 1;
1017 void*
1018 emalloc(int n)
1020 void *v;
1022 v = mallocz(n, 1);
1023 if(v == nil){
1024 abort();
1025 sysfatal("out of memory allocating %d", n);
1027 return v;
1030 void*
1031 erealloc(void *v, int n)
1033 v = realloc(v, n);
1034 if(v == nil){
1035 abort();
1036 sysfatal("out of memory reallocating %d", n);
1038 return v;