History

x20090330-0957

Charles.Forsyth committed 17 years ago

Commit 31dd3971 parent edb9e0715 files · +2261 −0Browse files

Unified · Split

15 files
  1. CHANGES +2 −0
  2. appl/cmd/ssh/authpassword.b +64 −0
  3. appl/cmd/ssh/authrsa.b +184 −0
  4. appl/cmd/ssh/authtis.b +116 −0
  5. appl/cmd/ssh/cipher3des.b +46 −0
  6. appl/cmd/ssh/cipherblowfish.b +38 −0
  7. appl/cmd/ssh/cipherdes.b +38 −0
  8. appl/cmd/ssh/ciphernone.b +24 −0
  9. appl/cmd/ssh/cipherrc4.b +41 −0
  10. appl/cmd/ssh/cons.b +116 −0
  11. appl/cmd/ssh/mkfile +29 −0
  12. appl/cmd/ssh/shcons.b +296 −0
  13. appl/cmd/ssh/sshio.b +582 −0
  14. appl/cmd/ssh/sshio.m +194 −0
  15. appl/cmd/ssh/sshserve.b +491 −0
CHANGES+2−0
@@ -1,3 +1,5 @@
1+20090330
2+ include appl/cmd/ssh source (won't compile here because it needs the keyring changes)
13 20090325
24 emu/* adopt changes to Chan (Dev* dev pointer instead of int type)
35 20090320
appl/cmd/ssh/authpassword.b+64−0
@@ -0,0 +1,64 @@
1+implement Auth;
2+
3+include "sys.m";
4+ sys: Sys;
5+
6+include "keyring.m";
7+ kr: Keyring; # TO DO: needed to avoid compiler error
8+
9+include "factotum.m";
10+ factotum: Factotum;
11+
12+include "sshio.m";
13+ sshio: Sshio;
14+ Conn, Msg: import sshio;
15+
16+id(): int
17+{
18+ return SSH_AUTH_PASSWORD;
19+}
20+
21+init(mod: Sshio)
22+{
23+ sys = load Sys Sys->PATH;
24+ sshio = mod;
25+}
26+
27+firstmsg(): int
28+{
29+ return SSH_CMSG_AUTH_PASSWORD;
30+}
31+
32+authsrv(c: ref Conn, m: ref Msg): ref AuthInfo
33+{
34+ pass := m.getstring();
35+# return auth_userpasswd(c.user, pass);
36+ return ref AuthInfo(c.user, nil); # TO DO:
37+}
38+
39+auth(c: ref Conn): int
40+{
41+ if(factotum == nil)
42+ factotum = load Factotum Factotum->PATH;
43+ (user, pass) := factotum->getuserpasswd(sys->sprint("proto=pass service=ssh server=%q user=%q", c.host, c.user));
44+ if(user == nil){
45+ sshio->debug(DBG_AUTH, "getuserpasswd failed");
46+ return -1;
47+ }
48+
49+ sshio->debug(DBG_AUTH, "try using password from factotum\n");
50+ m := Msg.mk(SSH_CMSG_AUTH_PASSWORD, 4+Sys->UTFmax*len pass);
51+ m.putstring(pass);
52+ c.out <-= m;
53+
54+ m = sshio->recvmsg(c, -1);
55+ case m.mtype {
56+ SSH_SMSG_SUCCESS =>
57+ return 0;
58+ SSH_SMSG_FAILURE =>
59+ return -1;
60+ * =>
61+ sshio->badmsg(m, 0, nil);
62+ return -1;
63+ }
64+}
appl/cmd/ssh/authrsa.b+184−0
@@ -0,0 +1,184 @@
1+implement Auth;
2+
3+include "sys.m";
4+ sys: Sys;
5+
6+include "keyring.m";
7+ kr: Keyring;
8+ IPint: import kr;
9+ PK, SK: import kr;
10+
11+include "factotum.m";
12+ factotum: Factotum;
13+ Attr: import factotum;
14+ findattrval: import factotum;
15+
16+include "sshio.m";
17+ sshio: Sshio;
18+ Conn, Msg: import sshio;
19+ debug: import sshio;
20+
21+id(): int
22+{
23+ return SSH_AUTH_RSA;
24+}
25+
26+init(mod: Sshio)
27+{
28+ sshio = mod;
29+ sys = load Sys Sys->PATH;
30+ kr = load Keyring Keyring->PATH;
31+ factotum = load Factotum Factotum->PATH;
32+ factotum->init();
33+}
34+
35+firstmsg(): int
36+{
37+ return SSH_CMSG_AUTH_RSA;
38+}
39+
40+authsrv(c: ref Conn, m: ref Msg): ref AuthInfo
41+{
42+ # TO DO: use factotum
43+ hismod := m.getipint();
44+ if(hismod.bits() < 512){
45+ debug(DBG_AUTH, sys->sprint("rsa key for %s < 512 bits\n", c.user));
46+ return nil;
47+ }
48+ hispk := readpk("/keydb/ssh/"+c.user);
49+ if(hispk == nil){
50+ debug(DBG_AUTH, sys->sprint("no ssh/rsa key for %s: %r\n", c.user));
51+ return nil;
52+ }
53+ if(!hispk.n.eq(hismod)){
54+ debug(DBG_AUTH, sys->sprint("%s rsa key doesn't match modulus\n", c.user));
55+ return nil;
56+ }
57+ # encrypt a challenge with his pk
58+# chal := IPint.random(256).expmod(IPint.inttoip(1), hismod);
59+ chal := IPint.random(256);
60+ echal := kr->rsaencrypt(hispk, x := sshio->rsapad(chal, (hispk.n.bits()+7)/8));
61+debug(DBG_AUTH, sys->sprint("padded %s\nrsa chal %s\n", x.iptostr(16), echal.iptostr(16)));
62+ m = Msg.mk(SSH_SMSG_AUTH_RSA_CHALLENGE, 2048);
63+ m.putipint(echal);
64+ c.out <-= m;
65+
66+ m = sshio->recvmsg(c, SSH_CMSG_AUTH_RSA_RESPONSE);
67+ response := m.getbytes(Keyring->MD5dlen);
68+ chalbuf := array[32+SESSIDLEN] of byte;
69+ sshio->iptorjustbe(chal, chalbuf, 32);
70+ debug(DBG_AUTH, sys->sprint("\trjusted %s\n", sshio->hex(chalbuf[0:32])));
71+ chalbuf[32:] = c.sessid[0: SESSIDLEN];
72+ debug(DBG_AUTH, sys->sprint("\tappend sessid %s\n", sshio->hex(chalbuf)));
73+ expected := array[Keyring->MD5dlen] of byte;
74+ kr->md5(chalbuf, 32+SESSIDLEN, expected, nil);
75+ if(sshio->eqbytes(expected, response, len expected))
76+ return ref AuthInfo(c.user, nil);
77+ return nil;
78+}
79+
80+readpk(file: string): ref PK.RSA
81+{
82+ fd := sys->open(file, Sys->OREAD);
83+ if(fd == nil)
84+ return nil;
85+ buf := array[8192] of byte;
86+ nr := sys->readn(fd, buf, len buf);
87+ if(nr < 0)
88+ return nil;
89+ attrs := factotum->parseattrs(string buf[0: nr]);
90+ if(findattrval(attrs, "proto") != "rsa" ||
91+ (ns := findattrval(attrs, "n")) == nil ||
92+ (eks := findattrval(attrs, "ek")) == nil){
93+ sys->werrstr("missing rsa key attributes");
94+ return nil;
95+ }
96+ n := IPint.strtoip(ns, 16);
97+ ek := IPint.strtoip(eks, 16);
98+ if(n == nil || ek == nil){
99+ sys->werrstr("invalid rsa key values");
100+ return nil;
101+ }
102+ return ref PK.RSA(n, ek);
103+}
104+
105+auth(c: ref Conn): int
106+{
107+ chalbuf := array[32+SESSIDLEN] of byte;
108+ response := array[Keyring->MD5dlen] of byte;
109+
110+ debug(DBG_AUTH, "authrsa\n");
111+
112+ afd := sys->open("/mnt/factotum/rpc", Sys->ORDWR);
113+ if(afd == nil){
114+ debug(DBG_AUTH, sys->sprint("open /mnt/factotum/rpc: %r\n"));
115+ return -1;
116+ }
117+ s := "proto=rsa role=client";
118+ if(factotum->rpc(afd, "start", array of byte s).t0 != "ok"){
119+ debug(DBG_AUTH, sys->sprint("auth_rpc start %s failed: %r\n", s));
120+ return -1;
121+ }
122+
123+ debug(DBG_AUTH, "trying factotum rsa keys\n");
124+ for(;;){
125+ (tag, value) := factotum->rpc(afd, "read", nil);
126+ if(tag != "ok")
127+ break;
128+ textkey := string value;
129+ sshio->debug(DBG_AUTH, sys->sprint("try %q\n", textkey));
130+ mod := IPint.strtoip(textkey, 16);
131+ m := Msg.mk(SSH_CMSG_AUTH_RSA, 16+(mod.bits()+7/8));
132+ m.putipint(mod);
133+ c.out <-= m;
134+
135+ m = sshio->recvmsg(c, -1);
136+ case m.mtype {
137+ SSH_SMSG_FAILURE =>
138+ debug(DBG_AUTH, "\tnot accepted\n");
139+ continue;
140+ SSH_SMSG_AUTH_RSA_CHALLENGE =>
141+ ;
142+ * =>
143+ sshio->badmsg(m, 0, nil);
144+ }
145+ chal := m.getipint();
146+ p := chal.iptostr(16);
147+ debug(DBG_AUTH, sys->sprint("\tgot challenge %s\n", p));
148+ unpad: ref IPint;
149+ if(factotum->rpc(afd, "write", array of byte p).t0 == "ok" &&
150+ ((tag, value) = factotum->rpc(afd, "read", nil)).t0 == "ok"){
151+ debug(DBG_AUTH, sys->sprint("\tfactotum said %q\n", string value));
152+ decr := IPint.strtoip(string value, 16);
153+ if(decr != nil){
154+ debug(DBG_AUTH, sys->sprint("\tdecrypted %s\n", decr.iptostr(16)));
155+ unpad = sshio->rsaunpad(decr);
156+ }else
157+ unpad = IPint.inttoip(0);
158+ }else{
159+ debug(DBG_AUTH, sys->sprint("\tauth_rpc write or read failed: %r\n"));
160+ unpad = IPint.inttoip(0); # it will fail, we'll go round again
161+ }
162+ debug(DBG_AUTH, sys->sprint("\tunpadded %s\n", unpad.iptostr(16)));
163+ sshio->iptorjustbe(unpad, chalbuf, 32);
164+# debug(DBG_AUTH, sys->sprint("\trjusted %.*H\n", 32, chalbuf));
165+ chalbuf[32:] = c.sessid[0: SESSIDLEN];
166+# debug(DBG_AUTH, sys->sprint("\tappend sesskey %.*H\n", 32, chalbuf));
167+ kr->md5(chalbuf, 32+SESSIDLEN, response, nil);
168+
169+ m = Msg.mk(SSH_CMSG_AUTH_RSA_RESPONSE, Keyring->MD5dlen);
170+ m.putbytes(response, Keyring->MD5dlen);
171+ c.out <-= m;
172+
173+ m = sshio->recvmsg(c, -1);
174+ case m.mtype {
175+ SSH_SMSG_FAILURE =>
176+ ; # retry
177+ SSH_SMSG_SUCCESS =>
178+ return 0;
179+ * =>
180+ sshio->badmsg(m, 0, nil);
181+ }
182+ }
183+ return -1;
184+}
appl/cmd/ssh/authtis.b+116−0
@@ -0,0 +1,116 @@
1+implement Auth;
2+
3+# TO DO: add chal/resp to Factotum
4+
5+include "sys.m";
6+ sys: Sys;
7+
8+include "keyring.m";
9+ kr: Keyring;
10+ IPint: import kr;
11+
12+include "factotum.m";
13+ factotum: Factotum;
14+ Attr: import factotum;
15+ findattrval: import factotum;
16+
17+include "sshio.m";
18+ sshio: Sshio;
19+ Conn, Msg: import sshio;
20+ debug: import sshio;
21+
22+id(): int
23+{
24+ return SSH_AUTH_TIS;
25+}
26+
27+init(mod: Sshio)
28+{
29+ sshio = mod;
30+ sys = load Sys Sys->PATH;
31+ kr = load Keyring Keyring->PATH;
32+ factotum = load Factotum Factotum->PATH;
33+ factotum->init();
34+}
35+
36+firstmsg(): int
37+{
38+ return SSH_CMSG_AUTH_TIS;
39+}
40+
41+authsrv(conn: ref Conn, nil: ref Msg): ref AuthInfo
42+{
43+ if((c := factotum->challenge(sys->sprint("proto=p9cr user=%q role=server", conn.user))) == nil){
44+# sshlog("auth_challenge failed for %s", conn.user);
45+ return nil;
46+ }
47+ s := sys->sprint("Challenge: %s\nResponse: ", c.chal);
48+ m := Msg.mk(SSH_SMSG_AUTH_TIS_CHALLENGE, 4+len s);
49+ m.putstring(s);
50+ conn.out <-= m;
51+
52+ m = sshio->recvmsg(conn, 0);
53+ if(m == nil)
54+ return nil;
55+ if(m.mtype != SSH_CMSG_AUTH_TIS_RESPONSE){
56+ #
57+ # apparently you can just give up on
58+ # this protocol and start a new one.
59+ #
60+ sshio->unrecvmsg(conn, m);
61+ return nil;
62+ }
63+
64+ ai := factotum->response(c, m.getstring());
65+ if(ai == nil){
66+ debug(DBG_AUTH, sys->sprint("response rejected: %r\n"));
67+ return nil;
68+ }
69+ return ref AuthInfo(ai.cuid, ai.cap);
70+}
71+
72+auth(c: ref Conn): int
73+{
74+ if(!c.interactive)
75+ return -1;
76+
77+ debug(DBG_AUTH, "try TIS\n");
78+ c.out <-= Msg.mk(SSH_CMSG_AUTH_TIS, 0);
79+
80+ m := sshio->recvmsg(c, -1);
81+ case m.mtype {
82+ SSH_SMSG_FAILURE =>
83+ return -1;
84+ SSH_SMSG_AUTH_TIS_CHALLENGE =>
85+ ;
86+ * =>
87+ sshio->badmsg(m, SSH_SMSG_AUTH_TIS_CHALLENGE, nil);
88+ }
89+
90+ chal := m.getstring();
91+
92+ if((fd := sys->open("/dev/cons", Sys->ORDWR)) == nil)
93+ sshio->error(sys->sprint("can't open /dev/cons: %r"));
94+
95+ sys->fprint(fd, "TIS Authentication\n%s", chal);
96+ resp := array[256] of byte;
97+ n := sys->read(fd, resp, len resp);
98+ if(n <= 0 || resp[0] == byte '\n')
99+ return -1;
100+
101+ m = Msg.mk(SSH_CMSG_AUTH_TIS_RESPONSE, 4+n);
102+ m.put4(len resp);
103+ m.putbytes(resp, n);
104+ c.out <-= m;
105+
106+ m = sshio->recvmsg(c, -1);
107+ case m.mtype {
108+ SSH_SMSG_SUCCESS =>
109+ return 0;
110+ SSH_SMSG_FAILURE =>
111+ return -1;
112+ * =>
113+ sshio->badmsg(m, 0, nil);
114+ return -1;
115+ }
116+}
appl/cmd/ssh/cipher3des.b+46−0
@@ -0,0 +1,46 @@
1+implement Cipher;
2+
3+include "sys.m";
4+
5+include "keyring.m";
6+ kr: Keyring;
7+ DESstate: import kr;
8+
9+include "sshio.m";
10+
11+Cipherstate: adt
12+{
13+ enc: array of ref DESstate;
14+ dec: array of ref DESstate;
15+};
16+
17+cs: ref Cipherstate;
18+
19+id(): int
20+{
21+ return SSH_CIPHER_3DES;
22+}
23+
24+init(key: array of byte, nil: int)
25+{
26+ kr = load Keyring Keyring->PATH;
27+ cs = ref Cipherstate(array[3] of ref DESstate, array[3] of ref DESstate);
28+ for(i := 0; i < 3; i++){
29+ cs.enc[i] = kr->dessetup(key[i*8:], nil);
30+ cs.dec[i] = kr->dessetup(key[i*8:], nil);
31+ }
32+}
33+
34+encrypt(buf: array of byte, nbuf: int)
35+{
36+ kr->descbc(cs.enc[0], buf, nbuf, Keyring->Encrypt);
37+ kr->descbc(cs.enc[1], buf, nbuf, Keyring->Decrypt);
38+ kr->descbc(cs.enc[2], buf, nbuf, Keyring->Encrypt);
39+}
40+
41+decrypt(buf: array of byte, nbuf: int)
42+{
43+ kr->descbc(cs.dec[2], buf, nbuf, Keyring->Decrypt);
44+ kr->descbc(cs.dec[1], buf, nbuf, Keyring->Encrypt);
45+ kr->descbc(cs.dec[0], buf, nbuf, Keyring->Decrypt);
46+}
appl/cmd/ssh/cipherblowfish.b+38−0
@@ -0,0 +1,38 @@
1+implement Cipher;
2+
3+include "sys.m";
4+
5+include "keyring.m";
6+ kr: Keyring;
7+ BFstate: import kr;
8+
9+include "sshio.m";
10+
11+Cipherstate: adt
12+{
13+ enc: ref BFstate;
14+ dec: ref BFstate;
15+};
16+
17+cs: ref Cipherstate;
18+
19+id(): int
20+{
21+ return SSH_CIPHER_BLOWFISH;
22+}
23+
24+init(key: array of byte, nil: int)
25+{
26+ kr = load Keyring Keyring->PATH;
27+ cs = ref Cipherstate(kr->blowfishsetup(key, nil), kr->blowfishsetup(key, nil));
28+}
29+
30+encrypt(buf: array of byte, nbuf: int)
31+{
32+ kr->blowfishcbc(cs.enc, buf, nbuf, Keyring->Encrypt);
33+}
34+
35+decrypt(buf: array of byte, nbuf: int)
36+{
37+ kr->blowfishcbc(cs.dec, buf, nbuf, Keyring->Decrypt);
38+}
appl/cmd/ssh/cipherdes.b+38−0
@@ -0,0 +1,38 @@
1+implement Cipher;
2+
3+include "sys.m";
4+
5+include "keyring.m";
6+ kr: Keyring;
7+ DESstate: import kr;
8+
9+include "sshio.m";
10+
11+Cipherstate: adt
12+{
13+ enc: ref DESstate;
14+ dec: ref DESstate;
15+};
16+
17+cs: ref Cipherstate;
18+
19+id(): int
20+{
21+ return SSH_CIPHER_DES;
22+}
23+
24+init(key: array of byte, nil: int)
25+{
26+ kr = load Keyring Keyring->PATH;
27+ cs = ref Cipherstate(kr->dessetup(key, nil), kr->dessetup(key, nil));
28+}
29+
30+encrypt(buf: array of byte, nbuf: int)
31+{
32+ kr->descbc(cs.enc, buf, nbuf, Keyring->Encrypt);
33+}
34+
35+decrypt(buf: array of byte, nbuf: int)
36+{
37+ kr->descbc(cs.dec, buf, nbuf, Keyring->Decrypt);
38+}
appl/cmd/ssh/ciphernone.b+24−0
@@ -0,0 +1,24 @@
1+implement Cipher;
2+
3+include "sys.m";
4+
5+include "keyring.m";
6+
7+include "sshio.m";
8+
9+id(): int
10+{
11+ return SSH_CIPHER_NONE;
12+}
13+
14+init(nil: array of byte, nil: int)
15+{
16+}
17+
18+encrypt(nil: array of byte, nil: int)
19+{
20+}
21+
22+decrypt(nil: array of byte, nil: int)
23+{
24+}
appl/cmd/ssh/cipherrc4.b+41−0
@@ -0,0 +1,41 @@
1+implement Cipher;
2+
3+include "sys.m";
4+
5+include "keyring.m";
6+ kr: Keyring;
7+ RC4state: import kr;
8+
9+include "sshio.m";
10+
11+Cipherstate: adt
12+{
13+ enc: ref RC4state;
14+ dec: ref RC4state;
15+};
16+
17+cs: ref Cipherstate;
18+
19+id(): int
20+{
21+ return SSH_CIPHER_RC4;
22+}
23+
24+init(key: array of byte, isserver: int)
25+{
26+ kr = load Keyring Keyring->PATH;
27+ if(isserver)
28+ cs = ref Cipherstate(kr->rc4setup(key[0:16]), kr->rc4setup(key[16:32]));
29+ else
30+ cs = ref Cipherstate(kr->rc4setup(key[16:32]), kr->rc4setup(key[0:16]));
31+}
32+
33+encrypt(buf: array of byte, nbuf: int)
34+{
35+ kr->rc4(cs.enc, buf, nbuf);
36+}
37+
38+decrypt(buf: array of byte, nbuf: int)
39+{
40+ kr->rc4(cs.dec, buf, nbuf);
41+}
appl/cmd/ssh/cons.b+116−0
@@ -0,0 +1,116 @@
1+# promptstring
2+
3+RAWON_STR := "*";
4+
5+RAWON : con 0;
6+RAWOFF : con 1;
7+
8+promptstring(prompt, def: string, mode: int): string
9+{
10+ if(mode == RAWON || def == nil)
11+ sys->fprint(stdout, "%s: ", prompt);
12+ else
13+ sys->fprint(stdout, "%s [%s]: ", prompt, def);
14+ (eof, resp) := readline(stdin, mode);
15+ if(eof)
16+ exit;
17+ if(resp == nil)
18+ return def;
19+ return resp;
20+}
21+
22+readline(fd: ref Sys->FD, mode: int): (int, string)
23+{
24+ i: int;
25+ eof: int;
26+ fdctl: ref Sys->FD;
27+
28+ eof = 0;
29+ buf := array[128] of byte;
30+ tmp := array[128] of byte;
31+
32+ if(mode == RAWON){
33+ fdctl = sys->open("/dev/consctl", sys->OWRITE);
34+ if(fdctl == nil || sys->write(fdctl,array of byte "rawon",5) != 5){
35+ sys->fprint(stderr, "unable to change console mode");
36+ return (1,nil);
37+ }
38+ }
39+
40+ for(sofar := 0; sofar < 128; sofar += i){
41+ i = sys->read(fd, tmp, 128 - sofar);
42+ if(i <= 0){
43+ eof = 1;
44+ break;
45+ }
46+ if(tmp[i-1] == byte '\n'){
47+ for(j := 0; j < i-1; j++){
48+ buf[sofar+j] = tmp[j];
49+ if(mode == RAWON && RAWON_STR != nil)
50+ sys->write(stdout,array of byte RAWON_STR,1);
51+ }
52+ sofar += j;
53+ if(mode == RAWON)
54+ sys->write(stdout,array of byte "\n",1);
55+ break;
56+ }
57+ else {
58+ for(j := 0; j < i; j++){
59+ buf[sofar+j] = tmp[j];
60+ if(mode == RAWON && RAWON_STR != nil)
61+ sys->write(stdout,array of byte RAWON_STR,1);
62+ }
63+ }
64+ }
65+ if(mode == RAWON)
66+ sys->write(fdctl,array of byte "rawoff",6);
67+ return (eof, string buf[0:sofar]);
68+}
69+
70+# from keyfs
71+
72+readconsline(prompt: string, raw: int): (string, string)
73+{
74+ fd := sys->open("/dev/cons", Sys->ORDWR);
75+ if(fd == nil)
76+ return (nil, sys->sprint("can't open cons: %r"));
77+ sys->fprint(fd, "%s", prompt);
78+ fdctl: ref Sys->FD;
79+ if(raw){
80+ fdctl = sys->open("/dev/consctl", sys->OWRITE);
81+ if(fdctl == nil || sys->fprint(fdctl, "rawon") < 0)
82+ return (nil, sys->sprint("can't open consctl: %r"));
83+ }
84+ line := array[256] of byte;
85+ o := 0;
86+ err: string;
87+ buf := array[1] of byte;
88+ Read:
89+ while((r := sys->read(fd, buf, len buf)) > 0){
90+ c := int buf[0];
91+ case c {
92+ 16r7F =>
93+ err = "interrupt";
94+ break Read;
95+ '\b' =>
96+ if(o > 0)
97+ o--;
98+ '\n' or '\r' or 16r4 =>
99+ break Read;
100+ * =>
101+ if(o > len line){
102+ err = "line too long";
103+ break Read;
104+ }
105+ line[o++] = byte c;
106+ }
107+ }
108+ sys->fprint(fd, "\n");
109+ if(r < 0)
110+ err = sys->sprint("can't read cons: %r");
111+ if(raw)
112+ sys->fprint(fdctl, "rawoff");
113+ if(err != nil)
114+ return (nil, err);
115+ return (string line[0:o], err);
116+}
appl/cmd/ssh/mkfile+29−0
@@ -0,0 +1,29 @@
1+<../../../mkconfig
2+
3+TARG=\
4+ authpassword.dis\
5+ authrsa.dis\
6+ authtis.dis\
7+ cipher3des.dis\
8+ cipherblowfish.dis\
9+ cipherdes.dis\
10+ ciphernone.dis\
11+ cipherrc4.dis\
12+ sshio.dis\
13+ sshserve.dis\
14+# ssh.dis\
15+
16+SYSMODULES=\
17+ arg.m\
18+ keyring.m\
19+ security.m\
20+ rand.m\
21+ sys.m\
22+ draw.m\
23+
24+MODULES=\
25+ sshio.m\
26+
27+DISBIN=$ROOT/dis/ssh
28+
29+<$ROOT/mkfiles/mkdis
appl/cmd/ssh/shcons.b+296−0
@@ -0,0 +1,296 @@
1+implement Cons;
2+
3+# possibly useful bits from wm/sh
4+
5+include "sys.m";
6+ sys: Sys;
7+ FileIO: import sys;
8+
9+include "draw.m";
10+
11+include "sh.m";
12+
13+include "string.m";
14+ str: String;
15+
16+include "arg.m";
17+
18+Cons: module
19+{
20+ init: fn(ctxt: ref Draw->Context, args: list of string);
21+};
22+
23+BSW: con 23; # ^w bacspace word
24+BSL: con 21; # ^u backspace line
25+EOT: con 4; # ^d end of file
26+ESC: con 27; # hold mode
27+
28+Rdreq: adt
29+{
30+ off: int;
31+ nbytes: int;
32+ fid: int;
33+ rc: chan of (array of byte, string);
34+};
35+
36+rdreq: list of Rdreq;
37+rawon := 0;
38+rawinput := "";
39+partialread: array of byte;
40+
41+events: list of string;
42+evrdreq: list of Rdreq;
43+
44+init(ctxt: ref Context, args: list of string)
45+{
46+ sys = load Sys Sys->PATH;
47+ str = load String String->PATH;
48+ arg := load Arg Arg->PATH;
49+ arg->init(args);
50+ arg->setusage("aux/cons [-ilxvn] [-c command] [file [args...]]");
51+
52+ sys->pctl(Sys->FORKFD | Sys->FORKNS | Sys->NEWPGRP | Sys->FORKENV, nil);
53+
54+ shargs: list of string;
55+ while ((opt := arg->opt()) != 0) {
56+ case opt {
57+ 'c' =>
58+ shargs = arg->earg() :: "-c" :: shargs;
59+ 'i' or 'l' or 'x' or 'v' or 'n' =>
60+ shargs = sys->sprint("-%c", opt) :: shargs;
61+ * =>
62+ arg->usage();
63+ }
64+ }
65+ args = arg->args();
66+ for (; shargs != nil; shargs = tl shargs)
67+ args = hd shargs :: args;
68+
69+ ioc := chan of (int, ref FileIO, ref FileIO, string);
70+ spawn newsh(ctxt, ioc, args);
71+
72+ (nil, file, filectl, consfile) := <-ioc;
73+ if(file == nil || filectl == nil || shctl == nil) {
74+ sys->fprint(sys->fildes(2), "cons: shell /dev/cons creation failed\n");
75+ return;
76+ }
77+
78+ for(;;) alt {
79+ c := <-keys => # TO DO: input arriving from remote ...; echo, edit ...
80+ char := c[1];
81+ if(char == '\\')
82+ char = c[2];
83+ if(rawon){
84+ rawinput[len rawinput] = char;
85+ sendinput(t);
86+ break;
87+ }
88+ case char {
89+ * =>
90+ cmd(t, ".ft.t insert insert "+c);
91+ '\r' =>
92+ ; # TO DO
93+ '\n' or
94+ EOT =>
95+ cmd(t, ".ft.t insert insert "+c);
96+ sendinput(t);
97+ '\b' =>
98+ cmd(t, ".ft.t tkTextDelIns -c");
99+ 'u'& 8r37 =>
100+ cmd(t, ".ft.t tkTextDelIns -l");
101+ 'w'& 8r37 =>
102+ cmd(t, ".ft.t tkTextDelIns -w");
103+ }
104+
105+ rdrpc := <-filectl.read =>
106+ if(rdrpc.rc != nil)
107+ rdrpc.rc <-= (nil, "permission denied");
108+
109+ (nil, data, nil, wc) := <-filectl.write =>
110+ if(wc == nil) {
111+ # consctl closed - revert to cooked mode
112+ rawon = 0;
113+ continue;
114+ }
115+ (nc, cmdlst) := sys->tokenize(string data, " \n");
116+ if(nc == 1) {
117+ case hd cmdlst {
118+ "rawon" =>
119+ rawon = 1;
120+ rawinput = "";
121+ # discard previous input
122+ advance := string (len tk->cmd(t, ".ft.t get outpoint end") +1);
123+ cmd(t, ".ft.t mark set outpoint outpoint+" + advance + "chars");
124+ partialread = nil;
125+ "rawoff" =>
126+ rawon = 0;
127+ partialread = nil;
128+ "holdon" or "holdoff" =>
129+ ;
130+ * =>
131+ wc <-= (0, "unknown control message");
132+ continue;
133+ }
134+ wc <-= (len data, nil);
135+ continue;
136+ }
137+ wc <-= (0, "unknown control message");
138+
139+ rdrpc := <-file.read =>
140+ if(rdrpc.rc == nil) {
141+ (ok, nil) := sys->stat(consfile);
142+ if (ok < 0)
143+ return;
144+ continue;
145+ }
146+ append(rdrpc);
147+ sendinput(t);
148+
149+ (nil, data, nil, wc) := <-file.write =>
150+ if(wc == nil) {
151+ (ok, nil) := sys->stat(consfile);
152+ if (ok < 0)
153+ return;
154+ continue;
155+ }
156+ # TO DO: data from cons; edit (eg, add \r) and forward to remote
157+ wc <-= (len data, nil);
158+ data = nil;
159+ }
160+}
161+
162+RPCread: type (int, int, int, chan of (array of byte, string));
163+
164+append(r: RPCread)
165+{
166+ t := r :: nil;
167+ while(rdreq != nil) {
168+ t = hd rdreq :: t;
169+ rdreq = tl rdreq;
170+ }
171+ rdreq = t;
172+}
173+
174+sendinput(t: ref Tk->Toplevel)
175+{
176+ input: string;
177+ if(rawon)
178+ input = rawinput;
179+ else
180+ input = tk->cmd(t, ".ft.t get outpoint end");
181+ if(rdreq == nil || (input == nil && len partialread == 0))
182+ return;
183+ r := hd rdreq;
184+ (chars, bytes, partial) := triminput(r.nbytes, input, partialread);
185+ if(bytes == nil)
186+ return; # no terminator yet
187+ rdreq = tl rdreq;
188+
189+ alt {
190+ r.rc <-= (bytes, nil) =>
191+ # check that it really was sent
192+ alt {
193+ r.rc <-= (nil, nil) =>
194+ ;
195+ * =>
196+ return;
197+ }
198+ * =>
199+ return; # requester has disappeared; ignore his request and try another
200+ }
201+ if(rawon)
202+ rawinput = rawinput[chars:];
203+ else
204+ cmd(t, ".ft.t mark set outpoint outpoint+" + string chars + "chars");
205+ partialread = partial;
206+}
207+
208+# read at most nr bytes from the input string, returning the number of characters
209+# consumed, the bytes to be read, and any remaining bytes from a partially
210+# read multibyte UTF character.
211+triminput(nr: int, input: string, partial: array of byte): (int, array of byte, array of byte)
212+{
213+ if(nr <= len partial)
214+ return (0, partial[0:nr], partial[nr:]);
215+ if(holding)
216+ return (0, nil, partial);
217+
218+ # keep the array bounds within sensible limits
219+ if(nr > len input*Sys->UTFmax)
220+ nr = len input*Sys->UTFmax;
221+ buf := array[nr+Sys->UTFmax] of byte;
222+ t := len partial;
223+ buf[0:] = partial;
224+
225+ hold := !rawon;
226+ i := 0;
227+ while(i < len input){
228+ c := input[i++];
229+ # special case for ^D - don't read the actual ^D character
230+ if(!rawon && c == EOT){
231+ hold = 0;
232+ break;
233+ }
234+
235+ t += sys->char2byte(c, buf, t);
236+ if(c == '\n' && !rawon){
237+ hold = 0;
238+ break;
239+ }
240+ if(t >= nr)
241+ break;
242+ }
243+ if(hold){
244+ for(j := i; j < len input; j++){
245+ c := input[j];
246+ if(c == '\n' || c == EOT)
247+ break;
248+ }
249+ if(j == len input)
250+ return (0, nil, partial);
251+ # strip ^D when next read would read it, otherwise
252+ # we'll give premature EOF.
253+ if(i == j && input[i] == EOT)
254+ i++;
255+ }
256+ partial = nil;
257+ if(t > nr){
258+ partial = buf[nr:t];
259+ t = nr;
260+ }
261+ return (i, buf[0:t], partial);
262+}
263+
264+newsh(ctxt: ref Context, ioc: chan of (int, ref FileIO, ref FileIO, string, ref FileIO), args: list of string)
265+{
266+ pid := sys->pctl(sys->NEWFD, nil);
267+
268+ sh := load Command "/dis/sh.dis";
269+ if(sh == nil) {
270+ ioc <-= (0, nil, nil, nil);
271+ return;
272+ }
273+
274+ tty := "cons."+string pid;
275+
276+ sys->bind("#s","/chan",Sys->MBEFORE);
277+ fio := sys->file2chan("/chan", tty);
278+ fioctl := sys->file2chan("/chan", tty + "ctl");
279+ ioc <-= (pid, fio, fioctl, "/chan/"+tty);
280+ if(fio == nil || fioctl == nil)
281+ return;
282+
283+ sys->bind("/chan/"+tty, "/dev/cons", sys->MREPL);
284+ sys->bind("/chan/"+tty+"ctl", "/dev/consctl", sys->MREPL);
285+
286+ fd0 := sys->open("/dev/cons", Sys->OREAD|Sys->ORCLOSE);
287+ fd1 := sys->open("/dev/cons", Sys->OWRITE);
288+ fd2 := sys->open("/dev/cons", Sys->OWRITE);
289+
290+ {
291+ sh->init(ctxt, "sh" :: "-n" :: args);
292+ }exception{
293+ "fail:*" =>
294+ exit;
295+ }
296+}
appl/cmd/ssh/sshio.b+582−0
@@ -0,0 +1,582 @@
1+implement Sshio;
2+
3+include "sys.m";
4+ sys: Sys;
5+
6+include "keyring.m";
7+ kr: Keyring;
8+ IPint: import kr;
9+ PK, SK: import kr;
10+
11+include "sshio.m";
12+
13+include "rand.m";
14+ rand: Rand;
15+
16+init()
17+{
18+ sys = load Sys Sys->PATH;
19+ kr = load Keyring Keyring->PATH;
20+ rand = load Rand Rand->PATH;
21+ rand->init(sys->millisec());
22+}
23+
24+msgnames := array[45] of {
25+ "SSH_MSG_NONE", # 0
26+ "SSH_MSG_DISCONNECT",
27+ "SSH_SMSG_PUBLIC_KEY",
28+ "SSH_CMSG_SESSION_KEY",
29+ "SSH_CMSG_USER",
30+ "SSH_CMSG_AUTH_RHOSTS",
31+ "SSH_CMSG_AUTH_RSA",
32+ "SSH_SMSG_AUTH_RSA_CHALLENGE",
33+ "SSH_CMSG_AUTH_RSA_RESPONSE",
34+ "SSH_CMSG_AUTH_PASSWORD",
35+ "SSH_CMSG_REQUEST_PTY", # 10
36+ "SSH_CMSG_WINDOW_SIZE",
37+ "SSH_CMSG_EXEC_SHELL",
38+ "SSH_CMSG_EXEC_CMD",
39+ "SSH_SMSG_SUCCESS",
40+ "SSH_SMSG_FAILURE",
41+ "SSH_CMSG_STDIN_DATA",
42+ "SSH_SMSG_STDOUT_DATA",
43+ "SSH_SMSG_STDERR_DATA",
44+ "SSH_CMSG_EOF",
45+ "SSH_SMSG_EXITSTATUS", # 20
46+ "SSH_MSG_CHANNEL_OPEN_CONFIRMATION",
47+ "SSH_MSG_CHANNEL_OPEN_FAILURE",
48+ "SSH_MSG_CHANNEL_DATA",
49+ "SSH_MSG_CHANNEL_INPUT_EOF",
50+ "SSH_MSG_CHANNEL_OUTPUT_CLOSED",
51+ "SSH_MSG_UNIX_DOMAIN_X11_FORWARDING (obsolete)",
52+ "SSH_SMSG_X11_OPEN",
53+ "SSH_CMSG_PORT_FORWARD_REQUEST",
54+ "SSH_MSG_PORT_OPEN",
55+ "SSH_CMSG_AGENT_REQUEST_FORWARDING", # 30
56+ "SSH_SMSG_AGENT_OPEN",
57+ "SSH_MSG_IGNORE",
58+ "SSH_CMSG_EXIT_CONFIRMATION",
59+ "SSH_CMSG_X11_REQUEST_FORWARDING",
60+ "SSH_CMSG_AUTH_RHOSTS_RSA",
61+ "SSH_MSG_DEBUG",
62+ "SSH_CMSG_REQUEST_COMPRESSION",
63+ "SSH_CMSG_MAX_PACKET_SIZE",
64+ "SSH_CMSG_AUTH_TIS",
65+ "SSH_SMSG_AUTH_TIS_CHALLENGE", # 40
66+ "SSH_CMSG_AUTH_TIS_RESPONSE",
67+ "SSH_CMSG_AUTH_KERBEROS",
68+ "SSH_SMSG_AUTH_KERBEROS_RESPONSE",
69+ "SSH_CMSG_HAVE_KERBEROS_TGT",
70+};
71+
72+Conn.mk(host: string, fd: ref Sys->FD): ref Conn
73+{
74+ c := ref Conn;
75+ c.host = host;
76+ c.sesskey = array[SESSKEYLEN] of byte;
77+ c.sessid = array[SESSIDLEN] of byte;
78+ c.in = chan of (ref Msg, string);
79+ c.out = chan of ref Msg;
80+ c.flags = 0;
81+ c.interactive = 0;
82+ sync := chan of int;
83+ spawn msgreader(c, fd, sync);
84+ <-sync;
85+ spawn msgwriter(c, fd, sync);
86+ <-sync;
87+ return c;
88+}
89+
90+Conn.setkey(c: self ref Conn, key: ref PK.RSA)
91+{
92+ c.hostkey = key;
93+}
94+
95+msgreader(c: ref Conn, fd: ref Sys->FD, sync: chan of int)
96+{
97+ sys->pctl(Sys->NEWFD, 2 :: fd.fd :: nil);
98+ sync <-= 1;
99+ fd = sys->fildes(fd.fd);
100+ for(;;){
101+ m := readmsg(c, fd);
102+ if(m == nil){
103+ c.in <-= (nil, sys->sprint("%r"));
104+ break;
105+ }
106+ debug(DBG_PROTO, sys->sprint("<-[%d] %s\n", m.ep-m.rp, m.fulltext()));
107+ case m.mtype {
108+ SSH_MSG_IGNORE =>
109+ ;
110+ SSH_MSG_DEBUG =>
111+ debug(DBG_PROTO, sys->sprint("remote DEBUG: %s\n", m.getstring()));
112+ * =>
113+ c.in <-= (m, nil);
114+ }
115+ }
116+}
117+
118+msgwriter(c: ref Conn, fd: ref Sys->FD, sync: chan of int)
119+{
120+ sys->pctl(Sys->NEWFD, 2 :: fd.fd :: nil);
121+ sync <-= 1;
122+ fd = sys->fildes(fd.fd);
123+ while((m := <-c.out) != nil)
124+ if(writemsg(c, m, fd) < 0){
125+ while(<-c.out != nil)
126+ {} # flush
127+ exit;
128+ }
129+}
130+
131+#
132+# read initial SSH-m.n-comment line
133+#
134+readversion(fd: ref Sys->FD): (int, int, string)
135+{
136+ buf := array[128] of byte;
137+ if((n := readstrnl(fd, buf, len buf)) < 0)
138+ return (-1, -1, sys->sprint("error reading version: %r"));
139+ # id string is "SSH-m.n-comment". We need m=1, n>=5.
140+ s := string buf[0: n];
141+ (nf, fld) := sys->tokenize(s, "-\r\n");
142+ if(nf < 3 || hd fld != "SSH")
143+ return (-1, -1, sys->sprint("unexpected protocol reply: %s", s));
144+ (nf, fld) = sys->tokenize(hd tl fld, ".");
145+ if(nf < 2)
146+ return (-1, -1, "invalid SSH version string in "+s);
147+ return (1, int hd tl fld, s);
148+}
149+
150+calcsessid(hostmod: ref IPint, servermod: ref IPint, cookie: array of byte): array of byte
151+{
152+ b1 := hostmod.iptobebytes();
153+ b2 := servermod.iptobebytes();
154+ buf := array[len b1+len b2+COOKIELEN] of byte;
155+ buf[0:] = b1;
156+ buf[len b1:] = b2;
157+ buf[len b1+len b2:] = cookie[0: COOKIELEN];
158+ sessid := array[Keyring->MD5dlen] of byte;
159+ kr->md5(buf, len buf, sessid, nil);
160+ return sessid;
161+}
162+
163+Msg.text(m: self ref Msg): string
164+{
165+ if(0 <= m.mtype && m.mtype < len msgnames)
166+ return msgnames[m.mtype];
167+ return sys->sprint("<unknown type %d>", m.mtype);
168+}
169+
170+Msg.fulltext(m: self ref Msg): string
171+{
172+ s := m.text();
173+ n := m.ep;
174+ if(n > 64)
175+ n = 64;
176+ for(i := 0; i < n; i++)
177+ s += sys->sprint(" %.2ux", int m.data[i]);
178+ if(n != m.ep)
179+ s += " ...";
180+ return s;
181+}
182+
183+badmsg(m: ref Msg, want: int, errmsg: string)
184+{
185+ if(m == nil)
186+ s := sys->sprint("<early eof: %s>", errmsg);
187+ else
188+ s = m.text();
189+ if(want)
190+ error(sys->sprint("got %s message expecting %s", s, msgnames[want]));
191+ error(sys->sprint("got unexpected %s message", s));
192+}
193+
194+Msg.mk(mtype: int, length: int): ref Msg
195+{
196+ if(length > 256*1024)
197+ raise "message too large";
198+ return ref Msg(mtype, array[4+8+1+length+4] of byte, 0, 0, length);
199+}
200+
201+# used by auth tis
202+unrecvmsg(c: ref Conn, m: ref Msg)
203+{
204+ debug(DBG_PROTO, sys->sprint("unreceived %s len %d\n", msgnames[m.mtype], m.ep-m.rp));
205+ c.unget = m;
206+}
207+
208+readmsg(c: ref Conn, fd: ref Sys->FD): ref Msg
209+{
210+ if(c.unget != nil){ # TO DO: assumes state of processes ensures exclusive access
211+ m := c.unget;
212+ c.unget = nil;
213+ return m;
214+ }
215+ buf := array[4] of byte;
216+ if((n := sys->readn(fd, buf, len buf)) != len buf){
217+ if(n < 0)
218+ sys->werrstr("short net read: %r");
219+ else
220+ sys->werrstr("short net read");
221+ return nil;
222+ }
223+ length := get4(buf, 0);
224+ if(length < 5 || length > 256*1024){
225+ sys->werrstr(sys->sprint("implausible packet length: %.8ux", length));
226+ return nil;
227+ }
228+ pad := 8-length%8;
229+ m := ref Msg(0, array[pad+length] of byte, pad, 0, pad+length-4);
230+ if(sys->readn(fd, m.data, len m.data) != len m.data){
231+ sys->werrstr(sys->sprint("short net read: %r"));
232+ return nil;
233+ }
234+ if(c.cipher != nil)
235+ c.cipher->decrypt(m.data, length+pad);
236+ crc := sum32(0, m.data, m.ep);
237+ crc0 := get4(m.data, m.ep);
238+ if(crc != crc0){
239+ sys->werrstr(sys->sprint("bad crc %#ux != %#ux (packet length %ud)", crc, crc0, length));
240+ return nil;
241+ }
242+ m.mtype = int m.data[m.rp++];
243+ return m;
244+}
245+
246+recvmsg(c: ref Conn, mtype: int): ref Msg
247+{
248+ (m, errmsg) := <-c.in;
249+ if(mtype == 0){
250+ # no checking
251+ }else if(mtype == -1){
252+ # must not be nil
253+ if(m == nil)
254+ error(Ehangup);
255+ }else if(m == nil || m.mtype != mtype) # must be given type
256+ badmsg(m, mtype, errmsg);
257+ return m;
258+}
259+
260+writemsg(c: ref Conn, m: ref Msg, fd: ref Sys->FD): int
261+{
262+ datalen := m.wp;
263+ length := datalen+1+4; # will add type and crc
264+ pad := 8-length%8;
265+ debug(DBG_PROTO, sys->sprint("->[%d] %s\n", datalen, m.fulltext()));
266+ m.data[4+pad+1:] = m.data[0: datalen]; # slide data to correct position (is this guaranteed?) TO DO
267+ put4(m.data, 0, length);
268+ p := 4;
269+ if(c.cipher != nil)
270+ for(i := 0; i < pad; i++)
271+ m.data[p++] = byte fastrand();
272+ else{
273+ for(i = 0; i < pad; i++)
274+ m.data[p++] = byte 0;
275+ }
276+ m.data[p++] = byte m.mtype;
277+ # data already in position
278+ p += datalen;
279+ crc := sum32(0, m.data[4:], pad+1+datalen);
280+ put4(m.data, p, crc);
281+ p += 4;
282+ if(c.cipher != nil)
283+ c.cipher->encrypt(m.data[4:], length+pad);
284+ if(sys->write(fd, m.data, p) != p)
285+ return -1;
286+ return 0;
287+}
288+
289+Msg.get1(m: self ref Msg): int
290+{
291+ if(m.rp >= m.ep)
292+ raise Edecode;
293+ return int m.data[m.rp++];
294+}
295+
296+Msg.get2(m: self ref Msg): int
297+{
298+ if(m.rp+2 > m.ep)
299+ raise Edecode;
300+ x := (int m.data[m.rp+0]<<8) | int m.data[m.rp+1];
301+ m.rp += 2;
302+ return x;
303+}
304+
305+Msg.get4(m: self ref Msg): int
306+{
307+ if(m.rp+4 > m.ep)
308+ raise Edecode;
309+ 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];
310+ m.rp += 4;
311+ return x;
312+}
313+
314+Msg.getarray(m: self ref Msg): array of byte
315+{
316+ length := m.get4();
317+ if(m.rp+length > m.ep)
318+ raise Edecode;
319+ p := m.data[m.rp: m.rp+length];
320+ m.rp += length;
321+ return p;
322+}
323+
324+Msg.getstring(m: self ref Msg): string
325+{
326+ return string m.getarray();
327+}
328+
329+Msg.getbytes(m: self ref Msg, n: int): array of byte
330+{
331+ if(m.rp+n > m.ep)
332+ raise Edecode;
333+ p := m.data[m.rp: m.rp+n];
334+ m.rp += n;
335+ return p;
336+}
337+
338+Msg.getipint(m: self ref Msg): ref IPint
339+{
340+ n := (m.get2()+7)/8; # get2 returns # bits
341+ return IPint.bebytestoip(m.getbytes(n));
342+}
343+
344+Msg.getpk(m: self ref Msg): ref PK.RSA
345+{
346+ m.get4();
347+ ek := m.getipint();
348+ n := m.getipint();
349+ return ref PK.RSA(n, ek);
350+}
351+
352+Msg.put1(m: self ref Msg, x: int)
353+{
354+ if(m.wp >= m.ep)
355+ raise Eencode;
356+ m.data[m.wp++] = byte x;
357+}
358+
359+Msg.put2(m: self ref Msg, x: int)
360+{
361+ if(m.wp+2 > m.ep)
362+ raise Eencode;
363+ (m.data[m.wp+0], m.data[m.wp+1]) = (byte (x>>8), byte x);
364+ m.wp += 2;
365+}
366+
367+Msg.put4(m: self ref Msg, x: int)
368+{
369+ if(m.wp+4 > m.ep)
370+ raise Eencode;
371+ (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);
372+ m.wp += 4;
373+}
374+
375+Msg.putstring(m: self ref Msg, s: string)
376+{
377+ b := array of byte s;
378+ m.put4(len b);
379+ m.putbytes(b, len b);
380+}
381+
382+Msg.putbytes(m: self ref Msg, a: array of byte, n: int)
383+{
384+ if(m.wp+n > m.ep)
385+ raise Eencode;
386+ m.data[m.wp:] = a[0: n];
387+ m.wp += n;
388+}
389+
390+Msg.putipint(m: self ref Msg, b: ref IPint)
391+{
392+ bits := b.bits();
393+ m.put2(bits);
394+# n := (bits+7)/8;
395+ ba := b.iptobebytes();
396+ n := len ba;
397+ if(m.wp+n > m.ep)
398+ raise Eencode;
399+ m.data[m.wp:] = ba;
400+ m.wp += n;
401+}
402+
403+Msg.putpk(m: self ref Msg, key: ref PK.RSA)
404+{
405+ m.put4(key.n.bits());
406+ m.putipint(key.ek);
407+ m.putipint(key.n);
408+}
409+
410+crctab := array[256] of int;
411+
412+initsum32()
413+{
414+ poly := int 16redb88320;
415+ for(i := 0; i < 256; i++){
416+ crc := i;
417+ for(j := 0; j < 8; j++)
418+ if(crc&1)
419+ crc = ((crc>>1) & int ~16r80000000)^poly; # need unsigned shift
420+ else
421+ crc = (crc>>1) & int ~16r80000000;
422+ crctab[i] = crc;
423+ }
424+}
425+
426+first_38: int = 1;
427+
428+sum32(lcrc: int, buf: array of byte, n: int): int
429+{
430+ crc := lcrc;
431+ if(first_38){
432+ first_38 = 0;
433+ initsum32();
434+ }
435+ s := 0;
436+ while(n-- > 0)
437+ crc = crctab[(crc^int buf[s++])&16rff]^((crc>>8)&int ~16rFF000000);
438+ return crc;
439+}
440+
441+erase(b: array of byte)
442+{
443+ for(i := 0; i < len b; i++)
444+ b[i] = byte 0;
445+}
446+
447+#
448+# PKCS#1 padding
449+#
450+rsapad(b: ref IPint, n: int): ref IPint
451+{
452+ a := b.iptobebytes();
453+ pad := n - len a - 3;
454+ if(pad < 0)
455+ error("value too large to pad"); # can't happen if keys are required size
456+ buf := array[n] of byte;
457+ buf[0] = byte 0;
458+ buf[1] = byte 2;
459+ for(i := 2; --pad >= 0; i++)
460+ buf[i] = byte (1+fastrand()%255);
461+ buf[i++] = byte 0;
462+ buf[i:] = a;
463+ c := IPint.bebytestoip(buf);
464+ erase(buf);
465+ erase(a);
466+ return c;
467+}
468+
469+rsaunpad(b: ref IPint): ref IPint
470+{
471+ buf := b.iptobebytes();
472+ i := 0;
473+ if(buf[0] == byte 0)
474+ i++;
475+ if(buf[i] != byte 2)
476+ error("bad data in rsaunpad");
477+ for(; i < len buf; i++)
478+ if(buf[i] == byte 0)
479+ break;
480+ c := IPint.bebytestoip(buf[i:]);
481+ erase(buf);
482+ return c;
483+}
484+
485+rsaencryptbuf(key: ref PK.RSA, buf: array of byte, nbuf: int): ref IPint
486+{
487+ n := (key.n.bits()+7)/8;
488+ a := IPint.bebytestoip(buf[0: nbuf]);
489+ b := rsapad(a, n);
490+ return kr->rsaencrypt(key, b);
491+}
492+
493+iptorjustbe(b: ref IPint, buf: array of byte, length: int)
494+{
495+ a := b.iptobebytes();
496+ if(len a < length){
497+ length -= len a;
498+ erase(buf[0: length]);
499+ buf[length:] = a;
500+ }else
501+ buf[0:] = a[0: length];
502+ erase(a);
503+}
504+
505+hex(a: array of byte): string
506+{
507+ s := "";
508+ for(i := 0; i < len a; i++)
509+ s += sys->sprint("%.2ux", int a[i]);
510+ return s;
511+}
512+
513+debug(n: int, s: string)
514+{
515+ sys->fprint(sys->fildes(2), "debug: %s", s);
516+}
517+
518+error(s: string)
519+{
520+ sys->fprint(sys->fildes(2), "error: %s\n", s);
521+ raise "error";
522+}
523+
524+rsagen(bits: int): ref SK.RSA
525+{
526+ return kr->rsagen(bits, 6, 0);
527+}
528+
529+rsaencrypt(key: ref PK.RSA, b: ref IPint): ref IPint
530+{
531+ return kr->rsaencrypt(key, b);
532+}
533+
534+rsadecrypt(key: ref SK.RSA, b: ref IPint): ref IPint
535+{
536+ return kr->rsadecrypt(key, b);
537+}
538+
539+fastrand(): int
540+{
541+ return int rand->bigrand(4294967295);
542+}
543+
544+readstrnl(fd: ref Sys->FD, buf: array of byte, nbuf: int): int
545+{
546+ for(i := 0; i < nbuf; i++)
547+ case sys->read(fd, buf[i:], 1) {
548+ -1 =>
549+ return -1;
550+ 0 =>
551+ sys->werrstr("unexpected EOF");
552+ return -1;
553+ * =>
554+ if(buf[i] == byte '\n')
555+ return i;
556+ }
557+ sys->werrstr("line too long");
558+ return -1;
559+}
560+
561+eqbytes(a: array of byte, b: array of byte, n: int): int
562+{
563+ if(len a > n || len b > n)
564+ return 0;
565+ for(i := 0; i < n; i++)
566+ if(a[i] != b[i])
567+ return 0;
568+ return 1;
569+}
570+
571+get4(a: array of byte, o: int): int
572+{
573+ return int a[o+0]<<24 | int a[o+1]<<16 | int a[o+2]<<8 | int a[o+3];
574+}
575+
576+put4(a: array of byte, o: int, v: int)
577+{
578+ a[o+0] = byte (v>>24);
579+ a[o+1] = byte (v>>16);
580+ a[o+2] = byte (v>>8);
581+ a[o+3] = byte v;
582+}
appl/cmd/ssh/sshio.m+194−0
@@ -0,0 +1,194 @@
1+
2+ # internal debugging flags
3+ DBG, DBG_CRYPTO, DBG_PACKET, DBG_AUTH, DBG_PROC, DBG_PROTO, DBG_IO, DBG_SCP: con 1<<iota;
4+
5+ # protocol packet types
6+ SSH_MSG_NONE, # 0
7+ SSH_MSG_DISCONNECT,
8+ SSH_SMSG_PUBLIC_KEY,
9+ SSH_CMSG_SESSION_KEY,
10+ SSH_CMSG_USER,
11+ SSH_CMSG_AUTH_RHOSTS,
12+ SSH_CMSG_AUTH_RSA,
13+ SSH_SMSG_AUTH_RSA_CHALLENGE,
14+ SSH_CMSG_AUTH_RSA_RESPONSE,
15+ SSH_CMSG_AUTH_PASSWORD, # 10
16+ SSH_CMSG_REQUEST_PTY,
17+ SSH_CMSG_WINDOW_SIZE,
18+ SSH_CMSG_EXEC_SHELL,
19+ SSH_CMSG_EXEC_CMD,
20+ SSH_SMSG_SUCCESS,
21+ SSH_SMSG_FAILURE,
22+ SSH_CMSG_STDIN_DATA,
23+ SSH_SMSG_STDOUT_DATA,
24+ SSH_SMSG_STDERR_DATA,
25+ SSH_CMSG_EOF, # 20
26+ SSH_SMSG_EXITSTATUS,
27+ SSH_MSG_CHANNEL_OPEN_CONFIRMATION,
28+ SSH_MSG_CHANNEL_OPEN_FAILURE,
29+ SSH_MSG_CHANNEL_DATA,
30+ SSH_MSG_CHANNEL_INPUT_EOF,
31+ SSH_MSG_CHANNEL_OUTPUT_CLOSED,
32+ SSH_MSG_UNIX_DOMAIN_X11_FORWARDING, # obsolete
33+ SSH_SMSG_X11_OPEN,
34+ SSH_CMSG_PORT_FORWARD_REQUEST,
35+ SSH_MSG_PORT_OPEN, # 30
36+ SSH_CMSG_AGENT_REQUEST_FORWARDING,
37+ SSH_SMSG_AGENT_OPEN,
38+ SSH_MSG_IGNORE,
39+ SSH_CMSG_EXIT_CONFIRMATION,
40+ SSH_CMSG_X11_REQUEST_FORWARDING,
41+ SSH_CMSG_AUTH_RHOSTS_RSA,
42+ SSH_MSG_DEBUG,
43+ SSH_CMSG_REQUEST_COMPRESSION,
44+ SSH_CMSG_MAX_PACKET_SIZE,
45+ SSH_CMSG_AUTH_TIS, # 40
46+ SSH_SMSG_AUTH_TIS_CHALLENGE,
47+ SSH_CMSG_AUTH_TIS_RESPONSE,
48+ SSH_CMSG_AUTH_KERBEROS,
49+ SSH_SMSG_AUTH_KERBEROS_RESPONSE,
50+ SSH_CMSG_HAVE_KERBEROS_TGT: con iota;
51+
52+ SSH_MSG_ERROR: con -1;
53+
54+ # protocol flags
55+ SSH_PROTOFLAG_SCREEN_NUMBER: con 1<<0;
56+ SSH_PROTOFLAG_HOST_IN_FWD_OPEN: con 1<<1;
57+
58+ # agent protocol packet types
59+ SSH_AGENTC_NONE,
60+ SSH_AGENTC_REQUEST_RSA_IDENTITIES,
61+ SSH_AGENT_RSA_IDENTITIES_ANSWER,
62+ SSH_AGENTC_RSA_CHALLENGE,
63+ SSH_AGENT_RSA_RESPONSE,
64+ SSH_AGENT_FAILURE,
65+ SSH_AGENT_SUCCESS,
66+ SSH_AGENTC_ADD_RSA_IDENTITY,
67+ SSH_AGENTC_REMOVE_RSA_IDENTITY: con iota;
68+
69+ # protocol constants
70+ SSH_MAX_DATA: con 256*1024;
71+ SSH_MAX_MSG: con SSH_MAX_DATA+4;
72+ SESSKEYLEN: con 32;
73+ SESSIDLEN: con 16;
74+ COOKIELEN: con 8;
75+
76+ # crypto ids
77+ SSH_CIPHER_NONE,
78+ SSH_CIPHER_IDEA,
79+ SSH_CIPHER_DES,
80+ SSH_CIPHER_3DES,
81+ SSH_CIPHER_TSS,
82+ SSH_CIPHER_RC4,
83+ SSH_CIPHER_BLOWFISH: con iota;
84+
85+ # auth method ids
86+ SSH_AUTH_RHOSTS,
87+ SSH_AUTH_RSA,
88+ SSH_AUTH_PASSWORD,
89+ SSH_AUTH_RHOSTS_RSA,
90+ SSH_AUTH_TIS,
91+ SSH_AUTH_USER_RSA: con 1+iota;
92+
93+Edecode: con "error decoding input packet";
94+Eencode: con "out of space encoding output packet (BUG)";
95+Ehangup: con "hungup connection";
96+Ememory: con "out of memory";
97+
98+Cipher: module
99+{
100+ id: fn(): int;
101+ init: fn(key: array of byte, isserver: int);
102+ encrypt: fn(a: array of byte, n: int);
103+ decrypt: fn(a: array of byte, n: int);
104+};
105+
106+Auth: module
107+{
108+ AuthInfo: adt{
109+ user: string;
110+ cap: string;
111+ };
112+
113+ id: fn(): int;
114+ firstmsg: fn(): int;
115+ init: fn(nil: Sshio);
116+ authsrv: fn(nil: ref Sshio->Conn, nil: ref Sshio->Msg): ref AuthInfo;
117+ auth: fn(nil: ref Sshio->Conn): int;
118+};
119+
120+Sshio: module
121+{
122+ PATH: con "sshio.dis";
123+
124+ Conn: adt{
125+ in: chan of (ref Msg, string);
126+ out: chan of ref Msg;
127+
128+ sessid: array of byte;
129+ sesskey: array of byte;
130+ hostkey: ref Keyring->PK.RSA;
131+ flags: int;
132+ cipher: Cipher; # chosen cipher
133+ user: string;
134+ host: string;
135+ interactive: int;
136+ unget: ref Msg;
137+
138+ mk: fn(host: string, fd: ref Sys->FD): ref Conn;
139+ setkey: fn(c: self ref Conn, key: ref Keyring->PK.RSA);
140+ };
141+
142+ Msg: adt{
143+ mtype: int;
144+ data: array of byte;
145+ rp: int; # read pointer
146+ wp: int; # write pointer
147+ ep: int; # byte just beyond message data
148+
149+ mk: fn(mtype: int, length: int): ref Msg;
150+ text: fn(m: self ref Msg): string;
151+ fulltext: fn(m: self ref Msg): string;
152+
153+ get1: fn(m: self ref Msg): int;
154+ get2: fn(m: self ref Msg): int;
155+ get4: fn(m: self ref Msg): int;
156+ getstring: fn(m: self ref Msg): string;
157+ getbytes: fn(m: self ref Msg, n: int): array of byte;
158+ getarray: fn(m: self ref Msg): array of byte;
159+ getipint: fn(m: self ref Msg): ref Keyring->IPint;
160+ getpk: fn(m: self ref Msg): ref Keyring->PK.RSA;
161+
162+ put1: fn(m: self ref Msg, nil: int);
163+ put2: fn(m: self ref Msg, nil: int);
164+ put4: fn(m: self ref Msg, nil: int);
165+ putstring: fn(m: self ref Msg, s: string);
166+ putbytes: fn(m: self ref Msg, a: array of byte, n: int);
167+ putipint: fn(m: self ref Msg, mp: ref Keyring->IPint);
168+ putpk: fn(m: self ref Msg, pk: ref Keyring->PK.RSA);
169+ };
170+
171+ init: fn();
172+
173+ badmsg: fn(nil: ref Msg, nil: int, err: string);
174+ recvmsg: fn(nil: ref Conn, nil: int): ref Msg;
175+ unrecvmsg: fn(nil: ref Conn, nil: ref Msg);
176+ rsapad: fn(nil: ref Keyring->IPint, nil: int): ref Keyring->IPint;
177+ rsaunpad: fn(nil: ref Keyring->IPint): ref Keyring->IPint;
178+ iptorjustbe: fn(nil: ref Keyring->IPint, nil: array of byte, nil: int);
179+ rsaencryptbuf: fn(nil: ref Keyring->PK.RSA, nil: array of byte, nil: int): ref Keyring->IPint;
180+ rsagen: fn(nbits: int): ref Keyring->SK.RSA;
181+ rsaencrypt: fn(key: ref Keyring->PK.RSA, b: ref Keyring->IPint): ref Keyring->IPint;
182+ rsadecrypt: fn(key: ref Keyring->SK.RSA, b: ref Keyring->IPint): ref Keyring->IPint;
183+
184+ debug: fn(nil: int, nil: string);
185+ error: fn(nil: string);
186+ readstrnl: fn(fd: ref Sys->FD, buf: array of byte, nbytes: int): int;
187+ calcsessid: fn(hostmod: ref Keyring->IPint, servermod: ref Keyring->IPint, cookie: array of byte): array of byte;
188+# sshlog: fn(nil: array of byte); # TBA was ...
189+
190+ fastrand: fn(): int;
191+ eqbytes: fn(a: array of byte, b: array of byte, n: int): int;
192+ readversion: fn(fd: ref Sys->FD): (int, int, string);
193+ hex: fn(a: array of byte): string;
194+};
appl/cmd/ssh/sshserve.b+491−0
@@ -0,0 +1,491 @@
1+implement Sshserve;
2+
3+include "sys.m";
4+ sys: Sys;
5+
6+include "draw.m";
7+
8+include "keyring.m";
9+ kr: Keyring;
10+ IPint: import kr;
11+ PK, SK: import kr;
12+
13+include "env.m";
14+ env: Env;
15+
16+include "sh.m";
17+ sh: Sh;
18+
19+include "wait.m";
20+ wait: Wait;
21+
22+include "arg.m";
23+
24+include "sshio.m";
25+ sshio: Sshio;
26+ Conn, Msg: import sshio;
27+ recvmsg: import sshio;
28+ error, debug: import sshio;
29+
30+Sshserve: module
31+{
32+ init: fn(nil: ref Draw->Context, argl: list of string);
33+};
34+
35+AuthRpc: adt {};
36+debuglevel := 0;
37+
38+cipherlist := "blowfish rc4 3des";
39+ciphers: list of Cipher;
40+
41+authlist := "rsa password tis";
42+authsrvs: list of Auth;
43+
44+maxmsg := 256*1024;
45+
46+serverpriv: ref SK.RSA;
47+serverkey: ref PK.RSA;
48+hostpriv: ref SK.RSA;
49+
50+init(nil: ref Draw->Context, args: list of string)
51+{
52+ sys = load Sys Sys->PATH;
53+ kr = load Keyring Keyring->PATH;
54+ env = load Env Env->PATH;
55+ sh = load Sh Sh->PATH;
56+ sshio = load Sshio Sshio->PATH;
57+ sshio->init();
58+ wait = load Wait Wait->PATH;
59+ wait->init();
60+# fmtinstall('B', mpfmt);
61+# fmtinstall('H', encodefmt);
62+ sys->pctl(Sys->NEWPGRP|Sys->FORKFD|Sys->FORKNS|Sys->FORKENV, nil);
63+ keyfile: string;
64+ arg := load Arg Arg->PATH;
65+ arg->setusage("sshserve [-A authlist] [-c cipherlist] [-k keyfile] client-ip-address");
66+ arg->init(args);
67+ while((o := arg->opt()) != 0){
68+ case o {
69+ 'D' =>
70+ debuglevel = int arg->earg();
71+ 'A' =>
72+ authlist = arg->earg();
73+ 'c' =>
74+ cipherlist = arg->earg();
75+ 'k' =>
76+ keyfile = arg->earg();
77+ * =>
78+ arg->usage();
79+ }
80+ }
81+ args = arg->argv();
82+ if(len args != 1)
83+ arg->usage();
84+ arg = nil;
85+
86+ sys->dup(2, 1);
87+# if(keyfile != nil)
88+# ; # read hostpriv from file
89+# sshlog("connect from %s", c.host);
90+ authsrvs = loadlist("auth", authlist, authload);
91+ ciphers = loadlist("cipher", cipherlist, cipherload);
92+ hostpriv = kr->rsagen(1024, 6, 0);
93+ serverpriv = kr->rsagen(768, 6, 0);
94+ serverkey = serverpriv.pk;
95+ {
96+ versioning(sys->fildes(0));
97+ c := Conn.mk(hd args, sys->fildes(0));
98+ c.setkey(hostpriv.pk);
99+ authenticate(c);
100+ comms(c);
101+ }exception e{
102+ "fail:*" =>
103+ raise e;
104+ "error*" =>
105+ notegrp(sys->pctl(0, nil), "error");
106+ raise "fail:"+e;
107+ }
108+}
109+
110+authload(f: string): Auth
111+{
112+ return load Auth f;
113+}
114+
115+cipherload(f: string): Cipher
116+{
117+ return load Cipher f;
118+}
119+
120+loadlist[T](sort: string, set: string, loadf: ref fn(f: string): T): list of T
121+{
122+ l: list of T;
123+ (nil, fld) := sys->tokenize(set, " \t,");
124+ for(; fld != nil; fld = tl fld){
125+ f := "/dis/ssh/"+sort+hd fld+".dis";
126+ m := loadf(f);
127+ if(m == nil)
128+ error(sys->sprint("unknown %s scheme %s (%s)", sort, hd fld, f));
129+ l = m :: l;
130+ }
131+ return l;
132+}
133+
134+comms(c: ref Conn)
135+{
136+ (kidpid, infd, waiting) := prelude(c);
137+Work:
138+ for(;;)alt{
139+ (m, nil) := <-c.in =>
140+ if(m == nil){
141+ notegrp(kidpid, "hungup");
142+ exit;
143+ }
144+ case m.mtype {
145+ * =>
146+ sshio->badmsg(m, 0, nil);
147+ SSH_MSG_DISCONNECT =>
148+ notegrp(kidpid, "hungup");
149+ sysfatal("client disconnected");
150+ SSH_CMSG_STDIN_DATA =>
151+ if(infd != nil){
152+ n := m.get4();
153+ sys->write(infd, m.getbytes(n), n);
154+ }
155+ SSH_CMSG_EOF =>
156+ infd = nil;
157+ SSH_CMSG_EXIT_CONFIRMATION =>
158+ # sent by some clients as dying breath
159+ notegrp(kidpid, "hungup");
160+ break Work;
161+ SSH_CMSG_WINDOW_SIZE =>
162+ ; # we don't care
163+ }
164+ (pid, nil, status) := <-waiting =>
165+ if(pid == kidpid){
166+ if(status != "" && status != "0"){
167+ m := Msg.mk(SSH_MSG_DISCONNECT, 4+Sys->UTFmax*len status);
168+ m.putstring(status);
169+ sendmsg(c, m);
170+ }else{
171+ m := Msg.mk(SSH_SMSG_EXITSTATUS, 4);
172+ m.put4(0);
173+ sendmsg(c, m);
174+ }
175+ sendmsg(c, nil);
176+ break Work;
177+ }
178+ }
179+ notegrp(sys->pctl(0, nil), "done");
180+}
181+
182+prelude(c: ref Conn): (int, ref Sys->FD, chan of (int, string, string))
183+{
184+ for(;;){
185+ m := recvmsg(c, -1);
186+ if(m == nil)
187+ return (-1, nil, nil);
188+ case m.mtype {
189+ * =>
190+ sendmsg(c, Msg.mk(SSH_SMSG_FAILURE, 0));
191+ SSH_MSG_DISCONNECT =>
192+ sysfatal("client disconnected");
193+ SSH_CMSG_REQUEST_PTY =>
194+ sendmsg(c, Msg.mk(SSH_SMSG_SUCCESS, 0));
195+ SSH_CMSG_MAX_PACKET_SIZE =>
196+ n := m.get4();
197+ if(n >= 32 && n <= SSH_MAX_MSG){
198+ maxmsg = n;
199+ sendmsg(c, Msg.mk(SSH_SMSG_SUCCESS, 0));
200+ }else
201+ sendmsg(c, Msg.mk(SSH_SMSG_FAILURE, 0));
202+ SSH_CMSG_EXEC_SHELL =>
203+ return startcmd(c, nil);
204+ SSH_CMSG_EXEC_CMD =>
205+ cmd := m.getstring();
206+ return startcmd(c, cmd);
207+ }
208+ }
209+}
210+
211+copyout(c: ref Conn, fd: ref Sys->FD, mtype: int)
212+{
213+ buf := array[8192] of byte;
214+ max := len buf;
215+ if(max > maxmsg-32) # 32 is an overestimate of packet overhead
216+ max = maxmsg-32;
217+ if(max <= 0)
218+ sysfatal("maximum message size too small");
219+ while((n := sys->read(fd, buf, max)) > 0){
220+ m := Msg.mk(mtype, 4+n);
221+ m.put4(n);
222+ m.putbytes(buf, n);
223+ sendmsg(c, m);
224+ }
225+}
226+
227+send_ssh_smsg_public_key(c: ref Conn, cookie: array of byte)
228+{
229+ m := Msg.mk(SSH_SMSG_PUBLIC_KEY, 2048);
230+ m.putbytes(cookie, COOKIELEN);
231+ m.putpk(serverkey);
232+ m.putpk(c.hostkey);
233+ m.put4(c.flags);
234+ ciphermask := 0;
235+ for(l1 := ciphers; l1 != nil; l1 = tl l1)
236+ ciphermask |= 1<<(hd l1)->id();
237+ m.put4(ciphermask);
238+ authmask := 0;
239+ for(l2 := authsrvs; l2 != nil; l2 = tl l2)
240+ authmask |= 1<<(hd l2)->id();
241+ m.put4(authmask);
242+ sendmsg(c, m);
243+}
244+
245+rpcdecrypt(rpc: ref AuthRpc, b: ref IPint): ref IPint
246+{
247+ raise "rpcdecrypt";
248+# p := array of byte b.iptostr(16);
249+# if(auth_rpc(rpc, "write", p, len p) != ARok)
250+# sysfatal("factotum rsa write: %r");
251+# if(auth_rpc(rpc, "read", nil, 0) != ARok)
252+# sysfatal("factotum rsa read: %r");
253+# return strtomp(rpc.arg, nil, 16, nil);
254+}
255+
256+recv_ssh_cmsg_session_key(c: ref Conn, rpc: ref AuthRpc, cookie: array of byte)
257+{
258+ m := recvmsg(c, SSH_CMSG_SESSION_KEY);
259+ id := m.get1();
260+ c.cipher = nil;
261+ for(l := ciphers; l != nil; l = tl l)
262+ if((hd l)->id() == id){
263+ c.cipher = hd l;
264+ break;
265+ }
266+ if(c.cipher == nil)
267+ sysfatal(sys->sprint("invalid cipher %d selected", id));
268+ if(!sshio->eqbytes(m.getbytes(COOKIELEN), cookie, len cookie))
269+ sysfatal("bad cookie");
270+ serverkeylen := serverkey.n.bits();
271+ hostkeylen := c.hostkey.n.bits();
272+ ksmall, kbig: ref SK.RSA;
273+ if(serverkeylen+128 <= hostkeylen){
274+ ksmall = serverpriv;
275+ kbig = nil;
276+ }else if(hostkeylen+128 <= serverkeylen){
277+ ksmall = nil;
278+ kbig = serverpriv;
279+ }else
280+ sysfatal("server session and host keys do not differ by at least 128 bits");
281+ b := m.getipint();
282+ debug(DBG_CRYPTO, sys->sprint("encrypted with kbig is %s\n", b.iptostr(16)));
283+ if(kbig != nil)
284+ b = sshio->rsadecrypt(kbig, b);
285+ else
286+# b = rpcdecrypt(rpc, b);
287+ b = sshio->rsadecrypt(hostpriv, b);
288+ b = sshio->rsaunpad(b);
289+ sshio->debug(DBG_CRYPTO, sys->sprint("encrypted with ksmall is %s\n", b.iptostr(16)));
290+ if(ksmall != nil)
291+ b = sshio->rsadecrypt(ksmall, b);
292+ else
293+# b = rpcdecrypt(rpc, b);
294+ b = sshio->rsadecrypt(hostpriv, b);
295+ b = sshio->rsaunpad(b);
296+ debug(DBG_CRYPTO, sys->sprint("munged is %s\n", b.iptostr(16)));
297+ n := (b.bits()+7)/8;
298+ if(n < SESSKEYLEN)
299+ sysfatal("client sent short session key");
300+ buf := array[SESSKEYLEN] of byte;
301+ sshio->iptorjustbe(b, buf, SESSKEYLEN);
302+ for(i := 0; i < SESSIDLEN; i++)
303+ buf[i] ^= c.sessid[i];
304+ c.sesskey[0: ] = buf[0: SESSKEYLEN];
305+ debug(DBG_CRYPTO, sys->sprint("unmunged is %.*s\n", SESSKEYLEN*2, sshio->hex(buf)));
306+ c.flags = m.get4();
307+}
308+
309+authsrvuser(c: ref Conn)
310+{
311+ m := recvmsg(c, SSH_CMSG_USER);
312+ user := m.getstring();
313+ c.user = user;
314+ inited := 0;
315+ ai: ref Auth->AuthInfo;
316+ while(authsrvs != nil && ai == nil){
317+# #
318+# # * clumsy: if the client aborted the auth_tis early
319+# # * we don't send a new failure. we check this by
320+# # * looking at c->unget, which is only used in that
321+# # * case.
322+# #
323+ if(c.unget == nil)
324+ sendmsg(c, Msg.mk(SSH_SMSG_FAILURE, 0));
325+ m = recvmsg(c, -1);
326+ for(l := authsrvs; l != nil; l = tl l)
327+ if((hd l)->firstmsg() == m.mtype){
328+ bit := 1 << (hd l)->id();
329+ if((inited & bit) == 0){
330+ (hd l)->init(sshio);
331+ inited |= bit;
332+ }
333+ ai = (hd l)->authsrv(c, m);
334+ break;
335+ }
336+ if(l == nil)
337+ sshio->badmsg(m, 0, nil);
338+ }
339+ sendmsg(c, Msg.mk(SSH_SMSG_SUCCESS, 0));
340+# if(noworld(ai.cuid))
341+# ns := "/lib/namespace.noworld";
342+# else
343+# ns = nil;
344+# if(auth_chuid(ai, ns) < 0){
345+# sshlog("auth_chuid to %s: %r", ai.cuid);
346+# sysfatal("auth_chuid: %r");
347+# }
348+# sshlog("logged in as %q", ai.user);
349+ if(ai != nil)
350+ sys->print("logged in as %q\n", ai.user);
351+}
352+
353+keyjunk()
354+{
355+ p: array of byte;
356+ m: ref IPint;
357+ rpc: ref AuthRpc;
358+ key: ref PK.RSA;
359+
360+# #
361+# # BUG: should use `attr' to get the key attributes
362+# # after the read, but that's not implemented yet.
363+# #
364+# if((b = Bopen("/mnt/factotum/ctl", OREAD)) == nil)
365+# sysfatal("open /mnt/factotum/ctl: %r");
366+# while((p = Brdline(b, '\n')) != nil){
367+# if(strstr(p, " proto=rsa ") != nil && strstr(p, " service=sshserve ") != nil)
368+# break;
369+# }
370+# if(p == nil)
371+# sysfatal("no sshserve keys found in /mnt/factotum/ctl");
372+# a = _parseattr(p);
373+# Bterm(b);
374+# key = rsaprivalloc();
375+# if((p = _strfindattr(a, "n")) == nil)
376+# sysfatal("no n in sshserve key");
377+# if((key.n = IPint.strtoip(p, 16)) == nil)
378+# sysfatal("bad n in sshserve key");
379+# if((p = _strfindattr(a, "ek")) == nil)
380+# sysfatal("no ek in sshserve key");
381+# if((key.ek = IPint.strtoip(p, 16)) == nil)
382+# sysfatal("bad ek in sshserve key");
383+# _freeattr(a);
384+# if((afd = sys->open("/mnt/factotum/rpc", ORDWR)) == nil)
385+# sysfatal("open /mnt/factotum/rpc: %r");
386+# if((rpc = auth_allocrpc(afd)) == nil)
387+# sysfatal("auth_allocrpc: %r");
388+# p = "proto=rsa role=client service=sshserve";
389+# if(auth_rpc(rpc, "start", p, len p) != ARok)
390+# sysfatal("auth_rpc start %s: %r", p);
391+# if(auth_rpc(rpc, "read", nil, 0) != ARok)
392+# sysfatal("auth_rpc read: %r");
393+# m = strtomp(rpc.arg, nil, 16, nil);
394+# if(mpcmp(m, key.n) != 0)
395+# sysfatal("key in /mnt/factotum/ctl does not match rpc key");
396+# mpfree(m);
397+# c.hostkey = key;
398+}
399+
400+versioning(fd: ref Sys->FD)
401+{
402+ sys->fprint(fd, "SSH-1.5-Inferno\n");
403+ (maj, min, err_or_id) := sshio->readversion(fd);
404+ if(maj < 0)
405+ sysfatal(err_or_id);
406+ if(maj != 1 || min < 5)
407+ sysfatal(sys->sprint("protocol mismatch; got %s, need SSH-1.x for x >= 5", err_or_id));
408+}
409+
410+authenticate(c: ref Conn)
411+{
412+ rpc: ref AuthRpc;
413+
414+ cookie := array[COOKIELEN] of {* => byte sshio->fastrand()};
415+ c.sessid = sshio->calcsessid(c.hostkey.n, serverkey.n, cookie);
416+ send_ssh_smsg_public_key(c, cookie);
417+ recv_ssh_cmsg_session_key(c, rpc, cookie);
418+# afd = nil;
419+ c.cipher->init(c.sesskey, 1); # turns on encryption
420+ sendmsg(c, Msg.mk(SSH_SMSG_SUCCESS, 0));
421+ authsrvuser(c);
422+}
423+
424+startcmd(c: ref Conn, cmd: string): (int, ref Sys->FD, chan of (int, string, string))
425+{
426+ pfd := array[3] of {* => array[2] of ref Sys->FD};
427+ for(i := 0; i < 3; i++)
428+ if(sys->pipe(pfd[i]) < 0)
429+ sysfatal(sys->sprint("pipe: %r"));
430+ wfd := sys->open("#p/"+string sys->pctl(0, nil)+"/wait", Sys->OREAD);
431+ if(wfd == nil)
432+ sysfatal(sys->sprint("open wait: %r"));
433+ pidc := chan of int;
434+ spawn startcmd1(c, cmd, pfd, pidc);
435+ kidpid := <-pidc;
436+ (nil, waited) := wait->monitor(wfd);
437+ spawn copyout(c, pfd[1][0], SSH_SMSG_STDOUT_DATA);
438+ pfd[1][0] = nil;
439+ spawn copyout(c, pfd[2][0], SSH_SMSG_STDERR_DATA);
440+ pfd[2][0] = nil;
441+ return (kidpid, pfd[0][0], waited);
442+}
443+
444+startcmd1(c: ref Conn, cmd: string, pfd: array of array of ref Sys->FD, pidc: chan of int)
445+{
446+ sysname := env->getenv("sysname");
447+ tz := env->getenv("timezone");
448+ sys->pctl(Sys->FORKFD, nil);
449+ for(i := 0; i < len pfd; i++)
450+ if(sys->dup(pfd[i][1].fd, i) < 0)
451+ sysfatal(sys->sprint("dup: %r"));
452+ pfd = nil;
453+ sys->pctl(Sys->NEWPGRP|Sys->FORKNS|Sys->FORKENV|Sys->NEWFD, 0::1::2::nil);
454+ pidc <-= sys->pctl(0, nil);
455+ env->setenv("user", c.user);
456+ if(sysname != nil)
457+ env->setenv("sysname", sysname);
458+ if(tz != nil)
459+ env->setenv("tz", tz);
460+ if(sys->chdir("/usr/"+c.user) < 0)
461+ sys->chdir("/");
462+ if(cmd != nil){
463+ env->setenv("service", "rx");
464+ status := sh->run(nil, list of {"/dis/sh.dis", "-lc", cmd});
465+ if(status != nil)
466+ raise "fail:"+status;
467+ }else{
468+ env->setenv("service", "con");
469+ #execl("/bin/ip/telnetd", "telnetd", "-tn", nil); # TO DO: just for echo and line editing
470+ sys->fprint(sys->fildes(2), "sshserve: cannot run /dis/ip/telnetd: %r");
471+ }
472+}
473+
474+sysfatal(s: string)
475+{
476+ sys->print("sysfatal: %s\n", s);
477+ notegrp(sys->pctl(0, nil), "zap");
478+ exit;
479+}
480+
481+notegrp(pid: int, nil: string)
482+{
483+ fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);
484+ if(fd != nil)
485+ sys->fprint(fd, "killgrp");
486+}
487+
488+sendmsg(c: ref Conn, m: ref Msg)
489+{
490+ c.out <-= m;
491+}