Compare commits

...

151 commits

Author SHA1 Message Date
Russ Magee
71f2bdfe8d Fix conflict on cptest.sha1sum 2021-01-15 01:33:02 -08:00
Russ Magee
c1ab12dc1b Updated cptest.sha1sum 2021-01-15 01:28:59 -08:00
Russ Magee
435e2b6380 Updated cptest.sha1sum 2021-01-15 01:23:01 -08:00
Russ Magee
e8986d3464 Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2021-01-15 01:15:27 -08:00
Russ Magee
c511af6c13 Removed static cptest/ dir, now managed by CI tests 2021-01-15 01:15:08 -08:00
Russ Magee
bcc07a8fac Removed static cptest/ dir, now managed by CI tests 2021-01-15 00:31:36 -08:00
Russ Magee
242d39192c Bump version to v0.9.2 2021-01-13 00:59:27 -08:00
Russ Magee
3fc74bae70 Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2021-01-13 00:44:20 -08:00
Russ Magee
287f9d1035 use crypto/rand in key spots (for KEM seeds) rather than math/rand 2021-01-13 00:43:26 -08:00
Russ Magee
f2929120b6 use crypto/rand in key spots (for KEM seeds) rather than math/rand
Signed-off-by: Russ Magee <rmagee@gmail.com>
2021-01-12 23:45:58 -08:00
Russ Magee
4e8e064c74 Bump version to v0.9.0 2021-01-10 22:10:18 -08:00
Russ Magee
c23edc6874 Fixed FrodoKEM neg bug (sending cipheropts,opts order)
Also tweaked pad size random to use full range; removed some junk logging
2021-01-10 22:04:52 -08:00
Russ Magee
3193ede825 **BREAKING** Switch to EtA per Krawczyk2001
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-12-15 22:09:18 -08:00
Russ Magee
640e59be4f Comment re: AtE vs. EtA 2020-12-15 21:15:15 -08:00
Russ Magee
5f42894ac2 Added FrodoKEM 2020-12-10 19:21:04 -08:00
Russ Magee
d0f76fd3b4 Added sysv init script
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-10-27 13:22:50 -07:00
Russ Magee
035df99069 Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-09-13 22:31:11 -07:00
Russ Magee
0f22f35e7c Tweaked cleanup of .xs_id again
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-09-13 21:51:18 -07:00
Russ Magee
5aca04e1d5 Fix to the fix (sigh) for .xs_id test 2020-09-13 21:36:48 -07:00
Russ Magee
569230a2af Fixed .xs_id file save/restore during tests 2020-09-13 21:29:09 -07:00
Russtopia
01aa67d1f7 Update 'bacillus/ci_pushbuild.sh' 2020-08-23 23:33:58 -07:00
Russtopia
7901cd8809 Update 'bacillus/ci_pushbuild.sh' 2020-08-21 19:49:17 -07:00
Russtopia
611b6853b2 Update 'bacillus/ci_pushbuild.sh' 2020-08-21 19:47:37 -07:00
Russtopia
61b7a80e00 Update 'README.md' 2020-08-21 19:42:15 -07:00
Russ Magee
b3a359a84e Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2020-08-08 02:00:25 -07:00
Russ Magee
1943b2314a 2020 Copyright update; minor comment typo fixes
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-08-08 01:59:27 -07:00
Russ Magee
499eaa665b Minor doc typo fixes
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-08-08 01:54:46 -07:00
Russ Magee
eb373ff37b Fixed misuse of iota in xsnet/consts.go that broke channel status opcodes
Cleaned up var declarations and added some greppable comments to show xs setup & flow
2020-07-24 23:10:43 -07:00
Russ Magee
1b01ed14f2 Fixed non-MSYS xsd install
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-07-21 22:10:04 -07:00
Russ Magee
09055dffe6 Resynced w/master
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-07-21 22:08:46 -07:00
Russ Magee
f480c8cf78 Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-07-21 21:57:31 -07:00
Russ Magee
69a3fb5d08 Fixed login timeout term handling/restoreState * NOTE breaking change to exit codes
Also some value => ref fixes for Conn receiver methods

Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-07-21 21:52:58 -07:00
Russ Magee
3522976ef7 Correct implicit username for MSYS2 eg. xs @server.com
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-06-04 00:53:20 -07:00
Russ Magee
f0a2bb0295 Updates to fix MSYSTEM=MSYS build and install targets
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-06-03 21:39:42 -07:00
Russ Magee
1addd18806 Fixed GOOS=windows MSYSTEM=MSYS install
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-05-24 21:15:28 -07:00
Russ Magee
57b71e7925 FreeBSD note into README.md
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-05-18 23:59:33 -07:00
Russtopia
58c87f7e0e Update 'README.md' 2020-05-18 23:27:40 -07:00
Russ Magee
769df46fb1 Fixed freeBSD gmake note
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-04-26 05:40:09 +00:00
Russ Magee
51488fab6d Allow override on cmdline of MAKE variable to support gmake in freeBSD or via both 'make and 'gmake' names in Linux
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-04-28 17:49:09 -07:00
Russ Magee
0962c0b811 Bumped version, use latest goutmp v1.0.5
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-04-25 19:45:30 -07:00
Russ Magee
3bea3e6ebb Updated go.{mod,sum}
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-04-25 19:35:24 -07:00
Russ Magee
1d30b8582d Fixes for build constraints, MakeRaw()/ReadPassword() etc. fd arg type
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-04-25 18:42:29 -07:00
Russ Magee
47a3c6f1b8 Fixes for int vs. uintptr args (linux vs. freebsd) 2020-04-26 01:15:44 +00:00
Russ Magee
6bb4b90c87 Fixes for int vs. uintptr args (linux vs. freebsd) 2020-04-26 01:14:19 +00:00
Russ Magee
531c846899 Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2020-04-26 01:04:11 +00:00
Russ Magee
cba36b66f1 FreeBSD 12 support (NOTE: xc not yet functional)
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-04-26 01:03:29 +00:00
Russtopia
5448dbc7b9 Update 'README.md' wrt. default xsd passwd file mode 2020-04-16 00:19:03 -07:00
Russ Magee
09c2408c06 Tweaked 'pv' progress bar output format options
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-04-03 20:08:45 -07:00
Russtopia
4686361ece Update 'CODE_OF_CONDUCT.md' 2020-03-29 23:22:22 -07:00
Russ Magee
e803d3da09 Bumped version, xc reports -v correctly
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-26 20:25:01 -08:00
Russ Magee
0c1d5e036e Merge tag 'v0.8.20' into xc-bigfile-EOF 2020-02-26 20:08:46 -08:00
Russ Magee
3b0ddba7f2 Merge tag 'v0.8.19' into xc-bigfile-EOF 2020-02-26 20:06:52 -08:00
Russ Magee
b3ebd0db17 Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-25 23:33:09 -08:00
Russtopia
d1076c9bd9 Update 'README.md' with xc progress info
'pv' util progress notes added
2020-02-25 23:25:39 -08:00
Russ Magee
2e16c9a692 Remote->Client cp with pv status TODO: Remote total bytesize estimate
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-25 23:08:42 -08:00
Russ Magee
9d674315f9 Merge branch 'master' of ssh://blitter.com/var/git/xs 2020-02-25 21:17:37 -08:00
Russ Magee
64a314df11 Client->Remote xc progress indication using pv
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-25 21:16:55 -08:00
Russtopia
f6910162b4 Update 'README.md'
ChaCha20 support
2020-02-24 16:50:55 -08:00
Russ Magee
6ac6f02b3b Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-23 21:52:45 -08:00
Russ Magee
f3bf6d9041 Added login timeout (30s)
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-23 21:52:20 -08:00
Russ Magee
138f855a82 Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-21 20:48:23 -08:00
Russ Magee
e9aa0072a5 Initial aead/chacha20 support (ChaCha20_12)
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-21 17:21:19 -08:00
Russ Magee
8de76520e4 Added session_test.go
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-19 13:27:56 -08:00
Russ Magee
e84f432033 auth.go: added new unit tests
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-18 13:41:44 -08:00
Russ Magee
654de563dc auth.go: AuthCtx added to structure mockable entities for testing
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-18 10:53:04 -08:00
Russ Magee
733fc46d86 Added unit tests for auth.go: AuthUserByToken
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-17 23:14:37 -08:00
Russ Magee
d18396f535 Added unit tests for auth.go
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-17 21:17:11 -08:00
Russ Magee
e320725e07 Added unit tests for auth.go
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-17 21:16:10 -08:00
Russ Magee
d4a3521437 Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-17 00:06:38 -08:00
Russ Magee
c2c43c1c50 Fix for issue #23: null panic if logger obj is null due no active syslog sink
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-17 00:02:28 -08:00
Russ Magee
9ffeab2456 Tagged version v0.8.16
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-06 19:15:52 -08:00
Russ Magee
0eb785a9dc Removed WANDERER exp c_alg
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-06 19:12:45 -08:00
Russ Magee
58652ce935 Updated go.mod deps
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-06 19:02:34 -08:00
Russ Magee
1b964a4066 Updated intf to cryptmt
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-06 18:56:36 -08:00
Russ Magee
3eee573231 Resync w/cryptmt, wanderer repos
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-02-05 21:26:03 -08:00
Russ Magee
d3d7ac9aae Merge branch 'master' into xc-bigfile-EOF 2020-01-30 23:33:41 -08:00
Russ Magee
6c049dde08 More throughput-friendly hack (just delay at very end vs. each packet)
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 23:32:36 -08:00
Russ Magee
862c0c3d7f Hack to throttle data overrun (sender->rcvr) during large xc operations
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 21:14:35 -08:00
Russ Magee
15a1a39d81 Fixed xsd.initrc $ARGS passing 2020-01-30 17:38:39 -08:00
Russ Magee
977380e61f Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 17:22:36 -08:00
Russ Magee
bebfd1cb1f Removed redundant assigns of stdin/out/err to conn prior to ptmx setup 2020-01-30 17:14:56 -08:00
Russ Magee
399babd62a Removed stdout redir in push build script
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 13:01:10 -08:00
Russ Magee
0303b394c8 fixed /home/russtopia/bin:/opt/eclipse:/usr/lib/go/bin:/home/russtopia/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/bin in push build script
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 12:58:08 -08:00
Russ Magee
0933aa4ea5 Added lint stage to push script
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 12:55:15 -08:00
Russ Magee
d3ebcab1d4 Added golangci-lint config
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 12:50:38 -08:00
Russ Magee
89b6e8bce7 Version tags now reflect mod/vendor build setup.
Switched 'make lint' to use golangci-lint as gometalinter is deprecated.

Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 12:40:18 -08:00
Russ Magee
eb9ce0e0e2 Updated imports and vendor/ for local fork of schwanenlied.me crypto (chacha20, newhope, kyber)
Signed-off-by: Russ Magee <rmagee@gmail.com>
2020-01-30 12:18:39 -08:00
Russ Magee
d75b419c7a Merge branch 'master' of ssh://blitter.com/var/git/xs 2020-01-29 17:25:47 -08:00
Russ Magee
f5be3578a8 1/3 Updated Makefile to allow VENDOR flag (adds -vendor to version string)
2/3 Added vendor/ dir to lock down dependent pkg versions.
The author of git.schwanenlied.me/yawning/{chacha20,newhope,kyber}.git has copied
their repos to gitlab.com/yawning/ but some imports of chacha20 from newhope still
inconsistently refer to git.schwanenlied.me/, breaking build.
Licenses for chacha20 also changed from CC0 to AGPL, which may or may not be an
issue. Until the two aforementioned issues are resolved, locking to last-good
versions is probably the best way forward for now.

To build with vendored deps, use make VENDOR=1 clean all

3/3 Moved body of CI push script into bacillus/
2020-01-29 17:23:44 -08:00
Russ Magee
e55b560230 Introduced bacillus ci_pushbuild script 2020-01-29 16:33:12 -08:00
Russ Magee
d6cd51c79f Moved body of CI push script into project from bacillus 2020-01-29 15:57:43 -08:00
Russ Magee
caac02a77b 1/2 Updated Makefile to allow VENDOR flag (adds -vendor to version string)
2/2 Added vendor/ dir to lock down dependent pkg versions.
The author of git.schwanenlied.me/yawning/{chacha20,newhope,kyber}.git has copied
their repos to gitlab.com/yawning/ but some imports of chacha20 from newhope still
inconsistently refer to git.schwanenlied.me/, breaking build.
Licenses for chacha20 also changed from CC0 to AGPL, which may or may not be an
issue. Until the two aforementioned issues are resolved, locking to last-good
versions is probably the best way forward for now.

To build with vendored deps, use make VENDOR=1 clean all
2020-01-29 13:55:38 -08:00
Russ Magee
7fe915450b Bumped tag 2020-01-24 18:50:33 -08:00
Russ Magee
3be1243bf9 Updated parms to test WANDERER alg to set sboxUpdate mode 2020-01-24 18:49:34 -08:00
Russ Magee
7ce725c7d2 Bumped version -> v0.8.10 2020-01-08 17:16:24 -08:00
Russ Magee
4e0ddf282a Comments on main() xsd 2019-12-19 20:34:05 -08:00
Russ Magee
9f956cff62 branch temp version 2019-12-19 20:04:22 -08:00
Russ Magee
f8f4bcbe77 go.mod updates for gopkg.in/hlandau/passlib.v1 2019-12-19 20:03:32 -08:00
Russ Magee
faf4d5c50a Add (as default) option to use system shadow passwds 2019-12-19 20:01:39 -08:00
Russ Magee
1f84bc95ff Bumped version 2019-12-15 12:27:50 -08:00
Russ Magee
d4f50bfdc0 xsd: Added -aK,-aC,-aH to control accepted client proposals 2019-12-15 11:38:04 -08:00
Russ Magee
9b90c0558e Fixed leftover hkexsh -> xs refs in xspasswd 2019-12-11 17:37:56 -08:00
Russ Magee
8dbb10f324 Fixed xc mode detection
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-10-29 23:50:27 -07:00
Russ Magee
b19687c80b The Great Renaming: hkexsh -> xs (Xperimental Shell)
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-10-29 23:11:03 -07:00
Russ Magee
423410bb40 WIP integrating experimental WANDERER alg 2019-09-27 09:44:57 -07:00
Russ Magee
a990c5e1f3 Cleaned up flag strings
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-09-14 23:47:41 -07:00
Russ Magee
3b73c6d731 Merge branch 'master' into kcp-udp 2019-09-14 22:56:19 -07:00
Russ Magee
ba52356e6c Made default deeper in init funcs HERRADURAKEX512
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-09-14 22:56:03 -07:00
Russ Magee
6483c8cc33 Version -kcp
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-08-22 23:32:56 -07:00
Russ Magee
ce1adf7a07 Merge branch 'master' into kcp-udp 2019-08-22 23:31:36 -07:00
Russ Magee
dc3f6f9bd5 Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-08-22 23:29:22 -07:00
Russ Magee
c354036952 Fixed incomplete -x cmd output due to premature ptmx.Close for non-interactive sessions
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-08-22 23:18:20 -07:00
Russ Magee
c95794da1f Uncoupled kcp-go UDP support by moving into hkexnet/kcp.go
TODO: cmdline param to set KCP symmetric key & salt at launch (consider
also from a file to avoid putting inline in invocations, eg., init scripts)

Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-08-16 23:16:40 -07:00
Russ Magee
d7dbcd8fdf Added experimental support (-K) for kcp-go reliable-UDP instead of TCP
github.com/xtaci/kcp-go

