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 | 20090325 | 3 | 20090325 |
| 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) |
| 3 | 20090320 | 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 | } | ||