diff --git a/.golangci.yml b/.golangci.yml deleted file mode 100644 index a4cad3d..0000000 --- a/.golangci.yml +++ /dev/null @@ -1,160 +0,0 @@ -linters-settings: - depguard: - list-type: blacklist - packages: - # logging is allowed only by logutils.Log, logrus - # is allowed to use only in logutils package - - github.com/sirupsen/logrus - packages-with-error-message: - - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" - dupl: - threshold: 125 - funlen: - lines: 125 - statements: 50 - gci: - local-prefixes: github.com/golangci/golangci-lint - goconst: - min-len: 2 - min-occurrences: 2 - gocritic: - enabled-tags: - - diagnostic - - experimental - - performance - #- style - #- opinionated - disabled-checks: - - commentFormatting - - commentedOutCode - - dupImport # https://github.com/go-critic/go-critic/issues/845 - - ifElseChain - - octalLiteral - - whyNoLint - - wrapperFunc - gocyclo: - min-complexity: 15 - goimports: - local-prefixes: github.com/golangci/golangci-lint - gomnd: - settings: - mnd: - # don't include the "operation" and "assign" - checks: argument,case,condition,return - govet: - check-shadowing: true - settings: - printf: - funcs: - - (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 - lll: - line-length: 140 - maligned: - suggest-new: true - misspell: - locale: en_CA - nolintlint: - allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space) - allow-unused: false # report any unused nolint directives - require-explanation: false # don't require an explanation for nolint directives - require-specific: false # don't require nolint directives to be specific about which linter is being skipped - -linters: - # please, do not use `enable-all`: it's deprecated and will be removed soon. - # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint - disable-all: true - enable: - - bodyclose - - depguard - - dogsled - - dupl - - errcheck - - exhaustive - - exportloopref - - funlen - - gochecknoinits - - goconst - - gocritic - - gocyclo - - gofmt - - goimports - #- golint - - gomnd - - goprintffuncname - - gosec - - gosimple - - govet - - ineffassign - #- interfacer - - lll - - misspell - #- nakedret - - noctx - - nolintlint - - rowserrcheck - #- scopelint - - staticcheck - #- structcheck - - stylecheck - - typecheck - - unconvert - - unparam - - unused - #- varcheck - - whitespace - - # don't enable: - # - asciicheck - # - deadcode - # - gochecknoglobals - # - gocognit - # - godot - # - godox - # - goerr113 - # - golint - # - interfacer - # - maligned - # - nestif - # - prealloc - ## - rowserrcheck - # - scopelint - # - structcheck - # - testpackage - # - varcheck - # - wsl - -issues: - # Excluding configuration per-path, per-linter, per-text and per-source - exclude-rules: - - path: _test\.go - linters: - - gomnd - - # https://github.com/go-critic/go-critic/issues/926 - - linters: - - gocritic - text: "unnecessaryDefer:" - - # TODO temporary rule, must be removed - # seems related to v0.34.1, but I was not able to reproduce locally, - # I was also not able to reproduce in the CI of a fork, - # only the golangci-lint CI seems to be affected by this invalid analysis. - - path: pkg/golinters/scopelint.go - text: 'directive `//nolint:interfacer` is unused for linter interfacer' - -run: - skip-dirs: - - test/testdata_etc - - internal/cache - - internal/renameio - - internal/robustio - -# golangci.com configuration -# https://github.com/golangci/golangci/wiki/Configuration -service: - golangci-lint-version: 1.23.x # use the fixed version to not introduce new linters unexpectedly - prepare: - - echo "here I can run custom commands, but no preparation needed for this repo" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_MERIT.md similarity index 94% rename from CODE_OF_CONDUCT.md rename to CODE_OF_MERIT.md index 9bf135e..149ec81 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_MERIT.md @@ -55,7 +55,5 @@ 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). +This document is based upon the 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/ diff --git a/LICENSE b/LICENSE.mit similarity index 93% rename from LICENSE rename to LICENSE.mit index 13144f3..0cd89ee 100644 --- a/LICENSE +++ b/LICENSE.mit @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 - 2021 Russell Magee (xs/xsd/xsnet/xspasswd) +Copyright (c) 2017 - 2018 Russell Magee (hkexsh/hkexshd/hkexnet/hkexpasswd) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile index 67eb22d..876aeba 100644 --- a/Makefile +++ b/Makefile @@ -1,60 +1,24 @@ -VERSION := 0.9.11 -.PHONY: lint vis clean common client server passwd\ - subpkgs install uninstall reinstall scc - -## Tag version of binaries with build info wrt. -## GO111MODULE(=on) and vendor/ setup vs. $GOPATH pkg builds -############################################################ -ifeq ($(shell go env GOMOD),) -MTAG= -else -MTAG="-m" -endif +.PHONY: lint vis clean common client server passwd subpkgs install uninstall reinstall -# If available, one may build 'garbled' binaries -# See https://github.com/burrowers/garble.git -ifeq ($(GARBLE),y) -GO=garble -literals -tiny -debugdir=garbled -else -GO=go -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)\"" +#ifeq ($(MAKEOPTS),) + MAKEOPTS = $(MAKEOPTS) #endif -SUBPKGS = logger spinsult xsnet -TOOLS = xs xsd +GIT_COMMIT := $(shell git rev-list -1 HEAD) +VERSION := 0.8.7 +#ifeq ($(BUILDOPTS),) +BUILDOPTS :=$(BUILDOPTS)" -ldflags \"-X main.version=$(VERSION) -X main.gitCommit=$(GIT_COMMIT)\"" +#endif + +SUBPKGS = logger spinsult hkexnet +TOOLS = hkexpasswd hkexsh hkexshd 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 +all: common client server passwd clean: - @echo "Make: $(MAKE)" go clean . for d in $(SUBDIRS); do\ $(MAKE) -C $$d clean;\ @@ -72,56 +36,69 @@ tools: common: - $(GO) build . - go install -a . + go build . + go install . client: common - $(MAKE) BUILDOPTS=$(BUILDOPTS) -C xs + $(MAKE) BUILDOPTS=$(BUILDOPTS) -C hkexsh +ifeq ($(MSYSTEM),) +ifneq ($(GOOS),windows) server: common -ifeq ($(MSYSTEM),MSYS) - echo "Build of xsd server for Windows not yet supported" + $(MAKE) BUILDOPTS=$(BUILDOPTS) -C hkexshd else - $(MAKE) BUILDOPTS=$(BUILDOPTS) -C xsd + echo "Cross-build of hkexshd server for Windows not yet supported" +endif +else +server: common + echo "hkexshd server not (yet) supported on Windows" endif -scc: - @scc --exclude-dir=bacillus,vendor,garbled --cocomo-project-type=rlabs,3,1.12,1,1 + +passwd: common + $(MAKE) BUILDOPTS=$(BUILDOPTS) -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 https://github.com/TrueFurby/go-callvis to install."; \ + /bin/echo "go-callvis not found. Run go get github.com/Russtopia/go-callvis to install."; \ else \ - $(MAKE) -C xs vis;\ - $(MAKE) -C xsd vis;\ + make -C hkexsh vis;\ + make -C hkexshd vis;\ + make -C hkexpasswd vis; \ fi lint: - $(MAKE) -C xsd lint - $(MAKE) -C xs lint + make -C hkexpasswd lint + make -C hkexshd lint + make -C hkexsh lint reinstall: uninstall install install: - 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" + cp hkexsh/hkexsh $(INSTPREFIX)/bin +ifeq ($(MSYSTEM),) +ifneq ($(GOOS),windows) + cp hkexshd/hkexshd hkexpasswd/hkexpasswd $(INSTPREFIX)/sbin else - cp xs/xs $(INSTPREFIX)/bin - cd $(INSTPREFIX)/bin && ln -s xs xc && cd - - cp xsd/xsd $(INSTPREFIX)/sbin + 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" 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/xs $(INSTPREFIX)/bin/xc \ - $(INSTPREFIX)/bin/_xs $(INSTPREFIX)/bin/_xc -ifndef $(WIN_MSYS) - rm -f $(INSTPREFIX)/sbin/xsd + 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 endif diff --git a/README.md b/README.md index c7278f3..e00f8c6 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,19 @@ -[![GoDoc](https://godoc.org/blitter.com/go/xs?status.svg)](https://godoc.org/blitter.com/go/xs) +[![GoDoc](https://godoc.org/blitter.com/go/hkexsh?status.svg)](https://godoc.org/blitter.com/go/hkexsh) -# XS -![last build status](https://bacillus.blitter.com/onPush-xs-build/lastStatusIcon) +# HKExSh -- -XS (**X**perimental **S**hell) is a simple alternative to ssh (<5% total SLOCC) written from scratch in Go. -A testbed for candidate PQC (Post-Quantum Cryptography) KEMs (Key-Encapsulation Mechanisms) and symmetric -session encryption algorithms. - -xs also features integrated traffic chaffing to obscure interactive session and file copy activity. -Supports encrypted interactive and non-interactive sessions (remote commands), remote file copying and tunnels. - -Runs on Linux, FreeBSD, Windows (client only, MSYS) and Android (within Termux). https://gogs.blitter.com/RLabs/xs - -It is stable to the point that I use it for day-to-day remote access in place of, and in preference to, ssh. +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'). *** **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.** *** -The client and server programs (xs and xsd) use a mostly drop-in +The client and server programs (hkexsh and hkexshd) 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 @@ -32,9 +24,9 @@ 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://gitlab.com/yawning/kyber) -* The NEWHOPE algorithm [newhopecrypto.org](https://www.newhopecrypto.org/) :: [Yawning/newhope golang implementation](https://gitlab.com/yawning/newhope) -* The FrodoKEM algorithm [frodokem.org](https://frodokem.org/) :: Go version by [Eduardo E. S. Riccardi](https://github.com/kuking/go-frodokem) +* 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) + Currently supported session algorithms: @@ -43,34 +35,17 @@ Currently supported session algorithms: * Twofish-128 * Blowfish-64 * CryptMTv1 (64bit) (https://eprint.iacr.org/2005/165.pdf) -* ChaCha20 (https://github.com/aead/chacha20) -* HOPSCOTCH (https://gogs.blitter.com/Russtopia/hopscotch) [HMAC] * HMAC-SHA256 * HMAC-SHA512 -* WHIRLPOOL - -*** -**A Note on 'cryptographic agility'** - -It has been suggested recently to me that offering multiple cryptographic primitives is considered bad in 2021. - -An interesting question. See [this write-up for a discussion](https://paragonie.com/blog/2019/10/against-agility-in-cryptography-protocols). - -xs operates via the philosophy that **it is the server admin's prerogitive to configure local policy wrt. allowed cryptographic primitives**. The connection protocol makes no allowance for any sort of 'downgrades' or algo substitution during negotiation; there is no 'fallback' mode or two-way negotiation of what primitives to use, which would open the possibility of downgrade attacks. Unlike `ssh`, the server does not offer to clients a list of supported algorithms; the client can only offer a single configuration to the server, which it simply accepts or rejects without comment to the client. - -In all releases prior to v0.9.3, absent a specific whitelist of algs to allow, the server allows 'all' combinations of the above cryptographic primitives to be proposed by clients (but again, **only one** combination is proposed by the client in a single connect attempt). If the admin wishes to restrict the accepted algorithms now or at any future time, they may use the `-aK`, `-aC` and `-aH` options when launching the server to define a whitelist which excludes certain primitives. - -As of release v0.9.3, the default when supplying no explicit KEX, cipher or HMAC algorithms to `xsd` results in *no* algs being accepted; so the admin must decide on a specific whitelist of algorithms. -*** ### 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. +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. ### 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. +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 mode, tunnel specifiers, etc.) to be used for communication. ### 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). @@ -79,7 +54,8 @@ Packets are subject to padding (random size, randomly applied as prefix or postf 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```. +Within the hkexpasswd/ directory is a password-setting utility. HKExSh uses its own password file distinct from the system /etc/passwd to authenticate clients, using standard bcrypt+salt storage. This is currently done to allow alternate login credentials via hkexsh vs. console/ssh login, due to the experimental nature of the program. At some point in the future an option to use the system's /etc/passwd and /etc/shadow may be implemented, making the use of the auxilliary hkexpasswd utility optional or obsolete. + HERRADURA KEX @@ -94,69 +70,60 @@ 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: -### Installing - -As of Go 1.8, one can directly use `go install` to get the client `xs` and server `xsd` binaries; however it is not recommended, as `xsd` requires root and for general use should be in one of the system directories, akin to other daemons. If one insists, the following will work to place them in $HOME/go/bin: - -``` -$ go install blitter.com/go/xs/xs@latest -$ go install blitter.com/go/xs/xsd@latest -``` - -(NOTE the `-v` (version) option for binaries obtained in this manner will be blank; another reason to build them yourself locally using the steps below.) - +* Recent version of go (tested, at various times, with go-1.9 to go-1.12.4) +* [github.com/mattn/go-isatty](http://github.com/mattn/go-isatty) //terminal tty detection +* [github.com/kr/pty](http://github.com/kr/pty) //unix pty control (server pty connections) +* [github.com/jameskeane/bcrypt](http://github.com/jameskeane/bcrypt) //password storage/auth +* [blitter.com/go/goutmp](https://gogs.blitter.com/RLabs/goutmp) // wtmp/lastlog C bindings for user accounting +* [https://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 ``` -$ git clone https://gogs.blitter.com/RLabs/xs +$ go get -u blitter.com/go/hkexsh +$ cd $GOPATH/src/blitter.com/go/hkexsh +$ go build ./... # install all dependent go pkgs ``` ### To build ``` -$ cd xs -$ make clean && make +$ cd $GOPATH/src/blitter.com/go/hkexsh +$ make clean all ``` -### To install, uninstall, re-install (xsd server) +### To install, uninstall, re-install ``` $ sudo make [install | uninstall | reinstall] ``` -### To manage service (openrc init) +### To manage service (assuming openrc init) -An example init script (xsd.initrc) is provided. Consult your Linux distribution documentation for proper service/daemon installation. For openrc, +An example init script (hkexshd.initrc) is provided. Consult your Linux distribution documentation for proper service/daemon installation. For openrc, ``` -$ sudo cp xsd.initrc /etc/init.d/xsd -$ sudo rc-config add xsd default +$ sudo cp hkexshd.initrc /etc/init.d/hkexshd +$ sudo rc-config add hkexshd 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, +The make system assumes installation in /usr/local/sbin (hkexshd, hkexpasswd) and /usr/local/bin (hkexsh/hkexcp symlink). ``` -$ sudo cp xsd.sysvrc /etc/init.d/xsd -$ sudo sysv-rc-conf --level 2345 xsd on +$ sudo rc-config [start | restart | stop] hkexshd ``` -The make system assumes installation in /usr/local/sbin (xsd, xspasswd) and /usr/local/bin (xs/xc symlink). +### To set accounts & passwords: ``` -$ sudo rc-config [start | restart | stop] xsd -# .. or sudo /etc/init.d/xsd [start | restart stop] -``` - -### To set accounts & passwords (DEPRECATED: `-s` is now true by default) - -``` -$ sudo touch /etc/xs.passwd -$ sudo xspasswd/xspasswd -u joebloggs +$ sudo touch /etc/hkexsh.passwd +$ sudo hkexpasswd/hkexpasswd -u joebloggs $ ``` @@ -164,44 +131,36 @@ $ In separate shells A and B: ``` -[A]$ cd xsd && sudo ./xsd & # add -d for debugging +[A]$ cd hkexshd && sudo ./hkexshd & # add -d for debugging ``` Interactive shell ``` -[B]$ cd xs && ./xs joebloggs@host-or-ip # add -d for debugging +[B]$ cd hkexsh && ./hkexsh joebloggs@host-or-ip # add -d for debugging ``` One-shot command ``` -[B]$ cd xs && ./xs -x "ls /tmp" joebloggs@host-or-ip +[B]$ cd hkexsh && ./hkexsh -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 (xs) with -d, one will likely need to run 'reset' afterwards +NOTE if running client (hkexsh) 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 xs to request a token from the remote server, which will return a -hostname:token string. Place this string into $HOME/.config/xs/.xs_id to allow logins without -entering a password (obviously, $HOME/.config/xs/.xs_id on both server and client for the user +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 should *not* be world-readable.) -``` -$ xs -g user@host.net >>~/.config/xs/.xs_id -``` -[enter password blindly, authtoken entry will be stored in ~/.config/xs/.xs_id] +### File Copying using hkexcp -NOTE you may need to remove older entries for the same host if this is not the first time you have added -it to your .xs_id file. - -### File Copying using xc - -xc is a symlink to xs, and the binary checks its own filename to determine whether +hkexcp is a symlink to hkexsh, 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. @@ -212,46 +171,28 @@ as expected. Local (client) to remote (server) copy: ``` -$ xc fileA /some/where/fileB /some/where/else/dirC joebloggs@host-or-ip:remoteDir +$ hkexcp fileA /some/where/fileB /some/where/else/dirC joebloggs@host-or-ip:remoteDir ``` Remote (server) to local (client) copy: ``` -$ xc joebloggs@host-or-ip:/remoteDirOrFile /some/where/local/Dir +$ hkexcp 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. +hkexcp 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. -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 <bytes_per_second>). - -Special care should be taken when doing client → server copies: since the tarpipe (should) always succeed at least sending data to the remote side, a destination with no write permission will not return a nonzero status and the client closes its end after sending all data, giving the server no opportunity to send an error code to the client. -It is recommended to test beforehand if the server-side destination is writable (and optionally if the destination already exists, if one does not want to clobber an existing path) by: - -``` -$ xs -x "test -w /dest/path" me@myserver ## If clobbering /dest/path is OK, or -$ xs -x "test -w /dest/path -o ! -e /dest/path" me@myserver ## To prevent clobbering -``` - -Perhaps in future a more complex handshake will be devised to allow the client to half-close the tarpipe, allowing the server to complete its side of the operation and send back its success or failure code, but the current connection protocol does not allow this. If this is a deal-breaking feature, please contact the maintainer. - ### Tunnels -Simple tunnels (client → server, no reverse tunnels for now) are supported. +Simple tunnels (client -> server, no reverse tunnels for now) are supported. -Syntax: xs -T=<tunspec>{,<tunspec>...} +Syntax: hkexsh -T=<tunspec>{,<tunspec>...} .. where <tunspec> is <localport:remoteport> -Example, tunnelling ssh through xs +Example, tunnelling ssh through hkexsh * [server side] ```$ sudo /usr/sbin/sshd -p 7002``` -* [client side, term A] ```$ xs -T=6002:7002 user@server``` +* [client side, term A] ```$ hkexsh -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``` diff --git a/TODO.txt b/TODO.txt index 9a621fe..771a484 100644 --- a/TODO.txt +++ b/TODO.txt @@ -18,7 +18,6 @@ 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 diff --git a/auth.go b/auth.go deleted file mode 100755 index cc3b042..0000000 --- a/auth.go +++ /dev/null @@ -1,235 +0,0 @@ -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" - - "blitter.com/go/xs/xsnet" - "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 { - return false, errors.New("Unsupported platform") - } - 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/.config/xs/.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/%s", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE)) - if e != nil { - log.Printf("INFO: Cannot read %s/%s\n", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE) - return false - } - - r := csv.NewReader(bytes.NewReader(b)) - - r.Comma = ':' - r.Comment = '#' - r.FieldsPerRecord = 3 // connhost:username:authtoken - for { - record, err := r.Read() - if err == io.EOF { - return false - } - if len(record) < 3 || - len(record[0]) < 1 || - len(record[1]) < 1 || - len(record[2]) < 1 { - return false - } - record[0] = strings.TrimSpace(record[0]) - record[1] = strings.TrimSpace(record[1]) - record[2] = strings.TrimSpace(record[2]) - //fmt.Println("auth:", auth, "record:", - // strings.Join([]string{record[0], record[1], record[2]}, ":")) - - if (connhostname == record[0]) && - username == record[1] && - (auth == strings.Join([]string{record[0], record[1], record[2]}, ":")) { - 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 "" -} diff --git a/auth_test.go b/auth_test.go deleted file mode 100644 index bb63ab1..0000000 --- a/auth_test.go +++ /dev/null @@ -1,212 +0,0 @@ -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:johndoe:abcdefg\nhostB:imposter: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:johndoe: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") - } -} diff --git a/bacillus/ci_pushbuild.sh b/bacillus/ci_pushbuild.sh deleted file mode 100755 index 8cfe45f..0000000 --- a/bacillus/ci_pushbuild.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/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 -unset GO111MODULE -#export GOPROXY="direct" -#!# 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 - -go mod init -go mod tidy - -############ -stage "Build" -############ -echo "Invoking 'make clean' ..." -make clean -echo "Invoking 'make all' ..." -make all - -############ -stage "Lint" -############ -make lint - -############ -stage "UnitTests" -############ -go test -v . - -############ -stage "Test(Authtoken)" -############ -if [ -f ~/.config/xs/.xs_id ]; then - echo "Clearing test user $USER .xs_id file ..." - mv ~/.config/xs/.xs_id ~/.config/xs/.xs_id.bak -fi -echo "Setting dummy authtoken in .xs_id ..." -echo "localhost:${USER}:asdfasdfasdf" >~/.config/xs/.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 ~/.config/xs/.xs_id.bak ]; then - echo "Restoring test user $USER .xs_id file ..." - mv ~/.config/xs/.xs_id.bak ~/.config/xs/.xs_id -fi - -############ -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--" diff --git a/consts.go b/consts.go index 5ae9c34..56d54c8 100644 --- a/consts.go +++ b/consts.go @@ -1,10 +1,10 @@ -// Package xs - a secure terminal client/server written from scratch in Go +// Package hkexsh - a secure terminal client/server written from scratch in Go // -// Copyright (c) 2017-2020 Russell Magee +// 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) -package xs +package hkexsh -// common constants for the XS (Xperimental Shell) +// common constants for the HKExSh diff --git a/cptest.sha1sum b/cptest.sha1sum index 1183e23..0440760 100644 --- a/cptest.sha1sum +++ b/cptest.sha1sum @@ -1,6 +1,6 @@ -6010a446cdcf8c1203c2d08998cc69a8c88f77d5 cptest/file16KB -f086ea96f3718efd78e6791178f967585acb3701 cptest/file1KB -6061e16c3d3840712d0b8e5268c49b5c2f8137ac cptest/file32KB -490af32035bbe737480f06439a02d91a171ac407 cptest/file6B -e4a8a4ad9678b7265a28c5f0cb5b078e1049ea23 cptest/subdir/file32MB -82b50550c7d0e0d6ac18d9d0796b6814c3e038bf cptest/subdir/file64MB +306637b5c621892078ebadd9454a78820a000598 cptest/file16KB +1a118dfff291352eb4aec02c34f4f957669460fc cptest/file1KB +f474d5da45890b7cb5b0ae84c8ade5abcb3b4474 cptest/file32KB +03939175ceac92b9c6464d037a0243e22563c423 cptest/file6B +da67c7698b25d94c0cc20284ba9d4008cdee201b cptest/subdir/file32MB +9da9888265371375b48c224b94a0b3132b7ddc41 cptest/subdir/file64MB diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6b9f669 --- /dev/null +++ b/go.mod @@ -0,0 +1,18 @@ +module blitter.com/go/hkexsh + +go 1.12 + +require ( + blitter.com/go/cryptmt v1.0.0 + blitter.com/go/goutmp v1.0.1 + blitter.com/go/herradurakex v1.0.0 + blitter.com/go/mtwist v1.0.0 // indirect + git.schwanenlied.me/yawning/chacha20.git v0.0.0-20170904085104-e3b1f968fc63 // indirect + git.schwanenlied.me/yawning/kyber.git v0.0.0-20180530164001-a270899bd22c + git.schwanenlied.me/yawning/newhope.git v0.0.0-20170622154529-9598792ba8f2 + github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f + github.com/kr/pty v1.1.4 + github.com/mattn/go-isatty v0.0.7 + golang.org/x/crypto v0.0.0-20190417174047-f416ebab96af + golang.org/x/sys v0.0.0-20190416152802-12500544f89f +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..14ee240 --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +blitter.com/go/cryptmt v1.0.0/go.mod h1:tdME2J3O4agaDAYIYNQzzuB28yVGnPSMmV3a/ucSU84= +blitter.com/go/goutmp v1.0.1/go.mod h1:gtlbjC8xGzMk/Cf0BpnVltSa3awOqJ+B5WAxVptTMxk= +blitter.com/go/herradurakex v1.0.0/go.mod h1:m3+vYZX+2dDjdo+n/HDnXEYJX9pwmNeQLgAfJM8mtxw= +blitter.com/go/mtwist v1.0.0/go.mod h1:aU82Nx8+b1v8oZRNqImfEDzDTPim81rY0ACKAIclV18= +git.schwanenlied.me/yawning/chacha20.git v0.0.0-20170904085104-e3b1f968fc63/go.mod h1:NYi4Ifd1g/YbhIDgDfw6t7QdsW4tofQWMX/+FiDtJWs= +git.schwanenlied.me/yawning/kyber.git v0.0.0-20180530164001-a270899bd22c/go.mod h1:QrbgzU5EL/1jaMD5pD4Tiikj3R5elPMa+RMwFUTGwQU= +git.schwanenlied.me/yawning/newhope.git v0.0.0-20170622154529-9598792ba8f2/go.mod h1:weMqACFGzJs4Ni+K9shsRd02N4LkDrtGlkRxISK+II0= +github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f/go.mod h1:u+9Snq0w+ZdYKi8BBoaxnEwWu0fY4Kvu9ByFpM51t1s= +github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +golang.org/x/crypto v0.0.0-20190417174047-f416ebab96af/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190416152802-12500544f89f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/grml.yaml b/grml.yaml index 246eb52..0df2761 100644 --- a/grml.yaml +++ b/grml.yaml @@ -1,5 +1,5 @@ env: - project: xs + project: hkexsh version: 0.8.0 buildDir: build/ docDir: doc/ @@ -38,7 +38,7 @@ commands: app: aliases: [ build ] - help: build the xs tools + help: build the hkexsh tools exec: | make clean make all diff --git a/hkexauth.go b/hkexauth.go new file mode 100644 index 0000000..170a616 --- /dev/null +++ b/hkexauth.go @@ -0,0 +1,130 @@ +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.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() + + 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 +} diff --git a/xsnet/Makefile b/hkexnet/Makefile similarity index 62% rename from xsnet/Makefile rename to hkexnet/Makefile index 150ba25..f645a41 100644 --- a/xsnet/Makefile +++ b/hkexnet/Makefile @@ -1,18 +1,12 @@ .PHONY: info clean lib -ifeq ($(GARBLE),y) -GO = garble -tiny -literals -debugdir=garbled -else -GO = go -endif - all: lib clean: go clean . lib: info - $(GO) install . + go install . ifneq ($(MSYSTEM),) info: diff --git a/xsnet/consts.go b/hkexnet/consts.go similarity index 63% rename from xsnet/consts.go rename to hkexnet/consts.go index 9c58d6e..0916090 100644 --- a/xsnet/consts.go +++ b/hkexnet/consts.go @@ -1,17 +1,16 @@ -// consts.go - consts for xsnet +// consts.go - consts for hkexnet -// Copyright (c) 2017-2020 Russell Magee +// 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) -package xsnet +package hkexnet // 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 +// to hkexnet.Dial() const ( KEX_HERRADURA256 = iota // this MUST be first for default if omitted in ctor KEX_HERRADURA512 @@ -29,31 +28,21 @@ const ( 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 (see xsnet.KEX_HERRADURA256, ...) +// algo shall be used (see hkexnet.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 - CSEPtyGetNameFail // failed to obtain pty name - CSEKEXAlgDenied // server rejected proposed KEX alg - CSECipherAlgDenied // server rejected proposed Cipher alg - CSEHMACAlgDenied // server rejected proposed HMAC alg - CSEConnDead // connection keepalives expired - CSELoginTimeout + 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 ) // Extended (>255 UNIX exit status) codes @@ -77,8 +66,6 @@ const ( CSOTunKeepAlive // client tunnel heartbeat CSOTunDisconn // server -> client: tunnel rport disconnected CSOTunHangup // client -> server: tunnel lport hung up - CSOKeepAlive // bidir keepalive packet to monitor main connection - CSORekey // TODO: rekey/re-select session cipher/hash algs ) // TunEndpoint.tunCtl control values - used to control workers for client @@ -98,8 +85,8 @@ const ( // 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 = 2*1024*1024*1024 - 1 +//TODO: this should be small (max unfragmented packet size?) +const MAX_PAYLOAD_LEN = 4*1024*1024*1024 - 1 // Session symmetric crypto algs const ( @@ -107,8 +94,6 @@ const ( CAlgTwofish128 // golang.org/x/crypto/twofish CAlgBlowfish64 // golang.org/x/crypto/blowfish CAlgCryptMT1 //cryptmt using mtwist64 - CAlgChaCha20_12 - CAlgHopscotch CAlgNoneDisallowed ) @@ -119,27 +104,8 @@ type CSCipherAlg uint32 const ( HmacSHA256 = iota HmacSHA512 - HmacWHIRLPOOL HmacNoneDisallowed ) -// Conn opts outside of basic kex/cipher/hmac connect config -const ( - CONone = iota - CORemodulateShields // if set, rekeying also reselects random cipher/hmac alg -) - -type COValue uint32 - // Available HMACs for hkex.Conn type CSHmacAlg uint32 - -// Some bounds-checking consts -const ( - REKEY_SECS_MIN = 1 - REKEY_SECS_MAX = 28800 // 8 hours - CHAFF_FREQ_MSECS_MIN = 1 - CHAFF_FREQ_MSECS_MAX = 300000 // 5 minutes -) - -const XS_ID_AUTHTOKFILE = ".config/xs/.xs_id" diff --git a/xsnet/chan.go b/hkexnet/hkexchan.go similarity index 72% rename from xsnet/chan.go rename to hkexnet/hkexchan.go index 5847372..8fbf47b 100644 --- a/xsnet/chan.go +++ b/hkexnet/hkexchan.go @@ -1,6 +1,6 @@ -package xsnet +package hkexnet -// Copyright (c) 2017-2020 Russell Magee +// Copyright (c) 2017-2018 Russell Magee // Licensed under the terms of the MIT license (see LICENSE.mit in this // distribution) // @@ -20,13 +20,9 @@ import ( "hash" "log" - "blitter.com/go/cryptmt" - "blitter.com/go/hopscotch" - "blitter.com/go/xs/logger" - "github.com/aead/chacha20/chacha" "golang.org/x/crypto/blowfish" "golang.org/x/crypto/twofish" - whirlpool "github.com/jzelinskie/whirlpool" + "blitter.com/go/cryptmt" // hash algos must be manually imported thusly: // (Would be nice if the golang pkg docs were more clear // on this...) @@ -58,20 +54,10 @@ func expandKeyMat(keymat []byte, blocksize int) []byte { return keymat } -// Choose a cipher and hmac alg from supported sets, given two uint8 values -func getNewStreamAlgs(cb uint8, hb uint8) (config uint32) { - // Get new cipher and hash algs (clamped to valid values) based on - // the input rekeying data - c := (cb % CAlgNoneDisallowed) - h := (hb % HmacNoneDisallowed) - config = uint32(h<<8) | uint32(c) - logger.LogDebug(fmt.Sprintf("[Chose new algs [%d:%d]", h, c)) - return -} - -// (Re-)initialize the keystream and hmac state for an xsnet.Conn, returning -// a cipherStream and hash -func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err error) { +/* Support functionality to set up encryption after a channel has +been negotiated via hkexnet.go +*/ +func (hc Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err error) { var key []byte var block cipher.Block var iv []byte @@ -89,6 +75,7 @@ func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err er 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] @@ -97,6 +84,7 @@ func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err er 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] @@ -114,26 +102,11 @@ func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err er 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.New(nil, nil, keymat) - //NOTE: this alg is not based on block cipher, no IV + rc = cryptmt.NewCipher(keymat) log.Printf("[cipher CRYPTMT1 (%d)]\n", copts) - case CAlgHopscotch: - rc = hopscotch.New(nil, nil, 4, keymat) - //NOTE: this alg is not based on block cipher, no IV - log.Printf("[cipher HOPSCOTCH (%d)]\n", copts) - 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) + break default: log.Printf("[invalid cipher (%d)]\n", copts) fmt.Printf("DOOFUS SET A VALID CIPHER ALG (%d)\n", copts) @@ -150,6 +123,7 @@ func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err er if !halg.Available() { log.Fatal("hash not available!") } + break case HmacSHA512: log.Printf("[hash HmacSHA512 (%d)]\n", hopts) halg := crypto.SHA512 @@ -157,9 +131,7 @@ func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err er if !halg.Available() { log.Fatal("hash not available!") } - case HmacWHIRLPOOL: - log.Printf("[hash HmacWHIRLPOOL (%d)]\n", hopts) - mc = whirlpool.New() + break default: log.Printf("[invalid hmac (%d)]\n", hopts) fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts) @@ -168,7 +140,7 @@ func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err er //os.Exit(1) } - if err == nil && ivlen > 0 { + if err != nil { // Feed the IV into the hmac: all traffic in the connection must // feed its data into the hmac afterwards, so both ends can xor // that with the stream to detect corruption. diff --git a/xsnet/net.go b/hkexnet/hkexnet.go similarity index 62% rename from xsnet/net.go rename to hkexnet/hkexnet.go index 8727437..a085a12 100644 --- a/xsnet/net.go +++ b/hkexnet/hkexnet.go @@ -1,15 +1,15 @@ -// xsnet.go - net.Conn compatible channel setup with encrypted/HMAC +// hkexnet.go - net.Conn compatible channel setup with encrypted/HMAC // negotiation -// Copyright (c) 2017-2020 Russell Magee +// 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) -package xsnet +package hkexnet -// Implementation of key-exchange-wrapped versions of the golang standard +// Implementation of HKEx-wrapped versions of the golang standard // net package interfaces, allowing clients and servers to simply replace // 'net.Dial' and 'net.Listen' with 'hkex.Dial' and 'hkex.Listen' // (though some extra methods are implemented and must be used @@ -25,7 +25,6 @@ package xsnet import ( "bytes" "crypto/cipher" - crand "crypto/rand" "encoding/binary" "encoding/hex" "errors" @@ -39,19 +38,17 @@ import ( "net" "strings" "sync" - "syscall" "time" hkex "blitter.com/go/herradurakex" - "blitter.com/go/kyber" - "blitter.com/go/newhope" - "blitter.com/go/xs/logger" - frodo "github.com/kuking/go-frodokem" + "blitter.com/go/hkexsh/logger" + kyber "git.schwanenlied.me/yawning/kyber.git" + newhope "git.schwanenlied.me/yawning/newhope.git" ) /*---------------------------------------------------------------------*/ const PAD_SZ = 32 // max size of padding applied to each packet -const HMAC_CHK_SZ = 8 // leading bytes of HMAC to xmit for verification +const HMAC_CHK_SZ = 4 // leading bytes of HMAC to xmit for verification type ( WinSize struct { @@ -65,6 +62,7 @@ type ( // see: https://en.wikipedia.org/wiki/chaff_(countermeasure) ChaffConfig struct { shutdown bool //set to inform chaffHelper to shut down + enabled bool msecsMin uint //msecs min interval msecsMax uint //msecs max interval szMax uint // max size in bytes @@ -72,10 +70,9 @@ type ( // Conn is a connection wrapping net.Conn with KEX & session state Conn struct { - kex KEXAlg // KEX/KEM proposal (client -> server) - - m *sync.Mutex // (internal) - c *net.Conn // which also implements io.Reader, io.Writer, ... + kex KEXAlg // KEX/KEM proposal (client -> server) + m *sync.Mutex // (internal) + c *net.Conn // which also implements io.Reader, io.Writer, ... logCipherText bool // somewhat expensive, for debugging logPlainText bool // INSECURE and somewhat expensive, for debugging @@ -87,11 +84,8 @@ type ( Rows uint16 Cols uint16 - keepalive uint // if this reaches zero, conn is considered dead - rekey uint // if nonzero, rekeying interval in seconds - Pproc int // proc ID of command run on this conn - chaff ChaffConfig - tuns *map[uint16](*TunEndpoint) + chaff ChaffConfig + tuns *map[uint16](*TunEndpoint) closeStat *CSOType // close status (CSOExitStatus) r cipher.Stream //read cipherStream @@ -111,82 +105,9 @@ func (t *TunEndpoint) String() string { return fmt.Sprintf("[%d:%s:%d]", t.Lport, t.Peer, t.Rport) } -func (k *KEXAlg) String() string { - switch *k { - case KEX_HERRADURA256: - return "KEX_HERRADURA256" - case KEX_HERRADURA512: - return "KEX_HERRADURA512" - case KEX_HERRADURA1024: - return "KEX_HERRADURA1024" - case KEX_HERRADURA2048: - return "KEX_HERRADURA2048" - case KEX_KYBER512: - return "KEX_KYBER512" - case KEX_KYBER768: - return "KEX_KYBER768" - case KEX_KYBER1024: - return "KEX_KYBER1024" - case KEX_NEWHOPE: - return "KEX_NEWHOPE" - case KEX_NEWHOPE_SIMPLE: - return "KEX_NEWHOPE_SIMPLE" - case KEX_FRODOKEM_1344AES: - return "KEX_FRODOKEM_1344AES" - case KEX_FRODOKEM_1344SHAKE: - return "KEX_FRODOKEM_1344SHAKE" - case KEX_FRODOKEM_976AES: - return "KEX_FRODOKEM_976AES" - case KEX_FRODOKEM_976SHAKE: - return "KEX_FRODOKEM_976SHAKE" - default: - return "KEX_ERR_UNK" - } -} - -func (hc *Conn) CAlg() CSCipherAlg { - return CSCipherAlg(hc.cipheropts & 0x0FF) -} - -func (c *CSCipherAlg) String() string { - switch *c & 0x0FF { - case CAlgAES256: - return "C_AES_256" - case CAlgTwofish128: - return "C_TWOFISH_128" - case CAlgBlowfish64: - return "C_BLOWFISH_64" - case CAlgCryptMT1: - return "C_CRYPTMT1" - case CAlgHopscotch: - return "C_HOPSCOTCH" - case CAlgChaCha20_12: - return "C_CHACHA20_12" - default: - return "C_ERR_UNK" - } -} - -func (hc *Conn) HAlg() CSHmacAlg { - return CSHmacAlg((hc.cipheropts >> 8) & 0x0FF) -} - -func (h *CSHmacAlg) String() string { - switch *h & 0x0FF { - case HmacSHA256: - return "H_SHA256" - case HmacSHA512: - return "H_SHA512" - case HmacWHIRLPOOL: - return "H_WHIRLPOOL" - default: - return "H_ERR_UNK" - } -} - func _initLogging(d bool, c string, f logger.Priority) { if Log == nil { - Log, _ = logger.New(f, fmt.Sprintf("%s:xsnet", c)) + Log, _ = logger.New(f, fmt.Sprintf("%s:hkexnet", c)) } if d { log.SetFlags(0) // syslog will have date,time @@ -208,11 +129,7 @@ func (hc *Conn) Unlock() { hc.m.Unlock() } -func (hc *Conn) KEX() KEXAlg { - return hc.kex -} - -func (hc *Conn) GetStatus() CSOType { +func (hc Conn) GetStatus() CSOType { return *hc.closeStat } @@ -226,7 +143,7 @@ func (hc *Conn) SetStatus(stat CSOType) { // // (Used for protocol-level negotiations after KEx such as // cipher/HMAC algorithm options etc.) -func (hc *Conn) ConnOpts() uint32 { +func (hc Conn) ConnOpts() uint32 { return hc.cipheropts } @@ -243,7 +160,7 @@ func (hc *Conn) SetConnOpts(copts uint32) { // // Consumers of this lib may use this for protocol-level options not part // of the KEx or encryption info used by the connection. -func (hc *Conn) Opts() uint32 { +func (hc Conn) Opts() uint32 { return hc.opts } @@ -258,7 +175,7 @@ func (hc *Conn) SetOpts(opts uint32) { hc.opts = opts } -// Return a new xsnet.Conn +// Return a new hkexnet.Conn // // Note this is internal: use Dial() or Accept() func _new(kexAlg KEXAlg, conn *net.Conn) (hc *Conn, e error) { @@ -272,6 +189,8 @@ func _new(kexAlg KEXAlg, conn *net.Conn) (hc *Conn, e error) { tempMap := make(map[uint16]*TunEndpoint) hc.tuns = &tempMap + *hc.closeStat = CSEStillOpen // open or prematurely-closed status + // Set up KEx/KEM-specifics switch kexAlg { case KEX_HERRADURA256: @@ -291,31 +210,21 @@ func _new(kexAlg KEXAlg, conn *net.Conn) (hc *Conn, e error) { case KEX_NEWHOPE: fallthrough case KEX_NEWHOPE_SIMPLE: - fallthrough - case KEX_FRODOKEM_1344AES: - fallthrough - case KEX_FRODOKEM_1344SHAKE: - fallthrough - case KEX_FRODOKEM_976AES: - fallthrough - case KEX_FRODOKEM_976SHAKE: - //log.Printf("[KEx alg %d is valid]\n", kexAlg) + log.Printf("[KEx alg %d accepted]\n", kexAlg) default: // UNREACHABLE: _getkexalgnum() guarantees a valid KEX value - hc.kex = KEX_HERRADURA512 + hc.kex = KEX_HERRADURA256 log.Printf("[KEx alg %d ?? defaults to %d]\n", kexAlg, hc.kex) } - - //hc.logCipherText = true // !!! DEBUGGING ONLY !!! NEVER DEPLOY this uncommented !!! return } // applyConnExtensions processes optional Dial() negotiation // parameters. See also getkexalgnum(). // -// # Currently defined extension values +// Currently defined extension values // -// # KEx algs +// KEx algs // // KEX_HERRADURA256 KEX_HERRADURA512 KEX_HERRADURA1024 KEX_HERRADURA2048 // @@ -323,11 +232,11 @@ func _new(kexAlg KEXAlg, conn *net.Conn) (hc *Conn, e error) { // // KEX_NEWHOPE KEX_NEWHOPE_SIMPLE // -// # Session (symmetric) crypto +// Session (symmetric) crypto // -// C_AES_256 C_TWOFISH_128 C_BLOWFISH_128 C_CRYPTMT1 C_CHACHA20_12 C_HOPSCOTCH +// C_AES_256 C_TWOFISH_128 C_BLOWFISH_128 C_CRYPTMT1 // -// # Session HMACs +// Session HMACs // // H_SHA256 H_SHA512 func (hc *Conn) applyConnExtensions(extensions ...string) { @@ -349,14 +258,6 @@ func (hc *Conn) applyConnExtensions(extensions ...string) { log.Println("[extension arg = C_CRYPTMT1]") hc.cipheropts &= (0xFFFFFF00) hc.cipheropts |= CAlgCryptMT1 - case "C_HOPSCOTCH": - log.Println("[extension arg = C_HOPSCOTCH]") - hc.cipheropts &= (0xFFFFFF00) - hc.cipheropts |= CAlgHopscotch - case "C_CHACHA20_12": - log.Println("[extension arg = C_CHACHA20_12]") - hc.cipheropts &= (0xFFFFFF00) - hc.cipheropts |= CAlgChaCha20_12 case "H_SHA256": log.Println("[extension arg = H_SHA256]") hc.cipheropts &= (0xFFFF00FF) @@ -365,13 +266,6 @@ func (hc *Conn) applyConnExtensions(extensions ...string) { log.Println("[extension arg = H_SHA512]") hc.cipheropts &= (0xFFFF00FF) hc.cipheropts |= (HmacSHA512 << 8) - case "H_WHIRLPOOL": - log.Println("[extension arg = H_WHIRLPOOL]") - hc.cipheropts &= (0xFFFF00FF) - hc.cipheropts |= (HmacWHIRLPOOL << 8) - case "OPT_REMOD": - log.Println("[extension arg = OPT_REMOD]") - hc.opts |= CORemodulateShields //default: // log.Printf("[Dial ext \"%s\" ignored]\n", s) } @@ -379,7 +273,7 @@ func (hc *Conn) applyConnExtensions(extensions ...string) { } func getkexalgnum(extensions ...string) (k KEXAlg) { - k = KEX_HERRADURA512 // default + k = KEX_HERRADURA256 // default for _, s := range extensions { switch s { case "KEX_HERRADURA256": @@ -409,93 +303,29 @@ func getkexalgnum(extensions ...string) (k KEXAlg) { case "KEX_NEWHOPE_SIMPLE": k = KEX_NEWHOPE_SIMPLE break //out of for - case "KEX_FRODOKEM_1344AES": - k = KEX_FRODOKEM_1344AES - break //out of for - case "KEX_FRODOKEM_1344SHAKE": - k = KEX_FRODOKEM_1344SHAKE - break //out of for - case "KEX_FRODOKEM_976AES": - k = KEX_FRODOKEM_976AES - break //out of for - case "KEX_FRODOKEM_976SHAKE": - k = KEX_FRODOKEM_976SHAKE - break //out of for } } return } -func FrodoKEMDialSetup(c io.ReadWriter, hc *Conn) (err error) { - // Send xsnet.Conn parameters to remote side +// randReader wraps rand.Read() in a struct that implements io.Reader +// for use by the Kyber and NEWHOPE/NEWHOPE_SIMPLE KEM methods. +type randReader struct { +} - // Alice, step 1: Generate a key pair. - var kem frodo.FrodoKEM - - switch hc.kex { - case KEX_FRODOKEM_1344AES: - kem = frodo.Frodo1344AES() - case KEX_FRODOKEM_1344SHAKE: - kem = frodo.Frodo1344SHAKE() - case KEX_FRODOKEM_976AES: - kem = frodo.Frodo976AES() - default: - kem = frodo.Frodo976SHAKE() - } - pubA, secA := kem.Keygen() // pA - - // Alice, step 2: Send the public key (na,ea) to Bob - n, err := fmt.Fprintf(c, "0x%x\n", pubA) - if err != nil { - panic(err) - } - if n < len(pubA) { - panic(errors.New("incomplete Fprintf() of pubA")) - } - - // (... and send cipher, connection opts) - fmt.Fprintf(c, "0x%x:0x%x\n", hc.cipheropts, hc.opts) - - // [Bob does the same and sends use a public key (nb, eb) - pubB_bigint := big.NewInt(0) - _, err = fmt.Fscanf(c, "0x%x\n", pubB_bigint) - pubB := pubB_bigint.Bytes() - - // (... and sends us cipher, connection opts) - _, err = fmt.Fscanf(c, "0x%x:0x%x\n", - &hc.cipheropts, &hc.opts) - if err != nil { - return err - } - - // Alice, step 3: Create ctAtoB, shareA - ctAtoB, shareA, err := kem.Encapsulate(pubB) - if err != nil { - return err - } - - // Alice, step 4: Send ctAtoB to Bob - fmt.Fprintf(c, "0x%x\n", ctAtoB) - - // Alice, step 5: Receive ctBtoA from Bob - ctBtoA_bigint := big.NewInt(0) - _, err = fmt.Fscanf(c, "0x%x\n", ctBtoA_bigint) - ctBtoA := ctBtoA_bigint.Bytes() - - // Alice, step 6: compute Bob's share - shareB, err := kem.Dencapsulate(secA, ctBtoA) - sessionKey := append(shareA, shareB...) - - hc.r, hc.rm, err = hc.getStream(sessionKey) - hc.w, hc.wm, err = hc.getStream(sessionKey) +func (r randReader) Read(b []byte) (n int, e error) { + n, e = rand.Read(b) return } func NewHopeDialSetup(c io.ReadWriter, hc *Conn) (err error) { - // Send xsnet.Conn parameters to remote side + // Send hkexnet.Conn parameters to remote side // Alice, step 1: Generate a key pair. - privKeyAlice, pubKeyAlice, err := newhope.GenerateKeyPairAlice(crand.Reader) + r := new(randReader) + rand.Seed(time.Now().UnixNano()) + + privKeyAlice, pubKeyAlice, err := newhope.GenerateKeyPairAlice(r) if err != nil { panic(err) } @@ -511,7 +341,7 @@ func NewHopeDialSetup(c io.ReadWriter, hc *Conn) (err error) { for i := range pubKeyBob.Send { pubKeyBob.Send[i] = publicKeyBob.Bytes()[i] } - //log.Printf("[Got server pubKey[]:%v]\n", pubKeyBob) + log.Printf("[Got server pubKey[]:%v]\n", pubKeyBob) // Read cipheropts, session opts _, err = fmt.Fscanf(c, "0x%x:0x%x\n", @@ -527,17 +357,19 @@ func NewHopeDialSetup(c io.ReadWriter, hc *Conn) (err error) { if err != nil { panic(err) } - + log.Printf("[Derived sharedSecret:0x%x]\n", aliceSharedSecret) hc.r, hc.rm, err = hc.getStream(aliceSharedSecret) hc.w, hc.wm, err = hc.getStream(aliceSharedSecret) return } func NewHopeSimpleDialSetup(c io.ReadWriter, hc *Conn) (err error) { - // Send xsnet.Conn parameters to remote side + // Send hkexnet.Conn parameters to remote side // Alice, step 1: Generate a key pair. - privKeyAlice, pubKeyAlice, err := newhope.GenerateKeyPairSimpleAlice(crand.Reader) + r := new(randReader) + rand.Seed(time.Now().UnixNano()) + privKeyAlice, pubKeyAlice, err := newhope.GenerateKeyPairSimpleAlice(r) if err != nil { panic(err) } @@ -553,7 +385,7 @@ func NewHopeSimpleDialSetup(c io.ReadWriter, hc *Conn) (err error) { for i := range pubKeyBob.Send { pubKeyBob.Send[i] = publicKeyBob.Bytes()[i] } - //log.Printf("[Got server pubKey[]:%v]\n", pubKeyBob) + log.Printf("[Got server pubKey[]:%v]\n", pubKeyBob) // Read cipheropts, session opts _, err = fmt.Fscanf(c, "0x%x:0x%x\n", @@ -569,27 +401,29 @@ func NewHopeSimpleDialSetup(c io.ReadWriter, hc *Conn) (err error) { if err != nil { panic(err) } - + log.Printf("[Derived sharedSecret:0x%x]\n", aliceSharedSecret) hc.r, hc.rm, err = hc.getStream(aliceSharedSecret) hc.w, hc.wm, err = hc.getStream(aliceSharedSecret) return } func KyberDialSetup(c io.ReadWriter /*net.Conn*/, hc *Conn) (err error) { - // Send xsnet.Conn parameters to remote side + // Send hkexnet.Conn parameters to remote side // Alice, step 1: Generate a key pair. + r := new(randReader) + rand.Seed(time.Now().UnixNano()) var alicePublicKey *kyber.PublicKey var alicePrivateKey *kyber.PrivateKey switch hc.kex { case KEX_KYBER512: - alicePublicKey, alicePrivateKey, err = kyber.Kyber512.GenerateKeyPair(crand.Reader) + alicePublicKey, alicePrivateKey, err = kyber.Kyber512.GenerateKeyPair(r) case KEX_KYBER768: - alicePublicKey, alicePrivateKey, err = kyber.Kyber768.GenerateKeyPair(crand.Reader) + alicePublicKey, alicePrivateKey, err = kyber.Kyber768.GenerateKeyPair(r) case KEX_KYBER1024: - alicePublicKey, alicePrivateKey, err = kyber.Kyber1024.GenerateKeyPair(crand.Reader) + alicePublicKey, alicePrivateKey, err = kyber.Kyber1024.GenerateKeyPair(r) default: - alicePublicKey, alicePrivateKey, err = kyber.Kyber768.GenerateKeyPair(crand.Reader) + alicePublicKey, alicePrivateKey, err = kyber.Kyber768.GenerateKeyPair(r) } if err != nil { @@ -606,7 +440,7 @@ func KyberDialSetup(c io.ReadWriter /*net.Conn*/, hc *Conn) (err error) { //if err != nil { // return err //} - //log.Printf("[Got server pubKeyB[]:%v]\n", pubKeyB) + log.Printf("[Got server pubKeyB[]:%v]\n", pubKeyB) // Read cipheropts, session opts _, err = fmt.Fscanf(c, "0x%x:0x%x\n", @@ -618,6 +452,7 @@ func KyberDialSetup(c io.ReadWriter /*net.Conn*/, hc *Conn) (err error) { // Alice, step 3: Decrypt the KEM cipher text. aliceSharedSecret := alicePrivateKey.KEMDecrypt(pubKeyB) + log.Printf("[Derived sharedSecret:0x%x]\n", aliceSharedSecret) hc.r, hc.rm, err = hc.getStream(aliceSharedSecret) hc.w, hc.wm, err = hc.getStream(aliceSharedSecret) return @@ -638,7 +473,7 @@ func HKExDialSetup(c io.ReadWriter /*net.Conn*/, hc *Conn) (err error) { h = hkex.New(256, 64) } - // Send xsnet.Conn parameters to remote side + // Send hkexnet.Conn parameters to remote side // d is value for Herradura key exchange fmt.Fprintf(c, "0x%s\n0x%x:0x%x\n", h.D().Text(16), hc.cipheropts, hc.opts) @@ -666,74 +501,13 @@ func HKExDialSetup(c io.ReadWriter /*net.Conn*/, hc *Conn) (err error) { return } -func FrodoKEMAcceptSetup(c *net.Conn, hc *Conn) (err error) { - // Bob, step 1: Generate a key pair. - var kem frodo.FrodoKEM - - switch hc.kex { - case KEX_FRODOKEM_1344AES: - kem = frodo.Frodo1344AES() - case KEX_FRODOKEM_1344SHAKE: - kem = frodo.Frodo1344SHAKE() - case KEX_FRODOKEM_976AES: - kem = frodo.Frodo976AES() - default: - kem = frodo.Frodo976SHAKE() - } - pubB, secB := kem.Keygen() - - // [Alice sends use a public key (na, ea) - pubA_bigint := big.NewInt(0) - _, err = fmt.Fscanf(*c, "0x%x\n", pubA_bigint) - pubA := pubA_bigint.Bytes() - - // (... and sends us cipher, connection opts) - _, err = fmt.Fscanf(*c, "0x%x:0x%x\n", - &hc.cipheropts, &hc.opts) - if err != nil { - return err - } - - // Bob, step 2: Send the public key (nb,eb) to Alice - n, err := fmt.Fprintf(*c, "0x%x\n", pubB) - if err != nil { - panic(err) - } - if n < len(pubB) { - panic(errors.New("incomplete Fprintf() of pubB")) - } - - // (... and send cipher, connection opts) - fmt.Fprintf(*c, "0x%x:0x%x\n", hc.cipheropts, hc.opts) - - // Bob, step 3: Create ctBtoA, shareB - ctBtoA, shareB, err := kem.Encapsulate(pubA) - if err != nil { - return err - } - - // Bob, step 4: Send ctBtoA to Alice - fmt.Fprintf(*c, "0x%x\n", ctBtoA) - - // Bob, step 5: Receive ctAtoB from Alice - ctAtoB_bigint := big.NewInt(0) - _, err = fmt.Fscanf(*c, "0x%x\n", ctAtoB_bigint) - ctAtoB := ctAtoB_bigint.Bytes() - - // Alice, step 6: compute Bob's share - shareA, err := kem.Dencapsulate(secB, ctAtoB) - sessionKey := append(shareA, shareB...) - - hc.r, hc.rm, err = hc.getStream(sessionKey) - hc.w, hc.wm, err = hc.getStream(sessionKey) - return -} - func NewHopeAcceptSetup(c *net.Conn, hc *Conn) (err error) { + r := new(randReader) + rand.Seed(time.Now().UnixNano()) // Bob, step 1: Deserialize Alice's public key from the binary encoding. alicePublicKey := big.NewInt(0) _, err = fmt.Fscanln(*c, alicePublicKey) - //log.Printf("[Got client pubKey:0x%x\n]", alicePublicKey) + log.Printf("[Got client pubKey:0x%x\n]", alicePublicKey) if err != nil { return err } @@ -751,7 +525,7 @@ func NewHopeAcceptSetup(c *net.Conn, hc *Conn) (err error) { } // Bob, step 2: Generate the KEM cipher text and shared secret. - pubKeyBob, bobSharedSecret, err := newhope.KeyExchangeBob(crand.Reader, &pubKeyAlice) + pubKeyBob, bobSharedSecret, err := newhope.KeyExchangeBob(r, &pubKeyAlice) if err != nil { panic(err) } @@ -760,16 +534,19 @@ func NewHopeAcceptSetup(c *net.Conn, hc *Conn) (err error) { fmt.Fprintf(*c, "0x%x\n0x%x:0x%x\n", pubKeyBob.Send, hc.cipheropts, hc.opts) + log.Printf("[Derived sharedSecret:0x%x]\n", bobSharedSecret) hc.r, hc.rm, err = hc.getStream(bobSharedSecret) hc.w, hc.wm, err = hc.getStream(bobSharedSecret) return } func NewHopeSimpleAcceptSetup(c *net.Conn, hc *Conn) (err error) { + r := new(randReader) + rand.Seed(time.Now().UnixNano()) // Bob, step 1: Deserialize Alice's public key from the binary encoding. alicePublicKey := big.NewInt(0) _, err = fmt.Fscanln(*c, alicePublicKey) - //log.Printf("[Got client pubKey:0x%x\n]", alicePublicKey) + log.Printf("[Got client pubKey:0x%x\n]", alicePublicKey) if err != nil { return err } @@ -787,7 +564,7 @@ func NewHopeSimpleAcceptSetup(c *net.Conn, hc *Conn) (err error) { } // Bob, step 2: Generate the KEM cipher text and shared secret. - pubKeyBob, bobSharedSecret, err := newhope.KeyExchangeSimpleBob(crand.Reader, &pubKeyAlice) + pubKeyBob, bobSharedSecret, err := newhope.KeyExchangeSimpleBob(r, &pubKeyAlice) if err != nil { panic(err) } @@ -796,6 +573,7 @@ func NewHopeSimpleAcceptSetup(c *net.Conn, hc *Conn) (err error) { fmt.Fprintf(*c, "0x%x\n0x%x:0x%x\n", pubKeyBob.Send, hc.cipheropts, hc.opts) + log.Printf("[Derived sharedSecret:0x%x]\n", bobSharedSecret) hc.r, hc.rm, err = hc.getStream(bobSharedSecret) hc.w, hc.wm, err = hc.getStream(bobSharedSecret) return @@ -805,7 +583,7 @@ func KyberAcceptSetup(c *net.Conn, hc *Conn) (err error) { // Bob, step 1: Deserialize Alice's public key from the binary encoding. alicePublicKey := big.NewInt(0) _, err = fmt.Fscanln(*c, alicePublicKey) - //log.Printf("[Got client pubKey:0x%x\n]", alicePublicKey) + log.Printf("[Got client pubKey:0x%x\n]", alicePublicKey) if err != nil { return err } @@ -833,7 +611,9 @@ func KyberAcceptSetup(c *net.Conn, hc *Conn) (err error) { } // Bob, step 2: Generate the KEM cipher text and shared secret. - cipherText, bobSharedSecret, err := peerPublicKey.KEMEncrypt(crand.Reader) + r := new(randReader) + rand.Seed(time.Now().UnixNano()) + cipherText, bobSharedSecret, err := peerPublicKey.KEMEncrypt(r) if err != nil { panic(err) } @@ -842,6 +622,7 @@ func KyberAcceptSetup(c *net.Conn, hc *Conn) (err error) { fmt.Fprintf(*c, "0x%x\n0x%x:0x%x\n", cipherText, hc.cipheropts, hc.opts) + log.Printf("[Derived sharedSecret:0x%x]\n", bobSharedSecret) hc.r, hc.rm, err = hc.getStream(bobSharedSecret) hc.w, hc.wm, err = hc.getStream(bobSharedSecret) return @@ -862,7 +643,7 @@ func HKExAcceptSetup(c *net.Conn, hc *Conn) (err error) { h = hkex.New(256, 64) } - // Read in xsnet.Conn parameters over raw Conn c + // Read in hkexnet.Conn parameters over raw Conn c // d is value for Herradura key exchange d := big.NewInt(0) _, err = fmt.Fscanln(*c, d) @@ -894,33 +675,26 @@ func HKExAcceptSetup(c *net.Conn, hc *Conn) (err error) { // Dial as net.Dial(), but with implicit key exchange to set up secure // channel on connect // -// Can be called like net.Dial(), defaulting to C_AES_256/H_SHA256, -// or additional extensions can be passed amongst the following: +// Can be called like net.Dial(), defaulting to C_AES_256/H_SHA256, +// or additional extensions can be passed amongst the following: // -// "C_AES_256" | "C_TWOFISH_128" | ... +// "C_AES_256" | "C_TWOFISH_128" | ... // -// "H_SHA256" | "H_SHA512" | ... +// "H_SHA256" | "H_SHA512" | ... // -// See go doc -u xsnet.applyConnExtensions +// See go doc -u hkexnet.applyConnExtensions func Dial(protocol string, ipport string, extensions ...string) (hc Conn, err error) { if Log == nil { Init(false, "client", logger.LOG_DAEMON|logger.LOG_DEBUG) } - var c net.Conn - if protocol == "kcp" { - c, err = kcpDial(ipport, extensions) - if err != nil { - return Conn{}, err - } - } else { - // Open raw Conn c - c, err = net.Dial(protocol, ipport) - if err != nil { - return Conn{}, err - } + // Open raw Conn c + c, err := net.Dial(protocol, ipport) + if err != nil { + return Conn{}, err } - // Init xsnet.Conn hc over net.Conn c + + // Init hkexnet.Conn hc over net.Conn c ret, err := _new(getkexalgnum(extensions...), &c) if err != nil { return Conn{}, err @@ -964,17 +738,6 @@ func Dial(protocol string, ipport string, extensions ...string) (hc Conn, err er if NewHopeSimpleDialSetup(c, &hc) != nil { return Conn{}, nil } - case KEX_FRODOKEM_1344AES: - fallthrough - case KEX_FRODOKEM_1344SHAKE: - fallthrough - case KEX_FRODOKEM_976AES: - fallthrough - case KEX_FRODOKEM_976SHAKE: - log.Printf("[Setting up for KEX_FRODOKEM %d]\n", hc.kex) - if FrodoKEMDialSetup(c, &hc) != nil { - return Conn{}, nil - } default: return Conn{}, err } @@ -983,25 +746,12 @@ func Dial(protocol string, ipport string, extensions ...string) (hc Conn, err er // Close a hkex.Conn func (hc *Conn) Close() (err error) { - hc.ShutdownChaff() + hc.DisableChaff() s := make([]byte, 4) binary.BigEndian.PutUint32(s, uint32(*hc.closeStat)) log.Printf("** Writing closeStat %d at Close()\n", *hc.closeStat) //(*hc.c).SetWriteDeadline(time.Now().Add(500 * time.Millisecond)) hc.WritePacket(s, CSOExitStatus) - - // HACK: Bug #22,#33: Need to wait for rcvr to get final data - // TODO: Find a way to explicitly check if any outgoing data is pending - - //Ensure socket has sent all data to client prior to closing - //NOTE: This is not ideal, as it would be better to somehow - //determine if there is any pending outgoing (write) data to the - //underlying socket (TCP/KCP) prior to closing; however Go's net pkg - //completely hides lower-level stuff. net.Conn.Close() according to - //docs sends written data in the background, so how best to determine - //all data has been sent? -rlm 2022-10-04 - time.Sleep(10 * time.Millisecond) //nolint:gomnd - err = (*hc.c).Close() logger.LogDebug(fmt.Sprintln("[Conn Closing]")) return @@ -1058,32 +808,23 @@ func (hc *Conn) SetReadDeadline(t time.Time) error { // // See go doc net.Listener type HKExListener struct { - l net.Listener - proto string + l net.Listener } // Listen for a connection // // See go doc net.Listen -func Listen(proto string, ipport string, extensions ...string) (hl HKExListener, e error) { +func Listen(protocol string, ipport string) (hl HKExListener, e error) { if Log == nil { Init(false, "server", logger.LOG_DAEMON|logger.LOG_DEBUG) } - var lErr error - var l net.Listener - - if proto == "kcp" { - l, lErr = kcpListen(ipport, extensions) - } else { - l, lErr = net.Listen(proto, ipport) + l, err := net.Listen(protocol, ipport) + if err != nil { + return HKExListener{nil}, err } - if lErr != nil { - return HKExListener{nil, proto}, lErr - } - logger.LogDebug(fmt.Sprintf("[Listening (proto '%s') on %s]\n", proto, ipport)) + logger.LogDebug(fmt.Sprintf("[Listening on %s]\n", ipport)) hl.l = l - hl.proto = proto return } @@ -1096,7 +837,7 @@ func (hl HKExListener) Close() error { return hl.l.Close() } -// Addr returns the listener's network address. +// Addr returns a the listener's network address. // // See go doc net.Listener.Addr func (hl HKExListener) Addr() net.Addr { @@ -1107,22 +848,13 @@ func (hl HKExListener) Addr() net.Addr { // // See go doc net.Listener.Accept func (hl *HKExListener) Accept() (hc Conn, err error) { - var c net.Conn - if hl.proto == "kcp" { - c, err = hl.AcceptKCP() - if err != nil { - return Conn{}, err - } - logger.LogDebug(fmt.Sprintln("[kcp.Listener Accepted]")) - } else { - // Open raw Conn c - c, err = hl.l.Accept() - if err != nil { - return Conn{}, err - } - - logger.LogDebug(fmt.Sprintf("[net.Listener Accepted %v]\n", c.RemoteAddr())) + // Open raw Conn c + c, err := hl.l.Accept() + if err != nil { + return Conn{}, err } + logger.LogDebug(fmt.Sprintln("[net.Listener Accepted]")) + // Read KEx alg proposed by client var kexAlg KEXAlg //! NB. Was using fmt.FScanln() here, but integers with a leading zero @@ -1171,32 +903,11 @@ func (hl *HKExListener) Accept() (hc Conn, err error) { if NewHopeSimpleAcceptSetup(&c, &hc) != nil { return Conn{}, err } - case KEX_FRODOKEM_1344AES: - log.Printf("[Setting up for KEX_FRODOKEM_1344AES %d]\n", hc.kex) - if FrodoKEMAcceptSetup(&c, &hc) != nil { - return Conn{}, err - } - case KEX_FRODOKEM_1344SHAKE: - log.Printf("[Setting up for KEX_FRODOKEM_1344SHAKE %d]\n", hc.kex) - if FrodoKEMAcceptSetup(&c, &hc) != nil { - return Conn{}, err - } - case KEX_FRODOKEM_976AES: - log.Printf("[Setting up for KEX_FRODOKEM_976AES %d]\n", hc.kex) - if FrodoKEMAcceptSetup(&c, &hc) != nil { - return Conn{}, err - } - case KEX_FRODOKEM_976SHAKE: - log.Printf("[Setting up for KEX_FRODOKEM_976SHAKE %d]\n", hc.kex) - if FrodoKEMAcceptSetup(&c, &hc) != nil { - return Conn{}, err - } default: return Conn{}, err } - log.Println("[hc.Accept successful]") - return hc, err + return } /*---------------------------------------------------------------------*/ @@ -1208,7 +919,7 @@ func (hl *HKExListener) Accept() (hc Conn, err error) { // packet processing. // // See go doc io.Reader -func (hc *Conn) Read(b []byte) (n int, err error) { +func (hc Conn) Read(b []byte) (n int, err error) { for { if hc.dBuf.Len() > 0 { break @@ -1218,7 +929,6 @@ func (hc *Conn) Read(b []byte) (n int, err error) { var hmacIn [HMAC_CHK_SZ]uint8 var payloadLen uint32 - //------------- Read ctrl/status opcode -------------------- // Read ctrl/status opcode (CSOHmacInvalid on hmac mismatch) err = binary.Read(*hc.c, binary.BigEndian, &ctrlStatOp) if err != nil { @@ -1226,8 +936,7 @@ func (hc *Conn) Read(b []byte) (n int, err error) { return 0, io.EOF } if strings.HasSuffix(err.Error(), "use of closed network connection") { - logger.LogDebug(fmt.Sprintln("[Client hung up(1)]")) - //!rlm hc.SetStatus(CSENone) //FIXME: re-examine this (exit 9 w/o it - 2023-11-05) + logger.LogDebug(fmt.Sprintln("[Client hung up]")) return 0, io.EOF } etxt := fmt.Sprintf("** Failed read:%s (%s) **", "ctrlStatOp", err) @@ -1240,9 +949,7 @@ func (hc *Conn) Read(b []byte) (n int, err error) { hc.Close() return 0, errors.New("** ALERT - remote end detected HMAC mismatch - possible channel tampering **") } - //---------------------------------------------------------- - //------------------ Read HMAC len ------------------------ // Read the hmac and payload len first err = binary.Read(*hc.c, binary.BigEndian, &hmacIn) if err != nil { @@ -1250,30 +957,27 @@ func (hc *Conn) Read(b []byte) (n int, err error) { return 0, io.EOF } if strings.HasSuffix(err.Error(), "use of closed network connection") { - logger.LogDebug(fmt.Sprintln("[Client hung up(2)]")) + logger.LogDebug(fmt.Sprintln("[Client hung up]")) return 0, io.EOF } etxt := fmt.Sprintf("** Failed read:%s (%s) **", "HMAC", err) logger.LogDebug(etxt) return 0, errors.New(etxt) } - //---------------------------------------------------------- - //------------------ Read Payload len --------------------- err = binary.Read(*hc.c, binary.BigEndian, &payloadLen) if err != nil { if err.Error() == "EOF" { return 0, io.EOF } if strings.HasSuffix(err.Error(), "use of closed network connection") { - logger.LogDebug(fmt.Sprintln("[Client hung up(3)]")) + logger.LogDebug(fmt.Sprintln("[Client hung up]")) return 0, io.EOF } etxt := fmt.Sprintf("** Failed read:%s (%s) **", "payloadLen", err) logger.LogDebug(etxt) return 0, errors.New(etxt) } - //---------------------------------------------------------- if payloadLen > MAX_PAYLOAD_LEN { logger.LogDebug(fmt.Sprintf("[Insane payloadLen:%v]\n", payloadLen)) @@ -1281,7 +985,6 @@ func (hc *Conn) Read(b []byte) (n int, err error) { return 1, errors.New("Insane payloadLen") } - //-------------------- Read Payload ------------------------ var payloadBytes = make([]byte, payloadLen) n, err = io.ReadFull(*hc.c, payloadBytes) if err != nil { @@ -1289,33 +992,18 @@ func (hc *Conn) Read(b []byte) (n int, err error) { return 0, io.EOF } if strings.HasSuffix(err.Error(), "use of closed network connection") { - logger.LogDebug(fmt.Sprintln("[Client hung up(4)]")) + logger.LogDebug(fmt.Sprintln("[Client hung up]")) return 0, io.EOF } etxt := fmt.Sprintf("** Failed read:%s (%s) **", "payloadBytes", err) logger.LogDebug(etxt) return 0, errors.New(etxt) } - //---------------------------------------------------------- if hc.logCipherText { log.Printf(" <:ctext:\r\n%s\r\n", hex.Dump(payloadBytes[:n])) } - //fmt.Printf(" <:ctext:\r\n%s\r\n", hex.Dump(payloadBytes[:n])) - //---------------- Verify Payload via HMAC ----------------- - hc.rm.Write(payloadBytes) // Calc hmac on received data - hTmp := hc.rm.Sum(nil)[0:HMAC_CHK_SZ] - //log.Printf("<%04x) HMAC:(i)%s (c)%02x\r\n", decryptN, hex.EncodeToString([]byte(hmacIn[0:])), hTmp) - - // Log alert if hmac didn't match, corrupted channel - if !bytes.Equal(hTmp, []byte(hmacIn[0:])) /*|| hmacIn[0] > 0xf8*/ { - logger.LogDebug(fmt.Sprintln("** ALERT - detected HMAC mismatch, possible channel tampering **")) - _, _ = (*hc.c).Write([]byte{CSOHmacInvalid}) - } - //---------------------------------------------------------- - - //------------------- Decrypt Payload ---------------------- db := bytes.NewBuffer(payloadBytes[:n]) //copying payloadBytes to db // The StreamReader acts like a pipe, decrypting // whatever is available and forwarding the result @@ -1324,16 +1012,14 @@ func (hc *Conn) Read(b []byte) (n int, err error) { // The caller isn't necessarily reading the full payload so we need // to decrypt to an intermediate buffer, draining it on demand of caller decryptN, err := rs.Read(payloadBytes) - //---------------------------------------------------------- - if hc.logPlainText { - log.Printf(" <:ptext:\r\n%s\r\n", hex.Dump(payloadBytes[:n])) + log.Printf(" <-ptext:\r\n%s\r\n", hex.Dump(payloadBytes[:n])) } if err != nil { - log.Println("xsnet.Read():", err) + log.Println("hkexnet.Read():", err) //panic(err) } else { - //------------ Discard Padding --------------------- + hc.rm.Write(payloadBytes) // Calc hmac on received data // Padding: Read padSide, padLen, (padding | d) or (d | padding) padSide := payloadBytes[0] padLen := payloadBytes[1] @@ -1344,33 +1030,18 @@ func (hc *Conn) Read(b []byte) (n int, err error) { } else { payloadBytes = payloadBytes[0 : len(payloadBytes)-int(padLen)] } - //-------------------------------------------------- - switch ctrlStatOp { - case CSOChaff: - // Throw away pkt if it's chaff (ie., caller to Read() won't see this data) + //fmt.Printf("padSide:%d padLen:%d payloadBytes:%s\n", + // padSide, padLen, hex.Dump(payloadBytes)) + + // Throw away pkt if it's chaff (ie., caller to Read() won't see this data) + if ctrlStatOp == CSOChaff { log.Printf("[Chaff pkt, discarded (len %d)]\n", decryptN) - case CSOKeepAlive: - //logger.LogDebug(fmt.Sprintf("[got keepAlive pkt, discarded (len %d)]\n", decryptN)) - // payload of keepalive (2 bytes) is not currently used (0x55aa fixed) - _ = binary.BigEndian.Uint16(payloadBytes[0:2]) - hc.ResetKeepAlive() - case CSORekey: - // rekey - //logger.LogDebug(fmt.Sprintf("[Got rekey [%02x %02x %02x ...]\n", - // payloadBytes[0], payloadBytes[1], payloadBytes[2])) - rekeyData := payloadBytes - if (hc.opts & CORemodulateShields) != 0 { - hc.Lock() - hc.cipheropts = getNewStreamAlgs(rekeyData[0], rekeyData[1]) - hc.Unlock() - } - hc.r, hc.rm, err = hc.getStream(rekeyData) - case CSOTermSize: + } else if ctrlStatOp == CSOTermSize { fmt.Sscanf(string(payloadBytes), "%d %d", &hc.Rows, &hc.Cols) log.Printf("[TermSize pkt: rows %v cols %v]\n", hc.Rows, hc.Cols) hc.WinCh <- WinSize{hc.Rows, hc.Cols} - case CSOExitStatus: + } else if ctrlStatOp == CSOExitStatus { if len(payloadBytes) > 0 { hc.SetStatus(CSOType(binary.BigEndian.Uint32(payloadBytes))) } else { @@ -1378,7 +1049,7 @@ func (hc *Conn) Read(b []byte) (n int, err error) { hc.SetStatus(CSETruncCSO) } hc.Close() - case CSOTunSetup: + } else if ctrlStatOp == CSOTunSetup { // server side tunnel setup in response to client lport := binary.BigEndian.Uint16(payloadBytes[0:2]) rport := binary.BigEndian.Uint16(payloadBytes[2:4]) @@ -1390,7 +1061,7 @@ func (hc *Conn) Read(b []byte) (n int, err error) { logger.LogDebug(fmt.Sprintf("[Server] Got CSOTunSetup [%d:%d]", lport, rport)) } (*hc.tuns)[rport].Ctl <- 'd' // Dial() rport - case CSOTunSetupAck: + } else if ctrlStatOp == CSOTunSetupAck { lport := binary.BigEndian.Uint16(payloadBytes[0:2]) rport := binary.BigEndian.Uint16(payloadBytes[2:4]) if _, ok := (*hc.tuns)[rport]; !ok { @@ -1401,7 +1072,7 @@ func (hc *Conn) Read(b []byte) (n int, err error) { logger.LogDebug(fmt.Sprintf("[Client] Got CSOTunSetupAck [%d:%d]", lport, rport)) } (*hc.tuns)[rport].Ctl <- 'a' // Listen() for lport connection - case CSOTunRefused: + } else if ctrlStatOp == CSOTunRefused { // client side receiving CSOTunRefused means the remote side // could not dial() rport. So we cannot yet listen() // for client-side on lport. @@ -1413,7 +1084,7 @@ func (hc *Conn) Read(b []byte) (n int, err error) { } else { logger.LogDebug(fmt.Sprintf("[Client] CSOTunRefused on already-closed tun [%d:%d]", lport, rport)) } - case CSOTunDisconn: + } else if ctrlStatOp == CSOTunDisconn { // server side's rport has disconnected (server lost) lport := binary.BigEndian.Uint16(payloadBytes[0:2]) rport := binary.BigEndian.Uint16(payloadBytes[2:4]) @@ -1423,7 +1094,7 @@ func (hc *Conn) Read(b []byte) (n int, err error) { } else { logger.LogDebug(fmt.Sprintf("[Client] CSOTunDisconn on already-closed tun [%d:%d]", lport, rport)) } - case CSOTunHangup: + } else if ctrlStatOp == CSOTunHangup { // client side's lport has hung up lport := binary.BigEndian.Uint16(payloadBytes[0:2]) rport := binary.BigEndian.Uint16(payloadBytes[2:4]) @@ -1433,7 +1104,7 @@ func (hc *Conn) Read(b []byte) (n int, err error) { } else { logger.LogDebug(fmt.Sprintf("[Server] CSOTunHangup to already-closed tun [%d:%d]", lport, rport)) } - case CSOTunData: + } else if ctrlStatOp == CSOTunData { lport := binary.BigEndian.Uint16(payloadBytes[0:2]) rport := binary.BigEndian.Uint16(payloadBytes[2:4]) //fmt.Printf("[Got CSOTunData: [lport %d:rport %d] data:%v\n", lport, rport, payloadBytes[4:]) @@ -1446,25 +1117,35 @@ func (hc *Conn) Read(b []byte) (n int, err error) { } else { logger.LogDebug(fmt.Sprintf("[Attempt to write data to closed tun [%d:%d]", lport, rport)) } - case CSOTunKeepAlive: + } else if ctrlStatOp == CSOTunKeepAlive { // client side has sent keepalive for tunnels -- if client // dies or exits unexpectedly the absence of this will // let the server know to hang up on Dial()ed server rports. _ = binary.BigEndian.Uint16(payloadBytes[0:2]) //logger.LogDebug(fmt.Sprintf("[Server] Got CSOTunKeepAlive")) - // though CSOTunKeepAlive sends an endp (uint16), we don't use it, - // preferring to refresh *all* tunnels on the message. - // (?rlm 2023-11-04 -- TODO: verify this, it's been a while.) for _, t := range *hc.tuns { hc.Lock() t.KeepAlive = 0 hc.Unlock() } - case CSONone: + } else if ctrlStatOp == CSONone { hc.dBuf.Write(payloadBytes) - default: + } else { logger.LogDebug(fmt.Sprintf("[Unknown CSOType:%d]", ctrlStatOp)) } + + hTmp := hc.rm.Sum(nil)[0:HMAC_CHK_SZ] + log.Printf("<%04x) HMAC:(i)%s (c)%02x\r\n", decryptN, hex.EncodeToString([]byte(hmacIn[0:])), hTmp) + + if *hc.closeStat == CSETruncCSO { + logger.LogDebug(fmt.Sprintln("[cannot verify HMAC]")) + } else { + // Log alert if hmac didn't match, corrupted channel + if !bytes.Equal(hTmp, []byte(hmacIn[0:])) /*|| hmacIn[0] > 0xf8*/ { + logger.LogDebug(fmt.Sprintln("** ALERT - detected HMAC mismatch, possible channel tampering **")) + _, _ = (*hc.c).Write([]byte{CSOHmacInvalid}) + } + } } } @@ -1474,7 +1155,6 @@ func (hc *Conn) Read(b []byte) (n int, err error) { } log.Printf("Read() got %d bytes\n", retN) - copy(b, hc.dBuf.Next(retN)) return retN, nil } @@ -1482,18 +1162,15 @@ func (hc *Conn) Read(b []byte) (n int, err error) { // Write a byte slice // // See go doc io.Writer -func (hc *Conn) Write(b []byte) (n int, err error) { - //logger.LogDebug("[+Write]") +func (hc Conn) Write(b []byte) (n int, err error) { + //fmt.Printf("WRITE(%d)\n", len(b)) n, err = hc.WritePacket(b, CSONone) - //logger.LogDebug("[-Write]") + //fmt.Printf("WROTE(%d)\n", n) return n, err } // Write a byte slice with specified ctrlStatOp byte func (hc *Conn) WritePacket(b []byte, ctrlStatOp byte) (n int, err error) { - hc.Lock() - defer hc.Unlock() - //log.Printf("[Encrypting...]\r\n") var hmacOut []uint8 var payloadLen uint32 @@ -1503,63 +1180,60 @@ func (hc *Conn) WritePacket(b []byte, ctrlStatOp byte) (n int, err error) { } //Padding prior to encryption - padSz := rand.Intn(PAD_SZ-1) + 1 /*(rand.Intn(PAD_SZ) / 2) + (PAD_SZ / 2)*/ + padSz := (rand.Intn(PAD_SZ) / 2) + (PAD_SZ / 2) padLen := padSz - ((len(b) + padSz) % padSz) if padLen == padSz { // No padding required padLen = 0 } - padBytes := make([]byte, padLen) rand.Read(padBytes) // For a little more confusion let's support padding either before // or after the payload. padSide := rand.Intn(2) + //fmt.Printf("--\n") + //fmt.Printf("PRE_PADDING:%s\r\n", hex.Dump(b)) + //fmt.Printf("padSide:%d padLen:%d\r\n", padSide, padLen) if padSide == 0 { b = append([]byte{byte(padSide)}, append([]byte{byte(padLen)}, append(padBytes, b...)...)...) } else { b = append([]byte{byte(padSide)}, append([]byte{byte(padLen)}, append(b, padBytes...)...)...) } + //fmt.Printf("POST_PADDING:%s\r\n", hex.Dump(b)) + //fmt.Printf("--\r\n") + // N.B. Originally this Lock() surrounded only the + // calls to binary.Write(hc.c ..) however there appears + // to be some other unshareable state in the Conn + // struct that must be protected to serialize main and + // chaff data written to it. + // + // Would be nice to determine if the mutex scope + // could be tightened. + hc.Lock() payloadLen = uint32(len(b)) + //!fmt.Printf(" --== payloadLen:%d\n", payloadLen) if hc.logPlainText { - log.Printf(" >:ptext:\r\n%s\r\n", hex.Dump(b[0:payloadLen])) + log.Printf(" :>ptext:\r\n%s\r\n", hex.Dump(b[0:payloadLen])) } - // NOTE releases prior to v0.9 used Authenticate-then-Encrypt, - // which in block modes is insecure; however - // 1) we use exclusively XOR-stream modes with random padding, - // 2) are padding randomly either before or after the real payload, and - // 3) the padding side indicator value itself is part of the ciphertext - // ... thus are not subject to oracle attacks of the type used on SSL - // (described in (Krawczyk 2001/2014, - // https://link.springer.com/content/pdf/10.1007%2F3-540-44647-8_19.pdf) - // - // Nevertheless, to address any future concerns v0.9 onwards switches to - // Encrypt-then-Auth and breaks interop with earlier versions. - // -rlm 2020-12-15 + // Calculate hmac on payload + hc.wm.Write(b[0:payloadLen]) + hmacOut = hc.wm.Sum(nil)[0:HMAC_CHK_SZ] + + log.Printf(" (%04x> HMAC(o):%s\r\n", payloadLen, hex.EncodeToString(hmacOut)) var wb bytes.Buffer // The StreamWriter acts like a pipe, forwarding whatever is // written to it through the cipher, encrypting as it goes ws := &cipher.StreamWriter{S: hc.w, W: &wb} - wN, err := ws.Write(b[0:payloadLen]) + _, err = ws.Write(b[0:payloadLen]) if err != nil { panic(err) } - if wN < int(payloadLen) { - panic("truncated Write to cipher *****") - } - if hc.logCipherText { - log.Printf(" >:ctext:\r\n%s\r\n", hex.Dump(wb.Bytes())) + log.Printf(" ->ctext:\r\n%s\r\n", hex.Dump(wb.Bytes())) } - //fmt.Printf(" >:ctext:\r\n%s\r\n", hex.Dump(wb.Bytes())) - - // Calculate hmac on cipher payload - hc.wm.Write(wb.Bytes()) - hmacOut = hc.wm.Sum(nil)[0:HMAC_CHK_SZ] //finalize - //log.Printf(" (%08x> HMAC(o):%s\r\n", payloadLen, hex.EncodeToString(hmacOut)) err = binary.Write(*hc.c, binary.BigEndian, &ctrlStatOp) if err == nil { @@ -1578,6 +1252,7 @@ func (hc *Conn) WritePacket(b []byte, ctrlStatOp byte) (n int, err error) { } else { //fmt.Println("[a]WriteError!") } + hc.Unlock() if err != nil { log.Println(err) @@ -1585,187 +1260,56 @@ func (hc *Conn) WritePacket(b []byte, ctrlStatOp byte) (n int, err error) { // We must 'lie' to caller indicating the length of THEIR // data written (ie., not including the padding and padding headers) - retN := n - 2 - int(padLen) - if retN <= 0 { - retN = 0 - } - return retN, err + return n - 2 - int(padLen), err } -func (hc *Conn) StartupChaff() { +func (hc *Conn) EnableChaff() { hc.chaff.shutdown = false + hc.chaff.enabled = true log.Println("Chaffing ENABLED") hc.chaffHelper() } +func (hc *Conn) DisableChaff() { + hc.chaff.enabled = false + log.Println("Chaffing DISABLED") +} + func (hc *Conn) ShutdownChaff() { - hc.Lock() hc.chaff.shutdown = true - hc.Unlock() log.Println("Chaffing SHUTDOWN") } func (hc *Conn) SetupChaff(msecsMin uint, msecsMax uint, szMax uint) { - // Enforce bounds on chaff frequency and pkt size - hc.Lock() - if hc.chaff.msecsMin < CHAFF_FREQ_MSECS_MIN { - hc.chaff.msecsMin = CHAFF_FREQ_MSECS_MIN - } - if hc.chaff.msecsMax > CHAFF_FREQ_MSECS_MAX { - hc.chaff.msecsMax = CHAFF_FREQ_MSECS_MAX - } - hc.Unlock() - hc.chaff.msecsMin = msecsMin //move these to params of chaffHelper() ? hc.chaff.msecsMax = msecsMax hc.chaff.szMax = szMax } -func (hc *Conn) ShutdownRekey() { - hc.Lock() - hc.rekey = 0 - hc.Unlock() -} - -func (hc *Conn) RekeyHelper(intervalSecs uint) { - if intervalSecs < REKEY_SECS_MIN { - intervalSecs = REKEY_SECS_MIN - } - if intervalSecs > REKEY_SECS_MAX { - intervalSecs = REKEY_SECS_MAX - } - - go func() { - hc.Lock() - hc.rekey = intervalSecs - hc.Unlock() - - for { - hc.Lock() - rekey := hc.rekey - hc.Unlock() - - if rekey != 0 { - jitter := rand.Intn(int(rekey)) / 4 - rekey = rekey - uint(jitter) - if rekey < 1 { - rekey = 1 - } - - //logger.LogDebug(fmt.Sprintf("[rekeyHelper Loop]\n")) - time.Sleep(time.Duration(rekey) * time.Second) - - // Send rekey to other end - rekeyData := make([]byte, 64) - _, err := crand.Read(rekeyData) - //logger.LogDebug(fmt.Sprintf("[rekey [%02x %02x %02x ...]\n", - // rekeyData[0], rekeyData[1], rekeyData[2])) - //logger.LogDebug("[+rekeyHelper]") - _, err = hc.WritePacket(rekeyData, CSORekey) - hc.Lock() - if (hc.opts & CORemodulateShields) != 0 { - hc.cipheropts = getNewStreamAlgs(rekeyData[0], rekeyData[1]) - } - hc.w, hc.wm, err = hc.getStream(rekeyData) - //logger.LogDebug("[-rekeyHelper]") - hc.Unlock() - if err != nil { - log.Printf("[rekey WritePacket err! (%v) rekey dying ...]\n", err) - return - } - } else { - return - } - } - }() -} - // Helper routine to spawn a chaffing goroutine for each Conn func (hc *Conn) chaffHelper() { go func() { - var nextDuration int for { - //logger.LogDebug(fmt.Sprintf("[chaffHelper Loop]\n")) - hc.Lock() - shutdown := hc.chaff.shutdown - hc.Unlock() - if !shutdown { + var nextDuration int + if hc.chaff.enabled { var bufTmp []byte bufTmp = make([]byte, rand.Intn(int(hc.chaff.szMax))) min := int(hc.chaff.msecsMin) nextDuration = rand.Intn(int(hc.chaff.msecsMax)-min) + min _, _ = rand.Read(bufTmp) - //logger.LogDebug("[+chaffHelper]") _, err := hc.WritePacket(bufTmp, CSOChaff) - //logger.LogDebug("[-chaffHelper]") if err != nil { - log.Println("[ *** error - chaffHelper shutting down *** ]") - hc.Lock() - hc.chaff.shutdown = true - hc.Unlock() + log.Println("[ *** error - chaffHelper quitting *** ]") + hc.chaff.enabled = false break } - } else { - log.Println("[ *** chaffHelper shutting down *** ]") - break } time.Sleep(time.Duration(nextDuration) * time.Millisecond) - } - }() -} - -func (hc *Conn) StartupKeepAlive() { - hc.ResetKeepAlive() - log.Println("KeepAlive ENABLED") - hc.keepaliveHelper() -} - -func (hc *Conn) ShutdownKeepAlive() { - log.Println("Conn SHUTDOWN") - hc.Close() -} - -func (hc *Conn) ResetKeepAlive() { - hc.Lock() - hc.keepalive = 3 - hc.Unlock() - log.Println("KeepAlive RESET") -} - -// Helper routine to spawn a keepalive goroutine for each Conn -func (hc *Conn) keepaliveHelper() { - go func() { - for { - nextDuration := 10000 - bufTmp := []byte{0x55, 0xaa} - //logger.LogDebug("[+keepaliveHelper]") - _, err := hc.WritePacket(bufTmp, CSOKeepAlive) - //logger.LogDebug("[-keepaliveHelper]") - //logger.LogDebug(fmt.Sprintf("[keepalive]\n")) - if err != nil { - logger.LogDebug(fmt.Sprintf("[ *** error - keepaliveHelper quitting *** ]\n")) - break - } - time.Sleep(time.Duration(nextDuration) * time.Millisecond) - hc.Lock() - hc.keepalive -= 1 - hc.Unlock() - //logger.LogDebug(fmt.Sprintf("[keepAlive is now %d]\n", hc.keepalive)) - - //if rand.Intn(8) == 0 { - // hc.keepalive = 0 - //} - - if hc.keepalive == 0 { - logger.LogDebug(fmt.Sprintf("*** keepaliveHelper shutting down\n")) - hc.SetStatus(CSEConnDead) - hc.ShutdownKeepAlive() - if hc.Pproc != 0 { - //fmt.Printf("[pid %d needs to be killed]\n", hc.Pproc) - syscall.Kill(hc.Pproc, syscall.SIGABRT) //nolint:errcheck - } - break - } + if hc.chaff.shutdown { + log.Println("*** chaffHelper shutting down") + break + } + } }() } diff --git a/xsnet/tun.go b/hkexnet/hkextun.go similarity index 98% rename from xsnet/tun.go rename to hkexnet/hkextun.go index ebddcae..2709aa2 100644 --- a/xsnet/tun.go +++ b/hkexnet/hkextun.go @@ -1,12 +1,12 @@ -// hkextun.go - Tunnel setup using an established xsnet.Conn +// hkextun.go - Tunnel setup using an established hkexnet.Conn -// Copyright (c) 2017-2020 Russell Magee +// 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) -package xsnet +package hkexnet import ( "bytes" @@ -18,7 +18,7 @@ import ( "sync" "time" - "blitter.com/go/xs/logger" + "blitter.com/go/hkexsh/logger" ) type ( @@ -37,8 +37,6 @@ type ( // client starts worker to receive/send data using lport // ... client disconnects: sends remhost [CSOTunClose:rport] // ... or server disconnects: sends client [CSOTunClose:lport] - // ... or server disconnects: due to client failing to send TunKeepAlive - // events for too long // server at any time sends [CSOTunRefused:rport] if daemon died // -- diff --git a/xspasswd/Makefile b/hkexpasswd/Makefile similarity index 50% rename from xspasswd/Makefile rename to hkexpasswd/Makefile index 8e2b657..0e3475f 100644 --- a/xspasswd/Makefile +++ b/hkexpasswd/Makefile @@ -1,22 +1,16 @@ .PHONY: clean all vis lint -ifeq ($(GARBLE),y) -GO = garble -tiny -literals -debugdir=garbled -else -GO = go -endif - 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 $(BUILDOPTS) . + go build $(BUILDOPTS) . clean: $(RM) $(EXE) $(EXE).exe vis: - go-callvis -format png -file xspasswd-vis -ignore $(EXTPKGS) -group pkg,type . + go-callvis -format png -file hkexpasswd-vis -ignore $(EXTPKGS) -group pkg,type . lint: - -golangci-lint run + -gometalinter --deadline=60s | sort diff --git a/hkexpasswd/hkexpasswd-vis.gv b/hkexpasswd/hkexpasswd-vis.gv new file mode 100755 index 0000000..380724f --- /dev/null +++ b/hkexpasswd/hkexpasswd-vis.gv @@ -0,0 +1,63 @@ +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" { + label="main"; +labelloc="t"; +labeljust="c"; +fontsize="18"; +bgcolor="#e6ecfa"; + + "blitter.com/go/hkexsh/hkexpasswd.main" [ fillcolor="lightblue" label="main" penwidth="0.5" ] + + subgraph "cluster_blitter.com/go/hkexsh" { + label="[hkexsh]"; +penwidth="0.8"; +style="filled"; +rank="sink"; +URL="/?f=blitter.com/go/hkexsh"; +tooltip="package: blitter.com/go/hkexsh"; +fontsize="16"; +fillcolor="lightyellow"; +fontname="bold"; + + "blitter.com/go/hkexsh.ReadPassword" [ fillcolor="moccasin" label="ReadPassword" penwidth="1.5" ] + + } + + subgraph "cluster_github.com/jameskeane/bcrypt" { + fontsize="16"; +rank="sink"; +label="[bcrypt]"; +URL="/?f=github.com/jameskeane/bcrypt"; +tooltip="package: github.com/jameskeane/bcrypt"; +penwidth="0.8"; +style="filled"; +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" ] +} diff --git a/xspasswd/xspasswd-vis.png b/hkexpasswd/hkexpasswd-vis.png similarity index 54% rename from xspasswd/xspasswd-vis.png rename to hkexpasswd/hkexpasswd-vis.png index 46d8088..d33146a 100644 Binary files a/xspasswd/xspasswd-vis.png and b/hkexpasswd/hkexpasswd-vis.png differ diff --git a/xspasswd/xspasswd.go b/hkexpasswd/hkexpasswd.go similarity index 89% rename from xspasswd/xspasswd.go rename to hkexpasswd/hkexpasswd.go index ff18083..f02028c 100644 --- a/xspasswd/xspasswd.go +++ b/hkexpasswd/hkexpasswd.go @@ -1,7 +1,7 @@ // Util to generate/store passwords for users in a file akin to /etc/passwd -// suitable for the xs server, using bcrypt. +// suitable for the demo hkexsh server, using bcrypt. // -// Copyright (c) 2017-2020 Russell Magee +// Copyright (c) 2017-2018 Russell Magee // Licensed under the terms of the MIT license (see LICENSE.mit in this // distribution) // @@ -17,7 +17,7 @@ import ( "log" "os" - xs "blitter.com/go/xs" + hkexsh "blitter.com/go/hkexsh" "github.com/jameskeane/bcrypt" ) @@ -36,7 +36,7 @@ func main() { flag.BoolVar(&vopt, "v", false, "show version") flag.StringVar(&userName, "u", "", "username") - flag.StringVar(&pfName, "f", "/etc/xs.passwd", "passwd file") + flag.StringVar(&pfName, "f", "/etc/hkexsh.passwd", "passwd file") flag.Parse() if vopt { @@ -59,7 +59,7 @@ func main() { uname = userName fmt.Printf("New Password:") - ab, err := xs.ReadPassword(os.Stdin.Fd()) + ab, err := hkexsh.ReadPassword(int(os.Stdin.Fd())) fmt.Printf("\r\n") if err != nil { log.Fatal(err) @@ -68,7 +68,7 @@ func main() { newpw = string(ab) fmt.Printf("Confirm:") - ab, err = xs.ReadPassword(os.Stdin.Fd()) + ab, err = hkexsh.ReadPassword(int(os.Stdin.Fd())) fmt.Printf("\r\n") if err != nil { log.Fatal(err) @@ -130,7 +130,7 @@ func main() { records = append(records, newRec) } - outFile, err := ioutil.TempFile("", "xs-passwd") + outFile, err := ioutil.TempFile("", "hkexsh-passwd") if err != nil { log.Fatal(err) } diff --git a/session.go b/hkexsession.go similarity index 91% rename from session.go rename to hkexsession.go index 8003a24..8b4183f 100644 --- a/session.go +++ b/hkexsession.go @@ -1,8 +1,8 @@ -package xs +package hkexsh -// Package xs - a secure terminal client/server written from scratch in Go +// Package hkexsh - a secure terminal client/server written from scratch in Go // -// Copyright (c) 2017-2020 Russell Magee +// Copyright (c) 2017-2018 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("xs.Session:\nOp:%v\nWho:%v\nCmd:%v\nAuthCookie:%v\nStatus:%v", + return fmt.Sprintf("hkexsh.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 (essentially the password) used to +// SetAuthCookie stores the authcookie (essential the password) used to // authenticate the Session. func (h *Session) SetAuthCookie(a []byte) { h.authCookie = a diff --git a/hkexsh/Makefile b/hkexsh/Makefile new file mode 100644 index 0000000..8e2fd39 --- /dev/null +++ b/hkexsh/Makefile @@ -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 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 diff --git a/hkexsh/hkexcp b/hkexsh/hkexcp new file mode 120000 index 0000000..cf0eb1c --- /dev/null +++ b/hkexsh/hkexcp @@ -0,0 +1 @@ +hkexsh \ No newline at end of file diff --git a/hkexsh/hkexsh-vis-fixedup.png b/hkexsh/hkexsh-vis-fixedup.png new file mode 100644 index 0000000..312e863 Binary files /dev/null and b/hkexsh/hkexsh-vis-fixedup.png differ diff --git a/hkexsh/hkexsh-vis.gv b/hkexsh/hkexsh-vis.gv new file mode 100755 index 0000000..41242e3 --- /dev/null +++ b/hkexsh/hkexsh-vis.gv @@ -0,0 +1,263 @@ +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" { + bgcolor="#e6ecfa"; +label="main"; +labelloc="t"; +labeljust="c"; +fontsize="18"; + + "blitter.com/go/hkexsh/hkexsh.reqTunnel" [ penwidth="0.5" fillcolor="lightblue" label="reqTunnel" ] + "blitter.com/go/hkexsh/hkexsh.doShellMode$1" [ fillcolor="lightblue" label="shellRemoteToStdin" style="dotted,filled" ] + "blitter.com/go/hkexsh/hkexsh.doShellMode$1$1" [ fillcolor="lightblue" label="doShellMode$1$1" style="dotted,filled" ] + "blitter.com/go/hkexsh/hkexsh.doShellMode" [ fillcolor="lightblue" label="doShellMode" penwidth="0.5" ] + "blitter.com/go/hkexsh/hkexsh.handleTermResizes$1" [ fillcolor="lightblue" label="handleTermResizes$1" style="dotted,filled" ] + "blitter.com/go/hkexsh/hkexsh.GetSize" [ label="GetSize" penwidth="1.5" fillcolor="lightblue" ] + "blitter.com/go/hkexsh/hkexsh.handleTermResizes" [ penwidth="0.5" fillcolor="lightblue" label="handleTermResizes" ] + "blitter.com/go/hkexsh/hkexsh.copyBuffer" [ fillcolor="lightblue" label="copyBuffer" penwidth="0.5" ] + "blitter.com/go/hkexsh/hkexsh.copyBuffer$1" [ fillcolor="lightblue" label="copyBuffer$1" style="dotted,filled" ] + "blitter.com/go/hkexsh/hkexsh.copyBuffer$2" [ fillcolor="lightblue" label="copyBuffer$2" style="dotted,filled" ] + "blitter.com/go/hkexsh/hkexsh.copyBuffer$3" [ style="dotted,filled" fillcolor="lightblue" label="copyBuffer$3" ] + "blitter.com/go/hkexsh/hkexsh.Copy" [ fillcolor="lightblue" label="Copy" penwidth="1.5" ] + "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$2" [ fillcolor="lightblue" label="deferCloseChaff" style="dotted,filled" ] + "blitter.com/go/hkexsh/hkexsh.sendSessionParams" [ fillcolor="lightblue" label="sendSessionParams" penwidth="0.5" ] + "blitter.com/go/hkexsh/hkexsh.main$1" [ label="deferRestore" style="dotted,filled" fillcolor="lightblue" ] + "blitter.com/go/hkexsh/hkexsh.launchTuns" [ penwidth="0.5" fillcolor="lightblue" label="launchTuns" ] + "blitter.com/go/hkexsh/hkexsh.rejectUserMsg" [ label="rejectUserMsg" penwidth="0.5" fillcolor="lightblue" ] + "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.doCopyMode" [ fillcolor="lightblue" label="doCopyMode" penwidth="0.5" ] + "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" { + style="filled"; +fillcolor="lightyellow"; +rank="sink"; +label="[hkexsh]"; +tooltip="package: blitter.com/go/hkexsh"; +penwidth="0.8"; +fontsize="16"; +fontname="bold"; +URL="/?f=blitter.com/go/hkexsh"; + + "blitter.com/go/hkexsh.Restore" [ fillcolor="moccasin" label="Restore" penwidth="1.5" ] + "blitter.com/go/hkexsh.MakeRaw" [ fillcolor="moccasin" label="MakeRaw" penwidth="1.5" ] + "blitter.com/go/hkexsh.ReadPassword" [ penwidth="1.5" fillcolor="moccasin" label="ReadPassword" ] + "blitter.com/go/hkexsh.NewSession" [ label="NewSession" penwidth="1.5" fillcolor="moccasin" ] + + 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).SetStatus" [ label="SetStatus" penwidth="1.5" fillcolor="moccasin" ] + + } + + subgraph "cluster_blitter.com/go/hkexsh.Session" { + fillcolor="wheat2"; +label="(Session)"; +tooltip="type: blitter.com/go/hkexsh.Session"; +penwidth="0.5"; +fontsize="15"; +fontcolor="#222222"; +labelloc="b"; +style="rounded,filled"; + + "(blitter.com/go/hkexsh.Session).Status" [ label="Status" 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" [ fillcolor="moccasin" label="ConnHost" penwidth="1.5" ] + "(blitter.com/go/hkexsh.Session).TermType" [ penwidth="1.5" fillcolor="moccasin" label="TermType" ] + "(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" ] + + } + + } + + subgraph "cluster_blitter.com/go/hkexsh/hkexnet" { + fontname="bold"; +label="[hkexnet]"; +URL="/?f=blitter.com/go/hkexsh/hkexnet"; +tooltip="package: blitter.com/go/hkexsh/hkexnet"; +fontsize="16"; +fillcolor="lightyellow"; +rank="sink"; +penwidth="0.8"; +style="filled"; + + "blitter.com/go/hkexsh/hkexnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" ] + "blitter.com/go/hkexsh/hkexnet.Dial" [ penwidth="1.5" fillcolor="moccasin" label="Dial" ] + + subgraph "cluster_*blitter.com/go/hkexsh/hkexnet.Conn" { + penwidth="0.5"; +fontsize="15"; +fontcolor="#222222"; +labelloc="b"; +style="rounded,filled"; +fillcolor="wheat2"; +label="(*Conn)"; +tooltip="type: *blitter.com/go/hkexsh/hkexnet.Conn"; + + "(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ fillcolor="moccasin" label="WritePacket" penwidth="1.5" ] + "(*blitter.com/go/hkexsh/hkexnet.Conn).Close" [ fillcolor="moccasin" label="Close" penwidth="1.5" ] + "(*blitter.com/go/hkexsh/hkexnet.Conn).SetupChaff" [ fillcolor="moccasin" label="SetupChaff" penwidth="1.5" ] + "(*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" [ label="ShutdownChaff" penwidth="1.5" fillcolor="moccasin" ] + "(*blitter.com/go/hkexsh/hkexnet.Conn).SetStatus" [ penwidth="1.5" fillcolor="moccasin" label="SetStatus" ] + + } + + 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).GetStatus" [ fillcolor="moccasin" label="GetStatus" penwidth="1.5" ] + "(blitter.com/go/hkexsh/hkexnet.Conn).Write" [ fillcolor="moccasin" label="Write" penwidth="1.5" ] + "(blitter.com/go/hkexsh/hkexnet.Conn).Read" [ fillcolor="moccasin" label="Read" penwidth="1.5" ] + + } + + } + + subgraph "cluster_blitter.com/go/hkexsh/logger" { + fillcolor="lightyellow"; +fontname="bold"; +label="[logger]"; +tooltip="package: blitter.com/go/hkexsh/logger"; +penwidth="0.8"; +style="filled"; +rank="sink"; +URL="/?f=blitter.com/go/hkexsh/logger"; +fontsize="16"; + + "blitter.com/go/hkexsh/logger.LogDebug" [ fillcolor="moccasin" label="LogDebug" penwidth="1.5" ] + "blitter.com/go/hkexsh/logger.New" [ fillcolor="moccasin" label="New" penwidth="1.5" ] + + } + + subgraph "cluster_blitter.com/go/hkexsh/spinsult" { + penwidth="0.8"; +style="filled"; +rank="sink"; +tooltip="package: blitter.com/go/hkexsh/spinsult"; +fontsize="16"; +fillcolor="lightyellow"; +fontname="bold"; +label="[spinsult]"; +URL="/?f=blitter.com/go/hkexsh/spinsult"; + + "blitter.com/go/hkexsh/spinsult.GetSentence" [ fillcolor="moccasin" label="GetSentence" penwidth="1.5" ] + + } + + subgraph "cluster_github.com/mattn/go-isatty" { + tooltip="package: github.com/mattn/go-isatty"; +penwidth="0.8"; +fillcolor="lightyellow"; +rank="sink"; +label="[isatty]"; +URL="/?f=github.com/mattn/go-isatty"; +fontsize="16"; +style="filled"; +fontname="bold"; + + "github.com/mattn/go-isatty.IsTerminal" [ fillcolor="moccasin" label="IsTerminal" penwidth="1.5" ] + + } + + } + + "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.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.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.handleTermResizes" -> "blitter.com/go/hkexsh/hkexsh.handleTermResizes$1" [ arrowhead="normalnoneodot" ] + "blitter.com/go/hkexsh/hkexsh.doShellMode" -> "blitter.com/go/hkexsh/hkexsh.handleTermResizes" [ ] + "blitter.com/go/hkexsh/hkexsh.copyBuffer" -> "(blitter.com/go/hkexsh/hkexnet.Conn).Write" [ style="dashed" color="saddlebrown" ] + "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.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.doShellMode" -> "blitter.com/go/hkexsh/hkexsh.doShellMode$2" [ arrowhead="normalnoneodot" ] + "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" [ style="dashed" color="saddlebrown" ] + "blitter.com/go/hkexsh/hkexsh.main$1" -> "blitter.com/go/hkexsh.Restore" [ color="saddlebrown" ] + "blitter.com/go/hkexsh/hkexsh.launchTuns" -> "blitter.com/go/hkexsh/hkexsh.reqTunnel" [ ] + "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.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" [ color="saddlebrown" arrowhead="normalnoneodiamond" ] + "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" -> "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.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" [ arrowhead="normalnoneodiamond" color="saddlebrown" ] + "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.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.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.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" ] +} diff --git a/xs/xs.go b/hkexsh/hkexsh.go similarity index 58% rename from xs/xs.go rename to hkexsh/hkexsh.go index 018c542..d8a7f13 100755 --- a/xs/xs.go +++ b/hkexsh/hkexsh.go @@ -1,6 +1,6 @@ -// xs client +// hkexsh client // -// Copyright (c) 2017-2020 Russell Magee +// Copyright (c) 2017-2019 Russell Magee // Licensed under the terms of the MIT license (see LICENSE.mit in this // distribution) // @@ -14,8 +14,10 @@ import ( "flag" "fmt" "io" + "io/ioutil" "log" "math/rand" + "net" "os" "os/exec" "os/user" @@ -29,12 +31,12 @@ import ( "time" "net/http" - _ "net/http/pprof" //nolint:gosec + _ "net/http/pprof" - xs "blitter.com/go/xs" - "blitter.com/go/xs/logger" - "blitter.com/go/xs/spinsult" - "blitter.com/go/xs/xsnet" + hkexsh "blitter.com/go/hkexsh" + "blitter.com/go/hkexsh/hkexnet" + "blitter.com/go/hkexsh/logger" + "blitter.com/go/hkexsh/spinsult" isatty "github.com/mattn/go-isatty" ) @@ -44,9 +46,6 @@ var ( // 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 @@ -56,39 +55,27 @@ var ( //////////////////////////////////////////////////// -const ( - CmdExitedEarly = 2 - XSNetDialFailed = 3 - ErrReadingAuthReply = 253 - ServerRejectedSecureProposal = 254 - GeneralProtocolErr = 255 -) - -const ( - DeadCharPrefix = 0x1d -) - // 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 @@@ Bob\r\n" + + "@@@@ @@@@@@@@@@@@@@@@@@@@@@ I @@@\r\n" + "@@@@ @@@@@@@@@@@@@@@@@@@@*@[ i @@@\r\n" + - "@@@@ @@@@@@@@@@@@@@@@@@@@[][ | ]@@@ bOb\r\n" + + "@@@@ @@@@@@@@@@@@@@@@@@@@[][ | ]@@@\r\n" + "@@@@ ~_,,_ ~@@@@@@@~ ____~ @ @@@\r\n" + - "@@@@ _~ , , `@@@~ _ _`@ ]L J@@@ o\r\n" + + "@@@@ _~ , , `@@@~ _ _`@ ]L J@@@\r\n" + "@@@@ , @@w@ww+ @@@ww``,,@w@ ][ @@@@\r\n" + - "@@@@, @@@@www@@@ @@@@@@@ww@@@@@[ @@@@ BOB\r\n" + + "@@@@, @@@@www@@@ @@@@@@@ww@@@@@[ @@@@\r\n" + "@@@@@_|| @@@@@@P' @@P@@@@@@@@@@@[|c@@@@\r\n" + - "@@@@@@w| '@@P~ P]@@@-~, ~Y@@^'],@@@@@@ . o\r\n" + + "@@@@@@w| '@@P~ P]@@@-~, ~Y@@^'],@@@@@@\r\n" + "@@@@@@@[ _ _J@@Tk ]]@@@@@@\r\n" + "@@@@@@@@,@ @@, c,,,,,,,y ,w@@[ ,@@@@@@@\r\n" + - "@@@@@@@@@ i @w ====--_@@@@@ @@@@@@@@ o .\r\n" + + "@@@@@@@@@ i @w ====--_@@@@@ @@@@@@@@\r\n" + "@@@@@@@@@@`,P~ _ ~^^^^Y@@@@@ @@@@@@@@@\r\n" + - "@@@@^^=^@@^ ^' ,ww,w@@@@@ _@@@@@@@@@@ B o B\r\n" + + "@@@@^^=^@@^ ^' ,ww,w@@@@@ _@@@@@@@@@@\r\n" + "@@@_xJ~ ~ , @@@@@@@P~_@@@@@@@@@@@@\r\n" + "@@ @, ,@@@,_____ _,J@@@@@@@@@@@@@\r\n" + "@@L `' ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n" + @@ -102,14 +89,6 @@ type ( escSeqs map[byte]escHandler ) -var ( - escs = escSeqs{ - 'i': func(io.Writer) { os.Stdout.Write([]byte("\x1b[s\x1b[2;1H\x1b[1;31m[HKEXSH]\x1b[39;49m\x1b[u")) }, - 't': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m[HKEXSH]\x1b[39;49m")) }, - 'B': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m" + bob + "\x1b[39;49m")) }, - } -) - // Copy copies from src to dst until either EOF is reached // on src or an error occurs. It returns the number of bytes // copied and the first error encountered while copying, if any. @@ -128,7 +107,7 @@ var ( // some client escape sequences to trigger special actions during // interactive sessions. // -// (See go doc xs/xs.{escSeqs,escHandler}) +// (See go doc hkexsh/hkexsh.{escSeqs,escHandler}) func Copy(dst io.Writer, src io.Reader) (written int64, err error) { written, err = copyBuffer(dst, src, nil) return @@ -141,7 +120,7 @@ func Copy(dst io.Writer, src io.Reader) (written int64, err error) { // go stdlib pkg/io, with escape sequence interpretation to trigger // some special client-side actions. // -// (See go doc xs/xs.{escSeqs,escHandler}) +// (See go doc hkexsh/hkexsh.{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 @@ -157,6 +136,11 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er // or tunnel traffic indicator - note we cannot just spawn a goroutine // here, as copyBuffer() returns after each burst of data. Scope must // outlive individual copyBuffer calls). + escs := escSeqs{ + 'i': func(io.Writer) { os.Stdout.Write([]byte("\x1b[s\x1b[2;1H\x1b[1;31m[HKEXSH]\x1b[39;49m\x1b[u")) }, + 't': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m[HKEXSH]\x1b[39;49m")) }, + 'B': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m" + bob + "\x1b[39;49m")) }, + } /* // If the reader has a WriteTo method, use it to do the copy. @@ -168,7 +152,7 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er if rt, ok := dst.(io.ReaderFrom); ok { return rt.ReadFrom(src) } - */ //nolint:gocritic,nolintlint + */ if buf == nil { size := 32 * 1024 if l, ok := src.(*io.LimitedReader); ok && int64(size) > l.N { @@ -189,8 +173,8 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er // A repeat of 4 keys (conveniently 'dead' chars for most // interactive shells; here CTRL-]) shall introduce // some special responses or actions on the client side. - if seqPos < 4 { //nolint:gomnd - if buf[0] == DeadCharPrefix { + if seqPos < 4 { + if buf[0] == 0x1d { seqPos++ } } else { @@ -237,7 +221,7 @@ func GetSize() (cols, rows int, err error) { if err != nil { log.Println(err) - cols, rows = 80, 24 // failsafe + cols, rows = 80, 24 //failsafe } else { n, err := fmt.Sscanf(string(out), "%d %d\n", &rows, &cols) if n < 2 || @@ -253,137 +237,54 @@ func GetSize() (cols, rows int, err error) { return } -func buildCmdRemoteToLocal(copyQuiet bool, copyLimitBPS uint, destPath 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 copyQuiet || pverr != nil { - // copyQuiet and copyLimitBPS are not applicable in dumb copy mode - captureStderr = true - cmd = xs.GetTool("tar") - - args = []string{"-xz", "-C", destPath} - } else { - // TODO: Query remote side for total file/dir size - bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d ", copyLimitBPS) - displayOpts := " -pre " //nolint:goconst - cmd = xs.GetTool("bash") - args = []string{"-c", "pv " + displayOpts + bandwidthInBytesPerSec + "| tar -xz -C " + destPath} - } - log.Printf("[%v %v]\n", cmd, args) - return -} - -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 - // 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 = append(args, fileTmp) - } else { - args = append(args, "-C", dirTmp, fileTmp) - } - } - } else { - captureStderr = copyQuiet - bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d", copyLimitBPS) - displayOpts := " -pre " //nolint:goconst,nolintlint - 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", 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. +// doCopyMode begins a secure hkexsh 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) { +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())) var c *exec.Cmd - // os.Clearenv() - // os.Setenv("HOME", u.HomeDir) - // os.Setenv("TERM", "vt102") // TODO: server or client option? + //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)) + cmdName := "/bin/tar" + cmdArgs := []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 + // 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 == "" { + cmdArgs = append(cmdArgs, fileTmp) + } else { + cmdArgs = append(cmdArgs, "-C", dirTmp, fileTmp) + } + } + + 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) c = exec.Command(cmdName, cmdArgs...) // #nosec c.Dir, _ = os.Getwd() // #nosec log.Println("[wd:", c.Dir, "]") c.Stdout = conn stdErrBuffer := new(bytes.Buffer) - if captureStderr { - c.Stderr = stdErrBuffer - } else { - c.Stderr = os.Stderr - } + c.Stderr = stdErrBuffer // Start the command (no pty) err = c.Start() // returns immediately @@ -404,7 +305,7 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool, if err != nil { fmt.Println("cmd exited immediately. Cannot get cmd.Wait().ExitStatus()") err = errors.New("cmd exited prematurely") - exitStatus = uint32(CmdExitedEarly) + exitStatus = uint32(2) } else { if err = c.Wait(); err != nil { if exiterr, ok := err.(*exec.ExitError); ok { @@ -416,23 +317,21 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool, // an ExitStatus() method with the same signature. if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { exitStatus = uint32(status.ExitStatus()) - if captureStderr { - fmt.Print(stdErrBuffer) - } + fmt.Print(stdErrBuffer) } } } // send CSOExitStatus to inform remote (server) end cp is done log.Println("Sending local exitStatus:", exitStatus) - r := make([]byte, 4) //nolint:gomnd + r := make([]byte, 4) binary.BigEndian.PutUint32(r, exitStatus) - _, we := conn.WritePacket(r, xsnet.CSOExitStatus) + _, we := conn.WritePacket(r, hkexnet.CSOExitStatus) if we != nil { fmt.Println("Error:", we) } // Do a final read for remote's exit status - s := make([]byte, 4) //nolint:gomnd + s := make([]byte, 4) _, remErr := conn.Read(s) if remErr != io.EOF && !strings.Contains(remErr.Error(), "use of closed network") && @@ -451,11 +350,18 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool, } } else { log.Println("remote filepath:", string(rec.Cmd()), "local files:", files) + var c *exec.Cmd + + cmdName := "/bin/tar" destPath := files - _, cmdName, cmdArgs := buildCmdRemoteToLocal(copyQuiet, copyLimitBPS, destPath) - - c := exec.Command(cmdName, cmdArgs...) // #nosec + 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#`} + c = exec.Command(cmdName, cmdArgs...) // #nosec c.Stdin = conn c.Stdout = os.Stdout c.Stderr = os.Stderr @@ -489,11 +395,11 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool, return } -// doShellMode begins an xs shell session (one-shot command or +// doShellMode begins an hkexsh 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 +func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State, rec *hkexsh.Session) { + //client reader (from server) goroutine + //Read remote end's stdout wg.Add(1) // #gv:s/label=\"doShellMode\$1\"/label=\"shellRemoteToStdin\"/ @@ -511,7 +417,7 @@ func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec * // exit with inerr == nil _, inerr := io.Copy(os.Stdout, conn) if inerr != nil { - restoreTermState(oldState) + _ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // #nosec // Copy operations and user logging off will cause // a "use of closed network connection" so handle that // gracefully here @@ -526,7 +432,7 @@ func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec * if isInteractive { log.Println("[* Got EOF *]") - restoreTermState(oldState) + _ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // #nosec exitWithStatus(int(rec.Status())) } } @@ -544,11 +450,9 @@ func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec * // TODO:.gv:doShellMode:2:shellStdinToRemote shellStdinToRemote := func() { defer wg.Done() - _, outerr := func(conn *xsnet.Conn, r io.Reader) (w int64, e error) { + _, outerr := func(conn *hkexnet.Conn, r io.Reader) (w int64, e error) { // Copy() expects EOF so this will // exit with outerr == nil - // NOTE we use a local implementation of Copy() to allow - // for custom key sequences to trigger local actions w, e = Copy(conn, r) return w, e }(conn, os.Stdin) @@ -556,7 +460,7 @@ func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec * if outerr != nil { log.Println(outerr) fmt.Println(outerr) - restoreTermState(oldState) + _ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // #nosec log.Println("[Hanging up]") exitWithStatus(0) } @@ -570,15 +474,15 @@ func doShellMode(isInteractive bool, conn *xsnet.Conn, oldState *xs.State, rec * } func usageShell() { - fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) - fmt.Fprintf(os.Stderr, "%s [opts] [user]@server\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) // nolint: errcheck + fmt.Fprintf(os.Stderr, "%s [opts] [user]@server\n", os.Args[0]) // nolint: errcheck flag.PrintDefaults() } func usageCp() { - fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) - fmt.Fprintf(os.Stderr, "%s [opts] srcFileOrDir [...] [user]@server[:dstpath]\n", os.Args[0]) - fmt.Fprintf(os.Stderr, "%s [opts] [user]@server[:srcFileOrDir] dstPath\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) // nolint: errcheck + fmt.Fprintf(os.Stderr, "%s [opts] srcFileOrDir [...] [user]@server[:dstpath]\n", os.Args[0]) // nolint: errcheck + fmt.Fprintf(os.Stderr, "%s [opts] [user]@server[:srcFileOrDir] dstPath\n", os.Args[0]) // nolint: errcheck flag.PrintDefaults() } @@ -593,19 +497,19 @@ 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 xsnet.Read()) -func reqTunnel(hc *xsnet.Conn, lp uint16 /*, p string*/ /*net.Addr*/, rp uint16) { +// (handled in hkexnet.Read()) +func reqTunnel(hc *hkexnet.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 { - fmt.Fprintln(os.Stderr, "reqTunnel:", e) + fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck } if e := binary.Write(&bTmp, binary.BigEndian, rp); e != nil { - fmt.Fprintln(os.Stderr, "reqTunnel:", e) + fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck } - _ = logger.LogDebug(fmt.Sprintln("[Client sending CSOTunSetup]")) - if n, e := hc.WritePacket(bTmp.Bytes(), xsnet.CSOTunSetup); e != nil || n != len(bTmp.Bytes()) { - fmt.Fprintln(os.Stderr, "reqTunnel:", e) + _ = logger.LogDebug(fmt.Sprintln("[Client sending CSOTunSetup]")) // nolint: gosec + if n, e := hc.WritePacket(bTmp.Bytes(), hkexnet.CSOTunSetup); e != nil || n != len(bTmp.Bytes()) { + fmt.Fprintln(os.Stderr, "reqTunnel:", e) // nolint: errcheck } } @@ -618,7 +522,7 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other if strings.Contains(arg, ":") || strings.Contains(arg, "@") { fancyArg := strings.Split(flag.Arg(i), "@") var fancyHostPath []string - if len(fancyArg) < 2 { //nolint:gomnd + if len(fancyArg) < 2 { //TODO: no user specified, use current fancyUser = "[default:getUser]" fancyHostPath = strings.Split(fancyArg[0], ":") @@ -644,8 +548,8 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other return fancyUser, fancyHost, fancyPath, isDest, otherArgs } -func launchTuns(conn *xsnet.Conn /*remoteHost string,*/, tuns string) { - /*remAddrs, _ := net.LookupHost(remoteHost)*/ //nolint:gocritic,nolintlint +func launchTuns(conn *hkexnet.Conn, remoteHost string, tuns string) { + remAddrs, _ := net.LookupHost(remoteHost) // nolint: gosec if tuns == "" { return @@ -654,12 +558,12 @@ func launchTuns(conn *xsnet.Conn /*remoteHost string,*/, tuns string) { tunSpecs := strings.Split(tuns, ",") for _, tunItem := range tunSpecs { var lPort, rPort uint16 - _, _ = fmt.Sscanf(tunItem, "%d:%d", &lPort, &rPort) - reqTunnel(conn, lPort /*remAddrs[0],*/, rPort) + _, _ = fmt.Sscanf(tunItem, "%d:%d", &lPort, &rPort) // nolint: gosec + reqTunnel(conn, lPort, remAddrs[0], rPort) } } -func sendSessionParams(conn io.Writer /* *xsnet.Conn*/, rec *xs.Session) (e error) { +func sendSessionParams(conn io.Writer /* *hkexnet.Conn*/, rec *hkexsh.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 { @@ -690,107 +594,61 @@ func sendSessionParams(conn io.Writer /* *xsnet.Conn*/, rec *xs.Session) (e erro } // TODO: reduce gocyclo -func main() { //nolint: funlen, gocyclo - var ( - isInteractive bool - vopt bool - gopt bool // true: login via password, asking server to generate authToken - dbg bool - shellMode bool // true: act as shell, false: file copier - cipherAlg string - hmacAlg string - kexAlg string - server string - port uint - cmdStr string - tunSpecStr string // lport1:rport1[,lport2:rport2,...] - rekeySecs uint - remodRequested bool // true: when rekeying, switch to random cipher/hmac alg - copySrc []byte - copyDst string - copyQuiet bool - copyLimitBPS uint +func main() { + 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,...] - authCookie string - chaffEnabled bool - chaffFreqMin uint - chaffFreqMax uint - chaffBytesMax uint + var copySrc []byte + var copyDst string - op []byte - ) + var authCookie string + var chaffEnabled bool + var chaffFreqMin uint + var chaffFreqMax uint + var chaffBytesMax uint - // === Common (xs and xc) option parsing + var op []byte + isInteractive := false flag.BoolVar(&vopt, "v", false, "show version") flag.BoolVar(&dbg, "d", false, "debug logging") - flag.StringVar(&cipherAlg, "c", "C_AES_256", "session `cipher`"+` - C_AES_256 - C_TWOFISH_128 - C_BLOWFISH_64 - C_CRYPTMT1 - C_HOPSCOTCH - C_CHACHA20_12`) - flag.StringVar(&hmacAlg, "m", "H_SHA256", "session `HMAC`"+` - H_SHA256 - H_SHA512 - H_WHIRLPOOL`) - flag.StringVar(&kexAlg, "k", "KEX_HERRADURA512", "KEx `alg`"+` - KEX_HERRADURA256 - KEX_HERRADURA512 - KEX_HERRADURA1024 - KEX_HERRADURA2048 - KEX_KYBER512 - KEX_KYBER768 - KEX_KYBER1024 - KEX_NEWHOPE - KEX_NEWHOPE_SIMPLE - KEX_FRODOKEM_1344AES - KEX_FRODOKEM_1344SHAKE - KEX_FRODOKEM_976AES - KEX_FRODOKEM_976SHAKE`) - flag.StringVar(&kcpMode, "K", "unused", "KCP `alg`, one of [KCP_NONE | KCP_AES | KCP_BLOWFISH | KCP_CAST5 | KCP_SM4 | KCP_SALSA20 | KCP_SIMPLEXOR | KCP_TEA | KCP_3DES | KCP_TWOFISH | KCP_XTEA] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP") //nolint:lll - flag.UintVar(&port, "p", 2000, "``port") //nolint:gomnd,lll - flag.UintVar(&rekeySecs, "r", 300, "rekey interval in `secs`") - flag.BoolVar(&remodRequested, "R", false, "Borg Countermeasures (remodulate cipher/hmac alg on each rekey)") - //nolint:gocritic,nolintlint // flag.StringVar(&authCookie, "a", "", "auth cookie") + flag.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\" | \"H_SHA512\"]") + flag.StringVar(&kexAlg, "k", "KEX_HERRADURA512", "`kex` [\"KEX_HERRADURA{256/512/1024/2048}\" | \"KEX_KYBER{512/768/1024}\" | \"KEX_NEWHOPE\" | \"KEX_NEWHOPE_SIMPLE\"]") + flag.UintVar(&port, "p", 2000, "`port`") + //flag.StringVar(&authCookie, "a", "", "auth cookie") flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts") - flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min `msecs`") //nolint:gomnd - flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max `msecs`") //nolint:gomnd - flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max `bytes`") //nolint:gomnd + 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.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to <`file`>") - flag.StringVar(&memprofile, "memprofile", "", "write memory profile to <`file`>") - - // === xc vs. xs option parsing + flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to `file`") + flag.StringVar(&memprofile, "memprofile", "", "write memory profile to `file`") // Find out what program we are (shell or copier) myPath := strings.Split(os.Args[0], string(os.PathSeparator)) - 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 + if myPath[len(myPath)-1] != "hkexcp" && myPath[len(myPath)-1] != "hkexcp.exe" { + // hkexsh accepts a command (-x) but not // a srcpath (-r) or dstpath (-t) - flag.StringVar(&cmdStr, "x", "", "run <`command`> (if not specified, run interactive shell)") - flag.StringVar(&tunSpecStr, "T", "", "``tunnelspec - localPort:remotePort[,localPort:remotePort,...]") + flag.StringVar(&cmdStr, "x", "", "`command` to run (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(©Quiet, "q", false, "do not output progress bar during copy") - flag.UintVar(©LimitBPS, "L", 8589934592, "copy max rate in bytes per sec") //nolint:gomnd 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 { @@ -799,25 +657,23 @@ func main() { //nolint: funlen, gocyclo defer f.Close() fmt.Println("StartCPUProfile()") if err := pprof.StartCPUProfile(f); err != nil { - log.Fatal("could not start CPU profile: ", err) //nolint:gocritic + log.Fatal("could not start CPU profile: ", err) } else { defer pprof.StopCPUProfile() } - go func() { http.ListenAndServe("localhost:6060", nil) }() //nolint:errcheck,gosec + 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()) - //nolint:gocritic,nolintlint // fmt.Println("otherArgs:", otherArgs) + //fmt.Println("otherArgs:", otherArgs) // Set defaults if user doesn't specify user, path or port var uname string if remoteUser == "" { - u, _ := user.Current() - uname = localUserName(u) + u, _ := user.Current() // nolint: gosec + uname = u.Username } else { uname = remoteUser } @@ -829,8 +685,6 @@ func main() { //nolint: funlen, gocyclo tmpPath = "." } - // === Copy mode arg and copy src/dest setup - var fileArgs string if !shellMode /*&& tmpPath != ""*/ { // -if pathIsSrc && len(otherArgs) > 1 ERROR @@ -862,60 +716,59 @@ func main() { //nolint: funlen, gocyclo } } - // === Do some final option consistency checks + // Do some more option consistency checks - //nolint:gocritic,nolintlint // fmt.Println("server finally is:", server) + //fmt.Println("server finally is:", server) if flag.NFlag() == 0 && server == "" { flag.Usage() exitWithStatus(0) } - if cmdStr != "" && (len(copySrc) != 0 || copyDst != "") { + if vopt { + fmt.Printf("version %s (%s)\n", version, gitCommit) + 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, "xs") - xsnet.Init(dbg, "xs", 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, "hkexsh") // nolint: errcheck,gosec + hkexnet.Init(dbg, "hkexsh", logger.LOG_USER|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR) if dbg { log.SetOutput(Log) } else { - log.SetOutput(io.Discard) + log.SetOutput(ioutil.Discard) } - // === Auth token fetch for login - if !gopt { // See if we can log in via an auth token - u, _ := user.Current() - ab, aerr := os.ReadFile(fmt.Sprintf("%s/%s", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE)) + u, _ := user.Current() // nolint: gosec + ab, aerr := ioutil.ReadFile(fmt.Sprintf("%s/.hkexsh_id", u.HomeDir)) if aerr == nil { - for _, line := range strings.Split(string(ab), "\n") { - line += "\n" - idx := strings.Index(line, remoteHost+":"+uname) - if idx >= 0 { - line = line[idx:] - entries := strings.SplitN(line, "\n", -1) - authCookie = strings.TrimSpace(entries[0]) - // Security scrub - line = "" - break - } - } - if authCookie == "" { + idx := strings.Index(string(ab), remoteHost) + if idx >= 0 { + ab = ab[idx:] + entries := strings.SplitN(string(ab), "\n", -1) + authCookie = strings.TrimSpace(entries[0]) + // Security scrub + ab = nil + runtime.GC() + } else { _, _ = fmt.Fprintln(os.Stderr, "[no authtoken, use -g to request one from server]") } } else { - log.Printf("[cannot read %s/%s]\n", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE) + log.Printf("[cannot read %s/.hkexsh_id]\n", u.HomeDir) } } - runtime.GC() - // === Enforce some sane min/max vals on chaff flags - if chaffFreqMin < 2 { //nolint:gomnd + // Enforce some sane min/max vals on chaff flags + if chaffFreqMin < 2 { chaffFreqMin = 2 } if chaffFreqMax == 0 { @@ -925,17 +778,15 @@ func main() { //nolint: funlen, gocyclo 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 if gopt { - fmt.Fprintln(os.Stderr, "[requesting authtoken from server]") + fmt.Fprintln(os.Stderr, "[requesting authtoken from server]") // nolint: errcheck op = []byte{'A'} chaffFreqMin = 2 chaffFreqMax = 10 - } else if cmdStr == "" { + } else if len(cmdStr) == 0 { op = []byte{'s'} isInteractive = true } else { @@ -956,200 +807,127 @@ func main() { //nolint: funlen, gocyclo // client->server file copy // src file list is in copySrc op = []byte{'D'} - //nolint:gocritic,nolintlint // fmt.Println("client->server copy:", string(copySrc), "->", copyDst) + //fmt.Println("client->server copy:", string(copySrc), "->", copyDst) cmdStr = copyDst } else { // server->client file copy // remote src file(s) in copyDsr op = []byte{'S'} - //nolint:gocritic,nolintlint // fmt.Println("server->client copy:", string(copySrc), "->", copyDst) + //fmt.Println("server->client copy:", string(copySrc), "->", copyDst) cmdStr = string(copySrc) } } - // === TCP / KCP Dial setup - - proto := "tcp" - if kcpMode != "unused" { - proto = "kcp" - } - - remodExtArg := "" - if remodRequested { - remodExtArg = "OPT_REMOD" - } - // Pass opt to Dial() via extensions arg - conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode, remodExtArg) + conn, err := hkexnet.Dial("tcp", server, cipherAlg, hmacAlg, kexAlg) if err != nil { fmt.Println(err) - exitWithStatus(XSNetDialFailed) + exitWithStatus(3) } - - conn.RekeyHelper(rekeySecs) - defer conn.ShutdownRekey() - - // === Shell terminal mode (Shell vs. Copy) setup + defer conn.Close() // nolint: errcheck + // From this point on, conn is a secure encrypted channel // 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 *xs.State - defer conn.Close() - - // === From this point on, conn is a secure encrypted channel - + var oldState *hkexsh.State if shellMode { if isatty.IsTerminal(os.Stdin.Fd()) { - oldState, err = xs.MakeRaw(os.Stdin.Fd()) + oldState, err = hkexsh.MakeRaw(int(os.Stdin.Fd())) if err != nil { panic(err) } // #gv:s/label=\"main\$1\"/label=\"deferRestore\"/ // TODO:.gv:main:1:deferRestore - defer restoreTermState(oldState) + defer func() { _ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) }() // nolint: errcheck,gosec } 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() { //nolint:gomnd - restoreTermState(oldState) - fmt.Printf(" .. [login timeout]\n") - exitWithStatus(xsnet.CSELoginTimeout) - }) - - if authCookie == "" { - if !gopt { - // No auth token, prompt for password - fmt.Printf("Gimme cookie:") - } - ab, e := xs.ReadPassword(os.Stdin.Fd()) - if !gopt { - fmt.Printf("\r\n") - } + if len(authCookie) == 0 { + //No auth token, prompt for password + fmt.Printf("Gimme cookie:") + ab, e := hkexsh.ReadPassword(int(os.Stdin.Fd())) + fmt.Printf("\r\n") if e != nil { panic(e) } authCookie = string(ab) } - - //nolint:gocritic,nolintlint // i_ = loginImpatience.Stop() - _ = loginTimeout.Stop() // Security scrub runtime.GC() - // === Session param and TERM setup - // Set up session params and send over to server - rec := xs.NewSession(op, []byte(uname), []byte(remoteHost), []byte(os.Getenv("TERM")), []byte(cmdStr), []byte(authCookie), 0) + rec := hkexsh.NewSession(op, []byte(uname), []byte(remoteHost), []byte(os.Getenv("TERM")), []byte(cmdStr), []byte(authCookie), 0) sendErr := sendSessionParams(&conn, rec) if sendErr != nil { - restoreTermState(oldState) - rec.SetStatus(ServerRejectedSecureProposal) - fmt.Fprintln(os.Stderr, "Error: server rejected secure proposal params or login timed out") - exitWithStatus(int(rec.Status())) - //nolint:gocritic,nolintlint // log.Fatal(sendErr) + log.Fatal(sendErr) } - // Security scrub - authCookie = "" //nolint: ineffassign + //Security scrub + authCookie = "" // nolint: ineffassign runtime.GC() - // === Login Auth - - // === Read auth reply from server + // 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") - rec.SetStatus(ErrReadingAuthReply) + 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()) - rec.SetStatus(GeneralProtocolErr) + fmt.Fprintln(os.Stderr, rejectUserMsg()) // nolint: errcheck + rec.SetStatus(255) } else { - // === Set up connection keepalive to server - conn.StartupKeepAlive() // goroutine, returns immediately - defer conn.ShutdownKeepAlive() - - // === 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\"/ // TODO:.gv:main:2:deferCloseChaff - conn.StartupChaff() // goroutine, returns immediately + conn.EnableChaff() // goroutine, returns immediately + defer conn.DisableChaff() defer conn.ShutdownChaff() } - // === (goroutine) Start keepAliveWorker for tunnels + // Keepalive for any tunnels that may exist // #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 != "" { + //[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 { // 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) //nolint:gosec,gomnd + 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 + conn.WritePacket([]byte{0, 0}, hkexnet.CSOTunKeepAlive) // nolint: errcheck,gosec } } go keepAliveWorker() - // [1]} + //[1]} - // === Session entry (shellMode or copyMode) if shellMode { - // === (shell) launch tunnels - launchTuns(&conn /*remoteHost,*/, tunSpecStr) + launchTuns(&conn, remoteHost, tunSpecStr) doShellMode(isInteractive, &conn, oldState, rec) - } else { - // === (.. or file copy) - s, _ := doCopyMode(&conn, pathIsDest, fileArgs, copyQuiet, copyLimitBPS, rec) + } else { // copyMode + s, _ := doCopyMode(&conn, pathIsDest, fileArgs, rec) // nolint: errcheck,gosec rec.SetStatus(s) } if rec.Status() != 0 { - restoreTermState(oldState) - fmt.Fprintln(os.Stderr, "Session exited with status:", rec.Status()) + _ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // nolint: errcheck,gosec + fmt.Fprintln(os.Stderr, "Session exited with status:", rec.Status()) // nolint: errcheck } } if oldState != nil { - restoreTermState(oldState) - oldState = nil + _ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // nolint: gosec } - // === 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) -} - // exitWithStatus wraps os.Exit() plus does any required pprof housekeeping func exitWithStatus(status int) { if cpuprofile != "" { @@ -1164,8 +942,9 @@ func exitWithStatus(status int) { defer f.Close() runtime.GC() // get up-to-date statistics if err := pprof.WriteHeapProfile(f); err != nil { - log.Fatal("could not write memory profile: ", err) //nolint:gocritic + log.Fatal("could not write memory profile: ", err) } } + os.Exit(status) } diff --git a/hkexsh/hkexsh_seq.png b/hkexsh/hkexsh_seq.png new file mode 100644 index 0000000..5b54cf6 Binary files /dev/null and b/hkexsh/hkexsh_seq.png differ diff --git a/xs/mintty_wrapper.sh b/hkexsh/mintty_wrapper.sh similarity index 86% rename from xs/mintty_wrapper.sh rename to hkexsh/mintty_wrapper.sh index dbc678a..cef7de8 100755 --- a/xs/mintty_wrapper.sh +++ b/hkexsh/mintty_wrapper.sh @@ -1,7 +1,7 @@ #!/bin/bash # ## This wrapper may be used within the MSYS/mintty Windows -## shell environment to have a functioning xs client with +## shell environment to have a functioning hkexsh client with ## working 'raw' mode and hidden password entry. ## ## mintty uses named pipes and ptys to get a more POSIX-like @@ -28,12 +28,10 @@ cleanup() { stty sane } -me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")" - if [ ${1}x == "-hx" ]; then - _${me} -h + ./hkexsh -h else stty -echo raw icrnl - _${me} $@ + ./hkexsh $@ fi diff --git a/xs/termsize_unix.go b/hkexsh/termsize_linux.go similarity index 76% rename from xs/termsize_unix.go rename to hkexsh/termsize_linux.go index 112947a..2ee0d7c 100644 --- a/xs/termsize_unix.go +++ b/hkexsh/termsize_linux.go @@ -1,5 +1,4 @@ -//go:build linux || freebsd -// +build linux freebsd +// +build linux package main @@ -10,11 +9,11 @@ import ( "os/signal" "syscall" - "blitter.com/go/xs/xsnet" + "blitter.com/go/hkexsh/hkexnet" ) // Handle pty resizes (notify server side) -func handleTermResizes(conn *xsnet.Conn) { +func handleTermResizes(conn *hkexnet.Conn) { ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGWINCH) wg.Add(1) @@ -31,7 +30,7 @@ func handleTermResizes(conn *xsnet.Conn) { log.Println(err) } termSzPacket := fmt.Sprintf("%d %d", rows, cols) - conn.WritePacket([]byte(termSzPacket), xsnet.CSOTermSize) //nolint:errcheck + conn.WritePacket([]byte(termSzPacket), hkexnet.CSOTermSize) // nolint: errcheck,gosec } }() ch <- syscall.SIGWINCH // Initial resize. diff --git a/xs/termsize_windows.go b/hkexsh/termsize_windows.go similarity index 88% rename from xs/termsize_windows.go rename to hkexsh/termsize_windows.go index 6131093..47f6b17 100644 --- a/xs/termsize_windows.go +++ b/hkexsh/termsize_windows.go @@ -6,11 +6,11 @@ import ( "log" "time" - "blitter.com/go/xs/xsnet" + "blitter.com/go/hkexsh/hkexnet" ) // Handle pty resizes (notify server side) -func handleTermResizes(conn *xsnet.Conn) { +func handleTermResizes(conn *hkexnet.Conn) { var hasStty bool curCols, curRows := 0, 0 _, _, err := GetSize() @@ -57,7 +57,7 @@ func handleTermResizes(conn *xsnet.Conn) { log.Println(err) } termSzPacket := fmt.Sprintf("%d %d", curRows, curCols) - conn.WritePacket([]byte(termSzPacket), xsnet.CSOTermSize) + conn.WritePacket([]byte(termSzPacket), hkexnet.CSOTermSize) } } }() diff --git a/xsd.initrc b/hkexshd.initrc similarity index 51% rename from xsd.initrc rename to hkexshd.initrc index ee71029..80b8bed 100755 --- a/xsd.initrc +++ b/hkexshd.initrc @@ -1,14 +1,12 @@ #!/sbin/openrc-run -SVCNAME=xsd -XSD_PIDFILE=/var/run/xsd.pid -XSD_USER=root -XSD_HOME=/var/run +SVCNAME=hkexshd +HKEXSHD_PIDFILE=/var/run/hkexshd.pid +HKEXSHD_USER=root +HKEXSHD_HOME=/var/run INST_PREFIX=/usr/local -COMMAND=$INST_PREFIX/sbin/xsd -#ARGS="-L -aK KEX_all -aC C_all -aH H_all" -echo "SET ARGS in this script to define allow KEX, cipher and hmac algs" -exit 1 +COMMAND=$INST_PREFIX/sbin/hkexshd +ARGS="" depend() { need net @@ -27,15 +25,15 @@ start() { ebegin "Starting ${SVCNAME}" start-stop-daemon \ - -d ${XSD_HOME} \ - --make-pidfile --pidfile ${XSD_PIDFILE} \ + -d ${HKEXSHD_HOME} \ + --make-pidfile --pidfile ${HKEXSHD_PIDFILE} \ --start --quiet --background \ - --exec "${COMMAND}" -- ${ARGS} + --exec "${COMMAND}" "${ARGS}" eend $? } stop() { ebegin "Stopping ${SVCNAME}" - start-stop-daemon --stop --quiet --pidfile $XSD_PIDFILE + start-stop-daemon --stop --quiet --pidfile $HKEXSHD_PIDFILE eend $? } diff --git a/hkexshd/Makefile b/hkexshd/Makefile new file mode 100644 index 0000000..9963a7b --- /dev/null +++ b/hkexshd/Makefile @@ -0,0 +1,18 @@ +.PHONY: clean all vis lint + +EXTPKGS = binary,bytes,crypto,encoding,errors,flag,fmt,internal,io,log,net,os,path,runtime,time,strings,sync,syscall +EXE = $(notdir $(shell pwd)) + +all: + 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 + +lint: + -gometalinter --deadline=60s | sort + diff --git a/hkexshd/hkexshd-vis-fixedup.png b/hkexshd/hkexshd-vis-fixedup.png new file mode 100644 index 0000000..518a2fd Binary files /dev/null and b/hkexshd/hkexshd-vis-fixedup.png differ diff --git a/hkexshd/hkexshd-vis.gv b/hkexshd/hkexshd-vis.gv new file mode 100755 index 0000000..dfecee9 --- /dev/null +++ b/hkexshd/hkexshd-vis.gv @@ -0,0 +1,281 @@ +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" { + bgcolor="#e6ecfa"; +label="main"; +labelloc="t"; +labeljust="c"; +fontsize="18"; + + "blitter.com/go/hkexsh/hkexshd.main$1" [ fillcolor="lightblue" label="main$1" style="dotted,filled" ] + "blitter.com/go/hkexsh/hkexshd.main$2$1" [ fillcolor="lightblue" label="main$2$1" style="dotted,filled" ] + "blitter.com/go/hkexsh/hkexshd.runShellAs$4" [ fillcolor="lightblue" label="deferChaffShutdown" style="dotted,filled" ] + "blitter.com/go/hkexsh/hkexshd.main" [ fillcolor="lightblue" label="main" penwidth="0.5" ] + "blitter.com/go/hkexsh/hkexshd.main$2" [ fillcolor="lightblue" label="main$2" style="dotted,filled" ] + "blitter.com/go/hkexsh/hkexshd.GenAuthToken" [ fillcolor="lightblue" label="GenAuthToken" penwidth="1.5" ] + "blitter.com/go/hkexsh/hkexshd.runShellAs" [ fillcolor="lightblue" label="runShellAs" penwidth="0.5" ] + "blitter.com/go/hkexsh/hkexshd.runShellAs$1" [ label="deferPtmxClose" style="dotted,filled" fillcolor="lightblue" ] + "blitter.com/go/hkexsh/hkexshd.runShellAs$2" [ style="dotted,filled" fillcolor="lightblue" label="termResizeWatcher" ] + "blitter.com/go/hkexsh/hkexshd.runShellAs$3" [ fillcolor="lightblue" label="stdinToPtyWorker" style="dotted,filled" ] + "blitter.com/go/hkexsh/hkexshd.runShellAs$5" [ fillcolor="lightblue" label="ptyToStdoutWorker" style="dotted,filled" ] + "blitter.com/go/hkexsh/hkexshd.runClientToServerCopyAs" [ fillcolor="lightblue" label="runClientToServerCopyAs" penwidth="0.5" ] + "blitter.com/go/hkexsh/hkexshd.runServerToClientCopyAs" [ fillcolor="lightblue" label="runServerToClientCopyAs" penwidth="0.5" ] + + subgraph "cluster_blitter.com/go/goutmp" { + fontname="bold"; +rank="sink"; +tooltip="package: blitter.com/go/goutmp"; +penwidth="0.8"; +fillcolor="lightyellow"; +label="[goutmp]"; +URL="/?f=blitter.com/go/goutmp"; +fontsize="16"; +style="filled"; + + "blitter.com/go/goutmp.Unput_utmp" [ label="Unput_utmp" penwidth="1.5" fillcolor="moccasin" ] + "blitter.com/go/goutmp.GetHost" [ fillcolor="moccasin" label="GetHost" penwidth="1.5" ] + "blitter.com/go/goutmp.Put_utmp" [ label="Put_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/hkexsh" { + fillcolor="lightyellow"; +label="[hkexsh]"; +tooltip="package: blitter.com/go/hkexsh"; +penwidth="0.8"; +style="filled"; +fontname="bold"; +rank="sink"; +URL="/?f=blitter.com/go/hkexsh"; +fontsize="16"; + + "blitter.com/go/hkexsh.AuthUserByToken" [ label="AuthUserByToken" penwidth="1.5" fillcolor="moccasin" ] + "blitter.com/go/hkexsh.AuthUserByPasswd" [ fillcolor="moccasin" label="AuthUserByPasswd" penwidth="1.5" ] + + subgraph "cluster_*blitter.com/go/hkexsh.Session" { + fontsize="15"; +fontcolor="#222222"; +labelloc="b"; +style="rounded,filled"; +fillcolor="wheat2"; +label="(*Session)"; +tooltip="type: *blitter.com/go/hkexsh.Session"; +penwidth="0.5"; + + "(*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" [ penwidth="1.5" fillcolor="moccasin" label="SetConnHost" ] + "(*blitter.com/go/hkexsh.Session).SetTermType" [ label="SetTermType" penwidth="1.5" fillcolor="moccasin" ] + "(*blitter.com/go/hkexsh.Session).SetCmd" [ fillcolor="moccasin" label="SetCmd" penwidth="1.5" ] + "(*blitter.com/go/hkexsh.Session).SetAuthCookie" [ fillcolor="moccasin" label="SetAuthCookie" penwidth="1.5" ] + "(*blitter.com/go/hkexsh.Session).ClearAuthCookie" [ label="ClearAuthCookie" penwidth="1.5" fillcolor="moccasin" ] + + } + + subgraph "cluster_blitter.com/go/hkexsh.Session" { + fillcolor="wheat2"; +label="(Session)"; +tooltip="type: blitter.com/go/hkexsh.Session"; +penwidth="0.5"; +fontsize="15"; +fontcolor="#222222"; +labelloc="b"; +style="rounded,filled"; + + "(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" [ penwidth="1.5" fillcolor="moccasin" label="Cmd" ] + "(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"; +URL="/?f=blitter.com/go/hkexsh/hkexnet"; +rank="sink"; +label="[hkexnet]"; +tooltip="package: blitter.com/go/hkexsh/hkexnet"; +penwidth="0.8"; +style="filled"; +fillcolor="lightyellow"; +fontname="bold"; + + "blitter.com/go/hkexsh/hkexnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" ] + "blitter.com/go/hkexsh/hkexnet.Listen" [ fillcolor="moccasin" label="Listen" penwidth="1.5" ] + + subgraph "cluster_*blitter.com/go/hkexsh/hkexnet.Conn" { + tooltip="type: *blitter.com/go/hkexsh/hkexnet.Conn"; +penwidth="0.5"; +fontsize="15"; +fontcolor="#222222"; +labelloc="b"; +style="rounded,filled"; +fillcolor="wheat2"; +label="(*Conn)"; + + "(*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" [ label="RemoteAddr" 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).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" ] + "(*blitter.com/go/hkexsh/hkexnet.Conn).WritePacket" [ label="WritePacket" penwidth="1.5" fillcolor="moccasin" ] + + } + + subgraph "cluster_*blitter.com/go/hkexsh/hkexnet.HKExListener" { + tooltip="type: *blitter.com/go/hkexsh/hkexnet.HKExListener"; +penwidth="0.5"; +fontsize="15"; +fontcolor="#222222"; +labelloc="b"; +style="rounded,filled"; +fillcolor="wheat2"; +label="(*HKExListener)"; + + "(*blitter.com/go/hkexsh/hkexnet.HKExListener).Accept" [ fillcolor="moccasin" label="Accept" penwidth="1.5" ] + + } + + subgraph "cluster_blitter.com/go/hkexsh/hkexnet.Conn" { + fontcolor="#222222"; +labelloc="b"; +style="rounded,filled"; +fillcolor="wheat2"; +label="(Conn)"; +tooltip="type: blitter.com/go/hkexsh/hkexnet.Conn"; +penwidth="0.5"; +fontsize="15"; + + "(blitter.com/go/hkexsh/hkexnet.Conn).Write" [ label="Write" penwidth="1.5" fillcolor="moccasin" ] + + } + + 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" [ fillcolor="moccasin" label="Close" penwidth="1.5" ] + + } + + } + + subgraph "cluster_blitter.com/go/hkexsh/logger" { + penwidth="0.8"; +fontsize="16"; +style="filled"; +rank="sink"; +fillcolor="lightyellow"; +fontname="bold"; +label="[logger]"; +URL="/?f=blitter.com/go/hkexsh/logger"; +tooltip="package: blitter.com/go/hkexsh/logger"; + + "blitter.com/go/hkexsh/logger.LogNotice" [ fillcolor="moccasin" label="LogNotice" penwidth="1.5" ] + "blitter.com/go/hkexsh/logger.New" [ fillcolor="moccasin" label="New" penwidth="1.5" ] + "blitter.com/go/hkexsh/logger.LogErr" [ fillcolor="moccasin" label="LogErr" penwidth="1.5" ] + + } + + subgraph "cluster_github.com/kr/pty" { + penwidth="0.8"; +fillcolor="lightyellow"; +tooltip="package: github.com/kr/pty"; +label="[pty]"; +URL="/?f=github.com/kr/pty"; +fontsize="16"; +style="filled"; +fontname="bold"; +rank="sink"; + + "github.com/kr/pty.Start" [ label="Start" penwidth="1.5" fillcolor="moccasin" ] + "github.com/kr/pty.Setsize" [ fillcolor="moccasin" label="Setsize" penwidth="1.5" ] + + } + + } + + "blitter.com/go/hkexsh/hkexshd.main$1" -> "blitter.com/go/hkexsh/logger.LogNotice" [ color="saddlebrown" ] + "blitter.com/go/hkexsh/hkexshd.main$2$1" -> "blitter.com/go/goutmp.Unput_utmp" [ 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.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" -> "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" [ arrowhead="normalnoneodiamond" color="saddlebrown" ] + "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" -> "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" -> "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.runClientToServerCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).EnableChaff" [ color="saddlebrown" ] + "blitter.com/go/hkexsh/hkexshd.runClientToServerCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" ] + "blitter.com/go/hkexsh/hkexshd.runClientToServerCopyAs" -> "(*blitter.com/go/hkexsh/hkexnet.Conn).ShutdownChaff" [ color="saddlebrown" arrowhead="normalnoneodiamond" ] + "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" ] +} diff --git a/xsd/hkexshd-vis.png b/hkexshd/hkexshd-vis.png similarity index 100% rename from xsd/hkexshd-vis.png rename to hkexshd/hkexshd-vis.png diff --git a/hkexshd/hkexshd.go b/hkexshd/hkexshd.go new file mode 100755 index 0000000..0a19c24 --- /dev/null +++ b/hkexshd/hkexshd.go @@ -0,0 +1,723 @@ +// hkexshd server +// +// 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) +package main + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "encoding/hex" + "errors" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "os/signal" + "os/user" + "path" + "sync" + "syscall" + "unsafe" + + "blitter.com/go/goutmp" + hkexsh "blitter.com/go/hkexsh" + "blitter.com/go/hkexsh/hkexnet" + "blitter.com/go/hkexsh/logger" + "github.com/kr/pty" +) + +var ( + version string + gitCommit string // set in -ldflags by build + + useSysLogin bool + + // 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) { + u, _ := user.Lookup(who) // nolint: gosec + var uid, gid uint32 + fmt.Sscanf(u.Uid, "%d", &uid) // nolint: gosec,errcheck + fmt.Sscanf(u.Gid, "%d", &gid) // nolint: gosec,errcheck + log.Println("uid:", uid, "gid:", gid) + + // Need to clear server's env and set key vars of the + // target user. This isn't perfect (TERM doesn't seem to + // work 100%; ANSI/xterm colour isn't working even + // if we set "xterm" or "ansi" here; and line count + // reported by 'stty -a' defaults to 24 regardless + // of client shell window used to run client. + // Investigate -- rlm 2018-01-26) + os.Clearenv() + os.Setenv("HOME", u.HomeDir) // nolint: gosec,errcheck + os.Setenv("TERM", ttype) // nolint: gosec,errcheck + os.Setenv("HKEXSH", "1") // nolint: gosec,errcheck + + var c *exec.Cmd + cmdName := "/bin/tar" + + var destDir string + if path.IsAbs(fpath) { + destDir = fpath + } else { + destDir = path.Join(u.HomeDir, fpath) + } + + cmdArgs := []string{"-xz", "-C", destDir} + + // 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{"-x", "-C", destDir, `--xform=s#.*/\(.*\)#\1#`} + c = exec.Command(cmdName, cmdArgs...) // nolint: gosec + + c.Dir = destDir + + //If os.Clearenv() isn't called by server above these will be seen in the + //client's session env. + //c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who} + //c.Dir = u.HomeDir + c.SysProcAttr = &syscall.SysProcAttr{} + c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid} + c.Stdin = conn + c.Stdout = os.Stdout + c.Stderr = os.Stderr + + if chaffing { + conn.EnableChaff() + } + defer conn.DisableChaff() + defer conn.ShutdownChaff() + + // Start the command (no pty) + log.Printf("[%v %v]\n", cmdName, cmdArgs) + err = c.Start() // returns immediately + ///////////// + // NOTE: There is, apparently, a bug in Go stdlib here. Start() + // can actually return immediately, on a command which *does* + // start but exits quickly, with c.Wait() error + // "c.Wait status: exec: not started". + // As in this example, attempting a client->server copy to + // a nonexistent remote dir (it's tar exiting right away, exitStatus + // 2, stderr + // /bin/tar -xz -C /home/someuser/nosuchdir + // stderr: fork/exec /bin/tar: no such file or directory + // + // In this case, c.Wait() won't give us the real + // exit status (is it lost?). + ///////////// + if err != nil { + log.Println("cmd exited immediately. Cannot get cmd.Wait().ExitStatus()") + err = errors.New("cmd exited prematurely") + //exitStatus = uint32(254) + exitStatus = hkexnet.CSEExecFail + } else { + if err := c.Wait(); err != nil { + //fmt.Println("*** c.Wait() done ***") + if exiterr, ok := err.(*exec.ExitError); ok { + // The program has exited with an exit code != 0 + + // This works on both Unix and Windows. Although package + // syscall is generally platform dependent, WaitStatus is + // defined for both Unix and Windows and in both cases has + // an ExitStatus() method with the same signature. + if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { + exitStatus = uint32(status.ExitStatus()) + //err = errors.New("cmd returned nonzero status") + log.Printf("Exit Status: %d\n", exitStatus) + } + } + } + log.Println("*** client->server cp finished ***") + } + return +} + +// Perform a server->client copy +func runServerToClientCopyAs(who, ttype string, conn *hkexnet.Conn, srcPath string, chaffing bool) (exitStatus uint32, err error) { + u, err := user.Lookup(who) + if err != nil { + exitStatus = 1 + return + } + var uid, gid uint32 + _, _ = fmt.Sscanf(u.Uid, "%d", &uid) // nolint: gosec + _, _ = fmt.Sscanf(u.Gid, "%d", &gid) // nolint: gosec + log.Println("uid:", uid, "gid:", gid) + + // Need to clear server's env and set key vars of the + // target user. This isn't perfect (TERM doesn't seem to + // work 100%; ANSI/xterm colour isn't working even + // if we set "xterm" or "ansi" here; and line count + // reported by 'stty -a' defaults to 24 regardless + // of client shell window used to run client. + // Investigate -- rlm 2018-01-26) + os.Clearenv() + _ = os.Setenv("HOME", u.HomeDir) // nolint: gosec + _ = os.Setenv("TERM", ttype) // nolint: gosec + _ = os.Setenv("HKEXSH", "1") // nolint: gosec + + var c *exec.Cmd + cmdName := "/bin/tar" + if !path.IsAbs(srcPath) { + srcPath = fmt.Sprintf("%s%c%s", u.HomeDir, os.PathSeparator, srcPath) + } + + srcDir, srcBase := path.Split(srcPath) + cmdArgs := []string{"-cz", "-C", srcDir, "-f", "-", srcBase} + + c = exec.Command(cmdName, cmdArgs...) // 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.Stdout = conn + // Stderr sinkholing (or buffering to something other than stdout) + // is important. Any extraneous output to tarpipe messes up remote + // side as it's expecting pure tar data. + // (For example, if user specifies abs paths, tar outputs + // "Removing leading '/' from path names") + stdErrBuffer := new(bytes.Buffer) + c.Stderr = stdErrBuffer + //c.Stderr = nil + + if chaffing { + conn.EnableChaff() + } + //defer conn.Close() + defer conn.DisableChaff() + defer conn.ShutdownChaff() + + // Start the command (no pty) + log.Printf("[%v %v]\n", cmdName, cmdArgs) + err = c.Start() // returns immediately + if err != nil { + log.Printf("Command finished with error: %v", err) + return hkexnet.CSEExecFail, err // !? + } + if err := c.Wait(); err != nil { + //fmt.Println("*** c.Wait() done ***") + if exiterr, ok := err.(*exec.ExitError); ok { + // The program has exited with an exit code != 0 + + // This works on both Unix and Windows. Although package + // syscall is generally platform dependent, WaitStatus is + // defined for both Unix and Windows and in both cases has + // an ExitStatus() method with the same signature. + if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { + exitStatus = uint32(status.ExitStatus()) + if len(stdErrBuffer.Bytes()) > 0 { + log.Print(stdErrBuffer) + } + log.Printf("Exit Status: %d", exitStatus) + } + } + } + //fmt.Println("*** server->client cp finished ***") + return +} + +// Run a command (via default shell) as a specific user +// +// Uses ptys to support commands which expect a terminal. +// nolint: gocyclo +func runShellAs(who, hname, ttype, cmd string, interactive bool, conn *hkexnet.Conn, chaffing bool) (exitStatus uint32, err error) { + var wg sync.WaitGroup + u, err := user.Lookup(who) + if err != nil { + exitStatus = 1 + return + } + var uid, gid uint32 + _, _ = fmt.Sscanf(u.Uid, "%d", &uid) // nolint: gosec + _, _ = fmt.Sscanf(u.Gid, "%d", &gid) // nolint: gosec + log.Println("uid:", uid, "gid:", gid) + + // Need to clear server's env and set key vars of the + // target user. This isn't perfect (TERM doesn't seem to + // work 100%; ANSI/xterm colour isn't working even + // if we set "xterm" or "ansi" here; and line count + // reported by 'stty -a' defaults to 24 regardless + // of client shell window used to run client. + // Investigate -- rlm 2018-01-26) + os.Clearenv() + _ = os.Setenv("HOME", u.HomeDir) // nolint: gosec + _ = os.Setenv("TERM", ttype) // nolint: gosec + _ = os.Setenv("HKEXSH", "1") // nolint: gosec + + var c *exec.Cmd + if interactive { + 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("/bin/login", "-f", "-p", who) // nolint: gosec + } else { + c = exec.Command("/bin/bash", "-i", "-l") // nolint: gosec + } + } else { + c = exec.Command("/bin/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{} + 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} + } + c.Stdin = conn + c.Stdout = conn + c.Stderr = conn + + // Start the command with a pty. + ptmx, err := pty.Start(c) // returns immediately with ptmx file + if err != nil { + log.Println(err) + return hkexnet.CSEPtyExecFail, err + } + // Make sure to close the pty at the end. + // #gv:s/label=\"runShellAs\$1\"/label=\"deferPtmxClose\"/ + 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 hkexnet.CSEPtyGetNameFail, err + } + utmpx := goutmp.Put_utmp(who, pts, hname) + defer func() { goutmp.Unput_utmp(utmpx) }() + goutmp.Put_lastlog_entry("hkexsh", who, pts, hname) + + log.Printf("[%s]\n", cmd) + if err != nil { + log.Printf("Command finished with error: %v", err) + } else { + // Watch for term resizes + // #gv:s/label=\"runShellAs\$2\"/label=\"termResizeWatcher\"/ + go func() { + for sz := range conn.WinCh { + log.Printf("[Setting term size to: %v %v]\n", sz.Rows, sz.Cols) + pty.Setsize(ptmx, &pty.Winsize{Rows: sz.Rows, Cols: sz.Cols}) // nolint: gosec,errcheck + } + log.Println("*** WinCh goroutine done ***") + }() + + // Copy stdin to the pty.. (bgnd goroutine) + // #gv:s/label=\"runShellAs\$3\"/label=\"stdinToPtyWorker\"/ + go func() { + _, e := io.Copy(ptmx, conn) + if e != nil { + log.Println("** stdin->pty ended **:", e.Error()) + } else { + log.Println("*** stdin->pty goroutine done ***") + } + }() + + if chaffing { + conn.EnableChaff() + } + // #gv:s/label=\"runShellAs\$4\"/label=\"deferChaffShutdown\"/ + defer func() { + conn.DisableChaff() + conn.ShutdownChaff() + }() + + // ..and the pty to stdout. + // This may take some time exceeding that of the + // actual command's lifetime, so the c.Wait() below + // must synchronize with the completion of this goroutine + // to ensure all stdout data gets to the client before + // connection is closed. + wg.Add(1) + // #gv:s/label=\"runShellAs\$5\"/label=\"ptyToStdoutWorker\"/ + go func() { + defer wg.Done() + _, e := io.Copy(conn, ptmx) + if e != nil { + log.Println("** pty->stdout ended **:", e.Error()) + } else { + // The above io.Copy() will exit when the command attached + // to the pty exits + log.Println("*** pty->stdout goroutine done ***") + } + }() + + if err := c.Wait(); err != nil { + //fmt.Println("*** c.Wait() done ***") + if exiterr, ok := err.(*exec.ExitError); ok { + // The program has exited with an exit code != 0 + + // This works on both Unix and Windows. Although package + // syscall is generally platform dependent, WaitStatus is + // defined for both Unix and Windows and in both cases has + // 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) + } + } + conn.SetStatus(hkexnet.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 + } + return +} + +// GenAuthToken generates a pseudorandom auth token for a specific +// user from a specific host to allow non-interactive logins. +func GenAuthToken(who string, connhost string) string { + //tokenA, e := os.Hostname() + //if e != nil { + // tokenA = "badhost" + //} + tokenA := connhost + + tokenB := make([]byte, 64) + _, _ = rand.Read(tokenB) // nolint: gosec + 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. +// TODO: reduce gocyclo +func main() { + var vopt bool + var chaffEnabled bool + var chaffFreqMin uint + var chaffFreqMax uint + var chaffBytesMax uint + var dbg bool + var laddr string + + flag.BoolVar(&vopt, "v", false, "show version") + flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen") + 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(&dbg, "d", false, "debug logging") + flag.Parse() + + if vopt { + fmt.Printf("version %s (%s)\n", version, gitCommit) + os.Exit(0) + } + + { + me, e := user.Current() + if e != nil || me.Uid != "0" { + log.Fatal("Must run as root.") + } + } + + // 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, "hkexshd") // nolint: gosec + hkexnet.Init(dbg, "hkexshd", logger.LOG_DAEMON|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR) + if dbg { + log.SetOutput(Log) + } else { + log.SetOutput(ioutil.Discard) + } + + // 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)) + go func() { + for { + sig := <-exitCh + switch sig.String() { + case "terminated": + logger.LogNotice(fmt.Sprintf("[Got signal: %s]", sig)) // nolint: gosec,errcheck + signal.Reset() + syscall.Kill(0, syscall.SIGTERM) // nolint: gosec,errcheck + case "interrupt": + logger.LogNotice(fmt.Sprintf("[Got signal: %s]", sig)) // nolint: gosec,errcheck + signal.Reset() + syscall.Kill(0, syscall.SIGINT) // nolint: gosec,errcheck + case "hangup": + logger.LogNotice(fmt.Sprintf("[Got signal: %s - nop]", sig)) // nolint:gosec,errcheck + default: + logger.LogNotice(fmt.Sprintf("[Got signal: %s - ignored]", sig)) // nolint: gosec,errcheck + } + } + }() + + // Listen on TCP port 2000 on all available unicast and + // anycast IP addresses of the local system. + l, err := hkexnet.Listen("tcp", laddr) + if err != nil { + log.Fatal(err) + } + defer l.Close() // nolint: errcheck + + log.Println("Serving on", laddr) + for { + // Wait for a connection. + conn, err := l.Accept() + if err != nil { + log.Printf("Accept() got error(%v), hanging up.\n", err) + } else { + log.Println("Accepted client") + + // Set up chaffing to client + // Will only start when runShellAs() is called + // after stdin/stdout are hooked up + conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // configure server->client chaffing + + // 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) { + defer hc.Close() // nolint: errcheck + + //We use io.ReadFull() here to guarantee we consume + //just the data we want for the hkexsh.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 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) + + if err != nil || n < 6 { + log.Println("[Bad hkexsh.Session fmt]") + return err + } + + tmp := make([]byte, len1) + _, err = io.ReadFull(hc, tmp) + if err != nil { + log.Println("[Bad hkexsh.Session.Op]") + return err + } + rec.SetOp(tmp) + + tmp = make([]byte, len2) + _, err = io.ReadFull(hc, tmp) + if err != nil { + log.Println("[Bad hkexsh.Session.Who]") + return err + } + rec.SetWho(tmp) + + tmp = make([]byte, len3) + _, err = io.ReadFull(hc, tmp) + if err != nil { + log.Println("[Bad hkexsh.Session.ConnHost]") + return err + } + rec.SetConnHost(tmp) + + tmp = make([]byte, len4) + _, err = io.ReadFull(hc, tmp) + if err != nil { + log.Println("[Bad hkexsh.Session.TermType]") + return err + } + rec.SetTermType(tmp) + + tmp = make([]byte, len5) + _, err = io.ReadFull(hc, tmp) + if err != nil { + log.Println("[Bad hkexsh.Session.Cmd]") + return err + } + rec.SetCmd(tmp) + + tmp = make([]byte, len6) + _, err = io.ReadFull(hc, tmp) + if err != nil { + log.Println("[Bad hkexsh.Session.AuthCookie]") + return err + } + rec.SetAuthCookie(tmp) + + log.Printf("[hkexsh.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))) { + valid = true + } else { + valid, allowedCmds = hkexsh.AuthUserByPasswd(string(rec.Who()), string(rec.AuthCookie(true)), "/etc/hkexsh.passwd") + } + + // Security scrub + rec.ClearAuthCookie() + + // Tell client if auth was valid + if valid { + hc.Write([]byte{1}) // nolint: gosec,errcheck + } else { + logger.LogNotice(fmt.Sprintln("Invalid user", string(rec.Who()))) // nolint: errcheck,gosec + hc.Write([]byte{0}) // nolint: gosec,errcheck + return + } + + log.Printf("[allowedCmds:%s]\n", allowedCmds) + + if rec.Op()[0] == 'A' { + // Generate automated login token + addr := hc.RemoteAddr() + hname := goutmp.GetHost(addr.String()) + logger.LogNotice(fmt.Sprintf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck + token := GenAuthToken(string(rec.Who()), string(rec.ConnHost())) + tokenCmd := fmt.Sprintf("echo \"%s\" | tee -a ~/.hkexsh_id", token) + cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), tokenCmd, false, hc, chaffEnabled) + // Returned hopefully via an EOF or exit/logout; + // Clear current op so user can enter next, or EOF + rec.SetOp([]byte{0}) + if runErr != nil { + logger.LogErr(fmt.Sprintf("[Error generating autologin token for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck + } else { + log.Printf("[Autologin token generation completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus) + hc.SetStatus(hkexnet.CSOType(cmdStatus)) + } + } else if rec.Op()[0] == 'c' { + // Non-interactive command + addr := hc.RemoteAddr() + hname := goutmp.GetHost(addr.String()) + logger.LogNotice(fmt.Sprintf("[Running command for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck + cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), string(rec.Cmd()), false, hc, chaffEnabled) + // Returned hopefully via an EOF or exit/logout; + // Clear current op so user can enter next, or EOF + rec.SetOp([]byte{0}) + if runErr != nil { + logger.LogErr(fmt.Sprintf("[Error spawning cmd for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck + } else { + logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck + hc.SetStatus(hkexnet.CSOType(cmdStatus)) + } + } else if rec.Op()[0] == 's' { + // Interactive session + addr := hc.RemoteAddr() + hname := goutmp.GetHost(addr.String()) + logger.LogNotice(fmt.Sprintf("[Running shell for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck + + cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), string(rec.Cmd()), true, hc, chaffEnabled) + // Returned hopefully via an EOF or exit/logout; + // Clear current op so user can enter next, or EOF + rec.SetOp([]byte{0}) + if runErr != nil { + Log.Err(fmt.Sprintf("[Error spawning shell for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck + } else { + logger.LogNotice(fmt.Sprintf("[Shell completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck + hc.SetStatus(hkexnet.CSOType(cmdStatus)) + } + } else if rec.Op()[0] == 'D' { + // File copy (destination) operation - client copy to server + log.Printf("[Client->Server copy]\n") + addr := hc.RemoteAddr() + hname := goutmp.GetHost(addr.String()) + logger.LogNotice(fmt.Sprintf("[Running copy for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck + cmdStatus, runErr := runClientToServerCopyAs(string(rec.Who()), string(rec.TermType()), hc, string(rec.Cmd()), chaffEnabled) + // Returned hopefully via an EOF or exit/logout; + // Clear current op so user can enter next, or EOF + rec.SetOp([]byte{0}) + if runErr != nil { + logger.LogErr(fmt.Sprintf("[Error running cp 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)) + + // 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 + } else if rec.Op()[0] == 'S' { + // File copy (src) operation - server copy to client + log.Printf("[Server->Client copy]\n") + addr := hc.RemoteAddr() + hname := goutmp.GetHost(addr.String()) + logger.LogNotice(fmt.Sprintf("[Running copy for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck + cmdStatus, runErr := runServerToClientCopyAs(string(rec.Who()), string(rec.TermType()), hc, string(rec.Cmd()), chaffEnabled) + if runErr != nil { + logger.LogErr(fmt.Sprintf("[Error spawning cp for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck + } else { + // 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 + } + // Clear current op so user can enter next, or EOF + rec.SetOp([]byte{0}) + hc.SetStatus(hkexnet.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 + } + return + }(&conn) // nolint: errcheck + } // Accept() success + } //endfor + //logger.LogNotice(fmt.Sprintln("[Exiting]")) // nolint: gosec,errcheck +} diff --git a/logger/Makefile b/logger/Makefile index cc83cbb..0287ffe 100644 --- a/logger/Makefile +++ b/logger/Makefile @@ -2,14 +2,8 @@ EXE = $(notdir $(shell pwd)) -ifeq ($(GARBLE),y) -GO=garble -literals -tiny -debugdir=garbled -else -GO = go -endif - all: - $(GO) build . + go build . clean: $(RM) $(EXE) $(EXE).exe diff --git a/logger/logger_bsd.go b/logger/logger_bsd.go deleted file mode 100644 index 640a6a3..0000000 --- a/logger/logger_bsd.go +++ /dev/null @@ -1,156 +0,0 @@ -// +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 -} diff --git a/logger/logger_linux.go b/logger/logger_linux.go index 5e6f304..7c635fa 100644 --- a/logger/logger_linux.go +++ b/logger/logger_linux.go @@ -11,7 +11,6 @@ import ( // Priority is the logger priority type Priority = sl.Priority - // Writer is a syslog Writer type Writer = sl.Writer @@ -76,81 +75,50 @@ 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 } diff --git a/logger/logger_windows.go b/logger/logger_windows.go index 24dd40d..793e982 100644 --- a/logger/logger_windows.go +++ b/logger/logger_windows.go @@ -1,5 +1,5 @@ // +build windows - +// // Wrapper around UNIX syslog, so that it also may be wrapped // with something else for Windows. package logger diff --git a/session_test.go b/session_test.go deleted file mode 100644 index 0670ea8..0000000 --- a/session_test.go +++ /dev/null @@ -1,30 +0,0 @@ -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") - } -} diff --git a/spinsult/Makefile b/spinsult/Makefile index 555e232..f645a41 100644 --- a/spinsult/Makefile +++ b/spinsult/Makefile @@ -1,18 +1,11 @@ .PHONY: info clean lib -ifeq ($(GARBLE),y) -GO = garble -literals -tiny -debugdir=garbled -else -GO = go -endif - all: lib clean: go clean . lib: info - $(GO) build . go install . ifneq ($(MSYSTEM),) diff --git a/termmode_bsd.go b/termmode_bsd.go deleted file mode 100644 index 3c1d7fe..0000000 --- a/termmode_bsd.go +++ /dev/null @@ -1,128 +0,0 @@ -// +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 - } - } -} diff --git a/termmode_linux.go b/termmode_unix.go similarity index 78% rename from termmode_linux.go rename to termmode_unix.go index 466742b..e1b4cd1 100644 --- a/termmode_linux.go +++ b/termmode_unix.go @@ -1,6 +1,6 @@ // +build linux -package xs +package hkexsh import ( "errors" @@ -12,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, late 2019, core go stdlib hasn't yet done + * ssh, but as of Go v1.10, early 2018, core go stdlib hasn't yet done * the planned terminal lib reorgs.) * ------------- */ @@ -30,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 uintptr) (*State, error) { - termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios) +func MakeRaw(fd int) (*State, error) { + termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) if err != nil { return nil, err } @@ -47,7 +47,7 @@ func MakeRaw(fd uintptr) (*State, error) { termios.Cflag |= unix.CS8 termios.Cc[unix.VMIN] = 1 termios.Cc[unix.VTIME] = 0 - if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios); err != nil { + if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil { return nil, err } @@ -56,8 +56,8 @@ func MakeRaw(fd uintptr) (*State, error) { // GetState returns the current state of a terminal which may be useful to // restore the terminal after a signal. -func GetState(fd uintptr) (*State, error) { - termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios) +func GetState(fd int) (*State, error) { + termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) if err != nil { return nil, err } @@ -67,9 +67,9 @@ func GetState(fd uintptr) (*State, error) { // Restore restores the terminal connected to the given file descriptor to a // previous state. -func Restore(fd uintptr, state *State) error { +func Restore(fd int, state *State) error { if state != nil { - return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.termios) + return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios) } else { return errors.New("nil State") } @@ -78,8 +78,8 @@ func Restore(fd uintptr, 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 uintptr) ([]byte, error) { - termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios) +func ReadPassword(fd int) ([]byte, error) { + termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) if err != nil { return nil, err } @@ -88,12 +88,12 @@ func ReadPassword(fd uintptr) ([]byte, error) { newState.Lflag &^= unix.ECHO newState.Lflag |= unix.ICANON | unix.ISIG newState.Iflag |= unix.ICRNL - if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &newState); err != nil { + if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil { return nil, err } defer func() { - _ = unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios) // nolint: gosec + _ = unix.IoctlSetTermios(fd, ioctlWriteTermios, termios) // nolint: gosec }() return readPasswordLine(passwordReader(fd)) diff --git a/termmode_windows.go b/termmode_windows.go index c8931c9..6254ece 100644 --- a/termmode_windows.go +++ b/termmode_windows.go @@ -1,7 +1,7 @@ // +build windows - +// // Note the terminal manipulation functions herein are mostly stubs. They -// don't really do anything and the xs demo client depends on a wrapper +// don't really do anything and the hkexsh 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 xs +package hkexsh 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 uintptr) (*State, error) { +func MakeRaw(fd int) (*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 uintptr) (*State, error) { // GetState returns the current state of a terminal which may be useful to // restore the terminal after a signal. -func GetState(fd uintptr) (*State, error) { +func GetState(fd int) (*State, error) { return &State{}, nil } // Restore restores the terminal connected to the given file descriptor to a // previous state. -func Restore(fd uintptr, state *State) error { +func Restore(fd int, state *State) error { cmd := exec.Command("stty", "echo cooked") cmd.Run() return nil @@ -52,7 +52,7 @@ func Restore(fd uintptr, 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 uintptr) ([]byte, error) { +func ReadPassword(fd int) ([]byte, error) { return readPasswordLine(passwordReader(fd)) } diff --git a/viz_xs_dot.png b/viz_hkexsh_dot.png similarity index 100% rename from viz_xs_dot.png rename to viz_hkexsh_dot.png diff --git a/xc_testfiles.sh b/xc_testfiles.sh deleted file mode 100755 index 9fe5375..0000000 --- a/xc_testfiles.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/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 diff --git a/xs/Makefile b/xs/Makefile deleted file mode 100644 index 27d6356..0000000 --- a/xs/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -.PHONY: clean all vis lint - -ifeq ($(GARBLE),y) -GO = garble -literals -tiny -debugdir=garbled -else -GO = go -endif - -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 diff --git a/xs/xs-vis-fixedup.png b/xs/xs-vis-fixedup.png deleted file mode 100644 index 1526a41..0000000 Binary files a/xs/xs-vis-fixedup.png and /dev/null differ diff --git a/xs/xs-vis.gv b/xs/xs-vis.gv deleted file mode 100755 index c470a0e..0000000 --- a/xs/xs-vis.gv +++ /dev/null @@ -1,622 +0,0 @@ -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="box" style="filled,rounded" 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.restoreTermState" [ fillcolor="lightblue" label="restoreTermState" penwidth="0.5" tooltip="blitter.com/go/xs/xs.restoreTermState | defined in xs.go:1117\nat xs.go:1118: calling [blitter.com/go/xs.Restore]" ] - "blitter.com/go/xs/xs.main$2" [ fillcolor="lightblue" label="deferCloseChaff" style="dotted,filled" tooltip="blitter.com/go/xs/xs.main$2 | defined in xs.go:996\nat xs.go:999: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:997: calling [blitter.com/go/xs/xs.restoreTermState]" ] - "blitter.com/go/xs/xs.exitWithStatus" [ penwidth="0.5" tooltip="blitter.com/go/xs/xs.exitWithStatus | defined in xs.go:1122" fillcolor="lightblue" label="exitWithStatus" ] - "blitter.com/go/xs/xs.doCopyMode" [ fillcolor="lightblue" label="doCopyMode" penwidth="0.5" tooltip="blitter.com/go/xs/xs.doCopyMode | defined in xs.go:354\nat xs.go:435: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:438: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:473: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:475: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:430: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xs.go:444: calling [blitter.com/go/xs/xs.buildCmdRemoteToLocal]\nat xs.go:424: calling [(blitter.com/go/xs/xsnet.Conn).Read]\nat xs.go:356: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:441: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:417: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]\nat xs.go:364: calling [blitter.com/go/xs/xs.buildCmdLocalToRemote]" ] - "blitter.com/go/xs/xs.buildCmdLocalToRemote" [ tooltip="blitter.com/go/xs/xs.buildCmdLocalToRemote | defined in xs.go:270\nat xs.go:283: calling [blitter.com/go/xs.GetTool]\nat xs.go:311: calling [blitter.com/go/xs.GetTool]\nat xs.go:312: calling [blitter.com/go/xs.GetTool]\nat xs.go:335: calling [blitter.com/go/xs/xs.getTreeSizeSubCmd]" fillcolor="lightblue" label="buildCmdLocalToRemote" penwidth="0.5" ] - "blitter.com/go/xs/xs.getTreeSizeSubCmd" [ fillcolor="lightblue" label="getTreeSizeSubCmd" penwidth="0.5" tooltip="blitter.com/go/xs/xs.getTreeSizeSubCmd | defined in xs.go:342" ] - "blitter.com/go/xs/xs.buildCmdRemoteToLocal" [ fillcolor="lightblue" label="buildCmdRemoteToLocal" penwidth="0.5" tooltip="blitter.com/go/xs/xs.buildCmdRemoteToLocal | defined in xs.go:244\nat xs.go:256: calling [blitter.com/go/xs.GetTool]\nat xs.go:263: calling [blitter.com/go/xs.GetTool]" ] - "blitter.com/go/xs/xs.copyBuffer" [ fillcolor="lightblue" label="copyBuffer" penwidth="0.5" tooltip="blitter.com/go/xs/xs.copyBuffer | defined in xs.go:128\nat xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$2]\nat xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$3]\nat xs.go:193: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$1]" ] - "blitter.com/go/xs/xs.copyBuffer$1" [ fillcolor="lightblue" label="copyBuffer$1" style="dotted,filled" tooltip="blitter.com/go/xs/xs.copyBuffer$1 | defined in xs.go:144" ] - "blitter.com/go/xs/xs.copyBuffer$2" [ fillcolor="lightblue" label="copyBuffer$2" style="dotted,filled" tooltip="blitter.com/go/xs/xs.copyBuffer$2 | defined in xs.go:145" ] - "blitter.com/go/xs/xs.copyBuffer$3" [ fillcolor="lightblue" label="copyBuffer$3" style="dotted,filled" tooltip="blitter.com/go/xs/xs.copyBuffer$3 | defined in xs.go:146" ] - "blitter.com/go/xs/xs.reqTunnel" [ tooltip="blitter.com/go/xs/xs.reqTunnel | defined in xs.go:584\nat xs.go:594: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]\nat xs.go:593: calling [blitter.com/go/xs/logger.LogDebug]" fillcolor="lightblue" label="reqTunnel" penwidth="0.5" ] - "blitter.com/go/xs/xs.main" [ fillcolor="lightblue" label="main" penwidth="0.5" tooltip="blitter.com/go/xs/xs.main | defined in xs.go:680\nat xs.go:1056: calling [(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff]\nat xs.go:1027: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1089: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1091: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1101: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:864: calling [blitter.com/go/xs/logger.New]\nat xs.go:978: calling [blitter.com/go/xs.MakeRaw]\nat xs.go:1075: calling [blitter.com/go/xs/xs.main$3]\nat xs.go:1039: calling [(blitter.com/go/xs/xsnet.Conn).Read]\nat xs.go:960: calling [blitter.com/go/xs/xsnet.Dial]\nat xs.go:1081: calling [blitter.com/go/xs/xs.launchTuns]\nat xs.go:977: calling [github.com/mattn/go-isatty.IsTerminal]\nat xs.go:772: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:853: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:963: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:1027: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:1101: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:1082: calling [blitter.com/go/xs/xs.doShellMode]\nat xs.go:1050: calling [(*blitter.com/go/xs/xsnet.Conn).SetupChaff]\nat xs.go:1022: calling [blitter.com/go/xs/xs.sendSessionParams]\nat xs.go:796: calling [blitter.com/go/xs/xs.parseNonSwitchArgs]\nat xs.go:1024: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1090: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1096: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1055: calling [(*blitter.com/go/xs/xsnet.Conn).DisableChaff]\nat xs.go:1025: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1043: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1047: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1086: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:852: calling [blitter.com/go/xs/xs.usageShell]\nat xs.go:984: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1005: calling [blitter.com/go/xs.ReadPassword]\nat xs.go:1054: calling [(*blitter.com/go/xs/xsnet.Conn).EnableChaff]\nat xs.go:1085: calling [blitter.com/go/xs/xs.doCopyMode]\nat xs.go:972: calling [(*blitter.com/go/xs/xsnet.Conn).Close]\nat xs.go:1046: calling [blitter.com/go/xs/xs.rejectUserMsg]\nat xs.go:852: calling [blitter.com/go/xs/xs.usageCp]\nat xs.go:790: calling [blitter.com/go/xs/xs.main$1]\nat xs.go:803: calling [blitter.com/go/xs/xs.localUserName]\nat xs.go:865: calling [blitter.com/go/xs/xsnet.Init]\nat xs.go:1021: calling [blitter.com/go/xs.NewSession]" ] - "blitter.com/go/xs/xs.parseNonSwitchArgs" [ label="parseNonSwitchArgs" penwidth="0.5" tooltip="blitter.com/go/xs/xs.parseNonSwitchArgs | defined in xs.go:599" fillcolor="lightblue" ] - "blitter.com/go/xs/xs.main$1" [ fillcolor="lightblue" label="deferRestore" style="dotted,filled" tooltip="blitter.com/go/xs/xs.main$1 | defined in xs.go:790" ] - "blitter.com/go/xs/xs.localUserName" [ fillcolor="lightblue" label="localUserName" penwidth="0.5" tooltip="blitter.com/go/xs/xs.localUserName | defined in xs.go:1106" ] - "blitter.com/go/xs/xs.sendSessionParams" [ fillcolor="lightblue" label="sendSessionParams" penwidth="0.5" tooltip="blitter.com/go/xs/xs.sendSessionParams | defined in xs.go:649\nat xs.go:651: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:671: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:651: calling [(blitter.com/go/xs.Session).AuthCookie]\nat xs.go:675: calling [(blitter.com/go/xs.Session).AuthCookie]\nat xs.go:651: calling [(blitter.com/go/xs.Session).Who]\nat xs.go:659: calling [(blitter.com/go/xs.Session).Who]\nat xs.go:651: calling [(blitter.com/go/xs.Session).Op]\nat xs.go:655: calling [(blitter.com/go/xs.Session).Op]\nat xs.go:651: calling [(blitter.com/go/xs.Session).TermType]\nat xs.go:667: calling [(blitter.com/go/xs.Session).TermType]\nat xs.go:651: calling [(blitter.com/go/xs.Session).ConnHost]\nat xs.go:663: calling [(blitter.com/go/xs.Session).ConnHost]\nat xs.go:675: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:671: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:667: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:663: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:659: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:655: calling [(blitter.com/go/xs/xsnet.Conn).Write]" ] - "blitter.com/go/xs/xs.rejectUserMsg" [ fillcolor="lightblue" label="rejectUserMsg" penwidth="0.5" tooltip="blitter.com/go/xs/xs.rejectUserMsg | defined in xs.go:576\nat xs.go:577: calling [blitter.com/go/xs/spinsult.GetSentence]" ] - "blitter.com/go/xs/xs.main$3" [ fillcolor="lightblue" label="main$3" style="dotted,filled" tooltip="blitter.com/go/xs/xs.main$3 | defined in xs.go:1065\nat xs.go:1068: calling [math/rand.Intn]\nat xs.go:1072: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" ] - "blitter.com/go/xs/xs.launchTuns" [ fillcolor="lightblue" label="launchTuns" penwidth="0.5" tooltip="blitter.com/go/xs/xs.launchTuns | defined in xs.go:634\nat xs.go:645: calling [blitter.com/go/xs/xs.reqTunnel]" ] - "blitter.com/go/xs/xs.doShellMode$1" [ tooltip="blitter.com/go/xs/xs.doShellMode$1 | defined in xs.go:490\nat xs.go:509: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:519: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:507: calling [(crypto/x509.SystemRootsError).Error]\nat xs.go:513: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:507: calling [(crypto/x509.CertificateInvalidError).Error]\nat xs.go:507: calling [(vendor/golang.org/x/net/idna.labelError).Error]\nat xs.go:507: calling [(crypto/tls.RecordHeaderError).Error]\nat xs.go:507: calling [(crypto/x509.UnknownAuthorityError).Error]\nat xs.go:513: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:507: calling [(crypto/tls.alert).Error]\nat xs.go:507: calling [(context.deadlineExceededError).Error]\nat xs.go:507: calling [(crypto/aes.KeySizeError).Error]\nat xs.go:507: calling [(crypto/x509.UnhandledCriticalExtension).Error]\nat xs.go:507: calling [(compress/flate.CorruptInputError).Error]\nat xs.go:507: calling [(*github.com/pkg/errors.fundamental).Error]\nat xs.go:491: calling [blitter.com/go/xs/xs.doShellMode$1$1]\nat xs.go:507: calling [(crypto/x509.HostnameError).Error]\nat xs.go:507: calling [(*crypto/tls.permanentError).Error]\nat xs.go:507: calling [(vendor/golang.org/x/net/idna.runeError).Error]\nat xs.go:514: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:519: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:503: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:518: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:507: calling [(compress/flate.InternalError).Error]" fillcolor="lightblue" label="shellRemoteToStdin" style="dotted,filled" ] - "blitter.com/go/xs/xs.doShellMode$1$1" [ fillcolor="lightblue" label="doShellMode$1$1" style="dotted,filled" tooltip="blitter.com/go/xs/xs.doShellMode$1$1 | defined in xs.go:491" ] - "blitter.com/go/xs/xs.doShellMode" [ fillcolor="lightblue" label="doShellMode" penwidth="0.5" tooltip="blitter.com/go/xs/xs.doShellMode | defined in xs.go:483\nat xs.go:522: calling [blitter.com/go/xs/xs.doShellMode$1]\nat xs.go:551: calling [blitter.com/go/xs/xs.doShellMode$2]\nat xs.go:527: calling [blitter.com/go/xs/xs.handleTermResizes]" ] - "blitter.com/go/xs/xs.handleTermResizes$1" [ tooltip="blitter.com/go/xs/xs.handleTermResizes$1 | defined in termsize_unix.go:21\nat termsize_unix.go:33: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]\nat termsize_unix.go:27: calling [blitter.com/go/xs/xs.GetSize]" fillcolor="lightblue" label="handleTermResizes$1" style="dotted,filled" ] - "blitter.com/go/xs/xs.GetSize" [ penwidth="1.5" tooltip="blitter.com/go/xs/xs.GetSize | defined in xs.go:221" fillcolor="lightblue" label="GetSize" ] - "blitter.com/go/xs/xs.handleTermResizes" [ fillcolor="lightblue" label="handleTermResizes" penwidth="0.5" tooltip="blitter.com/go/xs/xs.handleTermResizes | defined in termsize_unix.go:16\nat termsize_unix.go:21: calling [blitter.com/go/xs/xs.handleTermResizes$1]" ] - "blitter.com/go/xs/xs.Copy" [ tooltip="blitter.com/go/xs/xs.Copy | defined in xs.go:115\nat xs.go:116: calling [blitter.com/go/xs/xs.copyBuffer]" fillcolor="lightblue" label="Copy" penwidth="1.5" ] - "blitter.com/go/xs/xs.doShellMode$2$1" [ fillcolor="lightblue" label="doShellMode$2$1" style="dotted,filled" tooltip="blitter.com/go/xs/xs.doShellMode$2$1 | defined in xs.go:536\nat xs.go:539: calling [blitter.com/go/xs/xs.Copy]" ] - "blitter.com/go/xs/xs.doShellMode$2" [ style="dotted,filled" tooltip="blitter.com/go/xs/xs.doShellMode$2 | defined in xs.go:534\nat xs.go:546: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:548: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:541: calling [blitter.com/go/xs/xs.doShellMode$2$1]" fillcolor="lightblue" label="shellStdinToRemote" ] - "blitter.com/go/xs/xs.usageShell" [ fillcolor="lightblue" label="usageShell" penwidth="0.5" tooltip="blitter.com/go/xs/xs.usageShell | defined in xs.go:559" ] - "blitter.com/go/xs/xs.usageCp" [ label="usageCp" penwidth="0.5" tooltip="blitter.com/go/xs/xs.usageCp | defined in xs.go:565" fillcolor="lightblue" ] - - subgraph "cluster_blitter.com/go/xs" { - style="filled"; -rank="sink"; -tooltip="package: blitter.com/go/xs"; -label="xs"; -URL="/?f=blitter.com/go/xs"; -penwidth="0.8"; -fontsize="16"; -fillcolor="lightyellow"; -fontname="Tahoma bold"; - - "blitter.com/go/xs.Restore" [ tooltip="blitter.com/go/xs.Restore | defined in termmode_linux.go:70" fillcolor="moccasin" label="Restore" penwidth="1.5" ] - "blitter.com/go/xs.GetTool" [ fillcolor="moccasin" label="GetTool" penwidth="1.5" tooltip="blitter.com/go/xs.GetTool | defined in auth.go:217" ] - "blitter.com/go/xs.MakeRaw" [ fillcolor="moccasin" label="MakeRaw" penwidth="1.5" tooltip="blitter.com/go/xs.MakeRaw | defined in termmode_linux.go:33" ] - "blitter.com/go/xs.ReadPassword" [ tooltip="blitter.com/go/xs.ReadPassword | defined in termmode_linux.go:81" fillcolor="moccasin" label="ReadPassword" penwidth="1.5" ] - "blitter.com/go/xs.NewSession" [ fillcolor="moccasin" label="NewSession" penwidth="1.5" tooltip="blitter.com/go/xs.NewSession | defined in session.go:130" ] - - 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).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" tooltip="(*blitter.com/go/xs.Session).SetStatus | defined in session.go:125" ] - - } - - subgraph "cluster_blitter.com/go/xs.Session" { - penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(Session)"; -tooltip="type: blitter.com/go/xs.Session"; - - "(blitter.com/go/xs.Session).Cmd" [ fillcolor="moccasin" label="Cmd" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).Cmd | defined in session.go:79" ] - "(blitter.com/go/xs.Session).Op" [ fillcolor="moccasin" label="Op" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).Op | defined in session.go:36" ] - "(blitter.com/go/xs.Session).Who" [ fillcolor="moccasin" label="Who" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).Who | defined in session.go:46" ] - "(blitter.com/go/xs.Session).ConnHost" [ label="ConnHost" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).ConnHost | defined in session.go:56" fillcolor="moccasin" ] - "(blitter.com/go/xs.Session).TermType" [ fillcolor="moccasin" label="TermType" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).TermType | defined in session.go:67" ] - "(blitter.com/go/xs.Session).AuthCookie" [ fillcolor="moccasin" label="AuthCookie" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).AuthCookie | defined in session.go:92" ] - "(blitter.com/go/xs.Session).Status" [ penwidth="1.5" tooltip="(blitter.com/go/xs.Session).Status | defined in session.go:120" fillcolor="moccasin" label="Status" ] - - } - - } - - subgraph "cluster_blitter.com/go/xs/logger" { - penwidth="0.8"; -fontsize="16"; -fontname="Tahoma bold"; -URL="/?f=blitter.com/go/xs/logger"; -tooltip="package: blitter.com/go/xs/logger"; -style="filled"; -fillcolor="lightyellow"; -rank="sink"; -label="logger"; - - "blitter.com/go/xs/logger.LogDebug" [ fillcolor="moccasin" label="LogDebug" penwidth="1.5" tooltip="blitter.com/go/xs/logger.LogDebug | defined in logger_linux.go:103" ] - "blitter.com/go/xs/logger.New" [ fillcolor="moccasin" label="New" penwidth="1.5" tooltip="blitter.com/go/xs/logger.New | defined in logger_linux.go:71" ] - - } - - subgraph "cluster_blitter.com/go/xs/spinsult" { - penwidth="0.8"; -style="filled"; -fontname="Tahoma bold"; -rank="sink"; -fontsize="16"; -fillcolor="lightyellow"; -label="spinsult"; -URL="/?f=blitter.com/go/xs/spinsult"; -tooltip="package: blitter.com/go/xs/spinsult"; - - "blitter.com/go/xs/spinsult.GetSentence" [ label="GetSentence" penwidth="1.5" tooltip="blitter.com/go/xs/spinsult.GetSentence | defined in spinsult.go:43" fillcolor="moccasin" ] - - } - - subgraph "cluster_blitter.com/go/xs/xsnet" { - fontname="Tahoma bold"; -rank="sink"; -label="xsnet"; -tooltip="package: blitter.com/go/xs/xsnet"; -penwidth="0.8"; -fontsize="16"; -style="filled"; -fillcolor="lightyellow"; -URL="/?f=blitter.com/go/xs/xsnet"; - - "blitter.com/go/xs/xsnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" tooltip="blitter.com/go/xs/xsnet.Init | defined in net.go:198" ] - "blitter.com/go/xs/xsnet.Dial" [ fillcolor="moccasin" label="Dial" penwidth="1.5" tooltip="blitter.com/go/xs/xsnet.Dial | defined in net.go:899" ] - - 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" [ fillcolor="moccasin" label="WritePacket" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).WritePacket | defined in net.go:1437" ] - "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ label="SetStatus" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).SetStatus | defined in net.go:218" fillcolor="moccasin" ] - "(*blitter.com/go/xs/xsnet.Conn).GetStatus" [ fillcolor="moccasin" label="GetStatus" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).GetStatus | defined in net.go:214" ] - "(*blitter.com/go/xs/xsnet.Conn).Close" [ fillcolor="moccasin" label="Close" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).Close | defined in net.go:979" ] - "(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).SetupChaff | defined in net.go:1563" fillcolor="moccasin" label="SetupChaff" ] - "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ fillcolor="moccasin" label="EnableChaff" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).EnableChaff | defined in net.go:1546" ] - "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ fillcolor="moccasin" label="DisableChaff" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).DisableChaff | defined in net.go:1553" ] - "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ tooltip="(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff | defined in net.go:1558" fillcolor="moccasin" label="ShutdownChaff" 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).Read" [ penwidth="1.5" tooltip="(blitter.com/go/xs/xsnet.Conn).Read | defined in net.go:1192" fillcolor="moccasin" label="Read" ] - "(blitter.com/go/xs/xsnet.Conn).Write" [ fillcolor="moccasin" label="Write" penwidth="1.5" tooltip="(blitter.com/go/xs/xsnet.Conn).Write | defined in net.go:1431" ] - - } - - } - - subgraph "cluster_compress/flate" { - fontsize="16"; -style="filled"; -fillcolor="lightyellow"; -label="flate"; -URL="/?f=compress/flate"; -penwidth="0.8"; -fontname="Tahoma bold"; -rank="sink"; -tooltip="package: compress/flate"; - - - subgraph "cluster_compress/flate.CorruptInputError" { - fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(CorruptInputError)"; -tooltip="type: compress/flate.CorruptInputError"; -penwidth="0.5"; -fontsize="15"; - - "(compress/flate.CorruptInputError).Error" [ penwidth="1.5" tooltip="(compress/flate.CorruptInputError).Error | defined in inflate.go:35" fillcolor="moccasin" label="Error" ] - - } - - subgraph "cluster_compress/flate.InternalError" { - tooltip="type: compress/flate.InternalError"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(InternalError)"; - - "(compress/flate.InternalError).Error" [ tooltip="(compress/flate.InternalError).Error | defined in inflate.go:42" fillcolor="moccasin" label="Error" penwidth="1.5" ] - - } - - } - - subgraph "cluster_context" { - fontsize="16"; -style="filled"; -label="context"; -penwidth="0.8"; -fillcolor="lightyellow"; -fontname="Tahoma bold"; -rank="sink"; -URL="/?f=context"; -tooltip="package: context"; - - - subgraph "cluster_context.deadlineExceededError" { - style="rounded,filled"; -fillcolor="wheat2"; -label="(deadlineExceededError)"; -tooltip="type: context.deadlineExceededError"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; - - "(context.deadlineExceededError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(context.deadlineExceededError).Error | defined in context.go:165" ] - - } - - } - - subgraph "cluster_crypto/aes" { - penwidth="0.8"; -style="filled"; -fontname="Tahoma bold"; -rank="sink"; -label="aes"; -URL="/?f=crypto/aes"; -tooltip="package: crypto/aes"; -fontsize="16"; -fillcolor="lightyellow"; - - - subgraph "cluster_crypto/aes.KeySizeError" { - style="rounded,filled"; -fillcolor="wheat2"; -label="(KeySizeError)"; -tooltip="type: crypto/aes.KeySizeError"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; - - "(crypto/aes.KeySizeError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/aes.KeySizeError).Error | defined in cipher.go:24" ] - - } - - } - - subgraph "cluster_crypto/tls" { - penwidth="0.8"; -fontsize="16"; -fontname="Tahoma bold"; -URL="/?f=crypto/tls"; -tooltip="package: crypto/tls"; -style="filled"; -fillcolor="lightyellow"; -rank="sink"; -label="tls"; - - - subgraph "cluster_*crypto/tls.permanentError" { - label="(*permanentError)"; -tooltip="type: *crypto/tls.permanentError"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; - - "(*crypto/tls.permanentError).Error" [ tooltip="(*crypto/tls.permanentError).Error | defined in conn.go:184" fillcolor="moccasin" label="Error" penwidth="1.5" ] - - } - - subgraph "cluster_crypto/tls.RecordHeaderError" { - fillcolor="wheat2"; -label="(RecordHeaderError)"; -tooltip="type: crypto/tls.RecordHeaderError"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; - - "(crypto/tls.RecordHeaderError).Error" [ tooltip="(crypto/tls.RecordHeaderError).Error | defined in conn.go:571" fillcolor="moccasin" label="Error" penwidth="1.5" ] - - } - - subgraph "cluster_crypto/tls.alert" { - fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(alert)"; -tooltip="type: crypto/tls.alert"; -penwidth="0.5"; -fontsize="15"; - - "(crypto/tls.alert).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/tls.alert).Error | defined in alert.go:97" ] - - } - - } - - subgraph "cluster_crypto/x509" { - URL="/?f=crypto/x509"; -style="filled"; -fillcolor="lightyellow"; -fontname="Tahoma bold"; -rank="sink"; -label="x509"; -tooltip="package: crypto/x509"; -penwidth="0.8"; -fontsize="16"; - - - subgraph "cluster_crypto/x509.CertificateInvalidError" { - label="(CertificateInvalidError)"; -tooltip="type: crypto/x509.CertificateInvalidError"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; - - "(crypto/x509.CertificateInvalidError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/x509.CertificateInvalidError).Error | defined in verify.go:67" ] - - } - - subgraph "cluster_crypto/x509.HostnameError" { - tooltip="type: crypto/x509.HostnameError"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(HostnameError)"; - - "(crypto/x509.HostnameError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(crypto/x509.HostnameError).Error | defined in verify.go:98" ] - - } - - subgraph "cluster_crypto/x509.SystemRootsError" { - tooltip="type: crypto/x509.SystemRootsError"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(SystemRootsError)"; - - "(crypto/x509.SystemRootsError).Error" [ label="Error" penwidth="1.5" tooltip="(crypto/x509.SystemRootsError).Error | defined in verify.go:159" fillcolor="moccasin" ] - - } - - subgraph "cluster_crypto/x509.UnhandledCriticalExtension" { - label="(UnhandledCriticalExtension)"; -tooltip="type: crypto/x509.UnhandledCriticalExtension"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; - - "(crypto/x509.UnhandledCriticalExtension).Error" [ label="Error" penwidth="1.5" tooltip="(crypto/x509.UnhandledCriticalExtension).Error | defined in x509.go:893" fillcolor="moccasin" ] - - } - - subgraph "cluster_crypto/x509.UnknownAuthorityError" { - fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(UnknownAuthorityError)"; -tooltip="type: crypto/x509.UnknownAuthorityError"; -penwidth="0.5"; - - "(crypto/x509.UnknownAuthorityError).Error" [ tooltip="(crypto/x509.UnknownAuthorityError).Error | defined in verify.go:138" fillcolor="moccasin" label="Error" penwidth="1.5" ] - - } - - } - - subgraph "cluster_github.com/mattn/go-isatty" { - penwidth="0.8"; -fontsize="16"; -fontname="Tahoma bold"; -rank="sink"; -style="filled"; -fillcolor="lightyellow"; -label="isatty"; -URL="/?f=github.com/mattn/go-isatty"; -tooltip="package: github.com/mattn/go-isatty"; - - "github.com/mattn/go-isatty.IsTerminal" [ penwidth="1.5" tooltip="github.com/mattn/go-isatty.IsTerminal | defined in isatty_tcgets.go:10" fillcolor="moccasin" label="IsTerminal" ] - - } - - subgraph "cluster_github.com/pkg/errors" { - fontname="Tahoma bold"; -label="errors"; -URL="/?f=github.com/pkg/errors"; -tooltip="package: github.com/pkg/errors"; -rank="sink"; -penwidth="0.8"; -fontsize="16"; -style="filled"; -fillcolor="lightyellow"; - - - 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" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(*github.com/pkg/errors.fundamental).Error | defined in errors.go:125" ] - - } - - } - - subgraph "cluster_math/rand" { - style="filled"; -fillcolor="lightyellow"; -rank="sink"; -URL="/?f=math/rand"; -tooltip="package: math/rand"; -penwidth="0.8"; -fontsize="16"; -fontname="Tahoma bold"; -label="rand"; - - "math/rand.Intn" [ fillcolor="moccasin" label="Intn" penwidth="1.5" tooltip="math/rand.Intn | defined in rand.go:337" ] - - } - - subgraph "cluster_vendor/golang.org/x/net/idna" { - URL="/?f=vendor/golang.org/x/net/idna"; -penwidth="0.8"; -fontname="Tahoma bold"; -fillcolor="lightyellow"; -rank="sink"; -label="idna"; -tooltip="package: vendor/golang.org/x/net/idna"; -fontsize="16"; -style="filled"; - - - subgraph "cluster_vendor/golang.org/x/net/idna.labelError" { - style="rounded,filled"; -fillcolor="wheat2"; -label="(labelError)"; -tooltip="type: vendor/golang.org/x/net/idna.labelError"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; - - "(vendor/golang.org/x/net/idna.labelError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(vendor/golang.org/x/net/idna.labelError).Error | defined in idna10.0.0.go:324" ] - - } - - subgraph "cluster_vendor/golang.org/x/net/idna.runeError" { - style="rounded,filled"; -fillcolor="wheat2"; -label="(runeError)"; -tooltip="type: vendor/golang.org/x/net/idna.runeError"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; - - "(vendor/golang.org/x/net/idna.runeError).Error" [ tooltip="(vendor/golang.org/x/net/idna.runeError).Error | defined in idna10.0.0.go:331" fillcolor="moccasin" label="Error" penwidth="1.5" ] - - } - - } - - } - - "blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs/xs.exitWithStatus" [ tooltip="at xs.go:509: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:519: calling [blitter.com/go/xs/xs.exitWithStatus]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.SystemRootsError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/x509.SystemRootsError).Error]" ] - "blitter.com/go/xs/xs.main$2" -> "blitter.com/go/xs/xs.exitWithStatus" [ tooltip="at xs.go:999: calling [blitter.com/go/xs/xs.exitWithStatus]" ] - "blitter.com/go/xs/xs.doCopyMode" -> "(*blitter.com/go/xs/xsnet.Conn).GetStatus" [ color="saddlebrown" tooltip="at xs.go:435: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:438: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:473: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]\nat xs.go:475: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]" ] - "blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" tooltip="at xs.go:1056: calling [(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(*blitter.com/go/xs.Session).SetStatus" [ color="saddlebrown" tooltip="at xs.go:513: calling [(*blitter.com/go/xs.Session).SetStatus]" ] - "blitter.com/go/xs/xs.doShellMode" -> "blitter.com/go/xs/xs.doShellMode$1" [ arrowhead="normalnoneodot" tooltip="at xs.go:522: calling [blitter.com/go/xs/xs.doShellMode$1]" ] - "blitter.com/go/xs/xs.main$2" -> "blitter.com/go/xs/xs.restoreTermState" [ tooltip="at xs.go:997: calling [blitter.com/go/xs/xs.restoreTermState]" ] - "blitter.com/go/xs/xs.doCopyMode" -> "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ color="saddlebrown" tooltip="at xs.go:430: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]" ] - "blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Cmd" [ tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:671: calling [(blitter.com/go/xs.Session).Cmd]" color="saddlebrown" ] - "blitter.com/go/xs/xs.main" -> "(blitter.com/go/xs.Session).Status" [ color="saddlebrown" tooltip="at xs.go:1027: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1089: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1091: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:1101: calling [(blitter.com/go/xs.Session).Status]" ] - "blitter.com/go/xs/xs.main$3" -> "math/rand.Intn" [ tooltip="at xs.go:1068: calling [math/rand.Intn]" color="saddlebrown" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.CertificateInvalidError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/x509.CertificateInvalidError).Error]" ] - "blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs/xs.restoreTermState" [ tooltip="at xs.go:546: calling [blitter.com/go/xs/xs.restoreTermState]" ] - "blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs/xs.exitWithStatus" [ tooltip="at xs.go:548: calling [blitter.com/go/xs/xs.exitWithStatus]" ] - "blitter.com/go/xs/xs.restoreTermState" -> "blitter.com/go/xs.Restore" [ color="saddlebrown" tooltip="at xs.go:1118: calling [blitter.com/go/xs.Restore]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/logger.New" [ color="saddlebrown" tooltip="at xs.go:864: calling [blitter.com/go/xs/logger.New]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.MakeRaw" [ color="saddlebrown" tooltip="at xs.go:978: calling [blitter.com/go/xs.MakeRaw]" ] - "blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).AuthCookie" [ color="saddlebrown" tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).AuthCookie]\nat xs.go:675: calling [(blitter.com/go/xs.Session).AuthCookie]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.main$3" [ arrowhead="normalnoneodot" tooltip="at xs.go:1075: calling [blitter.com/go/xs/xs.main$3]" ] - "blitter.com/go/xs/xs.doCopyMode" -> "blitter.com/go/xs/xs.buildCmdRemoteToLocal" [ tooltip="at xs.go:444: calling [blitter.com/go/xs/xs.buildCmdRemoteToLocal]" ] - "blitter.com/go/xs/xs.doCopyMode" -> "(blitter.com/go/xs/xsnet.Conn).Read" [ color="saddlebrown" tooltip="at xs.go:424: calling [(blitter.com/go/xs/xsnet.Conn).Read]" ] - "blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Who" [ tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).Who]\nat xs.go:659: calling [(blitter.com/go/xs.Session).Who]" color="saddlebrown" ] - "blitter.com/go/xs/xs.main" -> "(blitter.com/go/xs/xsnet.Conn).Read" [ color="saddlebrown" tooltip="at xs.go:1039: calling [(blitter.com/go/xs/xsnet.Conn).Read]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xsnet.Dial" [ color="saddlebrown" tooltip="at xs.go:960: calling [blitter.com/go/xs/xsnet.Dial]" ] - "blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).Op" [ color="saddlebrown" tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).Op]\nat xs.go:655: calling [(blitter.com/go/xs.Session).Op]" ] - "blitter.com/go/xs/xs.rejectUserMsg" -> "blitter.com/go/xs/spinsult.GetSentence" [ color="saddlebrown" tooltip="at xs.go:577: calling [blitter.com/go/xs/spinsult.GetSentence]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.launchTuns" [ tooltip="at xs.go:1081: calling [blitter.com/go/xs/xs.launchTuns]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(vendor/golang.org/x/net/idna.labelError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(vendor/golang.org/x/net/idna.labelError).Error]" ] - "blitter.com/go/xs/xs.Copy" -> "blitter.com/go/xs/xs.copyBuffer" [ tooltip="at xs.go:116: calling [blitter.com/go/xs/xs.copyBuffer]" ] - "blitter.com/go/xs/xs.main" -> "github.com/mattn/go-isatty.IsTerminal" [ color="saddlebrown" tooltip="at xs.go:977: calling [github.com/mattn/go-isatty.IsTerminal]" ] - "blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).TermType" [ color="saddlebrown" tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).TermType]\nat xs.go:667: calling [(blitter.com/go/xs.Session).TermType]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/tls.RecordHeaderError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/tls.RecordHeaderError).Error]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.UnknownAuthorityError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/x509.UnknownAuthorityError).Error]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.exitWithStatus" [ tooltip="at xs.go:772: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:853: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:963: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:1027: calling [blitter.com/go/xs/xs.exitWithStatus]\nat xs.go:1101: calling [blitter.com/go/xs/xs.exitWithStatus]" ] - "blitter.com/go/xs/xs.doShellMode$2" -> "blitter.com/go/xs/xs.doShellMode$2$1" [ tooltip="at xs.go:541: calling [blitter.com/go/xs/xs.doShellMode$2$1]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.doShellMode" [ tooltip="at xs.go:1082: calling [blitter.com/go/xs/xs.doShellMode]" ] - "blitter.com/go/xs/xs.doShellMode" -> "blitter.com/go/xs/xs.doShellMode$2" [ arrowhead="normalnoneodot" tooltip="at xs.go:551: calling [blitter.com/go/xs/xs.doShellMode$2]" ] - "blitter.com/go/xs/xs.buildCmdLocalToRemote" -> "blitter.com/go/xs.GetTool" [ color="saddlebrown" tooltip="at xs.go:283: calling [blitter.com/go/xs.GetTool]\nat xs.go:311: calling [blitter.com/go/xs.GetTool]\nat xs.go:312: calling [blitter.com/go/xs.GetTool]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(*blitter.com/go/xs/xsnet.Conn).GetStatus" [ color="saddlebrown" tooltip="at xs.go:513: calling [(*blitter.com/go/xs/xsnet.Conn).GetStatus]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/tls.alert).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/tls.alert).Error]" ] - "blitter.com/go/xs/xs.doShellMode" -> "blitter.com/go/xs/xs.handleTermResizes" [ tooltip="at xs.go:527: calling [blitter.com/go/xs/xs.handleTermResizes]" ] - "blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ color="saddlebrown" tooltip="at xs.go:1050: calling [(*blitter.com/go/xs/xsnet.Conn).SetupChaff]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(context.deadlineExceededError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(context.deadlineExceededError).Error]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/aes.KeySizeError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/aes.KeySizeError).Error]" ] - "blitter.com/go/xs/xs.doShellMode$2$1" -> "blitter.com/go/xs/xs.Copy" [ tooltip="at xs.go:539: calling [blitter.com/go/xs/xs.Copy]" ] - "blitter.com/go/xs/xs.doCopyMode" -> "(blitter.com/go/xs.Session).Cmd" [ color="saddlebrown" tooltip="at xs.go:356: calling [(blitter.com/go/xs.Session).Cmd]\nat xs.go:441: calling [(blitter.com/go/xs.Session).Cmd]" ] - "blitter.com/go/xs/xs.doCopyMode" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ tooltip="at xs.go:417: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" color="saddlebrown" ] - "blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs.Session).ConnHost" [ color="saddlebrown" tooltip="at xs.go:651: calling [(blitter.com/go/xs.Session).ConnHost]\nat xs.go:663: calling [(blitter.com/go/xs.Session).ConnHost]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.sendSessionParams" [ tooltip="at xs.go:1022: calling [blitter.com/go/xs/xs.sendSessionParams]" ] - "blitter.com/go/xs/xs.doCopyMode" -> "blitter.com/go/xs/xs.buildCmdLocalToRemote" [ tooltip="at xs.go:364: calling [blitter.com/go/xs/xs.buildCmdLocalToRemote]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.parseNonSwitchArgs" [ tooltip="at xs.go:796: calling [blitter.com/go/xs/xs.parseNonSwitchArgs]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.restoreTermState" [ tooltip="at xs.go:1024: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1090: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:1096: calling [blitter.com/go/xs/xs.restoreTermState]" ] - "blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ color="saddlebrown" tooltip="at xs.go:1055: calling [(*blitter.com/go/xs/xsnet.Conn).DisableChaff]" arrowhead="normalnoneodiamond" ] - "blitter.com/go/xs/xs.launchTuns" -> "blitter.com/go/xs/xs.reqTunnel" [ tooltip="at xs.go:645: calling [blitter.com/go/xs/xs.reqTunnel]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.UnhandledCriticalExtension).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/x509.UnhandledCriticalExtension).Error]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(compress/flate.CorruptInputError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(compress/flate.CorruptInputError).Error]" ] - "blitter.com/go/xs/xs.buildCmdLocalToRemote" -> "blitter.com/go/xs/xs.getTreeSizeSubCmd" [ tooltip="at xs.go:335: calling [blitter.com/go/xs/xs.getTreeSizeSubCmd]" ] - "blitter.com/go/xs/xs.buildCmdRemoteToLocal" -> "blitter.com/go/xs.GetTool" [ color="saddlebrown" tooltip="at xs.go:256: calling [blitter.com/go/xs.GetTool]\nat xs.go:263: calling [blitter.com/go/xs.GetTool]" ] - "blitter.com/go/xs/xs.sendSessionParams" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ style="dashed" color="saddlebrown" tooltip="at xs.go:675: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:671: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:667: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:663: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:659: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xs.go:655: calling [(blitter.com/go/xs/xsnet.Conn).Write]" ] - "blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs.Session).SetStatus" [ color="saddlebrown" tooltip="at xs.go:1025: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1043: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1047: calling [(*blitter.com/go/xs.Session).SetStatus]\nat xs.go:1086: calling [(*blitter.com/go/xs.Session).SetStatus]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(*github.com/pkg/errors.fundamental).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(*github.com/pkg/errors.fundamental).Error]" ] - "blitter.com/go/xs/xs.handleTermResizes" -> "blitter.com/go/xs/xs.handleTermResizes$1" [ arrowhead="normalnoneodot" tooltip="at termsize_unix.go:21: calling [blitter.com/go/xs/xs.handleTermResizes$1]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.usageShell" [ style="dashed" tooltip="at xs.go:852: calling [blitter.com/go/xs/xs.usageShell]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.restoreTermState" [ arrowhead="normalnoneodiamond" tooltip="at xs.go:984: calling [blitter.com/go/xs/xs.restoreTermState]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.ReadPassword" [ color="saddlebrown" tooltip="at xs.go:1005: calling [blitter.com/go/xs.ReadPassword]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs/xs.doShellMode$1$1" [ arrowhead="normalnoneodiamond" tooltip="at xs.go:491: calling [blitter.com/go/xs/xs.doShellMode$1$1]" ] - "blitter.com/go/xs/xs.handleTermResizes$1" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" tooltip="at termsize_unix.go:33: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" ] - "blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" tooltip="at xs.go:1054: calling [(*blitter.com/go/xs/xsnet.Conn).EnableChaff]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(crypto/x509.HostnameError).Error" [ style="dashed" color="saddlebrown" tooltip="at xs.go:507: calling [(crypto/x509.HostnameError).Error]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(*crypto/tls.permanentError).Error" [ color="saddlebrown" tooltip="at xs.go:507: calling [(*crypto/tls.permanentError).Error]" style="dashed" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.doCopyMode" [ tooltip="at xs.go:1085: calling [blitter.com/go/xs/xs.doCopyMode]" ] - "blitter.com/go/xs/xs.copyBuffer" -> "blitter.com/go/xs/xs.copyBuffer$2" [ style="dashed" tooltip="at xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$2]" ] - "blitter.com/go/xs/xs.copyBuffer" -> "blitter.com/go/xs/xs.copyBuffer$3" [ style="dashed" tooltip="at xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$3]" ] - "blitter.com/go/xs/xs.main" -> "(*blitter.com/go/xs/xsnet.Conn).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" tooltip="at xs.go:972: calling [(*blitter.com/go/xs/xsnet.Conn).Close]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.rejectUserMsg" [ tooltip="at xs.go:1046: calling [blitter.com/go/xs/xs.rejectUserMsg]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.usageCp" [ tooltip="at xs.go:852: calling [blitter.com/go/xs/xs.usageCp]" style="dashed" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(vendor/golang.org/x/net/idna.runeError).Error" [ color="saddlebrown" tooltip="at xs.go:507: calling [(vendor/golang.org/x/net/idna.runeError).Error]" style="dashed" ] - "blitter.com/go/xs/xs.copyBuffer" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ tooltip="at xs.go:193: calling [(blitter.com/go/xs/xsnet.Conn).Write]" style="dashed" color="saddlebrown" ] - "blitter.com/go/xs/xs.copyBuffer" -> "blitter.com/go/xs/xs.copyBuffer$1" [ style="dashed" tooltip="at xs.go:186: calling [blitter.com/go/xs/xs.copyBuffer$1]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.main$1" [ tooltip="at xs.go:790: calling [blitter.com/go/xs/xs.main$1]" arrowhead="normalnoneodot" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xs.localUserName" [ tooltip="at xs.go:803: calling [blitter.com/go/xs/xs.localUserName]" ] - "blitter.com/go/xs/xs.reqTunnel" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" tooltip="at xs.go:594: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs/xsnet.Init" [ color="saddlebrown" tooltip="at xs.go:865: calling [blitter.com/go/xs/xsnet.Init]" ] - "blitter.com/go/xs/xs.main" -> "blitter.com/go/xs.NewSession" [ tooltip="at xs.go:1021: calling [blitter.com/go/xs.NewSession]" color="saddlebrown" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(blitter.com/go/xs.Session).Status" [ tooltip="at xs.go:514: calling [(blitter.com/go/xs.Session).Status]\nat xs.go:519: calling [(blitter.com/go/xs.Session).Status]" color="saddlebrown" ] - "blitter.com/go/xs/xs.handleTermResizes$1" -> "blitter.com/go/xs/xs.GetSize" [ tooltip="at termsize_unix.go:27: calling [blitter.com/go/xs/xs.GetSize]" ] - "blitter.com/go/xs/xs.reqTunnel" -> "blitter.com/go/xs/logger.LogDebug" [ color="saddlebrown" tooltip="at xs.go:593: calling [blitter.com/go/xs/logger.LogDebug]" ] - "blitter.com/go/xs/xs.main$3" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ tooltip="at xs.go:1072: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" color="saddlebrown" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "blitter.com/go/xs/xs.restoreTermState" [ tooltip="at xs.go:503: calling [blitter.com/go/xs/xs.restoreTermState]\nat xs.go:518: calling [blitter.com/go/xs/xs.restoreTermState]" ] - "blitter.com/go/xs/xs.doShellMode$1" -> "(compress/flate.InternalError).Error" [ tooltip="at xs.go:507: calling [(compress/flate.InternalError).Error]" style="dashed" color="saddlebrown" ] -} diff --git a/xs/xs_seq.png b/xs/xs_seq.png deleted file mode 100644 index 859f646..0000000 Binary files a/xs/xs_seq.png and /dev/null differ diff --git a/xsd.sysvrc b/xsd.sysvrc deleted file mode 100755 index 415c591..0000000 --- a/xsd.sysvrc +++ /dev/null @@ -1,166 +0,0 @@ -#! /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 - -echo "SET XSD_OPTS in this script to define allow KEX, cipher and hmac algs" -#XSD_OPTS="-L -aK KEX_all -aC C_all -aH H_all" -exit 1 - -# /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 diff --git a/xsd/Makefile b/xsd/Makefile deleted file mode 100644 index 6a0ab14..0000000 --- a/xsd/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -.PHONY: clean all vis lint - -ifeq ($(GARBLE),y) -GO = garble -literals -tiny -debugdir=garbled -else -GO = go -endif - -EXTPKGS = binary,bytes,crypto,encoding,errors,flag,fmt,internal,io,log,net,os,path,runtime,time,strings,sync,syscall -EXE = $(notdir $(shell pwd)) - -all: - $(GO) build $(BUILDOPTS) . - -clean: - $(RM) $(EXE) $(EXE).exe - -vis: - 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: - -golangci-lint run - diff --git a/xsd/xsd-vis-fixedup.png b/xsd/xsd-vis-fixedup.png deleted file mode 100644 index 4a16553..0000000 Binary files a/xsd/xsd-vis-fixedup.png and /dev/null differ diff --git a/xsd/xsd-vis.gv b/xsd/xsd-vis.gv deleted file mode 100755 index d584224..0000000 --- a/xsd/xsd-vis.gv +++ /dev/null @@ -1,550 +0,0 @@ -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="box" style="filled,rounded" 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/xsd.main$2$1" [ fillcolor="lightblue" label="main$2$1" style="dotted,filled" tooltip="blitter.com/go/xs/xsd.main$2$1 | defined in xsd.go:675\nat xsd.go:677: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xsd.go:676: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:678: calling [(*blitter.com/go/xs/xsnet.Conn).Close]" ] - "blitter.com/go/xs/xsd.main$1" [ fillcolor="lightblue" label="main$1" style="dotted,filled" tooltip="blitter.com/go/xs/xsd.main$1 | defined in xsd.go:610\nat xsd.go:615: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:619: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:623: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:625: calling [blitter.com/go/xs/logger.LogNotice]" ] - "blitter.com/go/xs/xsd.runShellAs$3" [ tooltip="blitter.com/go/xs/xsd.runShellAs$3 | defined in xsd.go:345\nat xsd.go:348: calling [github.com/creack/pty.Setsize]" fillcolor="lightblue" label="stdinToPtyWorker" style="dotted,filled" ] - "blitter.com/go/xs/xsd.runShellAs$2" [ fillcolor="lightblue" label="termResizeWatcher" style="dotted,filled" tooltip="blitter.com/go/xs/xsd.runShellAs$2 | defined in xsd.go:336\nat xsd.go:336: calling [blitter.com/go/goutmp.Unput_utmp]" ] - "blitter.com/go/xs/xsd.main" [ penwidth="0.5" tooltip="blitter.com/go/xs/xsd.main | defined in xsd.go:508\nat xsd.go:583: calling [blitter.com/go/xs/logger.New]\nat xsd.go:650: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:654: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:658: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:648: calling [(blitter.com/go/xs/xsd.allowedKEXAlgs).allowed]\nat xsd.go:671: calling [blitter.com/go/xs/xsd.main$2]\nat xsd.go:644: calling [(*blitter.com/go/xs/xsnet.HKExListener).Accept]\nat xsd.go:648: calling [(*blitter.com/go/xs/xsnet.Conn).KEX]\nat xsd.go:649: calling [(*blitter.com/go/xs/xsnet.Conn).KEX]\nat xsd.go:656: calling [(*blitter.com/go/xs/xsnet.Conn).HAlg]\nat xsd.go:657: calling [(*blitter.com/go/xs/xsnet.Conn).HAlg]\nat xsd.go:595: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:600: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:605: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:651: calling [(*blitter.com/go/xs/xsnet.Conn).Close]\nat xsd.go:655: calling [(*blitter.com/go/xs/xsnet.Conn).Close]\nat xsd.go:659: calling [(*blitter.com/go/xs/xsnet.Conn).Close]\nat xsd.go:652: calling [(blitter.com/go/xs/xsd.allowedCipherAlgs).allowed]\nat xsd.go:638: calling [(blitter.com/go/xs/xsnet.HKExListener).Close]\nat xsd.go:666: calling [(*blitter.com/go/xs/xsnet.Conn).SetupChaff]\nat xsd.go:610: calling [blitter.com/go/xs/xsd.main$1]\nat xsd.go:656: calling [(blitter.com/go/xs/xsd.allowedHMACAlgs).allowed]\nat xsd.go:652: calling [(*blitter.com/go/xs/xsnet.Conn).CAlg]\nat xsd.go:653: calling [(*blitter.com/go/xs/xsnet.Conn).CAlg]\nat xsd.go:584: calling [blitter.com/go/xs/xsnet.Init]\nat xsd.go:634: calling [blitter.com/go/xs/xsnet.Listen]" fillcolor="lightblue" label="main" ] - "blitter.com/go/xs/xsd.main$2" [ style="dotted,filled" tooltip="blitter.com/go/xs/xsd.main$2 | defined in xsd.go:671\nat xsd.go:853: calling [blitter.com/go/xs/xsd.runServerToClientCopyAs]\nat xsd.go:672: calling [(*blitter.com/go/xs/xsnet.Conn).Close]\nat xsd.go:710: calling [(*blitter.com/go/xs.Session).SetWho]\nat xsd.go:790: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:805: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:821: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:840: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:866: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:726: calling [(*blitter.com/go/xs.Session).SetTermType]\nat xsd.go:766: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xsd.go:769: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xsd.go:782: calling [(blitter.com/go/xs.Session).TermType]\nat xsd.go:797: calling [(blitter.com/go/xs.Session).TermType]\nat xsd.go:813: calling [(blitter.com/go/xs.Session).TermType]\nat xsd.go:829: calling [(blitter.com/go/xs.Session).TermType]\nat xsd.go:853: calling [(blitter.com/go/xs.Session).TermType]\nat xsd.go:734: calling [(*blitter.com/go/xs.Session).SetCmd]\nat xsd.go:754: calling [blitter.com/go/xs.VerifyPass]\nat xsd.go:762: calling [(*blitter.com/go/xs.Session).ClearAuthCookie]\nat xsd.go:718: calling [(*blitter.com/go/xs.Session).SetConnHost]\nat xsd.go:745: calling [(blitter.com/go/xs.Session).Cmd]\nat xsd.go:797: calling [(blitter.com/go/xs.Session).Cmd]\nat xsd.go:813: calling [(blitter.com/go/xs.Session).Cmd]\nat xsd.go:829: calling [(blitter.com/go/xs.Session).Cmd]\nat xsd.go:853: calling [(blitter.com/go/xs.Session).Cmd]\nat xsd.go:829: calling [blitter.com/go/xs/xsd.runClientToServerCopyAs]\nat xsd.go:749: calling [(blitter.com/go/xs.Session).AuthCookie]\nat xsd.go:754: calling [(blitter.com/go/xs.Session).AuthCookie]\nat xsd.go:756: calling [(blitter.com/go/xs.Session).AuthCookie]\nat xsd.go:745: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:749: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:754: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:756: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:768: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:779: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:780: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:782: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:787: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:789: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:796: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:797: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:802: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:804: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:811: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:813: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:818: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:820: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:828: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:829: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:834: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:836: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:852: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:853: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:855: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:858: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:749: calling [blitter.com/go/xs.NewAuthCtx]\nat xsd.go:754: calling [blitter.com/go/xs.NewAuthCtx]\nat xsd.go:756: calling [blitter.com/go/xs.NewAuthCtx]\nat xsd.go:778: calling [blitter.com/go/goutmp.GetHost]\nat xsd.go:795: calling [blitter.com/go/goutmp.GetHost]\nat xsd.go:810: calling [blitter.com/go/goutmp.GetHost]\nat xsd.go:827: calling [blitter.com/go/goutmp.GetHost]\nat xsd.go:851: calling [blitter.com/go/goutmp.GetHost]\nat xsd.go:782: calling [blitter.com/go/xs/xsd.runShellAs]\nat xsd.go:797: calling [blitter.com/go/xs/xsd.runShellAs]\nat xsd.go:813: calling [blitter.com/go/xs/xsd.runShellAs]\nat xsd.go:768: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:779: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:796: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:804: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:811: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:820: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:828: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:836: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:852: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:858: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:787: calling [blitter.com/go/xs/logger.LogErr]\nat xsd.go:802: calling [blitter.com/go/xs/logger.LogErr]\nat xsd.go:834: calling [blitter.com/go/xs/logger.LogErr]\nat xsd.go:871: calling [blitter.com/go/xs/logger.LogErr]\nat xsd.go:855: calling [blitter.com/go/xs/logger.LogErr]\nat xsd.go:756: calling [blitter.com/go/xs.AuthUserByPasswd]\nat xsd.go:742: calling [(*blitter.com/go/xs.Session).SetAuthCookie]\nat xsd.go:745: calling [(blitter.com/go/xs.Session).ConnHost]\nat xsd.go:749: calling [(blitter.com/go/xs.Session).ConnHost]\nat xsd.go:780: calling [(blitter.com/go/xs.Session).ConnHost]\nat xsd.go:780: calling [blitter.com/go/xs/xsd.GenAuthToken]\nat xsd.go:777: calling [(*blitter.com/go/xs/xsnet.Conn).RemoteAddr]\nat xsd.go:794: calling [(*blitter.com/go/xs/xsnet.Conn).RemoteAddr]\nat xsd.go:809: calling [(*blitter.com/go/xs/xsnet.Conn).RemoteAddr]\nat xsd.go:826: calling [(*blitter.com/go/xs/xsnet.Conn).RemoteAddr]\nat xsd.go:850: calling [(*blitter.com/go/xs/xsnet.Conn).RemoteAddr]\nat xsd.go:846: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]\nat xsd.go:749: calling [blitter.com/go/xs.AuthUserByToken]\nat xsd.go:745: calling [(blitter.com/go/xs.Session).Op]\nat xsd.go:775: calling [(blitter.com/go/xs.Session).Op]\nat xsd.go:792: calling [(blitter.com/go/xs.Session).Op]\nat xsd.go:807: calling [(blitter.com/go/xs.Session).Op]\nat xsd.go:823: calling [(blitter.com/go/xs.Session).Op]\nat xsd.go:847: calling [(blitter.com/go/xs.Session).Op]\nat xsd.go:702: calling [(*blitter.com/go/xs.Session).SetOp]\nat xsd.go:785: calling [(*blitter.com/go/xs.Session).SetOp]\nat xsd.go:800: calling [(*blitter.com/go/xs.Session).SetOp]\nat xsd.go:816: calling [(*blitter.com/go/xs.Session).SetOp]\nat xsd.go:832: calling [(*blitter.com/go/xs.Session).SetOp]\nat xsd.go:865: calling [(*blitter.com/go/xs.Session).SetOp]" fillcolor="lightblue" label="main$2" ] - "blitter.com/go/xs/xsd.GenAuthToken" [ label="GenAuthToken" penwidth="1.5" tooltip="blitter.com/go/xs/xsd.GenAuthToken | defined in xsd.go:424" fillcolor="lightblue" ] - "blitter.com/go/xs/xsd.runShellAs" [ fillcolor="lightblue" label="runShellAs" penwidth="0.5" tooltip="blitter.com/go/xs/xsd.runShellAs | defined in xsd.go:259\nat xsd.go:355: calling [blitter.com/go/xs/xsd.runShellAs$4]\nat xsd.go:381: calling [blitter.com/go/xs/xsd.runShellAs$6]\nat xsd.go:308: calling [blitter.com/go/xs.GetTool]\nat xsd.go:297: calling [blitter.com/go/xs.GetTool]\nat xsd.go:303: calling [blitter.com/go/xs.GetTool]\nat xsd.go:331: calling [blitter.com/go/xs/xsd.ptsName]\nat xsd.go:365: calling [(*blitter.com/go/xs/xsnet.Conn).EnableChaff]\nat xsd.go:407: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:318: calling [github.com/creack/pty.Start]\nat xsd.go:335: calling [blitter.com/go/goutmp.Put_utmp]\nat xsd.go:368: calling [blitter.com/go/xs/xsd.runShellAs$5]\nat xsd.go:409: calling [blitter.com/go/xs/logger.LogDebug]\nat xsd.go:325: calling [blitter.com/go/xs/xsd.runShellAs$1]\nat xsd.go:336: calling [blitter.com/go/xs/xsd.runShellAs$2]\nat xsd.go:337: calling [blitter.com/go/goutmp.Put_lastlog_entry]\nat xsd.go:345: calling [blitter.com/go/xs/xsd.runShellAs$3]" ] - "blitter.com/go/xs/xsd.runShellAs$1" [ fillcolor="lightblue" label="deferPtmxClose" style="dotted,filled" tooltip="blitter.com/go/xs/xsd.runShellAs$1 | defined in xsd.go:325" ] - "blitter.com/go/xs/xsd.ptsName" [ fillcolor="lightblue" label="ptsName" penwidth="0.5" tooltip="blitter.com/go/xs/xsd.ptsName | defined in xsd.go:57\nat xsd.go:59: calling [blitter.com/go/xs/xsd.ioctl]" ] - "blitter.com/go/xs/xsd.ioctl" [ fillcolor="lightblue" label="ioctl" penwidth="0.5" tooltip="blitter.com/go/xs/xsd.ioctl | defined in xsd.go:50" ] - "blitter.com/go/xs/xsd.runShellAs$4" [ label="deferChaffShutdown" style="dotted,filled" tooltip="blitter.com/go/xs/xsd.runShellAs$4 | defined in xsd.go:355\nat xsd.go:358: calling [(context.deadlineExceededError).Error]\nat xsd.go:358: calling [(vendor/golang.org/x/net/idna.runeError).Error]\nat xsd.go:358: calling [(compress/flate.InternalError).Error]\nat xsd.go:358: calling [(compress/flate.CorruptInputError).Error]\nat xsd.go:358: calling [(vendor/golang.org/x/net/idna.labelError).Error]\nat xsd.go:358: calling [(*github.com/pkg/errors.fundamental).Error]" fillcolor="lightblue" ] - "blitter.com/go/xs/xsd.runShellAs$5" [ fillcolor="lightblue" label="ptyToStdoutWorker" style="dotted,filled" tooltip="blitter.com/go/xs/xsd.runShellAs$5 | defined in xsd.go:368\nat xsd.go:369: calling [(*blitter.com/go/xs/xsnet.Conn).DisableChaff]\nat xsd.go:370: calling [(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff]" ] - "blitter.com/go/xs/xsd.runShellAs$6" [ style="dotted,filled" tooltip="blitter.com/go/xs/xsd.runShellAs$6 | defined in xsd.go:381\nat xsd.go:385: calling [(vendor/golang.org/x/net/idna.labelError).Error]\nat xsd.go:385: calling [(*github.com/pkg/errors.fundamental).Error]\nat xsd.go:385: calling [(compress/flate.CorruptInputError).Error]\nat xsd.go:385: calling [(vendor/golang.org/x/net/idna.runeError).Error]\nat xsd.go:385: calling [(context.deadlineExceededError).Error]\nat xsd.go:385: calling [(compress/flate.InternalError).Error]" fillcolor="lightblue" label="runShellAs$6" ] - "blitter.com/go/xs/xsd.runClientToServerCopyAs" [ fillcolor="lightblue" label="runClientToServerCopyAs" penwidth="0.5" tooltip="blitter.com/go/xs/xsd.runClientToServerCopyAs | defined in xsd.go:68\nat xsd.go:119: calling [(*blitter.com/go/xs/xsnet.Conn).EnableChaff]\nat xsd.go:121: calling [(*blitter.com/go/xs/xsnet.Conn).DisableChaff]\nat xsd.go:122: calling [(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff]\nat xsd.go:88: calling [blitter.com/go/xs.GetTool]" ] - "blitter.com/go/xs/xsd.runServerToClientCopyAs" [ fillcolor="lightblue" label="runServerToClientCopyAs" penwidth="0.5" tooltip="blitter.com/go/xs/xsd.runServerToClientCopyAs | defined in xsd.go:169\nat xsd.go:220: calling [(*blitter.com/go/xs/xsnet.Conn).EnableChaff]\nat xsd.go:224: calling [(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff]\nat xsd.go:193: calling [blitter.com/go/xs.GetTool]\nat xsd.go:223: calling [(*blitter.com/go/xs/xsnet.Conn).DisableChaff]" ] - - subgraph "cluster_blitter.com/go/goutmp" { - penwidth="0.8"; -rank="sink"; -URL="/?f=blitter.com/go/goutmp"; -fontsize="16"; -style="filled"; -fillcolor="lightyellow"; -fontname="Tahoma bold"; -label="goutmp"; -tooltip="package: blitter.com/go/goutmp"; - - "blitter.com/go/goutmp.Unput_utmp" [ fillcolor="moccasin" label="Unput_utmp" penwidth="1.5" tooltip="blitter.com/go/goutmp.Unput_utmp | defined in goutmp_linux.go:115" ] - "blitter.com/go/goutmp.GetHost" [ fillcolor="moccasin" label="GetHost" penwidth="1.5" tooltip="blitter.com/go/goutmp.GetHost | defined in goutmp_linux.go:91" ] - "blitter.com/go/goutmp.Put_utmp" [ fillcolor="moccasin" label="Put_utmp" penwidth="1.5" tooltip="blitter.com/go/goutmp.Put_utmp | defined in goutmp_linux.go:106" ] - "blitter.com/go/goutmp.Put_lastlog_entry" [ tooltip="blitter.com/go/goutmp.Put_lastlog_entry | defined in goutmp_linux.go:120" fillcolor="moccasin" label="Put_lastlog_entry" penwidth="1.5" ] - - } - - subgraph "cluster_blitter.com/go/xs" { - penwidth="0.8"; -style="filled"; -fontname="Tahoma bold"; -tooltip="package: blitter.com/go/xs"; -fontsize="16"; -fillcolor="lightyellow"; -rank="sink"; -label="xs"; -URL="/?f=blitter.com/go/xs"; - - "blitter.com/go/xs.NewAuthCtx" [ fillcolor="moccasin" label="NewAuthCtx" penwidth="1.5" tooltip="blitter.com/go/xs.NewAuthCtx | defined in auth.go:35" ] - "blitter.com/go/xs.AuthUserByToken" [ fillcolor="moccasin" label="AuthUserByToken" penwidth="1.5" tooltip="blitter.com/go/xs.AuthUserByToken | defined in auth.go:161" ] - "blitter.com/go/xs.VerifyPass" [ fillcolor="moccasin" label="VerifyPass" penwidth="1.5" tooltip="blitter.com/go/xs.VerifyPass | defined in auth.go:44" ] - "blitter.com/go/xs.AuthUserByPasswd" [ fillcolor="moccasin" label="AuthUserByPasswd" penwidth="1.5" tooltip="blitter.com/go/xs.AuthUserByPasswd | defined in auth.go:96" ] - "blitter.com/go/xs.GetTool" [ fillcolor="moccasin" label="GetTool" penwidth="1.5" tooltip="blitter.com/go/xs.GetTool | defined in auth.go:217" ] - - subgraph "cluster_*blitter.com/go/xs.Session" { - fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(*Session)"; -tooltip="type: *blitter.com/go/xs.Session"; -penwidth="0.5"; -fontsize="15"; - - "(*blitter.com/go/xs.Session).SetOp" [ fillcolor="moccasin" label="SetOp" penwidth="1.5" tooltip="(*blitter.com/go/xs.Session).SetOp | defined in session.go:41" ] - "(*blitter.com/go/xs.Session).SetWho" [ fillcolor="moccasin" label="SetWho" penwidth="1.5" tooltip="(*blitter.com/go/xs.Session).SetWho | defined in session.go:51" ] - "(*blitter.com/go/xs.Session).SetConnHost" [ fillcolor="moccasin" label="SetConnHost" penwidth="1.5" tooltip="(*blitter.com/go/xs.Session).SetConnHost | defined in session.go:61" ] - "(*blitter.com/go/xs.Session).SetTermType" [ label="SetTermType" penwidth="1.5" tooltip="(*blitter.com/go/xs.Session).SetTermType | defined in session.go:73" fillcolor="moccasin" ] - "(*blitter.com/go/xs.Session).SetCmd" [ tooltip="(*blitter.com/go/xs.Session).SetCmd | defined in session.go:85" fillcolor="moccasin" label="SetCmd" penwidth="1.5" ] - "(*blitter.com/go/xs.Session).SetAuthCookie" [ fillcolor="moccasin" label="SetAuthCookie" penwidth="1.5" tooltip="(*blitter.com/go/xs.Session).SetAuthCookie | defined in session.go:101" ] - "(*blitter.com/go/xs.Session).ClearAuthCookie" [ fillcolor="moccasin" label="ClearAuthCookie" penwidth="1.5" tooltip="(*blitter.com/go/xs.Session).ClearAuthCookie | defined in session.go:109" ] - - } - - subgraph "cluster_blitter.com/go/xs.Session" { - label="(Session)"; -tooltip="type: blitter.com/go/xs.Session"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; - - "(blitter.com/go/xs.Session).Op" [ fillcolor="moccasin" label="Op" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).Op | defined in session.go:36" ] - "(blitter.com/go/xs.Session).Who" [ tooltip="(blitter.com/go/xs.Session).Who | defined in session.go:46" fillcolor="moccasin" label="Who" penwidth="1.5" ] - "(blitter.com/go/xs.Session).ConnHost" [ label="ConnHost" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).ConnHost | defined in session.go:56" fillcolor="moccasin" ] - "(blitter.com/go/xs.Session).Cmd" [ penwidth="1.5" tooltip="(blitter.com/go/xs.Session).Cmd | defined in session.go:79" fillcolor="moccasin" label="Cmd" ] - "(blitter.com/go/xs.Session).AuthCookie" [ fillcolor="moccasin" label="AuthCookie" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).AuthCookie | defined in session.go:92" ] - "(blitter.com/go/xs.Session).TermType" [ fillcolor="moccasin" label="TermType" penwidth="1.5" tooltip="(blitter.com/go/xs.Session).TermType | defined in session.go:67" ] - - } - - } - - subgraph "cluster_blitter.com/go/xs/logger" { - fillcolor="lightyellow"; -rank="sink"; -URL="/?f=blitter.com/go/xs/logger"; -penwidth="0.8"; -fontsize="16"; -label="logger"; -tooltip="package: blitter.com/go/xs/logger"; -style="filled"; -fontname="Tahoma bold"; - - "blitter.com/go/xs/logger.LogNotice" [ label="LogNotice" penwidth="1.5" tooltip="blitter.com/go/xs/logger.LogNotice | defined in logger_linux.go:135" fillcolor="moccasin" ] - "blitter.com/go/xs/logger.New" [ tooltip="blitter.com/go/xs/logger.New | defined in logger_linux.go:71" fillcolor="moccasin" label="New" penwidth="1.5" ] - "blitter.com/go/xs/logger.LogDebug" [ fillcolor="moccasin" label="LogDebug" penwidth="1.5" tooltip="blitter.com/go/xs/logger.LogDebug | defined in logger_linux.go:103" ] - "blitter.com/go/xs/logger.LogErr" [ fillcolor="moccasin" label="LogErr" penwidth="1.5" tooltip="blitter.com/go/xs/logger.LogErr | defined in logger_linux.go:119" ] - - } - - subgraph "cluster_blitter.com/go/xs/xsd.allowedCipherAlgs" { - fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="lightsteelblue"; -label="(allowedCipherAlgs)"; -tooltip="type: blitter.com/go/xs/xsd.allowedCipherAlgs"; -penwidth="0.5"; - - "(blitter.com/go/xs/xsd.allowedCipherAlgs).allowed" [ fillcolor="lightblue" label="allowed" penwidth="0.5" tooltip="(blitter.com/go/xs/xsd.allowedCipherAlgs).allowed | defined in xsd.go:464\nat xsd.go:466: calling [(*blitter.com/go/xs/xsnet.CSCipherAlg).String]" ] - - } - - subgraph "cluster_blitter.com/go/xs/xsd.allowedHMACAlgs" { - label="(allowedHMACAlgs)"; -tooltip="type: blitter.com/go/xs/xsd.allowedHMACAlgs"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="lightsteelblue"; - - "(blitter.com/go/xs/xsd.allowedHMACAlgs).allowed" [ label="allowed" penwidth="0.5" tooltip="(blitter.com/go/xs/xsd.allowedHMACAlgs).allowed | defined in xsd.go:482\nat xsd.go:484: calling [(*blitter.com/go/xs/xsnet.CSHmacAlg).String]" fillcolor="lightblue" ] - - } - - subgraph "cluster_blitter.com/go/xs/xsd.allowedKEXAlgs" { - style="rounded,filled"; -fillcolor="lightsteelblue"; -label="(allowedKEXAlgs)"; -tooltip="type: blitter.com/go/xs/xsd.allowedKEXAlgs"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; - - "(blitter.com/go/xs/xsd.allowedKEXAlgs).allowed" [ tooltip="(blitter.com/go/xs/xsd.allowedKEXAlgs).allowed | defined in xsd.go:446\nat xsd.go:448: calling [(*blitter.com/go/xs/xsnet.KEXAlg).String]" fillcolor="lightblue" label="allowed" penwidth="0.5" ] - - } - - subgraph "cluster_blitter.com/go/xs/xsnet" { - URL="/?f=blitter.com/go/xs/xsnet"; -fillcolor="lightyellow"; -fontname="Tahoma bold"; -rank="sink"; -label="xsnet"; -tooltip="package: blitter.com/go/xs/xsnet"; -penwidth="0.8"; -fontsize="16"; -style="filled"; - - "blitter.com/go/xs/xsnet.Init" [ fillcolor="moccasin" label="Init" penwidth="1.5" tooltip="blitter.com/go/xs/xsnet.Init | defined in net.go:198" ] - "blitter.com/go/xs/xsnet.Listen" [ fillcolor="moccasin" label="Listen" penwidth="1.5" tooltip="blitter.com/go/xs/xsnet.Listen | defined in net.go:1049" ] - - subgraph "cluster_*blitter.com/go/xs/xsnet.CSCipherAlg" { - fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(*CSCipherAlg)"; -tooltip="type: *blitter.com/go/xs/xsnet.CSCipherAlg"; -penwidth="0.5"; -fontsize="15"; - - "(*blitter.com/go/xs/xsnet.CSCipherAlg).String" [ fillcolor="moccasin" label="String" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.CSCipherAlg).String | defined in net.go:152" ] - - } - - subgraph "cluster_*blitter.com/go/xs/xsnet.CSHmacAlg" { - label="(*CSHmacAlg)"; -tooltip="type: *blitter.com/go/xs/xsnet.CSHmacAlg"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; - - "(*blitter.com/go/xs/xsnet.CSHmacAlg).String" [ fillcolor="moccasin" label="String" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.CSHmacAlg).String | defined in net.go:175" ] - - } - - subgraph "cluster_*blitter.com/go/xs/xsnet.Conn" { - fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(*Conn)"; -tooltip="type: *blitter.com/go/xs/xsnet.Conn"; -penwidth="0.5"; - - "(*blitter.com/go/xs/xsnet.Conn).Close" [ label="Close" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).Close | defined in net.go:979" fillcolor="moccasin" ] - "(*blitter.com/go/xs/xsnet.Conn).KEX" [ penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).KEX | defined in net.go:210" fillcolor="moccasin" label="KEX" ] - "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ fillcolor="moccasin" label="SetStatus" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).SetStatus | defined in net.go:218" ] - "(*blitter.com/go/xs/xsnet.Conn).CAlg" [ fillcolor="moccasin" label="CAlg" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).CAlg | defined in net.go:148" ] - "(*blitter.com/go/xs/xsnet.Conn).HAlg" [ fillcolor="moccasin" label="HAlg" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).HAlg | defined in net.go:171" ] - "(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ tooltip="(*blitter.com/go/xs/xsnet.Conn).SetupChaff | defined in net.go:1563" fillcolor="moccasin" label="SetupChaff" penwidth="1.5" ] - "(*blitter.com/go/xs/xsnet.Conn).RemoteAddr" [ fillcolor="moccasin" label="RemoteAddr" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).RemoteAddr | defined in net.go:997" ] - "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ fillcolor="moccasin" label="EnableChaff" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).EnableChaff | defined in net.go:1546" ] - "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ fillcolor="moccasin" label="DisableChaff" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).DisableChaff | defined in net.go:1553" ] - "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ fillcolor="moccasin" label="ShutdownChaff" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff | defined in net.go:1558" ] - "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ fillcolor="moccasin" label="WritePacket" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.Conn).WritePacket | defined in net.go:1437" ] - - } - - subgraph "cluster_*blitter.com/go/xs/xsnet.HKExListener" { - fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(*HKExListener)"; -tooltip="type: *blitter.com/go/xs/xsnet.HKExListener"; -penwidth="0.5"; - - "(*blitter.com/go/xs/xsnet.HKExListener).Accept" [ fillcolor="moccasin" label="Accept" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.HKExListener).Accept | defined in net.go:1090" ] - - } - - subgraph "cluster_*blitter.com/go/xs/xsnet.KEXAlg" { - fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(*KEXAlg)"; -tooltip="type: *blitter.com/go/xs/xsnet.KEXAlg"; -penwidth="0.5"; - - "(*blitter.com/go/xs/xsnet.KEXAlg).String" [ fillcolor="moccasin" label="String" penwidth="1.5" tooltip="(*blitter.com/go/xs/xsnet.KEXAlg).String | defined in net.go:115" ] - - } - - 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).Write" [ penwidth="1.5" tooltip="(blitter.com/go/xs/xsnet.Conn).Write | defined in net.go:1431" fillcolor="moccasin" label="Write" ] - - } - - 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).Close" [ fillcolor="moccasin" label="Close" penwidth="1.5" tooltip="(blitter.com/go/xs/xsnet.HKExListener).Close | defined in net.go:1075" ] - - } - - } - - subgraph "cluster_compress/flate" { - fontsize="16"; -style="filled"; -fillcolor="lightyellow"; -fontname="Tahoma bold"; -label="flate"; -URL="/?f=compress/flate"; -tooltip="package: compress/flate"; -penwidth="0.8"; -rank="sink"; - - - subgraph "cluster_compress/flate.CorruptInputError" { - fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(CorruptInputError)"; -tooltip="type: compress/flate.CorruptInputError"; -penwidth="0.5"; -fontsize="15"; - - "(compress/flate.CorruptInputError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(compress/flate.CorruptInputError).Error | defined in inflate.go:35" ] - - } - - subgraph "cluster_compress/flate.InternalError" { - penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(InternalError)"; -tooltip="type: compress/flate.InternalError"; - - "(compress/flate.InternalError).Error" [ penwidth="1.5" tooltip="(compress/flate.InternalError).Error | defined in inflate.go:42" fillcolor="moccasin" label="Error" ] - - } - - } - - subgraph "cluster_context" { - tooltip="package: context"; -fontsize="16"; -style="filled"; -fillcolor="lightyellow"; -label="context"; -URL="/?f=context"; -penwidth="0.8"; -fontname="Tahoma bold"; -rank="sink"; - - - subgraph "cluster_context.deadlineExceededError" { - penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(deadlineExceededError)"; -tooltip="type: context.deadlineExceededError"; - - "(context.deadlineExceededError).Error" [ penwidth="1.5" tooltip="(context.deadlineExceededError).Error | defined in context.go:165" fillcolor="moccasin" label="Error" ] - - } - - } - - subgraph "cluster_github.com/creack/pty" { - penwidth="0.8"; -style="filled"; -URL="/?f=github.com/creack/pty"; -tooltip="package: github.com/creack/pty"; -fontsize="16"; -fillcolor="lightyellow"; -fontname="Tahoma bold"; -rank="sink"; -label="pty"; - - "github.com/creack/pty.Setsize" [ fillcolor="moccasin" label="Setsize" penwidth="1.5" tooltip="github.com/creack/pty.Setsize | defined in winsize_unix.go:21" ] - "github.com/creack/pty.Start" [ fillcolor="moccasin" label="Start" penwidth="1.5" tooltip="github.com/creack/pty.Start | defined in run.go:14" ] - - } - - subgraph "cluster_github.com/pkg/errors" { - penwidth="0.8"; -fontname="Tahoma bold"; -rank="sink"; -label="errors"; -tooltip="package: github.com/pkg/errors"; -fontsize="16"; -style="filled"; -fillcolor="lightyellow"; -URL="/?f=github.com/pkg/errors"; - - - 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" tooltip="(*github.com/pkg/errors.fundamental).Error | defined in errors.go:125" fillcolor="moccasin" ] - - } - - } - - subgraph "cluster_vendor/golang.org/x/net/idna" { - rank="sink"; -fillcolor="lightyellow"; -fontsize="16"; -style="filled"; -fontname="Tahoma bold"; -label="idna"; -URL="/?f=vendor/golang.org/x/net/idna"; -tooltip="package: vendor/golang.org/x/net/idna"; -penwidth="0.8"; - - - subgraph "cluster_vendor/golang.org/x/net/idna.labelError" { - label="(labelError)"; -tooltip="type: vendor/golang.org/x/net/idna.labelError"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; -labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; - - "(vendor/golang.org/x/net/idna.labelError).Error" [ label="Error" penwidth="1.5" tooltip="(vendor/golang.org/x/net/idna.labelError).Error | defined in idna10.0.0.go:324" fillcolor="moccasin" ] - - } - - subgraph "cluster_vendor/golang.org/x/net/idna.runeError" { - labelloc="b"; -style="rounded,filled"; -fillcolor="wheat2"; -label="(runeError)"; -tooltip="type: vendor/golang.org/x/net/idna.runeError"; -penwidth="0.5"; -fontsize="15"; -fontcolor="#222222"; - - "(vendor/golang.org/x/net/idna.runeError).Error" [ fillcolor="moccasin" label="Error" penwidth="1.5" tooltip="(vendor/golang.org/x/net/idna.runeError).Error | defined in idna10.0.0.go:331" ] - - } - - } - - } - - "blitter.com/go/xs/xsd.runClientToServerCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" tooltip="at xsd.go:119: calling [(*blitter.com/go/xs/xsnet.Conn).EnableChaff]" ] - "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.runServerToClientCopyAs" [ tooltip="at xsd.go:853: calling [blitter.com/go/xs/xsd.runServerToClientCopyAs]" ] - "blitter.com/go/xs/xsd.main$2$1" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ color="saddlebrown" tooltip="at xsd.go:677: calling [(blitter.com/go/xs/xsnet.Conn).Write]" ] - "blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/logger.New" [ color="saddlebrown" tooltip="at xsd.go:583: calling [blitter.com/go/xs/logger.New]" ] - "blitter.com/go/xs/xsd.main" -> "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ color="saddlebrown" tooltip="at xsd.go:650: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:654: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:658: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]" ] - "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" tooltip="at xsd.go:672: calling [(*blitter.com/go/xs/xsnet.Conn).Close]" ] - "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetWho" [ color="saddlebrown" tooltip="at xsd.go:710: calling [(*blitter.com/go/xs.Session).SetWho]" ] - "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ color="saddlebrown" tooltip="at xsd.go:790: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:805: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:821: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:840: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]\nat xsd.go:866: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]" ] - "blitter.com/go/xs/xsd.runClientToServerCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ tooltip="at xsd.go:121: calling [(*blitter.com/go/xs/xsnet.Conn).DisableChaff]" arrowhead="normalnoneodiamond" color="saddlebrown" ] - "(blitter.com/go/xs/xsd.allowedKEXAlgs).allowed" -> "(*blitter.com/go/xs/xsnet.KEXAlg).String" [ color="saddlebrown" tooltip="at xsd.go:448: calling [(*blitter.com/go/xs/xsnet.KEXAlg).String]" ] - "blitter.com/go/xs/xsd.runShellAs$3" -> "github.com/creack/pty.Setsize" [ color="saddlebrown" tooltip="at xsd.go:348: calling [github.com/creack/pty.Setsize]" ] - "blitter.com/go/xs/xsd.main" -> "(blitter.com/go/xs/xsd.allowedKEXAlgs).allowed" [ tooltip="at xsd.go:648: calling [(blitter.com/go/xs/xsd.allowedKEXAlgs).allowed]" ] - "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetTermType" [ tooltip="at xsd.go:726: calling [(*blitter.com/go/xs.Session).SetTermType]" color="saddlebrown" ] - "blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs/xsnet.Conn).Write" [ color="saddlebrown" tooltip="at xsd.go:766: calling [(blitter.com/go/xs/xsnet.Conn).Write]\nat xsd.go:769: calling [(blitter.com/go/xs/xsnet.Conn).Write]" ] - "blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).TermType" [ color="saddlebrown" tooltip="at xsd.go:782: calling [(blitter.com/go/xs.Session).TermType]\nat xsd.go:797: calling [(blitter.com/go/xs.Session).TermType]\nat xsd.go:813: calling [(blitter.com/go/xs.Session).TermType]\nat xsd.go:829: calling [(blitter.com/go/xs.Session).TermType]\nat xsd.go:853: calling [(blitter.com/go/xs.Session).TermType]" ] - "blitter.com/go/xs/xsd.runServerToClientCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" tooltip="at xsd.go:220: calling [(*blitter.com/go/xs/xsnet.Conn).EnableChaff]" ] - "blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsd.main$2" [ arrowhead="normalnoneodot" tooltip="at xsd.go:671: calling [blitter.com/go/xs/xsd.main$2]" ] - "blitter.com/go/xs/xsd.main" -> "(*blitter.com/go/xs/xsnet.HKExListener).Accept" [ color="saddlebrown" tooltip="at xsd.go:644: calling [(*blitter.com/go/xs/xsnet.HKExListener).Accept]" ] - "blitter.com/go/xs/xsd.main" -> "(*blitter.com/go/xs/xsnet.Conn).KEX" [ color="saddlebrown" tooltip="at xsd.go:648: calling [(*blitter.com/go/xs/xsnet.Conn).KEX]\nat xsd.go:649: calling [(*blitter.com/go/xs/xsnet.Conn).KEX]" ] - "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$4" [ arrowhead="normalnoneodot" tooltip="at xsd.go:355: calling [blitter.com/go/xs/xsd.runShellAs$4]" ] - "blitter.com/go/xs/xsd.main$2$1" -> "blitter.com/go/xs/logger.LogNotice" [ color="saddlebrown" tooltip="at xsd.go:676: calling [blitter.com/go/xs/logger.LogNotice]" ] - "blitter.com/go/xs/xsd.main$2$1" -> "(*blitter.com/go/xs/xsnet.Conn).Close" [ color="saddlebrown" tooltip="at xsd.go:678: calling [(*blitter.com/go/xs/xsnet.Conn).Close]" ] - "blitter.com/go/xs/xsd.main" -> "(*blitter.com/go/xs/xsnet.Conn).HAlg" [ color="saddlebrown" tooltip="at xsd.go:656: calling [(*blitter.com/go/xs/xsnet.Conn).HAlg]\nat xsd.go:657: calling [(*blitter.com/go/xs/xsnet.Conn).HAlg]" ] - "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetCmd" [ color="saddlebrown" tooltip="at xsd.go:734: calling [(*blitter.com/go/xs.Session).SetCmd]" ] - "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$6" [ arrowhead="normalnoneodot" tooltip="at xsd.go:381: calling [blitter.com/go/xs/xsd.runShellAs$6]" ] - "blitter.com/go/xs/xsd.runServerToClientCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" tooltip="at xsd.go:224: calling [(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff]" ] - "blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/logger.LogNotice" [ color="saddlebrown" tooltip="at xsd.go:595: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:600: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:605: calling [blitter.com/go/xs/logger.LogNotice]" ] - "blitter.com/go/xs/xsd.main" -> "(*blitter.com/go/xs/xsnet.Conn).Close" [ color="saddlebrown" tooltip="at xsd.go:651: calling [(*blitter.com/go/xs/xsnet.Conn).Close]\nat xsd.go:655: calling [(*blitter.com/go/xs/xsnet.Conn).Close]\nat xsd.go:659: calling [(*blitter.com/go/xs/xsnet.Conn).Close]" ] - "blitter.com/go/xs/xsd.runShellAs$6" -> "(vendor/golang.org/x/net/idna.labelError).Error" [ style="dashed" color="saddlebrown" tooltip="at xsd.go:385: calling [(vendor/golang.org/x/net/idna.labelError).Error]" ] - "blitter.com/go/xs/xsd.main" -> "(blitter.com/go/xs/xsd.allowedCipherAlgs).allowed" [ tooltip="at xsd.go:652: calling [(blitter.com/go/xs/xsd.allowedCipherAlgs).allowed]" ] - "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs.GetTool" [ color="saddlebrown" tooltip="at xsd.go:308: calling [blitter.com/go/xs.GetTool]\nat xsd.go:297: calling [blitter.com/go/xs.GetTool]\nat xsd.go:303: calling [blitter.com/go/xs.GetTool]" ] - "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs.VerifyPass" [ color="saddlebrown" tooltip="at xsd.go:754: calling [blitter.com/go/xs.VerifyPass]" ] - "blitter.com/go/xs/xsd.runShellAs$4" -> "(context.deadlineExceededError).Error" [ style="dashed" color="saddlebrown" tooltip="at xsd.go:358: calling [(context.deadlineExceededError).Error]" ] - "blitter.com/go/xs/xsd.runShellAs$6" -> "(*github.com/pkg/errors.fundamental).Error" [ style="dashed" color="saddlebrown" tooltip="at xsd.go:385: calling [(*github.com/pkg/errors.fundamental).Error]" ] - "blitter.com/go/xs/xsd.runServerToClientCopyAs" -> "blitter.com/go/xs.GetTool" [ color="saddlebrown" tooltip="at xsd.go:193: calling [blitter.com/go/xs.GetTool]" ] - "blitter.com/go/xs/xsd.runShellAs$2" -> "blitter.com/go/goutmp.Unput_utmp" [ color="saddlebrown" tooltip="at xsd.go:336: calling [blitter.com/go/goutmp.Unput_utmp]" ] - "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).ClearAuthCookie" [ color="saddlebrown" tooltip="at xsd.go:762: calling [(*blitter.com/go/xs.Session).ClearAuthCookie]" ] - "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.ptsName" [ tooltip="at xsd.go:331: calling [blitter.com/go/xs/xsd.ptsName]" ] - "blitter.com/go/xs/xsd.runShellAs$4" -> "(vendor/golang.org/x/net/idna.runeError).Error" [ style="dashed" color="saddlebrown" tooltip="at xsd.go:358: calling [(vendor/golang.org/x/net/idna.runeError).Error]" ] - "blitter.com/go/xs/xsd.runClientToServerCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ tooltip="at xsd.go:122: calling [(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff]" arrowhead="normalnoneodiamond" color="saddlebrown" ] - "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetConnHost" [ color="saddlebrown" tooltip="at xsd.go:718: calling [(*blitter.com/go/xs.Session).SetConnHost]" ] - "blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).Cmd" [ color="saddlebrown" tooltip="at xsd.go:745: calling [(blitter.com/go/xs.Session).Cmd]\nat xsd.go:797: calling [(blitter.com/go/xs.Session).Cmd]\nat xsd.go:813: calling [(blitter.com/go/xs.Session).Cmd]\nat xsd.go:829: calling [(blitter.com/go/xs.Session).Cmd]\nat xsd.go:853: calling [(blitter.com/go/xs.Session).Cmd]" ] - "blitter.com/go/xs/xsd.runShellAs$4" -> "(compress/flate.InternalError).Error" [ style="dashed" color="saddlebrown" tooltip="at xsd.go:358: calling [(compress/flate.InternalError).Error]" ] - "blitter.com/go/xs/xsd.runShellAs" -> "(*blitter.com/go/xs/xsnet.Conn).EnableChaff" [ color="saddlebrown" tooltip="at xsd.go:365: calling [(*blitter.com/go/xs/xsnet.Conn).EnableChaff]" ] - "blitter.com/go/xs/xsd.runShellAs$5" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ tooltip="at xsd.go:369: calling [(*blitter.com/go/xs/xsnet.Conn).DisableChaff]" color="saddlebrown" ] - "blitter.com/go/xs/xsd.runShellAs$6" -> "(compress/flate.CorruptInputError).Error" [ style="dashed" color="saddlebrown" tooltip="at xsd.go:385: calling [(compress/flate.CorruptInputError).Error]" ] - "blitter.com/go/xs/xsd.runShellAs$6" -> "(vendor/golang.org/x/net/idna.runeError).Error" [ color="saddlebrown" tooltip="at xsd.go:385: calling [(vendor/golang.org/x/net/idna.runeError).Error]" style="dashed" ] - "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.runClientToServerCopyAs" [ tooltip="at xsd.go:829: calling [blitter.com/go/xs/xsd.runClientToServerCopyAs]" ] - "blitter.com/go/xs/xsd.main" -> "(blitter.com/go/xs/xsnet.HKExListener).Close" [ arrowhead="normalnoneodiamond" color="saddlebrown" tooltip="at xsd.go:638: calling [(blitter.com/go/xs/xsnet.HKExListener).Close]" ] - "blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).AuthCookie" [ color="saddlebrown" tooltip="at xsd.go:749: calling [(blitter.com/go/xs.Session).AuthCookie]\nat xsd.go:754: calling [(blitter.com/go/xs.Session).AuthCookie]\nat xsd.go:756: calling [(blitter.com/go/xs.Session).AuthCookie]" ] - "blitter.com/go/xs/xsd.runServerToClientCopyAs" -> "(*blitter.com/go/xs/xsnet.Conn).DisableChaff" [ arrowhead="normalnoneodiamond" color="saddlebrown" tooltip="at xsd.go:223: calling [(*blitter.com/go/xs/xsnet.Conn).DisableChaff]" ] - "blitter.com/go/xs/xsd.runShellAs$6" -> "(context.deadlineExceededError).Error" [ style="dashed" color="saddlebrown" tooltip="at xsd.go:385: calling [(context.deadlineExceededError).Error]" ] - "blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).Who" [ tooltip="at xsd.go:745: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:749: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:754: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:756: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:768: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:779: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:780: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:782: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:787: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:789: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:796: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:797: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:802: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:804: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:811: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:813: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:818: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:820: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:828: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:829: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:834: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:836: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:852: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:853: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:855: calling [(blitter.com/go/xs.Session).Who]\nat xsd.go:858: calling [(blitter.com/go/xs.Session).Who]" color="saddlebrown" ] - "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs.NewAuthCtx" [ color="saddlebrown" tooltip="at xsd.go:749: calling [blitter.com/go/xs.NewAuthCtx]\nat xsd.go:754: calling [blitter.com/go/xs.NewAuthCtx]\nat xsd.go:756: calling [blitter.com/go/xs.NewAuthCtx]" ] - "blitter.com/go/xs/xsd.main" -> "(*blitter.com/go/xs/xsnet.Conn).SetupChaff" [ color="saddlebrown" tooltip="at xsd.go:666: calling [(*blitter.com/go/xs/xsnet.Conn).SetupChaff]" ] - "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/goutmp.GetHost" [ color="saddlebrown" tooltip="at xsd.go:778: calling [blitter.com/go/goutmp.GetHost]\nat xsd.go:795: calling [blitter.com/go/goutmp.GetHost]\nat xsd.go:810: calling [blitter.com/go/goutmp.GetHost]\nat xsd.go:827: calling [blitter.com/go/goutmp.GetHost]\nat xsd.go:851: calling [blitter.com/go/goutmp.GetHost]" ] - "blitter.com/go/xs/xsd.runShellAs$6" -> "(compress/flate.InternalError).Error" [ style="dashed" color="saddlebrown" tooltip="at xsd.go:385: calling [(compress/flate.InternalError).Error]" ] - "blitter.com/go/xs/xsd.runShellAs" -> "(*blitter.com/go/xs/xsnet.Conn).SetStatus" [ color="saddlebrown" tooltip="at xsd.go:407: calling [(*blitter.com/go/xs/xsnet.Conn).SetStatus]" ] - "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.runShellAs" [ tooltip="at xsd.go:782: calling [blitter.com/go/xs/xsd.runShellAs]\nat xsd.go:797: calling [blitter.com/go/xs/xsd.runShellAs]\nat xsd.go:813: calling [blitter.com/go/xs/xsd.runShellAs]" ] - "blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsd.main$1" [ tooltip="at xsd.go:610: calling [blitter.com/go/xs/xsd.main$1]" arrowhead="normalnoneodot" ] - "blitter.com/go/xs/xsd.main" -> "(blitter.com/go/xs/xsd.allowedHMACAlgs).allowed" [ tooltip="at xsd.go:656: calling [(blitter.com/go/xs/xsd.allowedHMACAlgs).allowed]" ] - "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/logger.LogNotice" [ color="saddlebrown" tooltip="at xsd.go:768: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:779: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:796: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:804: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:811: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:820: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:828: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:836: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:852: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:858: calling [blitter.com/go/xs/logger.LogNotice]" ] - "blitter.com/go/xs/xsd.runShellAs" -> "github.com/creack/pty.Start" [ color="saddlebrown" tooltip="at xsd.go:318: calling [github.com/creack/pty.Start]" ] - "blitter.com/go/xs/xsd.ptsName" -> "blitter.com/go/xs/xsd.ioctl" [ tooltip="at xsd.go:59: calling [blitter.com/go/xs/xsd.ioctl]" ] - "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/goutmp.Put_utmp" [ color="saddlebrown" tooltip="at xsd.go:335: calling [blitter.com/go/goutmp.Put_utmp]" ] - "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$5" [ arrowhead="normalnoneodiamond" tooltip="at xsd.go:368: calling [blitter.com/go/xs/xsd.runShellAs$5]" ] - "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/logger.LogErr" [ color="saddlebrown" tooltip="at xsd.go:787: calling [blitter.com/go/xs/logger.LogErr]\nat xsd.go:802: calling [blitter.com/go/xs/logger.LogErr]\nat xsd.go:834: calling [blitter.com/go/xs/logger.LogErr]\nat xsd.go:871: calling [blitter.com/go/xs/logger.LogErr]\nat xsd.go:855: calling [blitter.com/go/xs/logger.LogErr]" ] - "blitter.com/go/xs/xsd.main" -> "(*blitter.com/go/xs/xsnet.Conn).CAlg" [ color="saddlebrown" tooltip="at xsd.go:652: calling [(*blitter.com/go/xs/xsnet.Conn).CAlg]\nat xsd.go:653: calling [(*blitter.com/go/xs/xsnet.Conn).CAlg]" ] - "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs.AuthUserByPasswd" [ color="saddlebrown" tooltip="at xsd.go:756: calling [blitter.com/go/xs.AuthUserByPasswd]" ] - "blitter.com/go/xs/xsd.runClientToServerCopyAs" -> "blitter.com/go/xs.GetTool" [ color="saddlebrown" tooltip="at xsd.go:88: calling [blitter.com/go/xs.GetTool]" ] - "blitter.com/go/xs/xsd.runShellAs$4" -> "(compress/flate.CorruptInputError).Error" [ style="dashed" color="saddlebrown" tooltip="at xsd.go:358: calling [(compress/flate.CorruptInputError).Error]" ] - "blitter.com/go/xs/xsd.runShellAs$4" -> "(vendor/golang.org/x/net/idna.labelError).Error" [ style="dashed" color="saddlebrown" tooltip="at xsd.go:358: calling [(vendor/golang.org/x/net/idna.labelError).Error]" ] - "blitter.com/go/xs/xsd.runShellAs$5" -> "(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff" [ color="saddlebrown" tooltip="at xsd.go:370: calling [(*blitter.com/go/xs/xsnet.Conn).ShutdownChaff]" ] - "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/logger.LogDebug" [ tooltip="at xsd.go:409: calling [blitter.com/go/xs/logger.LogDebug]" color="saddlebrown" ] - "(blitter.com/go/xs/xsd.allowedCipherAlgs).allowed" -> "(*blitter.com/go/xs/xsnet.CSCipherAlg).String" [ color="saddlebrown" tooltip="at xsd.go:466: calling [(*blitter.com/go/xs/xsnet.CSCipherAlg).String]" ] - "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$1" [ arrowhead="normalnoneodiamond" tooltip="at xsd.go:325: calling [blitter.com/go/xs/xsd.runShellAs$1]" ] - "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetAuthCookie" [ color="saddlebrown" tooltip="at xsd.go:742: calling [(*blitter.com/go/xs.Session).SetAuthCookie]" ] - "blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).ConnHost" [ color="saddlebrown" tooltip="at xsd.go:745: calling [(blitter.com/go/xs.Session).ConnHost]\nat xsd.go:749: calling [(blitter.com/go/xs.Session).ConnHost]\nat xsd.go:780: calling [(blitter.com/go/xs.Session).ConnHost]" ] - "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs/xsd.GenAuthToken" [ tooltip="at xsd.go:780: calling [blitter.com/go/xs/xsd.GenAuthToken]" ] - "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$2" [ tooltip="at xsd.go:336: calling [blitter.com/go/xs/xsd.runShellAs$2]" arrowhead="normalnoneodiamond" ] - "blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsnet.Init" [ color="saddlebrown" tooltip="at xsd.go:584: calling [blitter.com/go/xs/xsnet.Init]" ] - "blitter.com/go/xs/xsd.main" -> "blitter.com/go/xs/xsnet.Listen" [ color="saddlebrown" tooltip="at xsd.go:634: calling [blitter.com/go/xs/xsnet.Listen]" ] - "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).RemoteAddr" [ color="saddlebrown" tooltip="at xsd.go:777: calling [(*blitter.com/go/xs/xsnet.Conn).RemoteAddr]\nat xsd.go:794: calling [(*blitter.com/go/xs/xsnet.Conn).RemoteAddr]\nat xsd.go:809: calling [(*blitter.com/go/xs/xsnet.Conn).RemoteAddr]\nat xsd.go:826: calling [(*blitter.com/go/xs/xsnet.Conn).RemoteAddr]\nat xsd.go:850: calling [(*blitter.com/go/xs/xsnet.Conn).RemoteAddr]" ] - "blitter.com/go/xs/xsd.runShellAs$4" -> "(*github.com/pkg/errors.fundamental).Error" [ style="dashed" color="saddlebrown" tooltip="at xsd.go:358: calling [(*github.com/pkg/errors.fundamental).Error]" ] - "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs/xsnet.Conn).WritePacket" [ color="saddlebrown" tooltip="at xsd.go:846: calling [(*blitter.com/go/xs/xsnet.Conn).WritePacket]" ] - "(blitter.com/go/xs/xsd.allowedHMACAlgs).allowed" -> "(*blitter.com/go/xs/xsnet.CSHmacAlg).String" [ color="saddlebrown" tooltip="at xsd.go:484: calling [(*blitter.com/go/xs/xsnet.CSHmacAlg).String]" ] - "blitter.com/go/xs/xsd.main$2" -> "blitter.com/go/xs.AuthUserByToken" [ color="saddlebrown" tooltip="at xsd.go:749: calling [blitter.com/go/xs.AuthUserByToken]" ] - "blitter.com/go/xs/xsd.main$2" -> "(blitter.com/go/xs.Session).Op" [ color="saddlebrown" tooltip="at xsd.go:745: calling [(blitter.com/go/xs.Session).Op]\nat xsd.go:775: calling [(blitter.com/go/xs.Session).Op]\nat xsd.go:792: calling [(blitter.com/go/xs.Session).Op]\nat xsd.go:807: calling [(blitter.com/go/xs.Session).Op]\nat xsd.go:823: calling [(blitter.com/go/xs.Session).Op]\nat xsd.go:847: calling [(blitter.com/go/xs.Session).Op]" ] - "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/goutmp.Put_lastlog_entry" [ color="saddlebrown" tooltip="at xsd.go:337: calling [blitter.com/go/goutmp.Put_lastlog_entry]" ] - "blitter.com/go/xs/xsd.runShellAs" -> "blitter.com/go/xs/xsd.runShellAs$3" [ arrowhead="normalnoneodot" tooltip="at xsd.go:345: calling [blitter.com/go/xs/xsd.runShellAs$3]" ] - "blitter.com/go/xs/xsd.main$1" -> "blitter.com/go/xs/logger.LogNotice" [ color="saddlebrown" tooltip="at xsd.go:615: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:619: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:623: calling [blitter.com/go/xs/logger.LogNotice]\nat xsd.go:625: calling [blitter.com/go/xs/logger.LogNotice]" ] - "blitter.com/go/xs/xsd.main$2" -> "(*blitter.com/go/xs.Session).SetOp" [ color="saddlebrown" tooltip="at xsd.go:702: calling [(*blitter.com/go/xs.Session).SetOp]\nat xsd.go:785: calling [(*blitter.com/go/xs.Session).SetOp]\nat xsd.go:800: calling [(*blitter.com/go/xs.Session).SetOp]\nat xsd.go:816: calling [(*blitter.com/go/xs.Session).SetOp]\nat xsd.go:832: calling [(*blitter.com/go/xs.Session).SetOp]\nat xsd.go:865: calling [(*blitter.com/go/xs.Session).SetOp]" ] -} diff --git a/xsd/xsd.go b/xsd/xsd.go deleted file mode 100755 index 62f9a39..0000000 --- a/xsd/xsd.go +++ /dev/null @@ -1,960 +0,0 @@ -// xsd server -// -// 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 main - -import ( - "bytes" - "crypto/rand" - "encoding/binary" - "encoding/hex" - "errors" - "flag" - "fmt" - "io" - "log" - "net/http" - "os" - "os/exec" - "os/signal" - "os/user" - "path" - "runtime" - "runtime/pprof" - "strings" - "sync" - "syscall" - "time" - "unsafe" - - "blitter.com/go/goutmp" - 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 - - cpuprofile string - memprofile string -) - -const ( - AuthTokenLen = 64 - LoginTimeoutSecs = 30 -) - -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 *xsnet.Conn, fpath string, chaffing bool) (exitStatus uint32, err error) { - u, _ := user.Lookup(who) - var uid, gid uint32 - fmt.Sscanf(u.Uid, "%d", &uid) - fmt.Sscanf(u.Gid, "%d", &gid) - log.Println("uid:", uid, "gid:", gid) - - // Need to clear server's env and set key vars of the - // target user. - os.Clearenv() - os.Setenv("HOME", u.HomeDir) - os.Setenv("TERM", ttype) - os.Setenv("XS_SESSION", "1") - - var c *exec.Cmd - cmdName := xs.GetTool("tar") - - var destDir string - if path.IsAbs(fpath) { - destDir = fpath - } else { - destDir = path.Join(u.HomeDir, fpath) - } - - cmdArgs := []string{"-xz", "-C", destDir} - - // 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{"-x", "-C", destDir, `--xform=s#.*/\(.*\)#\1#`} - fmt.Println(cmdName, cmdArgs) - c = exec.Command(cmdName, cmdArgs...) - - c.Dir = destDir - - //If os.Clearenv() isn't called by server above these will be seen - //in the client's session env. - //c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", - // "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who} - //c.Dir = u.HomeDir - c.SysProcAttr = &syscall.SysProcAttr{} - c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid} - c.Stdin = conn - c.Stdout = os.Stdout - c.Stderr = os.Stderr - - // === Set up connection keepalive to client - conn.StartupKeepAlive() // goroutine, returns immediately - defer conn.ShutdownKeepAlive() - - if chaffing { - conn.StartupChaff() - } - defer conn.ShutdownChaff() - - // Start the command (no pty) - log.Printf("[%v %v]\n", cmdName, cmdArgs) - err = c.Start() // returns immediately - ///////////// - // NOTE: There is, apparently, a bug in Go stdlib here. Start() - // can actually return immediately, on a command which *does* - // start but exits quickly, with c.Wait() error - // "c.Wait status: exec: not started". - // As in this example, attempting a client->server copy to - // a nonexistent remote dir (it's tar exiting right away, exitStatus - // 2, stderr - // /bin/tar -xz -C /home/someuser/nosuchdir - // stderr: fork/exec /bin/tar: no such file or directory - // - // In this case, c.Wait() won't give us the real - // exit status (is it lost?). - ///////////// - if err != nil { - log.Println("cmd exited immediately. Cannot get cmd.Wait().ExitStatus()") - err = errors.New("cmd exited prematurely") - //exitStatus = uint32(254) - exitStatus = xsnet.CSEExecFail - } else { - if err := c.Wait(); err != nil { - //fmt.Println("*** c.Wait() done ***") - if exiterr, ok := err.(*exec.ExitError); ok { - // The program has exited with an exit code != 0 - - // This works on both Unix and Windows. Although package - // syscall is generally platform dependent, WaitStatus is - // defined for both Unix and Windows and in both cases has - // an ExitStatus() method with the same signature. - if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { - exitStatus = uint32(status.ExitStatus()) - //err = errors.New("cmd returned nonzero status") - log.Printf("Exit Status: %d\n", exitStatus) - } - } - } - log.Println("*** client->server cp finished ***") - } - return -} - -// Perform a server->client copy -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 - return - } - var uid, gid uint32 - _, _ = fmt.Sscanf(u.Uid, "%d", &uid) - _, _ = fmt.Sscanf(u.Gid, "%d", &gid) - log.Println("uid:", uid, "gid:", gid) - - // Need to clear server's env and set key vars of the - // target user. - os.Clearenv() - _ = os.Setenv("HOME", u.HomeDir) - _ = os.Setenv("TERM", ttype) - _ = os.Setenv("XS_SESSION", "1") - - var c *exec.Cmd - cmdName := xs.GetTool("tar") - if !path.IsAbs(srcPath) { - srcPath = fmt.Sprintf("%s%c%s", u.HomeDir, os.PathSeparator, srcPath) - } - - srcDir, srcBase := path.Split(srcPath) - cmdArgs := []string{"-cz", "-C", srcDir, "-f", "-", srcBase} - - c = exec.Command(cmdName, cmdArgs...) - - //If os.Clearenv() isn't called by server above these will be seen - //in the client's session env. - //c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", "SUDO_USER=", - // "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who} - c.Dir = u.HomeDir - c.SysProcAttr = &syscall.SysProcAttr{} - c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid} - c.Stdout = conn - // Stderr sinkholing (or buffering to something other than stdout) - // is important. Any extraneous output to tarpipe messes up remote - // side as it's expecting pure tar data. - // (For example, if user specifies abs paths, tar outputs - // "Removing leading '/' from path names") - stdErrBuffer := new(bytes.Buffer) - c.Stderr = stdErrBuffer - //c.Stderr = nil - - // === Set up connection keepalive to client - conn.StartupKeepAlive() // goroutine, returns immediately - defer conn.ShutdownKeepAlive() - - if chaffing { - conn.StartupChaff() - } - //defer conn.Close() - defer conn.ShutdownChaff() - - // Start the command (no pty) - log.Printf("[%v %v]\n", cmdName, cmdArgs) - err = c.Start() // returns immediately - if err != nil { - log.Printf("Command finished with error: %v", err) - return xsnet.CSEExecFail, err // !? - } - if err := c.Wait(); err != nil { - //fmt.Println("*** c.Wait() done ***") - if exiterr, ok := err.(*exec.ExitError); ok { - // The program has exited with an exit code != 0 - - // This works on both Unix and Windows. Although package - // syscall is generally platform dependent, WaitStatus is - // defined for both Unix and Windows and in both cases has - // an ExitStatus() method with the same signature. - if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { - exitStatus = uint32(status.ExitStatus()) - if len(stdErrBuffer.Bytes()) > 0 { - log.Print(stdErrBuffer) - } - log.Printf("Exit Status: %d", exitStatus) - } - } - } - //fmt.Println("*** server->client cp finished ***") - return -} - -// Run a command (via default shell) as a specific user. Uses -// ptys to support commands which expect a terminal. //nolint:gofmt -func runShellAs(who, hname, ttype, cmd string, interactive bool, //nolint:funlen - conn *xsnet.Conn, chaffing bool) (exitStatus uint32, err error) { - var wg sync.WaitGroup - u, err := user.Lookup(who) - if err != nil { - exitStatus = 1 - return - } - var uid, gid uint32 - _, _ = fmt.Sscanf(u.Uid, "%d", &uid) - _, _ = fmt.Sscanf(u.Gid, "%d", &gid) - log.Println("uid:", uid, "gid:", gid) - - // Need to clear server's env and set key vars of the - // target user. - os.Clearenv() - _ = os.Setenv("HOME", u.HomeDir) - _ = os.Setenv("TERM", ttype) - _ = os.Setenv("XS_SESSION", "1") - - var c *exec.Cmd - - if interactive { - if useSysLogin { - // Use the server's login binary (post-auth) - // - // Things UNIX login does, like print the 'motd', - // and use the shell specified by /etc/passwd, will be done - // automagically, at the cost of another external tool - // dependency. - // - // One drawback of using 'login' is that the remote side - // cannot give us back the shell's exit code, since it - // exits back to 'login', which usually returns its own - // 0 status back to us. - // - // Note login will drop privs to the intended user for us. - // - c = exec.Command(xs.GetTool("login"), "-f", "-p", who) //nolint:gosec - } else { - // Run shell directly (which allows nonzero exit codes back to - // the local system upon shell exit, whereas 'login' does not.) - // - // Note we must drop privs ourselves for the user shell since - // we aren't using 'login' on the remote end which would do it - // for us. - // - c = exec.Command(xs.GetTool("bash"), "-i", "-l") //nolint:gosec - c.SysProcAttr = &syscall.SysProcAttr{} - c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid} - } - } else { - c = exec.Command(xs.GetTool("bash"), "-c", cmd) //nolint:gosec - c.SysProcAttr = &syscall.SysProcAttr{} - c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid} - } - //If os.Clearenv() isn't called by server above these will be seen - //in the client's session env. - //c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", - // "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who} - c.Dir = u.HomeDir - - // Start the command with a pty. - ptmx, err := pty.Start(c) // returns immediately with ptmx file - if err != nil { - 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() { - //logger.LogDebug(fmt.Sprintf("[Exited process was %d]", c.Process.Pid)) - _ = ptmx.Close() - }() - - // 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) - - conn.Pproc = c.Process.Pid - //fmt.Printf("[process %d started]\n", c.Process.Pid) - - log.Printf("[%s]\n", cmd) - if err != nil { - log.Printf("Command finished with error: %v", err) - } else { - // Watch for term resizes - // #gv:s/label=\"runShellAs\$2\"/label=\"termResizeWatcher\"/ - go func() { - for sz := range conn.WinCh { - log.Printf("[Setting term size to: %v %v]\n", sz.Rows, sz.Cols) - pty.Setsize(ptmx, &pty.Winsize{Rows: sz.Rows, Cols: sz.Cols}) //nolint:errcheck - } - log.Println("*** WinCh goroutine done ***") - }() - - // Copy stdin to the pty.. (bgnd goroutine) - // #gv:s/label=\"runShellAs\$3\"/label=\"stdinToPtyWorker\"/ - go func() { - _, e := io.Copy(ptmx, conn) - if e != nil { - log.Println("** stdin->pty ended **:", e.Error()) - } else { - log.Println("*** stdin->pty goroutine done ***") - } - }() - - // === Set up connection keepalive to client - conn.StartupKeepAlive() // goroutine, returns immediately - defer conn.ShutdownKeepAlive() - - if chaffing { - conn.StartupChaff() - } - // #gv:s/label=\"runShellAs\$4\"/label=\"deferChaffShutdown\"/ - defer func() { - conn.ShutdownChaff() - }() - - // ..and the pty to stdout. - // This may take some time exceeding that of the - // actual command's lifetime, so the c.Wait() below - // must synchronize with the completion of this goroutine - // to ensure all stdout data gets to the client before - // connection is closed. - wg.Add(1) - // #gv:s/label=\"runShellAs\$5\"/label=\"ptyToStdoutWorker\"/ - go func() { - defer wg.Done() - _, e := io.Copy(conn, ptmx) - if e != nil { - log.Println("** pty->stdout ended **:", e.Error()) - } else { - // The above io.Copy() will exit when the command attached - // to the pty exits - log.Println("*** pty->stdout goroutine done ***") - } - }() - - if err := c.Wait(); err != nil { - //fmt.Println("*** c.Wait() done ***") - if exiterr, ok := err.(*exec.ExitError); ok { - // The program has exited with an exit code != 0 - - // This works on both Unix and Windows. Although package - // syscall is generally platform dependent, WaitStatus is - // defined for both Unix and Windows and in both cases has - // 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) - } - } - conn.SetStatus(xsnet.CSOType(exitStatus)) - } else { - logger.LogDebug(fmt.Sprintf("*** Main proc has exited (%d) ***", c.ProcessState.ExitCode())) //nolint:errcheck - // 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 - } - return -} - -// GenAuthToken generates a pseudorandom auth token for a specific -// user from a specific host to allow non-interactive logins. -func GenAuthToken(who string, connhost string) string { - //hname, e := os.Hostname() - //if e != nil { - // hname = "#badhost#" - //} - hname := connhost - - token := make([]byte, AuthTokenLen) - _, _ = rand.Read(token) - return fmt.Sprintf("%s:%s:%s", hname, who, hex.EncodeToString(token)) -} - -var ( - aKEXAlgs allowedKEXAlgs - aCipherAlgs allowedCipherAlgs - aHMACAlgs allowedHMACAlgs -) - -type allowedKEXAlgs []string -type allowedCipherAlgs []string -type allowedHMACAlgs []string - -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() { //nolint:funlen,gocyclo - var vopt bool - var chaffEnabled bool - var chaffFreqMin uint - var chaffFreqMax uint - var chaffBytesMax uint - var dbg bool - var laddr string - var rekeySecs uint - var remodSupported bool // true: when rekeying, switch to random cipher/hmac alg - - var useSystemPasswd bool - - flag.BoolVar(&vopt, "v", false, "show version") - flag.UintVar(&rekeySecs, "r", 300, "rekey interval in `secs`") - flag.BoolVar(&remodSupported, "R", false, "Borg Countermeasures (remodulate cipher/hmac alg on each rekey)") - flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen") //nolint:gomnd,lll - flag.StringVar(&kcpMode, "K", "unused", `set to one of ["KCP_NONE","KCP_AES", "KCP_BLOWFISH", "KCP_CAST5", "KCP_SM4", "KCP_SALSA20", "KCP_SIMPLEXOR", "KCP_TEA", "KCP_3DES", "KCP_TWOFISH", "KCP_XTEA"] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP`) //nolint:lll - flag.BoolVar(&useSysLogin, "L", false, "use system login") - flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts") - flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min (msecs)") //nolint:gomnd - flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max (msecs)") //nolint:gomnd - flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max (bytes)") //nolint:gomnd - flag.BoolVar(&useSystemPasswd, "s", true, "use system shadow passwds") - flag.BoolVar(&dbg, "d", false, "debug logging") - flag.Var(&aKEXAlgs, "aK", "Allowed KEX `alg`s (eg. '-aK KEXAlgA -aK KEXAlgB ...')"+` - KEX_all - KEX_HERRADURA256 - KEX_HERRADURA512 - KEX_HERRADURA1024 - KEX_HERRADURA2048 - KEX_KYBER512 - KEX_KYBER768 - KEX_KYBER1024 - KEX_NEWHOPE - KEX_NEWHOPE_SIMPLE - KEX_FRODOKEM_1344AES - KEX_FRODOKEM_1344SHAKE - KEX_FRODOKEM_976AES - KEX_FRODOKEM_976SHAKE`) - flag.Var(&aCipherAlgs, "aC", "Allowed `cipher`s (eg. '-aC CAlgA -aC CAlgB ...')"+` - C_all - C_AES_256 - C_TWOFISH_128 - C_BLOWFISH_64 - C_CRYPTMT1 - C_HOPSCOTCH - C_CHACHA20_12`) - flag.Var(&aHMACAlgs, "aH", "Allowed `HMAC`s (eg. '-aH HMACAlgA -aH HMACAlgB ...')"+` - H_all - H_SHA256 - H_SHA512 - H_WHIRLPOOL`) - - flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to <`file`>") - flag.StringVar(&memprofile, "memprofile", "", "write memory profile to <`file`>") - - flag.Parse() - - if vopt { - fmt.Printf("version %s (%s)\n", version, gitCommit) - os.Exit(0) - } - - { - me, e := user.Current() - if e != nil || me.Uid != "0" { - log.Fatal("Must run as root.") - } - } - - // === Profiling instrumentation - - if cpuprofile != "" { - f, err := os.Create(cpuprofile) - if err != nil { - log.Fatal("could not create CPU profile: ", err) - } - defer f.Close() - fmt.Println("StartCPUProfile()") - if err := pprof.StartCPUProfile(f); err != nil { - log.Fatal("could not start CPU profile: ", err) //nolint:gocritic - } else { - defer pprof.StopCPUProfile() - } - - go func() { http.ListenAndServe("localhost:6060", nil) }() //nolint:errcheck,gosec - } - - // Enforce some sane min/max vals on chaff flags - if chaffFreqMin < 2 { //nolint:gomnd - chaffFreqMin = 2 - } - 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") - xsnet.Init(dbg, "xsd", logger.LOG_DAEMON|logger.LOG_DEBUG|logger.LOG_NOTICE|logger.LOG_ERR) - if dbg { - log.SetOutput(Log) - } else { - log.SetOutput(io.Discard) - } - - // Set up allowed algs, if specified (default allow all) - if len(aKEXAlgs) == 0 { - aKEXAlgs = []string{"none"} - } - logger.LogNotice(fmt.Sprintf("Allowed KEXAlgs: %v\n", aKEXAlgs)) //nolint:errcheck - - if len(aCipherAlgs) == 0 { - aCipherAlgs = []string{"none"} - } - logger.LogNotice(fmt.Sprintf("Allowed CipherAlgs: %v\n", aCipherAlgs)) //nolint:errcheck - - if len(aHMACAlgs) == 0 { - aHMACAlgs = []string{"none"} - } - logger.LogNotice(fmt.Sprintf("Allowed HMACAlgs: %v\n", aHMACAlgs)) //nolint: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)) //nolint:lll - go func() { - for { - sig := <-exitCh - switch sig { - case syscall.SIGTERM: //"terminated": - logger.LogNotice(fmt.Sprintf("[Got signal: %s]", sig.String())) //nolint:errcheck - signal.Reset() - syscall.Kill(0, syscall.SIGTERM) //nolint:errcheck - case syscall.SIGINT: //"interrupt": - logger.LogNotice(fmt.Sprintf("[Got signal: %s]", sig.String())) //nolint:errcheck - signal.Reset() - syscall.Kill(0, syscall.SIGINT) //nolint:errcheck - case syscall.SIGHUP: //"hangup": - logger.LogNotice(fmt.Sprintf("[Got signal: %s - nop]", sig.String())) //nolint:errcheck - if cpuprofile != "" || memprofile != "" { - dumpProf() - } - default: - logger.LogNotice(fmt.Sprintf("[Got signal: %s - ignored]", sig.String())) //nolint:errcheck - } - } - }() - - proto := "tcp" - if kcpMode != "unused" { - proto = "kcp" - } - l, err := xsnet.Listen(proto, laddr, kcpMode) - if err != nil { - log.Fatal(err) - } - defer l.Close() - - log.Println("Serving on", laddr) - for { - // Wait for a connection. - // Then check if client-proposed algs are allowed - conn, err := l.Accept() - //logger.LogDebug(fmt.Sprintf("l.Accept()\n")) - if err != nil { - log.Printf("Accept() got error(%v), hanging up.\n", err) - } else { - 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") - - // Only enable cipher alg changes on re-key if we were told - // to support it (launching xsd with -R), *and* the client - // proposes to use it. - if !remodSupported { - if (conn.Opts() & xsnet.CORemodulateShields) != 0 { - logger.LogDebug("[client proposed cipher/hmac remod, but we don't support it.]") - conn.Close() - continue - } - } else { - if conn.Opts()&xsnet.CORemodulateShields != 0 { - logger.LogDebug("[cipher/hmac remodulation active]") - } else { - logger.LogDebug("[cipher/hmac remodulation inactive]") - } - } - conn.RekeyHelper(rekeySecs) - - // Set up chaffing to client - // Will only start when runShellAs() is called - // after stdin/stdout are hooked up - conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // configure server->client chaffing - - // Handle the connection in a new goroutine. - // The loop then returns to accepting, so that - // multiple connections may be served concurrently. - go func(hc *xsnet.Conn) (e error) { - defer hc.ShutdownRekey() - defer hc.Close() - - // Start login timeout here and disconnect if user/pass phase stalls - loginTimeout := time.AfterFunc(LoginTimeoutSecs*time.Second, func() { - logger.LogNotice(fmt.Sprintln("Login timed out")) //nolint:errcheck - hc.Write([]byte{0}) //nolint:errcheck - hc.Close() - }) - - //We use io.ReadFull() here to guarantee we consume - //just the data we want for the xs.Session, and no more. - //Otherwise data will be sitting in the channel that isn't - //passed down to the command handlers. - var rec xs.Session - var len1, len2, len3, len4, len5, len6 uint32 - - n, err := fmt.Fscanf(hc, "%d %d %d %d %d %d\n", &len1, &len2, &len3, &len4, &len5, &len6) - log.Printf("xs.Session read:%d %d %d %d %d %d\n", len1, len2, len3, len4, len5, len6) - - if err != nil || n < 6 { - log.Println("[Bad xs.Session fmt]") - return err - } - - tmp := make([]byte, len1) - _, err = io.ReadFull(hc, tmp) - if err != nil { - log.Println("[Bad xs.Session.Op]") - return err - } - rec.SetOp(tmp) - - tmp = make([]byte, len2) - _, err = io.ReadFull(hc, tmp) - if err != nil { - log.Println("[Bad xs.Session.Who]") - return err - } - rec.SetWho(tmp) - - tmp = make([]byte, len3) - _, err = io.ReadFull(hc, tmp) - if err != nil { - log.Println("[Bad xs.Session.ConnHost]") - return err - } - rec.SetConnHost(tmp) - - tmp = make([]byte, len4) - _, err = io.ReadFull(hc, tmp) - if err != nil { - log.Println("[Bad xs.Session.TermType]") - return err - } - rec.SetTermType(tmp) - - tmp = make([]byte, len5) - _, err = io.ReadFull(hc, tmp) - if err != nil { - log.Println("[Bad xs.Session.Cmd]") - return err - } - rec.SetCmd(tmp) - - tmp = make([]byte, len6) - _, err = io.ReadFull(hc, tmp) - if err != nil { - log.Println("[Bad xs.Session.AuthCookie]") - return err - } - rec.SetAuthCookie(tmp) - - log.Printf("[xs.Session: op:%c who:%s connhost:%s cmd:%s auth:****]\n", - rec.Op()[0], string(rec.Who()), string(rec.ConnHost()), string(rec.Cmd())) - - var valid bool - var allowedCmds string // Currently unused - if xs.AuthUserByToken(xs.NewAuthCtx(), string(rec.Who()), string(rec.ConnHost()), string(rec.AuthCookie(true))) { - valid = true - } else { - if useSystemPasswd { - //var passErr error - valid, _ /*passErr*/ = xs.VerifyPass(xs.NewAuthCtx(), string(rec.Who()), string(rec.AuthCookie(true))) - } else { - valid, allowedCmds = xs.AuthUserByPasswd(xs.NewAuthCtx(), string(rec.Who()), string(rec.AuthCookie(true)), "/etc/xs.passwd") - } - } - - _ = loginTimeout.Stop() - // Security scrub - rec.ClearAuthCookie() - - // Tell client if auth was valid - if valid { - hc.Write([]byte{1}) //nolint:errcheck - } else { - logger.LogNotice(fmt.Sprintln("Invalid user", string(rec.Who()))) //nolint:errcheck - hc.Write([]byte{0}) //nolint:errcheck - return - } - - log.Printf("[allowedCmds:%s]\n", allowedCmds) - - if rec.Op()[0] == 'A' { - // Generate automated login token - addr := hc.RemoteAddr() - hname := goutmp.GetHost(addr.String()) - logger.LogNotice(fmt.Sprintf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname)) //nolint:errcheck - token := GenAuthToken(string(rec.Who()), string(rec.ConnHost())) - tokenCmd := fmt.Sprintf("echo %q | tee -a ~/%s", token, xsnet.XS_ID_AUTHTOKFILE) - cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), tokenCmd, false, hc, chaffEnabled) - // Returned hopefully via an EOF or exit/logout; - // Clear current op so user can enter next, or EOF - rec.SetOp([]byte{0}) - if runErr != nil { - logger.LogErr(fmt.Sprintf("[Error generating autologin token for %s@%s]\n", rec.Who(), hname)) //nolint:errcheck - } else { - log.Printf("[Autologin token generation completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus) - hc.SetStatus(xsnet.CSOType(cmdStatus)) - } - } else if rec.Op()[0] == 'c' { - // Non-interactive command - addr := hc.RemoteAddr() - hname := goutmp.GetHost(addr.String()) - logger.LogNotice(fmt.Sprintf("[Running command for [%s@%s]]\n", rec.Who(), hname)) //nolint:errcheck - cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), string(rec.Cmd()), false, hc, chaffEnabled) - // Returned hopefully via an EOF or exit/logout; - // Clear current op so user can enter next, or EOF - rec.SetOp([]byte{0}) - if runErr != nil { - logger.LogErr(fmt.Sprintf("[Error spawning cmd for %s@%s]\n", rec.Who(), hname)) //nolint:errcheck - } else { - logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) //nolint:errcheck - hc.SetStatus(xsnet.CSOType(cmdStatus)) - } - } else if rec.Op()[0] == 's' { - // Interactive session - addr := hc.RemoteAddr() - hname := goutmp.GetHost(addr.String()) - logger.LogNotice(fmt.Sprintf("[Running shell for [%s@%s]]\n", rec.Who(), hname)) //nolint:errcheck - - cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), string(rec.Cmd()), true, hc, chaffEnabled) - // Returned hopefully via an EOF or exit/logout; - // Clear current op so user can enter next, or EOF - rec.SetOp([]byte{0}) - if runErr != nil { - Log.Err(fmt.Sprintf("[Error spawning shell for %s@%s]\n", rec.Who(), hname)) //nolint:errcheck - } else { - logger.LogNotice(fmt.Sprintf("[Shell completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) //nolint:errcheck - hc.SetStatus(xsnet.CSOType(cmdStatus)) - } - } else if rec.Op()[0] == 'D' { - // File copy (destination) operation - client copy to server - log.Printf("[Client->Server copy]\n") - addr := hc.RemoteAddr() - hname := goutmp.GetHost(addr.String()) - logger.LogNotice(fmt.Sprintf("[c->s copy for %s@%s]\n", rec.Who(), hname)) //nolint:errcheck - cmdStatus, runErr := runClientToServerCopyAs(string(rec.Who()), string(rec.TermType()), hc, string(rec.Cmd()), chaffEnabled) - // Returned hopefully via an EOF or exit/logout; - // Clear current op so user can enter next, or EOF - rec.SetOp([]byte{0}) - if runErr != nil { - logger.LogErr(fmt.Sprintf("[c->s copy error for %s@%s]\n", rec.Who(), hname)) //nolint:errcheck - } else { - logger.LogNotice(fmt.Sprintf("[c->s copy completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) //nolint:errcheck - } - // TODO: Test this with huge files.. see Bug #22 - do we need to - // sync w/sender (client) that we've gotten all data? - hc.SetStatus(xsnet.CSOType(cmdStatus)) - - // Send CSOExitStatus *before* client closes channel - s := make([]byte, 4) //nolint:gomnd - binary.BigEndian.PutUint32(s, cmdStatus) - log.Printf("** cp writing closeStat %d at Close()\n", cmdStatus) - hc.WritePacket(s, xsnet.CSOExitStatus) //nolint:errcheck - } else if rec.Op()[0] == 'S' { - // File copy (src) operation - server copy to client - log.Printf("[Server->Client copy]\n") - addr := hc.RemoteAddr() - hname := goutmp.GetHost(addr.String()) - logger.LogNotice(fmt.Sprintf("[s->c copy for %s@%s]\n", rec.Who(), hname)) //nolint:errcheck - cmdStatus, runErr := runServerToClientCopyAs(string(rec.Who()), string(rec.TermType()), hc, string(rec.Cmd()), chaffEnabled) - if runErr != nil { - logger.LogErr(fmt.Sprintf("[s->c copy error for %s@%s]\n", rec.Who(), hname)) //nolint:errcheck - } else { - // Returned hopefully via an EOF or exit/logout; - logger.LogNotice(fmt.Sprintf("[s->c copy completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) //nolint:errcheck - } - - // Clear current op so user can enter next, or EOF - rec.SetOp([]byte{0}) - hc.SetStatus(xsnet.CSOType(cmdStatus)) - //fmt.Println("Waiting for EOF from other end.") - //_, _ = hc.Read(nil /*ackByte*/) - //fmt.Println("Got remote end ack.") - } else { - logger.LogErr(fmt.Sprintln("[Bad xs.Session]")) //nolint:errcheck - } - return - }(&conn) //nolint:errcheck - } // algs valid and not blacklisted - } // Accept() success - } //endfor - //logger.LogNotice(fmt.Sprintln("[Exiting]")) //nolint:errcheck -} - -func dumpProf() { - if cpuprofile != "" { - pprof.StopCPUProfile() - } - - if memprofile != "" { - f, err := os.Create(memprofile) - if err != nil { - log.Fatal("could not create memory profile: ", err) - } - defer f.Close() - runtime.GC() // get up-to-date statistics - if err := pprof.WriteHeapProfile(f); err != nil { - log.Fatal("could not write memory profile: ", err) //nolint:gocritic - } - } - - //os.Exit(status) -} diff --git a/xsnet/kcp.go b/xsnet/kcp.go deleted file mode 100644 index 95c91b3..0000000 --- a/xsnet/kcp.go +++ /dev/null @@ -1,129 +0,0 @@ -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() -}