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 @@
120090330
2 include appl/cmd/ssh source (won't compile here because it needs the keyring changes)
120090325320090325
2 emu/* adopt changes to Chan (Dev* dev pointer instead of int type)4 emu/* adopt changes to Chan (Dev* dev pointer instead of int type)
320090320520090320
appl/cmd/ssh/authpassword.b+64−0
@@ -0,0 +1,64 @@
1implement Auth;
2
3include "sys.m";
4 sys: Sys;
5
6include "keyring.m";
7 kr: Keyring; # TO DO: needed to avoid compiler error
8
9include "factotum.m";
10 factotum: Factotum;
11
12include "sshio.m";
13 sshio: Sshio;
14 Conn, Msg: import sshio;
15
16id(): int
17{
18 return SSH_AUTH_PASSWORD;
19}
20
21init(mod: Sshio)
22{
23 sys = load Sys Sys->PATH;
24 sshio = mod;
25}
26
27firstmsg(): int
28{
29 return SSH_CMSG_AUTH_PASSWORD;
30}
31
32authsrv(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
39auth(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 @@
1implement Auth;
2
3include "sys.m";
4 sys: Sys;
5
6include "keyring.m";
7 kr: Keyring;
8 IPint: import kr;
9 PK, SK: import kr;
10
11include "factotum.m";
12 factotum: Factotum;
13 Attr: import factotum;
14 findattrval: import factotum;
15
16include "sshio.m";
17 sshio: Sshio;
18 Conn, Msg: import sshio;
19 debug: import sshio;
20
21id(): int
22{
23 return SSH_AUTH_RSA;
24}
25
26init(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
35firstmsg(): int
36{
37 return SSH_CMSG_AUTH_RSA;
38}
39
40authsrv(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));
61debug(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
80readpk(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
105auth(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 @@
1implement Auth;
2
3# TO DO: add chal/resp to Factotum
4
5include "sys.m";
6 sys: Sys;
7
8include "keyring.m";
9 kr: Keyring;
10 IPint: import kr;
11
12include "factotum.m";
13 factotum: Factotum;
14 Attr: import factotum;
15 findattrval: import factotum;
16
17include "sshio.m";
18 sshio: Sshio;
19 Conn, Msg: import sshio;
20 debug: import sshio;
21
22id(): int
23{
24 return SSH_AUTH_TIS;
25}
26
27init(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
36firstmsg(): int
37{
38 return SSH_CMSG_AUTH_TIS;
39}
40
41authsrv(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
72auth(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 @@
1implement Cipher;
2
3include "sys.m";
4
5include "keyring.m";
6 kr: Keyring;
7 DESstate: import kr;
8
9include "sshio.m";
10
11Cipherstate: adt
12{
13 enc: array of ref DESstate;
14 dec: array of ref DESstate;
15};
16
17cs: ref Cipherstate;
18
19id(): int
20{
21 return SSH_CIPHER_3DES;
22}
23
24init(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
34encrypt(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
41decrypt(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 @@
1implement Cipher;
2
3include "sys.m";
4
5include "keyring.m";
6 kr: Keyring;
7 BFstate: import kr;
8
9include "sshio.m";
10
11Cipherstate: adt
12{
13 enc: ref BFstate;
14 dec: ref BFstate;
15};
16
17cs: ref Cipherstate;
18
19id(): int
20{
21 return SSH_CIPHER_BLOWFISH;
22}
23
24init(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
30encrypt(buf: array of byte, nbuf: int)
31{
32 kr->blowfishcbc(cs.enc, buf, nbuf, Keyring->Encrypt);
33}
34
35decrypt(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 @@
1implement Cipher;
2
3include "sys.m";
4
5include "keyring.m";
6 kr: Keyring;
7 DESstate: import kr;
8
9include "sshio.m";
10
11Cipherstate: adt
12{
13 enc: ref DESstate;
14 dec: ref DESstate;
15};
16
17cs: ref Cipherstate;
18
19id(): int
20{
21 return SSH_CIPHER_DES;
22}
23
24init(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
30encrypt(buf: array of byte, nbuf: int)
31{
32 kr->descbc(cs.enc, buf, nbuf, Keyring->Encrypt);
33}
34
35decrypt(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 @@
1implement Cipher;
2
3include "sys.m";
4
5include "keyring.m";
6
7include "sshio.m";
8
9id(): int
10{
11 return SSH_CIPHER_NONE;
12}
13
14init(nil: array of byte, nil: int)
15{
16}
17
18encrypt(nil: array of byte, nil: int)
19{
20}
21
22decrypt(nil: array of byte, nil: int)
23{
24}
appl/cmd/ssh/cipherrc4.b+41−0
@@ -0,0 +1,41 @@
1implement Cipher;
2
3include "sys.m";
4
5include "keyring.m";
6 kr: Keyring;
7 RC4state: import kr;
8
9include "sshio.m";
10
11Cipherstate: adt
12{
13 enc: ref RC4state;
14 dec: ref RC4state;
15};
16
17cs: ref Cipherstate;
18
19id(): int
20{
21 return SSH_CIPHER_RC4;
22}
23
24init(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
33encrypt(buf: array of byte, nbuf: int)
34{
35 kr->rc4(cs.enc, buf, nbuf);
36}
37
38decrypt(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
3RAWON_STR := "*";
4
5RAWON : con 0;
6RAWOFF : con 1;
7
8promptstring(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
22readline(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
72readconsline(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
3TARG=\
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
16SYSMODULES=\
17 arg.m\
18 keyring.m\
19 security.m\
20 rand.m\
21 sys.m\
22 draw.m\
23
24MODULES=\
25 sshio.m\
26
27DISBIN=$ROOT/dis/ssh
28
29<$ROOT/mkfiles/mkdis
appl/cmd/ssh/shcons.b+296−0
@@ -0,0 +1,296 @@
1implement Cons;
2
3# possibly useful bits from wm/sh
4
5include "sys.m";
6 sys: Sys;
7 FileIO: import sys;
8
9include "draw.m";
10
11include "sh.m";
12
13include "string.m";
14 str: String;
15
16include "arg.m";
17
18Cons: module
19{
20 init: fn(ctxt: ref Draw->Context, args: list of string);
21};
22
23BSW: con 23; # ^w bacspace word
24BSL: con 21; # ^u backspace line
25EOT: con 4; # ^d end of file
26ESC: con 27; # hold mode
27
28Rdreq: adt
29{
30 off: int;
31 nbytes: int;
32 fid: int;
33 rc: chan of (array of byte, string);
34};
35
36rdreq: list of Rdreq;
37rawon := 0;
38rawinput := "";
39partialread: array of byte;
40
41events: list of string;
42evrdreq: list of Rdreq;
43
44init(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
162RPCread: type (int, int, int, chan of (array of byte, string));
163
164append(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
174sendinput(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.
211triminput(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
264newsh(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 @@
1implement Sshio;
2
3include "sys.m";
4 sys: Sys;
5
6include "keyring.m";
7 kr: Keyring;
8 IPint: import kr;
9 PK, SK: import kr;
10
11include "sshio.m";
12
13include "rand.m";
14 rand: Rand;
15
16init()
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
24msgnames := 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
72Conn.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
90Conn.setkey(c: self ref Conn, key: ref PK.RSA)
91{
92 c.hostkey = key;
93}
94
95msgreader(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
118msgwriter(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#
134readversion(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
150calcsessid(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
163Msg.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
170Msg.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
183badmsg(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
194Msg.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
202unrecvmsg(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
208readmsg(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
246recvmsg(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
260writemsg(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
289Msg.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
296Msg.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
305Msg.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
314Msg.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
324Msg.getstring(m: self ref Msg): string
325{
326 return string m.getarray();
327}
328
329Msg.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
338Msg.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
344Msg.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
352Msg.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
359Msg.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
367Msg.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
375Msg.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
382Msg.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
390Msg.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
403Msg.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
410crctab := array[256] of int;
411
412initsum32()
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
426first_38: int = 1;
427
428sum32(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
441erase(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#
450rsapad(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
469rsaunpad(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
485rsaencryptbuf(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
493iptorjustbe(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
505hex(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
513debug(n: int, s: string)
514{
515 sys->fprint(sys->fildes(2), "debug: %s", s);
516}
517
518error(s: string)
519{
520 sys->fprint(sys->fildes(2), "error: %s\n", s);
521 raise "error";
522}
523
524rsagen(bits: int): ref SK.RSA
525{
526 return kr->rsagen(bits, 6, 0);
527}
528
529rsaencrypt(key: ref PK.RSA, b: ref IPint): ref IPint
530{
531 return kr->rsaencrypt(key, b);
532}
533
534rsadecrypt(key: ref SK.RSA, b: ref IPint): ref IPint
535{
536 return kr->rsadecrypt(key, b);
537}
538
539fastrand(): int
540{
541 return int rand->bigrand(4294967295);
542}
543
544readstrnl(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
561eqbytes(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
571get4(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
576put4(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
93Edecode: con "error decoding input packet";
94Eencode: con "out of space encoding output packet (BUG)";
95Ehangup: con "hungup connection";
96Ememory: con "out of memory";
97
98Cipher: 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
106Auth: 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
120Sshio: 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 @@
1implement Sshserve;
2
3include "sys.m";
4 sys: Sys;
5
6include "draw.m";
7
8include "keyring.m";
9 kr: Keyring;
10 IPint: import kr;
11 PK, SK: import kr;
12
13include "env.m";
14 env: Env;
15
16include "sh.m";
17 sh: Sh;
18
19include "wait.m";
20 wait: Wait;
21
22include "arg.m";
23
24include "sshio.m";
25 sshio: Sshio;
26 Conn, Msg: import sshio;
27 recvmsg: import sshio;
28 error, debug: import sshio;
29
30Sshserve: module
31{
32 init: fn(nil: ref Draw->Context, argl: list of string);
33};
34
35AuthRpc: adt {};
36debuglevel := 0;
37
38cipherlist := "blowfish rc4 3des";
39ciphers: list of Cipher;
40
41authlist := "rsa password tis";
42authsrvs: list of Auth;
43
44maxmsg := 256*1024;
45
46serverpriv: ref SK.RSA;
47serverkey: ref PK.RSA;
48hostpriv: ref SK.RSA;
49
50init(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
110authload(f: string): Auth
111{
112 return load Auth f;
113}
114
115cipherload(f: string): Cipher
116{
117 return load Cipher f;
118}
119
120loadlist[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
134comms(c: ref Conn)
135{
136 (kidpid, infd, waiting) := prelude(c);
137Work:
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
182prelude(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
211copyout(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
227send_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
245rpcdecrypt(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
256recv_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
309authsrvuser(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
353keyjunk()
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
400versioning(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
410authenticate(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
424startcmd(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
444startcmd1(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
474sysfatal(s: string)
475{
476 sys->print("sysfatal: %s\n", s);
477 notegrp(sys->pctl(0, nil), "zap");
478 exit;
479}
480
481notegrp(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
488sendmsg(c: ref Conn, m: ref Msg)
489{
490 c.out <-= m;
491}