diff options
Diffstat (limited to 'appl/cmd')
| -rw-r--r-- | appl/cmd/auth/dsagen.b | 14 | ||||
| -rw-r--r-- | appl/cmd/auth/factotum/proto/rsa.b | 64 | ||||
| -rw-r--r-- | appl/cmd/auth/getpk.b | 32 | ||||
| -rw-r--r-- | appl/cmd/auth/rsagen.b | 44 | ||||
| -rw-r--r-- | appl/cmd/auth/signer.b | 83 | ||||
| -rw-r--r-- | appl/cmd/auth/verify.b | 13 | ||||
| -rw-r--r-- | appl/cmd/cpu.b | 27 | ||||
| -rw-r--r-- | appl/cmd/dial.b | 12 | ||||
| -rw-r--r-- | appl/cmd/listen.b | 33 | ||||
| -rw-r--r-- | appl/cmd/sh/mpexpr.b | 10 | ||||
| -rw-r--r-- | appl/cmd/spki/verify.b | 10 | ||||
| -rw-r--r-- | appl/cmd/ssh/authpassword.b | 68 | ||||
| -rw-r--r-- | appl/cmd/ssh/authrsa.b | 188 | ||||
| -rw-r--r-- | appl/cmd/ssh/authtis.b | 119 | ||||
| -rw-r--r-- | appl/cmd/ssh/cipher3des.b | 51 | ||||
| -rw-r--r-- | appl/cmd/ssh/cipherblowfish.b | 43 | ||||
| -rw-r--r-- | appl/cmd/ssh/cipherdes.b | 43 | ||||
| -rw-r--r-- | appl/cmd/ssh/ciphernone.b | 28 | ||||
| -rw-r--r-- | appl/cmd/ssh/cipherrc4.b | 46 | ||||
| -rw-r--r-- | appl/cmd/ssh/mkfile | 29 | ||||
| -rw-r--r-- | appl/cmd/ssh/sshio.b | 586 | ||||
| -rw-r--r-- | appl/cmd/ssh/sshio.m | 194 | ||||
| -rw-r--r-- | appl/cmd/ssh/sshserve.b | 495 | ||||
| -rw-r--r-- | appl/cmd/styxlisten.b | 34 |
24 files changed, 2094 insertions, 172 deletions
diff --git a/appl/cmd/auth/dsagen.b b/appl/cmd/auth/dsagen.b index 3e24df2f..51b50a12 100644 --- a/appl/cmd/auth/dsagen.b +++ b/appl/cmd/auth/dsagen.b @@ -5,9 +5,12 @@ include "sys.m"; include "draw.m"; -include "keyring.m"; - kr: Keyring; - IPint, DSAsk, DSApk, DSAsig: import kr; +include "ipints.m"; + ipints: IPints; + IPint: import ipints; + +include "crypt.m"; + crypt: Crypt; include "arg.m"; @@ -19,7 +22,8 @@ Dsagen: module init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; - kr = load Keyring Keyring->PATH; + ipints = load IPints IPints->PATH; + crypt = load Crypt Crypt->PATH; arg := load Arg Arg->PATH; arg->init(args); @@ -37,7 +41,7 @@ init(nil: ref Draw->Context, args: list of string) arg->usage(); arg = nil; - sk := DSAsk.gen(nil); + sk := crypt->dsagen(nil); if(tag != nil) tag = " "+tag; s := add("p", sk.pk.p); diff --git a/appl/cmd/auth/factotum/proto/rsa.b b/appl/cmd/auth/factotum/proto/rsa.b index 24dcef43..8af64578 100644 --- a/appl/cmd/auth/factotum/proto/rsa.b +++ b/appl/cmd/auth/factotum/proto/rsa.b @@ -1,6 +1,10 @@ implement Authproto; -# SSH RSA authentication. +# SSH RSA authentication +# +# this version is compatible with Plan 9 factotum +# Plan 9 port's factotum works differently, and eventually +# we'll support both (the role= attribute distinguishes the cases), but not today # # Client protocol: # read public key @@ -16,35 +20,38 @@ include "sys.m"; include "draw.m"; -include "keyring.m"; - kr: Keyring; - IPint, RSAsk, RSApk: import kr; +include "ipints.m"; + ipints: IPints; + IPint: import ipints; +include "crypt.m"; + crypt: Crypt; + SK, PK: import crypt; include "../authio.m"; authio: Authio; Aattr, Aval, Aquery: import Authio; - Attr, IO, Key, Authinfo: import authio; + Attr, IO, Key: import authio; eqbytes, memrandom: import authio; - lookattrval: import authio; + findattrval: import authio; init(f: Authio): string { authio = f; sys = load Sys Sys->PATH; - kr = load Keyring Keyring->PATH; -# base16 = load Encoding Encoding->BASE16PATH; + ipints = load IPints IPints->PATH; + crypt = load Crypt Crypt->PATH; return nil; } interaction(attrs: list of ref Attr, io: ref IO): string { - role := lookattrval(attrs, "role"); + role := findattrval(attrs, "role"); if(role == nil) return "role not specified"; if(role != "client") return "only client role supported"; - sk: ref RSAsk; + sk: ref SK.RSA; keys: list of ref Key; err: string; for(;;){ @@ -67,7 +74,7 @@ interaction(attrs: list of ref Attr, io: ref IO): string io.error("invalid challenge value"); continue; } - m := sk.decrypt(chal); + m := crypt->rsadecrypt(sk, chal); b := array of byte m.iptostr(16); io.write(b, len b); io.done(nil); @@ -89,30 +96,33 @@ waitread(io: ref IO) Badkey: exception(string); -ipint(attrs: list of ref Attr, name: string): ref IPint raises Badkey +kv(key: ref Key, name: string): ref IPint raises Badkey { - s := lookattrval(attrs, name); - if(s == nil) + if(name[0] == '!') + a := authio->findattrval(key.secrets, name); + else + a = authio->findattrval(key.attrs, name); + if(a == nil) raise Badkey("missing attribute "+name); - m := IPint.strtoip(s, 16); + m := IPint.strtoip(a, 16); if(m == nil) - raise Badkey("invalid value for "+name); + raise Badkey("bad value for "+name); return m; } -keytorsa(k: ref Key): (ref RSAsk, string) +keytorsa(k: ref Key): (ref SK.RSA, string) { - sk := ref RSAsk; - sk.pk = ref RSApk; + sk := ref SK.RSA; + sk.pk = ref PK.RSA; { - sk.pk.ek = ipint(k.attrs, "ek"); - sk.pk.n = ipint(k.attrs, "n"); - sk.dk = ipint(k.secrets, "!dk"); - sk.p = ipint(k.secrets, "!p"); - sk.q = ipint(k.secrets, "!q"); - sk.kp = ipint(k.secrets, "!kp"); - sk.kq = ipint(k.secrets, "!kq"); - sk.c2 = ipint(k.secrets, "!c2"); + sk.pk.ek = kv(k, "ek"); + sk.pk.n = kv(k, "n"); + sk.dk = kv(k, "!dk"); + sk.p = kv(k, "!p"); + sk.q = kv(k, "!q"); + sk.kp = kv(k, "!kp"); + sk.kq = kv(k, "!kq"); + sk.c2 = kv(k, "!c2"); }exception e{ Badkey => return (nil, "rsa key "+e); diff --git a/appl/cmd/auth/getpk.b b/appl/cmd/auth/getpk.b index 24283340..e2273d17 100644 --- a/appl/cmd/auth/getpk.b +++ b/appl/cmd/auth/getpk.b @@ -3,10 +3,14 @@ include "sys.m"; sys: Sys; include "draw.m"; include "arg.m"; -include "keyring.m"; - keyring: Keyring; +include "ipints.m"; +include "crypt.m"; + crypt: Crypt; +include "oldauth.m"; + oldauth: Oldauth; -Getpk: module { +Getpk: module +{ init: fn(nil: ref Draw->Context, argv: list of string); }; @@ -19,14 +23,18 @@ badmodule(p: string) init(nil: ref Draw->Context, argv: list of string) { sys = load Sys Sys->PATH; - keyring = load Keyring Keyring->PATH; - if(keyring == nil) - badmodule(Keyring->PATH); + crypt = load Crypt Crypt->PATH; + if(crypt == nil) + badmodule(Crypt->PATH); + oldauth = load Oldauth Oldauth->PATH; + if(oldauth == nil) + badmodule(Oldauth->PATH); + oldauth->init(); arg := load Arg Arg->PATH; if(arg == nil) badmodule(Arg->PATH); arg->init(argv); - arg->setusage("usage: getpk [-asu] file..."); + arg->setusage("getpk [-asu] file..."); aflag := 0; sflag := 0; uflag := 0; @@ -47,7 +55,7 @@ init(nil: ref Draw->Context, argv: list of string) arg->usage(); multi := len argv > 1; for(; argv != nil; argv = tl argv){ - info := keyring->readauthinfo(hd argv); + info := oldauth->readauthinfo(hd argv); if(info == nil){ sys->fprint(sys->fildes(2), "getpk: cannot read %s: %r\n", hd argv); continue; @@ -55,13 +63,13 @@ init(nil: ref Draw->Context, argv: list of string) pk := info.mypk; if(sflag) pk = info.spk; - s := keyring->pktostr(pk); + s := oldauth->pktostr(pk, info.owner); if(!aflag) s = hex(hash(s)); if(multi) s = hd argv + ": " + s; if(uflag) - s += " " + pk.owner; + s += " " + info.owner; sys->print("%s\n", s); } } @@ -69,8 +77,8 @@ init(nil: ref Draw->Context, argv: list of string) hash(s: string): array of byte { d := array of byte s; - digest := array[Keyring->SHA1dlen] of byte; - keyring->sha1(d, len d, digest, nil); + digest := array[Crypt->SHA1dlen] of byte; + crypt->sha1(d, len d, digest, nil); return digest; } diff --git a/appl/cmd/auth/rsagen.b b/appl/cmd/auth/rsagen.b index a1a4477c..48fcba6d 100644 --- a/appl/cmd/auth/rsagen.b +++ b/appl/cmd/auth/rsagen.b @@ -5,8 +5,12 @@ include "sys.m"; include "draw.m"; -include "keyring.m"; - kr: Keyring; +include "ipints.m"; + ipints: IPints; + IPint: import ipints; + +include "crypt.m"; + crypt: Crypt; include "arg.m"; @@ -18,7 +22,8 @@ Rsagen: module init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; - kr = load Keyring Keyring->PATH; + ipints = load IPints IPints->PATH; + crypt = load Crypt Crypt->PATH; arg := load Arg Arg->PATH; arg->init(args); @@ -43,34 +48,31 @@ init(nil: ref Draw->Context, args: list of string) arg->usage(); arg = nil; - sk := kr->genSK("rsa", "", nbits); + sk := crypt->rsagen(nbits, 6, 0); if(sk == nil) error("unable to generate key"); - s := kr->sktoattr(sk); - # need to fix the attr interface so the following isn't needed: - s = skip(s, "alg"); - s = skip(s, "owner"); if(tag != nil) tag = " "+tag; - a := sys->aprint("key proto=rsa%s size=%d %s\n", tag, nbits, s); + s := add("ek", sk.pk.ek); + s += add("n", sk.pk.n); + s += add("!dk", sk.dk); + s += add("!p", sk.p); + s += add("!q", sk.q); + s += add("!kp", sk.kp); + s += add("!kq", sk.kq); + s += add("!c2", sk.c2); + a := sys->aprint("key proto=rsa%s size=%d%s\n", tag, sk.pk.n.bits(), s); if(sys->write(sys->fildes(1), a, len a) != len a) error(sys->sprint("error writing key: %r")); } -skip(s: string, attr: string): string -{ - for(i := 0; i < len s && s[i] != ' '; i++) - {} - if(i >= len s) - return s; - (nf, fld) := sys->tokenize(s[0:i], "="); - if(nf == 2 && hd fld == attr) - s = s[i+1:]; - return s; -} - error(s: string) { sys->fprint(sys->fildes(2), "rsagen: %s\n", s); raise "fail:error"; } + +add(name: string, b: ref IPint): string +{ + return " "+name+"="+b.iptostr(16); +} diff --git a/appl/cmd/auth/signer.b b/appl/cmd/auth/signer.b index b3f4669d..b27a719c 100644 --- a/appl/cmd/auth/signer.b +++ b/appl/cmd/auth/signer.b @@ -5,10 +5,20 @@ include "sys.m"; include "draw.m"; -include "keyring.m"; - kr: Keyring; - IPint: import kr; +include "ipints.m"; + ipints: IPints; + IPint: import ipints; + +include "crypt.m"; + crypt: Crypt; + +include "oldauth.m"; + oldauth: Oldauth; +include "msgio.m"; + msgio: Msgio; + +include "keyring.m"; include "security.m"; random: Random; @@ -29,7 +39,12 @@ init(nil: ref Draw->Context, nil: list of string) { sys = load Sys Sys->PATH; random = load Random Random->PATH; - kr = load Keyring Keyring->PATH; + ipints = load IPints IPints->PATH; + crypt = load Crypt Crypt->PATH; + oldauth = load Oldauth Oldauth->PATH; + oldauth->init(); + msgio = load Msgio Msgio->PATH; + msgio->init(); stdin = sys->fildes(0); stdout = sys->fildes(1); @@ -55,75 +70,75 @@ sign(): string return "can't read key"; # send public part to client - mypkbuf := array of byte kr->pktostr(kr->sktopk(info.mysk)); - kr->sendmsg(stdout, mypkbuf, len mypkbuf); + mypkbuf := array of byte oldauth->pktostr(crypt->sktopk(info.mysk), info.owner); + msgio->sendmsg(stdout, mypkbuf, len mypkbuf); alphabuf := array of byte info.alpha.iptob64(); - kr->sendmsg(stdout, alphabuf, len alphabuf); + msgio->sendmsg(stdout, alphabuf, len alphabuf); pbuf := array of byte info.p.iptob64(); - kr->sendmsg(stdout, pbuf, len pbuf); + msgio->sendmsg(stdout, pbuf, len pbuf); # get client's public key - hisPKbuf := kr->getmsg(stdin); + hisPKbuf := msgio->getmsg(stdin); if(hisPKbuf == nil) return "caller hung up"; - hisPK := kr->strtopk(string hisPKbuf); + (hisPK, hisname) := oldauth->strtopk(string hisPKbuf); if(hisPK == nil) return "illegal caller PK"; # hash, sign, and blind - state := kr->sha1(hisPKbuf, len hisPKbuf, nil, nil); - cert := kr->sign(info.mysk, 0, state, "sha1"); + state := crypt->sha1(hisPKbuf, len hisPKbuf, nil, nil); + cert := oldauth->sign(info.mysk, info.owner, 0, state, "sha1"); # sanity clause - state = kr->sha1(hisPKbuf, len hisPKbuf, nil, nil); - if(kr->verify(info.mypk, cert, state) == 0) + state = crypt->sha1(hisPKbuf, len hisPKbuf, nil, nil); + if(oldauth->verify(info.mypk, cert, state) == 0) return "bad signer certificate"; - certbuf := array of byte kr->certtostr(cert); + certbuf := array of byte oldauth->certtostr(cert); blind := random->randombuf(random->ReallyRandom, len certbuf); for(i := 0; i < len blind; i++) certbuf[i] = certbuf[i] ^ blind[i]; # sum PKs and blinded certificate - state = kr->md5(mypkbuf, len mypkbuf, nil, nil); - kr->md5(hisPKbuf, len hisPKbuf, nil, state); + state = crypt->md5(mypkbuf, len mypkbuf, nil, nil); + crypt->md5(hisPKbuf, len hisPKbuf, nil, state); digest := array[Keyring->MD5dlen] of byte; - kr->md5(certbuf, len certbuf, digest, state); + crypt->md5(certbuf, len certbuf, digest, state); # save sum and blinded cert in a file - file := "signed/"+hisPK.owner; + file := "signed/"+hisname; fd := sys->create(file, Sys->OWRITE, 8r600); if(fd == nil) return "can't create "+file+sys->sprint(": %r"); - if(kr->sendmsg(fd, blind, len blind) < 0 || - kr->sendmsg(fd, digest, len digest) < 0){ + if(msgio->sendmsg(fd, blind, len blind) < 0 || + msgio->sendmsg(fd, digest, len digest) < 0){ sys->remove(file); return "can't write "+file+sys->sprint(": %r"); } # send blinded cert to client - kr->sendmsg(stdout, certbuf, len certbuf); + msgio->sendmsg(stdout, certbuf, len certbuf); return nil; } -signerkey(filename: string): ref Keyring->Authinfo +signerkey(filename: string): ref Oldauth->Authinfo { - info := kr->readauthinfo(filename); + info := oldauth->readauthinfo(filename); if(info != nil) return info; # generate a local key - info = ref Keyring->Authinfo; - info.mysk = kr->genSK("elgamal", "*", PKmodlen); - info.mypk = kr->sktopk(info.mysk); - info.spk = kr->sktopk(info.mysk); - myPKbuf := array of byte kr->pktostr(info.mypk); - state := kr->sha1(myPKbuf, len myPKbuf, nil, nil); - info.cert = kr->sign(info.mysk, 0, state, "sha1"); - (info.alpha, info.p) = kr->dhparams(DHmodlen); - - if(kr->writeauthinfo(filename, info) < 0){ + info = ref Oldauth->Authinfo; + info.mysk = crypt->genSK("elgamal", PKmodlen); + info.mypk = crypt->sktopk(info.mysk); + info.spk = crypt->sktopk(info.mysk); + myPKbuf := array of byte oldauth->pktostr(info.mypk, "*"); + state := crypt->sha1(myPKbuf, len myPKbuf, nil, nil); + info.cert = oldauth->sign(info.mysk, "*", 0, state, "sha1"); + (info.alpha, info.p) = crypt->dhparams(DHmodlen); + + if(oldauth->writeauthinfo(filename, info) < 0){ sys->fprint(stderr, "can't write signerkey file: %r\n"); return nil; } diff --git a/appl/cmd/auth/verify.b b/appl/cmd/auth/verify.b index d829a76c..91fe1a86 100644 --- a/appl/cmd/auth/verify.b +++ b/appl/cmd/auth/verify.b @@ -3,8 +3,8 @@ implement Verify; include "sys.m"; sys: Sys; -include "keyring.m"; - kr: Keyring; +include "msgio.m"; + msgio: Msgio; include "draw.m"; @@ -25,7 +25,8 @@ pro := array[] of { init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; - kr = load Keyring Keyring->PATH; + msgio = load Msgio Msgio->PATH; + msgio->init(); stdin = sys->fildes(0); stderr = sys->fildes(2); @@ -50,8 +51,8 @@ init(nil: ref Draw->Context, args: list of string) sys->fprint(stderr, "signer: can't open %s: %r\n", file); raise "fail:no certificate"; } - certbuf := kr->getmsg(fd); - digest := kr->getmsg(fd); + certbuf := msgio->getmsg(fd); + digest := msgio->getmsg(fd); if(digest == nil || certbuf == nil){ sys->fprint(stderr, "signer: can't read %s: %r\n", file); raise "fail:bad certificate"; @@ -78,7 +79,7 @@ init(nil: ref Draw->Context, args: list of string) sys->fprint(stderr, "signer: can't create %s: %r\n", nfile); raise "fail:create"; } - if(kr->sendmsg(fd, certbuf, len certbuf) < 0){ + if(msgio->sendmsg(fd, certbuf, len certbuf) < 0){ sys->fprint(stderr, "signer: can't write %s: %r\n", nfile); raise "fail:write"; } diff --git a/appl/cmd/cpu.b b/appl/cmd/cpu.b index 75728e57..8acab430 100644 --- a/appl/cmd/cpu.b +++ b/appl/cmd/cpu.b @@ -5,6 +5,9 @@ include "sys.m"; stderr: ref Sys->FD; include "draw.m"; Context: import Draw; + +include "dial.m"; + include "string.m"; str: String; include "arg.m"; @@ -50,6 +53,9 @@ init(nil: ref Context, argv: list of string) kr := load Keyring Keyring->PATH; if (kr == nil) badmodule(Keyring->PATH); + dial := load Dial Dial->PATH; + if(dial == nil) badmodule(Dial->PATH); + arg->init(argv); alg := ""; while ((opt := arg->opt()) != 0) { @@ -79,7 +85,7 @@ init(nil: ref Context, argv: list of string) user := getuser(); kd := "/usr/" + user + "/keyring/"; - cert := kd + netmkaddr(mach, "tcp", ""); + cert := kd + dial->netmkaddr(mach, "tcp", ""); if (!exists(cert)) { cert = kd + "default"; if (!exists(cert)) { @@ -92,8 +98,8 @@ init(nil: ref Context, argv: list of string) if(!exists("/dev/draw/new")) sys->bind("#d", "/dev", Sys->MBEFORE); - (ok, c) := sys->dial(netmkaddr(mach, "net", "rstyx"), nil); - if(ok < 0){ + c := dial->dial(dial->netmkaddr(mach, "net", "rstyx"), nil); + if(c == nil){ sys->fprint(stderr, "Error: cpu: dial: %r\n"); return; } @@ -151,18 +157,3 @@ getuser(): string return string buf[0:n]; } - -netmkaddr(addr, net, svc: string): string -{ - if(net == nil) - net = "net"; - (n, nil) := sys->tokenize(addr, "!"); - if(n <= 1){ - if(svc== nil) - return sys->sprint("%s!%s", net, addr); - return sys->sprint("%s!%s!%s", net, addr, svc); - } - if(svc == nil || n > 2) - return addr; - return sys->sprint("%s!%s", addr, svc); -} diff --git a/appl/cmd/dial.b b/appl/cmd/dial.b index c562a570..8821b49a 100644 --- a/appl/cmd/dial.b +++ b/appl/cmd/dial.b @@ -1,4 +1,4 @@ -implement Dial; +implement Dialcmd; include "sys.m"; sys: Sys; include "draw.m"; @@ -7,11 +7,12 @@ include "keyring.m"; keyring: Keyring; include "security.m"; auth: Auth; +include "dial.m"; include "sh.m"; sh: Sh; Context: import sh; -Dial: module { +Dialcmd: module { init: fn(nil: ref Draw->Context, argv: list of string); }; @@ -35,6 +36,9 @@ init(drawctxt: ref Draw->Context, argv: list of string) arg := load Arg Arg->PATH; if (arg == nil) badmodule(Arg->PATH); + dial := load Dial Dial->PATH; + if(dial == nil) + badmodule(Dial->PATH); sh = load Sh Sh->PATH; if (sh == nil) badmodule(Sh->PATH); @@ -87,8 +91,8 @@ init(drawctxt: ref Draw->Context, argv: list of string) } } - (ok, c) := sys->dial(addr, nil); - if (ok == -1) { + c := dial->dial(addr, nil); + if (c == nil) { sys->fprint(stderr(), "dial: cannot dial %s:: %r\n", addr); raise "fail:errors"; } diff --git a/appl/cmd/listen.b b/appl/cmd/listen.b index 25869223..5a06892d 100644 --- a/appl/cmd/listen.b +++ b/appl/cmd/listen.b @@ -7,6 +7,8 @@ include "keyring.m"; keyring: Keyring; include "security.m"; auth: Auth; +include "dial.m"; + dial: Dial; include "sh.m"; sh: Sh; Context: import sh; @@ -31,6 +33,9 @@ init(drawctxt: ref Draw->Context, argv: list of string) auth = load Auth Auth->PATH; if (auth == nil) badmodule(Auth->PATH); + dial = load Dial Dial->PATH; + if (dial == nil) + badmodule(Dial->PATH); sh = load Sh Sh->PATH; if (sh == nil) badmodule(Sh->PATH); @@ -120,8 +125,8 @@ listen1(drawctxt: ref Draw->Context, addr: string, argv: list of string, sys->pctl(Sys->FORKFD, nil); ctxt := Context.new(drawctxt); - (ok, acon) := sys->announce(addr); - if (ok == -1) { + acon := dial->announce(addr); + if (acon == nil) { sys->fprint(stderr(), "listen: failed to announce on '%s': %r\n", addr); sync <-= "cannot announce"; exit; @@ -146,15 +151,15 @@ listen1(drawctxt: ref Draw->Context, addr: string, argv: list of string, } sync <-= nil; - listench := chan of (int, Sys->Connection); - authch := chan of (string, Sys->Connection); + listench := chan of ref Dial->Connection; + authch := chan of (string, ref Dial->Connection); spawn listener(listench, acon, addr); for (;;) { user := ""; - ccon: Sys->Connection; + ccon: ref Dial->Connection; alt { - (lok, c) := <-listench => - if (lok == -1){ + c := <-listench => + if (c == nil){ sync <-= "listen"; exit; } @@ -182,13 +187,13 @@ listen1(drawctxt: ref Draw->Context, addr: string, argv: list of string, } } -listener(listench: chan of (int, Sys->Connection), c: Sys->Connection, addr: string) +listener(listench: chan of ref Dial->Connection, c: ref Dial->Connection, addr: string) { for (;;) { - (ok, nc) := sys->listen(c); - if (ok == -1) { + nc := dial->listen(c); + if (nc == nil) { sys->fprint(stderr(), "listen: listen error on '%s': %r\n", addr); - listench <-= (-1, nc); + listench <-= nc; exit; } if (verbose) @@ -200,13 +205,13 @@ listener(listench: chan of (int, Sys->Connection), c: Sys->Connection, addr: str else{ if(nc.cfd != nil) sys->fprint(nc.cfd, "keepalive"); - listench <-= (ok, nc); + listench <-= nc; } } } -authenticator(authch: chan of (string, Sys->Connection), - c: Sys->Connection, algs: list of string, addr: string) +authenticator(authch: chan of (string, ref Dial->Connection), + c: ref Dial->Connection, algs: list of string, addr: string) { err: string; (c.dfd, err) = auth->server(algs, serverkey, c.dfd, 0); diff --git a/appl/cmd/sh/mpexpr.b b/appl/cmd/sh/mpexpr.b index 682c01e8..0aaf6cda 100644 --- a/appl/cmd/sh/mpexpr.b +++ b/appl/cmd/sh/mpexpr.b @@ -3,9 +3,9 @@ implement Shellbuiltin; include "sys.m"; sys: Sys; include "draw.m"; -include "keyring.m"; - keyring: Keyring; - IPint: import keyring; +include "ipints.m"; + ipints: IPints; + IPint: import ipints; include "sh.m"; sh: Sh; Listnode, Context: import sh; @@ -17,7 +17,7 @@ One: Big; initbuiltin(ctxt: ref Context, shmod: Sh): string { sys = load Sys Sys->PATH; - keyring = load Keyring Keyring->PATH; + ipints = load IPints IPints->PATH; sh = shmod; myself = load Shellbuiltin "$self"; if (myself == nil) @@ -233,7 +233,7 @@ oper(ctxt: ref Context, args: list of Big, op, lastop, lastn: int, BITS => r = mki(n1.bits()); EXPMOD => r = n1.expmod(n2, n3); EXP => r = n1.expmod(n2, nil); - RAND => r = IPint.random(0, n1.iptoint()); + RAND => r = IPint.random(n1.iptoint()); INVERT => r = n1.invert(n2); } return r :: stk; diff --git a/appl/cmd/spki/verify.b b/appl/cmd/spki/verify.b index 9eab6b41..daf563ac 100644 --- a/appl/cmd/spki/verify.b +++ b/appl/cmd/spki/verify.b @@ -11,9 +11,11 @@ include "sys.m"; include "draw.m"; -include "keyring.m"; - kr: Keyring; - IPint: import kr; +include "ipints.m"; + ipints: IPints; + IPint: import ipints; + +include "crypt.m"; include "bufio.m"; bufio: Bufio; @@ -44,7 +46,7 @@ debug := 0; init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; - kr = load Keyring Keyring->PATH; + ipints = load IPints IPints->PATH; bufio = load Bufio Bufio->PATH; sexprs = load Sexprs Sexprs->PATH; spki = load SPKI SPKI->PATH; diff --git a/appl/cmd/ssh/authpassword.b b/appl/cmd/ssh/authpassword.b new file mode 100644 index 00000000..1a149117 --- /dev/null +++ b/appl/cmd/ssh/authpassword.b @@ -0,0 +1,68 @@ +implement Auth; + +include "sys.m"; + sys: Sys; + +include "ipints.m"; + ipints: IPints; + IPint: import ipints; + +include "crypt.m"; + crypt: Crypt; # TO DO: needed to avoid compiler error + +include "factotum.m"; + factotum: Factotum; + +include "sshio.m"; + sshio: Sshio; + Conn, Msg: import sshio; + +id(): int +{ + return SSH_AUTH_PASSWORD; +} + +init(mod: Sshio) +{ + sys = load Sys Sys->PATH; + sshio = mod; +} + +firstmsg(): int +{ + return SSH_CMSG_AUTH_PASSWORD; +} + +authsrv(c: ref Conn, m: ref Msg): ref AuthInfo +{ + pass := m.getstring(); +# return auth_userpasswd(c.user, pass); + return ref AuthInfo(c.user, nil); # TO DO: +} + +auth(c: ref Conn): int +{ + if(factotum == nil) + factotum = load Factotum Factotum->PATH; + (user, pass) := factotum->getuserpasswd(sys->sprint("proto=pass service=ssh server=%q user=%q", c.host, c.user)); + if(user == nil){ + sshio->debug(DBG_AUTH, "getuserpasswd failed"); + return -1; + } + + sshio->debug(DBG_AUTH, "try using password from factotum\n"); + m := Msg.mk(SSH_CMSG_AUTH_PASSWORD, 4+Sys->UTFmax*len pass); + m.putstring(pass); + c.out <-= m; + + m = sshio->recvmsg(c, -1); + case m.mtype { + SSH_SMSG_SUCCESS => + return 0; + SSH_SMSG_FAILURE => + return -1; + * => + sshio->badmsg(m, 0, nil); + return -1; + } +} diff --git a/appl/cmd/ssh/authrsa.b b/appl/cmd/ssh/authrsa.b new file mode 100644 index 00000000..427355d5 --- /dev/null +++ b/appl/cmd/ssh/authrsa.b @@ -0,0 +1,188 @@ +implement Auth; + +include "sys.m"; + sys: Sys; + +include "ipints.m"; + ipints: IPints; + IPint: import ipints; + +include "crypt.m"; + crypt: Crypt; + PK, SK: import crypt; + +include "factotum.m"; + factotum: Factotum; + Attr: import factotum; + findattrval: import factotum; + +include "sshio.m"; + sshio: Sshio; + Conn, Msg: import sshio; + debug: import sshio; + +id(): int +{ + return SSH_AUTH_RSA; +} + +init(mod: Sshio) +{ + sshio = mod; + sys = load Sys Sys->PATH; + ipints = load IPints IPints->PATH; + crypt = load Crypt Crypt->PATH; + factotum = load Factotum Factotum->PATH; + factotum->init(); +} + +firstmsg(): int +{ + return SSH_CMSG_AUTH_RSA; +} + +authsrv(c: ref Conn, m: ref Msg): ref AuthInfo +{ + # TO DO: use factotum + hismod := m.getipint(); + if(hismod.bits() < 512){ + debug(DBG_AUTH, sys->sprint("rsa key for %s < 512 bits\n", c.user)); + return nil; + } + hispk := readpk("/keydb/ssh/"+c.user); + if(hispk == nil){ + debug(DBG_AUTH, sys->sprint("no ssh/rsa key for %s: %r\n", c.user)); + return nil; + } + if(!hispk.n.eq(hismod)){ + debug(DBG_AUTH, sys->sprint("%s rsa key doesn't match modulus\n", c.user)); + return nil; + } + # encrypt a challenge with his pk +# chal := IPint.random(256).expmod(IPint.inttoip(1), hismod); + chal := IPint.random(256); + echal := crypt->rsaencrypt(hispk, x := sshio->rsapad(chal, (hispk.n.bits()+7)/8)); +debug(DBG_AUTH, sys->sprint("padded %s\nrsa chal %s\n", x.iptostr(16), echal.iptostr(16))); + m = Msg.mk(SSH_SMSG_AUTH_RSA_CHALLENGE, 2048); + m.putipint(echal); + c.out <-= m; + + m = sshio->recvmsg(c, SSH_CMSG_AUTH_RSA_RESPONSE); + response := m.getbytes(Crypt->MD5dlen); + chalbuf := array[32+SESSIDLEN] of byte; + sshio->iptorjustbe(chal, chalbuf, 32); + debug(DBG_AUTH, sys->sprint("\trjusted %s\n", sshio->hex(chalbuf[0:32]))); + chalbuf[32:] = c.sessid[0: SESSIDLEN]; + debug(DBG_AUTH, sys->sprint("\tappend sessid %s\n", sshio->hex(chalbuf))); + expected := array[Crypt->MD5dlen] of byte; + crypt->md5(chalbuf, 32+SESSIDLEN, expected, nil); + if(sshio->eqbytes(expected, response, len expected)) + return ref AuthInfo(c.user, nil); + return nil; +} + +readpk(file: string): ref PK.RSA +{ + fd := sys->open(file, Sys->OREAD); + if(fd == nil) + return nil; + buf := array[8192] of byte; + nr := sys->readn(fd, buf, len buf); + if(nr < 0) + return nil; + attrs := factotum->parseattrs(string buf[0: nr]); + if(findattrval(attrs, "proto") != "rsa" || + (ns := findattrval(attrs, "n")) == nil || + (eks := findattrval(attrs, "ek")) == nil){ + sys->werrstr("missing rsa key attributes"); + return nil; + } + n := IPint.strtoip(ns, 16); + ek := IPint.strtoip(eks, 16); + if(n == nil || ek == nil){ + sys->werrstr("invalid rsa key values"); + return nil; + } + return ref PK.RSA(n, ek); +} + +auth(c: ref Conn): int +{ + chalbuf := array[32+SESSIDLEN] of byte; + response := array[Crypt->MD5dlen] of byte; + + debug(DBG_AUTH, "authrsa\n"); + + afd := sys->open("/mnt/factotum/rpc", Sys->ORDWR); + if(afd == nil){ + debug(DBG_AUTH, sys->sprint("open /mnt/factotum/rpc: %r\n")); + return -1; + } + s := "proto=rsa role=client"; + if(factotum->rpc(afd, "start", array of byte s).t0 != "ok"){ + debug(DBG_AUTH, sys->sprint("auth_rpc start %s failed: %r\n", s)); + return -1; + } + + debug(DBG_AUTH, "trying factotum rsa keys\n"); + for(;;){ + (tag, value) := factotum->rpc(afd, "read", nil); + if(tag != "ok") + break; + textkey := string value; + sshio->debug(DBG_AUTH, sys->sprint("try %q\n", textkey)); + mod := IPint.strtoip(textkey, 16); + m := Msg.mk(SSH_CMSG_AUTH_RSA, 16+(mod.bits()+7/8)); + m.putipint(mod); + c.out <-= m; + + m = sshio->recvmsg(c, -1); + case m.mtype { + SSH_SMSG_FAILURE => + debug(DBG_AUTH, "\tnot accepted\n"); + continue; + SSH_SMSG_AUTH_RSA_CHALLENGE => + ; + * => + sshio->badmsg(m, 0, nil); + } + chal := m.getipint(); + p := chal.iptostr(16); + debug(DBG_AUTH, sys->sprint("\tgot challenge %s\n", p)); + unpad: ref IPint; + if(factotum->rpc(afd, "write", array of byte p).t0 == "ok" && + ((tag, value) = factotum->rpc(afd, "read", nil)).t0 == "ok"){ + debug(DBG_AUTH, sys->sprint("\tfactotum said %q\n", string value)); + decr := IPint.strtoip(string value, 16); + if(decr != nil){ + debug(DBG_AUTH, sys->sprint("\tdecrypted %s\n", decr.iptostr(16))); + unpad = sshio->rsaunpad(decr); + }else + unpad = IPint.inttoip(0); + }else{ + debug(DBG_AUTH, sys->sprint("\tauth_rpc write or read failed: %r\n")); + unpad = IPint.inttoip(0); # it will fail, we'll go round again + } + debug(DBG_AUTH, sys->sprint("\tunpadded %s\n", unpad.iptostr(16))); + sshio->iptorjustbe(unpad, chalbuf, 32); +# debug(DBG_AUTH, sys->sprint("\trjusted %.*H\n", 32, chalbuf)); + chalbuf[32:] = c.sessid[0: SESSIDLEN]; +# debug(DBG_AUTH, sys->sprint("\tappend sesskey %.*H\n", 32, chalbuf)); + crypt->md5(chalbuf, 32+SESSIDLEN, response, nil); + + m = Msg.mk(SSH_CMSG_AUTH_RSA_RESPONSE, Crypt->MD5dlen); + m.putbytes(response, Crypt->MD5dlen); + c.out <-= m; + + m = sshio->recvmsg(c, -1); + case m.mtype { + SSH_SMSG_FAILURE => + ; # retry + SSH_SMSG_SUCCESS => + return 0; + * => + sshio->badmsg(m, 0, nil); + } + } + return -1; +} diff --git a/appl/cmd/ssh/authtis.b b/appl/cmd/ssh/authtis.b new file mode 100644 index 00000000..0fd5edd6 --- /dev/null +++ b/appl/cmd/ssh/authtis.b @@ -0,0 +1,119 @@ +implement Auth; + +# TO DO: add chal/resp to Factotum + +include "sys.m"; + sys: Sys; + +include "ipints.m"; + ipints: IPints; + IPint: import ipints; + +include "crypt.m"; + crypt: Crypt; # avoid compiler error + +include "factotum.m"; + factotum: Factotum; + Attr: import factotum; + findattrval: import factotum; + +include "sshio.m"; + sshio: Sshio; + Conn, Msg: import sshio; + debug: import sshio; + +id(): int +{ + return SSH_AUTH_TIS; +} + +init(mod: Sshio) +{ + sshio = mod; + sys = load Sys Sys->PATH; + ipints = load IPints IPints->PATH; + factotum = load Factotum Factotum->PATH; + factotum->init(); +} + +firstmsg(): int +{ + return SSH_CMSG_AUTH_TIS; +} + +authsrv(conn: ref Conn, nil: ref Msg): ref AuthInfo +{ + if((c := factotum->challenge(sys->sprint("proto=p9cr user=%q role=server", conn.user))) == nil){ +# sshlog("auth_challenge failed for %s", conn.user); + return nil; + } + s := sys->sprint("Challenge: %s\nResponse: ", c.chal); + m := Msg.mk(SSH_SMSG_AUTH_TIS_CHALLENGE, 4+len s); + m.putstring(s); + conn.out <-= m; + + m = sshio->recvmsg(conn, 0); + if(m == nil) + return nil; + if(m.mtype != SSH_CMSG_AUTH_TIS_RESPONSE){ + # + # apparently you can just give up on + # this protocol and start a new one. + # + sshio->unrecvmsg(conn, m); + return nil; + } + + ai := factotum->response(c, m.getstring()); + if(ai == nil){ + debug(DBG_AUTH, sys->sprint("response rejected: %r\n")); + return nil; + } + return ref AuthInfo(ai.cuid, ai.cap); +} + +auth(c: ref Conn): int +{ + if(!c.interactive) + return -1; + + debug(DBG_AUTH, "try TIS\n"); + c.out <-= Msg.mk(SSH_CMSG_AUTH_TIS, 0); + + m := sshio->recvmsg(c, -1); + case m.mtype { + SSH_SMSG_FAILURE => + return -1; + SSH_SMSG_AUTH_TIS_CHALLENGE => + ; + * => + sshio->badmsg(m, SSH_SMSG_AUTH_TIS_CHALLENGE, nil); + } + + chal := m.getstring(); + + if((fd := sys->open("/dev/cons", Sys->ORDWR)) == nil) + sshio->error(sys->sprint("can't open /dev/cons: %r")); + + sys->fprint(fd, "TIS Authentication\n%s", chal); + resp := array[256] of byte; + n := sys->read(fd, resp, len resp); + if(n <= 0 || resp[0] == byte '\n') + return -1; + + m = Msg.mk(SSH_CMSG_AUTH_TIS_RESPONSE, 4+n); + m.put4(len resp); + m.putbytes(resp, n); + c.out <-= m; + + m = sshio->recvmsg(c, -1); + case m.mtype { + SSH_SMSG_SUCCESS => + return 0; + SSH_SMSG_FAILURE => + return -1; + * => + sshio->badmsg(m, 0, nil); + return -1; + } +} diff --git a/appl/cmd/ssh/cipher3des.b b/appl/cmd/ssh/cipher3des.b new file mode 100644 index 00000000..e6f347f1 --- /dev/null +++ b/appl/cmd/ssh/cipher3des.b @@ -0,0 +1,51 @@ +implement Cipher; + +include "sys.m"; + +include "ipints.m"; + ipints: IPints; + IPint: import ipints; + +include "crypt.m"; + crypt: Crypt; + DESstate: import crypt; + +include "sshio.m"; + +Cipherstate: adt +{ + enc: array of ref DESstate; + dec: array of ref DESstate; +}; + +cs: ref Cipherstate; + +id(): int +{ + return SSH_CIPHER_3DES; +} + +init(key: array of byte, nil: int) +{ + ipints = load IPints IPints->PATH; + crypt = load Crypt Crypt->PATH; + cs = ref Cipherstate(array[3] of ref DESstate, array[3] of ref DESstate); + for(i := 0; i < 3; i++){ + cs.enc[i] = crypt->dessetup(key[i*8:], nil); + cs.dec[i] = crypt->dessetup(key[i*8:], nil); + } +} + +encrypt(buf: array of byte, nbuf: int) +{ + crypt->descbc(cs.enc[0], buf, nbuf, Crypt->Encrypt); + crypt->descbc(cs.enc[1], buf, nbuf, Crypt->Decrypt); + crypt->descbc(cs.enc[2], buf, nbuf, Crypt->Encrypt); +} + +decrypt(buf: array of byte, nbuf: int) +{ + crypt->descbc(cs.dec[2], buf, nbuf, Crypt->Decrypt); + crypt->descbc(cs.dec[1], buf, nbuf, Crypt->Encrypt); + crypt->descbc(cs.dec[0], buf, nbuf, Crypt->Decrypt); +} diff --git a/appl/cmd/ssh/cipherblowfish.b b/appl/cmd/ssh/cipherblowfish.b new file mode 100644 index 00000000..8d3b0c31 --- /dev/null +++ b/appl/cmd/ssh/cipherblowfish.b @@ -0,0 +1,43 @@ +implement Cipher; + +include "sys.m"; + +include "ipints.m"; + ipints: IPints; + IPint: import ipints; + +include "crypt.m"; + crypt: Crypt; + BFstate: import crypt; + +include "sshio.m"; + +Cipherstate: adt +{ + enc: ref BFstate; + dec: ref BFstate; +}; + +cs: ref Cipherstate; + +id(): int +{ + return SSH_CIPHER_BLOWFISH; +} + +init(key: array of byte, nil: int) +{ + ipints = load IPints IPints->PATH; + crypt = load Crypt Crypt->PATH; + cs = ref Cipherstate(crypt->blowfishsetup(key, nil), crypt->blowfishsetup(key, nil)); +} + +encrypt(buf: array of byte, nbuf: int) +{ + crypt->blowfishcbc(cs.enc, buf, nbuf, Crypt->Encrypt); +} + +decrypt(buf: array of byte, nbuf: int) +{ + crypt->blowfishcbc(cs.dec, buf, nbuf, Crypt->Decrypt); +} diff --git a/appl/cmd/ssh/cipherdes.b b/appl/cmd/ssh/cipherdes.b new file mode 100644 index 00000000..7de0a7ca --- /dev/null +++ b/appl/cmd/ssh/cipherdes.b @@ -0,0 +1,43 @@ +implement Cipher; + +include "sys.m"; + +include "ipints.m"; + ipints: IPints; + IPint: import ipints; + +include "crypt.m"; + crypt: Crypt; + DESstate: import crypt; + +include "sshio.m"; + +Cipherstate: adt +{ + enc: ref DESstate; + dec: ref DESstate; +}; + +cs: ref Cipherstate; + +id(): int +{ + return SSH_CIPHER_DES; +} + +init(key: array of byte, nil: int) +{ + ipints = load IPints IPints->PATH; + crypt = load Crypt Crypt->PATH; + cs = ref Cipherstate(crypt->dessetup(key, nil), crypt->dessetup(key, nil)); +} + +encrypt(buf: array of byte, nbuf: int) +{ + crypt->descbc(cs.enc, buf, nbuf, Crypt->Encrypt); +} + +decrypt(buf: array of byte, nbuf: int) +{ + crypt->descbc(cs.dec, buf, nbuf, Crypt->Decrypt); +} diff --git a/appl/cmd/ssh/ciphernone.b b/appl/cmd/ssh/ciphernone.b new file mode 100644 index 00000000..01a7a1f3 --- /dev/null +++ b/appl/cmd/ssh/ciphernone.b @@ -0,0 +1,28 @@ +implement Cipher; + +include "sys.m"; + +include "ipints.m"; + ipints: IPints; + IPint: import ipints; + +include "crypt.m"; + +include "sshio.m"; + +id(): int +{ + return SSH_CIPHER_NONE; +} + +init(nil: array of byte, nil: int) +{ +} + +encrypt(nil: array of byte, nil: int) +{ +} + +decrypt(nil: array of byte, nil: int) +{ +} diff --git a/appl/cmd/ssh/cipherrc4.b b/appl/cmd/ssh/cipherrc4.b new file mode 100644 index 00000000..f43f9c8d --- /dev/null +++ b/appl/cmd/ssh/cipherrc4.b @@ -0,0 +1,46 @@ +implement Cipher; + +include "sys.m"; + +include "ipints.m"; + ipints: IPints; + IPint: import ipints; + +include "crypt.m"; + crypt: Crypt; + RC4state: import crypt; + +include "sshio.m"; + +Cipherstate: adt +{ + enc: ref RC4state; + dec: ref RC4state; +}; + +cs: ref Cipherstate; + +id(): int +{ + return SSH_CIPHER_RC4; +} + +init(key: array of byte, isserver: int) +{ + ipints = load IPints IPints->PATH; + crypt = load Crypt Crypt->PATH; + if(isserver) + cs = ref Cipherstate(crypt->rc4setup(key[0:16]), crypt->rc4setup(key[16:32])); + else + cs = ref Cipherstate(crypt->rc4setup(key[16:32]), crypt->rc4setup(key[0:16])); +} + +encrypt(buf: array of byte, nbuf: int) +{ + crypt->rc4(cs.enc, buf, nbuf); +} + +decrypt(buf: array of byte, nbuf: int) +{ + crypt->rc4(cs.dec, buf, nbuf); +} diff --git a/appl/cmd/ssh/mkfile b/appl/cmd/ssh/mkfile new file mode 100644 index 00000000..afb4c903 --- /dev/null +++ b/appl/cmd/ssh/mkfile @@ -0,0 +1,29 @@ +<../../../mkconfig + +TARG=\ + authpassword.dis\ + authrsa.dis\ + authtis.dis\ + cipher3des.dis\ + cipherblowfish.dis\ + cipherdes.dis\ + ciphernone.dis\ + cipherrc4.dis\ + sshio.dis\ + sshserve.dis\ +# ssh.dis\ + +SYSMODULES=\ + arg.m\ + keyring.m\ + security.m\ + rand.m\ + sys.m\ + draw.m\ + +MODULES=\ + sshio.m\ + +DISBIN=$ROOT/dis/ssh + +<$ROOT/mkfiles/mkdis diff --git a/appl/cmd/ssh/sshio.b b/appl/cmd/ssh/sshio.b new file mode 100644 index 00000000..942b06df --- /dev/null +++ b/appl/cmd/ssh/sshio.b @@ -0,0 +1,586 @@ +implement Sshio; + +include "sys.m"; + sys: Sys; + +include "ipints.m"; + ipints: IPints; + IPint: import ipints; + +include "crypt.m"; + crypt: Crypt; + PK, SK: import crypt; + +include "sshio.m"; + +include "rand.m"; + rand: Rand; + +init() +{ + sys = load Sys Sys->PATH; + ipints = load IPints IPints->PATH; + crypt = load Crypt Crypt->PATH; + rand = load Rand Rand->PATH; + rand->init(sys->millisec()); +} + +msgnames := array[45] of { + "SSH_MSG_NONE", # 0 + "SSH_MSG_DISCONNECT", + "SSH_SMSG_PUBLIC_KEY", + "SSH_CMSG_SESSION_KEY", + "SSH_CMSG_USER", + "SSH_CMSG_AUTH_RHOSTS", + "SSH_CMSG_AUTH_RSA", + "SSH_SMSG_AUTH_RSA_CHALLENGE", + "SSH_CMSG_AUTH_RSA_RESPONSE", + "SSH_CMSG_AUTH_PASSWORD", + "SSH_CMSG_REQUEST_PTY", # 10 + "SSH_CMSG_WINDOW_SIZE", + "SSH_CMSG_EXEC_SHELL", + "SSH_CMSG_EXEC_CMD", + "SSH_SMSG_SUCCESS", + "SSH_SMSG_FAILURE", + "SSH_CMSG_STDIN_DATA", + "SSH_SMSG_STDOUT_DATA", + "SSH_SMSG_STDERR_DATA", + "SSH_CMSG_EOF", + "SSH_SMSG_EXITSTATUS", # 20 + "SSH_MSG_CHANNEL_OPEN_CONFIRMATION", + "SSH_MSG_CHANNEL_OPEN_FAILURE", + "SSH_MSG_CHANNEL_DATA", + "SSH_MSG_CHANNEL_INPUT_EOF", + "SSH_MSG_CHANNEL_OUTPUT_CLOSED", + "SSH_MSG_UNIX_DOMAIN_X11_FORWARDING (obsolete)", + "SSH_SMSG_X11_OPEN", + "SSH_CMSG_PORT_FORWARD_REQUEST", + "SSH_MSG_PORT_OPEN", + "SSH_CMSG_AGENT_REQUEST_FORWARDING", # 30 + "SSH_SMSG_AGENT_OPEN", + "SSH_MSG_IGNORE", + "SSH_CMSG_EXIT_CONFIRMATION", + "SSH_CMSG_X11_REQUEST_FORWARDING", + "SSH_CMSG_AUTH_RHOSTS_RSA", + "SSH_MSG_DEBUG", + "SSH_CMSG_REQUEST_COMPRESSION", + "SSH_CMSG_MAX_PACKET_SIZE", + "SSH_CMSG_AUTH_TIS", + "SSH_SMSG_AUTH_TIS_CHALLENGE", # 40 + "SSH_CMSG_AUTH_TIS_RESPONSE", + "SSH_CMSG_AUTH_KERBEROS", + "SSH_SMSG_AUTH_KERBEROS_RESPONSE", + "SSH_CMSG_HAVE_KERBEROS_TGT", +}; + +Conn.mk(host: string, fd: ref Sys->FD): ref Conn +{ + c := ref Conn; + c.host = host; + c.sesskey = array[SESSKEYLEN] of byte; + c.sessid = array[SESSIDLEN] of byte; + c.in = chan of (ref Msg, string); + c.out = chan of ref Msg; + c.flags = 0; + c.interactive = 0; + sync := chan of int; + spawn msgreader(c, fd, sync); + <-sync; + spawn msgwriter(c, fd, sync); + <-sync; + return c; +} + +Conn.setkey(c: self ref Conn, key: ref PK.RSA) +{ + c.hostkey = key; +} + +msgreader(c: ref Conn, fd: ref Sys->FD, sync: chan of int) +{ + sys->pctl(Sys->NEWFD, 2 :: fd.fd :: nil); + sync <-= 1; + fd = sys->fildes(fd.fd); + for(;;){ + m := readmsg(c, fd); + if(m == nil){ + c.in <-= (nil, sys->sprint("%r")); + break; + } + debug(DBG_PROTO, sys->sprint("<-[%d] %s\n", m.ep-m.rp, m.fulltext())); + case m.mtype { + SSH_MSG_IGNORE => + ; + SSH_MSG_DEBUG => + debug(DBG_PROTO, sys->sprint("remote DEBUG: %s\n", m.getstring())); + * => + c.in <-= (m, nil); + } + } +} + +msgwriter(c: ref Conn, fd: ref Sys->FD, sync: chan of int) +{ + sys->pctl(Sys->NEWFD, 2 :: fd.fd :: nil); + sync <-= 1; + fd = sys->fildes(fd.fd); + while((m := <-c.out) != nil) + if(writemsg(c, m, fd) < 0){ + while(<-c.out != nil) + {} # flush + exit; + } +} + +# +# read initial SSH-m.n-comment line +# +readversion(fd: ref Sys->FD): (int, int, string) +{ + buf := array[128] of byte; + if((n := readstrnl(fd, buf, len buf)) < 0) + return (-1, -1, sys->sprint("error reading version: %r")); + # id string is "SSH-m.n-comment". We need m=1, n>=5. + s := string buf[0: n]; + (nf, fld) := sys->tokenize(s, "-\r\n"); + if(nf < 3 || hd fld != "SSH") + return (-1, -1, sys->sprint("unexpected protocol reply: %s", s)); + (nf, fld) = sys->tokenize(hd tl fld, "."); + if(nf < 2) + return (-1, -1, "invalid SSH version string in "+s); + return (1, int hd tl fld, s); +} + +calcsessid(hostmod: ref IPint, servermod: ref IPint, cookie: array of byte): array of byte +{ + b1 := hostmod.iptobebytes(); + b2 := servermod.iptobebytes(); + buf := array[len b1+len b2+COOKIELEN] of byte; + buf[0:] = b1; + buf[len b1:] = b2; + buf[len b1+len b2:] = cookie[0: COOKIELEN]; + sessid := array[Crypt->MD5dlen] of byte; + crypt->md5(buf, len buf, sessid, nil); + return sessid; +} + +Msg.text(m: self ref Msg): string +{ + if(0 <= m.mtype && m.mtype < len msgnames) + return msgnames[m.mtype]; + return sys->sprint("<unknown type %d>", m.mtype); +} + +Msg.fulltext(m: self ref Msg): string +{ + s := m.text(); + n := m.ep; + if(n > 64) + n = 64; + for(i := 0; i < n; i++) + s += sys->sprint(" %.2ux", int m.data[i]); + if(n != m.ep) + s += " ..."; + return s; +} + +badmsg(m: ref Msg, want: int, errmsg: string) +{ + if(m == nil) + s := sys->sprint("<early eof: %s>", errmsg); + else + s = m.text(); + if(want) + error(sys->sprint("got %s message expecting %s", s, msgnames[want])); + error(sys->sprint("got unexpected %s message", s)); +} + +Msg.mk(mtype: int, length: int): ref Msg +{ + if(length > 256*1024) + raise "message too large"; + return ref Msg(mtype, array[4+8+1+length+4] of byte, 0, 0, length); +} + +# used by auth tis +unrecvmsg(c: ref Conn, m: ref Msg) +{ + debug(DBG_PROTO, sys->sprint("unreceived %s len %d\n", msgnames[m.mtype], m.ep-m.rp)); + c.unget = m; +} + +readmsg(c: ref Conn, fd: ref Sys->FD): ref Msg +{ + if(c.unget != nil){ # TO DO: assumes state of processes ensures exclusive access + m := c.unget; + c.unget = nil; + return m; + } + buf := array[4] of byte; + if((n := sys->readn(fd, buf, len buf)) != len buf){ + if(n < 0) + sys->werrstr("short net read: %r"); + else + sys->werrstr("short net read"); + return nil; + } + length := get4(buf, 0); + if(length < 5 || length > 256*1024){ + sys->werrstr(sys->sprint("implausible packet length: %.8ux", length)); + return nil; + } + pad := 8-length%8; + m := ref Msg(0, array[pad+length] of byte, pad, 0, pad+length-4); + if(sys->readn(fd, m.data, len m.data) != len m.data){ + sys->werrstr(sys->sprint("short net read: %r")); + return nil; + } + if(c.cipher != nil) + c.cipher->decrypt(m.data, length+pad); + crc := sum32(0, m.data, m.ep); + crc0 := get4(m.data, m.ep); + if(crc != crc0){ + sys->werrstr(sys->sprint("bad crc %#ux != %#ux (packet length %ud)", crc, crc0, length)); + return nil; + } + m.mtype = int m.data[m.rp++]; + return m; +} + +recvmsg(c: ref Conn, mtype: int): ref Msg +{ + (m, errmsg) := <-c.in; + if(mtype == 0){ + # no checking + }else if(mtype == -1){ + # must not be nil + if(m == nil) + error(Ehangup); + }else if(m == nil || m.mtype != mtype) # must be given type + badmsg(m, mtype, errmsg); + return m; +} + +writemsg(c: ref Conn, m: ref Msg, fd: ref Sys->FD): int +{ + datalen := m.wp; + length := datalen+1+4; # will add type and crc + pad := 8-length%8; + debug(DBG_PROTO, sys->sprint("->[%d] %s\n", datalen, m.fulltext())); + m.data[4+pad+1:] = m.data[0: datalen]; # slide data to correct position (is this guaranteed?) TO DO + put4(m.data, 0, length); + p := 4; + if(c.cipher != nil) + for(i := 0; i < pad; i++) + m.data[p++] = byte fastrand(); + else{ + for(i = 0; i < pad; i++) + m.data[p++] = byte 0; + } + m.data[p++] = byte m.mtype; + # data already in position + p += datalen; + crc := sum32(0, m.data[4:], pad+1+datalen); + put4(m.data, p, crc); + p += 4; + if(c.cipher != nil) + c.cipher->encrypt(m.data[4:], length+pad); + if(sys->write(fd, m.data, p) != p) + return -1; + return 0; +} + +Msg.get1(m: self ref Msg): int +{ + if(m.rp >= m.ep) + raise Edecode; + return int m.data[m.rp++]; +} + +Msg.get2(m: self ref Msg): int +{ + if(m.rp+2 > m.ep) + raise Edecode; + x := (int m.data[m.rp+0]<<8) | int m.data[m.rp+1]; + m.rp += 2; + return x; +} + +Msg.get4(m: self ref Msg): int +{ + if(m.rp+4 > m.ep) + raise Edecode; + x := int m.data[m.rp+0]<<24|int m.data[m.rp+1]<<16|int m.data[m.rp+2]<<8|int m.data[m.rp+3]; + m.rp += 4; + return x; +} + +Msg.getarray(m: self ref Msg): array of byte +{ + length := m.get4(); + if(m.rp+length > m.ep) + raise Edecode; + p := m.data[m.rp: m.rp+length]; + m.rp += length; + return p; +} + +Msg.getstring(m: self ref Msg): string +{ + return string m.getarray(); +} + +Msg.getbytes(m: self ref Msg, n: int): array of byte +{ + if(m.rp+n > m.ep) + raise Edecode; + p := m.data[m.rp: m.rp+n]; + m.rp += n; + return p; +} + +Msg.getipint(m: self ref Msg): ref IPint +{ + n := (m.get2()+7)/8; # get2 returns # bits + return IPint.bebytestoip(m.getbytes(n)); +} + +Msg.getpk(m: self ref Msg): ref PK.RSA +{ + m.get4(); + ek := m.getipint(); + n := m.getipint(); + return ref PK.RSA(n, ek); +} + +Msg.put1(m: self ref Msg, x: int) +{ + if(m.wp >= m.ep) + raise Eencode; + m.data[m.wp++] = byte x; +} + +Msg.put2(m: self ref Msg, x: int) +{ + if(m.wp+2 > m.ep) + raise Eencode; + (m.data[m.wp+0], m.data[m.wp+1]) = (byte (x>>8), byte x); + m.wp += 2; +} + +Msg.put4(m: self ref Msg, x: int) +{ + if(m.wp+4 > m.ep) + raise Eencode; + (m.data[m.wp+0], m.data[m.wp+1], m.data[m.wp+2], m.data[m.wp+3]) = (byte (x>>24), byte (x>>16), byte (x>>8), byte x); + m.wp += 4; +} + +Msg.putstring(m: self ref Msg, s: string) +{ + b := array of byte s; + m.put4(len b); + m.putbytes(b, len b); +} + +Msg.putbytes(m: self ref Msg, a: array of byte, n: int) +{ + if(m.wp+n > m.ep) + raise Eencode; + m.data[m.wp:] = a[0: n]; + m.wp += n; +} + +Msg.putipint(m: self ref Msg, b: ref IPint) +{ + bits := b.bits(); + m.put2(bits); +# n := (bits+7)/8; + ba := b.iptobebytes(); + n := len ba; + if(m.wp+n > m.ep) + raise Eencode; + m.data[m.wp:] = ba; + m.wp += n; +} + +Msg.putpk(m: self ref Msg, key: ref PK.RSA) +{ + m.put4(key.n.bits()); + m.putipint(key.ek); + m.putipint(key.n); +} + +crctab := array[256] of int; + +initsum32() +{ + poly := int 16redb88320; + for(i := 0; i < 256; i++){ + crc := i; + for(j := 0; j < 8; j++) + if(crc&1) + crc = ((crc>>1) & int ~16r80000000)^poly; # need unsigned shift + else + crc = (crc>>1) & int ~16r80000000; + crctab[i] = crc; + } +} + +first_38: int = 1; + +sum32(lcrc: int, buf: array of byte, n: int): int +{ + crc := lcrc; + if(first_38){ + first_38 = 0; + initsum32(); + } + s := 0; + while(n-- > 0) + crc = crctab[(crc^int buf[s++])&16rff]^((crc>>8)&int ~16rFF000000); + return crc; +} + +erase(b: array of byte) +{ + for(i := 0; i < len b; i++) + b[i] = byte 0; +} + +# +# PKCS#1 padding +# +rsapad(b: ref IPint, n: int): ref IPint +{ + a := b.iptobebytes(); + pad := n - len a - 3; + if(pad < 0) + error("value too large to pad"); # can't happen if keys are required size + buf := array[n] of byte; + buf[0] = byte 0; + buf[1] = byte 2; + for(i := 2; --pad >= 0; i++) + buf[i] = byte (1+fastrand()%255); + buf[i++] = byte 0; + buf[i:] = a; + c := IPint.bebytestoip(buf); + erase(buf); + erase(a); + return c; +} + +rsaunpad(b: ref IPint): ref IPint +{ + buf := b.iptobebytes(); + i := 0; + if(buf[0] == byte 0) + i++; + if(buf[i] != byte 2) + error("bad data in rsaunpad"); + for(; i < len buf; i++) + if(buf[i] == byte 0) + break; + c := IPint.bebytestoip(buf[i:]); + erase(buf); + return c; +} + +rsaencryptbuf(key: ref PK.RSA, buf: array of byte, nbuf: int): ref IPint +{ + n := (key.n.bits()+7)/8; + a := IPint.bebytestoip(buf[0: nbuf]); + b := rsapad(a, n); + return crypt->rsaencrypt(key, b); +} + +iptorjustbe(b: ref IPint, buf: array of byte, length: int) +{ + a := b.iptobebytes(); + if(len a < length){ + length -= len a; + erase(buf[0: length]); + buf[length:] = a; + }else + buf[0:] = a[0: length]; + erase(a); +} + +hex(a: array of byte): string +{ + s := ""; + for(i := 0; i < len a; i++) + s += sys->sprint("%.2ux", int a[i]); + return s; +} + +debug(n: int, s: string) +{ + sys->fprint(sys->fildes(2), "debug: %s", s); +} + +error(s: string) +{ + sys->fprint(sys->fildes(2), "error: %s\n", s); + raise "error"; +} + +rsagen(bits: int): ref SK.RSA +{ + return crypt->rsagen(bits, 6, 0); +} + +rsaencrypt(key: ref PK.RSA, b: ref IPint): ref IPint +{ + return crypt->rsaencrypt(key, b); +} + +rsadecrypt(key: ref SK.RSA, b: ref IPint): ref IPint +{ + return crypt->rsadecrypt(key, b); +} + +fastrand(): int +{ + return int rand->bigrand(4294967295); +} + +readstrnl(fd: ref Sys->FD, buf: array of byte, nbuf: int): int +{ + for(i := 0; i < nbuf; i++) + case sys->read(fd, buf[i:], 1) { + -1 => + return -1; + 0 => + sys->werrstr("unexpected EOF"); + return -1; + * => + if(buf[i] == byte '\n') + return i; + } + sys->werrstr("line too long"); + return -1; +} + +eqbytes(a: array of byte, b: array of byte, n: int): int +{ + if(len a > n || len b > n) + return 0; + for(i := 0; i < n; i++) + if(a[i] != b[i]) + return 0; + return 1; +} + +get4(a: array of byte, o: int): int +{ + return int a[o+0]<<24 | int a[o+1]<<16 | int a[o+2]<<8 | int a[o+3]; +} + +put4(a: array of byte, o: int, v: int) +{ + a[o+0] = byte (v>>24); + a[o+1] = byte (v>>16); + a[o+2] = byte (v>>8); + a[o+3] = byte v; +} diff --git a/appl/cmd/ssh/sshio.m b/appl/cmd/ssh/sshio.m new file mode 100644 index 00000000..6c186e63 --- /dev/null +++ b/appl/cmd/ssh/sshio.m @@ -0,0 +1,194 @@ + + # internal debugging flags + DBG, DBG_CRYPTO, DBG_PACKET, DBG_AUTH, DBG_PROC, DBG_PROTO, DBG_IO, DBG_SCP: con 1<<iota; + + # protocol packet types + SSH_MSG_NONE, # 0 + SSH_MSG_DISCONNECT, + SSH_SMSG_PUBLIC_KEY, + SSH_CMSG_SESSION_KEY, + SSH_CMSG_USER, + SSH_CMSG_AUTH_RHOSTS, + SSH_CMSG_AUTH_RSA, + SSH_SMSG_AUTH_RSA_CHALLENGE, + SSH_CMSG_AUTH_RSA_RESPONSE, + SSH_CMSG_AUTH_PASSWORD, # 10 + SSH_CMSG_REQUEST_PTY, + SSH_CMSG_WINDOW_SIZE, + SSH_CMSG_EXEC_SHELL, + SSH_CMSG_EXEC_CMD, + SSH_SMSG_SUCCESS, + SSH_SMSG_FAILURE, + SSH_CMSG_STDIN_DATA, + SSH_SMSG_STDOUT_DATA, + SSH_SMSG_STDERR_DATA, + SSH_CMSG_EOF, # 20 + SSH_SMSG_EXITSTATUS, + SSH_MSG_CHANNEL_OPEN_CONFIRMATION, + SSH_MSG_CHANNEL_OPEN_FAILURE, + SSH_MSG_CHANNEL_DATA, + SSH_MSG_CHANNEL_INPUT_EOF, + SSH_MSG_CHANNEL_OUTPUT_CLOSED, + SSH_MSG_UNIX_DOMAIN_X11_FORWARDING, # obsolete + SSH_SMSG_X11_OPEN, + SSH_CMSG_PORT_FORWARD_REQUEST, + SSH_MSG_PORT_OPEN, # 30 + SSH_CMSG_AGENT_REQUEST_FORWARDING, + SSH_SMSG_AGENT_OPEN, + SSH_MSG_IGNORE, + SSH_CMSG_EXIT_CONFIRMATION, + SSH_CMSG_X11_REQUEST_FORWARDING, + SSH_CMSG_AUTH_RHOSTS_RSA, + SSH_MSG_DEBUG, + SSH_CMSG_REQUEST_COMPRESSION, + SSH_CMSG_MAX_PACKET_SIZE, + SSH_CMSG_AUTH_TIS, # 40 + SSH_SMSG_AUTH_TIS_CHALLENGE, + SSH_CMSG_AUTH_TIS_RESPONSE, + SSH_CMSG_AUTH_KERBEROS, + SSH_SMSG_AUTH_KERBEROS_RESPONSE, + SSH_CMSG_HAVE_KERBEROS_TGT: con iota; + + SSH_MSG_ERROR: con -1; + + # protocol flags + SSH_PROTOFLAG_SCREEN_NUMBER: con 1<<0; + SSH_PROTOFLAG_HOST_IN_FWD_OPEN: con 1<<1; + + # agent protocol packet types + SSH_AGENTC_NONE, + SSH_AGENTC_REQUEST_RSA_IDENTITIES, + SSH_AGENT_RSA_IDENTITIES_ANSWER, + SSH_AGENTC_RSA_CHALLENGE, + SSH_AGENT_RSA_RESPONSE, + SSH_AGENT_FAILURE, + SSH_AGENT_SUCCESS, + SSH_AGENTC_ADD_RSA_IDENTITY, + SSH_AGENTC_REMOVE_RSA_IDENTITY: con iota; + + # protocol constants + SSH_MAX_DATA: con 256*1024; + SSH_MAX_MSG: con SSH_MAX_DATA+4; + SESSKEYLEN: con 32; + SESSIDLEN: con 16; + COOKIELEN: con 8; + + # crypto ids + SSH_CIPHER_NONE, + SSH_CIPHER_IDEA, + SSH_CIPHER_DES, + SSH_CIPHER_3DES, + SSH_CIPHER_TSS, + SSH_CIPHER_RC4, + SSH_CIPHER_BLOWFISH: con iota; + + # auth method ids + SSH_AUTH_RHOSTS, + SSH_AUTH_RSA, + SSH_AUTH_PASSWORD, + SSH_AUTH_RHOSTS_RSA, + SSH_AUTH_TIS, + SSH_AUTH_USER_RSA: con 1+iota; + +Edecode: con "error decoding input packet"; +Eencode: con "out of space encoding output packet (BUG)"; +Ehangup: con "hungup connection"; +Ememory: con "out of memory"; + +Cipher: module +{ + id: fn(): int; + init: fn(key: array of byte, isserver: int); + encrypt: fn(a: array of byte, n: int); + decrypt: fn(a: array of byte, n: int); +}; + +Auth: module +{ + AuthInfo: adt{ + user: string; + cap: string; + }; + + id: fn(): int; + firstmsg: fn(): int; + init: fn(nil: Sshio); + authsrv: fn(nil: ref Sshio->Conn, nil: ref Sshio->Msg): ref AuthInfo; + auth: fn(nil: ref Sshio->Conn): int; +}; + +Sshio: module +{ + PATH: con "sshio.dis"; + + Conn: adt{ + in: chan of (ref Msg, string); + out: chan of ref Msg; + + sessid: array of byte; + sesskey: array of byte; + hostkey: ref Crypt->PK.RSA; + flags: int; + cipher: Cipher; # chosen cipher + user: string; + host: string; + interactive: int; + unget: ref Msg; + + mk: fn(host: string, fd: ref Sys->FD): ref Conn; + setkey: fn(c: self ref Conn, key: ref Crypt->PK.RSA); + }; + + Msg: adt{ + mtype: int; + data: array of byte; + rp: int; # read pointer + wp: int; # write pointer + ep: int; # byte just beyond message data + + mk: fn(mtype: int, length: int): ref Msg; + text: fn(m: self ref Msg): string; + fulltext: fn(m: self ref Msg): string; + + get1: fn(m: self ref Msg): int; + get2: fn(m: self ref Msg): int; + get4: fn(m: self ref Msg): int; + getstring: fn(m: self ref Msg): string; + getbytes: fn(m: self ref Msg, n: int): array of byte; + getarray: fn(m: self ref Msg): array of byte; + getipint: fn(m: self ref Msg): ref IPints->IPint; + getpk: fn(m: self ref Msg): ref Crypt->PK.RSA; + + put1: fn(m: self ref Msg, nil: int); + put2: fn(m: self ref Msg, nil: int); + put4: fn(m: self ref Msg, nil: int); + putstring: fn(m: self ref Msg, s: string); + putbytes: fn(m: self ref Msg, a: array of byte, n: int); + putipint: fn(m: self ref Msg, mp: ref IPints->IPint); + putpk: fn(m: self ref Msg, pk: ref Crypt->PK.RSA); + }; + + init: fn(); + + badmsg: fn(nil: ref Msg, nil: int, err: string); + recvmsg: fn(nil: ref Conn, nil: int): ref Msg; + unrecvmsg: fn(nil: ref Conn, nil: ref Msg); + rsapad: fn(nil: ref IPints->IPint, nil: int): ref IPints->IPint; + rsaunpad: fn(nil: ref IPints->IPint): ref IPints->IPint; + iptorjustbe: fn(nil: ref IPints->IPint, nil: array of byte, nil: int); + rsaencryptbuf: fn(nil: ref Crypt->PK.RSA, nil: array of byte, nil: int): ref IPints->IPint; + rsagen: fn(nbits: int): ref Crypt->SK.RSA; + rsaencrypt: fn(key: ref Crypt->PK.RSA, b: ref IPints->IPint): ref IPints->IPint; + rsadecrypt: fn(key: ref Crypt->SK.RSA, b: ref IPints->IPint): ref IPints->IPint; + + debug: fn(nil: int, nil: string); + error: fn(nil: string); + readstrnl: fn(fd: ref Sys->FD, buf: array of byte, nbytes: int): int; + calcsessid: fn(hostmod: ref IPints->IPint, servermod: ref IPints->IPint, cookie: array of byte): array of byte; +# sshlog: fn(nil: array of byte); # TBA was ... + + fastrand: fn(): int; + eqbytes: fn(a: array of byte, b: array of byte, n: int): int; + readversion: fn(fd: ref Sys->FD): (int, int, string); + hex: fn(a: array of byte): string; +}; diff --git a/appl/cmd/ssh/sshserve.b b/appl/cmd/ssh/sshserve.b new file mode 100644 index 00000000..dc8bbd31 --- /dev/null +++ b/appl/cmd/ssh/sshserve.b @@ -0,0 +1,495 @@ +implement Sshserve; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "ipints.m"; + ipints: IPints; + IPint: import ipints; + +include "crypt.m"; + crypt: Crypt; + PK, SK: import crypt; + +include "env.m"; + env: Env; + +include "sh.m"; + sh: Sh; + +include "wait.m"; + wait: Wait; + +include "arg.m"; + +include "sshio.m"; + sshio: Sshio; + Conn, Msg: import sshio; + recvmsg: import sshio; + error, debug: import sshio; + +Sshserve: module +{ + init: fn(nil: ref Draw->Context, argl: list of string); +}; + +AuthRpc: adt {}; +debuglevel := 0; + +cipherlist := "blowfish rc4 3des"; +ciphers: list of Cipher; + +authlist := "rsa password tis"; +authsrvs: list of Auth; + +maxmsg := 256*1024; + +serverpriv: ref SK.RSA; +serverkey: ref PK.RSA; +hostpriv: ref SK.RSA; + +init(nil: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + ipints = load IPints IPints->PATH; + crypt = load Crypt Crypt->PATH; + env = load Env Env->PATH; + sh = load Sh Sh->PATH; + sshio = load Sshio Sshio->PATH; + sshio->init(); + wait = load Wait Wait->PATH; + wait->init(); +# fmtinstall('B', mpfmt); +# fmtinstall('H', encodefmt); + sys->pctl(Sys->NEWPGRP|Sys->FORKFD|Sys->FORKNS|Sys->FORKENV, nil); + keyfile: string; + arg := load Arg Arg->PATH; + arg->setusage("sshserve [-A authlist] [-c cipherlist] [-k keyfile] client-ip-address"); + arg->init(args); + while((o := arg->opt()) != 0){ + case o { + 'D' => + debuglevel = int arg->earg(); + 'A' => + authlist = arg->earg(); + 'c' => + cipherlist = arg->earg(); + 'k' => + keyfile = arg->earg(); + * => + arg->usage(); + } + } + args = arg->argv(); + if(len args != 1) + arg->usage(); + arg = nil; + + sys->dup(2, 1); +# if(keyfile != nil) +# ; # read hostpriv from file +# sshlog("connect from %s", c.host); + authsrvs = loadlist("auth", authlist, authload); + ciphers = loadlist("cipher", cipherlist, cipherload); + hostpriv = crypt->rsagen(1024, 6, 0); + serverpriv = crypt->rsagen(768, 6, 0); + serverkey = serverpriv.pk; + { + versioning(sys->fildes(0)); + c := Conn.mk(hd args, sys->fildes(0)); + c.setkey(hostpriv.pk); + authenticate(c); + comms(c); + }exception e{ + "fail:*" => + raise e; + "error*" => + notegrp(sys->pctl(0, nil), "error"); + raise "fail:"+e; + } +} + +authload(f: string): Auth +{ + return load Auth f; +} + +cipherload(f: string): Cipher +{ + return load Cipher f; +} + +loadlist[T](sort: string, set: string, loadf: ref fn(f: string): T): list of T +{ + l: list of T; + (nil, fld) := sys->tokenize(set, " \t,"); + for(; fld != nil; fld = tl fld){ + f := "/dis/ssh/"+sort+hd fld+".dis"; + m := loadf(f); + if(m == nil) + error(sys->sprint("unknown %s scheme %s (%s)", sort, hd fld, f)); + l = m :: l; + } + return l; +} + +comms(c: ref Conn) +{ + (kidpid, infd, waiting) := prelude(c); +Work: + for(;;)alt{ + (m, nil) := <-c.in => + if(m == nil){ + notegrp(kidpid, "hungup"); + exit; + } + case m.mtype { + * => + sshio->badmsg(m, 0, nil); + SSH_MSG_DISCONNECT => + notegrp(kidpid, "hungup"); + sysfatal("client disconnected"); + SSH_CMSG_STDIN_DATA => + if(infd != nil){ + n := m.get4(); + sys->write(infd, m.getbytes(n), n); + } + SSH_CMSG_EOF => + infd = nil; + SSH_CMSG_EXIT_CONFIRMATION => + # sent by some clients as dying breath + notegrp(kidpid, "hungup"); + break Work; + SSH_CMSG_WINDOW_SIZE => + ; # we don't care + } + (pid, nil, status) := <-waiting => + if(pid == kidpid){ + if(status != "" && status != "0"){ + m := Msg.mk(SSH_MSG_DISCONNECT, 4+Sys->UTFmax*len status); + m.putstring(status); + sendmsg(c, m); + }else{ + m := Msg.mk(SSH_SMSG_EXITSTATUS, 4); + m.put4(0); + sendmsg(c, m); + } + sendmsg(c, nil); + break Work; + } + } + notegrp(sys->pctl(0, nil), "done"); +} + +prelude(c: ref Conn): (int, ref Sys->FD, chan of (int, string, string)) +{ + for(;;){ + m := recvmsg(c, -1); + if(m == nil) + return (-1, nil, nil); + case m.mtype { + * => + sendmsg(c, Msg.mk(SSH_SMSG_FAILURE, 0)); + SSH_MSG_DISCONNECT => + sysfatal("client disconnected"); + SSH_CMSG_REQUEST_PTY => + sendmsg(c, Msg.mk(SSH_SMSG_SUCCESS, 0)); + SSH_CMSG_MAX_PACKET_SIZE => + n := m.get4(); + if(n >= 32 && n <= SSH_MAX_MSG){ + maxmsg = n; + sendmsg(c, Msg.mk(SSH_SMSG_SUCCESS, 0)); + }else + sendmsg(c, Msg.mk(SSH_SMSG_FAILURE, 0)); + SSH_CMSG_EXEC_SHELL => + return startcmd(c, nil); + SSH_CMSG_EXEC_CMD => + cmd := m.getstring(); + return startcmd(c, cmd); + } + } +} + +copyout(c: ref Conn, fd: ref Sys->FD, mtype: int) +{ + buf := array[8192] of byte; + max := len buf; + if(max > maxmsg-32) # 32 is an overestimate of packet overhead + max = maxmsg-32; + if(max <= 0) + sysfatal("maximum message size too small"); + while((n := sys->read(fd, buf, max)) > 0){ + m := Msg.mk(mtype, 4+n); + m.put4(n); + m.putbytes(buf, n); + sendmsg(c, m); + } +} + +send_ssh_smsg_public_key(c: ref Conn, cookie: array of byte) +{ + m := Msg.mk(SSH_SMSG_PUBLIC_KEY, 2048); + m.putbytes(cookie, COOKIELEN); + m.putpk(serverkey); + m.putpk(c.hostkey); + m.put4(c.flags); + ciphermask := 0; + for(l1 := ciphers; l1 != nil; l1 = tl l1) + ciphermask |= 1<<(hd l1)->id(); + m.put4(ciphermask); + authmask := 0; + for(l2 := authsrvs; l2 != nil; l2 = tl l2) + authmask |= 1<<(hd l2)->id(); + m.put4(authmask); + sendmsg(c, m); +} + +rpcdecrypt(rpc: ref AuthRpc, b: ref IPint): ref IPint +{ + raise "rpcdecrypt"; +# p := array of byte b.iptostr(16); +# if(auth_rpc(rpc, "write", p, len p) != ARok) +# sysfatal("factotum rsa write: %r"); +# if(auth_rpc(rpc, "read", nil, 0) != ARok) +# sysfatal("factotum rsa read: %r"); +# return strtomp(rpc.arg, nil, 16, nil); +} + +recv_ssh_cmsg_session_key(c: ref Conn, rpc: ref AuthRpc, cookie: array of byte) +{ + m := recvmsg(c, SSH_CMSG_SESSION_KEY); + id := m.get1(); + c.cipher = nil; + for(l := ciphers; l != nil; l = tl l) + if((hd l)->id() == id){ + c.cipher = hd l; + break; + } + if(c.cipher == nil) + sysfatal(sys->sprint("invalid cipher %d selected", id)); + if(!sshio->eqbytes(m.getbytes(COOKIELEN), cookie, len cookie)) + sysfatal("bad cookie"); + serverkeylen := serverkey.n.bits(); + hostkeylen := c.hostkey.n.bits(); + ksmall, kbig: ref SK.RSA; + if(serverkeylen+128 <= hostkeylen){ + ksmall = serverpriv; + kbig = nil; + }else if(hostkeylen+128 <= serverkeylen){ + ksmall = nil; + kbig = serverpriv; + }else + sysfatal("server session and host keys do not differ by at least 128 bits"); + b := m.getipint(); + debug(DBG_CRYPTO, sys->sprint("encrypted with kbig is %s\n", b.iptostr(16))); + if(kbig != nil) + b = sshio->rsadecrypt(kbig, b); + else +# b = rpcdecrypt(rpc, b); + b = sshio->rsadecrypt(hostpriv, b); + b = sshio->rsaunpad(b); + sshio->debug(DBG_CRYPTO, sys->sprint("encrypted with ksmall is %s\n", b.iptostr(16))); + if(ksmall != nil) + b = sshio->rsadecrypt(ksmall, b); + else +# b = rpcdecrypt(rpc, b); + b = sshio->rsadecrypt(hostpriv, b); + b = sshio->rsaunpad(b); + debug(DBG_CRYPTO, sys->sprint("munged is %s\n", b.iptostr(16))); + n := (b.bits()+7)/8; + if(n < SESSKEYLEN) + sysfatal("client sent short session key"); + buf := array[SESSKEYLEN] of byte; + sshio->iptorjustbe(b, buf, SESSKEYLEN); + for(i := 0; i < SESSIDLEN; i++) + buf[i] ^= c.sessid[i]; + c.sesskey[0: ] = buf[0: SESSKEYLEN]; + debug(DBG_CRYPTO, sys->sprint("unmunged is %.*s\n", SESSKEYLEN*2, sshio->hex(buf))); + c.flags = m.get4(); +} + +authsrvuser(c: ref Conn) +{ + m := recvmsg(c, SSH_CMSG_USER); + user := m.getstring(); + c.user = user; + inited := 0; + ai: ref Auth->AuthInfo; + while(authsrvs != nil && ai == nil){ +# # +# # * clumsy: if the client aborted the auth_tis early +# # * we don't send a new failure. we check this by +# # * looking at c->unget, which is only used in that +# # * case. +# # + if(c.unget == nil) + sendmsg(c, Msg.mk(SSH_SMSG_FAILURE, 0)); + m = recvmsg(c, -1); + for(l := authsrvs; l != nil; l = tl l) + if((hd l)->firstmsg() == m.mtype){ + bit := 1 << (hd l)->id(); + if((inited & bit) == 0){ + (hd l)->init(sshio); + inited |= bit; + } + ai = (hd l)->authsrv(c, m); + break; + } + if(l == nil) + sshio->badmsg(m, 0, nil); + } + sendmsg(c, Msg.mk(SSH_SMSG_SUCCESS, 0)); +# if(noworld(ai.cuid)) +# ns := "/lib/namespace.noworld"; +# else +# ns = nil; +# if(auth_chuid(ai, ns) < 0){ +# sshlog("auth_chuid to %s: %r", ai.cuid); +# sysfatal("auth_chuid: %r"); +# } +# sshlog("logged in as %q", ai.user); + if(ai != nil) + sys->print("logged in as %q\n", ai.user); +} + +keyjunk() +{ + p: array of byte; + m: ref IPint; + rpc: ref AuthRpc; + key: ref PK.RSA; + +# # +# # BUG: should use `attr' to get the key attributes +# # after the read, but that's not implemented yet. +# # +# if((b = Bopen("/mnt/factotum/ctl", OREAD)) == nil) +# sysfatal("open /mnt/factotum/ctl: %r"); +# while((p = Brdline(b, '\n')) != nil){ +# if(strstr(p, " proto=rsa ") != nil && strstr(p, " service=sshserve ") != nil) +# break; +# } +# if(p == nil) +# sysfatal("no sshserve keys found in /mnt/factotum/ctl"); +# a = _parseattr(p); +# Bterm(b); +# key = rsaprivalloc(); +# if((p = _strfindattr(a, "n")) == nil) +# sysfatal("no n in sshserve key"); +# if((key.n = IPint.strtoip(p, 16)) == nil) +# sysfatal("bad n in sshserve key"); +# if((p = _strfindattr(a, "ek")) == nil) +# sysfatal("no ek in sshserve key"); +# if((key.ek = IPint.strtoip(p, 16)) == nil) +# sysfatal("bad ek in sshserve key"); +# _freeattr(a); +# if((afd = sys->open("/mnt/factotum/rpc", ORDWR)) == nil) +# sysfatal("open /mnt/factotum/rpc: %r"); +# if((rpc = auth_allocrpc(afd)) == nil) +# sysfatal("auth_allocrpc: %r"); +# p = "proto=rsa role=client service=sshserve"; +# if(auth_rpc(rpc, "start", p, len p) != ARok) +# sysfatal("auth_rpc start %s: %r", p); +# if(auth_rpc(rpc, "read", nil, 0) != ARok) +# sysfatal("auth_rpc read: %r"); +# m = strtomp(rpc.arg, nil, 16, nil); +# if(mpcmp(m, key.n) != 0) +# sysfatal("key in /mnt/factotum/ctl does not match rpc key"); +# mpfree(m); +# c.hostkey = key; +} + +versioning(fd: ref Sys->FD) +{ + sys->fprint(fd, "SSH-1.5-Inferno\n"); + (maj, min, err_or_id) := sshio->readversion(fd); + if(maj < 0) + sysfatal(err_or_id); + if(maj != 1 || min < 5) + sysfatal(sys->sprint("protocol mismatch; got %s, need SSH-1.x for x >= 5", err_or_id)); +} + +authenticate(c: ref Conn) +{ + rpc: ref AuthRpc; + + cookie := array[COOKIELEN] of {* => byte sshio->fastrand()}; + c.sessid = sshio->calcsessid(c.hostkey.n, serverkey.n, cookie); + send_ssh_smsg_public_key(c, cookie); + recv_ssh_cmsg_session_key(c, rpc, cookie); +# afd = nil; + c.cipher->init(c.sesskey, 1); # turns on encryption + sendmsg(c, Msg.mk(SSH_SMSG_SUCCESS, 0)); + authsrvuser(c); +} + +startcmd(c: ref Conn, cmd: string): (int, ref Sys->FD, chan of (int, string, string)) +{ + pfd := array[3] of {* => array[2] of ref Sys->FD}; + for(i := 0; i < 3; i++) + if(sys->pipe(pfd[i]) < 0) + sysfatal(sys->sprint("pipe: %r")); + wfd := sys->open("#p/"+string sys->pctl(0, nil)+"/wait", Sys->OREAD); + if(wfd == nil) + sysfatal(sys->sprint("open wait: %r")); + pidc := chan of int; + spawn startcmd1(c, cmd, pfd, pidc); + kidpid := <-pidc; + (nil, waited) := wait->monitor(wfd); + spawn copyout(c, pfd[1][0], SSH_SMSG_STDOUT_DATA); + pfd[1][0] = nil; + spawn copyout(c, pfd[2][0], SSH_SMSG_STDERR_DATA); + pfd[2][0] = nil; + return (kidpid, pfd[0][0], waited); +} + +startcmd1(c: ref Conn, cmd: string, pfd: array of array of ref Sys->FD, pidc: chan of int) +{ + sysname := env->getenv("sysname"); + tz := env->getenv("timezone"); + sys->pctl(Sys->FORKFD, nil); + for(i := 0; i < len pfd; i++) + if(sys->dup(pfd[i][1].fd, i) < 0) + sysfatal(sys->sprint("dup: %r")); + pfd = nil; + sys->pctl(Sys->NEWPGRP|Sys->FORKNS|Sys->FORKENV|Sys->NEWFD, 0::1::2::nil); + pidc <-= sys->pctl(0, nil); + env->setenv("user", c.user); + if(sysname != nil) + env->setenv("sysname", sysname); + if(tz != nil) + env->setenv("tz", tz); + if(sys->chdir("/usr/"+c.user) < 0) + sys->chdir("/"); + if(cmd != nil){ + env->setenv("service", "rx"); + status := sh->run(nil, list of {"/dis/sh.dis", "-lc", cmd}); + if(status != nil) + raise "fail:"+status; + }else{ + env->setenv("service", "con"); + #execl("/bin/ip/telnetd", "telnetd", "-tn", nil); # TO DO: just for echo and line editing + sys->fprint(sys->fildes(2), "sshserve: cannot run /dis/ip/telnetd: %r"); + } +} + +sysfatal(s: string) +{ + sys->print("sysfatal: %s\n", s); + notegrp(sys->pctl(0, nil), "zap"); + exit; +} + +notegrp(pid: int, nil: string) +{ + fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "killgrp"); +} + +sendmsg(c: ref Conn, m: ref Msg) +{ + c.out <-= m; +} diff --git a/appl/cmd/styxlisten.b b/appl/cmd/styxlisten.b index df136e47..4c8bcc94 100644 --- a/appl/cmd/styxlisten.b +++ b/appl/cmd/styxlisten.b @@ -1,6 +1,8 @@ implement Styxlisten; include "sys.m"; sys: Sys; +include "dial.m"; + dial: Dial; include "draw.m"; include "keyring.m"; keyring: Keyring; @@ -28,6 +30,9 @@ init(ctxt: ref Draw->Context, argv: list of string) auth = load Auth Auth->PATH; if (auth == nil) badmodule(Auth->PATH); + dial = load Dial Dial->PATH; + if (dial == nil) + badmodule(Dial->PATH); if ((e := auth->init()) != nil) error("auth init failed: " + e); keyring = load Keyring Keyring->PATH; @@ -76,7 +81,7 @@ init(ctxt: ref Draw->Context, argv: list of string) arg = nil; if (doauth && algs == nil) algs = getalgs(); - addr := netmkaddr(hd argv, "tcp", "styx"); + addr := dial->netmkaddr(hd argv, "tcp", "styx"); cmd := tl argv; authinfo: ref Keyring->Authinfo; @@ -88,8 +93,8 @@ init(ctxt: ref Draw->Context, argv: list of string) error(sys->sprint("cannot read %s: %r", keyfile)); } - (ok, c) := sys->announce(addr); - if (ok == -1) + c := dial->announce(addr); + if (c == nil) error(sys->sprint("cannot announce on %s: %r", addr)); if(!trusted){ sys->unmount(nil, "/mnt/keys"); # should do for now @@ -103,17 +108,17 @@ init(ctxt: ref Draw->Context, argv: list of string) spawn listener(c, popen(ctxt, cmd, lsync), authinfo, algs, lsync); } -listener(c: Sys->Connection, mfd: ref Sys->FD, authinfo: ref Keyring->Authinfo, algs: list of string, lsync: chan of int) +listener(c: ref Dial->Connection, mfd: ref Sys->FD, authinfo: ref Keyring->Authinfo, algs: list of string, lsync: chan of int) { lsync <-= sys->pctl(0, nil); for (;;) { - (n, nc) := sys->listen(c); - if (n == -1) + nc := dial->listen(c); + if (nc == nil) error(sys->sprint("listen failed: %r")); if (verbose) sys->fprint(stderr(), "styxlisten: got connection from %s", readfile(nc.dir + "/remote")); - dfd := sys->open(nc.dir + "/data", Sys->ORDWR); + dfd := dial->accept(nc); if (dfd != nil) { if(nc.cfd != nil) sys->fprint(nc.cfd, "keepalive"); @@ -245,18 +250,3 @@ stderr(): ref Sys->FD { return sys->fildes(2); } - -netmkaddr(addr, net, svc: string): string -{ - if(net == nil) - net = "net"; - (n, nil) := sys->tokenize(addr, "!"); - if(n <= 1){ - if(svc== nil) - return sys->sprint("%s!%s", net, addr); - return sys->sprint("%s!%s!%s", net, addr, svc); - } - if(svc == nil || n > 2) - return addr; - return sys->sprint("%s!%s", addr, svc); -} |
