22 MSG_USERAUTH_REQUEST = 50,
27 MSG_USERAUTH_PK_OK = 60,
28 MSG_USERAUTH_INFO_REQUEST = 60,
29 MSG_USERAUTH_INFO_RESPONSE = 61,
31 MSG_GLOBAL_REQUEST = 80,
35 MSG_CHANNEL_OPEN = 90,
36 MSG_CHANNEL_OPEN_CONFIRMATION,
37 MSG_CHANNEL_OPEN_FAILURE,
38 MSG_CHANNEL_WINDOW_ADJUST,
40 MSG_CHANNEL_EXTENDED_DATA,
50 Overhead = 256, // enougth for MSG_CHANNEL_DATA header
52 WinPackets = 8, // (1<<15) * 8 = 256K
55 int MaxPwTries = 3; // retry this often for keyboard-interactive
72 uchar b[Overhead + MaxPacket];
81 char thumb[2*SHA2_256dlen+1], *thumbfile;
83 int fd, intr, raw, port, mux, debug;
84 char *user, *service, *status, *host, *remote, *cmd;
92 recv.eof = send.eof = 1;
94 postnote(PNPROC, send.pid, "shutdown");
98 catch(void*, char *msg)
100 if(strcmp(msg, "interrupt") == 0){
113 memset(err, 0, sizeof(err));
114 errstr(err, sizeof(err));
115 r = strcmp(err, "interrupted") == 0;
116 errstr(err, sizeof(err));
120 #define PUT4(p, u) (p)[0] = (u)>>24, (p)[1] = (u)>>16, (p)[2] = (u)>>8, (p)[3] = (u)
121 #define GET4(p) (u32int)(p)[3] | (u32int)(p)[2]<<8 | (u32int)(p)[1]<<16 | (u32int)(p)[0]<<24
124 vpack(uchar *p, int n, char *fmt, va_list a)
126 uchar *p0 = p, *e = p+n;
137 if(++p > e) goto err;
140 *va_arg(a, void**) = p;
144 *p++ = va_arg(a, int);
147 m = va_arg(a, mpint*);
148 u = (mpsignif(m)+8)/8;
149 if(p+4 > e) goto err;
151 if(u > e-p) goto err;
152 mptober(m, p, u), p += u;
156 s = va_arg(a, void*);
159 if(p+4 > e) goto err;
162 if(u > e-p) goto err;
168 if(p+4 > e) goto err;
178 vunpack(uchar *p, int n, char *fmt, va_list a)
180 uchar *p0 = p, *e = p+n;
190 if(++p > e) goto err;
193 *va_arg(a, void**) = p;
197 *va_arg(a, int*) = *p++;
200 if(p+4 > e) goto err;
202 if(u > e-p) goto err;
203 m = va_arg(a, mpint*);
204 betomp(p, u, m), p += u;
207 if(p+4 > e) goto err;
209 if(u > e-p) goto err;
210 *va_arg(a, void**) = p;
211 *va_arg(a, int*) = u;
215 s = va_arg(a, void*);
217 if(u > e-p) goto err;
222 if(p+4 > e) goto err;
224 *va_arg(a, int*) = u;
234 pack(uchar *p, int n, char *fmt, ...)
238 n = vpack(p, n, fmt, a);
243 unpack(uchar *p, int n, char *fmt, ...)
247 n = vunpack(p, n, fmt, a);
253 setupcs(Oneway *c, uchar otk[32])
258 pack(iv, sizeof(iv), "uu", 0, c->seq);
259 chacha_setiv(&c->cs1, iv);
260 chacha_setiv(&c->cs2, iv);
261 chacha_setblock(&c->cs1, 0);
262 chacha_setblock(&c->cs2, 0);
263 chacha_encrypt(otk, 32, &c->cs2);
267 sendpkt(char *fmt, ...)
269 static uchar buf[sizeof(send.b)];
274 n = vpack(send.b, sizeof(send.b), fmt, a);
277 toobig: sysfatal("sendpkt: message too big");
284 fprint(2, "sendpkt: (%d) %.*H\n", send.r[0], (int)(send.w-send.r), send.r);
288 pad = ChachaBsize - ((5+n) % ChachaBsize) + 4;
290 for(pad=4; (5+n+pad) % 8; pad++)
294 n = pack(buf, sizeof(buf)-16, "ub[[", 1+n+pad, pad, send.b, n, send.w, pad);
295 if(n < 0) goto toobig;
300 chacha_encrypt(buf, 4, &send.cs1);
301 chacha_encrypt(buf+4, n-4, &send.cs2);
302 poly1305(buf, n, otk, sizeof(otk), buf+n, nil);
306 if(write(fd, buf, n) != n)
307 sysfatal("write: %r");
313 readall(int fd, uchar *data, int len)
317 for(tot = 0; tot < len; tot += n){
318 n = read(fd, data+tot, len-tot);
320 if(n < 0 && wasintr()){
334 uchar otk[32], tag[16];
335 DigestState *ds = nil;
338 if(readall(fd, recv.b, 4) != 4)
339 sysfatal("read1: %r");
342 ds = poly1305(recv.b, 4, otk, sizeof(otk), nil, nil);
343 chacha_encrypt(recv.b, 4, &recv.cs1);
344 unpack(recv.b, 4, "u", &n);
347 unpack(recv.b, 4, "u", &n);
349 if(n < 8 || n > sizeof(recv.b)){
350 badlen: sysfatal("bad length %d", n);
352 if(readall(fd, recv.b, n) != n)
353 sysfatal("read2: %r");
356 if(n < 0) goto badlen;
357 poly1305(recv.b, n, otk, sizeof(otk), tag, ds);
358 if(tsmemcmp(tag, recv.b+n, 16) != 0)
360 chacha_encrypt(recv.b, n, &recv.cs2);
363 if(n < 1) goto badlen;
370 fprint(2, "recvpkt: (%d) %.*H\n", recv.r[0], (int)(recv.w-recv.r), recv.r);
375 static char sshrsa[] = "ssh-rsa";
378 rsapub2ssh(RSApub *rsa, uchar *data, int len)
380 return pack(data, len, "smm", sshrsa, sizeof(sshrsa)-1, rsa->ek, rsa->n);
384 ssh2rsapub(uchar *data, int len)
393 if(unpack(data, len, "smm", &s, &n, pub->ek, pub->n) < 0
394 || n != sizeof(sshrsa)-1 || memcmp(s, sshrsa, n) != 0){
402 rsasig2ssh(RSApub *pub, mpint *S, uchar *data, int len)
404 int l = (mpsignif(pub->n)+7)/8;
407 mptober(S, data+4+7+4, l);
408 return pack(data, len, "ss", sshrsa, sizeof(sshrsa)-1, data+4+7+4, l);
412 ssh2rsasig(uchar *data, int len)
419 if(unpack(data, len, "sm", &s, &n, m) < 0
420 || n != sizeof(sshrsa)-1 || memcmp(s, sshrsa, n) != 0){
428 pkcs1digest(uchar *data, int len, RSApub *pub)
430 uchar digest[SHA1dlen], buf[256];
432 sha1(data, len, digest, nil);
433 return pkcs1padbuf(buf, asn1encodedigest(sha1, digest, buf, sizeof(buf)), pub->n, 1);
437 pkcs1verify(uchar *data, int len, RSApub *pub, mpint *S)
442 V = pkcs1digest(data, len, pub);
445 rsaencrypt(pub, S, S);
446 ret = mpcmp(V, S) == 0;
453 hashstr(void *data, ulong len, DigestState *ds)
456 pack(l, 4, "u", len);
457 return sha2_256((uchar*)data, len, nil, sha2_256(l, 4, nil, ds));
461 kdf(uchar *k, int nk, uchar *h, char x, uchar *out, int len)
463 uchar digest[SHA2_256dlen], *out0;
467 ds = hashstr(k, nk, nil);
468 ds = sha2_256(h, sizeof(digest), nil, ds);
469 ds = sha2_256((uchar*)&x, 1, nil, ds);
470 sha2_256(sid, nsid, digest, ds);
473 if(n > sizeof(digest))
475 memmove(out, digest, n);
480 ds = hashstr(k, nk, nil);
481 ds = sha2_256(h, sizeof(digest), nil, ds);
482 sha2_256(out0, out-out0, digest, ds);
489 static char kexalgs[] = "curve25519-sha256,curve25519-sha256@libssh.org";
490 static char cipheralgs[] = "chacha20-poly1305@openssh.com";
491 static char zipalgs[] = "none";
492 static char macalgs[] = "hmac-sha1"; /* work around for github.com */
493 static char langs[] = "";
495 uchar cookie[16], x[32], yc[32], z[32], k[32+1], h[SHA2_256dlen], *ys, *ks, *sig;
496 uchar k12[2*ChachaKeylen];
497 int i, nk, nys, nks, nsig;
502 ds = hashstr(send.v, strlen(send.v), nil);
503 ds = hashstr(recv.v, strlen(recv.v), ds);
505 genrandom(cookie, sizeof(cookie));
506 sendpkt("b[ssssssssssbu", MSG_KEXINIT,
507 cookie, sizeof(cookie),
508 kexalgs, sizeof(kexalgs)-1,
509 sshrsa, sizeof(sshrsa)-1,
510 cipheralgs, sizeof(cipheralgs)-1,
511 cipheralgs, sizeof(cipheralgs)-1,
512 macalgs, sizeof(macalgs)-1,
513 macalgs, sizeof(macalgs)-1,
514 zipalgs, sizeof(zipalgs)-1,
515 zipalgs, sizeof(zipalgs)-1,
516 langs, sizeof(langs)-1,
517 langs, sizeof(langs)-1,
520 ds = hashstr(send.r, send.w-send.r, ds);
523 Next0: switch(recvpkt()){
531 ds = hashstr(recv.r, recv.w-recv.r, ds);
535 "kexalgs", "hostalgs",
536 "cipher1", "cipher2",
542 uchar *p = recv.r+17;
544 for(t=tab; *t != nil; t++){
545 if(unpack(p, recv.w-p, "s.", &s, &n, &p) < 0)
547 fprint(2, "%s: %.*s\n", *t, utfnlen(s, n), s);
551 curve25519_dh_new(x, yc);
554 sendpkt("bs", MSG_ECDH_INIT, yc, sizeof(yc));
555 Next1: switch(recvpkt()){
560 sysfatal("inception");
562 if(unpack(recv.r, recv.w-recv.r, "_sss", &ks, &nks, &ys, &nys, &sig, &nsig) < 0)
563 sysfatal("bad ECDH_REPLY");
568 sysfatal("bad server ECDH ephermal public key length");
570 ds = hashstr(ks, nks, ds);
571 ds = hashstr(yc, 32, ds);
572 ds = hashstr(ys, 32, ds);
577 sha2_256(ks, nks, h, nil);
578 i = enc64(thumb, sizeof(thumb), h, sizeof(h));
579 while(i > 0 && thumb[i-1] == '=')
584 fprint(2, "host fingerprint: %s\n", thumb);
586 ok = initThumbprints(thumbfile, nil, "ssh");
587 if(ok == nil || !okThumbprint(h, sizeof(h), ok)){
588 if(ok != nil) werrstr("unknown host");
589 fprint(2, "%s: %r\n", argv0);
590 fprint(2, "verify hostkey: %s %.*[\n", sshrsa, nks, ks);
591 fprint(2, "add thumbprint after verification:\n");
592 fprint(2, "\techo 'ssh sha256=%s server=%s' >> %q\n", thumb, host, thumbfile);
593 sysfatal("checking hostkey failed: %r");
598 if((pub = ssh2rsapub(ks, nks)) == nil)
599 sysfatal("bad server public key");
600 if((S = ssh2rsasig(sig, nsig)) == nil)
601 sysfatal("bad server signature");
603 if(!curve25519_dh_finish(x, ys, z))
604 sysfatal("unlucky shared key");
606 K = betomp(z, 32, nil);
607 nk = (mpsignif(K)+8)/8;
611 ds = hashstr(k, nk, ds);
612 sha2_256(nil, 0, h, ds);
613 if(!pkcs1verify(h, sizeof(h), pub, S))
614 sysfatal("server verification failed");
618 sendpkt("b", MSG_NEWKEYS);
619 Next2: switch(recvpkt()){
624 sysfatal("inception");
629 /* next key exchange */
630 recv.kex = recv.seq + 100000;
631 send.kex = send.seq + 100000;
634 memmove(sid, h, nsid = sizeof(h));
636 kdf(k, nk, h, 'C', k12, sizeof(k12));
637 setupChachastate(&send.cs1, k12+1*ChachaKeylen, ChachaKeylen, nil, 64/8, 20);
638 setupChachastate(&send.cs2, k12+0*ChachaKeylen, ChachaKeylen, nil, 64/8, 20);
640 kdf(k, nk, h, 'D', k12, sizeof(k12));
641 setupChachastate(&recv.cs1, k12+1*ChachaKeylen, ChachaKeylen, nil, 64/8, 20);
642 setupChachastate(&recv.cs2, k12+0*ChachaKeylen, ChachaKeylen, nil, 64/8, 20);
645 static char *authnext;
650 int ok = authnext == nil || strstr(authnext, meth) != nil;
652 fprint(2, "userauth %s %s\n", meth, ok ? "ok" : "skipped");
657 authfailure(char *meth)
662 if(unpack(recv.r, recv.w-recv.r, "_sb", &s, &n, &partial) < 0)
663 sysfatal("bad auth failure response");
665 authnext = smprint("%.*s", utfnlen(s, n), s);
667 fprint(2, "userauth %s failed: partial=%d, next=%s\n", meth, partial, authnext);
668 return partial != 0 || !authok(meth);
674 static char authmeth[] = "none";
676 if(!authok(authmeth))
679 sendpkt("bsss", MSG_USERAUTH_REQUEST,
681 service, strlen(service),
682 authmeth, sizeof(authmeth)-1);
684 Next0: switch(recvpkt()){
688 case MSG_USERAUTH_FAILURE:
689 werrstr("authentication needed");
690 authfailure(authmeth);
692 case MSG_USERAUTH_SUCCESS:
700 static char authmeth[] = "publickey";
702 uchar pk[4096], sig[4096];
711 if(!authok(authmeth))
714 if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0)
716 if((rpc = auth_allocrpc(afd)) == nil){
721 s = "proto=rsa service=ssh role=client";
722 if(auth_rpc(rpc, "start", s, strlen(s)) != ARok){
732 while(auth_rpc(rpc, "read", nil, 0) == ARok){
734 if(strtomp(s, &s, 16, pub->n) == nil)
738 if(strtomp(s, nil, 16, pub->ek) == nil)
740 npk = rsapub2ssh(pub, pk, sizeof(pk));
742 sendpkt("bsssbss", MSG_USERAUTH_REQUEST,
744 service, strlen(service),
745 authmeth, sizeof(authmeth)-1,
747 sshrsa, sizeof(sshrsa)-1,
749 Next1: switch(recvpkt()){
753 case MSG_USERAUTH_FAILURE:
754 if(authfailure(authmeth))
757 case MSG_USERAUTH_SUCCESS:
758 case MSG_USERAUTH_PK_OK:
762 /* sign sid and the userauth request */
763 n = pack(send.b, sizeof(send.b), "sbsssbss",
765 MSG_USERAUTH_REQUEST,
767 service, strlen(service),
768 authmeth, sizeof(authmeth)-1,
770 sshrsa, sizeof(sshrsa)-1,
772 S = pkcs1digest(send.b, n, pub);
773 n = snprint((char*)send.b, sizeof(send.b), "%B", S);
776 if(auth_rpc(rpc, "write", (char*)send.b, n) != ARok)
778 if(auth_rpc(rpc, "read", nil, 0) != ARok)
781 S = strtomp(rpc->arg, nil, 16, nil);
782 nsig = rsasig2ssh(pub, S, sig, sizeof(sig));
785 /* send final userauth request with the signature */
786 sendpkt("bsssbsss", MSG_USERAUTH_REQUEST,
788 service, strlen(service),
789 authmeth, sizeof(authmeth)-1,
791 sshrsa, sizeof(sshrsa)-1,
794 Next2: switch(recvpkt()){
798 case MSG_USERAUTH_FAILURE:
799 if(authfailure(authmeth))
802 case MSG_USERAUTH_SUCCESS:
820 static char authmeth[] = "password";
823 if(!authok(authmeth))
826 up = auth_getuserpasswd(auth_getkey, "proto=pass service=ssh user=%q server=%q thumb=%q",
831 sendpkt("bsssbs", MSG_USERAUTH_REQUEST,
833 service, strlen(service),
834 authmeth, sizeof(authmeth)-1,
836 up->passwd, strlen(up->passwd));
838 memset(up->passwd, 0, strlen(up->passwd));
841 Next0: switch(recvpkt()){
845 case MSG_USERAUTH_FAILURE:
846 werrstr("wrong password");
847 authfailure(authmeth);
849 case MSG_USERAUTH_SUCCESS:
857 static char authmeth[] = "keyboard-interactive";
860 char *name, *inst, *s, *a;
866 if(!authok(authmeth))
870 if(++tries > MaxPwTries)
873 sendpkt("bsssss", MSG_USERAUTH_REQUEST,
875 service, strlen(service),
876 authmeth, sizeof(authmeth)-1,
880 Next0: switch(recvpkt()){
884 case MSG_USERAUTH_FAILURE:
885 werrstr("keyboard-interactive failed");
886 if(authfailure(authmeth))
889 case MSG_USERAUTH_SUCCESS:
891 case MSG_USERAUTH_INFO_REQUEST:
895 if((fd = open("/dev/cons", OWRITE)) < 0)
898 if(unpack(recv.r, recv.w-recv.r, "_ss.", &name, &n, &inst, &m, &recv.r) < 0)
899 sysfatal("bad info request: name, inst");
901 while(n > 0 && strchr("\r\n\t ", name[n-1]) != nil)
903 while(m > 0 && strchr("\r\n\t ", inst[m-1]) != nil)
907 fprint(fd, "%.*s\n", utfnlen(name, n), name);
909 fprint(fd, "%.*s\n", utfnlen(inst, m), inst);
912 if(unpack(recv.r, recv.w-recv.r, "su.", &s, &n, &nquest, &recv.r) < 0)
913 sysfatal("bad info request: lang, #quest");
916 for(i = 0; i < nquest; i++){
917 if(unpack(recv.r, recv.w-recv.r, "sb.", &s, &n, &echo, &recv.r) < 0)
918 sysfatal("bad info request: question [%d]", i);
920 while(n > 0 && strchr("\r\n\t :", s[n-1]) != nil)
924 if((a = readcons(s, nil, !echo)) == nil)
925 sysfatal("readcons: %r");
929 if((s = realloc(ans, n + m)) == nil)
930 sysfatal("realloc: %r");
933 answ += pack(answ, m, "s", a, m-4);
936 sendpkt("bu[", MSG_USERAUTH_INFO_RESPONSE, i, ans, answ - ans);
940 Next1: switch(recvpkt()){
944 case MSG_USERAUTH_INFO_REQUEST:
946 case MSG_USERAUTH_FAILURE:
947 werrstr("keyboard-interactive failed");
948 if(authfailure(authmeth))
951 case MSG_USERAUTH_SUCCESS:
966 case MSG_GLOBAL_REQUEST:
967 if(unpack(recv.r, recv.w-recv.r, "_sb", &s, &n, &b) < 0)
970 fprint(2, "%s: global request: %.*s\n",
971 argv0, utfnlen(s, n), s);
973 sendpkt("b", MSG_REQUEST_FAILURE);
976 if(unpack(recv.r, recv.w-recv.r, "_us", &c, &s, &n) < 0)
978 sysfatal("disconnect: (%d) %.*s", c, utfnlen(s, n), s);
981 if(unpack(recv.r, recv.w-recv.r, "__sb", &s, &n, &c) < 0)
984 fprint(2, "%s: %.*s\n", argv0, utfnlen(s, n), s);
986 case MSG_USERAUTH_BANNER:
987 if(unpack(recv.r, recv.w-recv.r, "_s", &s, &n) < 0)
989 if(raw) write(2, s, n);
998 if(write(1, recv.r, n) != n)
999 sysfatal("write out: %r");
1004 case MSG_CHANNEL_DATA:
1005 if(unpack(recv.r, recv.w-recv.r, "_us", &c, &s, &n) < 0)
1009 if(write(1, s, n) != n)
1010 sysfatal("write out: %r");
1013 if(recv.win < recv.pkt){
1014 n = WinPackets*recv.pkt;
1016 sendpkt("buu", MSG_CHANNEL_WINDOW_ADJUST, send.chan, n);
1019 case MSG_CHANNEL_EXTENDED_DATA:
1020 if(unpack(recv.r, recv.w-recv.r, "_uus", &c, &b, &s, &n) < 0)
1024 if(b == 1) write(2, s, n);
1026 case MSG_CHANNEL_WINDOW_ADJUST:
1027 if(unpack(recv.r, recv.w-recv.r, "_uu", &c, &n) < 0)
1032 if(send.win >= send.pkt)
1035 case MSG_CHANNEL_REQUEST:
1036 if(unpack(recv.r, recv.w-recv.r, "_usb.", &c, &s, &n, &b, &p) < 0)
1040 if(n == 11 && memcmp(s, "exit-signal", n) == 0){
1041 if(unpack(p, recv.w-p, "s", &s, &n) < 0)
1043 if(n != 0 && status == nil)
1044 status = smprint("%.*s", utfnlen(s, n), s);
1045 c = MSG_CHANNEL_SUCCESS;
1046 } else if(n == 11 && memcmp(s, "exit-status", n) == 0){
1047 if(unpack(p, recv.w-p, "u", &n) < 0)
1049 if(n != 0 && status == nil)
1050 status = smprint("%d", n);
1051 c = MSG_CHANNEL_SUCCESS;
1054 fprint(2, "%s: channel request: %.*s\n",
1055 argv0, utfnlen(s, n), s);
1056 c = MSG_CHANNEL_FAILURE;
1059 sendpkt("bu", c, recv.chan);
1061 case MSG_CHANNEL_EOF:
1063 if(!raw) write(1, "", 0);
1065 case MSG_CHANNEL_CLOSE:
1069 sysfatal("got: %.*H", (int)(recv.w - recv.r), recv.r);
1077 for(p = send.b; p < &send.b[sizeof(send.b)-1]; p++){
1079 if(read(fd, p, 1) != 1 || *p == '\n')
1082 while(p >= send.b && (*p == '\n' || *p == '\r'))
1084 return (char*)send.b;
1102 if(s = getenv("WINCH")){
1109 if(s = getenv("XPIXELS")){
1110 tty.xpixels = atoi(s);
1113 if(s = getenv("YPIXELS")){
1114 tty.ypixels = atoi(s);
1117 if(s = getenv("LINES")){
1118 tty.lines = atoi(s);
1121 if(s = getenv("COLS")){
1134 if(open("/dev/cons", OREAD) != 0)
1135 sysfatal("open: %r");
1137 if(open("/dev/cons", OWRITE) != 1)
1138 sysfatal("open: %r");
1140 if((ctl = open("/dev/consctl", OWRITE)) >= 0){
1141 write(ctl, "rawon", 5);
1142 write(ctl, "winchon", 7); /* vt(1): interrupt note on window change */
1147 #pragma varargck type "k" char*
1154 s = va_arg(f->args, char*);
1155 n = fmtstrcpy(f, "'");
1156 while((p = strchr(s, '\'')) != nil){
1158 n += fmtstrcpy(f, s);
1160 n += fmtstrcpy(f, "'\\''");
1163 n += fmtstrcpy(f, s);
1164 n += fmtstrcpy(f, "'");
1171 fprint(2, "usage: %s [-dR] [-t thumbfile] [-T tries] [-u user] [-h] [user@]host [-W remote!port] [cmd args...]\n", argv0);
1176 main(int argc, char *argv[])
1183 fmtinstall('B', mpfmt);
1184 fmtinstall('H', encodefmt);
1185 fmtinstall('[', encodefmt);
1186 fmtinstall('k', kfmt);
1189 tty.term = getenv("TERM");
1192 raw = *tty.term != 0;
1199 remote = EARGF(usage());
1200 s = strrchr(remote, '!');
1202 s = strrchr(remote, ':');
1213 raw = 2; /* bloody */
1216 user = EARGF(usage());
1219 host = EARGF(usage());
1222 thumbfile = EARGF(usage());
1225 MaxPwTries = strtol(EARGF(usage()), &s, 0);
1226 if(*s != 0) usage();
1243 s = strchr(host, '@');
1251 for(cmd = nil; *argv != nil; argv++){
1253 cmd = strdup(*argv);
1257 s = smprint("%s %k", cmd, *argv);
1263 if(remote != nil && cmd != nil)
1266 if((fd = dial(netmkaddr(host, nil, "ssh"), nil, nil, nil)) < 0)
1267 sysfatal("dial: %r");
1269 send.v = "SSH-2.0-(9)";
1270 fprint(fd, "%s\r\n", send.v);
1271 recv.v = readline();
1273 fprint(2, "server verison: %s\n", recv.v);
1274 if(strncmp("SSH-2.0-", recv.v, 8) != 0)
1275 sysfatal("bad server version: %s", recv.v);
1276 recv.v = strdup(recv.v);
1278 send.l = recv.l = &sl;
1282 if(thumbfile == nil)
1283 thumbfile = smprint("%s/lib/sshthumbs", getenv("home"));
1287 sendpkt("bs", MSG_SERVICE_REQUEST, "ssh-userauth", 12);
1288 Next0: switch(recvpkt()){
1292 case MSG_SERVICE_ACCEPT:
1296 service = "ssh-connection";
1297 if(noneauth() < 0 && pubkeyauth() < 0 && passauth() < 0 && kbintauth() < 0)
1298 sysfatal("auth: %r");
1300 recv.pkt = send.pkt = MaxPacket;
1301 recv.win = send.win = WinPackets*recv.pkt;
1302 recv.chan = send.win = 0;
1307 /* open hailing frequencies */
1309 NetConnInfo *nci = getnetconninfo(nil, fd);
1311 sysfatal("can't get netconninfo: %r");
1312 sendpkt("bsuuususu", MSG_CHANNEL_OPEN,
1317 remote, strlen(remote),
1319 nci->laddr, strlen(nci->laddr),
1323 sendpkt("bsuuu", MSG_CHANNEL_OPEN,
1329 Next1: switch(recvpkt()){
1333 case MSG_CHANNEL_OPEN_FAILURE:
1334 if(unpack(recv.r, recv.w-recv.r, "_uus", &c, &b, &s, &n) < 0)
1335 n = strlen(s = "???");
1336 sysfatal("channel open failure: (%d) %.*s", b, utfnlen(s, n), s);
1337 case MSG_CHANNEL_OPEN_CONFIRMATION:
1341 if(unpack(recv.r, recv.w-recv.r, "_uuuu", &recv.chan, &send.chan, &send.win, &send.pkt) < 0)
1342 sysfatal("bad channel open confirmation");
1343 if(send.pkt <= 0 || send.pkt > MaxPacket)
1344 send.pkt = MaxPacket;
1351 sendpkt("busbsuuuus", MSG_CHANNEL_REQUEST,
1355 tty.term, strlen(tty.term),
1363 sendpkt("busb", MSG_CHANNEL_REQUEST,
1367 } else if(*cmd == '#') {
1368 sendpkt("busbs", MSG_CHANNEL_REQUEST,
1372 cmd+1, strlen(cmd)-1);
1374 sendpkt("busbs", MSG_CHANNEL_REQUEST,
1385 recv.pid = getpid();
1386 n = rfork(RFPROC|RFMEM);
1388 sysfatal("fork: %r");
1390 /* parent reads and dispatches packets */
1393 while(recv.eof == 0){
1397 if((int)(send.kex - send.seq) <= 0 || (int)(recv.kex - recv.seq) <= 0)
1404 /* child reads input and sends packets */
1407 static uchar buf[MaxPacket];
1409 n = read(0, buf, send.pkt);
1413 if(n < 0 && wasintr())
1418 sendpkt("busbuuuu", MSG_CHANNEL_REQUEST,
1420 "window-change", 13,
1427 sendpkt("busbs", MSG_CHANNEL_REQUEST,
1439 sendpkt("[", buf, n);
1445 sendpkt("bus", MSG_CHANNEL_DATA,
1449 if(send.eof++ == 0 && !mux)
1450 sendpkt("bu", raw ? MSG_CHANNEL_CLOSE : MSG_CHANNEL_EOF, send.chan);
1451 else if(recv.pid > 0 && mux)
1452 postnote(PNPROC, recv.pid, "shutdown");