implement Ssh; include "sys.m"; sys: Sys; sprint: import sys;include "draw.m";include "arg.m";include "bufio.m";include "dial.m"; dial: Dial;include "string.m"; str: String;include "keyring.m";include "security.m"; random: Random;include "util0.m"; util: Util0; l2a, readfile, join, min, pid, killgrp, max, warn: import util;include "../lib/sshfmt.m"; sshfmt: Sshfmt; Val: import sshfmt; Tbyte, Tbool, Tint, Tbig, Tnames, Tstr, Tmpint: import sshfmt; valbyte, valbool, valint, valintb, valbig, valnames, valstr, valbytes, valmpint: import sshfmt;include "../lib/sshlib.m"; sshlib: Sshlib; Rssh, Tssh, Sshc, Cfg, Keys: import sshlib; Ssh: module { init: fn(nil: ref Draw->Context, args: list of string);}; dflag,sflag: int;term: string;command: string; packetc: chan of (ref Rssh, string, string, chan of int);inc: chan of (array of byte, string);readc: chan of int;outc: chan of Out;wrotec: chan of (int, string);termc: chan of (string, string);sshc: ref Sshc; termfd: ref Sys->FD; Link: adt[T] { v: T; next: cyclic ref Link;};Out: adt { toerr: int; buf: array of byte;};pendingfirst,pendinglast: ref Link[ref Out]; # buffers read from remote, not yet sent to outwriter.written: int; # bytes written to local stdout/stderr, but not yet used for increasing windowtoremwindowfromrem,windowtorem: big;inwaiting: int; # whether inreader is waiting for key exchange to be finished Windowlow: con big (128*1024);Windowhigh: con big (256*1024);Minreadwin: con 32*1024; # only let inreader read when it can read MinreadwinMaxpayloadsize: con 32*1024; Maxkeypackets: con big 2**31;Maxkeybytes: con big (1*1024*1024); localchannel := 1;remotechannel := 2;outmaxpayloadsize: big; Xfirstkex,Xuserauthservice,Xuserauthresult,Xchannelopenresult,Xptyresult,Xcommandresult,Xnormal: con iota; expecting := Xfirstkex; expectstrs := array[] of {"firstkex", "userauthservice", "userauthresult", "channelopenresult", "ptyresult", "commandresult", "normal",}; init(nil: ref Draw->Context, args: list of string){ sys = load Sys Sys->PATH; arg := load Arg Arg->PATH; dial = load Dial Dial->PATH; str = load String String->PATH; random = load Random Random->PATH; util = load Util0 Util0->PATH; util->init(); sshfmt = load Sshfmt Sshfmt->PATH; sshfmt->init(); sshlib = load Sshlib Sshlib->PATH; sshlib->init(); sys->pctl(Sys->NEWPGRP, nil); cfg := Cfg.default(); arg->init(args); arg->setusage(arg->progname()+" [-d] [-t | -T term] [-A auth-methods] [-e enc-algs] [-m mac-algs] [-K kex-algs] [-H hostkey-algs] [-C compr-algs] [-k keyspec] [-s] [user@]addr [cmd]"); while((cc := arg->opt()) != 0) case cc { 'd' => sshlib->dflag = dflag++; 'A' or 'e' or 'm' or 'K' or 'H' or 'C' or 'k' => err := cfg.setopt(cc, arg->earg()); if(err != nil) fail(err); 's'=> sflag++; 't' => term = "ansi"; 'T' => term = arg->earg(); * => arg->usage(); } args = arg->argv(); if(len args != 1 && len args != 2) arg->usage(); (user, addr) := str->splitstrl(hd args, "@"); if(addr == nil) { addr = user; user = ""; } else addr = addr[1:]; if(addr == nil) fail("empty address"); addr = dial->netmkaddr(addr, nil, "ssh"); if(len args == 2) command = hd tl args; else if(sflag) fail("-s requires command"); packetc = chan of (ref Rssh, string, string, chan of int); readc = chan of int; inc = chan of (array of byte, string); outc = chan[1] of Out; wrotec = chan of (int, string); termc = chan of (string, string); conn := dial->dial(addr, nil); if(conn == nil) fail(sprint("dial %q: %r", addr)); if(sys->fprint(conn.cfd, "keepalive") < 0) warn(sprint("setting keepalive: %r")); lerr: string; (sshc, lerr) = sshlib->handshake(conn.dfd, addr, cfg); if(lerr != nil) fail(lerr); say("handshake done"); sshc.user = user; kextm := sshlib->keyexchangestart(sshc); ewritemsg(kextm); spawn packetreader(); spawn inreader(); spawn outwriter(); vals: array of ref Val; for(;;) alt { (s, err) := <-termc => if(err != nil) { warn("termctl: "+err); continue; } t := l2a(str->unquoted(s)); if(t == nil) continue; case t[0] { "break" => vals = array[] of { valint(remotechannel), valstr("break"), valbool(0), # no reply valint(500), # 500ms }; ewritepacket(Sshlib->SSH_MSG_CHANNEL_REQUEST, vals); "dimensions" => if(len t != 3 || !isnum(t[1]) || !isnum(t[2])) continue; columns := int t[1]; rows := int t[2]; vals = array[] of { valint(remotechannel), valstr("window-change"), valbool(0), valint(columns), valint(rows), valint(0), valint(0), }; ewritepacket(Sshlib->SSH_MSG_CHANNEL_REQUEST, vals); * => say("termctl: bad command: "+s); } (d, err) := <-inc => if(err != nil) disconnect(err); if(sshc.kexbusy()) { readc <-= -1; inwaiting++; continue; } if(len d == 0) { vals = array[] of {valint(remotechannel)}; ewritepacket(Sshlib->SSH_MSG_CHANNEL_EOF, vals); continue; } vals = array[] of { valint(remotechannel), valbytes(d), }; tm := ref Tssh (big 0, Sshlib->SSH_MSG_CHANNEL_DATA, vals, random->randomint(Random->NotQuiteRandom)&16r7f, nil); ewritemsg(tm); windowtorem -= big len d; if(windowtorem > big 0) readc <-= int bigmin(outmaxpayloadsize, windowtorem); (n, err) := <-wrotec => if(err != nil) fail("local write: "+err); written += n; if(dflag) say(sprint("wrote %d, new written %d, current windowfromrem %bd", n, written, windowfromrem)); if(windowfromrem <= Windowlow && windowfromrem+big written > Windowlow) { say("increasing window for remote"); vals = array[] of { valint(remotechannel), valint(written), }; ewritepacket(Sshlib->SSH_MSG_CHANNEL_WINDOW_ADJUST, vals); windowfromrem += big written; written = 0; } if(pendingfirst != nil) { outc <-= *pendingfirst.v; pendingfirst = pendingfirst.next; if(pendingfirst == nil) pendinglast = nil; } (m, ioerr, protoerr, rc) := <-packetc => if(ioerr != nil) fail(ioerr); if(protoerr != nil) disconnect(protoerr); if(dflag) say("<- "+m.text()); case m.t { 1 to 19 or 20 to 29 or 30 to 49 => transport(m); 50 to 59 or 60 to 79 => expect(expecting==Xuserauthresult, "userauth layer message"); userauth(m); 80 to 89 or 90 to 127 => connection(m); * => ewritepacket(Sshlib->SSH_MSG_UNIMPLEMENTED, array[] of {valintb(m.seq)}); if(dflag) say("received packet we do not implement: "+m.text()); } if(dflag) say(sprint("nkeypkts %bd, nkeybytes %bd", sshc.nkeypkts, sshc.nkeybytes)); if((sshc.nkeypkts >= Maxkeypackets || sshc.nkeybytes >= Maxkeybytes) && sshc.kexbusy()) { kexmsg := sshlib->keyexchangestart(sshc); ewritemsg(kexmsg); say("key re-exchange started"); } rc <-= 0; }} transport(m: ref Rssh){ case m.t { Sshlib->SSH_MSG_DISCONNECT => v := eparsepacket(m, list of {Tint, Tstr, Tstr}); code := v[0].getint(); errmsg := v[1].getstr(); #lang := v[2].getstr(); fail(sprint("remote disconnected: %s (%d)", errmsg, code)); Sshlib->SSH_MSG_IGNORE => v := eparsepacket(m, list of {Tstr}); data := v[0].getstr(); if(dflag) say("msg ignore, data: "+data); Sshlib->SSH_MSG_UNIMPLEMENTED => v := eparsepacket(m, list of {Tint}); pktno := v[0].getintb(); disconnect(sprint("packetno %bd 'unimplemented' by remote", pktno)); Sshlib->SSH_MSG_DEBUG => v := eparsepacket(m, list of {Tbool, Tstr, Tstr}); display := v[0].getbool(); text := v[1].getstr(); lang := v[2].getstr(); if(dflag) say(sprint("remote debug, display=%d, text=%q, lang=%q", display, text, lang)); Sshlib->SSH_MSG_SERVICE_REQUEST => v := eparse(m.buf[1:], list of {Tstr}); name := v[0].getstr(); disconnect(sprint("remote sent 'service request' for %#q, invalid", name)); Sshlib->SSH_MSG_SERVICE_ACCEPT => v := eparsepacket(m, list of {Tstr}); name := v[0].getstr(); if(dflag) say("service accepted: "+name); case name { "ssh-userauth" => expect(expecting==Xuserauthservice, "userauth service accept"); (tm, err) := sshlib->userauthnext(sshc); if(err != nil) disconnect(err); if(tm != nil) ewritemsg(tm); expecting = Xuserauthresult; * => disconnect(sprint("remote sent 'server accept' for unknown service %#q", name)); } 20 to 29 or 30 to 49 => (notimpl, newkeys, tms, err) := sshlib->keyexchange(sshc, m); if(err != nil) disconnect(err); if(notimpl) { ewritepacket(Sshlib->SSH_MSG_UNIMPLEMENTED, array[] of {valintb(m.seq)}); if(dflag) say("received key exchange transport packet we do not implement: "+m.text()); return; } for(; tms != nil; tms = tl tms) ewritemsg(hd tms); if(newkeys && expecting == Xfirstkex) { vals := array[] of {valstr("ssh-userauth")}; ewritepacket(Sshlib->SSH_MSG_SERVICE_REQUEST, vals); # remember we sent this, verify when response comes in expecting = Xuserauthservice; } if(newkeys && inwaiting) { readc <-= 0; inwaiting = 0; } * => ewritepacket(Sshlib->SSH_MSG_UNIMPLEMENTED, array[] of {valintb(m.seq)}); if(dflag) say("received transport packet we do not implement: "+m.text()); }} userauth(m: ref Rssh){ case m.t { Sshlib->SSH_MSG_USERAUTH_REQUEST => disconnect("userauth request from server, invalid"); Sshlib->SSH_MSG_USERAUTH_BANNER => v := eparsepacket(m, list of {Tstr, Tstr}); msg := v[0].getstr(); lang := v[1].getstr(); lang = nil; warn("auth banner: "+msg); Sshlib->SSH_MSG_USERAUTH_FAILURE or Sshlib->SSH_MSG_USERAUTH_SUCCESS => expect(expecting==Xuserauthresult, "userauth result"); (notimpl, authok, tm, err) := sshlib->userauth(sshc, m); if(err != nil) disconnect(err); if(notimpl) { ewritepacket(Sshlib->SSH_MSG_UNIMPLEMENTED, array[] of {valintb(m.seq)}); if(dflag) say("received userauth packet we do not implement: "+m.text()); } if(tm != nil) ewritemsg(tm); if(authok) { vals := array[] of { valstr("session"), valint(localchannel), valint(0), valint(Maxpayloadsize), }; ewritepacket(Sshlib->SSH_MSG_CHANNEL_OPEN, vals); expecting = Xchannelopenresult; } * => ewritepacket(Sshlib->SSH_MSG_UNIMPLEMENTED, array[] of {valintb(m.seq)}); if(dflag) say("received userauth packet we do not implement: "+m.text()); }} connection(m: ref Rssh){ if(expecting < Xchannelopenresult) disconnect(sprint("remote sent connection messages before we requested them")); case m.t { Sshlib->SSH_MSG_GLOBAL_REQUEST => v := eparse(m.buf[1:], list of {Tstr, Tbool}); name := v[0].getstr(); wantreply := v[1].getbool(); if(dflag) say(sprint("msg-global-request %#q, wantreply %d", name, wantreply)); if(wantreply) ewritepacket(Sshlib->SSH_MSG_REQUEST_FAILURE, nil); Sshlib->SSH_MSG_REQUEST_SUCCESS => # response to global request disconnect("remote sent unsolicited SSH_MSG_REQUEST_SUCCESS"); Sshlib->SSH_MSG_REQUEST_FAILURE => # response to global request disconnect("remote sent unsolicited SSH_MSG_REQUEST_FAILURE"); Sshlib->SSH_MSG_CHANNEL_OPEN => v := eparse(m.buf[1:], list of {Tstr, Tint, Tint, Tint}); rch := v[1].getint(); vals := array[] of {valint(rch), valint(Sshlib->SSH_OPEN_ADMINISTRATIVELY_PROHIBITED), valstr("no inboud channels"), valstr("")}; ewritepacket(Sshlib->SSH_MSG_CHANNEL_OPEN_FAILURE, vals); Sshlib->SSH_MSG_CHANNEL_OPEN_CONFIRMATION => v := eparsepacket(m, list of {Tint, Tint, Tint, Tint}); lch := v[0].getint(); rch := v[1].getint(); winsize := v[2].getintb(); maxpayloadsize := v[3].getintb(); # rfc naming is confusing, this is about data channel payload size, not (ssh) "packets" expect(expecting==Xchannelopenresult, "channel open confirmation"); if(dflag) say(sprint("open confirmation, lch %d rch %d winsize %bd maxpayloadsize %bd", lch, rch, winsize, maxpayloadsize)); if(lch != localchannel) disconnect(sprint("remote sent confirmation for unknown local channel %d (expected %d)", lch, localchannel)); if(maxpayloadsize < big (32*1024)) disconnect(sprint("remote sent max packet size that is too small, %bd, minimum is 32*1024", maxpayloadsize)); outmaxpayloadsize = maxpayloadsize; remotechannel = rch; if(term != nil) { # see rfc4254, section 8 for more modes ONLCR: con byte 72; # map NL to CR-NL termmode := array[] of { valbyte(ONLCR), valint(0), # off valbyte(byte 0), }; termmode = nil; vals := array[] of { valint(remotechannel), valstr("pty-req"), valbool(1), # want reply valstr(term), valint(80), valint(24), # dimensions chars valint(0), valint(0), # dimensions pixels valbytes(sshfmt->pack(termmode, 0)), }; ewritepacket(Sshlib->SSH_MSG_CHANNEL_REQUEST, vals); say("wrote pty allocation request"); expecting = Xptyresult; } else expecting = Xcommandresult; if(sflag) { vals := array[] of { valint(remotechannel), valstr("subsystem"), valbool(1), # want reply valstr(command), }; ewritepacket(Sshlib->SSH_MSG_CHANNEL_REQUEST, vals); say("wrote subsystem request"); } else if(command != nil) { vals := array[] of { valint(remotechannel),Lines 1–500 of 888. Load more
implement Ssh; include "sys.m"; sys: Sys; sprint: import sys; include "draw.m"; include "arg.m"; include "bufio.m"; include "dial.m"; dial: Dial; include "string.m"; str: String; include "keyring.m"; include "security.m"; random: Random; include "util0.m"; util: Util0; l2a, readfile, join, min, pid, killgrp, max, warn: import util; include "../lib/sshfmt.m"; sshfmt: Sshfmt; Val: import sshfmt; Tbyte, Tbool, Tint, Tbig, Tnames, Tstr, Tmpint: import sshfmt; valbyte, valbool, valint, valintb, valbig, valnames, valstr, valbytes, valmpint: import sshfmt; include "../lib/sshlib.m"; sshlib: Sshlib; Rssh, Tssh, Sshc, Cfg, Keys: import sshlib; Ssh: module { init: fn(nil: ref Draw->Context, args: list of string); }; dflag, sflag: int; term: string; command: string; packetc: chan of (ref Rssh, string, string, chan of int); inc: chan of (array of byte, string); readc: chan of int; outc: chan of Out; wrotec: chan of (int, string); termc: chan of (string, string); sshc: ref Sshc; termfd: ref Sys->FD; Link: adt[T] { v: T; next: cyclic ref Link; }; Out: adt { toerr: int; buf: array of byte; }; pendingfirst, pendinglast: ref Link[ref Out]; # buffers read from remote, not yet sent to outwriter. written: int; # bytes written to local stdout/stderr, but not yet used for increasing windowtorem windowfromrem, windowtorem: big; inwaiting: int; # whether inreader is waiting for key exchange to be finished Windowlow: con big (128*1024); Windowhigh: con big (256*1024); Minreadwin: con 32*1024; # only let inreader read when it can read Minreadwin Maxpayloadsize: con 32*1024; Maxkeypackets: con big 2**31; Maxkeybytes: con big (1*1024*1024); localchannel := 1; remotechannel := 2; outmaxpayloadsize: big; Xfirstkex, Xuserauthservice, Xuserauthresult, Xchannelopenresult, Xptyresult, Xcommandresult, Xnormal: con iota; expecting := Xfirstkex; expectstrs := array[] of { "firstkex", "userauthservice", "userauthresult", "channelopenresult", "ptyresult", "commandresult", "normal", }; init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; arg := load Arg Arg->PATH; dial = load Dial Dial->PATH; str = load String String->PATH; random = load Random Random->PATH; util = load Util0 Util0->PATH; util->init(); sshfmt = load Sshfmt Sshfmt->PATH; sshfmt->init(); sshlib = load Sshlib Sshlib->PATH; sshlib->init(); sys->pctl(Sys->NEWPGRP, nil); cfg := Cfg.default(); arg->init(args); arg->setusage(arg->progname()+" [-d] [-t | -T term] [-A auth-methods] [-e enc-algs] [-m mac-algs] [-K kex-algs] [-H hostkey-algs] [-C compr-algs] [-k keyspec] [-s] [user@]addr [cmd]"); while((cc := arg->opt()) != 0) case cc { 'd' => sshlib->dflag = dflag++; 'A' or 'e' or 'm' or 'K' or 'H' or 'C' or 'k' => err := cfg.setopt(cc, arg->earg()); if(err != nil) fail(err); 's'=> sflag++; 't' => term = "ansi"; 'T' => term = arg->earg(); * => arg->usage(); } args = arg->argv(); if(len args != 1 && len args != 2) arg->usage(); (user, addr) := str->splitstrl(hd args, "@"); if(addr == nil) { addr = user; user = ""; } else addr = addr[1:]; if(addr == nil) fail("empty address"); addr = dial->netmkaddr(addr, nil, "ssh"); if(len args == 2) command = hd tl args; else if(sflag) fail("-s requires command"); packetc = chan of (ref Rssh, string, string, chan of int); readc = chan of int; inc = chan of (array of byte, string); outc = chan[1] of Out; wrotec = chan of (int, string); termc = chan of (string, string); conn := dial->dial(addr, nil); if(conn == nil) fail(sprint("dial %q: %r", addr)); if(sys->fprint(conn.cfd, "keepalive") < 0) warn(sprint("setting keepalive: %r")); lerr: string; (sshc, lerr) = sshlib->handshake(conn.dfd, addr, cfg); if(lerr != nil) fail(lerr); say("handshake done"); sshc.user = user; kextm := sshlib->keyexchangestart(sshc); ewritemsg(kextm); spawn packetreader(); spawn inreader(); spawn outwriter(); vals: array of ref Val; for(;;) alt { (s, err) := <-termc => if(err != nil) { warn("termctl: "+err); continue; } t := l2a(str->unquoted(s)); if(t == nil) continue; case t[0] { "break" => vals = array[] of { valint(remotechannel), valstr("break"), valbool(0), # no reply valint(500), # 500ms }; ewritepacket(Sshlib->SSH_MSG_CHANNEL_REQUEST, vals); "dimensions" => if(len t != 3 || !isnum(t[1]) || !isnum(t[2])) continue; columns := int t[1]; rows := int t[2]; vals = array[] of { valint(remotechannel), valstr("window-change"), valbool(0), valint(columns), valint(rows), valint(0), valint(0), }; ewritepacket(Sshlib->SSH_MSG_CHANNEL_REQUEST, vals); * => say("termctl: bad command: "+s); } (d, err) := <-inc => if(err != nil) disconnect(err); if(sshc.kexbusy()) { readc <-= -1; inwaiting++; continue; } if(len d == 0) { vals = array[] of {valint(remotechannel)}; ewritepacket(Sshlib->SSH_MSG_CHANNEL_EOF, vals); continue; } vals = array[] of { valint(remotechannel), valbytes(d), }; tm := ref Tssh (big 0, Sshlib->SSH_MSG_CHANNEL_DATA, vals, random->randomint(Random->NotQuiteRandom)&16r7f, nil); ewritemsg(tm); windowtorem -= big len d; if(windowtorem > big 0) readc <-= int bigmin(outmaxpayloadsize, windowtorem); (n, err) := <-wrotec => if(err != nil) fail("local write: "+err); written += n; if(dflag) say(sprint("wrote %d, new written %d, current windowfromrem %bd", n, written, windowfromrem)); if(windowfromrem <= Windowlow && windowfromrem+big written > Windowlow) { say("increasing window for remote"); vals = array[] of { valint(remotechannel), valint(written), }; ewritepacket(Sshlib->SSH_MSG_CHANNEL_WINDOW_ADJUST, vals); windowfromrem += big written; written = 0; } if(pendingfirst != nil) { outc <-= *pendingfirst.v; pendingfirst = pendingfirst.next; if(pendingfirst == nil) pendinglast = nil; } (m, ioerr, protoerr, rc) := <-packetc => if(ioerr != nil) fail(ioerr); if(protoerr != nil) disconnect(protoerr); if(dflag) say("<- "+m.text()); case m.t { 1 to 19 or 20 to 29 or 30 to 49 => transport(m); 50 to 59 or 60 to 79 => expect(expecting==Xuserauthresult, "userauth layer message"); userauth(m); 80 to 89 or 90 to 127 => connection(m); * => ewritepacket(Sshlib->SSH_MSG_UNIMPLEMENTED, array[] of {valintb(m.seq)}); if(dflag) say("received packet we do not implement: "+m.text()); } if(dflag) say(sprint("nkeypkts %bd, nkeybytes %bd", sshc.nkeypkts, sshc.nkeybytes)); if((sshc.nkeypkts >= Maxkeypackets || sshc.nkeybytes >= Maxkeybytes) && sshc.kexbusy()) { kexmsg := sshlib->keyexchangestart(sshc); ewritemsg(kexmsg); say("key re-exchange started"); } rc <-= 0; } } transport(m: ref Rssh) { case m.t { Sshlib->SSH_MSG_DISCONNECT => v := eparsepacket(m, list of {Tint, Tstr, Tstr}); code := v[0].getint(); errmsg := v[1].getstr(); #lang := v[2].getstr(); fail(sprint("remote disconnected: %s (%d)", errmsg, code)); Sshlib->SSH_MSG_IGNORE => v := eparsepacket(m, list of {Tstr}); data := v[0].getstr(); if(dflag) say("msg ignore, data: "+data); Sshlib->SSH_MSG_UNIMPLEMENTED => v := eparsepacket(m, list of {Tint}); pktno := v[0].getintb(); disconnect(sprint("packetno %bd 'unimplemented' by remote", pktno)); Sshlib->SSH_MSG_DEBUG => v := eparsepacket(m, list of {Tbool, Tstr, Tstr}); display := v[0].getbool(); text := v[1].getstr(); lang := v[2].getstr(); if(dflag) say(sprint("remote debug, display=%d, text=%q, lang=%q", display, text, lang)); Sshlib->SSH_MSG_SERVICE_REQUEST => v := eparse(m.buf[1:], list of {Tstr}); name := v[0].getstr(); disconnect(sprint("remote sent 'service request' for %#q, invalid", name)); Sshlib->SSH_MSG_SERVICE_ACCEPT => v := eparsepacket(m, list of {Tstr}); name := v[0].getstr(); if(dflag) say("service accepted: "+name); case name { "ssh-userauth" => expect(expecting==Xuserauthservice, "userauth service accept"); (tm, err) := sshlib->userauthnext(sshc); if(err != nil) disconnect(err); if(tm != nil) ewritemsg(tm); expecting = Xuserauthresult; * => disconnect(sprint("remote sent 'server accept' for unknown service %#q", name)); } 20 to 29 or 30 to 49 => (notimpl, newkeys, tms, err) := sshlib->keyexchange(sshc, m); if(err != nil) disconnect(err); if(notimpl) { ewritepacket(Sshlib->SSH_MSG_UNIMPLEMENTED, array[] of {valintb(m.seq)}); if(dflag) say("received key exchange transport packet we do not implement: "+m.text()); return; } for(; tms != nil; tms = tl tms) ewritemsg(hd tms); if(newkeys && expecting == Xfirstkex) { vals := array[] of {valstr("ssh-userauth")}; ewritepacket(Sshlib->SSH_MSG_SERVICE_REQUEST, vals); # remember we sent this, verify when response comes in expecting = Xuserauthservice; } if(newkeys && inwaiting) { readc <-= 0; inwaiting = 0; } * => ewritepacket(Sshlib->SSH_MSG_UNIMPLEMENTED, array[] of {valintb(m.seq)}); if(dflag) say("received transport packet we do not implement: "+m.text()); } } userauth(m: ref Rssh) { case m.t { Sshlib->SSH_MSG_USERAUTH_REQUEST => disconnect("userauth request from server, invalid"); Sshlib->SSH_MSG_USERAUTH_BANNER => v := eparsepacket(m, list of {Tstr, Tstr}); msg := v[0].getstr(); lang := v[1].getstr(); lang = nil; warn("auth banner: "+msg); Sshlib->SSH_MSG_USERAUTH_FAILURE or Sshlib->SSH_MSG_USERAUTH_SUCCESS => expect(expecting==Xuserauthresult, "userauth result"); (notimpl, authok, tm, err) := sshlib->userauth(sshc, m); if(err != nil) disconnect(err); if(notimpl) { ewritepacket(Sshlib->SSH_MSG_UNIMPLEMENTED, array[] of {valintb(m.seq)}); if(dflag) say("received userauth packet we do not implement: "+m.text()); } if(tm != nil) ewritemsg(tm); if(authok) { vals := array[] of { valstr("session"), valint(localchannel), valint(0), valint(Maxpayloadsize), }; ewritepacket(Sshlib->SSH_MSG_CHANNEL_OPEN, vals); expecting = Xchannelopenresult; } * => ewritepacket(Sshlib->SSH_MSG_UNIMPLEMENTED, array[] of {valintb(m.seq)}); if(dflag) say("received userauth packet we do not implement: "+m.text()); } } connection(m: ref Rssh) { if(expecting < Xchannelopenresult) disconnect(sprint("remote sent connection messages before we requested them")); case m.t { Sshlib->SSH_MSG_GLOBAL_REQUEST => v := eparse(m.buf[1:], list of {Tstr, Tbool}); name := v[0].getstr(); wantreply := v[1].getbool(); if(dflag) say(sprint("msg-global-request %#q, wantreply %d", name, wantreply)); if(wantreply) ewritepacket(Sshlib->SSH_MSG_REQUEST_FAILURE, nil); Sshlib->SSH_MSG_REQUEST_SUCCESS => # response to global request disconnect("remote sent unsolicited SSH_MSG_REQUEST_SUCCESS"); Sshlib->SSH_MSG_REQUEST_FAILURE => # response to global request disconnect("remote sent unsolicited SSH_MSG_REQUEST_FAILURE"); Sshlib->SSH_MSG_CHANNEL_OPEN => v := eparse(m.buf[1:], list of {Tstr, Tint, Tint, Tint}); rch := v[1].getint(); vals := array[] of {valint(rch), valint(Sshlib->SSH_OPEN_ADMINISTRATIVELY_PROHIBITED), valstr("no inboud channels"), valstr("")}; ewritepacket(Sshlib->SSH_MSG_CHANNEL_OPEN_FAILURE, vals); Sshlib->SSH_MSG_CHANNEL_OPEN_CONFIRMATION => v := eparsepacket(m, list of {Tint, Tint, Tint, Tint}); lch := v[0].getint(); rch := v[1].getint(); winsize := v[2].getintb(); maxpayloadsize := v[3].getintb(); # rfc naming is confusing, this is about data channel payload size, not (ssh) "packets" expect(expecting==Xchannelopenresult, "channel open confirmation"); if(dflag) say(sprint("open confirmation, lch %d rch %d winsize %bd maxpayloadsize %bd", lch, rch, winsize, maxpayloadsize)); if(lch != localchannel) disconnect(sprint("remote sent confirmation for unknown local channel %d (expected %d)", lch, localchannel)); if(maxpayloadsize < big (32*1024)) disconnect(sprint("remote sent max packet size that is too small, %bd, minimum is 32*1024", maxpayloadsize)); outmaxpayloadsize = maxpayloadsize; remotechannel = rch; if(term != nil) { # see rfc4254, section 8 for more modes ONLCR: con byte 72; # map NL to CR-NL termmode := array[] of { valbyte(ONLCR), valint(0), # off valbyte(byte 0), }; termmode = nil; vals := array[] of { valint(remotechannel), valstr("pty-req"), valbool(1), # want reply valstr(term), valint(80), valint(24), # dimensions chars valint(0), valint(0), # dimensions pixels valbytes(sshfmt->pack(termmode, 0)), }; ewritepacket(Sshlib->SSH_MSG_CHANNEL_REQUEST, vals); say("wrote pty allocation request"); expecting = Xptyresult; } else expecting = Xcommandresult; if(sflag) { vals := array[] of { valint(remotechannel), valstr("subsystem"), valbool(1), # want reply valstr(command), }; ewritepacket(Sshlib->SSH_MSG_CHANNEL_REQUEST, vals); say("wrote subsystem request"); } else if(command != nil) { vals := array[] of { valint(remotechannel),