** Note: hkexcp appears to hang (client-side) on completion w/complete file copy
   (Note server-side logs on final missed ctrlStatOp msg)

Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-08-13 20:56:01 -07:00
Russtopia
c9d478ff30 Update 'README.md' 2019-08-08 22:34:28 -07:00
Russ Magee
35e23b20ce Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-08-08 21:38:03 -07:00
Russ Magee
795adf6aa0 Added server -L option to use host builtin login
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-08-08 21:36:37 -07:00
Russ Magee
4c0b3a405d Bumped version
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-08-08 00:32:46 -07:00
Russ Magee
2946618a02 Hopeful fix for issue #18
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-08-08 00:21:34 -07:00
Russ Magee
0146a5f4fb Merge branch 'master' of ssh://blitter.com/var/git/hkexsh 2019-07-11 12:18:26 -07:00
Russ Magee
06854f7a03 Added make-controlled version, gitCommit (thanks to https://preslav.me/2019/07/09/adding-version-information-to-go-binaries/ 2019-07-11 10:12:38 -07:00
Russ Magee
2087aab2d5 Minor cleanup to keepalive timing jitter comments 2019-07-10 20:44:02 -07:00
Russ Magee
f5480553df Random jitter to tun keepalive timing
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-07-10 01:11:23 -07:00
Russtopia
4648188bdc Updated README.md 2019-07-05 23:56:18 -07:00
Russ Magee
b8d32ed882 Bumped goutmp -> 1.0.1
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-07-04 21:20:02 -07:00
Russ Magee
1aa8a1549b Used fixed goutmp pty logging
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-07-04 20:27:49 -07:00
Russ Magee
9e885d5bd7 Bumped version in consts.go
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-07-03 09:56:53 -07:00
Russ Magee
825429003b Added enforcement of min/max vals for chaff freq, bytesize
Made HerraduraKEx 512 default KEx (was HerraduraKEx 256)

Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-07-03 09:50:37 -07:00
Russ Magee
3a720cfb8e More race cleanup, (hc.tuns).Data/ShutdownTun() races A,B,C still remain.
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-06-28 22:50:58 -07:00
Russ Magee
78b2006af6 gofmt
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-06-27 22:28:53 -07:00
Russ Magee
8f5366fff4 Added locking APIs for most Conn/Tun fields, save <- Data/ShutdownTun() race
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-06-27 22:10:59 -07:00
Russ Magee
c327b2ec72 Fixed tun reuse hang/error after closure
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-06-27 01:19:52 -07:00
Russ Magee
c4c2e24af1 Merge branch 'master' of https://gogs.blitter.com/RLabs/hkexsh 2019-06-22 23:28:03 -07:00
Russ Magee
1d4c16dd76 Moved server keepalive ageing for tunnels to goroutine -- fix for premature tunnel collapses 2019-06-22 23:27:53 -07:00
Russ Magee
3ae48addbc Made server keepalive ageing for tunnels to goroutine -- fix for premature tunnel collapses
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-06-22 00:44:23 -07:00
Russ Magee
54396a4e4b BUILDOPTS passed to client/server makes 2019-06-20 22:44:19 -07:00
Russ Magee
c24529a1d4 log of cipher/plaintext disabled (flags unexposed to re-enable); pprof tooling 2019-06-19 21:42:34 -07:00
Russ Magee
5511fc83cd Better error handling (no panic) for client connrefused
Server logs error w/o exiting if missing hkexsh.passwd file

Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-05-19 22:30:32 -07:00
Russ Magee
cbf3c77342 Cleaned up/added commenting (hkexsh/, hkexnet/)
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-05-09 22:46:08 -07:00
Russ Magee
7e60a395ad Updated go.mod to include newhope deps
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-04-17 20:00:22 -07:00
Russtopia
1bb31c5e28 Update 'README.md' 2019-04-08 22:26:03 -07:00
Russtopia
5cf7a0f8b3 Update 'README.md' 2019-04-08 22:24:01 -07:00
Russ Magee
df84580e63 Merge branch 'master' of https://gogs.blitter.com/RLabs/hkexsh 2019-04-08 22:00:06 -07:00
Russ Magee
f09d6bbfef Added NEWHOPE and NEWHOPE_SIMPLE KEM algs
Fixed some -h typos, missing H_SHA512 option
randReader seed time.Now().UnixNano()

Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-04-08 21:58:33 -07:00
Russ Magee
8920184ae4 Updated to stable archived link
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-04-06 23:19:26 -07:00
Russ Magee
9a72e02fc0 Comment and redundant file cleanup
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-04-06 23:14:28 -07:00
Russ Magee
a53ec4ac2d termmode.go: Added nil check in term.Restore() for nonexistent cp filename
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-04-05 19:10:38 -07:00
Russ Magee
306ea64299 Sorted cptest.sha1sum
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-04-03 22:37:43 -07:00
Russ Magee
b1bbd9c587 Moved/updated cptest.sha1sum
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-04-03 22:12:00 -07:00
Russ Magee
a7c01ccbcd Merge branch 'master' of https://gogs.blitter.com/RLabs/hkexsh 2019-03-22 22:41:05 -07:00
Russ Magee
5663fd0c79 Added test files for hkexcp testing
Signed-off-by: Russ Magee <rmagee@gmail.com>
2019-03-22 22:40:41 -07:00
60 changed files with 4329 additions and 1494 deletions

327
.golangci.yml Normal file
View file

@ -0,0 +1,327 @@
# This file contains all available configuration options
# with their default values.
# options for analysis running
run:
# default concurrency is a available CPU number
concurrency: 4
# timeout for analysis, e.g. 30s, 5m, default is 1m
timeout: 1m
# exit code when at least one issue was found, default is 1
issues-exit-code: 1
# include test files or not, default is true
tests: true
# list of build tags, all linters use it. Default is empty list.
build-tags:
- mytag
# which dirs to skip: issues from them won't be reported;
# can use regexp here: generated.*, regexp is applied on full path;
# default value is empty list, but default dirs are skipped independently
# from this option's value (see skip-dirs-use-default).
skip-dirs:
- src/external_libs
- autogenerated_by_my_lib
# default is true. Enables skipping of directories:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
skip-dirs-use-default: true
# which files to skip: they will be analyzed, but issues from them
# won't be reported. Default value is empty list, but there is
# no need to include all autogenerated files, we confidently recognize
# autogenerated files. If it's not please let us know.
skip-files:
- ".*\\.my\\.go$"
- lib/bad.go
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
# If invoked with -mod=readonly, the go command is disallowed from the implicit
# automatic updating of go.mod described above. Instead, it fails when any changes
# to go.mod are needed. This setting is most useful to check that go.mod does
# not need updates, such as in a continuous integration and testing system.
# If invoked with -mod=vendor, the go command assumes that the vendor
# directory holds the correct copies of dependencies and ignores
# the dependency descriptions in go.mod.
#! modules-download-mode: readonly|release|vendor
# output configuration options
output:
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
format: colored-line-number
# print lines of code with issue, default is true
print-issued-lines: true
# print linter name in the end of issue text, default is true
print-linter-name: true
# make issues output unique by line, default is true
uniq-by-line: true
# all available settings of specific linters
linters-settings:
dogsled:
# checks assignments with too many blank identifiers; default is 2
max-blank-identifiers: 2
dupl:
# tokens count to trigger issue, 150 by default
threshold: 100
errcheck:
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
# default is false: such cases aren't reported by default.
check-type-assertions: false
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
# default is false: such cases aren't reported by default.
check-blank: false
# [deprecated] comma-separated list of pairs of the form pkg:regex
# the regex is used to ignore names within pkg. (default "fmt:.*").
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
ignore: fmt:.*,io/ioutil:^Read.*
# path to a file containing a list of functions to exclude from checking
# see https://github.com/kisielk/errcheck#excluding-functions for details
#!exclude: /path/to/file.txt
funlen:
lines: 60
statements: 40
gocognit:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 10
goconst:
# minimal length of string constant, 3 by default
min-len: 3
# minimal occurrences count to trigger, 3 by default
min-occurrences: 3
gocritic:
# Which checks should be enabled; can't be combined with 'disabled-checks';
# See https://go-critic.github.io/overview#checks-overview
# To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run`
# By default list of stable checks is used.
enabled-checks:
#!- rangeValCopy
# Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty
disabled-checks:
- regexpMust
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
enabled-tags:
- performance
settings: # settings passed to gocritic
captLocal: # must be valid enabled check name
paramsOnly: true
rangeValCopy:
sizeThreshold: 32
gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 10
godox:
# report any comments starting with keywords, this is useful for TODO or FIXME comments that
# might be left in the code accidentally and should be resolved before merging
keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting
- NOTE
- OPTIMIZE # marks code that should be optimized before merging
- HACK # marks hack-arounds that should be removed before merging
gofmt:
# simplify code: gofmt with `-s` option, true by default
simplify: true
goimports:
# put imports beginning with prefix after 3rd-party packages;
# it's a comma-separated list of prefixes
local-prefixes: github.com/org/project
golint:
# minimal confidence for issues, default is 0.8
min-confidence: 0.8
gomnd:
settings:
mnd:
# the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.
checks: argument,case,condition,operation,return,assign
govet:
# report about shadowed variables
check-shadowing: true
# settings per analyzer
settings:
printf: # analyzer name, run `go tool vet help` to see all analyzers
funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
# enable or disable analyzers by name
enable:
- atomicalign
enable-all: false
disable:
- shadow
disable-all: false
depguard:
list-type: blacklist
include-go-root: false
packages:
- github.com/sirupsen/logrus
packages-with-error-message:
# specify an error message to output when a blacklisted package is used
- github.com/sirupsen/logrus: "logging is allowed only by logutils.Log"
lll:
# max line length, lines longer will be reported. Default is 120.
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
line-length: 120
# tab width in spaces. Default to 1.
tab-width: 1
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
misspell:
# Correct spellings using locale preferences for US or UK.
# Default is to use a neutral variety of English.
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
locale: US
ignore-words:
- someword
nakedret:
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
max-func-lines: 30
prealloc:
# XXX: we don't recommend using this linter before doing performance profiling.
# For most programs usage of prealloc will be a premature optimization.
# Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.
# True by default.
simple: true
range-loops: true # Report preallocation suggestions on range loops, true by default
for-loops: false # Report preallocation suggestions on for loops, false by default
rowserrcheck:
packages:
- github.com/jmoiron/sqlx
unparam:
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
unused:
# treat code as a program (not a library) and report unused exported identifiers; default is false.
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
whitespace:
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
wsl:
# If true append is only allowed to be cuddled if appending value is
# matching variables, fields or types on line above. Default is true.
strict-append: true
# Allow calls and assignments to be cuddled as long as the lines have any
# matching variables, fields or types. Default is true.
allow-assign-and-call: true
# Allow multiline assignments to be cuddled. Default is true.
allow-multiline-assign: true
# Allow declarations (var) to be cuddled.
allow-cuddle-declarations: false
# Allow trailing comments in ending of blocks
allow-trailing-comment: false
# Force newlines in end of case at this limit (0 = never).
force-case-trailing-whitespace: 0
# The custom section can be used to define linter plugins to be loaded at runtime. See README doc
# for more info.
custom:
# Each custom linter should have a unique name.
#! example:
#! # The path to the plugin *.so. Can be absolute or local. Required for each custom linter
#! path: /path/to/example.so
#! # The description of the linter. Optional, just for documentation purposes.
#! description: This is an example usage of a plugin linter.
#! # Intended to point to the repo location of the linter. Optional, just for documentation purposes.
#! original-url: github.com/golangci/example-linter
linters:
enable:
- megacheck
- govet
disable:
- maligned
- prealloc
disable-all: false
presets:
- bugs
- unused
fast: false
issues:
# List of regexps of issue texts to exclude, empty list by default.
# But independently from this option we use default exclude patterns,
# it can be disabled by `exclude-use-default: false`. To list all
# excluded by default patterns execute `golangci-lint run --help`
exclude:
- abcdef
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
# Exclude some linters from running on tests files.
- path: _test\.go
linters:
- gocyclo
- errcheck
- dupl
- gosec
# Exclude known linters from partially hard-vendored code,
# which is impossible to exclude via "nolint" comments.
- path: internal/hmac/
text: "weak cryptographic primitive"
linters:
- gosec
# Exclude some staticcheck messages
- linters:
- staticcheck
text: "SA9003:"
# Exclude lll issues for long lines with go:generate
- linters:
- lll
source: "^//go:generate "
# Independently from option `exclude` we use default exclude patterns,
# it can be disabled by this option. To list all
# excluded by default patterns execute `golangci-lint run --help`.
# Default value for this option is true.
exclude-use-default: false
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
max-issues-per-linter: 0
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
max-same-issues: 0
# Show only new issues: if there are unstaged changes or untracked files,
# only those changes are analyzed, else only changes in HEAD~ are analyzed.
# It's a super-useful option for integration of golangci-lint into existing
# large codebase. It's not practical to fix all existing issues at the moment
# of integration: much better don't allow issues in new code.
# Default is false.
new: false
# Show only new issues created after git revision `REV`
#!new-from-rev: REV
#new-from-rev: HEAD^
# Show only new issues created in git patch with set file path.
#!new-from-patch: path/to/patch/file

View file

@ -1 +0,0 @@
CODE_OF_MERIT.md

61
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,61 @@
# Code of Merit (1.0-r1)
1. The project creators, lead developers, core team, ("The Maintainers") constitute
the managing members of the project and have final say in every decision
of the project, technical or otherwise, including overruling previous decisions.
There are no limitations to this decisional power.
2. Contributions are an expected result of your membership on the project.
Don't expect others to do your work or help you with your work forever.
3. All members have the same opportunities to seek any challenge they want
within the project.
4. Authority or position in the project will be proportional
to the accrued contribution. Seniority must be earned.
5. Software is evolutive: the better implementations must supersede lesser
implementations. Technical advantage is the primary evaluation metric.
6. This is a space for technical prowess; topics outside of the project
will not be tolerated.
7. Non technical conflicts will be discussed in a separate space. Disruption
of the project will not be allowed.
8. Individual characteristics, including but not limited to,
body, sex, sexual preference, race, language, religion, nationality,
or political preferences are irrelevant in the scope of the project and
will not be taken into account concerning your value or that of your contribution
to the project.
9. Discuss or debate the idea, not the person.
10. There is no room for ambiguity: Ambiguity will be met with questioning;
further ambiguity will be met with silence. It is the responsibility
of the originator to provide requested context.
11. If something is illegal outside the scope of the project, it is illegal
in the scope of the project. This Code of Merit does not take precedence over
governing law within the jurisdiction(s) of The Maintainers.
(11.a) If the algorithms implemented in this project (eg., cryptography)
are illegal or prohibited in your local jurisdiction, it is your personal
responsibility to consider how, or if, you should attempt to participate in this
project. The Maintainers disclaim all liability for others' actions and decisions
in this regard.
12. This Code of Merit governs the technical procedures of the project not the
activities outside of it.
13. Participation on the project equates to agreement of this Code of Merit.
14. No objectives beyond the stated objectives of this project are relevant
to the project. Any intent to deviate the project from its original purpose
of existence will constitute grounds for remedial action which may include
expulsion from the project.
This document is based upon the original Code of Merit version 1.0 (Dec 4 2018).
(https://web.archive.org/web/20181204203029/http://code-of-merit.org/)
Updated version (Mar 29 2020): https://codeofmerit.org/code/

View file

@ -1,58 +0,0 @@
# Code of Merit (1.0-r1)
1. The project creators, lead developers, core team, ("The Maintainers") constitute
the managing members of the project and have final say in every decision
of the project, technical or otherwise, including overruling previous decisions.
There are no limitations to this decisional power.
2. Contributions are an expected result of your membership on the project.
Don't expect others to do your work or help you with your work forever.
3. All members have the same opportunities to seek any challenge they want
within the project.
4. Authority or position in the project will be proportional
to the accrued contribution. Seniority must be earned.
5. Software is evolutive: the better implementations must supersede lesser
implementations. Technical advantage is the primary evaluation metric.
6. This is a space for technical prowess; topics outside of the project
will not be tolerated.
7. Non technical conflicts will be discussed in a separate space. Disruption
of the project will not be allowed.
8. Individual characteristics, including but not limited to,
body, sex, sexual preference, race, language, religion, nationality,
or political preferences are irrelevant in the scope of the project and
will not be taken into account concerning your value or that of your contribution
to the project.
9. Discuss or debate the idea, not the person.
10. There is no room for ambiguity: Ambiguity will be met with questioning;
further ambiguity will be met with silence. It is the responsibility
of the originator to provide requested context.
11. If something is illegal outside the scope of the project, it is illegal
in the scope of the project. This Code of Merit does not take precedence over
governing law within the jurisdiction(s) of The Maintainers.
(11.a) If the algorithms implemented in this project (eg., cryptography)
are illegal or prohibited in your local jurisdiction, it is your personal
responsibility to consider how, or if, you should attempt to participate in this
project. The Maintainers disclaim all liability for others' actions and decisions
in this regard.
12. This Code of Merit governs the technical procedures of the project not the
activities outside of it.
13. Participation on the project equates to agreement of this Code of Merit.
14. No objectives beyond the stated objectives of this project are relevant
to the project. Any intent to deviate the project from its original purpose
of existence will constitute grounds for remedial action which may include
expulsion from the project.
This document is based upon the Code of Merit (http://code-of-merit.org), version 1.0.

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017 - 2018 Russell Magee (hkexsh/hkexshd/hkexnet/hkexpasswd)
Copyright (c) 2017 - 2019 Russell Magee (xs/xsd/xsnet/xspasswd)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

110
Makefile
View file

@ -1,14 +1,51 @@
VERSION := 0.9.2
.PHONY: lint vis clean common client server passwd subpkgs install uninstall reinstall
SUBPKGS = logger spinsult hkexnet
TOOLS = hkexpasswd hkexsh hkexshd
## Tag version of binaries with build info wrt.
## GO111MODULE(=on) and vendor/ setup vs. $GOPATH pkg builds
############################################################
ifeq ($(shell go env GOMOD),)
MTAG=
else
MTAG="-m"
endif
ifneq ($(VENDOR),)
GOBUILDOPTS :=-v -mod vendor
VTAG = "-v"
else
GOBUILDOPTS=
VTAG =
endif
############################################################
GIT_COMMIT := $(shell git rev-list -1 HEAD)
#ifeq ($(BUILDOPTS),)
BUILDOPTS :=$(BUILDOPTS)"$(GOBUILDOPTS) -ldflags \"-X main.version=$(VERSION)$(MTAG)$(VTAG) -X main.gitCommit=$(GIT_COMMIT)\""
#endif
SUBPKGS = logger spinsult xsnet
TOOLS = xs xsd
SUBDIRS = $(LIBS) $(TOOLS)
ifeq ($(GOOS),)
GOOS=$(shell go env GOOS)
endif
ifeq ($(GOOS),windows)
ifeq ($(MSYSTEM),MSYS)
WIN_MSYS=1
endif
endif
INSTPREFIX = /usr/local
all: common client server passwd
all: common client server
clean:
@echo "Make: $(MAKE)"
go clean .
for d in $(SUBDIRS); do\
$(MAKE) -C $$d clean;\
@ -16,78 +53,63 @@ clean:
subpkgs:
for d in $(SUBPKGS); do\
$(MAKE) -C $$d all;\
$(MAKE) BUILDOPTS=$(BUILDOPTS) -C $$d all;\
done
tools:
for d in $(TOOLS); do\
$(MAKE) -C $$d all;\
$(MAKE) BUILDOPTS=$(BUILDOPTS) -C $$d all;\
done
common:
go build .
go install .
client: common
$(MAKE) -C hkexsh
$(MAKE) BUILDOPTS=$(BUILDOPTS) -C xs
ifeq ($(MSYSTEM),)
ifneq ($(GOOS),windows)
server: common
$(MAKE) -C hkexshd
ifeq ($(MSYSTEM),MSYS)
echo "Build of xsd server for Windows not yet supported"
else
echo "Cross-build of hkexshd server for Windows not yet supported"
$(MAKE) BUILDOPTS=$(BUILDOPTS) -C xsd
endif
else
server: common
echo "hkexshd server not (yet) supported on Windows"
endif
passwd: common
$(MAKE) -C hkexpasswd
vis:
@which go-callvis >/dev/null 2>&1; \
stat=$$?; if [ $$stat -ne "0" ]; then \
/bin/echo "go-callvis not found. Run go get github.com/Russtopia/go-callvis to install."; \
/bin/echo "go-callvis not found. Run go get https://github.com/TrueFurby/go-callvis to install."; \
else \
make -C hkexsh vis;\
make -C hkexshd vis;\
make -C hkexpasswd vis; \
$(MAKE) -C xs vis;\
$(MAKE) -C xsd vis;\
fi
lint:
make -C hkexpasswd lint
make -C hkexshd lint
make -C hkexsh lint
$(MAKE) -C xsd lint
$(MAKE) -C xs lint
reinstall: uninstall install
install:
cp hkexsh/hkexsh $(INSTPREFIX)/bin
ifeq ($(MSYSTEM),)
ifneq ($(GOOS),windows)
cp hkexshd/hkexshd hkexpasswd/hkexpasswd $(INSTPREFIX)/sbin
echo "WIN_MSYS:" $(WIN_MSYS)
ifdef WIN_MSYS
cp xs/mintty_wrapper.sh $(INSTPREFIX)/bin/xs
cp xs/mintty_wrapper.sh $(INSTPREFIX)/bin/xc
cp xs/xs $(INSTPREFIX)/bin/_xs
cp xs/xs $(INSTPREFIX)/bin/_xc
echo "Install of xsd server for Windows not yet supported"
else
mv $(INSTPREFIX)/bin/hkexsh $(INSTPREFIX)/bin/_hkexsh
cp hkexsh/mintty_wrapper.sh $(INSTPREFIX)/bin/hkexsh
echo "Cross-build of hkexshd server for Windows not yet supported"
cp xs/xs $(INSTPREFIX)/bin
cd $(INSTPREFIX)/bin && ln -s xs xc && cd -
cp xsd/xsd $(INSTPREFIX)/sbin
endif
else
echo "Cross-build of hkexshd server for Windows not yet supported"
endif
cd $(INSTPREFIX)/bin && ln -s hkexsh hkexcp && cd -
uninstall:
rm -f $(INSTPREFIX)/bin/hkexsh $(INSTPREFIX)/bin/hkexcp $(INSTPREFIX)/bin/_hkexsh
ifeq ($(MSYSTEM),)
ifneq ($(GOOS),windows)
rm -f $(INSTPREFIX)/sbin/hkexshd $(INSTPREFIX)/sbin/hkexpasswd
else
endif
else
rm -f $(INSTPREFIX)/bin/xs $(INSTPREFIX)/bin/xc \
$(INSTPREFIX)/bin/_xs $(INSTPREFIX)/bin/_xc
ifndef $(WIN_MSYS)
rm -f $(INSTPREFIX)/sbin/xsd
endif

183
README.md
View file

@ -1,30 +1,33 @@
[![GoDoc](https://godoc.org/blitter.com/go/hkexsh?status.svg)](https://godoc.org/blitter.com/go/hkexsh)
[![GoDoc](https://godoc.org/blitter.com/go/xs?status.svg)](https://godoc.org/blitter.com/go/xs)
HKExSh
# XS
--
HKExSh (**H**erradura**K**yber**Ex** **Sh**ell) is a golang implementation of a simple
remote shell client and server, similar in role to ssh, offering
encrypted interactive and non-interactive sessions, file copying and tunnels with traffic obfuscation ('chaffing').
XS (**X**perimental **S**hell) is a golang implementation of a simple remote shell client and
server, similar in role to ssh, offering encrypted interactive and non-interactive sessions (remote commands),
remote file copying and tunnels with optional traffic obfuscation ('chaffing').
It is stable to the point that I use it for day-to-day remote access in place of, and in preference to, ssh.
***
**NOTE: Due to the experimental nature of the KEX/KEM algorithms used, and the novelty of the overall codebase, this package SHOULD BE CONSIDERED EXTREMELY EXPERIMENTAL and USED WITH CAUTION. It DEFINITELY SHOULD NOT be used for any sensitive applications. USE AT YOUR OWN RISK. NEITHER WARRANTY NOR CLAIM OF FITNESS FOR PURPOSE IS EXPRESSED OR IMPLIED.**
***
**NOTE: Due to the experimental nature of the HerraduraKEx and Kyber IND-CCA-2 algorithms, and the novelty of the overall codebase, this package SHOULD BE CONSIDERED EXTREMELY EXPERIMENTAL and USED WITH CAUTION. It DEFINITELY SHOULD NOT be used for any sensitive applications. USE AT YOUR OWN RISK. NEITHER WARRANTY NOR CLAIM OF FITNESS FOR PURPOSE IS EXPRESSED OR IMPLIED.**
***
The client and server programs (hkexsh and hkexshd) use a mostly drop-in
The client and server programs (xs and xsd) use a mostly drop-in
replacement for golang's standard golang/pkg/net facilities (net.Dial(), net.Listen(), net.Accept()
and the net.Conn type), which automatically negotiate keying material for
secure sockets using one of a selectable set of experimental key exchange (KEX) or
key encapsulation mechanisms (KEM).
### Key Exchange
Currently supported exchanges are:
* The HerraduraKEx key exchange algorithm first released at
[Omar Elejandro Herrera Reyna's HerraduraKEx project](http://github.com/Caume/HerraduraKEx);
* The KYBER IND-CCA-2 secure key encapsulation mechanism, [pq-crystals Kyber](https://pq-crystals.org/kyber/) :: [Yawning/kyber golang implementation](https://git.schwanenlied.me/yawning/kyber)
* The NEWHOPE algorithm [newhopecrypto.org](https://www.newhopecrypto.org/) :: [Yawning/go-newhope golang implementation](https://git.schwanenlied.me/yawning/newhope)
* The FrodoKEM algorithm [frodokem.org](https://frodokem.org/) :: Go version by [Eduardo E. S. Riccardi](https://github.com/kuking/go-frodokem)
Currently supported session algorithms:
@ -33,23 +36,27 @@ Currently supported session algorithms:
* Twofish-128
* Blowfish-64
* CryptMTv1 (64bit) (https://eprint.iacr.org/2005/165.pdf)
* ChaCha20 (https://github.com/aead/chacha20)
[HMAC]
* HMAC-SHA256
* HMAC-SHA512
Calls to hkexnet.Dial() and hkexnet.Listen()/Accept() are generally the same as calls to the equivalents within the _net_ package; however upon connection a key exchange automatically occurs whereby client and server independently derive the same keying material, and all following traffic is secured by a symmetric encryption algorithm.
### Conn
Calls to xsnet.Dial() and xsnet.Listen()/Accept() are generally the same as calls to the equivalents within the _net_ package; however upon connection a key exchange automatically occurs whereby client and server independently derive the same keying material, and all following traffic is secured by a symmetric encryption algorithm.
Above the hkexnet.Conn layer, the server and client apps in this repository (hkexshd/ and hkexsh/ respectively) negotiate session settings (cipher/hmac algorithms, interactive/non-interactive, tunnels, if any, etc.) to be used for communication.
### Session Negotiation
Above the xsnet.Conn layer, the server and client apps in this repository (xsd/ and xs/ respectively) negotiate session settings (cipher/hmac algorithms, interactive/non-interactive mode, tunnel specifiers, etc.) to be used for communication.
Packets are subject to random padding (size, prefix/postfix), and (optionally) the client and server
channels can both send _chaff_ packets at random defineable intervals to help thwart analysis of session activity (applicable to interactive and non-interactive command sessions, file copies and tunnels).
### Padding and Chaffing
Packets are subject to padding (random size, randomly applied as prefix or postfix), and optionally the client and server channels can both send _chaff_ packets at random defineable intervals to help thwart analysis of session activity (applicable to interactive and non-interactive command sessions, file copies and tunnels).
Tunnels, if specified, are set up during initial client->server connection. Packets from the client local port(s) are sent through the main secured connection to the server's remote port(s), and vice versa, tagged with a tunnel specifier so that they can be de-multiplexed and delivered to the proper tunnel endpoints.
Finally, within the hkexpasswd/ directory is a password-setting utility. HKExSh uses its own passwd file distinct from the system /etc/passwd to authenticate clients, using standard bcrypt+salt storage.
### Mux/Demux of Chaffing and Tunnel Data
Chaffing and tunnels, if specified, are set up during initial client->server connection. Packets from the client local port(s) are sent through the main secured connection to the server's remote port(s), and vice versa, tagged with a chaff or tunnel specifier so that they can be discarded as chaff or de-multiplexed and delivered to the proper tunnel endpoints, respectively.
### Accounts and Passwords
Within the ```xspasswd/``` directory is a password-setting utility, ```xspasswd```, used if one wishes ```xs``` access to use separate credentials from those of the default (likely ssh) login method. In this mode, ```xsd``` uses its own password file distinct from the system /etc/passwd to authenticate clients, using standard bcrypt+salt storage. Activate this mode by invoking ```xsd``` with ```-s false```.
HERRADURA KEX
@ -64,76 +71,106 @@ 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.11.1)
### 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
* [blitter.com/go/goutmp](https://gogs.blitter.com/RLabs/goutmp) // wtmp/lastlog C bindings for user accounting
* [https://git.schwanenlied.me/yawning/kyber](https://git.schwanenlied.me/yawning/kyber) // golang Kyber KEM
* [https://git.schwanenlied.me/yawning/newhope](https://git.schwanenlied.me/yawning/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
Get source code
--
* $ go get -u blitter.com/go/hkexsh
* $ cd $GOPATH/src/blitter.com/go/hkexsh
* $ go build ./... # install all dependent go pkgs
### Get source code
```
$ go get -u blitter.com/go/xs
$ cd $GOPATH/src/blitter.com/go/xs
$ go build ./... # install all dependent go pkgs
```
To build
--
* $ cd $GOPATH/src/blitter.com/go/hkexsh
* $ make clean all
### To build
To install, uninstall, re-install
--
* $ sudo make [install | uninstall | reinstall]
```
$ cd $GOPATH/src/blitter.com/go/xs
$ make clean all
```
To manage service (assuming openrc init)
--
* $ sudo rc-config [start | restart | stop] hkexshd
### To install, uninstall, re-install
```
$ sudo make [install | uninstall | reinstall]
```
An example init script (hkexshd.initrc) is provided. Consult your Linux distribution documentation for proper service/daemon installation. Default assumes installation in /usr/local/sbin (hkexshd, hkexpasswd) and /usr/local/bin (hkexsh/hkexcp symlink).
### To manage service (openrc init)
An example init script (xsd.initrc) is provided. Consult your Linux distribution documentation for proper service/daemon installation. For openrc,
To set accounts & passwords:
--
* $ sudo touch /etc/hkexsh.passwd
* $ sudo hkexpasswd/hkexpasswd -u joebloggs
* $ &lt;enter a password, enter again to confirm&gt;
```
$ sudo cp xsd.initrc /etc/init.d/xsd
$ sudo rc-config add xsd default
```
### To manage service (sysV init)
An example init script (xsd.sysvrc) is provided. Consult your Linux distribution documentation for proper service/daemon installation. For sysV init,
```
$ sudo cp xsd.sysvrc /etc/init.d/xsd
$ sudo sysv-rc-conf --level 2345 xsd on
```
The make system assumes installation in /usr/local/sbin (xsd, xspasswd) and /usr/local/bin (xs/xc symlink).
```
$ sudo rc-config [start | restart | stop] xsd
```
### To set accounts & passwords:
```
$ sudo touch /etc/xs.passwd
$ sudo xspasswd/xspasswd -u joebloggs
$ <enter a password, enter again to confirm>
```
### Testing Client and Server from $GOPATH dev tree (w/o 'make install')
Testing Client and Server from $GOPATH dev tree (w/o 'make install')
--
In separate shells A and B:
* [A]$ cd hkexshd && sudo ./hkexshd & # add -d for debugging
```
[A]$ cd xsd && sudo ./xsd & # add -d for debugging
```
Interactive shell
* [B]$ cd hkexsh && ./hkexsh joebloggs@host-or-ip # add -d for debugging
```
[B]$ cd xs && ./xs joebloggs@host-or-ip # add -d for debugging
```
One-shot command
* [B]$ cd hkexsh && ./hkexsh -x "ls /tmp" joebloggs@host-or-ip
```
[B]$ cd xs && ./xs -x "ls /tmp" joebloggs@host-or-ip
```
WARNING WARNING WARNING: the -d debug flag will echo passwords to the log/console!
Logging on Linux usually goes to /var/log/syslog and/or /var/log/debug, /var/log/daemon.log.
NOTE if running client (hkexsh) with -d, one will likely need to run 'reset' afterwards
NOTE if running client (xs) with -d, one will likely need to run 'reset' afterwards
to fix up the shell tty afterwards, as stty echo may not be restored if client crashes
or is interrupted.
Setting up an 'authtoken' for scripted (password-free) logins
--
Use the -g option of hkexsh to request a token from the remote server, which will return a
hostname:token string. Place this string into $HOME/.hkexsh_id to allow logins without
entering a password (obviously, $HOME/.hkexsh_id on both server and client for the user
### 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/.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.)
File Copying using hkexcp
--
hkexcp is a symlink to hkexsh, and the binary checks its own filename to determine whether
### File Copying using xc
xc is a symlink to xs, and the binary checks its own filename to determine whether
it is being invoked in 'shell' or 'copy' mode. Refer to the '-h' output for differences in
accepted options.
@ -143,25 +180,37 @@ remote user. File operations are all performed as the remote user, so account pe
as expected.
Local (client) to remote (server) copy:
* hkexcp fileA /some/where/fileB /some/where/else/dirC joebloggs@host-or-ip:remoteDir
```
$ xc fileA /some/where/fileB /some/where/else/dirC joebloggs@host-or-ip:remoteDir
```
Remote (server) to local (client) copy:
* hkexcp joebloggs@host-or-ip:/remoteDirOrFile /some/where/local/Dir
```
$ xc joebloggs@host-or-ip:/remoteDirOrFile /some/where/local/Dir
```
xc uses a 'tarpipe' to send file data over the encrypted channel. Use the -d flag on client or server to see the generated tar commands if you're curious.
NOTE: Renaming while copying (eg., 'cp /foo/bar/fileA ./fileB') is NOT supported. Put another way, the destination (whether local or remote) must ALWAYS be a directory.
hkexcp uses tar (a 'tarpipe') with gzip compression, sending tar data over the hkex encrypted channel. Use the -d flag on client or server to see the generated tar commands if you're curious.
If the 'pv' pipeview utility is available (http://www.ivarch.com/programs/pv.shtml) file transfer progress and bandwidth control will be available (suppress the former with the -q option, set the latter with -L &lt;bytes_per_second&gt;).
### Tunnels
Tunnels
--
Simple tunnels (client -> server, no reverse tunnels for now) are supported.
Syntax: hkexsh -T=&lt;tunspec&gt;{,&lt;tunspec&gt;...}
Syntax: xs -T=&lt;tunspec&gt;{,&lt;tunspec&gt;...}
.. where &lt;tunspec&gt; is &lt;localport:remoteport&gt;
Example, tunnelling ssh through hkexsh (NOTE [issue #15](https://blitter.com:3000/RLabs/hkexsh/issues/15))
Example, tunnelling ssh through xs
* [server side] $ sudo /usr/sbin/sshd -p 7002
* [client side, term A] $ hkexsh -T=6002:7002 user@server
* [client side, term B] $ ssh user@localhost -p 6002
* [server side] ```$ sudo /usr/sbin/sshd -p 7002```
* [client side, term A] ```$ xs -T=6002:7002 user@server```
* [client side, term B] ```$ ssh user@localhost -p 6002```
### Building for FreeBSD
The Makefile(s) to build require GNU make (gmake).
Please install and invoke build via:
```$ gmake clean all```

View file

@ -18,6 +18,7 @@ Architecture
(parts split out into hkexnet/*, hkexsession.go)
(DONE) - Make KEx fully-pluggable: isolate all code to do with Herradura into a
KEx-neutral pkg so it can be swapped out for other methods (eg., DH etc.)
(DONE - test branch) - Use system password db (/etc/{passwd,shadow})
Features
(DONE) - Support for hkcp (hkex-cp) - secure file copy protocol

227
auth.go Executable file
View file

@ -0,0 +1,227 @@
package xs
// Package xs - a secure terminal client/server written from scratch in Go
//
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
// golang implementation by Russ Magee (rmagee_at_gmail.com)
// Authentication routines for the HKExSh
import (
"bytes"
"encoding/csv"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/user"
"runtime"
"strings"
"github.com/jameskeane/bcrypt"
passlib "gopkg.in/hlandau/passlib.v1"
)
type AuthCtx struct {
reader func(string) ([]byte, error) // eg. ioutil.ReadFile()
userlookup func(string) (*user.User, error) // eg. os/user.Lookup()
}
func NewAuthCtx( /*reader func(string) ([]byte, error), userlookup func(string) (*user.User, error)*/ ) (ret *AuthCtx) {
ret = &AuthCtx{ioutil.ReadFile, user.Lookup}
return
}
// --------- System passwd/shadow auth routine(s) --------------
// VerifyPass verifies a password against system standard shadow file
// Note auxilliary fields for expiry policy are *not* inspected.
func VerifyPass(ctx *AuthCtx, user, password string) (bool, error) {
if ctx.reader == nil {
ctx.reader = ioutil.ReadFile // dependency injection hides that this is required
}
passlib.UseDefaults(passlib.Defaults20180601)
var pwFileName string
if runtime.GOOS == "linux" {
pwFileName = "/etc/shadow"
} else if runtime.GOOS == "freebsd" {
pwFileName = "/etc/master.passwd"
} else {
pwFileName = "unsupported"
}
pwFileData, e := ctx.reader(pwFileName)
if e != nil {
return false, e
}
pwLines := strings.Split(string(pwFileData), "\n")
if len(pwLines) < 1 {
return false, errors.New("Empty shadow file!")
} else {
var line string
var hash string
var idx int
for idx = range pwLines {
line = pwLines[idx]
lFields := strings.Split(line, ":")
if lFields[0] == user {
hash = lFields[1]
break
}
}
if len(hash) == 0 {
return false, errors.New("nil hash!")
} else {
pe := passlib.VerifyNoUpgrade(password, hash)
if pe != nil {
return false, pe
}
}
}
return true, nil
}
// --------- End System passwd/shadow auth routine(s) ----------
// ------------- xs-local passwd auth routine(s) ---------------
// AuthUserByPasswd checks user login information using a password.
// This checks /etc/xs.passwd for auth info, and system /etc/passwd
// to cross-check the user actually exists.
// nolint: gocyclo
func AuthUserByPasswd(ctx *AuthCtx, username string, auth string, fname string) (valid bool, allowedCmds string) {
if ctx.reader == nil {
ctx.reader = ioutil.ReadFile // dependency injection hides that this is required
}
if ctx.userlookup == nil {
ctx.userlookup = user.Lookup // again for dependency injection as dep is now hidden
}
b, e := ctx.reader(fname) // nolint: gosec
if e != nil {
valid = false
log.Printf("ERROR: Cannot read %s!\n", fname)
}
r := csv.NewReader(bytes.NewReader(b))
r.Comma = ':'
r.Comment = '#'
r.FieldsPerRecord = 3 // username:salt:authCookie [TODO:disallowedCmdList (a,b,...)]
for {
record, err := r.Read()
if err == io.EOF {
// Use dummy entry if user not found
// (prevent user enumeration attack via obvious timing diff;
// ie., not attempting any auth at all)
record = []string{"$nosuchuser$",
"$2a$12$l0coBlRDNEJeQVl6GdEPbU",
"$2a$12$l0coBlRDNEJeQVl6GdEPbUC/xmuOANvqgmrMVum6S4i.EXPgnTXy6"}
username = "$nosuchuser$"
err = nil
}
if err != nil {
log.Fatal(err)
}
if username == record[0] {
tmp, err := bcrypt.Hash(auth, record[1])
if err != nil {
break
}
if tmp == record[2] && username != "$nosuchuser$" {
valid = true
}
break
}
}
// Security scrub
for i := range b {
b[i] = 0
}
r = nil
runtime.GC()
_, userErr := ctx.userlookup(username)
if userErr != nil {
valid = false
}
return
}
// ------------- 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/.xs_id and are requested
// via the -g option.
// The function also check system /etc/passwd to cross-check the user
// actually exists.
func AuthUserByToken(ctx *AuthCtx, username string, connhostname string, auth string) (valid bool) {
if ctx.reader == nil {
ctx.reader = ioutil.ReadFile // dependency injection hides that this is required
}
if ctx.userlookup == nil {
ctx.userlookup = user.Lookup // again for dependency injection as dep is now hidden
}
auth = strings.TrimSpace(auth)
u, ue := ctx.userlookup(username)
if ue != nil {
return false
}
b, e := ctx.reader(fmt.Sprintf("%s/.xs_id", u.HomeDir))
if e != nil {
log.Printf("INFO: Cannot read %s/.xs_id\n", u.HomeDir)
return false
}
r := csv.NewReader(bytes.NewReader(b))
r.Comma = ':'
r.Comment = '#'
r.FieldsPerRecord = 2 // connhost:authtoken
for {
record, err := r.Read()
if err == io.EOF {
return false
}
record[0] = strings.TrimSpace(record[0])
record[1] = strings.TrimSpace(record[1])
//fmt.Println("auth:", auth, "record:",
// strings.Join([]string{record[0], record[1]}, ":"))
if (connhostname == record[0]) &&
(auth == strings.Join([]string{record[0], record[1]}, ":")) {
valid = true
break
}
}
_, userErr := ctx.userlookup(username)
if userErr != nil {
valid = false
}
return
}
func GetTool(tool string) (ret string) {
ret = "/bin/"+tool
_, err := os.Stat(ret)
if err == nil {
return ret
}
ret = "/usr/bin/"+tool
_, err = os.Stat(ret)
if err == nil {
return ret
}
ret = "/usr/local/bin/"+tool
_, err = os.Stat(ret)
if err == nil {
return ret
}
return ""
}

212
auth_test.go Normal file
View file

@ -0,0 +1,212 @@
package xs
import (
"errors"
"fmt"
"os/user"
"strings"
"testing"
)
type userVerifs struct {
user string
passwd string
good bool
}
var (
dummyShadowA = `johndoe:$6$EeQlTtn/KXdSh6CW$UHbFuEw3UA0Jg9/GoPHxgWk6Ws31x3IjqsP22a9pVMOte0yQwX1.K34oI4FACu8GRg9DArJ5RyWUE9m98qwzZ1:18310:0:99999:7:::
joebloggs:$6$F.0IXOrb0w0VJHG1$3O4PYyng7F3hlh42mbroEdQZvslybY5etPPiLMQJ1xosjABY.Q4xqAfyIfe03Du61ZjGQIt3nL0j12P9k1fsK/:18310:0:99999:7:::
disableduser:!:18310::::::`
dummyAuthTokenFile = "hostA:abcdefg\nhostB:wxyz\n"
dummyXsPasswdFile = `#username:salt:authCookie
bobdobbs:$2a$12$9vqGkFqikspe/2dTARqu1O:$2a$12$9vqGkFqikspe/2dTARqu1OuDKCQ/RYWsnaFjmi.HtmECRkxcZ.kBK
notbob:$2a$12$cZpiYaq5U998cOkXzRKdyu:$2a$12$cZpiYaq5U998cOkXzRKdyuJ2FoEQyVLa3QkYdPQk74VXMoAzhvuP6
`
testGoodUsers = []userVerifs{
{"johndoe", "testpass", true},
{"joebloggs", "testpass2", true},
{"johndoe", "badpass", false},
}
testXsPasswdUsers = []userVerifs{
{"bobdobbs", "praisebob", true},
{"notbob", "imposter", false},
}
userlookup_arg_u string
readfile_arg_f string
)
func newMockAuthCtx(reader func(string) ([]byte, error), userlookup func(string) (*user.User, error)) (ret *AuthCtx) {
ret = &AuthCtx{reader, userlookup}
return
}
func _mock_user_Lookup(username string) (*user.User, error) {
username = userlookup_arg_u
if username == "baduser" {
return &user.User{}, errors.New("bad user")
}
urec := &user.User{Uid: "1000", Gid: "1000", Username: username, Name: "Full Name", HomeDir: "/home/user"}
fmt.Printf(" [mock user rec:%v]\n", urec)
return urec, nil
}
func _mock_ioutil_ReadFile(f string) ([]byte, error) {
f = readfile_arg_f
if f == "/etc/shadow" {
fmt.Println(" [mocking ReadFile(\"/etc/shadow\")]")
return []byte(dummyShadowA), nil
}
if f == "/etc/xs.passwd" {
fmt.Println(" [mocking ReadFile(\"/etc/xs.passwd\")]")
return []byte(dummyXsPasswdFile), nil
}
if strings.Contains(f, "/.xs_id") {
fmt.Println(" [mocking ReadFile(\".xs_id\")]")
return []byte(dummyAuthTokenFile), nil
}
return []byte{}, errors.New("no readfile_arg_f supplied")
}
func _mock_ioutil_ReadFileEmpty(f string) ([]byte, error) {
return []byte{}, nil
}
func _mock_ioutil_ReadFileHasError(f string) ([]byte, error) {
return []byte{}, errors.New("IO Error")
}
func TestVerifyPass(t *testing.T) {
readfile_arg_f = "/etc/shadow"
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, nil)
for idx, rec := range testGoodUsers {
stat, e := VerifyPass(ctx, rec.user, rec.passwd)
if rec.good && (!stat || e != nil) {
t.Fatalf("failed %d\n", idx)
}
}
}
func TestVerifyPassFailsOnEmptyFile(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFileEmpty, nil)
stat, e := VerifyPass(ctx, "johndoe", "somepass")
if stat || (e == nil) {
t.Fatal("failed to fail w/empty file")
}
}
func TestVerifyPassFailsOnFileError(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFileEmpty, nil)
stat, e := VerifyPass(ctx, "johndoe", "somepass")
if stat || (e == nil) {
t.Fatal("failed to fail on ioutil.ReadFile error")
}
}
func TestVerifyPassFailsOnDisabledEntry(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFileEmpty, nil)
stat, e := VerifyPass(ctx, "disableduser", "!")
if stat || (e == nil) {
t.Fatal("failed to fail on disabled user entry")
}
}
////
func TestAuthUserByTokenFailsOnMissingEntryForHost(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
stat := AuthUserByToken(ctx, "johndoe", "hostZ", "abcdefg")
if stat {
t.Fatal("failed to fail on missing/mismatched host entry")
}
}
func TestAuthUserByTokenFailsOnMissingEntryForUser(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
stat := AuthUserByToken(ctx, "unkuser", "hostA", "abcdefg")
if stat {
t.Fatal("failed to fail on wrong user")
}
}
func TestAuthUserByTokenFailsOnUserLookupFailure(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
userlookup_arg_u = "baduser"
stat := AuthUserByToken(ctx, "johndoe", "hostA", "abcdefg")
if stat {
t.Fatal("failed to fail with bad return from user.Lookup()")
}
}
func TestAuthUserByTokenFailsOnMismatchedTokenForUser(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
stat := AuthUserByToken(ctx, "johndoe", "hostA", "badtoken")
if stat {
t.Fatal("failed to fail with valid user, bad token")
}
}
func TestAuthUserByTokenSucceedsWithMatchedUserAndToken(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
userlookup_arg_u = "johndoe"
readfile_arg_f = "/.xs_id"
stat := AuthUserByToken(ctx, userlookup_arg_u, "hostA", "hostA:abcdefg")
if !stat {
t.Fatal("failed with valid user and token")
}
}
func TestAuthUserByPasswdFailsOnEmptyFile(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFileEmpty, _mock_user_Lookup)
userlookup_arg_u = "bobdobbs"
readfile_arg_f = "/etc/xs.passwd"
stat, _ := AuthUserByPasswd(ctx, userlookup_arg_u, "praisebob", readfile_arg_f)
if stat {
t.Fatal("failed to fail with missing xs.passwd file")
}
}
func TestAuthUserByPasswdFailsOnBadAuth(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
userlookup_arg_u = "bobdobbs"
readfile_arg_f = "/etc/xs.passwd"
stat, _ := AuthUserByPasswd(ctx, userlookup_arg_u, "wrongpass", readfile_arg_f)
if stat {
t.Fatal("failed to fail with valid user, incorrect passwd in xs.passwd file")
}
}
func TestAuthUserByPasswdFailsOnBadUser(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
userlookup_arg_u = "bobdobbs"
readfile_arg_f = "/etc/xs.passwd"
stat, _ := AuthUserByPasswd(ctx, userlookup_arg_u, "theotherbob", readfile_arg_f)
if stat {
t.Fatal("failed to fail on invalid user vs. xs.passwd file")
}
}
func TestAuthUserByPasswdPassesOnGoodAuth(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
userlookup_arg_u = "bobdobbs"
readfile_arg_f = "/etc/xs.passwd"
stat, _ := AuthUserByPasswd(ctx, userlookup_arg_u, "praisebob", readfile_arg_f)
if !stat {
t.Fatal("failed on valid user w/correct passwd in xs.passwd file")
}
}
func TestAuthUserByPasswdPassesOnOtherGoodAuth(t *testing.T) {
ctx := newMockAuthCtx(_mock_ioutil_ReadFile, _mock_user_Lookup)
userlookup_arg_u = "notbob"
readfile_arg_f = "/etc/xs.passwd"
stat, _ := AuthUserByPasswd(ctx, userlookup_arg_u, "imposter", readfile_arg_f)
if !stat {
t.Fatal("failed on valid user 2nd entry w/correct passwd in xs.passwd file")
}
}

107
bacillus/ci_pushbuild.sh Executable file
View file

@ -0,0 +1,107 @@
#!/bin/bash
#
## bacillus (https://gogs.blitter.com/Russtopia/bacillus) build/test CI script
export GOPATH="${HOME}/go"
export PATH=/usr/local/bin:/usr/bin:/usr/lib/ccache/bin:/bin:$GOPATH/bin
export GO111MODULE=on
# GOCACHE will be phased out in v1.12. [github.com/golang/go/issues/26809]
export GOCACHE="${HOME}/.cache/go-build"
echo "workdir: ${BACILLUS_WORKDIR}"
mkdir -p "${BACILLUS_ARTFDIR}"
echo "---"
go env
echo "---"
echo "passed env:"
env
echo "---"
cd ${REPO}
branch=$(git for-each-ref --sort=-committerdate --format='%(refname)' | head -n 1)
echo "Building most recent push on branch $branch"
git checkout "$branch"
ls
############
stage "Build"
############
make all
############
stage "UnitTests"
############
go test -v .
############
stage "Test(Authtoken)"
############
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: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
echo "AUTHTOKEN LOGIN FAILED"
exit 1
else
echo "client cmd performed OK."
unset tokentest
fi
############
stage "Test(xc S->C)"
############
echo "Testing secure copy from server -> client ..."
./xc_testfiles.sh
tmpdir=$$
mkdir -p /tmp/$tmpdir
cd /tmp/$tmpdir
xc @localhost:${BACILLUS_WORKDIR}/build/xs/cptest .
echo -n "Integrity check on copied files (sha1sum) ..."
sha1sum $(find cptest -type f | sort) >sc.sha1sum
diff sc.sha1sum ${BACILLUS_WORKDIR}/build/xs/cptest.sha1sum
stat=$?
cd -
rm -rf /tmp/$tmpdir
if [ $stat -eq "0" ]; then
echo "OK."
else
echo "FAILED!"
exit $stat
fi
############
stage "Test(xc C->S)"
############
echo "TODO ..."
if [ -f ~/.xs_id.bak ]; then
echo "Restoring test user $USER ~/.xs_id file ..."
mv ~/.xs_id.bak ~/.xs_id
fi
############
stage "Lint"
############
make lint
############
stage "Artifacts"
############
echo -n "Creating tarfile ..."
tar -cz --exclude=.git --exclude=cptest -f ${BACILLUS_ARTFDIR}/xs.tgz .
############
stage "Cleanup"
############
rm -rf cptest
echo
echo "--Done--"

View file

@ -1,13 +1,10 @@
// Package hkexsh - a secure terminal client/server written from scratch in Go
// Package xs - a secure terminal client/server written from scratch in Go
//
// Copyright (c) 2017-2018 Russell Magee
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
// golang implementation by Russ Magee (rmagee_at_gmail.com)
package hkexsh
package xs
// common constants for the HKExSh
// Version string returned by tools
const Version = "0.8.0 (NO WARRANTY)"
// common constants for the XS (Xperimental Shell)

6
cptest.sha1sum Normal file
View file

@ -0,0 +1,6 @@
6010a446cdcf8c1203c2d08998cc69a8c88f77d5 cptest/file16KB
f086ea96f3718efd78e6791178f967585acb3701 cptest/file1KB
6061e16c3d3840712d0b8e5268c49b5c2f8137ac cptest/file32KB
490af32035bbe737480f06439a02d91a171ac407 cptest/file6B
e4a8a4ad9678b7265a28c5f0cb5b078e1049ea23 cptest/subdir/file32MB
82b50550c7d0e0d6ac18d9d0796b6814c3e038bf cptest/subdir/file64MB

34
go.mod
View file

@ -1,14 +1,30 @@
module blitter.com/go/hkexsh
module blitter.com/go/xs
go 1.12
require (
blitter.com/go/cryptmt v1.0.0
blitter.com/go/goutmp v1.0.0
blitter.com/go/cryptmt v1.0.2
blitter.com/go/goutmp v1.0.5
blitter.com/go/herradurakex v1.0.0
blitter.com/go/mtwist v1.0.0 // indirect
git.schwanenlied.me/yawning/kyber.git v0.0.0-20180530164001-a270899bd22c
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
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
github.com/creack/pty v1.1.11
github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f
github.com/kr/pty v1.1.3
github.com/mattn/go-isatty v0.0.4
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06
github.com/klauspost/reedsolomon v1.9.9 // indirect
github.com/kuking/go-frodokem v1.0.1
github.com/mattn/go-isatty v0.0.12
github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect
github.com/tjfoc/gmsm v1.3.1 // indirect
github.com/xtaci/kcp-go v5.4.20+incompatible
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 // indirect
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37
golang.org/x/sys v0.0.0-20200523222454-059865788121
gopkg.in/hlandau/easymetric.v1 v1.0.0 // indirect
gopkg.in/hlandau/measurable.v1 v1.0.1 // indirect
gopkg.in/hlandau/passlib.v1 v1.0.10
)

118
go.sum
View file

@ -1,36 +1,94 @@
blitter.com/go/cryptmt v0.0.0-20181209042848-f3e54a9d98fa h1:M9Ewnr7XZYJANeUj9Jm1ucMwL8rEyHgXcHhIw3RQtB8=
blitter.com/go/cryptmt v0.0.0-20181209042848-f3e54a9d98fa/go.mod h1:tdME2J3O4agaDAYIYNQzzuB28yVGnPSMmV3a/ucSU84=
blitter.com/go/cryptmt v1.0.0 h1:n+cNP/ReZrNe/w5FbD8DSfv0Wpj48nxhmMoLEk4hPXs=
blitter.com/go/cryptmt v1.0.0/go.mod h1:tdME2J3O4agaDAYIYNQzzuB28yVGnPSMmV3a/ucSU84=
blitter.com/go/goutmp v0.0.0-20181114075424-907ffc4058d9 h1:kcFQxwfPpVMBcLg9GIcHJmSW4ZNrcpeTUFD8wpIwu9Y=
blitter.com/go/goutmp v0.0.0-20181114075424-907ffc4058d9/go.mod h1:gtlbjC8xGzMk/Cf0BpnVltSa3awOqJ+B5WAxVptTMxk=
blitter.com/go/goutmp v1.0.0 h1:9BPQTnahoMHyF+IC8gj/Em+i2RDZtVSupoEgRhmaJg4=
blitter.com/go/goutmp v1.0.0/go.mod h1:gtlbjC8xGzMk/Cf0BpnVltSa3awOqJ+B5WAxVptTMxk=
blitter.com/go/herradurakex v0.0.0-20181207001539-873ba2e58872 h1:W0dt8bwmGO7mUr2F5E1R4xjbgYc0xYF/kaAbkVudeNk=
blitter.com/go/herradurakex v0.0.0-20181207001539-873ba2e58872/go.mod h1:m3+vYZX+2dDjdo+n/HDnXEYJX9pwmNeQLgAfJM8mtxw=
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.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/mtwist v0.0.0-20181024062339-1a11c643f88c h1:zQ6vgMPQ8J3ZJNYsskpfjeY1eoBbLjCJPeZGCZYdVbI=
blitter.com/go/mtwist v0.0.0-20181024062339-1a11c643f88c/go.mod h1:aU82Nx8+b1v8oZRNqImfEDzDTPim81rY0ACKAIclV18=
blitter.com/go/mtwist v1.0.0 h1:/Vg6k12+DC+fokeRrLApL22hZS/EqEazJTo/FDdbDog=
blitter.com/go/mtwist v1.0.0/go.mod h1:aU82Nx8+b1v8oZRNqImfEDzDTPim81rY0ACKAIclV18=
git.schwanenlied.me/yawning/kyber.git v0.0.0-20180530164001-a270899bd22c h1:SGOx1s56QSOmuCegRcG3yvOG7W8PvRS9ZVnFQl5K2aQ=
git.schwanenlied.me/yawning/kyber.git v0.0.0-20180530164001-a270899bd22c/go.mod h1:QrbgzU5EL/1jaMD5pD4Tiikj3R5elPMa+RMwFUTGwQU=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=
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/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/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/kr/pty v1.1.3 h1:/Um6a/ZmD5tF7peoOJ5oN5KMQ0DrGVQSXLNwyckutPk=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/klauspost/cpuid v1.2.4 h1:EBfaK0SWSwk+fgk6efYFWdzl8MwRWoOO1gkmiaTXPW4=
github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54=
github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
github.com/kuking/go-frodokem v1.0.1 h1:13bks3u4CPpvUtOLttT+A37j9myV4kLnS7Z3qDiTm4o=
github.com/kuking/go-frodokem v1.0.1/go.mod h1:TzD0W9QnVOcwigeSySEuNZfJaGxWRtFRb7hXe/w/waI=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061 h1:UCU8+cLbbvyxi0sQ9fSeoEhZgvrrD9HKMtX6Gmc1vk8=
github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
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/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/sys v0.0.0-20181211161752-7da8ea5c8182 h1:3jwI9dC+BuoXWS+QtR/HhfGTGTuB6ZzL6II6S1IuVvo=
golang.org/x/sys v0.0.0-20181211161752-7da8ea5c8182/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM=
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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.3.1 h1:+k3IAlF81c31/TllJmIfuCYnjl8ziMdTWGWJcP9J1uo=
github.com/tjfoc/gmsm v1.3.1/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/ulikunitz/xz v0.5.7 h1:YvTNdFzX6+W5m9msiYg/zpkSURPPtOlzbqYjrFn7Yt4=
github.com/ulikunitz/xz v0.5.7/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=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c h1:iHhCR0b26amDCiiO+kBguKZom9aMF+NrFxh9zeKR/XU=
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
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.10 h1:q5xh9ZHp907XTjVw8/EqG03//fnlITnIYQmv4Gn7TpE=
gopkg.in/hlandau/passlib.v1 v1.0.10/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=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View file

@ -1,5 +1,5 @@
env:
project: hkexsh
project: xs
version: 0.8.0
buildDir: build/
docDir: doc/
@ -38,7 +38,7 @@ commands:
app:
aliases: [ build ]
help: build the hkexsh tools
help: build the xs tools
exec: |
make clean
make all

View file

@ -1,131 +0,0 @@
package hkexsh
// Package hkexsh - a secure terminal client/server written from scratch in Go
//
// Copyright (c) 2017-2018 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
// golang implementation by Russ Magee (rmagee_at_gmail.com)
// Authentication routines for the HKExSh
import (
"bytes"
"encoding/csv"
"fmt"
"io"
"io/ioutil"
"log"
"os/user"
"runtime"
"strings"
"github.com/jameskeane/bcrypt"
)
func userExistsOnSystem(who string) bool {
_, userErr := user.Lookup(who)
return userErr == nil
}
// AuthUserByPasswd checks user login information using a password.
// This checks /etc/hkexsh.passwd for auth info, and system /etc/passwd
// to cross-check the user actually exists.
// nolint: gocyclo
func AuthUserByPasswd(username string, auth string, fname string) (valid bool, allowedCmds string) {
b, e := ioutil.ReadFile(fname) // nolint: gosec
if e != nil {
valid = false
log.Println("ERROR: Cannot read hkexsh.passwd file!")
log.Fatal(e)
}
r := csv.NewReader(bytes.NewReader(b))
r.Comma = ':'
r.Comment = '#'
r.FieldsPerRecord = 3 // username:salt:authCookie [TODO:disallowedCmdList (a,b,...)]
for {
record, err := r.Read()
if err == io.EOF {
// Use dummy entry if user not found
// (prevent user enumeration attack via obvious timing diff;
// ie., not attempting any auth at all)
record = []string{"$nosuchuser$",
"$2a$12$l0coBlRDNEJeQVl6GdEPbU",
"$2a$12$l0coBlRDNEJeQVl6GdEPbUC/xmuOANvqgmrMVum6S4i.EXPgnTXy6"}
username = "$nosuchuser$"
err = nil
}
if err != nil {
log.Fatal(err)
}
if username == record[0] {
tmp, err := bcrypt.Hash(auth, record[1])
if err != nil {
break
}
if tmp == record[2] && username != "$nosuchuser$" {
valid = true
}
break
}
}
// Security scrub
for i := range b {
b[i] = 0
}
r = nil
runtime.GC()
if !userExistsOnSystem(username) {
valid = false
}
return
}
// AuthUserByToken checks user login information against an auth token.
// Auth tokens are stored in each user's $HOME/.hkexsh_id and are requested
// via the -g option.
// The function also check system /etc/passwd to cross-check the user
// actually exists.
func AuthUserByToken(username string, connhostname string, auth string) (valid bool) {
auth = strings.TrimSpace(auth)
u, ue := user.Lookup(username)
if ue != nil {
return false
}
b, e := ioutil.ReadFile(fmt.Sprintf("%s/.hkexsh_id", u.HomeDir))
if e != nil {
log.Printf("INFO: Cannot read %s/.hkexsh_id\n", u.HomeDir)
return false
}
r := csv.NewReader(bytes.NewReader(b))
r.Comma = ':'
r.Comment = '#'
r.FieldsPerRecord = 2 // connhost:authtoken
for {
record, err := r.Read()
if err == io.EOF {
return false
}
record[0] = strings.TrimSpace(record[0])
record[1] = strings.TrimSpace(record[1])
//fmt.Println("auth:", auth, "record:",
// strings.Join([]string{record[0], record[1]}, ":"))
if (connhostname == record[0]) &&
(auth == strings.Join([]string{record[0], record[1]}, ":")) {
valid = true
break
}
}
if !userExistsOnSystem(username) {
valid = false
}
return
}

View file

@ -1,63 +0,0 @@
digraph gocallvis {
label="blitter.com/go/hkexsh/hkexpasswd";
labeljust="l";
fontname="Arial";
fontsize="14";
rankdir="LR";
bgcolor="lightgray";
style="solid";
penwidth="0.5";
pad="0.0";
nodesep="0.35";
node [shape="ellipse" style="filled" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"];
edge [minlen="2"]
subgraph "cluster_focus" {
labelloc="t";
labeljust="c";
fontsize="18";
bgcolor="#e6ecfa";
label="main";
"blitter.com/go/hkexsh/hkexpasswd.main" [ fillcolor="lightblue" label="main" penwidth="0.5" ]
subgraph "cluster_blitter.com/go/hkexsh" {
penwidth="0.8";
style="filled";
fillcolor="lightyellow";
fontname="bold";
label="[hkexsh]";
URL="/?f=blitter.com/go/hkexsh";
tooltip="package: blitter.com/go/hkexsh";
fontsize="16";
rank="sink";
"blitter.com/go/hkexsh.ReadPassword" [ fillcolor="moccasin" label="ReadPassword" penwidth="1.5" ]
}
subgraph "cluster_github.com/jameskeane/bcrypt" {
tooltip="package: github.com/jameskeane/bcrypt";
penwidth="0.8";
fontsize="16";
style="filled";
rank="sink";
label="[bcrypt]";
URL="/?f=github.com/jameskeane/bcrypt";
fillcolor="lightyellow";
fontname="bold";
"github.com/jameskeane/bcrypt.Salt" [ fillcolor="moccasin" label="Salt" penwidth="1.5" ]
"github.com/jameskeane/bcrypt.Hash" [ fillcolor="moccasin" label="Hash" penwidth="1.5" ]
"github.com/jameskeane/bcrypt.Match" [ fillcolor="moccasin" label="Match" penwidth="1.5" ]
}
}
"blitter.com/go/hkexsh/hkexpasswd.main" -> "blitter.com/go/hkexsh.ReadPassword" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexpasswd.main" -> "github.com/jameskeane/bcrypt.Salt" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexpasswd.main" -> "github.com/jameskeane/bcrypt.Hash" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexpasswd.main" -> "github.com/jameskeane/bcrypt.Match" [ color="saddlebrown" ]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View file

@ -1,17 +0,0 @@
.PHONY: clean all vis lint
EXTPKGS = bytes,errors,flag,fmt,internal,io,log,net,os,path,runtime,time,strings,sync,syscall,binary,encoding
EXE = $(notdir $(shell pwd))
all:
go build .
clean:
$(RM) $(EXE) $(EXE).exe
vis:
go-callvis -file hkexsh-vis -format png -ignore $(EXTPKGS) -group pkg,type .
../fixup-gv.sh hkexsh.go && cat hkexsh-vis.gv | dot -Tpng -ohkexsh-vis-fixedup.png
lint:
-gometalinter --deadline=60s | sort

View file

@ -1 +0,0 @@
hkexsh

Binary file not shown.

Before

Width:  |  Height:  |  Size: 613 KiB

View file

@ -1,263 +0,0 @@
digraph gocallvis {
label="blitter.com/go/hkexsh/hkexsh";
labeljust="l";
fontname="Arial";
fontsize="14";
rankdir="LR";
bgcolor="lightgray";
style="solid";
penwidth="0.5";
pad="0.0";
nodesep="0.35";
node [shape="ellipse" style="filled" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"];
edge [minlen="2"]
subgraph "cluster_focus" {
fontsize="18";
bgcolor="#e6ecfa";
label="main";
labelloc="t";
labeljust="c";
"blitter.com/go/hkexsh/hkexsh.handleTermResizes$1" [ fillcolor="lightblue" label="handleTermResizes$1" style="dotted,filled" ]
"blitter.com/go/hkexsh/hkexsh.GetSize" [ fillcolor="lightblue" label="GetSize" penwidth="1.5" ]
"blitter.com/go/hkexsh/hkexsh.doCopyMode" [ fillcolor="lightblue" label="doCopyMode" penwidth="0.5" ]
"blitter.com/go/hkexsh/hkexsh.copyBuffer" [ fillcolor="lightblue" label="copyBuffer" penwidth="0.5" ]
"blitter.com/go/hkexsh/hkexsh.copyBuffer$1" [ style="dotted,filled" fillcolor="lightblue" label="copyBuffer$1" ]
"blitter.com/go/hkexsh/hkexsh.copyBuffer$2" [ fillcolor="lightblue" label="copyBuffer$2" style="dotted,filled" ]
"blitter.com/go/hkexsh/hkexsh.copyBuffer$3" [ fillcolor="lightblue" label="copyBuffer$3" style="dotted,filled" ]
"blitter.com/go/hkexsh/hkexsh.reqTunnel" [ fillcolor="lightblue" label="reqTunnel" penwidth="0.5" ]
"blitter.com/go/hkexsh/hkexsh.launchTuns" [ fillcolor="lightblue" label="launchTuns" penwidth="0.5" ]
"blitter.com/go/hkexsh/hkexsh.main$2" [ fillcolor="lightblue" label="deferCloseChaff" style="dotted,filled" ]
"blitter.com/go/hkexsh/hkexsh.sendSessionParams" [ label="sendSessionParams" penwidth="0.5" fillcolor="lightblue" ]
"blitter.com/go/hkexsh/hkexsh.handleTermResizes" [ fillcolor="lightblue" label="handleTermResizes" penwidth="0.5" ]
"blitter.com/go/hkexsh/hkexsh.Copy" [ penwidth="1.5" fillcolor="lightblue" label="Copy" ]
"blitter.com/go/hkexsh/hkexsh.doShellMode$2$1" [ fillcolor="lightblue" label="doShellMode$2$1" style="dotted,filled" ]
"blitter.com/go/hkexsh/hkexsh.doShellMode$2" [ fillcolor="lightblue" label="shellStdinToRemote" style="dotted,filled" ]
"blitter.com/go/hkexsh/hkexsh.main" [ fillcolor="lightblue" label="main" penwidth="0.5" ]
"blitter.com/go/hkexsh/hkexsh.parseNonSwitchArgs" [ fillcolor="lightblue" label="parseNonSwitchArgs" penwidth="0.5" ]
"blitter.com/go/hkexsh/hkexsh.main$1" [ fillcolor="lightblue" label="deferRestore" style="dotted,filled" ]
"blitter.com/go/hkexsh/hkexsh.rejectUserMsg" [ fillcolor="lightblue" label="rejectUserMsg" penwidth="0.5" ]
"blitter.com/go/hkexsh/hkexsh.doShellMode$1" [ fillcolor="lightblue" label="shellRemoteToStdin" style="dotted,filled" ]
"blitter.com/go/hkexsh/hkexsh.doShellMode$1$1" [ label="doShellMode$1$1" style="dotted,filled" fillcolor="lightblue" ]
"blitter.com/go/hkexsh/hkexsh.doShellMode" [ penwidth="0.5" fillcolor="lightblue" label="doShellMode" ]
"blitter.com/go/hkexsh/hkexsh.usageShell" [ fillcolor="lightblue" label="usageShell" penwidth="0.5" ]
"blitter.com/go/hkexsh/hkexsh.usageCp" [ fillcolor="lightblue" label="usageCp" penwidth="0.5" ]
subgraph "cluster_blitter.com/go/hkexsh" {
penwidth="0.8";
fontsize="16";
style="filled";
fillcolor="lightyellow";
fontname="bold";
rank="sink";
label="[hkexsh]";
URL="/?f=blitter.com/go/hkexsh";
tooltip="package: blitter.com/go/hkexsh";
"blitter.com/go/hkexsh.Restore" [ fillcolor="moccasin" label="Restore" penwidth="1.5" ]
"blitter.com/go/hkexsh.MakeRaw" [ penwidth="1.5" fillcolor="moccasin" label="MakeRaw" ]
"blitter.com/go/hkexsh.ReadPassword" [ fillcolor="moccasin" label="ReadPassword" penwidth="1.5" ]
"blitter.com/go/hkexsh.NewSession" [ fillcolor="moccasin" label="NewSession" penwidth="1.5" ]
subgraph "cluster_*blitter.com/go/hkexsh.Session" {
tooltip="type: *blitter.com/go/hkexsh.Session";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(*Session)";
"(*blitter.com/go/hkexsh.Session).SetStatus" [ label="SetStatus" penwidth="1.5" fillcolor="moccasin" ]
}
subgraph "cluster_blitter.com/go/hkexsh.Session" {
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(Session)";
tooltip="type: blitter.com/go/hkexsh.Session";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
"(blitter.com/go/hkexsh.Session).Cmd" [ label="Cmd" penwidth="1.5" fillcolor="moccasin" ]
"(blitter.com/go/hkexsh.Session).Op" [ fillcolor="moccasin" label="Op" penwidth="1.5" ]
"(blitter.com/go/hkexsh.Session).Who" [ fillcolor="moccasin" label="Who" penwidth="1.5" ]
"(blitter.com/go/hkexsh.Session).ConnHost" [ label="ConnHost" penwidth="1.5" fillcolor="moccasin" ]
"(blitter.com/go/hkexsh.Session).TermType" [ fillcolor="moccasin" label="TermType" penwidth="1.5" ]
"(blitter.com/go/hkexsh.Session).AuthCookie" [ fillcolor="moccasin" label="AuthCookie" penwidth="1.5" ]
"(blitter.com/go/hkexsh.Session).Status" [ fillcolor="moccasin" label="Status" penwidth="1.5" ]
}
}
subgraph "cluster_blitter.com/go/hkexsh/hkexnet" {
penwidth="0.8";
style="filled";
fillcolor="lightyellow";
fontname="bold";
label="[hkexnet]";
tooltip="package: blitter.com/go/hkexsh/hkexnet";
fontsize="16";
rank="sink";
URL="/?f=blitter.com/go/hkexsh/hkexnet";
"blitter.com/go/hkexsh/hkexnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" ]
"blitter.com/go/hkexsh/hkexnet.Dial" [ fillcolor="moccasin" label="Dial" penwidth="1.5" ]
subgraph "cluster_*blitter.com/go/hkexsh/hkexnet.Conn" {
style="rounded,filled";
fillcolor="wheat2";
label="(*Conn)";
tooltip="type: *blitter.com/go/hkexsh/hkexnet.Conn";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ label="WritePacket" penwidth="1.5" fillcolor="moccasin" ]
"(*blitter.com/go/hkexsh/hkexnet.Conn).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" ]
"(*blitter.com/go/hkexsh/hkexnet.Conn).Close" [ fillcolor="moccasin" label="Close" penwidth="1.5" ]
"(*blitter.com/go/hkexsh/hkexnet.Conn).SetupChaff" [ label="SetupChaff" penwidth="1.5" fillcolor="moccasin" ]
"(*blitter.com/go/hkexsh/hkexnet.Conn).EnableChaff" [ fillcolor="moccasin" label="EnableChaff" penwidth="1.5" ]
"(*blitter.com/go/hkexsh/hkexnet.Conn).DisableChaff" [ fillcolor="moccasin" label="DisableChaff" penwidth="1.5" ]
"(*blitter.com/go/hkexsh/hkexnet.Conn).ShutdownChaff" [ fillcolor="moccasin" label="ShutdownChaff" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/hkexsh/hkexnet.Conn" {
label="(Conn)";
tooltip="type: blitter.com/go/hkexsh/hkexnet.Conn";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
"(blitter.com/go/hkexsh/hkexnet.Conn).Read" [ fillcolor="moccasin" label="Read" penwidth="1.5" ]
"(blitter.com/go/hkexsh/hkexnet.Conn).GetStatus" [ label="GetStatus" penwidth="1.5" fillcolor="moccasin" ]
"(blitter.com/go/hkexsh/hkexnet.Conn).Write" [ fillcolor="moccasin" label="Write" penwidth="1.5" ]
}
}
subgraph "cluster_blitter.com/go/hkexsh/logger" {
fontsize="16";
rank="sink";
label="[logger]";
URL="/?f=blitter.com/go/hkexsh/logger";
tooltip="package: blitter.com/go/hkexsh/logger";
penwidth="0.8";
fillcolor="lightyellow";
fontname="bold";
style="filled";
"blitter.com/go/hkexsh/logger.LogDebug" [ label="LogDebug" penwidth="1.5" fillcolor="moccasin" ]
"blitter.com/go/hkexsh/logger.New" [ fillcolor="moccasin" label="New" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/hkexsh/spinsult" {
URL="/?f=blitter.com/go/hkexsh/spinsult";
tooltip="package: blitter.com/go/hkexsh/spinsult";
penwidth="0.8";
fontsize="16";
style="filled";
fillcolor="lightyellow";
fontname="bold";
rank="sink";
label="[spinsult]";
"blitter.com/go/hkexsh/spinsult.GetSentence" [ label="GetSentence" penwidth="1.5" fillcolor="moccasin" ]
}
subgraph "cluster_github.com/mattn/go-isatty" {
rank="sink";
label="[isatty]";
URL="/?f=github.com/mattn/go-isatty";
style="filled";
fontsize="16";
fillcolor="lightyellow";
fontname="bold";
tooltip="package: github.com/mattn/go-isatty";
penwidth="0.8";
"github.com/mattn/go-isatty.IsTerminal" [ fillcolor="moccasin" label="IsTerminal" penwidth="1.5" ]
}
}
"blitter.com/go/hkexsh/hkexsh.handleTermResizes$1" -> "blitter.com/go/hkexsh/hkexsh.GetSize" [ ]
"blitter.com/go/hkexsh/hkexsh.handleTermResizes$1" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.doCopyMode" -> "(blitter.com/go/hkexsh.Session).Cmd" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.doCopyMode" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.doCopyMode" -> "(blitter.com/go/hkexsh/hkexnet.Conn).Read" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.doCopyMode" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).SetStatus" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.doCopyMode" -> "(blitter.com/go/hkexsh/hkexnet.Conn).GetStatus" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.copyBuffer" -> "(blitter.com/go/hkexsh/hkexnet.Conn).Write" [ color="saddlebrown" style="dashed" ]
"blitter.com/go/hkexsh/hkexsh.copyBuffer" -> "blitter.com/go/hkexsh/hkexsh.copyBuffer$1" [ style="dashed" ]
"blitter.com/go/hkexsh/hkexsh.copyBuffer" -> "blitter.com/go/hkexsh/hkexsh.copyBuffer$2" [ style="dashed" ]
"blitter.com/go/hkexsh/hkexsh.copyBuffer" -> "blitter.com/go/hkexsh/hkexsh.copyBuffer$3" [ style="dashed" ]
"blitter.com/go/hkexsh/hkexsh.reqTunnel" -> "blitter.com/go/hkexsh/logger.LogDebug" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.reqTunnel" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.launchTuns" -> "blitter.com/go/hkexsh/hkexsh.reqTunnel" [ ]
"blitter.com/go/hkexsh/hkexsh.main$2" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.sendSessionParams" -> "(blitter.com/go/hkexsh.Session).Op" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.sendSessionParams" -> "(blitter.com/go/hkexsh.Session).Who" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.sendSessionParams" -> "(blitter.com/go/hkexsh.Session).ConnHost" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.sendSessionParams" -> "(blitter.com/go/hkexsh.Session).TermType" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.sendSessionParams" -> "(blitter.com/go/hkexsh.Session).Cmd" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.sendSessionParams" -> "(blitter.com/go/hkexsh.Session).AuthCookie" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.sendSessionParams" -> "(blitter.com/go/hkexsh/hkexnet.Conn).Write" [ color="saddlebrown" style="dashed" ]
"blitter.com/go/hkexsh/hkexsh.handleTermResizes" -> "blitter.com/go/hkexsh/hkexsh.handleTermResizes$1" [ arrowhead="normalnoneodot" ]
"blitter.com/go/hkexsh/hkexsh.Copy" -> "blitter.com/go/hkexsh/hkexsh.copyBuffer" [ ]
"blitter.com/go/hkexsh/hkexsh.doShellMode$2$1" -> "blitter.com/go/hkexsh/hkexsh.Copy" [ ]
"blitter.com/go/hkexsh/hkexsh.doShellMode$2" -> "blitter.com/go/hkexsh/hkexsh.doShellMode$2$1" [ ]
"blitter.com/go/hkexsh/hkexsh.doShellMode$2" -> "blitter.com/go/hkexsh.Restore" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.parseNonSwitchArgs" [ ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/logger.New" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexnet.Init" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexnet.Dial" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "github.com/mattn/go-isatty.IsTerminal" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh.MakeRaw" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main$1" -> "blitter.com/go/hkexsh.Restore" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.main$1" [ arrowhead="normalnoneodiamond" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh.ReadPassword" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh.NewSession" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.sendSessionParams" [ ]
"blitter.com/go/hkexsh/hkexsh.main" -> "(blitter.com/go/hkexsh/hkexnet.Conn).Read" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "(*blitter.com/go/hkexsh.Session).SetStatus" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.rejectUserMsg" -> "blitter.com/go/hkexsh/spinsult.GetSentence" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.rejectUserMsg" [ ]
"blitter.com/go/hkexsh/hkexsh.main" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).SetupChaff" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).EnableChaff" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).ShutdownChaff" [ color="saddlebrown" arrowhead="normalnoneodiamond" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.main$2" [ arrowhead="normalnoneodot" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.launchTuns" [ ]
"blitter.com/go/hkexsh/hkexsh.doShellMode$1" -> "blitter.com/go/hkexsh/hkexsh.doShellMode$1$1" [ arrowhead="normalnoneodiamond" ]
"blitter.com/go/hkexsh/hkexsh.doShellMode$1" -> "blitter.com/go/hkexsh.Restore" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.doShellMode$1" -> "(blitter.com/go/hkexsh/hkexnet.Conn).GetStatus" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.doShellMode$1" -> "(*blitter.com/go/hkexsh.Session).SetStatus" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.doShellMode$1" -> "(blitter.com/go/hkexsh.Session).Status" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.doShellMode" -> "blitter.com/go/hkexsh/hkexsh.doShellMode$1" [ arrowhead="normalnoneodot" ]
"blitter.com/go/hkexsh/hkexsh.doShellMode" -> "blitter.com/go/hkexsh/hkexsh.handleTermResizes" [ ]
"blitter.com/go/hkexsh/hkexsh.doShellMode" -> "blitter.com/go/hkexsh/hkexsh.doShellMode$2" [ arrowhead="normalnoneodot" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.doShellMode" [ ]
"blitter.com/go/hkexsh/hkexsh.main" -> "(blitter.com/go/hkexsh.Session).Status" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.doCopyMode" [ ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh.Restore" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.usageShell" [ style="dashed" ]
"blitter.com/go/hkexsh/hkexsh.main" -> "blitter.com/go/hkexsh/hkexsh.usageCp" [ style="dashed" ]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 630 KiB

View file

@ -1,281 +0,0 @@
digraph gocallvis {
label="blitter.com/go/hkexsh/hkexshd";
labeljust="l";
fontname="Arial";
fontsize="14";
rankdir="LR";
bgcolor="lightgray";
style="solid";
penwidth="0.5";
pad="0.0";
nodesep="0.35";
node [shape="ellipse" style="filled" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"];
edge [minlen="2"]
subgraph "cluster_focus" {
labeljust="c";
fontsize="18";
bgcolor="#e6ecfa";
label="main";
labelloc="t";
"blitter.com/go/hkexsh/hkexshd.runClientToServerCopyAs" [ label="runClientToServerCopyAs" penwidth="0.5" fillcolor="lightblue" ]
"blitter.com/go/hkexsh/hkexshd.main" [ fillcolor="lightblue" label="main" penwidth="0.5" ]
"blitter.com/go/hkexsh/hkexshd.main$1" [ style="dotted,filled" fillcolor="lightblue" label="main$1" ]
"blitter.com/go/hkexsh/hkexshd.main$2" [ fillcolor="lightblue" label="main$2" style="dotted,filled" ]
"blitter.com/go/hkexsh/hkexshd.GenAuthToken" [ penwidth="1.5" fillcolor="lightblue" label="GenAuthToken" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs" [ fillcolor="lightblue" label="runShellAs" penwidth="0.5" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs$1" [ style="dotted,filled" fillcolor="lightblue" label="deferPtmxClose" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs$2" [ style="dotted,filled" fillcolor="lightblue" label="termResizeWatcher" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs$3" [ style="dotted,filled" fillcolor="lightblue" label="stdinToPtyWorker" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs$4" [ fillcolor="lightblue" label="deferChaffShutdown" style="dotted,filled" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs$5" [ label="ptyToStdoutWorker" style="dotted,filled" fillcolor="lightblue" ]
"blitter.com/go/hkexsh/hkexshd.main$2$1" [ fillcolor="lightblue" label="main$2$1" style="dotted,filled" ]
"blitter.com/go/hkexsh/hkexshd.runServerToClientCopyAs" [ fillcolor="lightblue" label="runServerToClientCopyAs" penwidth="0.5" ]
subgraph "cluster_blitter.com/go/goutmp" {
URL="/?f=blitter.com/go/goutmp";
fontsize="16";
fillcolor="lightyellow";
fontname="bold";
rank="sink";
penwidth="0.8";
style="filled";
label="[goutmp]";
tooltip="package: blitter.com/go/goutmp";
"blitter.com/go/goutmp.GetHost" [ fillcolor="moccasin" label="GetHost" penwidth="1.5" ]
"blitter.com/go/goutmp.Put_utmp" [ fillcolor="moccasin" label="Put_utmp" penwidth="1.5" ]
"blitter.com/go/goutmp.Unput_utmp" [ fillcolor="moccasin" label="Unput_utmp" penwidth="1.5" ]
"blitter.com/go/goutmp.Put_lastlog_entry" [ fillcolor="moccasin" label="Put_lastlog_entry" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/hkexsh" {
tooltip="package: blitter.com/go/hkexsh";
style="filled";
fillcolor="lightyellow";
fontname="bold";
rank="sink";
URL="/?f=blitter.com/go/hkexsh";
penwidth="0.8";
fontsize="16";
label="[hkexsh]";
"blitter.com/go/hkexsh.AuthUserByToken" [ fillcolor="moccasin" label="AuthUserByToken" penwidth="1.5" ]
"blitter.com/go/hkexsh.AuthUserByPasswd" [ label="AuthUserByPasswd" penwidth="1.5" fillcolor="moccasin" ]
subgraph "cluster_*blitter.com/go/hkexsh.Session" {
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(*Session)";
tooltip="type: *blitter.com/go/hkexsh.Session";
"(*blitter.com/go/hkexsh.Session).SetOp" [ fillcolor="moccasin" label="SetOp" penwidth="1.5" ]
"(*blitter.com/go/hkexsh.Session).SetWho" [ fillcolor="moccasin" label="SetWho" penwidth="1.5" ]
"(*blitter.com/go/hkexsh.Session).SetConnHost" [ label="SetConnHost" penwidth="1.5" fillcolor="moccasin" ]
"(*blitter.com/go/hkexsh.Session).SetTermType" [ fillcolor="moccasin" label="SetTermType" penwidth="1.5" ]
"(*blitter.com/go/hkexsh.Session).SetCmd" [ fillcolor="moccasin" label="SetCmd" penwidth="1.5" ]
"(*blitter.com/go/hkexsh.Session).SetAuthCookie" [ penwidth="1.5" fillcolor="moccasin" label="SetAuthCookie" ]
"(*blitter.com/go/hkexsh.Session).ClearAuthCookie" [ fillcolor="moccasin" label="ClearAuthCookie" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/hkexsh.Session" {
style="rounded,filled";
fillcolor="wheat2";
label="(Session)";
tooltip="type: blitter.com/go/hkexsh.Session";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(blitter.com/go/hkexsh.Session).Op" [ fillcolor="moccasin" label="Op" penwidth="1.5" ]
"(blitter.com/go/hkexsh.Session).Who" [ fillcolor="moccasin" label="Who" penwidth="1.5" ]
"(blitter.com/go/hkexsh.Session).ConnHost" [ fillcolor="moccasin" label="ConnHost" penwidth="1.5" ]
"(blitter.com/go/hkexsh.Session).Cmd" [ label="Cmd" penwidth="1.5" fillcolor="moccasin" ]
"(blitter.com/go/hkexsh.Session).AuthCookie" [ fillcolor="moccasin" label="AuthCookie" penwidth="1.5" ]
"(blitter.com/go/hkexsh.Session).TermType" [ fillcolor="moccasin" label="TermType" penwidth="1.5" ]
}
}
subgraph "cluster_blitter.com/go/hkexsh/hkexnet" {
fontsize="16";
fontname="bold";
label="[hkexnet]";
penwidth="0.8";
style="filled";
fillcolor="lightyellow";
rank="sink";
URL="/?f=blitter.com/go/hkexsh/hkexnet";
tooltip="package: blitter.com/go/hkexsh/hkexnet";
"blitter.com/go/hkexsh/hkexnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" ]
"blitter.com/go/hkexsh/hkexnet.Listen" [ penwidth="1.5" fillcolor="moccasin" label="Listen" ]
subgraph "cluster_*blitter.com/go/hkexsh/hkexnet.Conn" {
label="(*Conn)";
tooltip="type: *blitter.com/go/hkexsh/hkexnet.Conn";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
"(*blitter.com/go/hkexsh/hkexnet.Conn).EnableChaff" [ penwidth="1.5" fillcolor="moccasin" label="EnableChaff" ]
"(*blitter.com/go/hkexsh/hkexnet.Conn).DisableChaff" [ fillcolor="moccasin" label="DisableChaff" penwidth="1.5" ]
"(*blitter.com/go/hkexsh/hkexnet.Conn).ShutdownChaff" [ fillcolor="moccasin" label="ShutdownChaff" penwidth="1.5" ]
"(*blitter.com/go/hkexsh/hkexnet.Conn).SetupChaff" [ fillcolor="moccasin" label="SetupChaff" penwidth="1.5" ]
"(*blitter.com/go/hkexsh/hkexnet.Conn).Close" [ fillcolor="moccasin" label="Close" penwidth="1.5" ]
"(*blitter.com/go/hkexsh/hkexnet.Conn).RemoteAddr" [ penwidth="1.5" fillcolor="moccasin" label="RemoteAddr" ]
"(*blitter.com/go/hkexsh/hkexnet.Conn).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" ]
"(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ fillcolor="moccasin" label="WritePacket" penwidth="1.5" ]
}
subgraph "cluster_*blitter.com/go/hkexsh/hkexnet.HKExListener" {
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(*HKExListener)";
tooltip="type: *blitter.com/go/hkexsh/hkexnet.HKExListener";
"(*blitter.com/go/hkexsh/hkexnet.HKExListener).Accept" [ penwidth="1.5" fillcolor="moccasin" label="Accept" ]
}
subgraph "cluster_blitter.com/go/hkexsh/hkexnet.Conn" {
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(Conn)";
tooltip="type: blitter.com/go/hkexsh/hkexnet.Conn";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
"(blitter.com/go/hkexsh/hkexnet.Conn).Write" [ fillcolor="moccasin" label="Write" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/hkexsh/hkexnet.HKExListener" {
fillcolor="wheat2";
label="(HKExListener)";
tooltip="type: blitter.com/go/hkexsh/hkexnet.HKExListener";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
"(blitter.com/go/hkexsh/hkexnet.HKExListener).Close" [ label="Close" penwidth="1.5" fillcolor="moccasin" ]
}
}
subgraph "cluster_blitter.com/go/hkexsh/logger" {
fillcolor="lightyellow";
rank="sink";
label="[logger]";
URL="/?f=blitter.com/go/hkexsh/logger";
fontsize="16";
style="filled";
fontname="bold";
tooltip="package: blitter.com/go/hkexsh/logger";
penwidth="0.8";
"blitter.com/go/hkexsh/logger.New" [ fillcolor="moccasin" label="New" penwidth="1.5" ]
"blitter.com/go/hkexsh/logger.LogNotice" [ fillcolor="moccasin" label="LogNotice" penwidth="1.5" ]
"blitter.com/go/hkexsh/logger.LogErr" [ fillcolor="moccasin" label="LogErr" penwidth="1.5" ]
}
subgraph "cluster_github.com/kr/pty" {
URL="/?f=github.com/kr/pty";
tooltip="package: github.com/kr/pty";
style="filled";
fontname="bold";
rank="sink";
label="[pty]";
penwidth="0.8";
fontsize="16";
fillcolor="lightyellow";
"github.com/kr/pty.Start" [ fillcolor="moccasin" label="Start" penwidth="1.5" ]
"github.com/kr/pty.Setsize" [ fillcolor="moccasin" label="Setsize" penwidth="1.5" ]
}
}
"blitter.com/go/hkexsh/hkexshd.runClientToServerCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).EnableChaff" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.runClientToServerCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).DisableChaff" [ color="saddlebrown" arrowhead="normalnoneodiamond" ]
"blitter.com/go/hkexsh/hkexshd.runClientToServerCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).ShutdownChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main" -> "blitter.com/go/hkexsh/logger.New" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main" -> "blitter.com/go/hkexsh/hkexnet.Init" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$1" -> "blitter.com/go/hkexsh/logger.LogNotice" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main" -> "blitter.com/go/hkexsh/hkexshd.main$1" [ arrowhead="normalnoneodot" ]
"blitter.com/go/hkexsh/hkexshd.main" -> "blitter.com/go/hkexsh/hkexnet.Listen" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main" -> "(blitter.com/go/hkexsh/hkexnet.HKExListener).Close" [ color="saddlebrown" arrowhead="normalnoneodiamond" ]
"blitter.com/go/hkexsh/hkexshd.main" -> "(*blitter.com/go/hkexsh/hkexnet.HKExListener).Accept" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).SetupChaff" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh.Session).SetOp" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh.Session).SetWho" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh.Session).SetConnHost" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh.Session).SetTermType" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh.Session).SetCmd" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh.Session).SetAuthCookie" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(blitter.com/go/hkexsh.Session).Op" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(blitter.com/go/hkexsh.Session).Who" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(blitter.com/go/hkexsh.Session).ConnHost" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(blitter.com/go/hkexsh.Session).Cmd" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(blitter.com/go/hkexsh.Session).AuthCookie" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh.AuthUserByToken" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh.Session).ClearAuthCookie" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh.AuthUserByPasswd" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(blitter.com/go/hkexsh/hkexnet.Conn).Write" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh/logger.LogNotice" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).RemoteAddr" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/goutmp.GetHost" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh/hkexshd.GenAuthToken" [ ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(blitter.com/go/hkexsh.Session).TermType" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs" -> "github.com/kr/pty.Start" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs" -> "blitter.com/go/hkexsh/hkexshd.runShellAs$1" [ arrowhead="normalnoneodiamond" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs$2" -> "github.com/kr/pty.Setsize" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs" -> "blitter.com/go/hkexsh/hkexshd.runShellAs$2" [ arrowhead="normalnoneodot" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs" -> "blitter.com/go/hkexsh/hkexshd.runShellAs$3" [ arrowhead="normalnoneodot" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).EnableChaff" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs$4" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).DisableChaff" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs$4" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).ShutdownChaff" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs" -> "blitter.com/go/hkexsh/hkexshd.runShellAs$4" [ arrowhead="normalnoneodiamond" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs" -> "blitter.com/go/hkexsh/hkexshd.runShellAs$5" [ arrowhead="normalnoneodot" ]
"blitter.com/go/hkexsh/hkexshd.runShellAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).SetStatus" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh/hkexshd.runShellAs" [ ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh/logger.LogErr" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).SetStatus" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/goutmp.Put_utmp" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2$1" -> "blitter.com/go/goutmp.Unput_utmp" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh/hkexshd.main$2$1" [ arrowhead="normalnoneodiamond" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/goutmp.Put_lastlog_entry" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh/hkexshd.runClientToServerCopyAs" [ ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.runServerToClientCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).EnableChaff" [ color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.runServerToClientCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.runServerToClientCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).ShutdownChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/hkexsh/hkexshd.main$2" -> "blitter.com/go/hkexsh/hkexshd.runServerToClientCopyAs" [ ]
"blitter.com/go/hkexsh/hkexshd.main" -> "blitter.com/go/hkexsh/hkexshd.main$2" [ arrowhead="normalnoneodot" ]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 626 KiB

156
logger/logger_bsd.go Normal file
View file

@ -0,0 +1,156 @@
// +build freebsd
// Package logger is a wrapper around UNIX syslog, so that it also may
// be wrapped with something else for Windows (Sadly, the stdlib log/syslog
// is frozen, and there is no Windows implementation.)
package logger
import (
sl "log/syslog"
)
// Priority is the logger priority
type Priority = sl.Priority
// Writer is a syslog Writer
type Writer = sl.Writer
// nolint: golint
const (
// Severity.
// From /usr/include/sys/syslog.h.
// These are the same on Linux, BSD, and OS X.
LOG_EMERG Priority = iota
LOG_ALERT
LOG_CRIT
LOG_ERR
LOG_WARNING
LOG_NOTICE
LOG_INFO
LOG_DEBUG
)
// nolint: golint
const (
// Facility.
// From /usr/include/sys/syslog.h.
// These are the same up to LOG_FTP on Linux, BSD, and OS X.
LOG_KERN Priority = iota << 3
LOG_USER
LOG_MAIL
LOG_DAEMON
LOG_AUTH
LOG_SYSLOG
LOG_LPR
LOG_NEWS
LOG_UUCP
LOG_CRON
LOG_AUTHPRIV
LOG_FTP
_ // unused
_ // unused
_ // unused
_ // unused
LOG_LOCAL0
LOG_LOCAL1
LOG_LOCAL2
LOG_LOCAL3
LOG_LOCAL4
LOG_LOCAL5
LOG_LOCAL6
LOG_LOCAL7
)
var (
l *sl.Writer
)
// New returns a new log Writer.
func New(flags Priority, tag string) (w *Writer, e error) {
w, e = sl.New(flags, tag)
l = w
return w, e
}
// Alert returns a log Alert error
func Alert(s string) error {
if l != nil {
return l.Alert(s)
}
return nil
}
// LogClose closes the log Writer.
func LogClose() error {
if l != nil {
return l.Close()
}
return nil
}
// LogCrit returns a log Alert error
func LogCrit(s string) error {
if l != nil {
return l.Crit(s)
}
return nil
}
// LogDebug returns a log Debug error
func LogDebug(s string) error {
if l != nil {
return l.Debug(s)
}
return nil
}
// LogEmerg returns a log Emerg error
func LogEmerg(s string) error {
if l != nil {
return l.Emerg(s)
}
return nil
}
// LogErr returns a log Err error
func LogErr(s string) error {
if l != nil {
return l.Err(s)
}
return nil
}
// LogInfo returns a log Info error
func LogInfo(s string) error {
if l != nil {
return l.Info(s)
}
return nil
}
// LogNotice returns a log Notice error
func LogNotice(s string) error {
if l != nil {
return l.Notice(s)
}
return nil
}
// LogWarning returns a log Warning error
func LogWarning(s string) error {
if l != nil {
return l.Warning(s)
}
return nil
}
// LogWrite writes to the logger at default level
func LogWrite(b []byte) (int, error) {
if l != nil {
return l.Write(b)
}
return len(b),nil
}

View file

@ -11,6 +11,7 @@ import (
// Priority is the logger priority
type Priority = sl.Priority
// Writer is a syslog Writer
type Writer = sl.Writer
@ -75,50 +76,81 @@ func New(flags Priority, tag string) (w *Writer, e error) {
// Alert returns a log Alert error
func Alert(s string) error {
if l != nil {
return l.Alert(s)
}
return nil
}
// LogClose closes the log Writer.
func LogClose() error {
if l != nil {
return l.Close()
}
return nil
}
// LogCrit returns a log Alert error
func LogCrit(s string) error {
if l != nil {
return l.Crit(s)
}
return nil
}
// LogDebug returns a log Debug error
func LogDebug(s string) error {
if l != nil {
return l.Debug(s)
}
return nil
}
// LogEmerg returns a log Emerg error
func LogEmerg(s string) error {
if l != nil {
return l.Emerg(s)
}
return nil
}
// LogErr returns a log Err error
func LogErr(s string) error {
if l != nil {
return l.Err(s)
}
return nil
}
// LogInfo returns a log Info error
func LogInfo(s string) error {
if l != nil {
return l.Info(s)
}
return nil
}
// LogNotice returns a log Notice error
func LogNotice(s string) error {
if l != nil {
return l.Notice(s)
}
return nil
}
// LogWarning returns a log Warning error
func LogWarning(s string) error {
if l != nil {
return l.Warning(s)
}
return nil
}
// LogWrite writes to the logger at default level
func LogWrite(b []byte) (int, error) {
if l != nil {
return l.Write(b)
}
return len(b),nil
}

View file

@ -1,5 +1,5 @@
// +build windows
//
// Wrapper around UNIX syslog, so that it also may be wrapped
// with something else for Windows.
package logger

View file

@ -1,8 +1,8 @@
package hkexsh
package xs
// Package hkexsh - a secure terminal client/server written from scratch in Go
// Package xs - a secure terminal client/server written from scratch in Go
//
// Copyright (c) 2017-2018 Russell Magee
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -28,7 +28,7 @@ type Session struct {
// Output Session record as a string. Implements Stringer interface.
func (h *Session) String() string {
return fmt.Sprintf("hkexsh.Session:\nOp:%v\nWho:%v\nCmd:%v\nAuthCookie:%v\nStatus:%v",
return fmt.Sprintf("xs.Session:\nOp:%v\nWho:%v\nCmd:%v\nAuthCookie:%v\nStatus:%v",
h.op, h.who, h.cmd, h.AuthCookie(false), h.status)
}
@ -96,7 +96,7 @@ func (h Session) AuthCookie(reallyShow bool) []byte {
return []byte("**REDACTED**")
}
// SetAuthCookie stores the authcookie (essential the password) used to
// SetAuthCookie stores the authcookie (essentially the password) used to
// authenticate the Session.
func (h *Session) SetAuthCookie(a []byte) {
h.authCookie = a

30
session_test.go Normal file
View file

@ -0,0 +1,30 @@
package xs
import (
"testing"
)
func _newMockSession() (s *Session) {
s = &Session{op: []byte("A"),
who: []byte("johndoe"),
connhost: []byte("host"),
termtype: []byte("vt100"),
cmd: []byte("/bin/false"),
authCookie: []byte("authcookie"),
status: 0}
return s
}
func TestSessionAuthCookieShowTrue(t *testing.T) {
sess := _newMockSession()
if string(sess.AuthCookie(true)) != string(sess.authCookie) {
t.Fatal("Failed to return unredacted authcookie on request")
}
}
func TestSessionAuthCookieShowFalse(t *testing.T) {
sess := _newMockSession()
if string(sess.AuthCookie(false)) != string("**REDACTED**") {
t.Fatal("Failed to return redacted authcookie on request")
}
}

128
termmode_bsd.go Normal file
View file

@ -0,0 +1,128 @@
// +build freebsd
package xs
import (
"errors"
"io"
"unsafe"
unix "golang.org/x/sys/unix"
)
/* -------------
* minimal terminal APIs brought in from ssh/terminal
* (they have no real business being there as they aren't specific to
* ssh, but as of Go v1.10, late 2019, core go stdlib hasn't yet done
* the planned terminal lib reorgs.)
* ------------- */
// From github.com/golang/crypto/blob/master/ssh/terminal/util_linux.go
const getTermios = unix.TIOCGETA
const setTermios = unix.TIOCSETA
// From github.com/golang/crypto/blob/master/ssh/terminal/util.go
// State contains the state of a terminal.
type State struct {
termios unix.Termios
}
// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd uintptr) (*State, error) {
var oldState State
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
return nil, err
}
newState := oldState.termios
newState.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON)
newState.Oflag &^= unix.OPOST
newState.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
newState.Cflag &^= (unix.CSIZE | unix.PARENB)
newState.Cflag |= unix.CS8
newState.Cc[unix.VMIN] = 1
newState.Cc[unix.VTIME] = 0
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
return nil, err
}
return &oldState, nil
}
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd uintptr, state *State) error {
if state != nil {
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(state))); err != 0 {
return err
} else {
return nil
}
} else {
return errors.New("nil State")
}
}
// ReadPassword reads a line of input from a terminal without local echo. This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd uintptr) ([]byte, error) {
var oldState State
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
return nil, err
}
newState := oldState.termios
newState.Lflag &^= unix.ECHO
newState.Lflag |= unix.ICANON | unix.ISIG
newState.Iflag |= unix.ICRNL
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
return nil, err
}
defer func() {
unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&oldState.termios)))
}()
return readPasswordLine(passwordReader(fd))
}
// passwordReader is an io.Reader that reads from a specific file descriptor.
type passwordReader int
func (r passwordReader) Read(buf []byte) (int, error) {
return unix.Read(int(r), buf)
}
// readPasswordLine reads from reader until it finds \n or io.EOF.
// The slice returned does not include the \n.
// readPasswordLine also ignores any \r it finds.
func readPasswordLine(reader io.Reader) ([]byte, error) {
var buf [1]byte
var ret []byte
for {
n, err := reader.Read(buf[:])
if n > 0 {
switch buf[0] {
case '\n':
return ret, nil
case '\r':
// remove \r from passwords on Windows
default:
ret = append(ret, buf[0])
}
continue
}
if err != nil {
if err == io.EOF && len(ret) > 0 {
return ret, nil
}
return ret, err
}
}
}

View file

@ -1,8 +1,9 @@
// +build linux
package hkexsh
package xs
import (
"errors"
"io"
unix "golang.org/x/sys/unix"
@ -11,7 +12,7 @@ import (
/* -------------
* minimal terminal APIs brought in from ssh/terminal
* (they have no real business being there as they aren't specific to
* ssh, but as of Go v1.10, early 2018, core go stdlib hasn't yet done
* ssh, but as of Go v1.10, late 2019, core go stdlib hasn't yet done
* the planned terminal lib reorgs.)
* ------------- */
@ -29,8 +30,8 @@ type State struct {
// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd int) (*State, error) {
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
func MakeRaw(fd uintptr) (*State, error) {
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil {
return nil, err
}
@ -46,7 +47,7 @@ func MakeRaw(fd int) (*State, error) {
termios.Cflag |= unix.CS8
termios.Cc[unix.VMIN] = 1
termios.Cc[unix.VTIME] = 0
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil {
if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios); err != nil {
return nil, err
}
@ -55,8 +56,8 @@ func MakeRaw(fd int) (*State, error) {
// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd int) (*State, error) {
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
func GetState(fd uintptr) (*State, error) {
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil {
return nil, err
}
@ -66,15 +67,19 @@ func GetState(fd int) (*State, error) {
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd int, state *State) error {
return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
func Restore(fd uintptr, state *State) error {
if state != nil {
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.termios)
} else {
return errors.New("nil State")
}
}
// ReadPassword reads a line of input from a terminal without local echo. This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd int) ([]byte, error) {
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
func ReadPassword(fd uintptr) ([]byte, error) {
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil {
return nil, err
}
@ -83,12 +88,12 @@ func ReadPassword(fd int) ([]byte, error) {
newState.Lflag &^= unix.ECHO
newState.Lflag |= unix.ICANON | unix.ISIG
newState.Iflag |= unix.ICRNL
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil {
if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &newState); err != nil {
return nil, err
}
defer func() {
_ = unix.IoctlSetTermios(fd, ioctlWriteTermios, termios) // nolint: gosec
_ = unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios) // nolint: gosec
}()
return readPasswordLine(passwordReader(fd))

View file

@ -1,7 +1,7 @@
// +build windows
//
// Note the terminal manipulation functions herein are mostly stubs. They
// don't really do anything and the hkexsh demo client depends on a wrapper
// don't really do anything and the xs demo client depends on a wrapper
// script using the 'stty' tool to actually set the proper mode for
// password login and raw mode required, then restoring it upon logout/exit.
//
@ -12,7 +12,7 @@
// here; the wrapper does the bare minimum to make the client workable
// under MSYS+mintty which is what I use.
package hkexsh
package xs
import (
"io"
@ -27,7 +27,7 @@ type State struct {
// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd int) (*State, error) {
func MakeRaw(fd uintptr) (*State, error) {
// This doesn't really work. The exec.Command() runs a sub-shell
// so the stty mods don't affect the client process.
cmd := exec.Command("stty", "-echo raw")
@ -37,13 +37,13 @@ func MakeRaw(fd int) (*State, error) {
// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd int) (*State, error) {
func GetState(fd uintptr) (*State, error) {
return &State{}, nil
}
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd int, state *State) error {
func Restore(fd uintptr, state *State) error {
cmd := exec.Command("stty", "echo cooked")
cmd.Run()
return nil
@ -52,7 +52,7 @@ func Restore(fd int, state *State) error {
// ReadPassword reads a line of input from a terminal without local echo. This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd int) ([]byte, error) {
func ReadPassword(fd uintptr) ([]byte, error) {
return readPasswordLine(passwordReader(fd))
}

View file

Before

Width:  |  Height:  |  Size: 675 KiB

After

Width:  |  Height:  |  Size: 675 KiB

Before After
Before After

14
xc_testfiles.sh Executable file
View file

@ -0,0 +1,14 @@
#!/bin/bash
## setup.sh - create some files for xc copy testing
dir=cptest
mkdir -p ${dir}/subdir
dd bs=1024 count=16 if=/dev/urandom of=${dir}/file16KB
dd bs=1024 count=16 if=/dev/urandom of=${dir}/file1KB
dd bs=1024 count=16 if=/dev/urandom of=${dir}/file32KB
dd bs=1024 count=16 if=/dev/urandom of=${dir}/file6B
dd bs=1048576 count=32 if=/dev/urandom of=${dir}/subdir/file32MB
dd bs=1048576 count=64 if=/dev/urandom of=${dir}/subdir/file64MB
sha1sum $(find ${dir} -type f | sort) >${dir}.sha1sum

18
xs/Makefile Normal file
View file

@ -0,0 +1,18 @@
.PHONY: clean all vis lint
EXTPKGS = bytes,errors,flag,fmt,internal,io,log,net,os,path,runtime,time,strings,sync,syscall,binary,encoding
EXE = $(notdir $(shell pwd))
all:
echo "BUILDOPTS:" $(BUILDOPTS)
go build $(BUILDOPTS) .
clean:
$(RM) $(EXE) $(EXE).exe
vis:
go-callvis -file xs-vis -format png -ignore $(EXTPKGS) -group pkg,type .
../fixup-gv.sh xs.go && cat xs-vis.gv | dot -Tpng -oxs-vis-fixedup.png
lint:
-golangci-lint run

View file

@ -28,10 +28,12 @@ cleanup() {
stty sane
}
me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"
if [ ${1}x == "-hx" ]; then
./hkexsh -h
_${me} -h
else
stty -echo raw icrnl
./hkexsh $@
_${me} $@
fi

View file

@ -1,4 +1,4 @@
// +build linux
// +build linux freebsd
package main
@ -9,11 +9,11 @@ import (
"os/signal"
"syscall"
"blitter.com/go/hkexsh/hkexnet"
"blitter.com/go/xs/xsnet"
)
// Handle pty resizes (notify server side)
func handleTermResizes(conn *hkexnet.Conn) {
func handleTermResizes(conn *xsnet.Conn) {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGWINCH)
wg.Add(1)
@ -30,7 +30,7 @@ func handleTermResizes(conn *hkexnet.Conn) {
log.Println(err)
}
termSzPacket := fmt.Sprintf("%d %d", rows, cols)
conn.WritePacket([]byte(termSzPacket), hkexnet.CSOTermSize) // nolint: errcheck,gosec
conn.WritePacket([]byte(termSzPacket), xsnet.CSOTermSize) // nolint: errcheck,gosec
}
}()
ch <- syscall.SIGWINCH // Initial resize.

View file

@ -6,11 +6,11 @@ import (
"log"
"time"
"blitter.com/go/hkexsh/hkexnet"
"blitter.com/go/xs/xsnet"
)
// Handle pty resizes (notify server side)
func handleTermResizes(conn *hkexnet.Conn) {
func handleTermResizes(conn *xsnet.Conn) {
var hasStty bool
curCols, curRows := 0, 0
_, _, err := GetSize()
@ -57,7 +57,7 @@ func handleTermResizes(conn *hkexnet.Conn) {
log.Println(err)
}
termSzPacket := fmt.Sprintf("%d %d", curRows, curCols)
conn.WritePacket([]byte(termSzPacket), hkexnet.CSOTermSize)
conn.WritePacket([]byte(termSzPacket), xsnet.CSOTermSize)
}
}
}()

BIN
xs/xs-vis-fixedup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

549
xs/xs-vis.gv Executable file
View file

@ -0,0 +1,549 @@
digraph gocallvis {
label="blitter.com/go/xs/xs";
labeljust="l";
fontname="Arial";
fontsize="14";
rankdir="LR";
bgcolor="lightgray";
style="solid";
penwidth="0.5";
pad="0.0";
nodesep="0.35";
node [shape="ellipse" style="filled" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"];
edge [minlen="2"]
subgraph "cluster_focus" {
bgcolor="#e6ecfa";
label="main";
labelloc="t";
labeljust="c";
fontsize="18";
"blitter.com/go/xs/xs.reqTunnel" [ fillcolor="lightblue" label="reqTunnel" penwidth="0.5" ]
"blitter.com/go/xs/xs.launchTuns" [ label="launchTuns" penwidth="0.5" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.main$3" [ fillcolor="lightblue" label="main$3" style="dotted,filled" ]
"blitter.com/go/xs/xs.doCopyMode" [ fillcolor="lightblue" label="doCopyMode" penwidth="0.5" ]
"blitter.com/go/xs/xs.copyBuffer" [ fillcolor="lightblue" label="copyBuffer" penwidth="0.5" ]
"blitter.com/go/xs/xs.copyBuffer$1" [ label="copyBuffer$1" style="dotted,filled" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.copyBuffer$2" [ label="copyBuffer$2" style="dotted,filled" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.copyBuffer$3" [ fillcolor="lightblue" label="copyBuffer$3" style="dotted,filled" ]
"blitter.com/go/xs/xs.Copy" [ label="Copy" penwidth="1.5" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.doShellMode$1" [ fillcolor="lightblue" label="shellRemoteToStdin" style="dotted,filled" ]
"blitter.com/go/xs/xs.doShellMode$1$1" [ fillcolor="lightblue" label="doShellMode$1$1" style="dotted,filled" ]
"blitter.com/go/xs/xs.exitWithStatus" [ fillcolor="lightblue" label="exitWithStatus" penwidth="0.5" ]
"blitter.com/go/xs/xs.doShellMode" [ label="doShellMode" penwidth="0.5" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.handleTermResizes$1" [ fillcolor="lightblue" label="handleTermResizes$1" style="dotted,filled" ]
"blitter.com/go/xs/xs.GetSize" [ fillcolor="lightblue" label="GetSize" penwidth="1.5" ]
"blitter.com/go/xs/xs.handleTermResizes" [ label="handleTermResizes" penwidth="0.5" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.doShellMode$2$1" [ fillcolor="lightblue" label="doShellMode$2$1" style="dotted,filled" ]
"blitter.com/go/xs/xs.doShellMode$2" [ fillcolor="lightblue" label="shellStdinToRemote" style="dotted,filled" ]
"blitter.com/go/xs/xs.sendSessionParams" [ fillcolor="lightblue" label="sendSessionParams" penwidth="0.5" ]
"blitter.com/go/xs/xs.main" [ label="main" penwidth="0.5" fillcolor="lightblue" ]
"blitter.com/go/xs/xs.parseNonSwitchArgs" [ fillcolor="lightblue" label="parseNonSwitchArgs" penwidth="0.5" ]
"blitter.com/go/xs/xs.main$1" [ fillcolor="lightblue" label="deferRestore" style="dotted,filled" ]
"blitter.com/go/xs/xs.main$2" [ fillcolor="lightblue" label="deferCloseChaff" style="dotted,filled" ]
"blitter.com/go/xs/xs.rejectUserMsg" [ fillcolor="lightblue" label="rejectUserMsg" penwidth="0.5" ]
"blitter.com/go/xs/xs.usageShell" [ fillcolor="lightblue" label="usageShell" penwidth="0.5" ]
"blitter.com/go/xs/xs.usageCp" [ label="usageCp" penwidth="0.5" fillcolor="lightblue" ]
subgraph "cluster_blitter.com/go/xs" {
penwidth="0.8";
style="filled";
rank="sink";
tooltip="package: blitter.com/go/xs";
fontsize="16";
fillcolor="lightyellow";
fontname="bold";
label="[xs]";
URL="/?f=blitter.com/go/xs";
"blitter.com/go/xs.Restore" [ fillcolor="moccasin" label="Restore" penwidth="1.5" ]
"blitter.com/go/xs.MakeRaw" [ penwidth="1.5" fillcolor="moccasin" label="MakeRaw" ]
"blitter.com/go/xs.ReadPassword" [ fillcolor="moccasin" label="ReadPassword" penwidth="1.5" ]
"blitter.com/go/xs.NewSession" [ fillcolor="moccasin" label="NewSession" penwidth="1.5" ]
subgraph "cluster_*blitter.com/go/xs.Session" {
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(*Session)";
tooltip="type: *blitter.com/go/xs.Session";
penwidth="0.5";
"(*blitter.com/go/xs.Session).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/xs.Session" {
style="rounded,filled";
fillcolor="wheat2";
label="(Session)";
tooltip="type: blitter.com/go/xs.Session";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(blitter.com/go/xs.Session).Cmd" [ fillcolor="moccasin" label="Cmd" penwidth="1.5" ]
"(blitter.com/go/xs.Session).Status" [ penwidth="1.5" fillcolor="moccasin" label="Status" ]
"(blitter.com/go/xs.Session).Op" [ fillcolor="moccasin" label="Op" penwidth="1.5" ]
"(blitter.com/go/xs.Session).Who" [ fillcolor="moccasin" label="Who" penwidth="1.5" ]
"(blitter.com/go/xs.Session).ConnHost" [ fillcolor="moccasin" label="ConnHost" penwidth="1.5" ]
"(blitter.com/go/xs.Session).TermType" [ fillcolor="moccasin" label="TermType" penwidth="1.5" ]
"(blitter.com/go/xs.Session).AuthCookie" [ label="AuthCookie" penwidth="1.5" fillcolor="moccasin" ]
}
}
subgraph "cluster_blitter.com/go/xs/logger" {
fontsize="16";
URL="/?f=blitter.com/go/xs/logger";
penwidth="0.8";
style="filled";
fillcolor="lightyellow";
fontname="bold";
rank="sink";
label="[logger]";
tooltip="package: blitter.com/go/xs/logger";
"blitter.com/go/xs/logger.LogDebug" [ fillcolor="moccasin" label="LogDebug" penwidth="1.5" ]
"blitter.com/go/xs/logger.New" [ fillcolor="moccasin" label="New" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/xs/spinsult" {
fillcolor="lightyellow";
fontname="bold";
label="[spinsult]";
URL="/?f=blitter.com/go/xs/spinsult";
tooltip="package: blitter.com/go/xs/spinsult";
penwidth="0.8";
fontsize="16";
style="filled";
rank="sink";
"blitter.com/go/xs/spinsult.GetSentence" [ fillcolor="moccasin" label="GetSentence" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/xs/xsnet" {
penwidth="0.8";
fillcolor="lightyellow";
fontname="bold";
rank="sink";
URL="/?f=blitter.com/go/xs/xsnet";
fontsize="16";
style="filled";
label="[xsnet]";
tooltip="package: blitter.com/go/xs/xsnet";
"blitter.com/go/xs/xsnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" ]
"blitter.com/go/xs/xsnet.Dial" [ label="Dial" penwidth="1.5" fillcolor="moccasin" ]
subgraph "cluster_*blitter.com/go/xs/xsnet.Conn" {
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(*Conn)";
tooltip="type: *blitter.com/go/xs/xsnet.Conn";
penwidth="0.5";
fontsize="15";
"(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ penwidth="1.5" fillcolor="moccasin" label="WritePacket" ]
"(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" ]
"(*blitter.com/go/xs/xsnet.Conn).Close" [ penwidth="1.5" fillcolor="moccasin" label="Close" ]
"(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ penwidth="1.5" fillcolor="moccasin" label="SetupChaff" ]
"(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ fillcolor="moccasin" label="EnableChaff" penwidth="1.5" ]
"(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ fillcolor="moccasin" label="DisableChaff" penwidth="1.5" ]
"(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ fillcolor="moccasin" label="ShutdownChaff" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/xs/xsnet.Conn" {
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(Conn)";
tooltip="type: blitter.com/go/xs/xsnet.Conn";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
"(blitter.com/go/xs/xsnet.Conn).Read" [ label="Read" penwidth="1.5" fillcolor="moccasin" ]
"(blitter.com/go/xs/xsnet.Conn).GetStatus" [ fillcolor="moccasin" label="GetStatus" penwidth="1.5" ]
"(blitter.com/go/xs/xsnet.Conn).Write" [ label="Write" penwidth="1.5" fillcolor="moccasin" ]
}
}
subgraph "cluster_compress/flate" {
fontsize="16";
fillcolor="#E0FFE1";
label="[compress/flate]";
URL="/?f=compress/flate";
penwidth="0.8";
style="filled";
fontname="bold";
rank="sink";
tooltip="package: compress/flate";
subgraph "cluster_compress/flate.CorruptInputError" {
tooltip="type: compress/flate.CorruptInputError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(CorruptInputError)";
"(compress/flate.CorruptInputError).Error" [ penwidth="1.5" fillcolor="#adedad" label="Error" ]
}
subgraph "cluster_compress/flate.InternalError" {
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(InternalError)";
tooltip="type: compress/flate.InternalError";
penwidth="0.5";
"(compress/flate.InternalError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
}
subgraph "cluster_context" {
fontsize="16";
style="filled";
penwidth="0.8";
fontname="bold";
rank="sink";
label="[context]";
URL="/?f=context";
tooltip="package: context";
fillcolor="#E0FFE1";
subgraph "cluster_context.deadlineExceededError" {
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(deadlineExceededError)";
tooltip="type: context.deadlineExceededError";
penwidth="0.5";
"(context.deadlineExceededError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
}
subgraph "cluster_crypto/aes" {
style="filled";
fillcolor="#E0FFE1";
rank="sink";
label="[crypto/aes]";
penwidth="0.8";
fontsize="16";
fontname="bold";
URL="/?f=crypto/aes";
tooltip="package: crypto/aes";
subgraph "cluster_crypto/aes.KeySizeError" {
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(KeySizeError)";
tooltip="type: crypto/aes.KeySizeError";
penwidth="0.5";
fontsize="15";
"(crypto/aes.KeySizeError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
}
subgraph "cluster_crypto/tls" {
tooltip="package: crypto/tls";
fontsize="16";
style="filled";
fontname="bold";
rank="sink";
label="[crypto/tls]";
URL="/?f=crypto/tls";
penwidth="0.8";
fillcolor="#E0FFE1";
subgraph "cluster_crypto/tls.RecordHeaderError" {
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(RecordHeaderError)";
tooltip="type: crypto/tls.RecordHeaderError";
penwidth="0.5";
fontsize="15";
"(crypto/tls.RecordHeaderError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
subgraph "cluster_crypto/tls.alert" {
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(alert)";
tooltip="type: crypto/tls.alert";
"(crypto/tls.alert).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
}
subgraph "cluster_crypto/x509" {
penwidth="0.8";
tooltip="package: crypto/x509";
fontsize="16";
style="filled";
fillcolor="#E0FFE1";
fontname="bold";
rank="sink";
label="[crypto/x509]";
URL="/?f=crypto/x509";
subgraph "cluster_crypto/x509.CertificateInvalidError" {
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(CertificateInvalidError)";
tooltip="type: crypto/x509.CertificateInvalidError";
"(crypto/x509.CertificateInvalidError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
subgraph "cluster_crypto/x509.HostnameError" {
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(HostnameError)";
tooltip="type: crypto/x509.HostnameError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
"(crypto/x509.HostnameError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
subgraph "cluster_crypto/x509.SystemRootsError" {
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(SystemRootsError)";
tooltip="type: crypto/x509.SystemRootsError";
penwidth="0.5";
"(crypto/x509.SystemRootsError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
subgraph "cluster_crypto/x509.UnhandledCriticalExtension" {
style="rounded,filled";
fillcolor="#c2e3c2";
label="(UnhandledCriticalExtension)";
tooltip="type: crypto/x509.UnhandledCriticalExtension";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(crypto/x509.UnhandledCriticalExtension).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
subgraph "cluster_crypto/x509.UnknownAuthorityError" {
style="rounded,filled";
fillcolor="#c2e3c2";
label="(UnknownAuthorityError)";
tooltip="type: crypto/x509.UnknownAuthorityError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(crypto/x509.UnknownAuthorityError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
}
subgraph "cluster_github.com/mattn/go-isatty" {
fontname="bold";
penwidth="0.8";
fillcolor="lightyellow";
rank="sink";
label="[isatty]";
URL="/?f=github.com/mattn/go-isatty";
tooltip="package: github.com/mattn/go-isatty";
fontsize="16";
style="filled";
"github.com/mattn/go-isatty.IsTerminal" [ label="IsTerminal" penwidth="1.5" fillcolor="moccasin" ]
}
subgraph "cluster_github.com/pkg/errors" {
style="filled";
fillcolor="lightyellow";
URL="/?f=github.com/pkg/errors";
rank="sink";
label="[errors]";
tooltip="package: github.com/pkg/errors";
penwidth="0.8";
fontsize="16";
fontname="bold";
subgraph "cluster_*github.com/pkg/errors.fundamental" {
label="(*fundamental)";
tooltip="type: *github.com/pkg/errors.fundamental";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
"(*github.com/pkg/errors.fundamental).Error" [ penwidth="1.5" fillcolor="moccasin" label="Error" ]
}
}
subgraph "cluster_math/rand" {
penwidth="0.8";
fillcolor="#E0FFE1";
rank="sink";
URL="/?f=math/rand";
fontsize="16";
style="filled";
fontname="bold";
label="[math/rand]";
tooltip="package: math/rand";
"math/rand.Intn" [ fillcolor="#adedad" label="Intn" penwidth="1.5" ]
}
}
"blitter.com/go/xs/xs.reqTunnel" -> "blitter.com/go/xs/logger.LogDebug" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.reqTunnel" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.launchTuns" -> "blitter.com/go/xs/xs.reqTunnel" [ ]
"blitter.com/go/xs/xs.main$3" -> "math/rand.Intn" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main$3" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(blitter.com/go/xs.Session).Cmd" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(blitter.com/go/xs/xsnet.Conn).Read" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doCopyMode" -> "(blitter.com/go/xs/xsnet.Conn).GetStatus" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.copyBuffer" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ color="saddlebrown" style="dashed" ]
"blitter.com/go/xs/xs.copyBuffer" -> "blitter.com/go/xs/xs.copyBuffer$1" [ style="dashed" ]
"blitter.com/go/xs/xs.copyBuffer" -> "blitter.com/go/xs/xs.copyBuffer$2" [ style="dashed" ]
"blitter.com/go/xs/xs.copyBuffer" -> "blitter.com/go/xs/xs.copyBuffer$3" [ style="dashed" ]
"blitter.com/go/xs/xs.Copy" -> "blitter.com/go/xs/xs.copyBuffer" [ ]
"blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs/xs.doShellMode$1$1" [ arrowhead="normalnoneodiamond" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs.Restore" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(blitter.com/go/xs/xsnet.Conn).GetStatus" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(*blitter.com/go/xs.Session).SetStatus" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(blitter.com/go/xs.Session).Status" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs/xs.exitWithStatus" [ ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.CertificateInvalidError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/aes.KeySizeError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.HostnameError).Error" [ color="saddlebrown" style="dashed" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.UnhandledCriticalExtension).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(context.deadlineExceededError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(compress/flate.CorruptInputError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/tls.RecordHeaderError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.UnknownAuthorityError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.SystemRootsError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(compress/flate.InternalError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/tls.alert).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$1" -> "(*github.com/pkg/errors.fundamental).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode" -> "blitter.com/go/xs/xs.doShellMode$1" [ arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xs.handleTermResizes$1" -> "blitter.com/go/xs/xs.GetSize" [ ]
"blitter.com/go/xs/xs.handleTermResizes$1" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.handleTermResizes" -> "blitter.com/go/xs/xs.handleTermResizes$1" [ arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xs.doShellMode" -> "blitter.com/go/xs/xs.handleTermResizes" [ ]
"blitter.com/go/xs/xs.doShellMode$2$1" -> "blitter.com/go/xs/xs.Copy" [ ]
"blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs/xs.doShellMode$2$1" [ ]
"blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs.Restore" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs/xs.exitWithStatus" [ ]
"blitter.com/go/xs/xs.doShellMode" -> "blitter.com/go/xs/xs.doShellMode$2" [ arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Op" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Who" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).ConnHost" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).TermType" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Cmd" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).AuthCookie" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.parseNonSwitchArgs" [ ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.main$1" [ arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.exitWithStatus" [ ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/logger.New" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xsnet.Init" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xsnet.Dial" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "github.com/mattn/go-isatty.IsTerminal" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.MakeRaw" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main$2" -> "blitter.com/go/xs.Restore" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.main$2" [ arrowhead="normalnoneodiamond" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.ReadPassword" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.NewSession" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.sendSessionParams" [ ]
"blitter.com/go/xs/xs.main" -> "(blitter.com/go/xs/xsnet.Conn).Read" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs.Session).SetStatus" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.rejectUserMsg" -> "blitter.com/go/xs/spinsult.GetSentence" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.rejectUserMsg" [ ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.main$3" [ arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.launchTuns" [ ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.doShellMode" [ ]
"blitter.com/go/xs/xs.main" -> "(blitter.com/go/xs.Session).Status" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.doCopyMode" [ ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.Restore" [ color="saddlebrown" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.usageShell" [ style="dashed" ]
"blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.usageCp" [ style="dashed" ]
}

View file

@ -1,6 +1,7 @@
// hkexsh client
// xs client
//
// Copyright (c) 2017-2018 Russell Magee
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -16,6 +17,7 @@ import (
"io"
"io/ioutil"
"log"
"math/rand"
"net"
"os"
"os/exec"
@ -23,46 +25,61 @@ import (
"path"
"path/filepath"
"runtime"
"runtime/pprof"
"strings"
"sync"
"syscall"
"time"
hkexsh "blitter.com/go/hkexsh"
"blitter.com/go/hkexsh/hkexnet"
"blitter.com/go/hkexsh/logger"
"blitter.com/go/hkexsh/spinsult"
"net/http"
_ "net/http/pprof"
xs "blitter.com/go/xs"
"blitter.com/go/xs/logger"
"blitter.com/go/xs/spinsult"
"blitter.com/go/xs/xsnet"
isatty "github.com/mattn/go-isatty"
)
var (
version string
gitCommit string // set in -ldflags by build
// wg controls when the goroutines handling client I/O complete
wg sync.WaitGroup
kcpMode string // set to a valid KCP BlockCrypt alg tag to use rather than TCP
// Log defaults to regular syslog output (no -d)
Log *logger.Writer
cpuprofile string
memprofile string
)
////////////////////////////////////////////////////
// Praise Bob. Do not remove, lest ye lose Slack.
const bob = string("\r\n\r\n" +
"@@@@@@@^^~~~~~~~~~~~~~~~~~~~~^@@@@@@@@@\r\n" +
"@@@@@@^ ~^ @ @@ @ @ @ I ~^@@@@@@\r\n" +
"@@@@@ ~ ~~ ~I @@@@@\r\n" +
"@@@@' ' _,w@< @@@@\r\n" +
"@@@@' ' _,w@< @@@@ .\r\n" +
"@@@@ @@@@@@@@w___,w@@@@@@@@ @ @@@\r\n" +
"@@@@ @@@@@@@@@@@@@@@@@@@@@@ I @@@\r\n" +
"@@@@ @@@@@@@@@@@@@@@@@@@@@@ I @@@ Bob\r\n" +
"@@@@ @@@@@@@@@@@@@@@@@@@@*@[ i @@@\r\n" +
"@@@@ @@@@@@@@@@@@@@@@@@@@[][ | ]@@@\r\n" +
"@@@@ @@@@@@@@@@@@@@@@@@@@[][ | ]@@@ bOb\r\n" +
"@@@@ ~_,,_ ~@@@@@@@~ ____~ @ @@@\r\n" +
"@@@@ _~ , , `@@@~ _ _`@ ]L J@@@\r\n" +
"@@@@ _~ , , `@@@~ _ _`@ ]L J@@@ o\r\n" +
"@@@@ , @@w@ww+ @@@ww``,,@w@ ][ @@@@\r\n" +
"@@@@, @@@@www@@@ @@@@@@@ww@@@@@[ @@@@\r\n" +
"@@@@, @@@@www@@@ @@@@@@@ww@@@@@[ @@@@ BOB\r\n" +
"@@@@@_|| @@@@@@P' @@P@@@@@@@@@@@[|c@@@@\r\n" +
"@@@@@@w| '@@P~ P]@@@-~, ~Y@@^'],@@@@@@\r\n" +
"@@@@@@w| '@@P~ P]@@@-~, ~Y@@^'],@@@@@@ . o\r\n" +
"@@@@@@@[ _ _J@@Tk ]]@@@@@@\r\n" +
"@@@@@@@@,@ @@, c,,,,,,,y ,w@@[ ,@@@@@@@\r\n" +
"@@@@@@@@@ i @w ====--_@@@@@ @@@@@@@@\r\n" +
"@@@@@@@@@ i @w ====--_@@@@@ @@@@@@@@ o .\r\n" +
"@@@@@@@@@@`,P~ _ ~^^^^Y@@@@@ @@@@@@@@@\r\n" +
"@@@@^^=^@@^ ^' ,ww,w@@@@@ _@@@@@@@@@@\r\n" +
"@@@@^^=^@@^ ^' ,ww,w@@@@@ _@@@@@@@@@@ B o B\r\n" +
"@@@_xJ~ ~ , @@@@@@@P~_@@@@@@@@@@@@\r\n" +
"@@ @, ,@@@,_____ _,J@@@@@@@@@@@@@\r\n" +
"@@L `' ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n" +
@ -70,8 +87,10 @@ const bob = string("\r\n\r\n" +
"\r\n")
type (
// Handler for special functions invoked by escSeqs
escHandler func(io.Writer)
escSeqs map[byte]escHandler
// escSeqs is a map of special keystroke sequences to trigger escHandlers
escSeqs map[byte]escHandler
)
// Copy copies from src to dst until either EOF is reached
@ -91,6 +110,8 @@ type (
// calls a client-custom version of copyBuffer(), which allows
// some client escape sequences to trigger special actions during
// interactive sessions.
//
// (See go doc xs/xs.{escSeqs,escHandler})
func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
written, err = copyBuffer(dst, src, nil)
return
@ -102,9 +123,13 @@ func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
// This private version of copyBuffer is derived from the
// go stdlib pkg/io, with escape sequence interpretation to trigger
// some special client-side actions.
//
// (See go doc xs/xs.{escSeqs,escHandler})
func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) {
// NOTE: using dst.Write() in these esc funcs will cause the output
// to function as a 'macro', outputting as if user typed the sequence.
// to function as a 'macro', outputting as if user typed the sequence
// (that is, the client 'sees' the user type it, and the server 'sees'
// it as well).
//
// Using os.Stdout outputs to the client's term w/o it or the server
// 'seeing' the output.
@ -115,8 +140,6 @@ 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).
// (Note that since this custom copyBuffer func is used only by
// the hkexsh client, it should eventually be moved to client.)
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")) },
@ -158,7 +181,7 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er
if buf[0] == 0x1d {
seqPos++
}
} else /* seqPos > 0 */ {
} else {
if v, ok := escs[buf[0]]; ok {
v(dst)
nr--
@ -187,13 +210,14 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er
break
}
}
//_,_ = dst.Write([]byte{0x2f})
return written, err
}
////////////////////////////////////////////////////
// GetSize gets the terminal size using 'stty' command
//
// TODO: do in code someday instead of using external 'stty'
func GetSize() (cols, rows int, err error) {
cmd := exec.Command("stty", "size") // #nosec
cmd.Stdin = os.Stdin
@ -217,20 +241,47 @@ func GetSize() (cols, rows int, err error) {
return
}
// doCopyMode begins a secure hkexsh local<->remote file copy operation.
// TODO: reduce gocyclo
func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.Session) (exitStatus uint32, err error) {
if remoteDest {
log.Println("local files:", files, "remote filepath:", string(rec.Cmd()))
func buildCmdRemoteToLocal(copyQuiet bool, copyLimitBPS uint, destPath, files string) (captureStderr bool, cmd string, args []string) {
// Detect if we have 'pv'
// pipeview http://www.ivarch.com/programs/pv.shtml
// and use it for nice client progress display.
_, pverr := os.Stat("/usr/bin/pv")
if pverr != nil {
_, pverr = os.Stat("/usr/local/bin/pv")
}
var c *exec.Cmd
if copyQuiet || pverr != nil {
// copyQuiet and copyLimitBPS are not applicable in dumb copy mode
captureStderr = true
cmd = xs.GetTool("tar")
//os.Clearenv()
//os.Setenv("HOME", u.HomeDir)
//os.Setenv("TERM", "vt102") // TODO: server or client option?
args = []string{"-xz", "-C", destPath}
} else {
// TODO: Query remote side for total file/dir size
bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d ", copyLimitBPS)
displayOpts := " -pre "
cmd = xs.GetTool("bash")
args = []string{"-c", "pv " + displayOpts + bandwidthInBytesPerSec + "| tar -xz -C " + destPath}
}
log.Printf("[%v %v]\n", cmd, args)
return
}
cmdName := "/bin/tar"
cmdArgs := []string{"-cz", "-f", "/dev/stdout"}
func buildCmdLocalToRemote(copyQuiet bool, copyLimitBPS uint, files string) (captureStderr bool, cmd string, args []string) {
// Detect if we have 'pv'
// pipeview http://www.ivarch.com/programs/pv.shtml
// and use it for nice client progress display.
_, pverr := os.Stat("/usr/bin/pv")
if pverr != nil {
_, pverr = os.Stat("/usr/local/bin/pv")
}
if pverr != nil {
// copyQuiet and copyLimitBPS are not applicable in dumb copy mode
captureStderr = true
cmd = xs.GetTool("tar")
args = []string{"-cz", "-f", "/dev/stdout"}
files = strings.TrimSpace(files)
// Awesome fact: tar actually can take multiple -C args, and
// changes to the dest dir *as it sees each one*. This enables
@ -248,23 +299,79 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.S
v, _ = filepath.Abs(v) // #nosec
dirTmp, fileTmp := path.Split(v)
if dirTmp == "" {
cmdArgs = append(cmdArgs, fileTmp)
args = append(args, fileTmp)
} else {
cmdArgs = append(cmdArgs, "-C", dirTmp, fileTmp)
args = append(args, "-C", dirTmp, fileTmp)
}
}
} else {
captureStderr = copyQuiet
bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d", copyLimitBPS)
displayOpts := " -pre "
cmd = xs.GetTool("bash")
args = []string{"-c", xs.GetTool("tar") + " -cz -f /dev/stdout "}
files = strings.TrimSpace(files)
// Awesome fact: tar actually can take multiple -C args, and
// changes to the dest dir *as it sees each one*. This enables
// its use below, where clients can send scattered sets of source
// files and dirs to be extracted to a single dest dir server-side,
// whilst preserving the subtrees of dirs on the other side.
// Eg., tar -c -f /dev/stdout -C /dirA fileInA -C /some/where/dirB fileInB /foo/dirC
// packages fileInA, fileInB, and dirC at a single toplevel in the tar.
// The tar authors are/were real smarties :)
//
// This is the 'scatter/gather' logic to allow specification of
// files and dirs in different trees to be deposited in a single
// remote destDir.
for _, v := range strings.Split(files, " ") {
v, _ = filepath.Abs(v) // #nosec
dirTmp, fileTmp := path.Split(v)
if dirTmp == "" {
args[1] = args[1] + fileTmp + " "
} else {
args[1] = args[1] + " -C " + dirTmp + " " + fileTmp + " "
}
}
args[1] = args[1] + "| pv" + displayOpts + bandwidthInBytesPerSec + " -s " + getTreeSizeSubCmd(files) + " -c"
}
log.Printf("[%v %v]\n", cmdName, cmdArgs)
// NOTE the lack of quotes around --xform option's sed expression.
// When args are passed in exec() format, no quoting is required
// (as this isn't input from a shell) (right? -rlm 20180823)
//cmdArgs := []string{"-xvz", "-C", files, `--xform=s#.*/\(.*\)#\1#`}
log.Printf("[%v %v]\n", cmd, args)
return
}
func getTreeSizeSubCmd(paths string) (c string) {
if runtime.GOOS == "linux" {
c = " $(du -cb " + paths + " | tail -1 | cut -f 1) "
} else {
c = " $(expr $(du -c " + paths + ` | tail -1 | cut -f 1) \* 1024) `
}
return c
}
// doCopyMode begins a secure xs local<->remote file copy operation.
//
// TODO: reduce gocyclo
func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool, copyLimitBPS uint, rec *xs.Session) (exitStatus uint32, err error) {
if remoteDest {
log.Println("local files:", files, "remote filepath:", string(rec.Cmd()))
var c *exec.Cmd
//os.Clearenv()
//os.Setenv("HOME", u.HomeDir)
//os.Setenv("TERM", "vt102") // TODO: server or client option?
captureStderr, cmdName, cmdArgs := buildCmdLocalToRemote(copyQuiet, copyLimitBPS, strings.TrimSpace(files))
c = exec.Command(cmdName, cmdArgs...) // #nosec
c.Dir, _ = os.Getwd() // #nosec
log.Println("[wd:", c.Dir, "]")
c.Stdout = conn
stdErrBuffer := new(bytes.Buffer)
c.Stderr = stdErrBuffer
if captureStderr {
c.Stderr = stdErrBuffer
} else {
c.Stderr = os.Stderr
}
// Start the command (no pty)
err = c.Start() // returns immediately
@ -297,8 +404,9 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.S
// an ExitStatus() method with the same signature.
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
exitStatus = uint32(status.ExitStatus())
fmt.Print(stdErrBuffer)
fmt.Printf("Exit Status: %d\n", exitStatus) //#
if captureStderr {
fmt.Print(stdErrBuffer)
}
}
}
}
@ -306,7 +414,7 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.S
log.Println("Sending local exitStatus:", exitStatus)
r := make([]byte, 4)
binary.BigEndian.PutUint32(r, exitStatus)
_, we := conn.WritePacket(r, hkexnet.CSOExitStatus)
_, we := conn.WritePacket(r, xsnet.CSOExitStatus)
if we != nil {
fmt.Println("Error:", we)
}
@ -331,21 +439,11 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.S
}
} else {
log.Println("remote filepath:", string(rec.Cmd()), "local files:", files)
var c *exec.Cmd
//os.Clearenv()
//os.Setenv("HOME", u.HomeDir)
//os.Setenv("TERM", "vt102") // TODO: server or client option?
cmdName := "/bin/tar"
destPath := files
cmdArgs := []string{"-xz", "-C", destPath}
log.Printf("[%v %v]\n", cmdName, cmdArgs)
// NOTE the lack of quotes around --xform option's sed expression.
// When args are passed in exec() format, no quoting is required
// (as this isn't input from a shell) (right? -rlm 20180823)
//cmdArgs := []string{"-xvz", "-C", destPath, `--xform=s#.*/\(.*\)#\1#`}
_, cmdName, cmdArgs := buildCmdRemoteToLocal(copyQuiet, copyLimitBPS, destPath, strings.TrimSpace(files))
var c *exec.Cmd
c = exec.Command(cmdName, cmdArgs...) // #nosec
c.Stdin = conn
c.Stdout = os.Stdout
@ -355,7 +453,6 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.S
err = c.Start() // returns immediately
if err != nil {
fmt.Println(err)
//log.Fatal(err)
} else {
if err = c.Wait(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
@ -367,7 +464,6 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.S
// an ExitStatus() method with the same signature.
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
exitStatus = uint32(status.ExitStatus())
log.Printf("Exit Status: %d", exitStatus)
}
}
}
@ -382,8 +478,9 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *hkexsh.S
return
}
// doShellMode begins an hkexsh shell session (one-shot command or interactive).
func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State, rec *hkexsh.Session) {
// doShellMode begins an xs shell session (one-shot command or
// interactive).
func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec *xs.Session) {
//client reader (from server) goroutine
//Read remote end's stdout
@ -403,13 +500,13 @@ func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State,
// exit with inerr == nil
_, inerr := io.Copy(os.Stdout, conn)
if inerr != nil {
_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // #nosec
restoreTermState(oldState)
// Copy operations and user logging off will cause
// a "use of closed network connection" so handle that
// gracefully here
if !strings.HasSuffix(inerr.Error(), "use of closed network connection") {
log.Println(inerr)
os.Exit(1)
exitWithStatus(1)
}
}
@ -418,8 +515,8 @@ func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State,
if isInteractive {
log.Println("[* Got EOF *]")
_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // #nosec
os.Exit(int(rec.Status()))
restoreTermState(oldState)
exitWithStatus(int(rec.Status()))
}
}
go shellRemoteToStdin()
@ -436,8 +533,7 @@ func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State,
// TODO:.gv:doShellMode:2:shellStdinToRemote
shellStdinToRemote := func() {
defer wg.Done()
//!defer wg.Done()
_, outerr := func(conn *hkexnet.Conn, r io.Reader) (w int64, e error) {
_, outerr := func(conn *xsnet.Conn, r io.Reader) (w int64, e error) {
// Copy() expects EOF so this will
// exit with outerr == nil
w, e = Copy(conn, r)
@ -447,9 +543,9 @@ func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State,
if outerr != nil {
log.Println(outerr)
fmt.Println(outerr)
_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // #nosec
restoreTermState(oldState)
log.Println("[Hanging up]")
os.Exit(0)
exitWithStatus(0)
}
}
go shellStdinToRemote()
@ -473,6 +569,10 @@ func usageCp() {
flag.PrintDefaults()
}
// rejectUserMsg snarkily rebukes users giving incorrect
// credentials.
//
// TODO: do this from the server side and have client just emit that
func rejectUserMsg() string {
return "Begone, " + spinsult.GetSentence() + "\r\n"
}
@ -480,8 +580,8 @@ func rejectUserMsg() string {
// Transmit request to server for it to set up the remote end of a tunnel
//
// Server responds with [CSOTunAck:rport] or [CSOTunRefused:rport]
// (handled in hkexnet.Read())
func reqTunnel(hc *hkexnet.Conn, lp uint16, p string /*net.Addr*/, rp uint16) {
// (handled in xsnet.Read())
func reqTunnel(hc *xsnet.Conn, lp uint16, p string /*net.Addr*/, rp uint16) {
// Write request to server so it can attempt to set up its end
var bTmp bytes.Buffer
if e := binary.Write(&bTmp, binary.BigEndian, lp); e != nil {
@ -491,7 +591,7 @@ func reqTunnel(hc *hkexnet.Conn, lp uint16, p string /*net.Addr*/, rp uint16) {
fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck
}
_ = logger.LogDebug(fmt.Sprintln("[Client sending CSOTunSetup]")) // nolint: gosec
if n, e := hc.WritePacket(bTmp.Bytes(), hkexnet.CSOTunSetup); e != nil || n != len(bTmp.Bytes()) {
if n, e := hc.WritePacket(bTmp.Bytes(), xsnet.CSOTunSetup); e != nil || n != len(bTmp.Bytes()) {
fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck
}
}
@ -521,15 +621,9 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other
}
fancyHost = fancyHostPath[0]
//if fancyPath == "" {
// fancyPath = "."
//}
if i == len(a)-1 {
isDest = true
//fmt.Println("remote path isDest")
}
//fmt.Println("fancyArgs: user:", fancyUser, "host:", fancyHost, "path:", fancyPath)
} else {
otherArgs = append(otherArgs, a[i])
}
@ -537,7 +631,7 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other
return fancyUser, fancyHost, fancyPath, isDest, otherArgs
}
func launchTuns(conn *hkexnet.Conn, remoteHost string, tuns string) {
func launchTuns(conn *xsnet.Conn, remoteHost string, tuns string) {
remAddrs, _ := net.LookupHost(remoteHost) // nolint: gosec
if tuns == "" {
@ -552,7 +646,7 @@ func launchTuns(conn *hkexnet.Conn, remoteHost string, tuns string) {
}
}
func sendSessionParams(conn io.Writer /* *hkexnet.Conn*/, rec *hkexsh.Session) (e error) {
func sendSessionParams(conn io.Writer /* *xsnet.Conn*/, rec *xs.Session) (e error) {
_, e = fmt.Fprintf(conn, "%d %d %d %d %d %d\n",
len(rec.Op()), len(rec.Who()), len(rec.ConnHost()), len(rec.TermType()), len(rec.Cmd()), len(rec.AuthCookie(true)))
if e != nil {
@ -582,71 +676,101 @@ func sendSessionParams(conn io.Writer /* *hkexnet.Conn*/, rec *hkexsh.Session) (
return e
}
// hkexsh - a client for secure shell and file copy operations.
//
// While conforming to the basic net.Conn interface HKex.Conn has extra
// capabilities designed to allow apps to define connection options,
// encryption/hmac settings and operations across the encrypted channel.
//
// Initial setup is the same as using plain net.Dial(), but one may
// specify extra extension tags (strings) to set the cipher and hmac
// setting desired; as well as the intended operation mode for the
// connection (app-specific, passed through to the server to use or
// ignore at its discretion).
// TODO: reduce gocyclo
func main() {
version := hkexsh.Version
var vopt bool
var gopt bool //login via password, asking server to generate authToken
var dbg bool
var shellMode bool // if true act as shell, else file copier
var cipherAlg string //cipher alg
var hmacAlg string //hmac alg
var kexAlg string //KEX/KEM alg
var server string
var port uint
var cmdStr string
var tunSpecStr string // lport1:rport1[,lport2:rport2,...]
var (
isInteractive bool
vopt bool
gopt bool //login via password, asking server to generate authToken
dbg bool
shellMode bool // if true act as shell, else file copier
cipherAlg string //cipher alg
hmacAlg string //hmac alg
kexAlg string //KEX/KEM alg
server string
port uint
cmdStr string
tunSpecStr string // lport1:rport1[,lport2:rport2,...]
var copySrc []byte
var copyDst string
copySrc []byte
copyDst string
copyQuiet bool
copyLimitBPS uint
var authCookie string
var chaffEnabled bool
var chaffFreqMin uint
var chaffFreqMax uint
var chaffBytesMax uint
authCookie string
chaffEnabled bool
chaffFreqMin uint
chaffFreqMax uint
chaffBytesMax uint
var op []byte
isInteractive := false
op []byte
)
//=== Common (xs and xc) option parsing
flag.BoolVar(&vopt, "v", false, "show version")
flag.BoolVar(&dbg, "d", false, "debug logging")
flag.StringVar(&cipherAlg, "c", "C_AES_256", "`cipher` [\"C_AES_256\" | \"C_TWOFISH_128\" | \"C_BLOWFISH_64\" | \"C_CRYPTMT1\"]")
flag.StringVar(&hmacAlg, "m", "H_SHA256", "`hmac` [\"H_SHA256\"]")
flag.StringVar(&kexAlg, "k", "KEX_HERRADURA256", "`kex` [\"KEX_HERRADURA{256/512/1024/2048}\" | \"KEX_KYBER{512/768/1024}\"]")
flag.UintVar(&port, "p", 2000, "`port`")
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(&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")
flag.UintVar(&port, "p", 2000, "``port")
//flag.StringVar(&authCookie, "a", "", "auth cookie")
flag.BoolVar(&chaffEnabled, "e", true, "enabled chaff pkts (default true)")
flag.UintVar(&chaffFreqMin, "f", 100, "`msecs-min` chaff pkt freq min (msecs)")
flag.UintVar(&chaffFreqMax, "F", 5000, "`msecs-max` chaff pkt freq max (msecs)")
flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max (bytes)")
flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts")
flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min `msecs`")
flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max `msecs`")
flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max `bytes`")
flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to <`file`>")
flag.StringVar(&memprofile, "memprofile", "", "write memory profile to <`file`>")
//=== xc vs. xs option parsing
// Find out what program we are (shell or copier)
myPath := strings.Split(os.Args[0], string(os.PathSeparator))
if myPath[len(myPath)-1] != "hkexcp" && myPath[len(myPath)-1] != "hkexcp.exe" {
// hkexsh accepts a command (-x) but not
if myPath[len(myPath)-1] != "xc" &&
myPath[len(myPath)-1] != "_xc" &&
myPath[len(myPath)-1] != "xc.exe" &&
myPath[len(myPath)-1] != "_xc.exe" {
// xs accepts a command (-x) but not
// a srcpath (-r) or dstpath (-t)
flag.StringVar(&cmdStr, "x", "", "`command` to run (if not specified run interactive shell)")
flag.StringVar(&tunSpecStr, "T", "", "`tunnelspec` localPort:remotePort[,localPort:remotePort,...]")
flag.StringVar(&cmdStr, "x", "", "run <`command`> (if not specified, run interactive shell)")
flag.StringVar(&tunSpecStr, "T", "", "``tunnelspec - localPort:remotePort[,localPort:remotePort,...]")
flag.BoolVar(&gopt, "g", false, "ask server to generate authtoken")
shellMode = true
flag.Usage = usageShell
} else {
flag.BoolVar(&copyQuiet, "q", false, "do not output progress bar during copy")
flag.UintVar(&copyLimitBPS, "L", 8589934592, "copy max rate in bytes per sec")
flag.Usage = usageCp
}
flag.Parse()
if vopt {
fmt.Printf("version %s (%s)\n", version, gitCommit)
exitWithStatus(0)
}
//=== 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)
} else {
defer pprof.StopCPUProfile()
}
go func() { http.ListenAndServe("localhost:6060", nil) }()
}
//=== User, host, port and path args for file operations, if applicable
remoteUser, remoteHost, tmpPath, pathIsDest, otherArgs :=
parseNonSwitchArgs(flag.Args())
//fmt.Println("otherArgs:", otherArgs)
@ -655,7 +779,7 @@ func main() {
var uname string
if remoteUser == "" {
u, _ := user.Current() // nolint: gosec
uname = u.Username
uname = localUserName(u)
} else {
uname = remoteUser
}
@ -667,6 +791,8 @@ func main() {
tmpPath = "."
}
//=== Copy mode arg and copy src/dest setup
var fileArgs string
if !shellMode /*&& tmpPath != ""*/ {
// -if pathIsSrc && len(otherArgs) > 1 ERROR
@ -698,40 +824,36 @@ func main() {
}
}
// Do some more option consistency checks
//=== Do some final option consistency checks
//fmt.Println("server finally is:", server)
if flag.NFlag() == 0 && server == "" {
flag.Usage()
os.Exit(0)
}
if vopt {
fmt.Printf("version v%s\n", version)
os.Exit(0)
exitWithStatus(0)
}
if len(cmdStr) != 0 && (len(copySrc) != 0 || len(copyDst) != 0) {
log.Fatal("incompatible options -- either cmd (-x) or copy ops but not both")
}
//-------------------------------------------------------------------
// Here we have parsed all options and can now carry out
// either the shell session or copy operation.
_ = shellMode
Log, _ = logger.New(logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "hkexsh") // nolint: errcheck,gosec
hkexnet.Init(dbg, "hkexsh", logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR)
Log, _ = logger.New(logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "xs") // nolint: errcheck,gosec
xsnet.Init(dbg, "xs", logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR)
if dbg {
log.SetOutput(Log)
} else {
log.SetOutput(ioutil.Discard)
}
//=== Auth token fetch for login
if !gopt {
// See if we can log in via an auth token
u, _ := user.Current() // nolint: gosec
ab, aerr := ioutil.ReadFile(fmt.Sprintf("%s/.hkexsh_id", u.HomeDir))
ab, aerr := ioutil.ReadFile(fmt.Sprintf("%s/.xs_id", u.HomeDir))
if aerr == nil {
idx := strings.Index(string(ab), remoteHost)
if idx >= 0 {
@ -745,10 +867,23 @@ func main() {
_, _ = fmt.Fprintln(os.Stderr, "[no authtoken, use -g to request one from server]")
}
} else {
log.Printf("[cannot read %s/.hkexsh_id]\n", u.HomeDir)
log.Printf("[cannot read %s/.xs_id]\n", u.HomeDir)
}
}
//=== Enforce some sane min/max vals on chaff flags
if chaffFreqMin < 2 {
chaffFreqMin = 2
}
if chaffFreqMax == 0 {
chaffFreqMax = chaffFreqMin + 1
}
if chaffBytesMax == 0 || chaffBytesMax > 4096 {
chaffBytesMax = 64
}
//=== Shell vs. Copy mode chaff and cmd setup
if shellMode {
// We must make the decision about interactivity before Dial()
// as it affects chaffing behaviour. 20180805
@ -789,67 +924,102 @@ func main() {
}
}
conn, err := hkexnet.Dial("tcp", server, cipherAlg, hmacAlg, kexAlg)
//=== TCP / KCP Dial setup
proto := "tcp"
if kcpMode != "unused" {
proto = "kcp"
}
conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode)
if err != nil {
fmt.Println(err)
panic(err)
exitWithStatus(3)
}
defer conn.Close() // nolint: errcheck
// From this point on, conn is a secure encrypted channel
//=== Shell terminal mode (Shell vs. Copy) setup
// Set stdin in raw mode if it's an interactive session
// TODO: send flag to server side indicating this
// affects shell command used
var oldState *hkexsh.State
var oldState *xs.State
defer conn.Close() // nolint: errcheck
//=== From this point on, conn is a secure encrypted channel
if shellMode {
if isatty.IsTerminal(os.Stdin.Fd()) {
oldState, err = hkexsh.MakeRaw(int(os.Stdin.Fd()))
oldState, err = xs.MakeRaw(os.Stdin.Fd())
if err != nil {
panic(err)
}
// #gv:s/label=\"main\$1\"/label=\"deferRestore\"/
// TODO:.gv:main:1:deferRestore
defer func() { _ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) }() // nolint: errcheck,gosec
defer restoreTermState(oldState)
} else {
log.Println("NOT A TTY")
}
}
//=== Login phase
// Start login timeout here and disconnect if user/pass phase stalls
//iloginImpatience := time.AfterFunc(20*time.Second, func() {
//i fmt.Printf(" .. [you still there? Waiting for a password.]")
//i})
loginTimeout := time.AfterFunc(30*time.Second, func() {
restoreTermState(oldState)
fmt.Printf(" .. [login timeout]\n")
exitWithStatus(xsnet.CSOLoginTimeout)
})
if len(authCookie) == 0 {
//No auth token, prompt for password
fmt.Printf("Gimme cookie:")
ab, e := hkexsh.ReadPassword(int(os.Stdin.Fd()))
ab, e := xs.ReadPassword(os.Stdin.Fd())
fmt.Printf("\r\n")
if e != nil {
panic(e)
}
authCookie = string(ab)
}
//i_ = loginImpatience.Stop()
_ = loginTimeout.Stop()
// Security scrub
runtime.GC()
//=== Session param and TERM setup
// Set up session params and send over to server
rec := hkexsh.NewSession(op, []byte(uname), []byte(remoteHost), []byte(os.Getenv("TERM")), []byte(cmdStr), []byte(authCookie), 0)
rec := xs.NewSession(op, []byte(uname), []byte(remoteHost), []byte(os.Getenv("TERM")), []byte(cmdStr), []byte(authCookie), 0)
sendErr := sendSessionParams(&conn, rec)
if sendErr != nil {
log.Fatal(sendErr)
restoreTermState(oldState)
rec.SetStatus(254)
fmt.Fprintln(os.Stderr, "Error: server rejected secure proposal params or login timed out") // nolint: errcheck
exitWithStatus(int(rec.Status()))
//log.Fatal(sendErr)
}
//Security scrub
authCookie = "" // nolint: ineffassign
runtime.GC()
// Read auth reply from server
//=== Login Auth
//=== Read auth reply from server
authReply := make([]byte, 1) // bool: 0 = fail, 1 = pass
_, err = conn.Read(authReply)
if err != nil {
//=== Exit if auth reply not received
fmt.Fprintln(os.Stderr, "Error reading auth reply") // nolint: errcheck
rec.SetStatus(255)
} else if authReply[0] == 0 {
//=== .. or if auth failed
fmt.Fprintln(os.Stderr, rejectUserMsg()) // nolint: errcheck
rec.SetStatus(255)
} else {
// Set up chaffing to server
//=== Set up chaffing to server
conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // enable client->server chaffing
if chaffEnabled {
// #gv:s/label=\"main\$2\"/label=\"deferCloseChaff\"/
@ -859,34 +1029,85 @@ func main() {
defer conn.ShutdownChaff()
}
// Keepalive for any tunnels that may exist
//=== (goroutine) Start keepAliveWorker for tunnels
// #gv:s/label=\"main\$1\"/label=\"tunKeepAlive\"/
// TODO:.gv:main:1:tunKeepAlive
//[1]: better to always send tunnel keepAlives even if client didn't specify
// any, to prevent listeners from knowing this.
//[1] if tunSpecStr != "" {
keepAliveWorker := func() {
for {
time.Sleep(time.Duration(2) * time.Second)
conn.WritePacket([]byte{0, 0}, hkexnet.CSOTunKeepAlive) // nolint: errcheck,gosec
// Add a bit of jitter to keepAlive so it doesn't stand out quite as much
time.Sleep(time.Duration(2000-rand.Intn(200)) * time.Millisecond)
// FIXME: keepAlives should probably have small random packet len/data as well
// to further obscure them vs. interactive or tunnel data
// keepAlives must be >=2 bytes, due to processing elsewhere
conn.WritePacket([]byte{0, 0}, xsnet.CSOTunKeepAlive) // nolint: errcheck,gosec
}
}
go keepAliveWorker()
//[1]}
//=== Session entry (shellMode or copyMode)
if shellMode {
//=== (shell) launch tunnels
launchTuns(&conn, remoteHost, tunSpecStr)
doShellMode(isInteractive, &conn, oldState, rec)
} else { // copyMode
s, _ := doCopyMode(&conn, pathIsDest, fileArgs, rec) // nolint: errcheck,gosec
} else {
//=== (.. or file copy)
s, _ := doCopyMode(&conn, pathIsDest, fileArgs, copyQuiet, copyLimitBPS, rec) // nolint: errcheck,gosec
rec.SetStatus(s)
}
if rec.Status() != 0 {
_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // nolint: errcheck,gosec
restoreTermState(oldState)
fmt.Fprintln(os.Stderr, "Session exited with status:", rec.Status()) // nolint: errcheck
}
}
if oldState != nil {
_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // nolint: gosec
restoreTermState(oldState)
oldState = nil
}
os.Exit(int(rec.Status()))
//=== Exit
exitWithStatus(int(rec.Status()))
}
// currentUser returns the current username minus any OS-specific prefixes
// such as MS Windows workgroup prefixes (eg. workgroup\user).
func localUserName(u *user.User) string {
if u == nil {
log.Fatal("null User?!")
}
// WinAPI: username may have CIFS prefix %USERDOMAIN%\
userspec := strings.Split(u.Username, `\`)
username := userspec[len(userspec)-1]
return username
}
func restoreTermState(oldState *xs.State) {
_ = xs.Restore(os.Stdin.Fd(), oldState) // nolint: errcheck,gosec
}
// exitWithStatus wraps os.Exit() plus does any required pprof housekeeping
func exitWithStatus(status int) {
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)
}
}
os.Exit(status)
}

BIN
xs/xs_seq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View file

@ -1,12 +1,12 @@
#!/sbin/openrc-run
SVCNAME=hkexshd
HKEXSHD_PIDFILE=/var/run/hkexshd.pid
HKEXSHD_USER=root
HKEXSHD_HOME=/var/run
SVCNAME=xsd
XSD_PIDFILE=/var/run/xsd.pid
XSD_USER=root
XSD_HOME=/var/run
INST_PREFIX=/usr/local
COMMAND=$INST_PREFIX/sbin/hkexshd
ARGS=""
COMMAND=$INST_PREFIX/sbin/xsd
ARGS="-L"
depend() {
need net
@ -25,15 +25,15 @@ start() {
ebegin "Starting ${SVCNAME}"
start-stop-daemon \
-d ${HKEXSHD_HOME} \
--make-pidfile --pidfile ${HKEXSHD_PIDFILE} \
-d ${XSD_HOME} \
--make-pidfile --pidfile ${XSD_PIDFILE} \
--start --quiet --background \
--exec "${COMMAND}" "${ARGS}"
--exec "${COMMAND}" -- "${ARGS}"
eend $?
}
stop() {
ebegin "Stopping ${SVCNAME}"
start-stop-daemon --stop --quiet --pidfile $HKEXSHD_PIDFILE
start-stop-daemon --stop --quiet --pidfile $XSD_PIDFILE
eend $?
}

162
xsd.sysvrc Executable file
View file

@ -0,0 +1,162 @@
#! /bin/sh
### BEGIN INIT INFO
# Provides: xsd
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: eXperimental Shell Daemon
### END INIT INFO
set -e
# /etc/init.d/xsd: start and stop the eXperimental "secure" Shell Daemon
test -x /usr/local/sbin/xsd || exit 0
( /usr/local/sbin/xsd -h 2>&1 | grep -q chaff ) 2>/dev/null || exit 0
umask 022
#if test -f /etc/default/ssh; then
# . /etc/default/ssh
#fi
. /lib/lsb/init-functions
if [ -n "$2" ]; then
XSD_OPTS="$XSD_OPTS $2"
fi
# Are we running from init?
run_by_init() {
([ "$previous" ] && [ "$runlevel" ]) || [ "$runlevel" = S ]
}
check_for_no_start() {
# forget it if we're trying to start, and /etc/xsd_not_to_be_run exists
if [ -e /etc/xsd_not_to_be_run ]; then
if [ "$1" = log_end_msg ]; then
log_end_msg 0 || true
fi
if ! run_by_init; then
log_action_msg "eXperimental Shell Daemon not in use (/etc/xsd_not_to_be_run)" || true
fi
exit 0
fi
}
check_dev_null() {
if [ ! -c /dev/null ]; then
if [ "$1" = log_end_msg ]; then
log_end_msg 1 || true
fi
if ! run_by_init; then
log_action_msg "/dev/null is not a character device!" || true
fi
exit 1
fi
}
#check_privsep_dir() {
# # Create the PrivSep empty dir if necessary
# if [ ! -d /run/sshd ]; then
# mkdir /run/sshd
# chmod 0755 /run/sshd
# fi
#}
#check_config() {
# if [ ! -e /etc/xsd_not_to_be_run ]; then
# /usr/local/sbin/xsd $XSD_OPTS -t || exit 1
# fi
#}
export PATH="${PATH:+$PATH:}/usr/local/sbin:/usr/sbin:/sbin"
case "$1" in
start)
#check_privsep_dir
check_for_no_start
check_dev_null
log_daemon_msg "Starting eXperimental Shell Daemon" "xsd" || true
if start-stop-daemon --start -b --quiet --oknodo --chuid 0:0 --exec /usr/local/sbin/xsd -- $XSD_OPTS; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
stop)
log_daemon_msg "Stopping eXperimental Shell Daemon" "xsd" || true
if start-stop-daemon --stop --quiet --oknodo --exec /usr/local/sbin/xsd; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
reload|force-reload)
check_for_no_start
#check_config
log_daemon_msg "Reloading eXperimental Shell Daemon's configuration" "xsd" || true
if start-stop-daemon --stop --signal 1 --quiet --oknodo --exec /usr/local/sbin/xsd; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
restart)
#check_privsep_dir
#check_config
log_daemon_msg "Restarting eXperimental Shell Daemon" "xsd" || true
start-stop-daemon --stop --quiet --oknodo --retry 30 --exec /usr/local/sbin/xsd
check_for_no_start log_end_msg
check_dev_null log_end_msg
if start-stop-daemon --start -b --quiet --oknodo --chuid 0:0 --exec /usr/local/sbin/xsd -- $XSD_OPTS; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
try-restart)
#check_privsep_dir
#check_config
log_daemon_msg "Restarting eXperimental Shell Daemon" "xsd" || true
RET=0
start-stop-daemon --stop --quiet --retry 30 --exec /usr/local/sbin/xsd || RET="$?"
case $RET in
0)
# old daemon stopped
check_for_no_start log_end_msg
check_dev_null log_end_msg
if start-stop-daemon --start -b --quiet --oknodo --chuid 0:0 --exec /usr/local/sbin/xsd -- $XSD_OPTS; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
1)
# daemon not running
log_progress_msg "(not running)" || true
log_end_msg 0 || true
;;
*)
# failed to stop
log_progress_msg "(failed to stop)" || true
log_end_msg 1 || true
;;
esac
;;
status)
status_of_proc -p /run/xsd.pid /usr/local/sbin/xsd xsd && exit 0 || exit $?
;;
*)
log_action_msg "Usage: /etc/init.d/xsd {start|stop|reload|force-reload|restart|try-restart|status}" || true
exit 1
esac
exit 0

View file

@ -4,15 +4,15 @@ EXTPKGS = binary,bytes,crypto,encoding,errors,flag,fmt,internal,io,log,net,os,pa
EXE = $(notdir $(shell pwd))
all:
go build .
go build $(BUILDOPTS) .
clean:
$(RM) $(EXE) $(EXE).exe
vis:
go-callvis -file hkexshd-vis -format png -ignore $(EXTPKGS) -group pkg,type .
../fixup-gv.sh hkexshd.go && cat hkexshd-vis.gv | dot -Tpng -ohkexshd-vis-fixedup.png
go-callvis -file xsd-vis -format png -ignore $(EXTPKGS) -group pkg,type .
../fixup-gv.sh xsd.go && cat xsd-vis.gv | dot -Tpng -oxsd-vis-fixedup.png
lint:
-gometalinter --deadline=60s | sort
-golangci-lint run

BIN
xsd/hkexshd-vis.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 KiB

BIN
xsd/xsd-vis-fixedup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 KiB

347
xsd/xsd-vis.gv Executable file
View file

@ -0,0 +1,347 @@
digraph gocallvis {
label="blitter.com/go/xs/xsd";
labeljust="l";
fontname="Arial";
fontsize="14";
rankdir="LR";
bgcolor="lightgray";
style="solid";
penwidth="0.5";
pad="0.0";
nodesep="0.35";
node [shape="ellipse" style="filled" fillcolor="honeydew" fontname="Verdana" penwidth="1.0" margin="0.05,0.0"];
edge [minlen="2"]
subgraph "cluster_focus" {
label="main";
labelloc="t";
labeljust="c";
fontsize="18";
bgcolor="#e6ecfa";
"blitter.com/go/xs/xsd.main$2" [ style="dotted,filled" fillcolor="lightblue" label="main$2" ]
"blitter.com/go/xs/xsd.GenAuthToken" [ fillcolor="lightblue" label="GenAuthToken" penwidth="1.5" ]
"blitter.com/go/xs/xsd.runShellAs" [ fillcolor="lightblue" label="runShellAs" penwidth="0.5" ]
"blitter.com/go/xs/xsd.runShellAs$1" [ fillcolor="lightblue" label="deferPtmxClose" style="dotted,filled" ]
"blitter.com/go/xs/xsd.ptsName" [ label="ptsName" penwidth="0.5" fillcolor="lightblue" ]
"blitter.com/go/xs/xsd.ioctl" [ fillcolor="lightblue" label="ioctl" penwidth="0.5" ]
"blitter.com/go/xs/xsd.runShellAs$2" [ fillcolor="lightblue" label="termResizeWatcher" style="dotted,filled" ]
"blitter.com/go/xs/xsd.runShellAs$3" [ style="dotted,filled" fillcolor="lightblue" label="stdinToPtyWorker" ]
"blitter.com/go/xs/xsd.runShellAs$4" [ style="dotted,filled" fillcolor="lightblue" label="deferChaffShutdown" ]
"blitter.com/go/xs/xsd.runShellAs$5" [ fillcolor="lightblue" label="ptyToStdoutWorker" style="dotted,filled" ]
"blitter.com/go/xs/xsd.runShellAs$6" [ fillcolor="lightblue" label="runShellAs$6" style="dotted,filled" ]
"blitter.com/go/xs/xsd.runClientToServerCopyAs" [ fillcolor="lightblue" label="runClientToServerCopyAs" penwidth="0.5" ]
"blitter.com/go/xs/xsd.runServerToClientCopyAs" [ fillcolor="lightblue" label="runServerToClientCopyAs" penwidth="0.5" ]
"blitter.com/go/xs/xsd.main" [ fillcolor="lightblue" label="main" penwidth="0.5" ]
"blitter.com/go/xs/xsd.main$1" [ fillcolor="lightblue" label="main$1" style="dotted,filled" ]
subgraph "cluster_blitter.com/go/goutmp" {
URL="/?f=blitter.com/go/goutmp";
tooltip="package: blitter.com/go/goutmp";
fontsize="16";
style="filled";
fillcolor="lightyellow";
fontname="bold";
rank="sink";
label="[goutmp]";
penwidth="0.8";
"blitter.com/go/goutmp.GetHost" [ fillcolor="moccasin" label="GetHost" penwidth="1.5" ]
"blitter.com/go/goutmp.Put_utmp" [ fillcolor="moccasin" label="Put_utmp" penwidth="1.5" ]
"blitter.com/go/goutmp.Unput_utmp" [ label="Unput_utmp" penwidth="1.5" fillcolor="moccasin" ]
"blitter.com/go/goutmp.Put_lastlog_entry" [ fillcolor="moccasin" label="Put_lastlog_entry" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/xs" {
fontsize="16";
fontname="bold";
rank="sink";
penwidth="0.8";
fillcolor="lightyellow";
label="[xs]";
URL="/?f=blitter.com/go/xs";
tooltip="package: blitter.com/go/xs";
style="filled";
"blitter.com/go/xs.AuthUserByToken" [ fillcolor="moccasin" label="AuthUserByToken" penwidth="1.5" ]
"blitter.com/go/xs.AuthUserByPasswd" [ label="AuthUserByPasswd" penwidth="1.5" fillcolor="moccasin" ]
subgraph "cluster_*blitter.com/go/xs.Session" {
tooltip="type: *blitter.com/go/xs.Session";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(*Session)";
"(*blitter.com/go/xs.Session).SetOp" [ fillcolor="moccasin" label="SetOp" penwidth="1.5" ]
"(*blitter.com/go/xs.Session).SetWho" [ penwidth="1.5" fillcolor="moccasin" label="SetWho" ]
"(*blitter.com/go/xs.Session).SetConnHost" [ penwidth="1.5" fillcolor="moccasin" label="SetConnHost" ]
"(*blitter.com/go/xs.Session).SetTermType" [ fillcolor="moccasin" label="SetTermType" penwidth="1.5" ]
"(*blitter.com/go/xs.Session).SetCmd" [ fillcolor="moccasin" label="SetCmd" penwidth="1.5" ]
"(*blitter.com/go/xs.Session).SetAuthCookie" [ fillcolor="moccasin" label="SetAuthCookie" penwidth="1.5" ]
"(*blitter.com/go/xs.Session).ClearAuthCookie" [ fillcolor="moccasin" label="ClearAuthCookie" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/xs.Session" {
fillcolor="wheat2";
label="(Session)";
tooltip="type: blitter.com/go/xs.Session";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
"(blitter.com/go/xs.Session).Op" [ fillcolor="moccasin" label="Op" penwidth="1.5" ]
"(blitter.com/go/xs.Session).Who" [ fillcolor="moccasin" label="Who" penwidth="1.5" ]
"(blitter.com/go/xs.Session).ConnHost" [ penwidth="1.5" fillcolor="moccasin" label="ConnHost" ]
"(blitter.com/go/xs.Session).Cmd" [ penwidth="1.5" fillcolor="moccasin" label="Cmd" ]
"(blitter.com/go/xs.Session).AuthCookie" [ label="AuthCookie" penwidth="1.5" fillcolor="moccasin" ]
"(blitter.com/go/xs.Session).TermType" [ fillcolor="moccasin" label="TermType" penwidth="1.5" ]
}
}
subgraph "cluster_blitter.com/go/xs/logger" {
penwidth="0.8";
fontsize="16";
style="filled";
fontname="bold";
URL="/?f=blitter.com/go/xs/logger";
fillcolor="lightyellow";
rank="sink";
label="[logger]";
tooltip="package: blitter.com/go/xs/logger";
"blitter.com/go/xs/logger.LogNotice" [ fillcolor="moccasin" label="LogNotice" penwidth="1.5" ]
"blitter.com/go/xs/logger.LogDebug" [ fillcolor="moccasin" label="LogDebug" penwidth="1.5" ]
"blitter.com/go/xs/logger.LogErr" [ fillcolor="moccasin" label="LogErr" penwidth="1.5" ]
"blitter.com/go/xs/logger.New" [ label="New" penwidth="1.5" fillcolor="moccasin" ]
}
subgraph "cluster_blitter.com/go/xs/xsnet" {
penwidth="0.8";
label="[xsnet]";
fillcolor="lightyellow";
fontname="bold";
rank="sink";
URL="/?f=blitter.com/go/xs/xsnet";
tooltip="package: blitter.com/go/xs/xsnet";
fontsize="16";
style="filled";
"blitter.com/go/xs/xsnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" ]
"blitter.com/go/xs/xsnet.Listen" [ fillcolor="moccasin" label="Listen" penwidth="1.5" ]
subgraph "cluster_*blitter.com/go/xs/xsnet.Conn" {
label="(*Conn)";
tooltip="type: *blitter.com/go/xs/xsnet.Conn";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
"(*blitter.com/go/xs/xsnet.Conn).Close" [ fillcolor="moccasin" label="Close" penwidth="1.5" ]
"(*blitter.com/go/xs/xsnet.Conn).RemoteAddr" [ fillcolor="moccasin" label="RemoteAddr" penwidth="1.5" ]
"(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ fillcolor="moccasin" label="EnableChaff" penwidth="1.5" ]
"(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ label="DisableChaff" penwidth="1.5" fillcolor="moccasin" ]
"(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ fillcolor="moccasin" label="ShutdownChaff" penwidth="1.5" ]
"(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" ]
"(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ fillcolor="moccasin" label="WritePacket" penwidth="1.5" ]
"(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ penwidth="1.5" fillcolor="moccasin" label="SetupChaff" ]
}
subgraph "cluster_*blitter.com/go/xs/xsnet.HKExListener" {
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(*HKExListener)";
tooltip="type: *blitter.com/go/xs/xsnet.HKExListener";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
"(*blitter.com/go/xs/xsnet.HKExListener).Accept" [ penwidth="1.5" fillcolor="moccasin" label="Accept" ]
}
subgraph "cluster_blitter.com/go/xs/xsnet.Conn" {
tooltip="type: blitter.com/go/xs/xsnet.Conn";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(Conn)";
"(blitter.com/go/xs/xsnet.Conn).Write" [ fillcolor="moccasin" label="Write" penwidth="1.5" ]
}
subgraph "cluster_blitter.com/go/xs/xsnet.HKExListener" {
style="rounded,filled";
fillcolor="wheat2";
label="(HKExListener)";
tooltip="type: blitter.com/go/xs/xsnet.HKExListener";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
labelloc="b";
"(blitter.com/go/xs/xsnet.HKExListener).Close" [ label="Close" penwidth="1.5" fillcolor="moccasin" ]
}
}
subgraph "cluster_context" {
fontsize="16";
fillcolor="#E0FFE1";
label="[context]";
tooltip="package: context";
penwidth="0.8";
style="filled";
fontname="bold";
rank="sink";
URL="/?f=context";
subgraph "cluster_context.deadlineExceededError" {
labelloc="b";
style="rounded,filled";
fillcolor="#c2e3c2";
label="(deadlineExceededError)";
tooltip="type: context.deadlineExceededError";
penwidth="0.5";
fontsize="15";
fontcolor="#222222";
"(context.deadlineExceededError).Error" [ fillcolor="#adedad" label="Error" penwidth="1.5" ]
}
}
subgraph "cluster_github.com/kr/pty" {
label="[pty]";
URL="/?f=github.com/kr/pty";
penwidth="0.8";
style="filled";
fillcolor="lightyellow";
rank="sink";
fontsize="16";
fontname="bold";
tooltip="package: github.com/kr/pty";
"github.com/kr/pty.Start" [ fillcolor="moccasin" label="Start" penwidth="1.5" ]
"github.com/kr/pty.Setsize" [ label="Setsize" penwidth="1.5" fillcolor="moccasin" ]
}
subgraph "cluster_github.com/pkg/errors" {
fontname="bold";
label="[errors]";
style="filled";
fontsize="16";
fillcolor="lightyellow";
rank="sink";
URL="/?f=github.com/pkg/errors";
tooltip="package: github.com/pkg/errors";
penwidth="0.8";
subgraph "cluster_*github.com/pkg/errors.fundamental" {
fontcolor="#222222";
labelloc="b";
style="rounded,filled";
fillcolor="wheat2";
label="(*fundamental)";
tooltip="type: *github.com/pkg/errors.fundamental";
penwidth="0.5";
fontsize="15";
"(*github.com/pkg/errors.fundamental).Error" [ label="Error" penwidth="1.5" fillcolor="moccasin" ]
}
}
}
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetOp" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetWho" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetConnHost" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetTermType" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetCmd" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetAuthCookie" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).Op" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).Who" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).ConnHost" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).Cmd" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).AuthCookie" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs.AuthUserByToken" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).ClearAuthCookie" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs.AuthUserByPasswd" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/logger.LogNotice" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).RemoteAddr" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/goutmp.GetHost" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.GenAuthToken" [ ]
"blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).TermType" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.runShellAs" -> "github.com/kr/pty.Start" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$1" [ arrowhead="normalnoneodiamond" ]
"blitter.com/go/xs/xsd.ptsName" -> "blitter.com/go/xs/xsd.ioctl" [ ]
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.ptsName" [ ]
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/goutmp.Put_utmp" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.runShellAs$2" -> "blitter.com/go/goutmp.Unput_utmp" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$2" [ arrowhead="normalnoneodiamond" ]
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/goutmp.Put_lastlog_entry" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.runShellAs$3" -> "github.com/kr/pty.Setsize" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$3" [ arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xsd.runShellAs$4" -> "(context.deadlineExceededError).Error" [ color="saddlebrown" style="dashed" ]
"blitter.com/go/xs/xsd.runShellAs$4" -> "(*github.com/pkg/errors.fundamental).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$4" [ arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xsd.runShellAs" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.runShellAs$5" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.runShellAs$5" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$5" [ arrowhead="normalnoneodiamond" ]
"blitter.com/go/xs/xsd.runShellAs$6" -> "(context.deadlineExceededError).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xsd.runShellAs$6" -> "(*github.com/pkg/errors.fundamental).Error" [ style="dashed" color="saddlebrown" ]
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$6" [ arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/logger.LogDebug" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.runShellAs" -> "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.runShellAs" [ ]
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/logger.LogErr" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.runClientToServerCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.runClientToServerCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/xs/xsd.runClientToServerCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.runClientToServerCopyAs" [ ]
"blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.runServerToClientCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.runServerToClientCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/xs/xsd.runServerToClientCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ color="saddlebrown" arrowhead="normalnoneodiamond" ]
"blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.runServerToClientCopyAs" [ ]
"blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/logger.New" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsnet.Init" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main$1" -> "blitter.com/go/xs/logger.LogNotice" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsd.main$1" [ arrowhead="normalnoneodot" ]
"blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsnet.Listen" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main" -> "(blitter.com/go/xs/xsnet.HKExListener).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" ]
"blitter.com/go/xs/xsd.main" -> "(*blitter.com/go/xs/xsnet.HKExListener).Accept" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main" -> "(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ color="saddlebrown" ]
"blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsd.main$2" [ arrowhead="normalnoneodot" ]
}

View file

@ -1,6 +1,6 @@
// hkexshd server
// xsd server
//
// Copyright (c) 2017-2018 Russell Magee
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -23,24 +23,49 @@ import (
"os/signal"
"os/user"
"path"
"strings"
"sync"
"syscall"
"time"
"unsafe"
"blitter.com/go/goutmp"
hkexsh "blitter.com/go/hkexsh"
"blitter.com/go/hkexsh/hkexnet"
"blitter.com/go/hkexsh/logger"
"github.com/kr/pty"
xs "blitter.com/go/xs"
"blitter.com/go/xs/logger"
"blitter.com/go/xs/xsnet"
"github.com/creack/pty"
)
var (
version string
gitCommit string // set in -ldflags by build
useSysLogin bool
kcpMode string // set to a valid KCP BlockCrypt alg tag to use rather than TCP
// Log - syslog output (with no -d)
Log *logger.Writer
)
func ioctl(fd, request, argp uintptr) error {
if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, fd, request, argp, 0, 0, 0); e != 0 {
return e
}
return nil
}
func ptsName(fd uintptr) (string, error) {
var n uintptr
err := ioctl(fd, syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n)))
if err != nil {
return "", err
}
return fmt.Sprintf("/dev/pts/%d", n), nil
}
/* -------------------------------------------------------------- */
// Perform a client->server copy
func runClientToServerCopyAs(who, ttype string, conn *hkexnet.Conn, fpath string, chaffing bool) (exitStatus uint32, err error) {
func runClientToServerCopyAs(who, ttype string, conn *xsnet.Conn, fpath string, chaffing bool) (exitStatus uint32, err error) {
u, _ := user.Lookup(who) // nolint: gosec
var uid, gid uint32
fmt.Sscanf(u.Uid, "%d", &uid) // nolint: gosec,errcheck
@ -57,10 +82,10 @@ func runClientToServerCopyAs(who, ttype string, conn *hkexnet.Conn, fpath string
os.Clearenv()
os.Setenv("HOME", u.HomeDir) // nolint: gosec,errcheck
os.Setenv("TERM", ttype) // nolint: gosec,errcheck
os.Setenv("HKEXSH", "1") // nolint: gosec,errcheck
os.Setenv("XS_SESSION", "1") // nolint: gosec,errcheck
var c *exec.Cmd
cmdName := "/bin/tar"
cmdName := xs.GetTool("tar")
var destDir string
if path.IsAbs(fpath) {
@ -75,6 +100,7 @@ func runClientToServerCopyAs(who, ttype string, conn *hkexnet.Conn, fpath string
// When args are passed in exec() format, no quoting is required
// (as this isn't input from a shell) (right? -rlm 20180823)
//cmdArgs := []string{"-x", "-C", destDir, `--xform=s#.*/\(.*\)#\1#`}
fmt.Println(cmdName, cmdArgs)
c = exec.Command(cmdName, cmdArgs...) // nolint: gosec
c.Dir = destDir
@ -116,7 +142,7 @@ func runClientToServerCopyAs(who, ttype string, conn *hkexnet.Conn, fpath string
log.Println("cmd exited immediately. Cannot get cmd.Wait().ExitStatus()")
err = errors.New("cmd exited prematurely")
//exitStatus = uint32(254)
exitStatus = hkexnet.CSEExecFail
exitStatus = xsnet.CSEExecFail
} else {
if err := c.Wait(); err != nil {
//fmt.Println("*** c.Wait() done ***")
@ -140,7 +166,7 @@ func runClientToServerCopyAs(who, ttype string, conn *hkexnet.Conn, fpath string
}
// Perform a server->client copy
func runServerToClientCopyAs(who, ttype string, conn *hkexnet.Conn, srcPath string, chaffing bool) (exitStatus uint32, err error) {
func runServerToClientCopyAs(who, ttype string, conn *xsnet.Conn, srcPath string, chaffing bool) (exitStatus uint32, err error) {
u, err := user.Lookup(who)
if err != nil {
exitStatus = 1
@ -161,10 +187,10 @@ func runServerToClientCopyAs(who, ttype string, conn *hkexnet.Conn, srcPath stri
os.Clearenv()
_ = os.Setenv("HOME", u.HomeDir) // nolint: gosec
_ = os.Setenv("TERM", ttype) // nolint: gosec
_ = os.Setenv("HKEXSH", "1") // nolint: gosec
_ = os.Setenv("XS_SESSION", "1") // nolint: gosec
var c *exec.Cmd
cmdName := "/bin/tar"
cmdName := xs.GetTool("tar")
if !path.IsAbs(srcPath) {
srcPath = fmt.Sprintf("%s%c%s", u.HomeDir, os.PathSeparator, srcPath)
}
@ -202,7 +228,7 @@ func runServerToClientCopyAs(who, ttype string, conn *hkexnet.Conn, srcPath stri
err = c.Start() // returns immediately
if err != nil {
log.Printf("Command finished with error: %v", err)
return hkexnet.CSEExecFail, err // !?
return xsnet.CSEExecFail, err // !?
}
if err := c.Wait(); err != nil {
//fmt.Println("*** c.Wait() done ***")
@ -230,7 +256,7 @@ func runServerToClientCopyAs(who, ttype string, conn *hkexnet.Conn, srcPath stri
//
// Uses ptys to support commands which expect a terminal.
// nolint: gocyclo
func runShellAs(who, ttype string, cmd string, interactive bool, conn *hkexnet.Conn, chaffing bool) (exitStatus uint32, err error) {
func runShellAs(who, hname, ttype, cmd string, interactive bool, conn *xsnet.Conn, chaffing bool) (exitStatus uint32, err error) {
var wg sync.WaitGroup
u, err := user.Lookup(who)
if err != nil {
@ -252,32 +278,59 @@ func runShellAs(who, ttype string, cmd string, interactive bool, conn *hkexnet.C
os.Clearenv()
_ = os.Setenv("HOME", u.HomeDir) // nolint: gosec
_ = os.Setenv("TERM", ttype) // nolint: gosec
_ = os.Setenv("HKEXSH", "1") // nolint: gosec
_ = os.Setenv("XS_SESSION", "1") // nolint: gosec
var c *exec.Cmd
if interactive {
c = exec.Command("/bin/bash", "-i", "-l") // nolint: gosec
if useSysLogin {
// Use the server's login binary (post-auth
// which is still done via our own bcrypt file)
// 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.
//
c = exec.Command(xs.GetTool("login"), "-f", "-p", who) // nolint: gosec
} else {
c = exec.Command(xs.GetTool("bash"), "-i", "-l") // nolint: gosec
}
} else {
c = exec.Command("/bin/bash", "-c", cmd) // nolint: gosec
c = exec.Command(xs.GetTool("bash"), "-c", cmd) // nolint: gosec
}
//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}
c.Stdin = conn
c.Stdout = conn
c.Stderr = conn
if useSysLogin {
// If using server's login binary, drop to user creds
// is taken care of by it.
c.SysProcAttr.Credential = &syscall.Credential{}
} else {
c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
}
// Start the command with a pty.
ptmx, err := pty.Start(c) // returns immediately with ptmx file
if err != nil {
return hkexnet.CSEPtyExecFail, err
log.Println(err)
return xsnet.CSEPtyExecFail, err
}
// Make sure to close the pty at the end.
// #gv:s/label=\"runShellAs\$1\"/label=\"deferPtmxClose\"/
defer func() { _ = ptmx.Close() }() // nolint: gosec
defer func() {
//logger.LogDebug(fmt.Sprintf("[Exited process was %d]", c.Process.Pid))
_ = ptmx.Close()
}() // nolint: gosec
// get pty info for system accounting (who, lastlog)
pts, pe := ptsName(ptmx.Fd())
if pe != nil {
return xsnet.CSEPtyGetNameFail, err
}
utmpx := goutmp.Put_utmp(who, pts, hname)
defer func() { goutmp.Unput_utmp(utmpx) }()
goutmp.Put_lastlog_entry("xs", who, pts, hname)
log.Printf("[%s]\n", cmd)
if err != nil {
@ -347,7 +400,15 @@ func runShellAs(who, ttype string, cmd string, interactive bool, conn *hkexnet.C
log.Printf("Exit Status: %d", exitStatus)
}
}
conn.SetStatus(hkexnet.CSOType(exitStatus))
conn.SetStatus(xsnet.CSOType(exitStatus))
} else {
logger.LogDebug("*** Main proc has exited. ***")
// Background jobs still may be running; close the
// pty anyway, so the client can return before
// wg.Wait() below completes (Issue #18)
if interactive {
_ = ptmx.Close()
}
}
wg.Wait() // Wait on pty->stdout completion to client
}
@ -368,15 +429,79 @@ func GenAuthToken(who string, connhost string) string {
return fmt.Sprintf("%s:%s", tokenA, hex.EncodeToString(tokenB))
}
// Demo of a simple server that listens and spawns goroutines for each
// connecting client. Note this code is identical to standard tcp
// server code, save for declaring 'hkex' rather than 'net'
// Listener and Conns. The KEx and encrypt/decrypt is done within the type.
// Compare to 'serverp.go' in this directory to see the equivalence.
var (
aKEXAlgs allowedKEXAlgs
aCipherAlgs allowedCipherAlgs
aHMACAlgs allowedHMACAlgs
)
type allowedKEXAlgs []string // TODO
type allowedCipherAlgs []string // TODO
type allowedHMACAlgs []string // TODO
func (a allowedKEXAlgs) allowed(k xsnet.KEXAlg) bool {
for i := 0; i < len(a); i++ {
if a[i] == "KEX_all" || a[i] == k.String() {
return true
}
}
return false
}
func (a *allowedKEXAlgs) String() string {
return fmt.Sprintf("allowedKEXAlgs: %v", *a)
}
func (a *allowedKEXAlgs) Set(value string) error {
*a = append(*a, strings.TrimSpace(value))
return nil
}
func (a allowedCipherAlgs) allowed(c xsnet.CSCipherAlg) bool {
for i := 0; i < len(a); i++ {
if a[i] == "C_all" || a[i] == c.String() {
return true
}
}
return false
}
func (a *allowedCipherAlgs) String() string {
return fmt.Sprintf("allowedCipherAlgs: %v", *a)
}
func (a *allowedCipherAlgs) Set(value string) error {
*a = append(*a, strings.TrimSpace(value))
return nil
}
func (a allowedHMACAlgs) allowed(h xsnet.CSHmacAlg) bool {
for i := 0; i < len(a); i++ {
if a[i] == "H_all" || a[i] == h.String() {
return true
}
}
return false
}
func (a *allowedHMACAlgs) String() string {
return fmt.Sprintf("allowedHMACAlgs: %v", *a)
}
func (a *allowedHMACAlgs) Set(value string) error {
*a = append(*a, strings.TrimSpace(value))
return nil
}
// Main server that listens and spawns goroutines for each
// connecting client to serve interactive or file copy sessions
// and any requested tunnels.
// Note that this server does not do UNIX forks of itself to give
// each client its own separate manager process, so if the main
// daemon dies, all clients will be rudely disconnected.
// Consider this when planning to restart or upgrade in-place an installation.
// TODO: reduce gocyclo
func main() {
version := hkexsh.Version
var vopt bool
var chaffEnabled bool
var chaffFreqMin uint
@ -385,17 +510,27 @@ func main() {
var dbg bool
var laddr string
var useSystemPasswd bool
flag.BoolVar(&vopt, "v", false, "show version")
flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen")
flag.BoolVar(&chaffEnabled, "e", true, "enabled chaff pkts")
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`)
flag.BoolVar(&useSysLogin, "L", false, "use system login")
flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts")
flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min (msecs)")
flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max (msecs)")
flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max (bytes)")
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.Parse()
if vopt {
fmt.Printf("version v%s\n", version)
fmt.Printf("version %s (%s)\n", version, gitCommit)
os.Exit(0)
}
@ -406,14 +541,41 @@ func main() {
}
}
Log, _ = logger.New(logger.LOG_DAEMON|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "hkexshd") // nolint: gosec
hkexnet.Init(dbg, "hkexshd", logger.LOG_DAEMON|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR)
// Enforce some sane min/max vals on chaff flags
if chaffFreqMin < 2 {
chaffFreqMin = 2
}
if chaffFreqMax == 0 {
chaffFreqMax = chaffFreqMin + 1
}
if chaffBytesMax == 0 || chaffBytesMax > 4096 {
chaffBytesMax = 64
}
Log, _ = logger.New(logger.LOG_DAEMON|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR, "xsd") // nolint: gosec
xsnet.Init(dbg, "xsd", logger.LOG_DAEMON|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR)
if dbg {
log.SetOutput(Log)
} else {
log.SetOutput(ioutil.Discard)
}
// Set up allowed algs, if specified (default allow all)
if len(aKEXAlgs) == 0 {
aKEXAlgs = []string{"KEX_all"}
}
logger.LogNotice(fmt.Sprintf("Allowed KEXAlgs: %v\n", aKEXAlgs)) // nolint: gosec,errcheck
if len(aCipherAlgs) == 0 {
aCipherAlgs = []string{"C_all"}
}
logger.LogNotice(fmt.Sprintf("Allowed CipherAlgs: %v\n", aCipherAlgs)) // nolint: gosec,errcheck
if len(aHMACAlgs) == 0 {
aHMACAlgs = []string{"H_all"}
}
logger.LogNotice(fmt.Sprintf("Allowed HMACAlgs: %v\n", aHMACAlgs)) // nolint: gosec,errcheck
// Set up handler for daemon signalling
exitCh := make(chan os.Signal, 1)
signal.Notify(exitCh, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGINT), os.Signal(syscall.SIGHUP), os.Signal(syscall.SIGUSR1), os.Signal(syscall.SIGUSR2))
@ -437,9 +599,11 @@ func main() {
}
}()
// Listen on TCP port 2000 on all available unicast and
// anycast IP addresses of the local system.
l, err := hkexnet.Listen("tcp", laddr)
proto := "tcp"
if kcpMode != "unused" {
proto = "kcp"
}
l, err := xsnet.Listen(proto, laddr, kcpMode)
if err != nil {
log.Fatal(err)
}
@ -448,9 +612,22 @@ func main() {
log.Println("Serving on", laddr)
for {
// Wait for a connection.
// Then check if client-proposed algs are allowed
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")
@ -462,28 +639,35 @@ func main() {
// Handle the connection in a new goroutine.
// The loop then returns to accepting, so that
// multiple connections may be served concurrently.
go func(hc *hkexnet.Conn) (e error) {
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()
})
//We use io.ReadFull() here to guarantee we consume
//just the data we want for the hkexsh.Session, and no more.
//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 hkexsh.Session
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("hkexsh.Session read:%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 hkexsh.Session fmt]")
log.Println("[Bad xs.Session fmt]")
return err
}
tmp := make([]byte, len1)
_, err = io.ReadFull(hc, tmp)
if err != nil {
log.Println("[Bad hkexsh.Session.Op]")
log.Println("[Bad xs.Session.Op]")
return err
}
rec.SetOp(tmp)
@ -491,7 +675,7 @@ func main() {
tmp = make([]byte, len2)
_, err = io.ReadFull(hc, tmp)
if err != nil {
log.Println("[Bad hkexsh.Session.Who]")
log.Println("[Bad xs.Session.Who]")
return err
}
rec.SetWho(tmp)
@ -499,7 +683,7 @@ func main() {
tmp = make([]byte, len3)
_, err = io.ReadFull(hc, tmp)
if err != nil {
log.Println("[Bad hkexsh.Session.ConnHost]")
log.Println("[Bad xs.Session.ConnHost]")
return err
}
rec.SetConnHost(tmp)
@ -507,7 +691,7 @@ func main() {
tmp = make([]byte, len4)
_, err = io.ReadFull(hc, tmp)
if err != nil {
log.Println("[Bad hkexsh.Session.TermType]")
log.Println("[Bad xs.Session.TermType]")
return err
}
rec.SetTermType(tmp)
@ -515,7 +699,7 @@ func main() {
tmp = make([]byte, len5)
_, err = io.ReadFull(hc, tmp)
if err != nil {
log.Println("[Bad hkexsh.Session.Cmd]")
log.Println("[Bad xs.Session.Cmd]")
return err
}
rec.SetCmd(tmp)
@ -523,22 +707,28 @@ func main() {
tmp = make([]byte, len6)
_, err = io.ReadFull(hc, tmp)
if err != nil {
log.Println("[Bad hkexsh.Session.AuthCookie]")
log.Println("[Bad xs.Session.AuthCookie]")
return err
}
rec.SetAuthCookie(tmp)
log.Printf("[hkexsh.Session: op:%c who:%s connhost:%s cmd:%s auth:****]\n",
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 hkexsh.AuthUserByToken(string(rec.Who()), string(rec.ConnHost()), string(rec.AuthCookie(true))) {
if xs.AuthUserByToken(xs.NewAuthCtx(), string(rec.Who()), string(rec.ConnHost()), string(rec.AuthCookie(true))) {
valid = true
} else {
valid, allowedCmds = hkexsh.AuthUserByPasswd(string(rec.Who()), string(rec.AuthCookie(true)), "/etc/hkexsh.passwd")
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()
@ -559,8 +749,8 @@ func main() {
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 ~/.hkexsh_id", token)
cmdStatus, runErr := runShellAs(string(rec.Who()), string(rec.TermType()), tokenCmd, false, hc, chaffEnabled)
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})
@ -568,14 +758,14 @@ func main() {
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(hkexnet.CSOType(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()), string(rec.TermType()), string(rec.Cmd()), false, hc, chaffEnabled)
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})
@ -583,7 +773,7 @@ func main() {
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(hkexnet.CSOType(cmdStatus))
hc.SetStatus(xsnet.CSOType(cmdStatus))
}
} else if rec.Op()[0] == 's' {
// Interactive session
@ -591,10 +781,7 @@ func main() {
hname := goutmp.GetHost(addr.String())
logger.LogNotice(fmt.Sprintf("[Running shell for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck
utmpx := goutmp.Put_utmp(string(rec.Who()), hname)
defer func() { goutmp.Unput_utmp(utmpx) }()
goutmp.Put_lastlog_entry("hkexsh", string(rec.Who()), hname)
cmdStatus, runErr := runShellAs(string(rec.Who()), string(rec.TermType()), string(rec.Cmd()), true, hc, chaffEnabled)
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})
@ -602,7 +789,7 @@ func main() {
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(hkexnet.CSOType(cmdStatus))
hc.SetStatus(xsnet.CSOType(cmdStatus))
}
} else if rec.Op()[0] == 'D' {
// File copy (destination) operation - client copy to server
@ -619,13 +806,15 @@ func main() {
} else {
logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck
}
hc.SetStatus(hkexnet.CSOType(cmdStatus))
// 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, hkexnet.CSOExitStatus) // nolint: gosec,errcheck
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")
@ -639,14 +828,18 @@ func main() {
// Returned hopefully via an EOF or exit/logout;
logger.LogNotice(fmt.Sprintf("[Command 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(hkexnet.CSOType(cmdStatus))
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 hkexsh.Session]")) // nolint: gosec,errcheck
logger.LogErr(fmt.Sprintln("[Bad xs.Session]")) // nolint: gosec,errcheck
}
return
}(&conn) // nolint: errcheck

View file

@ -1,6 +1,6 @@
package hkexnet
package xsnet
// Copyright (c) 2017-2018 Russell Magee
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -20,9 +20,11 @@ import (
"hash"
"log"
"blitter.com/go/cryptmt"
"github.com/aead/chacha20/chacha"
"golang.org/x/crypto/blowfish"
"golang.org/x/crypto/twofish"
"blitter.com/go/cryptmt"
// hash algos must be manually imported thusly:
// (Would be nice if the golang pkg docs were more clear
// on this...)
@ -55,9 +57,9 @@ func expandKeyMat(keymat []byte, blocksize int) []byte {
}
/* Support functionality to set up encryption after a channel has
been negotiated via hkexnet.go
been negotiated via xsnet.go
*/
func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err error) {
func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err error) {
var key []byte
var block cipher.Block
var iv []byte
@ -75,7 +77,6 @@ func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err err
iv = keymat[aes.BlockSize : aes.BlockSize+ivlen]
rc = cipher.NewOFB(block, iv)
log.Printf("[cipher AES_256 (%d)]\n", copts)
break
case CAlgTwofish128:
keymat = expandKeyMat(keymat, twofish.BlockSize)
key = keymat[0:twofish.BlockSize]
@ -84,7 +85,6 @@ func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err err
iv = keymat[twofish.BlockSize : twofish.BlockSize+ivlen]
rc = cipher.NewOFB(block, iv)
log.Printf("[cipher TWOFISH_128 (%d)]\n", copts)
break
case CAlgBlowfish64:
keymat = expandKeyMat(keymat, blowfish.BlockSize)
key = keymat[0:blowfish.BlockSize]
@ -102,11 +102,21 @@ func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err err
iv = keymat[blowfish.BlockSize : blowfish.BlockSize+ivlen]
rc = cipher.NewOFB(block, iv)
log.Printf("[cipher BLOWFISH_64 (%d)]\n", copts)
break
case CAlgCryptMT1:
rc = cryptmt.NewCipher(keymat)
rc = cryptmt.New(nil, nil, keymat)
log.Printf("[cipher CRYPTMT1 (%d)]\n", copts)
break
case CAlgChaCha20_12:
keymat = expandKeyMat(keymat, chacha.KeySize)
key = keymat[0:chacha.KeySize]
ivlen = chacha.INonceSize
iv = keymat[chacha.KeySize : chacha.KeySize+ivlen]
rc, err = chacha.NewCipher(iv, key, chacha.INonceSize)
if err != nil {
log.Printf("[ChaCha20 config error]\n")
fmt.Printf("[ChaCha20 config error]\n")
}
// TODO: SetCounter() to something derived from key or nonce or extra keymat?
log.Printf("[cipher CHACHA20_12 (%d)]\n", copts)
default:
log.Printf("[invalid cipher (%d)]\n", copts)
fmt.Printf("DOOFUS SET A VALID CIPHER ALG (%d)\n", copts)
@ -123,7 +133,6 @@ func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err err
if !halg.Available() {
log.Fatal("hash not available!")
}
break
case HmacSHA512:
log.Printf("[hash HmacSHA512 (%d)]\n", hopts)
halg := crypto.SHA512
@ -131,7 +140,6 @@ func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err err
if !halg.Available() {
log.Fatal("hash not available!")
}
break
default:
log.Printf("[invalid hmac (%d)]\n", hopts)
fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts)

View file

@ -1,12 +1,17 @@
// consts.go - consts for hkexnet
// consts.go - consts for xsnet
// Copyright (c) 2017-2018 Russell Magee
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
// golang implementation by Russ Magee (rmagee_at_gmail.com)
package hkexnet
package xsnet
// KEX algorithm values
//
// Specified (in string form) as the extensions parameter
// to xsnet.Dial()
// Alg is sent in a uint8 so there are up to 256 possible
const (
KEX_HERRADURA256 = iota // this MUST be first for default if omitted in ctor
KEX_HERRADURA512
@ -20,24 +25,33 @@ const (
KEX_KYBER768
KEX_KYBER1024
KEX_resvd11
KEX_resvd12
KEX_resvd13
KEX_NEWHOPE
KEX_NEWHOPE_SIMPLE // 'NewHopeLP-Simple' - https://eprint.iacr.org/2016/1157
KEX_resvd14
KEX_resvd15
KEX_FRODOKEM_1344AES
KEX_FRODOKEM_1344SHAKE
KEX_FRODOKEM_976AES
KEX_FRODOKEM_976SHAKE
KEX_invalid = 255
)
// Sent from client to server in order to specify which
// algo shall be used (eg., HerraduraKEx, [TODO: others...])
// algo shall be used (see xsnet.KEX_HERRADURA256, ...)
type KEXAlg uint8
// Extended exit status codes - indicate comm/pty issues
// rather than remote end normal UNIX exit codes
const (
CSENone = 1024 + iota
CSETruncCSO // No CSOExitStatus in payload
CSEStillOpen // Channel closed unexpectedly
CSEExecFail // cmd.Start() (exec) failed
CSEPtyExecFail // pty.Start() (exec w/pty) failed
CSENone = 1024 + iota
CSETruncCSO // No CSOExitStatus in payload
CSEStillOpen // Channel closed unexpectedly
CSEExecFail // cmd.Start() (exec) failed
CSEPtyExecFail // pty.Start() (exec w/pty) failed
CSEPtyGetNameFail // failed to obtain pty name
CSEKEXAlgDenied // server rejected proposed KEX alg
CSECipherAlgDenied // server rejected proposed Cipher alg
CSEHMACAlgDenied // server rejected proposed HMAC alg
)
// Extended (>255 UNIX exit status) codes
@ -53,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
@ -63,8 +80,8 @@ const (
CSOTunHangup // client -> server: tunnel lport hung up
)
// TunEndpoint.tunCtl control values - used to control workers for client or server tunnels
// depending on the code
// TunEndpoint.tunCtl control values - used to control workers for client
// or server tunnels depending on the code
const (
TunCtl_Client_Listen = 'a'
// [CSOTunAccept]
@ -77,28 +94,31 @@ const (
// action:server side should dial() rport on client's behalf
)
// Channel status Op byte type
// Channel status Op byte type (see CSONone, ... and CSENone, ...)
type CSOType uint32
//TODO: this should be small (max unfragmented packet size?)
const MAX_PAYLOAD_LEN = 4*1024*1024*1024 - 1
const MAX_PAYLOAD_LEN = 2*1024*1024*1024 - 1
// Session symmetric crypto algs
const (
CAlgAES256 = iota
CAlgTwofish128 // golang.org/x/crypto/twofish
CAlgBlowfish64 // golang.org/x/crypto/blowfish
CAlgCryptMT1 //cryptmt using mtwist64
CAlgChaCha20_12
CAlgNoneDisallowed
)
// Available ciphers for hkex.Conn
type CSCipherAlg uint32
// Session packet auth HMAC algs
const (
HmacSHA256 = iota
HmacSHA512
HmacNoneDisallowed
)
// Available HMACs for hkex.Conn (TODO: not currently used)
// Available HMACs for hkex.Conn
type CSHmacAlg uint32

129
xsnet/kcp.go Normal file
View file

@ -0,0 +1,129 @@
package xsnet
import (
"crypto/sha1"
"errors"
"fmt"
"net"
"blitter.com/go/xs/logger"
kcp "github.com/xtaci/kcp-go"
"golang.org/x/crypto/pbkdf2"
)
const (
KCP_NONE = iota
KCP_AES
KCP_BLOWFISH
KCP_CAST5
KCP_SM4
KCP_SALSA20
KCP_SIMPLEXOR
KCP_TEA
KCP_3DES
KCP_TWOFISH
KCP_XTEA
)
// for github.com/xtaci/kcp-go BlockCrypt alg selection
type KCPAlg uint8
var (
kcpKeyBytes []byte = []byte("SET THIS") // symmetric crypto key for KCP (github.com/xtaci/kcp-go) if used
kcpSaltBytes []byte = []byte("ALSO SET THIS")
)
func getKCPalgnum(extensions []string) (k KCPAlg) {
k = KCP_AES // default
var s string
for _, s = range extensions {
switch s {
case "KCP_NONE":
k = KCP_NONE
break //golint:ignore SA4011 out of for
case "KCP_AES":
k = KCP_AES
break //out of for
case "KCP_BLOWFISH":
k = KCP_BLOWFISH
break //out of for
case "KCP_CAST5":
k = KCP_CAST5
break //out of for
case "KCP_SM4":
k = KCP_SM4
break //out of for
case "KCP_SALSA20":
k = KCP_SALSA20
break //out of for
case "KCP_SIMPLEXOR":
k = KCP_SIMPLEXOR
break //out of for
case "KCP_TEA":
k = KCP_TEA
break //out of for
case "KCP_3DES":
k = KCP_3DES
break //out of for
case "KCP_TWOFISH":
k = KCP_TWOFISH
break //out of for
case "KCP_XTEA":
k = KCP_XTEA
break //out of for
}
}
logger.LogDebug(fmt.Sprintf("[KCP BlockCrypt '%s' activated]", s))
return
}
func SetKCPKeyAndSalt(key []byte, salt []byte) {
kcpKeyBytes = key
kcpSaltBytes = salt
}
func _newKCPBlockCrypt(key []byte, extensions []string) (b kcp.BlockCrypt, e error) {
switch getKCPalgnum(extensions) {
case KCP_NONE:
return kcp.NewNoneBlockCrypt(key)
case KCP_AES:
return kcp.NewAESBlockCrypt(key)
case KCP_BLOWFISH:
return kcp.NewBlowfishBlockCrypt(key)
case KCP_CAST5:
return kcp.NewCast5BlockCrypt(key)
case KCP_SM4:
return kcp.NewSM4BlockCrypt(key)
case KCP_SALSA20:
return kcp.NewSalsa20BlockCrypt(key)
case KCP_SIMPLEXOR:
return kcp.NewSimpleXORBlockCrypt(key)
case KCP_TEA:
return kcp.NewTEABlockCrypt(key)
case KCP_3DES:
return kcp.NewTripleDESBlockCrypt(key)
case KCP_TWOFISH:
return kcp.NewTwofishBlockCrypt(key)
case KCP_XTEA:
return kcp.NewXTEABlockCrypt(key)
}
return nil, errors.New("Invalid KCP BlockCrypto specified")
}
func kcpDial(ipport string, extensions []string) (c net.Conn, err error) {
kcpKey := pbkdf2.Key(kcpKeyBytes, kcpSaltBytes, 1024, 32, sha1.New)
block, be := _newKCPBlockCrypt([]byte(kcpKey), extensions)
_ = be
return kcp.DialWithOptions(ipport, block, 10, 3)
}
func kcpListen(ipport string, extensions []string) (l net.Listener, err error) {
kcpKey := pbkdf2.Key(kcpKeyBytes, kcpSaltBytes, 1024, 32, sha1.New)
block, be := _newKCPBlockCrypt([]byte(kcpKey), extensions)
_ = be
return kcp.ListenWithOptions(ipport, block, 10, 3)
}
func (hl *HKExListener) AcceptKCP() (c net.Conn, e error) {
return hl.l.(*kcp.Listener).AcceptKCP()
}

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,12 @@
// hkextun.go - Tunnel setup using an established hkexnet.Conn
// hkextun.go - Tunnel setup using an established xsnet.Conn
// Copyright (c) 2017-2018 Russell Magee
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
// golang implementation by Russ Magee (rmagee_at_gmail.com)
package hkexnet
package xsnet
import (
"bytes"
@ -18,7 +18,7 @@ import (
"sync"
"time"
"blitter.com/go/hkexsh/logger"
"blitter.com/go/xs/logger"
)
type (
@ -46,7 +46,7 @@ type (
Lport uint16 // ... ie., RPort is on server, LPort is on client
Peer string //net.Addr
Died bool // set by client upon receipt of a CSOTunDisconn
KeepAlive uint // must be reset by client to keep server dial() alive
KeepAlive uint32 // must be reset by client to keep server dial() alive
Ctl chan rune //See TunCtl_* consts
Data chan []byte
}
@ -67,6 +67,8 @@ func (hc *Conn) CollapseAllTunnels(client bool) {
}
func (hc *Conn) InitTunEndpoint(lp uint16, p string /* net.Addr */, rp uint16) {
hc.Lock()
defer hc.Unlock()
if (*hc.tuns) == nil {
(*hc.tuns) = make(map[uint16]*TunEndpoint)
}
@ -87,6 +89,7 @@ func (hc *Conn) InitTunEndpoint(lp uint16, p string /* net.Addr */, rp uint16) {
// data channel removed on closure. Re-create it
(*hc.tuns)[rp].Data = make(chan []byte, 1)
}
(*hc.tuns)[rp].KeepAlive = 0
(*hc.tuns)[rp].Died = false
}
return
@ -149,34 +152,23 @@ func (hc *Conn) StartClientTunnel(lport, rport uint16) {
if e == io.EOF {
logger.LogDebug(fmt.Sprintf("[ClientTun] worker A: lport Disconnected: shutting down tunnel %v", (*hc.tuns)[rport]))
// if Died was already set, server-side already is gone.
if !(*hc.tuns)[rport].Died {
if hc.TunIsAlive(rport) {
hc.WritePacket(tunDst.Bytes(), CSOTunHangup)
}
(*hc.tuns)[rport].Died = true
if (*hc.tuns)[rport].Data != nil {
close((*hc.tuns)[rport].Data)
(*hc.tuns)[rport].Data = nil
}
hc.ShutdownTun(rport) // FIXME: race-C
break
} else if strings.Contains(e.Error(), "i/o timeout") {
if (*hc.tuns)[rport].Died {
if !hc.TunIsAlive(rport) {
logger.LogDebug(fmt.Sprintf("[ClientTun] worker A: timeout: Server side died, hanging up %v", (*hc.tuns)[rport]))
if (*hc.tuns)[rport].Data != nil {
close((*hc.tuns)[rport].Data)
(*hc.tuns)[rport].Data = nil
}
hc.ShutdownTun(rport)
break
}
} else {
logger.LogDebug(fmt.Sprintf("[ClientTun] worker A: Read error from lport of tun %v\n%s", (*hc.tuns)[rport], e))
if !(*hc.tuns)[rport].Died {
if hc.TunIsAlive(rport) {
hc.WritePacket(tunDst.Bytes(), CSOTunHangup)
}
(*hc.tuns)[rport].Died = true
if (*hc.tuns)[rport].Data != nil {
close((*hc.tuns)[rport].Data)
(*hc.tuns)[rport].Data = nil
}
hc.ShutdownTun(rport)
break
}
}
@ -207,7 +199,7 @@ func (hc *Conn) StartClientTunnel(lport, rport uint16) {
logger.LogDebug("[ClientTun] worker B: starting")
for {
bytes, ok := <-(*hc.tuns)[rport].Data
bytes, ok := <-(*hc.tuns)[rport].Data // FIXME: race-C w/ShutdownTun calls
if ok {
c.SetWriteDeadline(time.Now().Add(200 * time.Millisecond))
_, e := c.Write(bytes)
@ -229,7 +221,7 @@ func (hc *Conn) StartClientTunnel(lport, rport uint16) {
// When both workers have exited due to a disconnect or other
// condition, it's safe to remove the tunnel descriptor.
logger.LogDebug("[ClientTun] workers exited")
delete((*hc.tuns), rport)
hc.ShutdownTun(rport)
} // end for-accept
} // end Listen() block
}
@ -237,6 +229,50 @@ func (hc *Conn) StartClientTunnel(lport, rport uint16) {
}()
}
func (hc *Conn) AgeTunnel(endp uint16) uint32 {
hc.Lock()
defer hc.Unlock()
(*hc.tuns)[endp].KeepAlive += 1
return (*hc.tuns)[endp].KeepAlive
}
func (hc *Conn) ResetTunnelAge(endp uint16) {
hc.Lock()
defer hc.Unlock()
(*hc.tuns)[endp].KeepAlive = 0
}
func (hc *Conn) TunIsNil(endp uint16) bool {
hc.Lock()
defer hc.Unlock()
return (*hc.tuns)[endp] == nil
}
func (hc *Conn) TunIsAlive(endp uint16) bool {
hc.Lock()
defer hc.Unlock()
return !(*hc.tuns)[endp].Died
}
func (hc *Conn) MarkTunDead(endp uint16) {
hc.Lock()
defer hc.Unlock()
(*hc.tuns)[endp].Died = true
}
func (hc *Conn) ShutdownTun(endp uint16) {
hc.Lock()
defer hc.Unlock()
if (*hc.tuns)[endp] != nil {
(*hc.tuns)[endp].Died = true
if (*hc.tuns)[endp].Data != nil {
close((*hc.tuns)[endp].Data)
(*hc.tuns)[endp].Data = nil
}
}
delete((*hc.tuns), endp)
}
func (hc *Conn) StartServerTunnel(lport, rport uint16) {
hc.InitTunEndpoint(lport, "", rport)
var err error
@ -244,12 +280,34 @@ func (hc *Conn) StartServerTunnel(lport, rport uint16) {
go func() {
var wg sync.WaitGroup
//
// worker to age server tunnel and kill it if keepalives
// stop from client
//
wg.Add(1)
go func() {
defer wg.Done()
for {
time.Sleep(100 * time.Millisecond)
if hc.TunIsNil(rport) {
logger.LogDebug("[ServerTun] worker A: Client endpoint removed.")
break
}
age := hc.AgeTunnel(rport)
if age > 25 {
hc.MarkTunDead(rport)
logger.LogDebug("[ServerTun] worker A: Client died, hanging up.")
break
}
}
}()
for cmd := range (*hc.tuns)[rport].Ctl {
var c net.Conn
logger.LogDebug(fmt.Sprintf("[ServerTun] got Ctl '%c'.", cmd))
if cmd == 'd' {
// if re-using tunnel, re-init it
if (*hc.tuns)[rport] == nil {
if hc.TunIsNil(rport) {
hc.InitTunEndpoint(lport, "", rport)
}
logger.LogDebug("[ServerTun] dialling...")
@ -294,34 +352,23 @@ func (hc *Conn) StartServerTunnel(lport, rport uint16) {
if e != nil {
if e == io.EOF {
logger.LogDebug(fmt.Sprintf("[ServerTun] worker A: rport Disconnected: shutting down tunnel %v", (*hc.tuns)[rport]))
if !(*hc.tuns)[rport].Died {
if hc.TunIsAlive(rport) {
hc.WritePacket(tunDst.Bytes(), CSOTunDisconn)
}
(*hc.tuns)[rport].Died = true
if (*hc.tuns)[rport].Data != nil {
close((*hc.tuns)[rport].Data)
(*hc.tuns)[rport].Data = nil
}
hc.ShutdownTun(rport) // FIXME: race-A
break
} else if strings.Contains(e.Error(), "i/o timeout") {
if (*hc.tuns)[rport].Died {
if !hc.TunIsAlive(rport) {
logger.LogDebug(fmt.Sprintf("[ServerTun] worker A: timeout: Server side died, hanging up %v", (*hc.tuns)[rport]))
if (*hc.tuns)[rport].Data != nil {
close((*hc.tuns)[rport].Data)
(*hc.tuns)[rport].Data = nil
}
hc.ShutdownTun(rport) // FIXME: race-B
break
}
} else {
logger.LogDebug(fmt.Sprintf("[ServerTun] worker A: Read error from rport of tun %v: %s", (*hc.tuns)[rport], e))
if !(*hc.tuns)[rport].Died {
if hc.TunIsAlive(rport) {
hc.WritePacket(tunDst.Bytes(), CSOTunDisconn)
}
(*hc.tuns)[rport].Died = true
if (*hc.tuns)[rport].Data != nil {
close((*hc.tuns)[rport].Data)
(*hc.tuns)[rport].Data = nil
}
hc.ShutdownTun(rport) // FIXME: race-C
break
}
}
@ -329,14 +376,6 @@ func (hc *Conn) StartServerTunnel(lport, rport uint16) {
rBuf = append(tunDst.Bytes(), rBuf[:n]...)
hc.WritePacket(rBuf[:n+4], CSOTunData)
}
if (*hc.tuns)[rport].KeepAlive > 50 {
(*hc.tuns)[rport].Died = true
logger.LogDebug("[ServerTun] worker A: Client died, hanging up.")
} else {
(*hc.tuns)[rport].KeepAlive += 1
}
}
logger.LogDebug("[ServerTun] worker A: exiting")
}()
@ -354,7 +393,7 @@ func (hc *Conn) StartServerTunnel(lport, rport uint16) {
logger.LogDebug("[ServerTun] worker B: starting")
for {
rData, ok := <-(*hc.tuns)[rport].Data
rData, ok := <-(*hc.tuns)[rport].Data // FIXME: race-A, race-B, race-C (w/ShutdownTun() calls)
if ok {
c.SetWriteDeadline(time.Now().Add(200 * time.Millisecond))
_, e := c.Write(rData)

View file

@ -4,13 +4,13 @@ EXTPKGS = bytes,errors,flag,fmt,internal,io,log,net,os,path,runtime,time,strings
EXE = $(notdir $(shell pwd))
all:
go build .
go build $(BUILDOPTS) .
clean:
$(RM) $(EXE) $(EXE).exe
vis:
go-callvis -format png -file hkexpasswd-vis -ignore $(EXTPKGS) -group pkg,type .
go-callvis -format png -file xspasswd-vis -ignore $(EXTPKGS) -group pkg,type .
lint:
-gometalinter --deadline=60s | sort
-golangci-lint run

BIN
xspasswd/xspasswd-vis.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -1,7 +1,7 @@
// Util to generate/store passwords for users in a file akin to /etc/passwd
// suitable for the demo hkexsh server, using bcrypt.
//
// Copyright (c) 2017-2018 Russell Magee
// Copyright (c) 2017-2020 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
@ -17,21 +17,33 @@ import (
"log"
"os"
hkexsh "blitter.com/go/hkexsh"
xs "blitter.com/go/xs"
"github.com/jameskeane/bcrypt"
)
var (
version string
gitCommit string
)
// nolint: gocyclo
func main() {
var vopt bool
var pfName string
var newpw string
var confirmpw string
var userName string
flag.BoolVar(&vopt, "v", false, "show version")
flag.StringVar(&userName, "u", "", "username")
flag.StringVar(&pfName, "f", "/etc/hkexsh.passwd", "passwd file")
flag.StringVar(&pfName, "f", "/etc/xs.passwd", "passwd file")
flag.Parse()
if vopt {
fmt.Printf("version %s (%s)\n", version, gitCommit)
os.Exit(0)
}
var uname string
if len(userName) == 0 {
log.Println("specify username with -u")
@ -47,7 +59,7 @@ func main() {
uname = userName
fmt.Printf("New Password:")
ab, err := hkexsh.ReadPassword(int(os.Stdin.Fd()))
ab, err := xs.ReadPassword(os.Stdin.Fd())
fmt.Printf("\r\n")
if err != nil {
log.Fatal(err)
@ -56,7 +68,7 @@ func main() {
newpw = string(ab)
fmt.Printf("Confirm:")
ab, err = hkexsh.ReadPassword(int(os.Stdin.Fd()))
ab, err = xs.ReadPassword(os.Stdin.Fd())
fmt.Printf("\r\n")
if err != nil {
log.Fatal(err)
@ -118,7 +130,7 @@ func main() {
records = append(records, newRec)
}
outFile, err := ioutil.TempFile("", "hkexsh-passwd")
outFile, err := ioutil.TempFile("", "xs-passwd")
if err != nil {
log.Fatal(err)
}