diff --git a/Makefile b/Makefile index 67eb22d..9bba711 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ -VERSION := 0.9.11 +VERSION := 0.9.5.4 .PHONY: lint vis clean common client server passwd\ subpkgs install uninstall reinstall scc - + ## Tag version of binaries with build info wrt. ## GO111MODULE(=on) and vendor/ setup vs. $GOPATH pkg builds ############################################################ @@ -73,7 +73,7 @@ tools: common: $(GO) build . - go install -a . + go install . client: common diff --git a/README.md b/README.md index c7278f3..00aeb08 100644 --- a/README.md +++ b/README.md @@ -44,12 +44,10 @@ Currently supported session algorithms: * Blowfish-64 * CryptMTv1 (64bit) (https://eprint.iacr.org/2005/165.pdf) * ChaCha20 (https://github.com/aead/chacha20) -* HOPSCOTCH (https://gogs.blitter.com/Russtopia/hopscotch) [HMAC] * HMAC-SHA256 * HMAC-SHA512 -* WHIRLPOOL *** **A Note on 'cryptographic agility'** @@ -94,6 +92,18 @@ KYBER IND-CCA-2 KEM As of this time (Oct 2018) Kyber is one of the candidate algorithms submitted to the [NIST post-quantum cryptography project](https://csrc.nist.gov/Projects/Post-Quantum-Cryptography). The authors recommend using it in "... so-called hybrid mode in combination with established "pre-quantum" security; for example in combination with elliptic-curve Diffie-Hellman." THIS PROJECT DOES NOT DO THIS (in case you didn't notice yet, THIS PROJECT IS EXPERIMENTAL.) +### Dependencies: + +* Recent version of go (tested, at various times, with go-1.9 to go-1.12.4) +* [github.com/mattn/go-isatty](http://github.com/mattn/go-isatty) //terminal tty detection +* [github.com/kr/pty](http://github.com/kr/pty) //unix pty control (server pty connections) +* [github.com/jameskeane/bcrypt](http://github.com/jameskeane/bcrypt) //password storage/auth +* [blitter.com/go/goutmp](https://gogs.blitter.com/RLabs/goutmp) // wtmp/lastlog C bindings for user accounting +* [https://gitlab.com/yawning/kyber](https://gogs.blitter.com/RLabs/kyber) // golang Kyber KEM +* [https://gitlab.com/yawning/kyber](https://gogs.blitter.com/RLabs/newhope) // golang NEWHOPE,NEWHOPE-SIMPLE KEX +* [blitter.com/go/mtwist](https://gogs.blitter.com/RLabs/mtwist) // 64-bit Mersenne Twister PRNG +* [blitter.com/go/cryptmt](https://gogs.blitter.com/RLabs/cryptmt) // CryptMTv1 stream cipher + ### Installing @@ -187,17 +197,15 @@ or is interrupted. ### Setting up an 'authtoken' for scripted (password-free) logins Use the -g option of xs to request a token from the remote server, which will return a -hostname:token string. Place this string into $HOME/.config/xs/.xs_id to allow logins without -entering a password (obviously, $HOME/.config/xs/.xs_id on both server and client for the user +hostname:token string. Place this string into $HOME/.xs_id to allow logins without +entering a password (obviously, $HOME/.xs_id on both server and client for the user should *not* be world-readable.) ``` -$ xs -g user@host.net >>~/.config/xs/.xs_id +$ xs -g user@host.net >~/.xs_id ``` -[enter password blindly, authtoken entry will be stored in ~/.config/xs/.xs_id] +[enter password blindly, authtoken entry will be stored in ~/.xs_id] -NOTE you may need to remove older entries for the same host if this is not the first time you have added -it to your .xs_id file. ### File Copying using xc diff --git a/auth.go b/auth.go index cc3b042..5f4f2ed 100755 --- a/auth.go +++ b/auth.go @@ -23,7 +23,6 @@ import ( "runtime" "strings" - "blitter.com/go/xs/xsnet" "github.com/jameskeane/bcrypt" passlib "gopkg.in/hlandau/passlib.v1" ) @@ -53,7 +52,7 @@ func VerifyPass(ctx *AuthCtx, user, password string) (bool, error) { } else if runtime.GOOS == "freebsd" { pwFileName = "/etc/master.passwd" } else { - return false, errors.New("Unsupported platform") + pwFileName = "unsupported" } pwFileData, e := ctx.reader(pwFileName) if e != nil { @@ -155,7 +154,7 @@ func AuthUserByPasswd(ctx *AuthCtx, username string, auth string, fname string) // ------------- End xs-local passwd auth routine(s) ----------- // AuthUserByToken checks user login information against an auth token. -// Auth tokens are stored in each user's $HOME/.config/xs/.xs_id and are requested +// Auth tokens are stored in each user's $HOME/.xs_id and are requested // via the -g option. // The function also check system /etc/passwd to cross-check the user // actually exists. @@ -173,9 +172,9 @@ func AuthUserByToken(ctx *AuthCtx, username string, connhostname string, auth st return false } - b, e := ctx.reader(fmt.Sprintf("%s/%s", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE)) + b, e := ctx.reader(fmt.Sprintf("%s/.xs_id", u.HomeDir)) if e != nil { - log.Printf("INFO: Cannot read %s/%s\n", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE) + log.Printf("INFO: Cannot read %s/.xs_id\n", u.HomeDir) return false } diff --git a/bacillus/ci_pushbuild.sh b/bacillus/ci_pushbuild.sh index 8cfe45f..9ecb3b6 100755 --- a/bacillus/ci_pushbuild.sh +++ b/bacillus/ci_pushbuild.sh @@ -5,7 +5,7 @@ export GOPATH="${HOME}/go" export PATH=/usr/local/bin:/usr/bin:/usr/lib/ccache/bin:/bin:$GOPATH/bin unset GO111MODULE -#export GOPROXY="direct" +export GOPROXY="direct" #!# GOCACHE will be phased out in v1.12. [github.com/golang/go/issues/26809] #!export GOCACHE="${HOME}/.cache/go-build" @@ -25,9 +25,6 @@ echo "Building most recent push on branch $branch" git checkout "$branch" ls -go mod init -go mod tidy - ############ stage "Build" ############ @@ -49,12 +46,12 @@ go test -v . ############ stage "Test(Authtoken)" ############ -if [ -f ~/.config/xs/.xs_id ]; then - echo "Clearing test user $USER .xs_id file ..." - mv ~/.config/xs/.xs_id ~/.config/xs/.xs_id.bak +if [ -f ~/.xs_id ]; then + echo "Clearing test user $USER ~/.xs_id file ..." + mv ~/.xs_id ~/.xs_id.bak fi -echo "Setting dummy authtoken in .xs_id ..." -echo "localhost:${USER}:asdfasdfasdf" >~/.config/xs/.xs_id +echo "Setting dummy authtoken in ~/.xs_id ..." +echo "localhost:${USER}:asdfasdfasdf" >~/.xs_id echo "Performing remote command on @localhost via authtoken login ..." tokentest=$(timeout 10 xs -x "echo -n FOO" @localhost) if [ "${tokentest}" != "FOO" ]; then @@ -94,9 +91,9 @@ stage "Test(xc C->S)" ############ echo "TODO ..." -if [ -f ~/.config/xs/.xs_id.bak ]; then - echo "Restoring test user $USER .xs_id file ..." - mv ~/.config/xs/.xs_id.bak ~/.config/xs/.xs_id +if [ -f ~/.xs_id.bak ]; then + echo "Restoring test user $USER ~/.xs_id file ..." + mv ~/.xs_id.bak ~/.xs_id fi ############ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0699d6f --- /dev/null +++ b/go.mod @@ -0,0 +1,38 @@ +module blitter.com/go/xs + +go 1.20 + +require ( + blitter.com/go/chacha20 v0.0.0-20200130200441-214e4085f54c + blitter.com/go/cryptmt v1.0.2 + blitter.com/go/goutmp v1.0.6 + blitter.com/go/groestl v0.0.0-20220410000905-c4decbf31d64 + blitter.com/go/herradurakex v1.0.0 + blitter.com/go/hopscotch v0.1.1 + blitter.com/go/kyber v0.0.0-20200130200857-6f2021cb88d9 + blitter.com/go/mtwist v1.0.1 + blitter.com/go/newhope v0.0.0-20200130200750-192fc08a8aae + github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da + github.com/creack/pty v1.1.18 + github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f + github.com/klauspost/cpuid/v2 v2.2.5 + github.com/klauspost/reedsolomon v1.11.8 + github.com/kuking/go-frodokem v1.0.2 + github.com/mattn/go-isatty v0.0.19 + github.com/pkg/errors v0.9.1 + github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 + github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b + github.com/tjfoc/gmsm v1.4.1 + github.com/xtaci/kcp-go v5.4.20+incompatible + golang.org/x/crypto v0.13.0 + golang.org/x/net v0.15.0 + golang.org/x/sys v0.12.0 + gopkg.in/hlandau/easymetric.v1 v1.0.0 + gopkg.in/hlandau/measurable.v1 v1.0.1 + gopkg.in/hlandau/passlib.v1 v1.0.11 +) + +require ( + github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 // indirect + golang.org/x/text v0.13.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a800c7a --- /dev/null +++ b/go.sum @@ -0,0 +1,145 @@ +blitter.com/go/chacha20 v0.0.0-20200130200441-214e4085f54c h1:LcnFFg6MCIJHf26P7eOUST45fNLHJI5erq0gWZaDLCo= +blitter.com/go/chacha20 v0.0.0-20200130200441-214e4085f54c/go.mod h1:EMJtRcf22WCtHGiXCw+NB/Sb/PYcXtUgUql6LDEwyXo= +blitter.com/go/cryptmt v1.0.2 h1:ZcLhQk7onUssXyQwG3GdXDXctCVnNL+b7aFuvwOdKXc= +blitter.com/go/cryptmt v1.0.2/go.mod h1:tdME2J3O4agaDAYIYNQzzuB28yVGnPSMmV3a/ucSU84= +blitter.com/go/goutmp v1.0.6 h1:jRKRw2WalVBza4T50etAfbvT2xp9G5uykIHTvyB5r0k= +blitter.com/go/goutmp v1.0.6/go.mod h1:DnK/uLBu1/1yLFiuVlmwvWErzAWVp+pDv7t6ZaQRLNc= +blitter.com/go/groestl v0.0.0-20220410000905-c4decbf31d64 h1:SH6cZ4JiOTmWGeVd5hCgt8gsMvfPPHWpEwNdxfsBugM= +blitter.com/go/groestl v0.0.0-20220410000905-c4decbf31d64/go.mod h1:YMdIR/gCtFwU/a09jyWAwUu2J9CQejUFwkfD+PyVg+4= +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.1.1 h1:hh809THr3I52J5G5QozNhDSd+qGwXWGqLh3FJBGrp+o= +blitter.com/go/hopscotch v0.1.1/go.mod h1:hCz7oE31KjaO9M6+s2DcyVNlAA8saE/AaVYKFs7hl1I= +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= +blitter.com/go/mtwist v1.0.1/go.mod h1:aU82Nx8+b1v8oZRNqImfEDzDTPim81rY0ACKAIclV18= +blitter.com/go/newhope v0.0.0-20200130200750-192fc08a8aae h1:YBBaCcdYRrI1btsmcMTv1VMPmaSXXz0RwKOTgMJYSRU= +blitter.com/go/newhope v0.0.0-20200130200750-192fc08a8aae/go.mod h1:ywoxfDBqInPsqtnxYsmS4SYMJ5D/kNcrFgpvI+Xcun0= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f h1:UWGE8Vi+1Agt0lrvnd7UsmvwqWKRzb9byK9iQmsbY0Y= +github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f/go.mod h1:u+9Snq0w+ZdYKi8BBoaxnEwWu0fY4Kvu9ByFpM51t1s= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/reedsolomon v1.11.8 h1:s8RpUW5TK4hjr+djiOpbZJB4ksx+TdYbRH7vHQpwPOY= +github.com/klauspost/reedsolomon v1.11.8/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A= +github.com/kuking/go-frodokem v1.0.2 h1:sxdguENCyr6WnLbJ/cjz0AYCW75H1b+E6zXY2ldZnUU= +github.com/kuking/go-frodokem v1.0.2/go.mod h1:83ZX1kHOd72ouCsvbffCqJIj7Ih83MQTAjH2QbqzLZk= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= +github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= +github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI= +github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= +github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= +github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= +github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= +github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZWQ1bABv5xAg= +github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= +github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E= +github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190902133755-9109b7679e13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/hlandau/easymetric.v1 v1.0.0 h1:ZbfbH7W3giuVDjWUoFhDOjjv20hiPr5HZ2yMV5f9IeE= +gopkg.in/hlandau/easymetric.v1 v1.0.0/go.mod h1:yh75hypuFzAxmvECh3ZKGCvFnIfapYJh2wv7ASaX2RE= +gopkg.in/hlandau/measurable.v1 v1.0.1 h1:wH5UZKCRUnRr1iD+xIZfwhtxhmr+bprRJttqA1Rklf4= +gopkg.in/hlandau/measurable.v1 v1.0.1/go.mod h1:6N+SYJGMTmetsx7wskULP+juuO+++tsHJkAgzvzsbuM= +gopkg.in/hlandau/passlib.v1 v1.0.11 h1:vKeHwGRdWBD9mm4bJ56GAAdBXpFUYvg/BYYkmphjnmA= +gopkg.in/hlandau/passlib.v1 v1.0.11/go.mod h1:wxGAv2CtQHlzWY8NJp+p045yl4WHyX7v2T6XbOcmqjM= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/xs/mintty_wrapper.sh b/xs/mintty_wrapper.sh index dbc678a..9cba8ff 100755 --- a/xs/mintty_wrapper.sh +++ b/xs/mintty_wrapper.sh @@ -1,7 +1,7 @@ #!/bin/bash # ## This wrapper may be used within the MSYS/mintty Windows -## shell environment to have a functioning xs client with +## shell environment to have a functioning hkexsh client with ## working 'raw' mode and hidden password entry. ## ## mintty uses named pipes and ptys to get a more POSIX-like diff --git a/xs/xs.go b/xs/xs.go index 018c542..b30ea05 100755 --- a/xs/xs.go +++ b/xs/xs.go @@ -102,14 +102,6 @@ type ( escSeqs map[byte]escHandler ) -var ( - escs = escSeqs{ - 'i': func(io.Writer) { os.Stdout.Write([]byte("\x1b[s\x1b[2;1H\x1b[1;31m[HKEXSH]\x1b[39;49m\x1b[u")) }, - 't': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m[HKEXSH]\x1b[39;49m")) }, - 'B': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m" + bob + "\x1b[39;49m")) }, - } -) - // Copy copies from src to dst until either EOF is reached // on src or an error occurs. It returns the number of bytes // copied and the first error encountered while copying, if any. @@ -157,6 +149,11 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er // or tunnel traffic indicator - note we cannot just spawn a goroutine // here, as copyBuffer() returns after each burst of data. Scope must // outlive individual copyBuffer calls). + escs := escSeqs{ + 'i': func(io.Writer) { os.Stdout.Write([]byte("\x1b[s\x1b[2;1H\x1b[1;31m[HKEXSH]\x1b[39;49m\x1b[u")) }, + 't': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m[HKEXSH]\x1b[39;49m")) }, + 'B': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m" + bob + "\x1b[39;49m")) }, + } /* // If the reader has a WriteTo method, use it to do the copy. @@ -547,8 +544,6 @@ func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec * _, outerr := func(conn *xsnet.Conn, r io.Reader) (w int64, e error) { // Copy() expects EOF so this will // exit with outerr == nil - // NOTE we use a local implementation of Copy() to allow - // for custom key sequences to trigger local actions w, e = Copy(conn, r) return w, e }(conn, os.Stdin) @@ -692,24 +687,23 @@ func sendSessionParams(conn io.Writer /* *xsnet.Conn*/, rec *xs.Session) (e erro // TODO: reduce gocyclo func main() { //nolint: funlen, gocyclo var ( - isInteractive bool - vopt bool - gopt bool // true: login via password, asking server to generate authToken - dbg bool - shellMode bool // true: act as shell, false: file copier - cipherAlg string - hmacAlg string - kexAlg string - server string - port uint - cmdStr string - tunSpecStr string // lport1:rport1[,lport2:rport2,...] - rekeySecs uint - remodRequested bool // true: when rekeying, switch to random cipher/hmac alg - copySrc []byte - copyDst string - copyQuiet bool - copyLimitBPS uint + isInteractive bool + vopt bool + gopt bool // true: login via password, asking server to generate authToken + dbg bool + shellMode bool // true: act as shell, false: file copier + cipherAlg string + hmacAlg string + kexAlg string + server string + port uint + cmdStr string + tunSpecStr string // lport1:rport1[,lport2:rport2,...] + + copySrc []byte + copyDst string + copyQuiet bool + copyLimitBPS uint authCookie string chaffEnabled bool @@ -733,8 +727,7 @@ func main() { //nolint: funlen, gocyclo C_CHACHA20_12`) flag.StringVar(&hmacAlg, "m", "H_SHA256", "session `HMAC`"+` H_SHA256 - H_SHA512 - H_WHIRLPOOL`) + H_SHA512`) flag.StringVar(&kexAlg, "k", "KEX_HERRADURA512", "KEx `alg`"+` KEX_HERRADURA256 KEX_HERRADURA512 @@ -751,8 +744,6 @@ func main() { //nolint: funlen, gocyclo KEX_FRODOKEM_976SHAKE`) 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") //nolint:lll flag.UintVar(&port, "p", 2000, "``port") //nolint:gomnd,lll - flag.UintVar(&rekeySecs, "r", 300, "rekey interval in `secs`") - flag.BoolVar(&remodRequested, "R", false, "Borg Countermeasures (remodulate cipher/hmac alg on each rekey)") //nolint:gocritic,nolintlint // flag.StringVar(&authCookie, "a", "", "auth cookie") flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts") flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min `msecs`") //nolint:gomnd @@ -891,7 +882,7 @@ func main() { //nolint: funlen, gocyclo if !gopt { // See if we can log in via an auth token u, _ := user.Current() - ab, aerr := os.ReadFile(fmt.Sprintf("%s/%s", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE)) + ab, aerr := os.ReadFile(fmt.Sprintf("%s/.xs_id", u.HomeDir)) if aerr == nil { for _, line := range strings.Split(string(ab), "\n") { line += "\n" @@ -909,7 +900,7 @@ func main() { //nolint: funlen, gocyclo _, _ = fmt.Fprintln(os.Stderr, "[no authtoken, use -g to request one from server]") } } else { - log.Printf("[cannot read %s/%s]\n", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE) + log.Printf("[cannot read %s/.xs_id]\n", u.HomeDir) } } runtime.GC() @@ -973,21 +964,12 @@ func main() { //nolint: funlen, gocyclo if kcpMode != "unused" { proto = "kcp" } - - remodExtArg := "" - if remodRequested { - remodExtArg = "OPT_REMOD" - } - // Pass opt to Dial() via extensions arg - conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode, remodExtArg) + conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode) if err != nil { fmt.Println(err) exitWithStatus(XSNetDialFailed) } - conn.RekeyHelper(rekeySecs) - defer conn.ShutdownRekey() - // === Shell terminal mode (Shell vs. Copy) setup // Set stdin in raw mode if it's an interactive session @@ -1021,7 +1003,7 @@ func main() { //nolint: funlen, gocyclo loginTimeout := time.AfterFunc(30*time.Second, func() { //nolint:gomnd restoreTermState(oldState) fmt.Printf(" .. [login timeout]\n") - exitWithStatus(xsnet.CSELoginTimeout) + exitWithStatus(xsnet.CSOLoginTimeout) }) if authCookie == "" { @@ -1075,16 +1057,13 @@ func main() { //nolint: funlen, gocyclo fmt.Fprintln(os.Stderr, rejectUserMsg()) rec.SetStatus(GeneralProtocolErr) } else { - // === Set up connection keepalive to server - conn.StartupKeepAlive() // goroutine, returns immediately - defer conn.ShutdownKeepAlive() - // === Set up chaffing to server conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // enable client->server chaffing if chaffEnabled { // #gv:s/label=\"main\$2\"/label=\"deferCloseChaff\"/ // TODO:.gv:main:2:deferCloseChaff - conn.StartupChaff() // goroutine, returns immediately + conn.EnableChaff() // goroutine, returns immediately + defer conn.DisableChaff() defer conn.ShutdownChaff() } @@ -1167,5 +1146,6 @@ func exitWithStatus(status int) { log.Fatal("could not write memory profile: ", err) //nolint:gocritic } } + os.Exit(status) } diff --git a/xsd/xsd.go b/xsd/xsd.go index 62f9a39..1fcef4f 100755 --- a/xsd/xsd.go +++ b/xsd/xsd.go @@ -17,14 +17,11 @@ import ( "fmt" "io" "log" - "net/http" "os" "os/exec" "os/signal" "os/user" "path" - "runtime" - "runtime/pprof" "strings" "sync" "syscall" @@ -47,9 +44,6 @@ var ( // Log - syslog output (with no -d) Log *logger.Writer - - cpuprofile string - memprofile string ) const ( @@ -83,7 +77,12 @@ func runClientToServerCopyAs(who, ttype string, conn *xsnet.Conn, fpath string, log.Println("uid:", uid, "gid:", gid) // Need to clear server's env and set key vars of the - // target user. + // target user. This isn't perfect (TERM doesn't seem to + // work 100%; ANSI/xterm colour isn't working even + // if we set "xterm" or "ansi" here; and line count + // reported by 'stty -a' defaults to 24 regardless + // of client shell window used to run client. + // Investigate -- rlm 2018-01-26) os.Clearenv() os.Setenv("HOME", u.HomeDir) os.Setenv("TERM", ttype) @@ -110,10 +109,9 @@ func runClientToServerCopyAs(who, ttype string, conn *xsnet.Conn, fpath string, c.Dir = destDir - //If os.Clearenv() isn't called by server above these will be seen - //in the client's session env. - //c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", - // "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who} + //If os.Clearenv() isn't called by server above these will be seen in the + //client's session env. + //c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who} //c.Dir = u.HomeDir c.SysProcAttr = &syscall.SysProcAttr{} c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid} @@ -121,13 +119,10 @@ func runClientToServerCopyAs(who, ttype string, conn *xsnet.Conn, fpath string, c.Stdout = os.Stdout c.Stderr = os.Stderr - // === Set up connection keepalive to client - conn.StartupKeepAlive() // goroutine, returns immediately - defer conn.ShutdownKeepAlive() - if chaffing { - conn.StartupChaff() + conn.EnableChaff() } + defer conn.DisableChaff() defer conn.ShutdownChaff() // Start the command (no pty) @@ -187,7 +182,12 @@ func runServerToClientCopyAs(who, ttype string, conn *xsnet.Conn, srcPath string log.Println("uid:", uid, "gid:", gid) // Need to clear server's env and set key vars of the - // target user. + // target user. This isn't perfect (TERM doesn't seem to + // work 100%; ANSI/xterm colour isn't working even + // if we set "xterm" or "ansi" here; and line count + // reported by 'stty -a' defaults to 24 regardless + // of client shell window used to run client. + // Investigate -- rlm 2018-01-26) os.Clearenv() _ = os.Setenv("HOME", u.HomeDir) _ = os.Setenv("TERM", ttype) @@ -204,10 +204,9 @@ func runServerToClientCopyAs(who, ttype string, conn *xsnet.Conn, srcPath string c = exec.Command(cmdName, cmdArgs...) - //If os.Clearenv() isn't called by server above these will be seen - //in the client's session env. - //c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", "SUDO_USER=", - // "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who} + //If os.Clearenv() isn't called by server above these will be seen in the + //client's session env. + //c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who} c.Dir = u.HomeDir c.SysProcAttr = &syscall.SysProcAttr{} c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid} @@ -221,14 +220,11 @@ func runServerToClientCopyAs(who, ttype string, conn *xsnet.Conn, srcPath string c.Stderr = stdErrBuffer //c.Stderr = nil - // === Set up connection keepalive to client - conn.StartupKeepAlive() // goroutine, returns immediately - defer conn.ShutdownKeepAlive() - if chaffing { - conn.StartupChaff() + conn.EnableChaff() } //defer conn.Close() + defer conn.DisableChaff() defer conn.ShutdownChaff() // Start the command (no pty) @@ -276,7 +272,12 @@ func runShellAs(who, hname, ttype, cmd string, interactive bool, //nolint:funlen log.Println("uid:", uid, "gid:", gid) // Need to clear server's env and set key vars of the - // target user. + // target user. This isn't perfect (TERM doesn't seem to + // work 100%; ANSI/xterm colour isn't working even + // if we set "xterm" or "ansi" here; and line count + // reported by 'stty -a' defaults to 24 regardless + // of client shell window used to run client. + // Investigate -- rlm 2018-01-26) os.Clearenv() _ = os.Setenv("HOME", u.HomeDir) _ = os.Setenv("TERM", ttype) @@ -286,28 +287,21 @@ func runShellAs(who, hname, ttype, cmd string, interactive bool, //nolint:funlen if interactive { if useSysLogin { - // Use the server's login binary (post-auth) + // Use the server's login binary (post-auth, which + // is still done via our own bcrypt file) + // + // Note login will drop privs to the intended user for us // // Things UNIX login does, like print the 'motd', // and use the shell specified by /etc/passwd, will be done // automagically, at the cost of another external tool // dependency. // - // One drawback of using 'login' is that the remote side - // cannot give us back the shell's exit code, since it - // exits back to 'login', which usually returns its own - // 0 status back to us. - // - // Note login will drop privs to the intended user for us. - // c = exec.Command(xs.GetTool("login"), "-f", "-p", who) //nolint:gosec } else { - // Run shell directly (which allows nonzero exit codes back to - // the local system upon shell exit, whereas 'login' does not.) + // Using our separate login via local passwd file // - // Note we must drop privs ourselves for the user shell since - // we aren't using 'login' on the remote end which would do it - // for us. + // Note we must drop privs ourselves for the user shell // c = exec.Command(xs.GetTool("bash"), "-i", "-l") //nolint:gosec c.SysProcAttr = &syscall.SysProcAttr{} @@ -318,10 +312,9 @@ func runShellAs(who, hname, ttype, cmd string, interactive bool, //nolint:funlen c.SysProcAttr = &syscall.SysProcAttr{} c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid} } - //If os.Clearenv() isn't called by server above these will be seen - //in the client's session env. - //c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", - // "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who} + //If os.Clearenv() isn't called by server above these will be seen in the + //client's session env. + //c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who} c.Dir = u.HomeDir // Start the command with a pty. @@ -346,9 +339,6 @@ func runShellAs(who, hname, ttype, cmd string, interactive bool, //nolint:funlen defer func() { goutmp.Unput_utmp(utmpx) }() goutmp.Put_lastlog_entry("xs", who, pts, hname) - conn.Pproc = c.Process.Pid - //fmt.Printf("[process %d started]\n", c.Process.Pid) - log.Printf("[%s]\n", cmd) if err != nil { log.Printf("Command finished with error: %v", err) @@ -374,15 +364,12 @@ func runShellAs(who, hname, ttype, cmd string, interactive bool, //nolint:funlen } }() - // === Set up connection keepalive to client - conn.StartupKeepAlive() // goroutine, returns immediately - defer conn.ShutdownKeepAlive() - if chaffing { - conn.StartupChaff() + conn.EnableChaff() } // #gv:s/label=\"runShellAs\$4\"/label=\"deferChaffShutdown\"/ defer func() { + conn.DisableChaff() conn.ShutdownChaff() }() @@ -422,7 +409,7 @@ func runShellAs(who, hname, ttype, cmd string, interactive bool, //nolint:funlen } conn.SetStatus(xsnet.CSOType(exitStatus)) } else { - logger.LogDebug(fmt.Sprintf("*** Main proc has exited (%d) ***", c.ProcessState.ExitCode())) //nolint:errcheck + logger.LogDebug("*** Main proc has exited. ***") //nolint:errcheck // Background jobs still may be running; close the // pty anyway, so the client can return before // wg.Wait() below completes (Issue #18) @@ -529,15 +516,11 @@ func main() { //nolint:funlen,gocyclo var chaffBytesMax uint var dbg bool var laddr string - var rekeySecs uint - var remodSupported bool // true: when rekeying, switch to random cipher/hmac alg var useSystemPasswd bool flag.BoolVar(&vopt, "v", false, "show version") - flag.UintVar(&rekeySecs, "r", 300, "rekey interval in `secs`") - flag.BoolVar(&remodSupported, "R", false, "Borg Countermeasures (remodulate cipher/hmac alg on each rekey)") - flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen") //nolint:gomnd,lll + flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen") flag.StringVar(&kcpMode, "K", "unused", `set to 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`) //nolint:lll flag.BoolVar(&useSysLogin, "L", false, "use system login") flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts") @@ -572,11 +555,7 @@ func main() { //nolint:funlen,gocyclo flag.Var(&aHMACAlgs, "aH", "Allowed `HMAC`s (eg. '-aH HMACAlgA -aH HMACAlgB ...')"+` H_all H_SHA256 - H_SHA512 - H_WHIRLPOOL`) - - flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to <`file`>") - flag.StringVar(&memprofile, "memprofile", "", "write memory profile to <`file`>") + H_SHA512`) flag.Parse() @@ -592,24 +571,6 @@ func main() { //nolint:funlen,gocyclo } } - // === Profiling instrumentation - - if cpuprofile != "" { - f, err := os.Create(cpuprofile) - if err != nil { - log.Fatal("could not create CPU profile: ", err) - } - defer f.Close() - fmt.Println("StartCPUProfile()") - if err := pprof.StartCPUProfile(f); err != nil { - log.Fatal("could not start CPU profile: ", err) //nolint:gocritic - } else { - defer pprof.StopCPUProfile() - } - - go func() { http.ListenAndServe("localhost:6060", nil) }() //nolint:errcheck,gosec - } - // Enforce some sane min/max vals on chaff flags if chaffFreqMin < 2 { //nolint:gomnd chaffFreqMin = 2 @@ -651,22 +612,19 @@ func main() { //nolint:funlen,gocyclo go func() { for { sig := <-exitCh - switch sig { - case syscall.SIGTERM: //"terminated": - logger.LogNotice(fmt.Sprintf("[Got signal: %s]", sig.String())) //nolint:errcheck + switch sig.String() { + case "terminated": + logger.LogNotice(fmt.Sprintf("[Got signal: %s]", sig)) //nolint:errcheck signal.Reset() syscall.Kill(0, syscall.SIGTERM) //nolint:errcheck - case syscall.SIGINT: //"interrupt": - logger.LogNotice(fmt.Sprintf("[Got signal: %s]", sig.String())) //nolint:errcheck + case "interrupt": + logger.LogNotice(fmt.Sprintf("[Got signal: %s]", sig)) //nolint:errcheck signal.Reset() syscall.Kill(0, syscall.SIGINT) //nolint:errcheck - case syscall.SIGHUP: //"hangup": - logger.LogNotice(fmt.Sprintf("[Got signal: %s - nop]", sig.String())) //nolint:errcheck - if cpuprofile != "" || memprofile != "" { - dumpProf() - } + case "hangup": + logger.LogNotice(fmt.Sprintf("[Got signal: %s - nop]", sig)) //nolint:errcheck default: - logger.LogNotice(fmt.Sprintf("[Got signal: %s - ignored]", sig.String())) //nolint:errcheck + logger.LogNotice(fmt.Sprintf("[Got signal: %s - ignored]", sig)) //nolint:errcheck } } }() @@ -686,7 +644,6 @@ func main() { //nolint:funlen,gocyclo // Wait for a connection. // Then check if client-proposed algs are allowed conn, err := l.Accept() - //logger.LogDebug(fmt.Sprintf("l.Accept()\n")) if err != nil { log.Printf("Accept() got error(%v), hanging up.\n", err) } else { @@ -705,24 +662,6 @@ func main() { //nolint:funlen,gocyclo } else { log.Println("Accepted client") - // Only enable cipher alg changes on re-key if we were told - // to support it (launching xsd with -R), *and* the client - // proposes to use it. - if !remodSupported { - if (conn.Opts() & xsnet.CORemodulateShields) != 0 { - logger.LogDebug("[client proposed cipher/hmac remod, but we don't support it.]") - conn.Close() - continue - } - } else { - if conn.Opts()&xsnet.CORemodulateShields != 0 { - logger.LogDebug("[cipher/hmac remodulation active]") - } else { - logger.LogDebug("[cipher/hmac remodulation inactive]") - } - } - conn.RekeyHelper(rekeySecs) - // Set up chaffing to client // Will only start when runShellAs() is called // after stdin/stdout are hooked up @@ -732,7 +671,6 @@ func main() { //nolint:funlen,gocyclo // The loop then returns to accepting, so that // multiple connections may be served concurrently. go func(hc *xsnet.Conn) (e error) { - defer hc.ShutdownRekey() defer hc.Close() // Start login timeout here and disconnect if user/pass phase stalls @@ -842,7 +780,7 @@ func main() { //nolint:funlen,gocyclo hname := goutmp.GetHost(addr.String()) logger.LogNotice(fmt.Sprintf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname)) //nolint:errcheck token := GenAuthToken(string(rec.Who()), string(rec.ConnHost())) - tokenCmd := fmt.Sprintf("echo %q | tee -a ~/%s", token, xsnet.XS_ID_AUTHTOKFILE) + tokenCmd := fmt.Sprintf("echo %q | 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 @@ -938,23 +876,3 @@ func main() { //nolint:funlen,gocyclo } //endfor //logger.LogNotice(fmt.Sprintln("[Exiting]")) //nolint:errcheck } - -func dumpProf() { - if cpuprofile != "" { - pprof.StopCPUProfile() - } - - if memprofile != "" { - f, err := os.Create(memprofile) - if err != nil { - log.Fatal("could not create memory profile: ", err) - } - defer f.Close() - runtime.GC() // get up-to-date statistics - if err := pprof.WriteHeapProfile(f); err != nil { - log.Fatal("could not write memory profile: ", err) //nolint:gocritic - } - } - - //os.Exit(status) -} diff --git a/xsnet/chan.go b/xsnet/chan.go index 5847372..6e5a0f3 100644 --- a/xsnet/chan.go +++ b/xsnet/chan.go @@ -22,11 +22,10 @@ import ( "blitter.com/go/cryptmt" "blitter.com/go/hopscotch" - "blitter.com/go/xs/logger" "github.com/aead/chacha20/chacha" "golang.org/x/crypto/blowfish" "golang.org/x/crypto/twofish" - whirlpool "github.com/jzelinskie/whirlpool" + // hash algos must be manually imported thusly: // (Would be nice if the golang pkg docs were more clear // on this...) @@ -58,19 +57,9 @@ func expandKeyMat(keymat []byte, blocksize int) []byte { return keymat } -// Choose a cipher and hmac alg from supported sets, given two uint8 values -func getNewStreamAlgs(cb uint8, hb uint8) (config uint32) { - // Get new cipher and hash algs (clamped to valid values) based on - // the input rekeying data - c := (cb % CAlgNoneDisallowed) - h := (hb % HmacNoneDisallowed) - config = uint32(h<<8) | uint32(c) - logger.LogDebug(fmt.Sprintf("[Chose new algs [%d:%d]", h, c)) - return -} - -// (Re-)initialize the keystream and hmac state for an xsnet.Conn, returning -// a cipherStream and hash +/* Support functionality to set up encryption after a channel has +been negotiated via xsnet.go +*/ func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err error) { var key []byte var block cipher.Block @@ -157,9 +146,6 @@ func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err er if !halg.Available() { log.Fatal("hash not available!") } - case HmacWHIRLPOOL: - log.Printf("[hash HmacWHIRLPOOL (%d)]\n", hopts) - mc = whirlpool.New() default: log.Printf("[invalid hmac (%d)]\n", hopts) fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts) diff --git a/xsnet/consts.go b/xsnet/consts.go index 9c58d6e..30833a2 100644 --- a/xsnet/consts.go +++ b/xsnet/consts.go @@ -52,8 +52,6 @@ const ( CSEKEXAlgDenied // server rejected proposed KEX alg CSECipherAlgDenied // server rejected proposed Cipher alg CSEHMACAlgDenied // server rejected proposed HMAC alg - CSEConnDead // connection keepalives expired - CSELoginTimeout ) // Extended (>255 UNIX exit status) codes @@ -69,6 +67,9 @@ const ( CSOExitStatus // Remote cmd exit status CSOChaff // Dummy packet, do not pass beyond decryption + // Client side errors + CSOLoginTimeout + // Tunnel setup/control/status CSOTunSetup // client -> server tunnel setup request (dstport) CSOTunSetupAck // server -> client tunnel setup ack @@ -77,8 +78,6 @@ const ( CSOTunKeepAlive // client tunnel heartbeat CSOTunDisconn // server -> client: tunnel rport disconnected CSOTunHangup // client -> server: tunnel lport hung up - CSOKeepAlive // bidir keepalive packet to monitor main connection - CSORekey // TODO: rekey/re-select session cipher/hash algs ) // TunEndpoint.tunCtl control values - used to control workers for client @@ -98,7 +97,7 @@ const ( // Channel status Op byte type (see CSONone, ... and CSENone, ...) type CSOType uint32 -// TODO: this should be small (max unfragmented packet size?) +//TODO: this should be small (max unfragmented packet size?) const MAX_PAYLOAD_LEN = 2*1024*1024*1024 - 1 // Session symmetric crypto algs @@ -119,27 +118,8 @@ type CSCipherAlg uint32 const ( HmacSHA256 = iota HmacSHA512 - HmacWHIRLPOOL HmacNoneDisallowed ) -// Conn opts outside of basic kex/cipher/hmac connect config -const ( - CONone = iota - CORemodulateShields // if set, rekeying also reselects random cipher/hmac alg -) - -type COValue uint32 - // Available HMACs for hkex.Conn type CSHmacAlg uint32 - -// Some bounds-checking consts -const ( - REKEY_SECS_MIN = 1 - REKEY_SECS_MAX = 28800 // 8 hours - CHAFF_FREQ_MSECS_MIN = 1 - CHAFF_FREQ_MSECS_MAX = 300000 // 5 minutes -) - -const XS_ID_AUTHTOKFILE = ".config/xs/.xs_id" diff --git a/xsnet/net.go b/xsnet/net.go index 8727437..9ba7cd8 100644 --- a/xsnet/net.go +++ b/xsnet/net.go @@ -9,7 +9,7 @@ package xsnet -// Implementation of key-exchange-wrapped versions of the golang standard +// Implementation of HKEx-wrapped versions of the golang standard // net package interfaces, allowing clients and servers to simply replace // 'net.Dial' and 'net.Listen' with 'hkex.Dial' and 'hkex.Listen' // (though some extra methods are implemented and must be used @@ -39,7 +39,6 @@ import ( "net" "strings" "sync" - "syscall" "time" hkex "blitter.com/go/herradurakex" @@ -65,6 +64,7 @@ type ( // see: https://en.wikipedia.org/wiki/chaff_(countermeasure) ChaffConfig struct { shutdown bool //set to inform chaffHelper to shut down + enabled bool msecsMin uint //msecs min interval msecsMax uint //msecs max interval szMax uint // max size in bytes @@ -87,11 +87,8 @@ type ( Rows uint16 Cols uint16 - keepalive uint // if this reaches zero, conn is considered dead - rekey uint // if nonzero, rekeying interval in seconds - Pproc int // proc ID of command run on this conn - chaff ChaffConfig - tuns *map[uint16](*TunEndpoint) + chaff ChaffConfig + tuns *map[uint16](*TunEndpoint) closeStat *CSOType // close status (CSOExitStatus) r cipher.Stream //read cipherStream @@ -177,8 +174,6 @@ func (h *CSHmacAlg) String() string { return "H_SHA256" case HmacSHA512: return "H_SHA512" - case HmacWHIRLPOOL: - return "H_WHIRLPOOL" default: return "H_ERR_UNK" } @@ -243,7 +238,7 @@ func (hc *Conn) SetConnOpts(copts uint32) { // // Consumers of this lib may use this for protocol-level options not part // of the KEx or encryption info used by the connection. -func (hc *Conn) Opts() uint32 { +func (hc Conn) Opts() uint32 { return hc.opts } @@ -313,9 +308,9 @@ func _new(kexAlg KEXAlg, conn *net.Conn) (hc *Conn, e error) { // applyConnExtensions processes optional Dial() negotiation // parameters. See also getkexalgnum(). // -// # Currently defined extension values +// Currently defined extension values // -// # KEx algs +// KEx algs // // KEX_HERRADURA256 KEX_HERRADURA512 KEX_HERRADURA1024 KEX_HERRADURA2048 // @@ -323,11 +318,11 @@ func _new(kexAlg KEXAlg, conn *net.Conn) (hc *Conn, e error) { // // KEX_NEWHOPE KEX_NEWHOPE_SIMPLE // -// # Session (symmetric) crypto +// Session (symmetric) crypto // // C_AES_256 C_TWOFISH_128 C_BLOWFISH_128 C_CRYPTMT1 C_CHACHA20_12 C_HOPSCOTCH // -// # Session HMACs +// Session HMACs // // H_SHA256 H_SHA512 func (hc *Conn) applyConnExtensions(extensions ...string) { @@ -365,13 +360,6 @@ func (hc *Conn) applyConnExtensions(extensions ...string) { log.Println("[extension arg = H_SHA512]") hc.cipheropts &= (0xFFFF00FF) hc.cipheropts |= (HmacSHA512 << 8) - case "H_WHIRLPOOL": - log.Println("[extension arg = H_WHIRLPOOL]") - hc.cipheropts &= (0xFFFF00FF) - hc.cipheropts |= (HmacWHIRLPOOL << 8) - case "OPT_REMOD": - log.Println("[extension arg = OPT_REMOD]") - hc.opts |= CORemodulateShields //default: // log.Printf("[Dial ext \"%s\" ignored]\n", s) } @@ -894,12 +882,12 @@ func HKExAcceptSetup(c *net.Conn, hc *Conn) (err error) { // Dial as net.Dial(), but with implicit key exchange to set up secure // channel on connect // -// Can be called like net.Dial(), defaulting to C_AES_256/H_SHA256, -// or additional extensions can be passed amongst the following: +// Can be called like net.Dial(), defaulting to C_AES_256/H_SHA256, +// or additional extensions can be passed amongst the following: // -// "C_AES_256" | "C_TWOFISH_128" | ... +// "C_AES_256" | "C_TWOFISH_128" | ... // -// "H_SHA256" | "H_SHA512" | ... +// "H_SHA256" | "H_SHA512" | ... // // See go doc -u xsnet.applyConnExtensions func Dial(protocol string, ipport string, extensions ...string) (hc Conn, err error) { @@ -983,7 +971,7 @@ func Dial(protocol string, ipport string, extensions ...string) (hc Conn, err er // Close a hkex.Conn func (hc *Conn) Close() (err error) { - hc.ShutdownChaff() + hc.DisableChaff() s := make([]byte, 4) binary.BigEndian.PutUint32(s, uint32(*hc.closeStat)) log.Printf("** Writing closeStat %d at Close()\n", *hc.closeStat) @@ -1096,7 +1084,7 @@ func (hl HKExListener) Close() error { return hl.l.Close() } -// Addr returns the listener's network address. +// Addr returns a the listener's network address. // // See go doc net.Listener.Addr func (hl HKExListener) Addr() net.Addr { @@ -1121,7 +1109,7 @@ func (hl *HKExListener) Accept() (hc Conn, err error) { return Conn{}, err } - logger.LogDebug(fmt.Sprintf("[net.Listener Accepted %v]\n", c.RemoteAddr())) + logger.LogDebug(fmt.Sprintln("[net.Listener Accepted]")) } // Read KEx alg proposed by client var kexAlg KEXAlg @@ -1208,7 +1196,7 @@ func (hl *HKExListener) Accept() (hc Conn, err error) { // packet processing. // // See go doc io.Reader -func (hc *Conn) Read(b []byte) (n int, err error) { +func (hc Conn) Read(b []byte) (n int, err error) { for { if hc.dBuf.Len() > 0 { break @@ -1226,8 +1214,7 @@ func (hc *Conn) Read(b []byte) (n int, err error) { return 0, io.EOF } if strings.HasSuffix(err.Error(), "use of closed network connection") { - logger.LogDebug(fmt.Sprintln("[Client hung up(1)]")) - //!rlm hc.SetStatus(CSENone) //FIXME: re-examine this (exit 9 w/o it - 2023-11-05) + logger.LogDebug(fmt.Sprintln("[Client hung up]")) return 0, io.EOF } etxt := fmt.Sprintf("** Failed read:%s (%s) **", "ctrlStatOp", err) @@ -1250,7 +1237,7 @@ func (hc *Conn) Read(b []byte) (n int, err error) { return 0, io.EOF } if strings.HasSuffix(err.Error(), "use of closed network connection") { - logger.LogDebug(fmt.Sprintln("[Client hung up(2)]")) + logger.LogDebug(fmt.Sprintln("[Client hung up]")) return 0, io.EOF } etxt := fmt.Sprintf("** Failed read:%s (%s) **", "HMAC", err) @@ -1266,7 +1253,7 @@ func (hc *Conn) Read(b []byte) (n int, err error) { return 0, io.EOF } if strings.HasSuffix(err.Error(), "use of closed network connection") { - logger.LogDebug(fmt.Sprintln("[Client hung up(3)]")) + logger.LogDebug(fmt.Sprintln("[Client hung up]")) return 0, io.EOF } etxt := fmt.Sprintf("** Failed read:%s (%s) **", "payloadLen", err) @@ -1289,7 +1276,7 @@ func (hc *Conn) Read(b []byte) (n int, err error) { return 0, io.EOF } if strings.HasSuffix(err.Error(), "use of closed network connection") { - logger.LogDebug(fmt.Sprintln("[Client hung up(4)]")) + logger.LogDebug(fmt.Sprintln("[Client hung up]")) return 0, io.EOF } etxt := fmt.Sprintf("** Failed read:%s (%s) **", "payloadBytes", err) @@ -1350,22 +1337,6 @@ func (hc *Conn) Read(b []byte) (n int, err error) { case CSOChaff: // Throw away pkt if it's chaff (ie., caller to Read() won't see this data) log.Printf("[Chaff pkt, discarded (len %d)]\n", decryptN) - case CSOKeepAlive: - //logger.LogDebug(fmt.Sprintf("[got keepAlive pkt, discarded (len %d)]\n", decryptN)) - // payload of keepalive (2 bytes) is not currently used (0x55aa fixed) - _ = binary.BigEndian.Uint16(payloadBytes[0:2]) - hc.ResetKeepAlive() - case CSORekey: - // rekey - //logger.LogDebug(fmt.Sprintf("[Got rekey [%02x %02x %02x ...]\n", - // payloadBytes[0], payloadBytes[1], payloadBytes[2])) - rekeyData := payloadBytes - if (hc.opts & CORemodulateShields) != 0 { - hc.Lock() - hc.cipheropts = getNewStreamAlgs(rekeyData[0], rekeyData[1]) - hc.Unlock() - } - hc.r, hc.rm, err = hc.getStream(rekeyData) case CSOTermSize: fmt.Sscanf(string(payloadBytes), "%d %d", &hc.Rows, &hc.Cols) log.Printf("[TermSize pkt: rows %v cols %v]\n", hc.Rows, hc.Cols) @@ -1452,9 +1423,6 @@ func (hc *Conn) Read(b []byte) (n int, err error) { // let the server know to hang up on Dial()ed server rports. _ = binary.BigEndian.Uint16(payloadBytes[0:2]) //logger.LogDebug(fmt.Sprintf("[Server] Got CSOTunKeepAlive")) - // though CSOTunKeepAlive sends an endp (uint16), we don't use it, - // preferring to refresh *all* tunnels on the message. - // (?rlm 2023-11-04 -- TODO: verify this, it's been a while.) for _, t := range *hc.tuns { hc.Lock() t.KeepAlive = 0 @@ -1482,18 +1450,13 @@ func (hc *Conn) Read(b []byte) (n int, err error) { // Write a byte slice // // See go doc io.Writer -func (hc *Conn) Write(b []byte) (n int, err error) { - //logger.LogDebug("[+Write]") +func (hc Conn) Write(b []byte) (n int, err error) { n, err = hc.WritePacket(b, CSONone) - //logger.LogDebug("[-Write]") return n, err } // Write a byte slice with specified ctrlStatOp byte func (hc *Conn) WritePacket(b []byte, ctrlStatOp byte) (n int, err error) { - hc.Lock() - defer hc.Unlock() - //log.Printf("[Encrypting...]\r\n") var hmacOut []uint8 var payloadLen uint32 @@ -1521,6 +1484,15 @@ func (hc *Conn) WritePacket(b []byte, ctrlStatOp byte) (n int, err error) { b = append([]byte{byte(padSide)}, append([]byte{byte(padLen)}, append(b, padBytes...)...)...) } + // N.B. Originally this Lock() surrounded only the + // calls to binary.Write(hc.c ..) however there appears + // to be some other unshareable state in the Conn + // struct that must be protected to serialize main and + // chaff data written to it. + // + // Would be nice to determine if the mutex scope + // could be tightened. + hc.Lock() payloadLen = uint32(len(b)) if hc.logPlainText { log.Printf(" >:ptext:\r\n%s\r\n", hex.Dump(b[0:payloadLen])) @@ -1578,6 +1550,7 @@ func (hc *Conn) WritePacket(b []byte, ctrlStatOp byte) (n int, err error) { } else { //fmt.Println("[a]WriteError!") } + hc.Unlock() if err != nil { log.Println(err) @@ -1592,180 +1565,53 @@ func (hc *Conn) WritePacket(b []byte, ctrlStatOp byte) (n int, err error) { return retN, err } -func (hc *Conn) StartupChaff() { +func (hc *Conn) EnableChaff() { hc.chaff.shutdown = false + hc.chaff.enabled = true log.Println("Chaffing ENABLED") hc.chaffHelper() } +func (hc *Conn) DisableChaff() { + hc.chaff.enabled = false + log.Println("Chaffing DISABLED") +} + func (hc *Conn) ShutdownChaff() { - hc.Lock() hc.chaff.shutdown = true - hc.Unlock() log.Println("Chaffing SHUTDOWN") } func (hc *Conn) SetupChaff(msecsMin uint, msecsMax uint, szMax uint) { - // Enforce bounds on chaff frequency and pkt size - hc.Lock() - if hc.chaff.msecsMin < CHAFF_FREQ_MSECS_MIN { - hc.chaff.msecsMin = CHAFF_FREQ_MSECS_MIN - } - if hc.chaff.msecsMax > CHAFF_FREQ_MSECS_MAX { - hc.chaff.msecsMax = CHAFF_FREQ_MSECS_MAX - } - hc.Unlock() - hc.chaff.msecsMin = msecsMin //move these to params of chaffHelper() ? hc.chaff.msecsMax = msecsMax hc.chaff.szMax = szMax } -func (hc *Conn) ShutdownRekey() { - hc.Lock() - hc.rekey = 0 - hc.Unlock() -} - -func (hc *Conn) RekeyHelper(intervalSecs uint) { - if intervalSecs < REKEY_SECS_MIN { - intervalSecs = REKEY_SECS_MIN - } - if intervalSecs > REKEY_SECS_MAX { - intervalSecs = REKEY_SECS_MAX - } - - go func() { - hc.Lock() - hc.rekey = intervalSecs - hc.Unlock() - - for { - hc.Lock() - rekey := hc.rekey - hc.Unlock() - - if rekey != 0 { - jitter := rand.Intn(int(rekey)) / 4 - rekey = rekey - uint(jitter) - if rekey < 1 { - rekey = 1 - } - - //logger.LogDebug(fmt.Sprintf("[rekeyHelper Loop]\n")) - time.Sleep(time.Duration(rekey) * time.Second) - - // Send rekey to other end - rekeyData := make([]byte, 64) - _, err := crand.Read(rekeyData) - //logger.LogDebug(fmt.Sprintf("[rekey [%02x %02x %02x ...]\n", - // rekeyData[0], rekeyData[1], rekeyData[2])) - //logger.LogDebug("[+rekeyHelper]") - _, err = hc.WritePacket(rekeyData, CSORekey) - hc.Lock() - if (hc.opts & CORemodulateShields) != 0 { - hc.cipheropts = getNewStreamAlgs(rekeyData[0], rekeyData[1]) - } - hc.w, hc.wm, err = hc.getStream(rekeyData) - //logger.LogDebug("[-rekeyHelper]") - hc.Unlock() - if err != nil { - log.Printf("[rekey WritePacket err! (%v) rekey dying ...]\n", err) - return - } - } else { - return - } - } - }() -} - // Helper routine to spawn a chaffing goroutine for each Conn func (hc *Conn) chaffHelper() { go func() { - var nextDuration int for { - //logger.LogDebug(fmt.Sprintf("[chaffHelper Loop]\n")) - hc.Lock() - shutdown := hc.chaff.shutdown - hc.Unlock() - if !shutdown { + var nextDuration int + if hc.chaff.enabled { var bufTmp []byte bufTmp = make([]byte, rand.Intn(int(hc.chaff.szMax))) min := int(hc.chaff.msecsMin) nextDuration = rand.Intn(int(hc.chaff.msecsMax)-min) + min _, _ = rand.Read(bufTmp) - //logger.LogDebug("[+chaffHelper]") _, err := hc.WritePacket(bufTmp, CSOChaff) - //logger.LogDebug("[-chaffHelper]") if err != nil { - log.Println("[ *** error - chaffHelper shutting down *** ]") - hc.Lock() - hc.chaff.shutdown = true - hc.Unlock() + log.Println("[ *** error - chaffHelper quitting *** ]") + hc.chaff.enabled = false break } - } else { - log.Println("[ *** chaffHelper shutting down *** ]") - break } time.Sleep(time.Duration(nextDuration) * time.Millisecond) - } - }() -} - -func (hc *Conn) StartupKeepAlive() { - hc.ResetKeepAlive() - log.Println("KeepAlive ENABLED") - hc.keepaliveHelper() -} - -func (hc *Conn) ShutdownKeepAlive() { - log.Println("Conn SHUTDOWN") - hc.Close() -} - -func (hc *Conn) ResetKeepAlive() { - hc.Lock() - hc.keepalive = 3 - hc.Unlock() - log.Println("KeepAlive RESET") -} - -// Helper routine to spawn a keepalive goroutine for each Conn -func (hc *Conn) keepaliveHelper() { - go func() { - for { - nextDuration := 10000 - bufTmp := []byte{0x55, 0xaa} - //logger.LogDebug("[+keepaliveHelper]") - _, err := hc.WritePacket(bufTmp, CSOKeepAlive) - //logger.LogDebug("[-keepaliveHelper]") - //logger.LogDebug(fmt.Sprintf("[keepalive]\n")) - if err != nil { - logger.LogDebug(fmt.Sprintf("[ *** error - keepaliveHelper quitting *** ]\n")) - break - } - time.Sleep(time.Duration(nextDuration) * time.Millisecond) - hc.Lock() - hc.keepalive -= 1 - hc.Unlock() - //logger.LogDebug(fmt.Sprintf("[keepAlive is now %d]\n", hc.keepalive)) - - //if rand.Intn(8) == 0 { - // hc.keepalive = 0 - //} - - if hc.keepalive == 0 { - logger.LogDebug(fmt.Sprintf("*** keepaliveHelper shutting down\n")) - hc.SetStatus(CSEConnDead) - hc.ShutdownKeepAlive() - if hc.Pproc != 0 { - //fmt.Printf("[pid %d needs to be killed]\n", hc.Pproc) - syscall.Kill(hc.Pproc, syscall.SIGABRT) //nolint:errcheck - } - break - } + if hc.chaff.shutdown { + log.Println("*** chaffHelper shutting down") + break + } + } }() } diff --git a/xsnet/tun.go b/xsnet/tun.go index ebddcae..ac63047 100644 --- a/xsnet/tun.go +++ b/xsnet/tun.go @@ -37,8 +37,6 @@ type ( // client starts worker to receive/send data using lport // ... client disconnects: sends remhost [CSOTunClose:rport] // ... or server disconnects: sends client [CSOTunClose:lport] - // ... or server disconnects: due to client failing to send TunKeepAlive - // events for too long // server at any time sends [CSOTunRefused:rport] if daemon died // -- diff --git a/xspasswd/xspasswd.go b/xspasswd/xspasswd.go index ff18083..8cb41a9 100644 --- a/xspasswd/xspasswd.go +++ b/xspasswd/xspasswd.go @@ -1,5 +1,5 @@ // Util to generate/store passwords for users in a file akin to /etc/passwd -// suitable for the xs server, using bcrypt. +// suitable for the demo hkexsh server, using bcrypt. // // Copyright (c) 2017-2020 Russell Magee // Licensed under the terms of the MIT license (see LICENSE.mit in this