From 129dce4b0863e0e8db7f47743d67fbd557856a14 Mon Sep 17 00:00:00 2001 From: Russ Magee Date: Fri, 12 Nov 2021 20:39:44 -0800 Subject: [PATCH 1/2] added hopscotch cipher --- go.mod | 1 + go.sum | 2 ++ xs/xs.go | 2 +- xsnet/chan.go | 4 ++++ xsnet/consts.go | 1 + xsnet/net.go | 8 +++++++- 6 files changed, 16 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 4cfe086..3872ef3 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( blitter.com/go/cryptmt v1.0.2 blitter.com/go/goutmp v1.0.5 blitter.com/go/herradurakex v1.0.0 + blitter.com/go/hopscotch v0.0.0-20211113042251-b8a306eea4dc blitter.com/go/kyber v0.0.0-20200130200857-6f2021cb88d9 blitter.com/go/mtwist v1.0.1 // indirect blitter.com/go/newhope v0.0.0-20200130200750-192fc08a8aae diff --git a/go.sum b/go.sum index b0bb452..f8fc436 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ blitter.com/go/goutmp v1.0.5 h1:isP6bxSs1O06Oy7wB8u4y5SgLr22txfjg/gjG4qn0Og= blitter.com/go/goutmp v1.0.5/go.mod h1:gtlbjC8xGzMk/Cf0BpnVltSa3awOqJ+B5WAxVptTMxk= blitter.com/go/herradurakex v1.0.0 h1:6XaxY+JLT1HUWPF0gYJnjX3pVjrw4YhYZEzZ1U0wkyc= blitter.com/go/herradurakex v1.0.0/go.mod h1:m3+vYZX+2dDjdo+n/HDnXEYJX9pwmNeQLgAfJM8mtxw= +blitter.com/go/hopscotch v0.0.0-20211113042251-b8a306eea4dc h1:IS+jxdKSdlqp6TWG3yMoBde/cctBEMwMDg588JHxgTE= +blitter.com/go/hopscotch v0.0.0-20211113042251-b8a306eea4dc/go.mod h1:9Da1oy0t9aUw3wviba+2mP1inbLGbDuCKAO3mmGQha4= blitter.com/go/kyber v0.0.0-20200130200857-6f2021cb88d9 h1:D45AnrNphtvczBXRp5JQicZRTgaK/Is5bgPDDvRKhTc= blitter.com/go/kyber v0.0.0-20200130200857-6f2021cb88d9/go.mod h1:SK6QfGG72lIfKW1Td0wH7f0wwN5nSIhV3K+wvzGNjrw= blitter.com/go/mtwist v1.0.1 h1:PxmoWexfMpLmc8neHP/PcRc3s17ct7iz4d5W/qJVt04= diff --git a/xs/xs.go b/xs/xs.go index 2247f77..5a1ab51 100755 --- a/xs/xs.go +++ b/xs/xs.go @@ -710,7 +710,7 @@ func main() { flag.BoolVar(&vopt, "v", false, "show version") flag.BoolVar(&dbg, "d", false, "debug logging") - flag.StringVar(&cipherAlg, "c", "C_AES_256", "session `cipher` [C_AES_256 | C_TWOFISH_128 | C_BLOWFISH_64 | C_CRYPTMT1 | C_CHACHA20_12]") + flag.StringVar(&cipherAlg, "c", "C_AES_256", "session `cipher` [C_AES_256 | C_TWOFISH_128 | C_BLOWFISH_64 | C_CRYPTMT1 | C_CHACHA20_12 | C_HOPSCOTCH]") flag.StringVar(&hmacAlg, "m", "H_SHA256", "session `HMAC` [H_SHA256 | H_SHA512]") flag.StringVar(&kexAlg, "k", "KEX_HERRADURA512", "KEx `alg` [KEX_HERRADURA{256/512/1024/2048} | KEX_KYBER{512/768/1024} | KEX_NEWHOPE | KEX_NEWHOPE_SIMPLE | KEX_FRODOKEM_{1344|976}{AES|SHAKE}]") flag.StringVar(&kcpMode, "K", "unused", "KCP `alg`, one of [KCP_NONE | KCP_AES | KCP_BLOWFISH | KCP_CAST5 | KCP_SM4 | KCP_SALSA20 | KCP_SIMPLEXOR | KCP_TEA | KCP_3DES | KCP_TWOFISH | KCP_XTEA] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP") diff --git a/xsnet/chan.go b/xsnet/chan.go index d6b2924..819083d 100644 --- a/xsnet/chan.go +++ b/xsnet/chan.go @@ -21,6 +21,7 @@ import ( "log" "blitter.com/go/cryptmt" + "blitter.com/go/hopscotch" "github.com/aead/chacha20/chacha" "golang.org/x/crypto/blowfish" "golang.org/x/crypto/twofish" @@ -105,6 +106,9 @@ func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err er case CAlgCryptMT1: rc = cryptmt.New(nil, nil, keymat) log.Printf("[cipher CRYPTMT1 (%d)]\n", copts) + case CAlgHopscotch: + rc = hopscotch.New(nil, nil, 4, keymat) + log.Printf("[cipher HOPSCOTCH (%d)]\n", copts) case CAlgChaCha20_12: keymat = expandKeyMat(keymat, chacha.KeySize) key = keymat[0:chacha.KeySize] diff --git a/xsnet/consts.go b/xsnet/consts.go index ff372ed..30833a2 100644 --- a/xsnet/consts.go +++ b/xsnet/consts.go @@ -107,6 +107,7 @@ const ( CAlgBlowfish64 // golang.org/x/crypto/blowfish CAlgCryptMT1 //cryptmt using mtwist64 CAlgChaCha20_12 + CAlgHopscotch CAlgNoneDisallowed ) diff --git a/xsnet/net.go b/xsnet/net.go index 387c35d..45191b7 100644 --- a/xsnet/net.go +++ b/xsnet/net.go @@ -155,6 +155,8 @@ func (c *CSCipherAlg) String() string { return "C_BLOWFISH_64" case CAlgCryptMT1: return "C_CRYPTMT1" + case CAlgHopscotch: + return "C_HOPSCOTCH" case CAlgChaCha20_12: return "C_CHACHA20_12" default: @@ -320,7 +322,7 @@ func _new(kexAlg KEXAlg, conn *net.Conn) (hc *Conn, e error) { // // Session (symmetric) crypto // -// C_AES_256 C_TWOFISH_128 C_BLOWFISH_128 C_CRYPTMT1 C_CHACHA20_12 +// C_AES_256 C_TWOFISH_128 C_BLOWFISH_128 C_CRYPTMT1 C_CHACHA20_12 C_HOPSCOTCH // // Session HMACs // @@ -344,6 +346,10 @@ func (hc *Conn) applyConnExtensions(extensions ...string) { log.Println("[extension arg = C_CRYPTMT1]") hc.cipheropts &= (0xFFFFFF00) hc.cipheropts |= CAlgCryptMT1 + case "C_HOPSCOTCH": + log.Println("[extension arg = C_HOPSCOTCH]") + hc.cipheropts &= (0xFFFFFF00) + hc.cipheropts |= CAlgHopscotch case "C_CHACHA20_12": log.Println("[extension arg = C_CHACHA20_12]") hc.cipheropts &= (0xFFFFFF00) From cfc9ab8590d098ed8b3ea0a59e0581a1367267bc Mon Sep 17 00:00:00 2001 From: Russ Magee Date: Sun, 14 Nov 2021 21:17:56 -0800 Subject: [PATCH 2/2] Fixed error in processing of allowed HMAC algs. xsd: allowed algs default to none if unspecified. --- xsd/xsd.go | 466 +++++++++++++++++++++++++++------------------------ xsnet/net.go | 19 +-- 2 files changed, 254 insertions(+), 231 deletions(-) diff --git a/xsd/xsd.go b/xsd/xsd.go index 1e9b15e..5b931c3 100755 --- a/xsd/xsd.go +++ b/xsd/xsd.go @@ -439,9 +439,9 @@ var ( aHMACAlgs allowedHMACAlgs ) -type allowedKEXAlgs []string // TODO -type allowedCipherAlgs []string // TODO -type allowedHMACAlgs []string // TODO +type allowedKEXAlgs []string +type allowedCipherAlgs []string +type allowedHMACAlgs []string func (a allowedKEXAlgs) allowed(k xsnet.KEXAlg) bool { for i := 0; i < len(a); i++ { @@ -527,9 +527,33 @@ func main() { flag.BoolVar(&useSystemPasswd, "s", true, "use system shadow passwds") flag.BoolVar(&dbg, "d", false, "debug logging") - flag.Var(&aKEXAlgs, "aK", `List of allowed KEX algs (eg. 'KEXAlgA KEXAlgB ... KEXAlgN') (default allow all)`) - flag.Var(&aCipherAlgs, "aC", `List of allowed ciphers (eg. 'CipherAlgA CipherAlgB ... CipherAlgN') (default allow all)`) - flag.Var(&aHMACAlgs, "aH", `List of allowed HMACs (eg. 'HMACAlgA HMACAlgB ... HMACAlgN') (default allow all)`) + flag.Var(&aKEXAlgs, "aK", `Allowed KEX algs (eg. '-aK KEXAlgA -aK KEXAlgB ...') (default: none) + KEX_all + KEX_HERRADURA256 + KEX_HERRADURA512 + KEX_HERRADURA1024 + KEX_HERRADURA2048 + KEX_KYBER512 + KEX_KYBER768 + KEX_KYBER1024 + KEX_NEWHOPE + KEX_NEWHOPE_SIMPLE + KEX_FRODOKEM_1344AES + KEX_FRODOKEM_1344SHAKE + KEX_FRODOKEM_976AES + KEX_FRODOKEM_976SHAKE`) + flag.Var(&aCipherAlgs, "aC", `Allowed ciphers (eg. '-aC CAlgA -aC CAlgB ...') (default: none) + C_all + C_AES_256 + C_TWOFISH_128 + C_BLOWFISH_64 + C_CRYPTMT1 + C_HOPSCOTCH + C_CHACHA20_12`) + flag.Var(&aHMACAlgs, "aH", `Allowed HMACs (eg. '-aH HMACAlgA -aH HMACAlgB ...') (default: none) + H_all + H_SHA256 + H_SHA512`) flag.Parse() @@ -566,17 +590,17 @@ func main() { // Set up allowed algs, if specified (default allow all) if len(aKEXAlgs) == 0 { - aKEXAlgs = []string{"KEX_all"} + aKEXAlgs = []string{"none"} } logger.LogNotice(fmt.Sprintf("Allowed KEXAlgs: %v\n", aKEXAlgs)) // nolint: gosec,errcheck if len(aCipherAlgs) == 0 { - aCipherAlgs = []string{"C_all"} + aCipherAlgs = []string{"none"} } logger.LogNotice(fmt.Sprintf("Allowed CipherAlgs: %v\n", aCipherAlgs)) // nolint: gosec,errcheck if len(aHMACAlgs) == 0 { - aHMACAlgs = []string{"H_all"} + aHMACAlgs = []string{"none"} } logger.LogNotice(fmt.Sprintf("Allowed HMACAlgs: %v\n", aHMACAlgs)) // nolint: gosec,errcheck @@ -620,233 +644,235 @@ func main() { conn, err := l.Accept() if err != nil { log.Printf("Accept() got error(%v), hanging up.\n", err) - } else if !aKEXAlgs.allowed(conn.KEX()) { - log.Printf("Accept() rejected for banned KEX alg %d, hanging up.\n", conn.KEX()) - conn.SetStatus(xsnet.CSEKEXAlgDenied) - conn.Close() - } else if !aCipherAlgs.allowed(conn.CAlg()) { - log.Printf("Accept() rejected for banned Cipher alg %d, hanging up.\n", conn.CAlg()) - conn.SetStatus(xsnet.CSECipherAlgDenied) - conn.Close() - } else if !aHMACAlgs.allowed(conn.HAlg()) { - log.Printf("Accept() rejected for banned HMAC alg %d, hanging up.\n", conn.HAlg()) - conn.SetStatus(xsnet.CSEHMACAlgDenied) - conn.Close() } else { - log.Println("Accepted client") + if !aKEXAlgs.allowed(conn.KEX()) { + log.Printf("Accept() rejected for banned KEX alg %d, hanging up.\n", conn.KEX()) + conn.SetStatus(xsnet.CSEKEXAlgDenied) + conn.Close() + } else if !aCipherAlgs.allowed(conn.CAlg()) { + log.Printf("Accept() rejected for banned Cipher alg %d, hanging up.\n", conn.CAlg()) + conn.SetStatus(xsnet.CSECipherAlgDenied) + conn.Close() + } else if !aHMACAlgs.allowed(conn.HAlg()) { + log.Printf("Accept() rejected for banned HMAC alg %d, hanging up.\n", conn.HAlg()) + conn.SetStatus(xsnet.CSEHMACAlgDenied) + conn.Close() + } else { + log.Println("Accepted client") - // Set up chaffing to client - // Will only start when runShellAs() is called - // after stdin/stdout are hooked up - conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // configure server->client chaffing + // Set up chaffing to client + // Will only start when runShellAs() is called + // after stdin/stdout are hooked up + conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // configure server->client chaffing - // Handle the connection in a new goroutine. - // The loop then returns to accepting, so that - // multiple connections may be served concurrently. - go func(hc *xsnet.Conn) (e error) { - defer hc.Close() // nolint: errcheck + // Handle the connection in a new goroutine. + // The loop then returns to accepting, so that + // multiple connections may be served concurrently. + go func(hc *xsnet.Conn) (e error) { + defer hc.Close() // nolint: errcheck - // Start login timeout here and disconnect if user/pass phase stalls - loginTimeout := time.AfterFunc(30*time.Second, func() { - logger.LogNotice(fmt.Sprintln("Login timed out")) // nolint: errcheck,gosec - hc.Write([]byte{0}) // nolint: gosec,errcheck - hc.Close() - }) + // Start login timeout here and disconnect if user/pass phase stalls + loginTimeout := time.AfterFunc(30*time.Second, func() { + logger.LogNotice(fmt.Sprintln("Login timed out")) // nolint: errcheck,gosec + hc.Write([]byte{0}) // nolint: gosec,errcheck + hc.Close() + }) - //We use io.ReadFull() here to guarantee we consume - //just the data we want for the xs.Session, and no more. - //Otherwise data will be sitting in the channel that isn't - //passed down to the command handlers. - var rec xs.Session - var len1, len2, len3, len4, len5, len6 uint32 + //We use io.ReadFull() here to guarantee we consume + //just the data we want for the xs.Session, and no more. + //Otherwise data will be sitting in the channel that isn't + //passed down to the command handlers. + var rec xs.Session + var len1, len2, len3, len4, len5, len6 uint32 - n, err := fmt.Fscanf(hc, "%d %d %d %d %d %d\n", &len1, &len2, &len3, &len4, &len5, &len6) - log.Printf("xs.Session read:%d %d %d %d %d %d\n", len1, len2, len3, len4, len5, len6) + n, err := fmt.Fscanf(hc, "%d %d %d %d %d %d\n", &len1, &len2, &len3, &len4, &len5, &len6) + log.Printf("xs.Session read:%d %d %d %d %d %d\n", len1, len2, len3, len4, len5, len6) - if err != nil || n < 6 { - log.Println("[Bad xs.Session fmt]") - return err - } - - tmp := make([]byte, len1) - _, err = io.ReadFull(hc, tmp) - if err != nil { - log.Println("[Bad xs.Session.Op]") - return err - } - rec.SetOp(tmp) - - tmp = make([]byte, len2) - _, err = io.ReadFull(hc, tmp) - if err != nil { - log.Println("[Bad xs.Session.Who]") - return err - } - rec.SetWho(tmp) - - tmp = make([]byte, len3) - _, err = io.ReadFull(hc, tmp) - if err != nil { - log.Println("[Bad xs.Session.ConnHost]") - return err - } - rec.SetConnHost(tmp) - - tmp = make([]byte, len4) - _, err = io.ReadFull(hc, tmp) - if err != nil { - log.Println("[Bad xs.Session.TermType]") - return err - } - rec.SetTermType(tmp) - - tmp = make([]byte, len5) - _, err = io.ReadFull(hc, tmp) - if err != nil { - log.Println("[Bad xs.Session.Cmd]") - return err - } - rec.SetCmd(tmp) - - tmp = make([]byte, len6) - _, err = io.ReadFull(hc, tmp) - if err != nil { - log.Println("[Bad xs.Session.AuthCookie]") - return err - } - rec.SetAuthCookie(tmp) - - log.Printf("[xs.Session: op:%c who:%s connhost:%s cmd:%s auth:****]\n", - rec.Op()[0], string(rec.Who()), string(rec.ConnHost()), string(rec.Cmd())) - - var valid bool - var allowedCmds string // Currently unused - if xs.AuthUserByToken(xs.NewAuthCtx(), string(rec.Who()), string(rec.ConnHost()), string(rec.AuthCookie(true))) { - valid = true - } else { - if useSystemPasswd { - //var passErr error - valid, _ /*passErr*/ = xs.VerifyPass(xs.NewAuthCtx(), string(rec.Who()), string(rec.AuthCookie(true))) - } else { - valid, allowedCmds = xs.AuthUserByPasswd(xs.NewAuthCtx(), string(rec.Who()), string(rec.AuthCookie(true)), "/etc/xs.passwd") + if err != nil || n < 6 { + log.Println("[Bad xs.Session fmt]") + return err } - } - _ = loginTimeout.Stop() - // Security scrub - rec.ClearAuthCookie() - - // Tell client if auth was valid - if valid { - hc.Write([]byte{1}) // nolint: gosec,errcheck - } else { - logger.LogNotice(fmt.Sprintln("Invalid user", string(rec.Who()))) // nolint: errcheck,gosec - hc.Write([]byte{0}) // nolint: gosec,errcheck - return - } - - log.Printf("[allowedCmds:%s]\n", allowedCmds) - - if rec.Op()[0] == 'A' { - // Generate automated login token - addr := hc.RemoteAddr() - hname := goutmp.GetHost(addr.String()) - logger.LogNotice(fmt.Sprintf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck - token := GenAuthToken(string(rec.Who()), string(rec.ConnHost())) - tokenCmd := fmt.Sprintf("echo \"%s\" | tee -a ~/.xs_id", token) - cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), tokenCmd, false, hc, chaffEnabled) - // Returned hopefully via an EOF or exit/logout; - // Clear current op so user can enter next, or EOF - rec.SetOp([]byte{0}) - if runErr != nil { - logger.LogErr(fmt.Sprintf("[Error generating autologin token for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck - } else { - log.Printf("[Autologin token generation completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus) - hc.SetStatus(xsnet.CSOType(cmdStatus)) + tmp := make([]byte, len1) + _, err = io.ReadFull(hc, tmp) + if err != nil { + log.Println("[Bad xs.Session.Op]") + return err } - } else if rec.Op()[0] == 'c' { - // Non-interactive command - addr := hc.RemoteAddr() - hname := goutmp.GetHost(addr.String()) - logger.LogNotice(fmt.Sprintf("[Running command for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck - cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), string(rec.Cmd()), false, hc, chaffEnabled) - // Returned hopefully via an EOF or exit/logout; - // Clear current op so user can enter next, or EOF - rec.SetOp([]byte{0}) - if runErr != nil { - logger.LogErr(fmt.Sprintf("[Error spawning cmd for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck - } else { - logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck - hc.SetStatus(xsnet.CSOType(cmdStatus)) - } - } else if rec.Op()[0] == 's' { - // Interactive session - addr := hc.RemoteAddr() - hname := goutmp.GetHost(addr.String()) - logger.LogNotice(fmt.Sprintf("[Running shell for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck + rec.SetOp(tmp) - cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), string(rec.Cmd()), true, hc, chaffEnabled) - // Returned hopefully via an EOF or exit/logout; - // Clear current op so user can enter next, or EOF - rec.SetOp([]byte{0}) - if runErr != nil { - Log.Err(fmt.Sprintf("[Error spawning shell for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck - } else { - logger.LogNotice(fmt.Sprintf("[Shell completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck - hc.SetStatus(xsnet.CSOType(cmdStatus)) + tmp = make([]byte, len2) + _, err = io.ReadFull(hc, tmp) + if err != nil { + log.Println("[Bad xs.Session.Who]") + return err } - } else if rec.Op()[0] == 'D' { - // File copy (destination) operation - client copy to server - log.Printf("[Client->Server copy]\n") - addr := hc.RemoteAddr() - hname := goutmp.GetHost(addr.String()) - logger.LogNotice(fmt.Sprintf("[c->s copy for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck - cmdStatus, runErr := runClientToServerCopyAs(string(rec.Who()), string(rec.TermType()), hc, string(rec.Cmd()), chaffEnabled) - // Returned hopefully via an EOF or exit/logout; - // Clear current op so user can enter next, or EOF - rec.SetOp([]byte{0}) - if runErr != nil { - logger.LogErr(fmt.Sprintf("[c->s copy error for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck - } else { - logger.LogNotice(fmt.Sprintf("[c->s copy completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck - } - // TODO: Test this with huge files.. see Bug #22 - do we need to - // sync w/sender (client) that we've gotten all data? - hc.SetStatus(xsnet.CSOType(cmdStatus)) + rec.SetWho(tmp) - // Send CSOExitStatus *before* client closes channel - s := make([]byte, 4) - binary.BigEndian.PutUint32(s, cmdStatus) - log.Printf("** cp writing closeStat %d at Close()\n", cmdStatus) - hc.WritePacket(s, xsnet.CSOExitStatus) // nolint: gosec,errcheck - } else if rec.Op()[0] == 'S' { - // File copy (src) operation - server copy to client - log.Printf("[Server->Client copy]\n") - addr := hc.RemoteAddr() - hname := goutmp.GetHost(addr.String()) - logger.LogNotice(fmt.Sprintf("[s->c copy for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck - cmdStatus, runErr := runServerToClientCopyAs(string(rec.Who()), string(rec.TermType()), hc, string(rec.Cmd()), chaffEnabled) - if runErr != nil { - logger.LogErr(fmt.Sprintf("[s->c copy error for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck + tmp = make([]byte, len3) + _, err = io.ReadFull(hc, tmp) + if err != nil { + log.Println("[Bad xs.Session.ConnHost]") + return err + } + rec.SetConnHost(tmp) + + tmp = make([]byte, len4) + _, err = io.ReadFull(hc, tmp) + if err != nil { + log.Println("[Bad xs.Session.TermType]") + return err + } + rec.SetTermType(tmp) + + tmp = make([]byte, len5) + _, err = io.ReadFull(hc, tmp) + if err != nil { + log.Println("[Bad xs.Session.Cmd]") + return err + } + rec.SetCmd(tmp) + + tmp = make([]byte, len6) + _, err = io.ReadFull(hc, tmp) + if err != nil { + log.Println("[Bad xs.Session.AuthCookie]") + return err + } + rec.SetAuthCookie(tmp) + + log.Printf("[xs.Session: op:%c who:%s connhost:%s cmd:%s auth:****]\n", + rec.Op()[0], string(rec.Who()), string(rec.ConnHost()), string(rec.Cmd())) + + var valid bool + var allowedCmds string // Currently unused + if xs.AuthUserByToken(xs.NewAuthCtx(), string(rec.Who()), string(rec.ConnHost()), string(rec.AuthCookie(true))) { + valid = true } else { + if useSystemPasswd { + //var passErr error + valid, _ /*passErr*/ = xs.VerifyPass(xs.NewAuthCtx(), string(rec.Who()), string(rec.AuthCookie(true))) + } else { + valid, allowedCmds = xs.AuthUserByPasswd(xs.NewAuthCtx(), string(rec.Who()), string(rec.AuthCookie(true)), "/etc/xs.passwd") + } + } + + _ = loginTimeout.Stop() + // Security scrub + rec.ClearAuthCookie() + + // Tell client if auth was valid + if valid { + hc.Write([]byte{1}) // nolint: gosec,errcheck + } else { + logger.LogNotice(fmt.Sprintln("Invalid user", string(rec.Who()))) // nolint: errcheck,gosec + hc.Write([]byte{0}) // nolint: gosec,errcheck + return + } + + log.Printf("[allowedCmds:%s]\n", allowedCmds) + + if rec.Op()[0] == 'A' { + // Generate automated login token + addr := hc.RemoteAddr() + hname := goutmp.GetHost(addr.String()) + logger.LogNotice(fmt.Sprintf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck + token := GenAuthToken(string(rec.Who()), string(rec.ConnHost())) + tokenCmd := fmt.Sprintf("echo \"%s\" | tee -a ~/.xs_id", token) + cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), tokenCmd, false, hc, chaffEnabled) // Returned hopefully via an EOF or exit/logout; - logger.LogNotice(fmt.Sprintf("[s->c copy completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck - } - // HACK: Bug #22: (xc) Need to wait for rcvr to get final data - // TODO: Await specific msg from client to inform they have gotten all data from the tarpipe - time.Sleep(time.Duration(900 * time.Millisecond)) // Let rcvr set this on setup? + // Clear current op so user can enter next, or EOF + rec.SetOp([]byte{0}) + if runErr != nil { + logger.LogErr(fmt.Sprintf("[Error generating autologin token for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck + } else { + log.Printf("[Autologin token generation completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus) + hc.SetStatus(xsnet.CSOType(cmdStatus)) + } + } else if rec.Op()[0] == 'c' { + // Non-interactive command + addr := hc.RemoteAddr() + hname := goutmp.GetHost(addr.String()) + logger.LogNotice(fmt.Sprintf("[Running command for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck + cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), string(rec.Cmd()), false, hc, chaffEnabled) + // Returned hopefully via an EOF or exit/logout; + // Clear current op so user can enter next, or EOF + rec.SetOp([]byte{0}) + if runErr != nil { + logger.LogErr(fmt.Sprintf("[Error spawning cmd for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck + } else { + logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck + hc.SetStatus(xsnet.CSOType(cmdStatus)) + } + } else if rec.Op()[0] == 's' { + // Interactive session + addr := hc.RemoteAddr() + hname := goutmp.GetHost(addr.String()) + logger.LogNotice(fmt.Sprintf("[Running shell for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck - // Clear current op so user can enter next, or EOF - rec.SetOp([]byte{0}) - hc.SetStatus(xsnet.CSOType(cmdStatus)) - //fmt.Println("Waiting for EOF from other end.") - //_, _ = hc.Read(nil /*ackByte*/) - //fmt.Println("Got remote end ack.") - } else { - logger.LogErr(fmt.Sprintln("[Bad xs.Session]")) // nolint: gosec,errcheck - } - return - }(&conn) // nolint: errcheck + cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), string(rec.Cmd()), true, hc, chaffEnabled) + // Returned hopefully via an EOF or exit/logout; + // Clear current op so user can enter next, or EOF + rec.SetOp([]byte{0}) + if runErr != nil { + Log.Err(fmt.Sprintf("[Error spawning shell for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck + } else { + logger.LogNotice(fmt.Sprintf("[Shell completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck + hc.SetStatus(xsnet.CSOType(cmdStatus)) + } + } else if rec.Op()[0] == 'D' { + // File copy (destination) operation - client copy to server + log.Printf("[Client->Server copy]\n") + addr := hc.RemoteAddr() + hname := goutmp.GetHost(addr.String()) + logger.LogNotice(fmt.Sprintf("[c->s copy for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck + cmdStatus, runErr := runClientToServerCopyAs(string(rec.Who()), string(rec.TermType()), hc, string(rec.Cmd()), chaffEnabled) + // Returned hopefully via an EOF or exit/logout; + // Clear current op so user can enter next, or EOF + rec.SetOp([]byte{0}) + if runErr != nil { + logger.LogErr(fmt.Sprintf("[c->s copy error for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck + } else { + logger.LogNotice(fmt.Sprintf("[c->s copy completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck + } + // TODO: Test this with huge files.. see Bug #22 - do we need to + // sync w/sender (client) that we've gotten all data? + hc.SetStatus(xsnet.CSOType(cmdStatus)) + + // Send CSOExitStatus *before* client closes channel + s := make([]byte, 4) + binary.BigEndian.PutUint32(s, cmdStatus) + log.Printf("** cp writing closeStat %d at Close()\n", cmdStatus) + hc.WritePacket(s, xsnet.CSOExitStatus) // nolint: gosec,errcheck + } else if rec.Op()[0] == 'S' { + // File copy (src) operation - server copy to client + log.Printf("[Server->Client copy]\n") + addr := hc.RemoteAddr() + hname := goutmp.GetHost(addr.String()) + logger.LogNotice(fmt.Sprintf("[s->c copy for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck + cmdStatus, runErr := runServerToClientCopyAs(string(rec.Who()), string(rec.TermType()), hc, string(rec.Cmd()), chaffEnabled) + if runErr != nil { + logger.LogErr(fmt.Sprintf("[s->c copy error for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck + } else { + // Returned hopefully via an EOF or exit/logout; + logger.LogNotice(fmt.Sprintf("[s->c copy completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck + } + // HACK: Bug #22: (xc) Need to wait for rcvr to get final data + // TODO: Await specific msg from client to inform they have gotten all data from the tarpipe + time.Sleep(time.Duration(900 * time.Millisecond)) // Let rcvr set this on setup? + + // Clear current op so user can enter next, or EOF + rec.SetOp([]byte{0}) + hc.SetStatus(xsnet.CSOType(cmdStatus)) + //fmt.Println("Waiting for EOF from other end.") + //_, _ = hc.Read(nil /*ackByte*/) + //fmt.Println("Got remote end ack.") + } else { + logger.LogErr(fmt.Sprintln("[Bad xs.Session]")) // nolint: gosec,errcheck + } + return + }(&conn) // nolint: errcheck + } // algs valid and not blacklisted } // Accept() success } //endfor //logger.LogNotice(fmt.Sprintln("[Exiting]")) // nolint: gosec,errcheck diff --git a/xsnet/net.go b/xsnet/net.go index 45191b7..bfb002f 100644 --- a/xsnet/net.go +++ b/xsnet/net.go @@ -25,6 +25,7 @@ package xsnet import ( "bytes" "crypto/cipher" + crand "crypto/rand" "encoding/binary" "encoding/hex" "errors" @@ -39,7 +40,6 @@ import ( "strings" "sync" "time" - crand "crypto/rand" hkex "blitter.com/go/herradurakex" "blitter.com/go/kyber" @@ -169,11 +169,11 @@ func (hc *Conn) HAlg() CSHmacAlg { } func (h *CSHmacAlg) String() string { - switch (*h >> 8) & 0x0FF { + switch *h & 0x0FF { case HmacSHA256: return "H_SHA256" case HmacSHA512: - return "C_SHA512" + return "H_SHA512" default: return "H_ERR_UNK" } @@ -296,7 +296,7 @@ func _new(kexAlg KEXAlg, conn *net.Conn) (hc *Conn, e error) { case KEX_FRODOKEM_976AES: fallthrough case KEX_FRODOKEM_976SHAKE: - log.Printf("[KEx alg %d accepted]\n", kexAlg) + //log.Printf("[KEx alg %d is valid]\n", kexAlg) default: // UNREACHABLE: _getkexalgnum() guarantees a valid KEX value hc.kex = KEX_HERRADURA512 @@ -517,7 +517,7 @@ func NewHopeDialSetup(c io.ReadWriter, hc *Conn) (err error) { if err != nil { panic(err) } - + hc.r, hc.rm, err = hc.getStream(aliceSharedSecret) hc.w, hc.wm, err = hc.getStream(aliceSharedSecret) return @@ -559,7 +559,7 @@ func NewHopeSimpleDialSetup(c io.ReadWriter, hc *Conn) (err error) { if err != nil { panic(err) } - + hc.r, hc.rm, err = hc.getStream(aliceSharedSecret) hc.w, hc.wm, err = hc.getStream(aliceSharedSecret) return @@ -672,7 +672,6 @@ func FrodoKEMAcceptSetup(c *net.Conn, hc *Conn) (err error) { } pubB, secB := kem.Keygen() - // [Alice sends use a public key (na, ea) pubA_bigint := big.NewInt(0) _, err = fmt.Fscanf(*c, "0x%x\n", pubA_bigint) @@ -696,7 +695,7 @@ func FrodoKEMAcceptSetup(c *net.Conn, hc *Conn) (err error) { // (... and send cipher, connection opts) fmt.Fprintf(*c, "0x%x:0x%x\n", hc.cipheropts, hc.opts) - + // Bob, step 3: Create ctBtoA, shareB ctBtoA, shareB, err := kem.Encapsulate(pubA) if err != nil { @@ -1173,10 +1172,8 @@ func (hl *HKExListener) Accept() (hc Conn, err error) { return Conn{}, err } - // Finally, ensure alg proposed by client is allowed by server config - //if hc.kex.String() { log.Println("[hc.Accept successful]") - return + return hc, err } /*---------------------------------------------------------------------*/