1464 lines · 39.0 KB · Brainfuck
RawDownload
implement Sshlib; include "sys.m";	sys: Sys;	sprint: import sys;include "bufio.m";	bufio: Bufio;	Iobuf: import bufio;include "env.m";	env: Env;include "string.m";	str: String;include "security.m";	random: Random;include "keyring.m";	kr: Keyring;	IPint, RSAsk, RSApk, RSAsig, DSAsk, DSApk, DSAsig, DigestState: import kr;include "factotum.m";	fact: Factotum;include "encoding.m";	base16, base64: Encoding;include "util0.m";	util: Util0;	readfile, hex, prefix, suffix, rev, l2a, max, min, warn, join, eq, g32i, g64, p32, p32i, p64: import util;include "sshfmt.m";	sshfmt: Sshfmt;	Val: import sshfmt;	Tbyte, Tbool, Tint, Tbig, Tnames, Tstr, Tmpint: import sshfmt;	valbool, valbyte, valint, valbig, valmpint, valnames, valstr, valbytes, valbuf: import sshfmt;include "sshlib.m";  Padmin:	con 4;Packetunitmin:	con 8;Pktlenmin:	con 16;Pktlenmax:	con 35000;  # from ssh rfc Dhexchangemin:	con 1*1024;Dhexchangewant:	con 1*1024;  # 2*1024 is recommended, but it is too slowDhexchangemax:	con 8*1024; Seqmax:	con big 2**32; dhgroup1, dhgroup14: ref Dh; # what we support.  these arrays are index by types in sshlib.m, keep them in sync!knownkex := array[] of {	"diffie-hellman-group1-sha1",	"diffie-hellman-group14-sha1",	"diffie-hellman-group-exchange-sha1",};knownhostkey := array[] of {	"ssh-dss",	"ssh-rsa",};knownenc := array[] of {	"none",	"aes128-cbc",	"aes192-cbc",	"aes256-cbc",	"idea-cbc",  # untested	"arcfour",	"aes128-ctr",	"aes192-ctr",	"aes256-ctr",	"arcfour128",	"arcfour256",	"3des-cbc",	# "blowfish-cbc",  # doesn't work};knownmac := array[] of {	"none",	"hmac-sha1",	"hmac-sha1-96",	"hmac-md5",	"hmac-md5-96",};knowncompr := array[] of {	"none",};knownauthmeth := array[] of {	"publickey",	"password",}; # what we want to do by default, first is preferreddefkex :=	array[] of {Dgroupexchange, Dgroup14, Dgroup1};defhostkey :=	array[] of {Hrsa, Hdss};defenc :=	array[] of {Eaes128cbc, Eaes192cbc, Eaes256cbc, Eaes128ctr, Eaes192ctr, Eaes256ctr, Earcfour128, Earcfour256, Earcfour, E3descbc};defmac :=	array[] of {Msha1_96, Msha1, Mmd5, Mmd5_96};defcompr :=	array[] of {Cnone};defauthmeth :=	array[] of {Apublickey, Apassword}; msgnames := array[] of {SSH_MSG_DISCONNECT		=> "disconnect",SSH_MSG_IGNORE			=> "ignore",SSH_MSG_UNIMPLEMENTED		=> "unimplemented",SSH_MSG_DEBUG			=> "debug",SSH_MSG_SERVICE_REQUEST		=> "service request",SSH_MSG_SERVICE_ACCEPT		=> "service accept",SSH_MSG_KEXINIT			=> "kex init",SSH_MSG_NEWKEYS			=> "new keys", SSH_MSG_KEXDH_INIT		=> "kexdh init",SSH_MSG_KEXDH_REPLY		=> "kexdh reply",SSH_MSG_KEXDH_GEX_INIT		=> "kexdh gex init",SSH_MSG_KEXDH_GEX_REPLY		=> "kexdh gex reply",SSH_MSG_KEXDH_GEX_REQUEST	=> "kexdh gex request", SSH_MSG_USERAUTH_REQUEST	=> "userauth request",SSH_MSG_USERAUTH_FAILURE	=> "userauth failure",SSH_MSG_USERAUTH_SUCCESS	=> "userauth success",SSH_MSG_USERAUTH_BANNER		=> "userauth banner", SSH_MSG_GLOBAL_REQUEST		=> "global request",SSH_MSG_REQUEST_SUCCESS		=> "request success",SSH_MSG_REQUEST_FAILURE		=> "request failure",SSH_MSG_CHANNEL_OPEN		=> "channel open",SSH_MSG_CHANNEL_OPEN_CONFIRMATION	=> "channel open confirmation",SSH_MSG_CHANNEL_OPEN_FAILURE	=> "open failure",SSH_MSG_CHANNEL_WINDOW_ADJUST	=> "window adjust",SSH_MSG_CHANNEL_DATA		=> "channel data",SSH_MSG_CHANNEL_EXTENDED_DATA	=> "channel extended data",SSH_MSG_CHANNEL_EOF		=> "channel eof",SSH_MSG_CHANNEL_CLOSE		=> "channel close",SSH_MSG_CHANNEL_REQUEST		=> "channel request",SSH_MSG_CHANNEL_SUCCESS		=> "channel success",SSH_MSG_CHANNEL_FAILURE		=> "channel failure",}; init(){	sys = load Sys Sys->PATH;	bufio = load Bufio Bufio->PATH;	bufio->open("/dev/null", Bufio->OREAD);	env = load Env Env->PATH;	str = load String String->PATH;	random = load Random Random->PATH;	kr = load Keyring Keyring->PATH;	base16 = load Encoding Encoding->BASE16PATH;	base64 = load Encoding Encoding->BASE64PATH;	fact = load Factotum Factotum->PATH;	fact->init();	util = load Util0 Util0->PATH;	util->init();	sshfmt = load Sshfmt Sshfmt->PATH;	sshfmt->init(); 	group1primestr := 		"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"+		"29024E088A67CC74020BBEA63B139B22514A08798E3404DD"+		"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"+		"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"+		"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"+		"FFFFFFFFFFFFFFFF";	group1prime := IPint.strtoip(group1primestr, 16);	group1gen := IPint.inttoip(2);	dhgroup1 = ref Dh (group1prime, group1gen, 1024); 	group14primestr :=		"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"+		"29024E088A67CC74020BBEA63B139B22514A08798E3404DD"+		"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"+		"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"+		"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"+		"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"+		"83655D23DCA3AD961C62F356208552BB9ED529077096966D"+		"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"+		"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"+		"DE2BCBF6955817183995497CEA956AE515D2261898FA0510"+		"15728E5A8AACAA68FFFFFFFFFFFFFFFF";	group14prime := IPint.strtoip(group14primestr, 16);	group14gen := IPint.inttoip(2);	dhgroup14 = ref Dh (group14prime, group14gen, 2048);} msgname(t: int): string{	if(t < 0 || t >= len msgnames || msgnames[t] == nil)		return "unknown";	return msgnames[t];}  handshake(fd: ref Sys->FD, addr: string, wantcfg: ref Cfg): (ref Sshc, string){	b := bufio->fopen(fd, Bufio->OREAD);	if(b == nil)		return (nil, sprint("bufio fopen: %r")); 	lident := "SSH-2.0-inferno0";	if(sys->fprint(fd, "%s\r\n", lident) < 0)		return (nil, sprint("write handshake: %r")); 	rident: string;	for(;;) {		rident = b.gets('\n');		if(rident == nil || rident[len rident-1] != '\n')			return (nil, sprint("eof before identification line"));		if(!prefix("SSH-", rident))			continue;		if(len rident > 255)			return (nil, sprint("identification from remote too long, invalid")); 		rident = rident[:len rident-1];		if(suffix("\r", rident))			rident = rident[:len rident-1]; 		# note: rident (minus \n or \r\n) is used in key exchange, must be left as is		(rversion, rname) := str->splitstrl(rident[len "SSH-":], "-");		if(rname == nil)			return (nil, sprint("bad remote identification %#q, missing 'name'", rident));		rcomment: string;		(rname, rcomment) = str->splitstrl(rname[1:], " ");		say(sprint("have remote version %q, name %q, comment %q", rversion, rname, rcomment)); 		if(rversion != "2.0" && rversion != "1.99")			return (nil, sprint("unsupported remote version %#q", rversion));		break;	} 	nilkey := ref Keys (Cryptalg.new(Enone), Macalg.new(Enone));	c := ref Sshc (		fd, b, addr, nil,		big 0, big 0, big 0, big 0,		nilkey, nilkey, nil, nil,		lident, rident,		wantcfg, nil,		nil,		nil,		0, nil, nil, nil	);	return (c, nil);} keyexchangestart(c: ref Sshc): ref Tssh{	say("keyexchangestart");	nilnames := valnames(nil);	cookie := random->randombuf(Random->NotQuiteRandom, 16);	vals := array[] of {		valbuf(cookie),		valnames(c.wantcfg.kex),		valnames(c.wantcfg.hostkey),		valnames(c.wantcfg.encout), valnames(c.wantcfg.encin),		valnames(c.wantcfg.macout), valnames(c.wantcfg.macin),		valnames(c.wantcfg.comprout), valnames(c.wantcfg.comprin),		nilnames, nilnames,		valbool(0),		valint(0),	}; 	tm := ref Tssh(big 0, SSH_MSG_KEXINIT, vals, 0, nil);	c.kexstate |= Kexinitsent; 	nvals := array[1+len vals] of ref Val;	nvals[0] = valbyte(byte SSH_MSG_KEXINIT);	nvals[1:] = vals;	c.clkexinit = sshfmt->pack(nvals, 0); 	return tm;} keyexchange(c: ref Sshc, m: ref Rssh): (int, int, list of ref Tssh, string){	{		(notimpl, kexdone, tms) := xkeyexchange(c, m);		return (notimpl, kexdone, tms, nil);	} exception x {	"kex:*" =>		return (0, 0, nil, x[len "kex:":]);	"parse:*" =>		return (0, 0, nil, x[len "parse:":]);	}} xparseall(m: ref Rssh, l: list of int): array of ref Val{	(v, err) := sshfmt->parseall(m.buf[1:], l);	if(err != nil)		raise "parse:"+err;	return v;} kexerror(s: string){	raise "kex:"+s;} xkeyexchange(c: ref Sshc, m: ref Rssh): (int, int, list of ref Tssh){	case m.t {	SSH_MSG_KEXINIT =>		kexmsg := list of {16, Tnames, Tnames, Tnames, Tnames, Tnames, Tnames, Tnames, Tnames, Tnames, Tnames, Tbool, Tint};		v := xparseall(m, kexmsg); 		c.srvkexinit = m.buf;		o := 1;		remcfg := ref Cfg (			nil,			v[o++].getnames(),			v[o++].getnames(),			v[o++].getnames(), v[o++].getnames(),			v[o++].getnames(), v[o++].getnames(),			v[o++].getnames(), v[o++].getnames(),			nil		);		if(dflag) {			say("languages client to server: "+v[o++].text());			say("languages server to client: "+v[o++].text());			say("first kex packet follows: "+v[o++].text());			say("out config:");			say(c.wantcfg.text());			say("from remote:");			say(remcfg.text());		}		err: string;		(c.usecfg, err) = Cfg.match(c.wantcfg, remcfg);		if(err != nil)			kexerror(err);		if(dflag) say("chosen config:\n"+c.usecfg.text()); 		c.auths = authmethods(c.usecfg.authmeth);		(c.newtosrv, c.newfromsrv) = Keys.new(c.usecfg);		case hd c.usecfg.kex {		"diffie-hellman-group1-sha1" =>			c.kex = ref Kex (0, dhgroup1, nil, nil);		"diffie-hellman-group14-sha1" =>			c.kex = ref Kex (0, dhgroup14, nil, nil);		"diffie-hellman-group-exchange-sha1" =>			c.kex = ref Kex (1, nil, nil, nil);		* =>			raise "internal error, unknown kex alg";		} 		tms: list of ref Tssh;		c.kexstate |= Kexinitreceived;		if((c.kexstate & Kexinitsent) == 0)			tms = keyexchangestart(c)::nil; 		tm: ref Tssh;		if(c.kex.new) {			vals := array[] of {valint(Dhexchangemin), valint(Dhexchangewant), valint(Dhexchangemax)};			tm = ref Tssh (big 0, SSH_MSG_KEX_DH_GEX_REQUEST, vals, 0, nil);		} else {			gendh(c.kex);			vals := array[] of {valmpint(c.kex.e)};			tm = ref Tssh (big 0, SSH_MSG_KEXDH_INIT, vals, 0, nil);		}		tms = tm::tms;		return (0, 0, rev(tms)); 	SSH_MSG_NEWKEYS =>		xparseall(m, nil);		say("server wants to use newkeys"); 		if((c.kexstate & Havenewkeys) == 0)			kexerror("server wants to use new keys, but none are pending"); 		tms: list of ref Tssh;		if((c.kexstate & Newkeyssent) == 0) {			say("writing newkeys to remote");			tm := ref Tssh (big 0, SSH_MSG_NEWKEYS, nil, 0, nil);			# pack now we still have the old keys			tm.packed = packpacket(c, tm);			tms = tm::nil;			c.kexstate |= Newkeyssent;		} 		say("now using new keys");		c.tosrv = c.newtosrv;		c.fromsrv = c.newfromsrv;		c.newtosrv = c.newfromsrv = nil;		c.nkeypkts = c.nkeybytes = big 0;		c.kexstate &= ~(Kexinitsent|Kexinitreceived|Newkeyssent|Newkeysreceived|Havenewkeys);		return (0, 1, tms); 	SSH_MSG_KEXDH_INIT =>		kexerror("received SSH_MSG_KEXDH_INIT from server, invalid"); 	SSH_MSG_KEXDH_REPLY or	SSH_MSG_KEXDH_GEX_INIT to # xxx is gex init valid?	SSH_MSG_KEXDH_GEX_REQUEST => 		if((c.kexstate & (Kexinitsent|Kexinitreceived)) != (Kexinitsent|Kexinitreceived))			kexerror("kexdh messages but no kexinit in progress!");		if((c.kexstate & Havenewkeys) != 0)			kexerror("kexhd message, but already Havenewkeys?"); 		if(c.kex.new && m.t == SSH_MSG_KEX_DH_GEX_REPLY || !c.kex.new && m.t == SSH_MSG_KEXDH_REPLY) {			v := xparseall(m, list of {Tstr, Tmpint, Tstr}); 			#string    server public host key and certificates (K_S)			#mpint     f			#string    signature of H			srvksval := v[0];			srvfval := v[1];			srvsigh := v[2].getbytes();			srvks := srvksval.getbytes();			srvf := srvfval.getipint(); 			# C then			# computes K = f^x mod p, H = hash(V_C || V_S || I_C || I_S || K_S			# || e || f || K), and verifies the signature s on H.			say("calculating key from f from remote");			key := srvf.expmod(c.kex.x, c.kex.dhgroup.prime);			c.kex.x = nil;			#say(sprint("key %s", key.iptostr(16)));			hashbufs: list of array of byte;			if(c.kex.new)				hashbufs = list of {					valstr(c.lident).pack(),					valstr(c.rident).pack(),					valbytes(c.clkexinit).pack(),					valbytes(c.srvkexinit).pack(),					srvksval.pack(),					valint(Dhexchangemin).pack(),					valint(Dhexchangewant).pack(),					valint(Dhexchangemax).pack(),					valmpint(c.kex.dhgroup.prime).pack(),					valmpint(c.kex.dhgroup.gen).pack(),					valmpint(c.kex.e).pack(),					srvfval.pack(),					valmpint(key).pack()				};			else				hashbufs = list of {					valstr(c.lident).pack(),					valstr(c.rident).pack(),					valbytes(c.clkexinit).pack(),					valbytes(c.srvkexinit).pack(),					srvksval.pack(),					valmpint(c.kex.e).pack(),					srvfval.pack(),					valmpint(key).pack()				};			dhhash := sha1many(hashbufs);			zero(c.clkexinit);			c.clkexinit = nil;			zero(c.srvkexinit);			c.srvkexinit = nil;			srvfval = nil;			if(dflag) say(sprint("hash on dh %s", fingerprint(dhhash))); 			if(c.sessionid == nil)				c.sessionid = dhhash; 			err := verifyhostkey(c, hd c.usecfg.hostkey, srvks, srvsigh, dhhash);			if(err != nil)				kexerror(err); 			# calculate session keys			#Encryption keys MUST be computed as HASH, of a known value and K, as follows:			#o  Initial IV client to server: HASH(K || H || "A" || session_id)			#    (Here K is encoded as mpint and "A" as byte and session_id as raw			#   data.  "A" means the single character A, ASCII 65).			#o  Initial IV server to client: HASH(K || H || "B" || session_id)			#o  Encryption key client to server: HASH(K || H || "C" || session_id)			#o  Encryption key server to client: HASH(K || H || "D" || session_id)			#o  Integrity key client to server: HASH(K || H || "E" || session_id)			#o  Integrity key server to client: HASH(K || H || "F" || session_id) 			keypack := valmpint(key).pack(); 			keybitsout := c.newtosrv.crypt.keybits;			keybitsin := c.newfromsrv.crypt.keybits;			macbitsout := c.newtosrv.mac.keybytes*8;			macbitsin := c.newfromsrv.mac.keybytes*8; 			ivc2s := genkey(keybitsout, keypack, dhhash, "A", c.sessionid);			ivs2c := genkey(keybitsin, keypack, dhhash, "B", c.sessionid);			enckeyc2s := genkey(keybitsout, keypack, dhhash, "C", c.sessionid);			enckeys2c := genkey(keybitsin, keypack, dhhash, "D", c.sessionid);			mackeyc2s := genkey(macbitsout, keypack, dhhash, "E", c.sessionid);			mackeys2c := genkey(macbitsin, keypack, dhhash, "F", c.sessionid); 			c.newtosrv.crypt.setup(enckeyc2s, ivc2s);			c.newfromsrv.crypt.setup(enckeys2c, ivs2c);			c.newtosrv.mac.setup(mackeyc2s);			c.newfromsrv.mac.setup(mackeys2c); 			say("we want to use newkeys");			c.kexstate |= Havenewkeys|Newkeyssent;			tm := ref Tssh (big 0, SSH_MSG_NEWKEYS, nil, 0, nil);			return (0, 0, tm::nil); 		} else if(c.kex.new && m.t == SSH_MSG_KEX_DH_GEX_GROUP) {			v := xparseall(m, list of {Tmpint, Tmpint});			prime := v[0].getipint();			gen := v[1].getipint();			# xxx should verify these values are sane.			c.kex.dhgroup = ref Dh (prime, gen, prime.bits()); 			gendh(c.kex); 			vals := array[] of {valmpint(c.kex.e)};			tm := ref Tssh (big 0, SSH_MSG_KEX_DH_GEX_INIT, vals, 0, nil);			return (0, 0, tm::nil);		} else {			kexerror(sprint("unexpected kex message, t %d, new %d", m.t, c.kex.new));

Lines 1–500 of 1464.