Unified · Split
| @@ -1,3 +1,5 @@ | ||
| 1 | +20090330 | |
| 2 | + include appl/cmd/ssh source (won't compile here because it needs the keyring changes) | |
| 1 | 3 | 20090325 |
| 2 | 4 | emu/* adopt changes to Chan (Dev* dev pointer instead of int type) |
| 3 | 5 | 20090320 |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -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 | |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -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 | +}; | |
| @@ -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 | +} | |