mirror of https://gogs.blitter.com/RLabs/xs
Removed channel-based server loop goroutine, solving eaten initial byte issue.
Made receivers on hkex.Conn mutators *Conn again (whoops) TODO: Consider: padding (? probably not, XORKeyStream OFB/CBC/etc. modes prevent constant header/crib exposure, and would add lots of complexity to Read/Write) TODO: Add CTR, other modes
This commit is contained in:
parent
732005d9bf
commit
e3842e4219
|
@ -14,6 +14,14 @@ import (
|
|||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type cmdSpec struct {
|
||||
op []byte
|
||||
who []byte
|
||||
cmd []byte
|
||||
authCookie []byte
|
||||
status int
|
||||
}
|
||||
|
||||
// Demo of a simple client that dials up to a simple test server to
|
||||
// send data.
|
||||
//
|
||||
|
@ -39,6 +47,7 @@ func main() {
|
|||
flag.StringVar(&server, "s", "localhost:2000", "server hostname/address[:port]")
|
||||
flag.Parse()
|
||||
|
||||
//log.SetOutput(os.Stdout)
|
||||
log.SetOutput(ioutil.Discard)
|
||||
|
||||
conn, err := hkex.Dial("tcp", server, cAlg, hAlg)
|
||||
|
@ -60,6 +69,19 @@ func main() {
|
|||
fmt.Println("NOT A TTY")
|
||||
}
|
||||
|
||||
rec := &cmdSpec{op: []byte{'s'},
|
||||
who: []byte("ABCD"),
|
||||
cmd: []byte("EFGH"),
|
||||
authCookie: []byte("99"),
|
||||
status: 0}
|
||||
|
||||
_, err = fmt.Fprintf(conn, "%d %d %d %d\n", len(rec.op), len(rec.who), len(rec.cmd), len(rec.authCookie))
|
||||
_, err = conn.Write(rec.op)
|
||||
_, err = conn.Write(rec.who)
|
||||
_, err = conn.Write(rec.cmd)
|
||||
_, err = conn.Write(rec.authCookie)
|
||||
|
||||
//client reader (from server) goroutine
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
// By deferring a call to wg.Done(),
|
||||
|
@ -82,11 +104,12 @@ func main() {
|
|||
}
|
||||
}
|
||||
if isInteractive {
|
||||
log.Println("[Got Write EOF]")
|
||||
wg.Done() // client hanging up, close WaitGroup to exit client
|
||||
log.Println("[Got EOF]")
|
||||
wg.Done() // server hung up, close WaitGroup to exit client
|
||||
}
|
||||
}()
|
||||
|
||||
// client writer (to server) goroutine
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
@ -100,8 +123,8 @@ func main() {
|
|||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
log.Println("[Got Read EOF]")
|
||||
wg.Done() // server hung up, close WaitGroup to exit client
|
||||
log.Println("[Sent EOF]")
|
||||
wg.Done() // client hung up, close WaitGroup to exit client
|
||||
}()
|
||||
|
||||
// Wait until both stdin and stdout goroutines finish
|
||||
|
|
|
@ -4,13 +4,12 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
hkex "blitter.com/herradurakex"
|
||||
"github.com/kr/pty"
|
||||
|
@ -32,13 +31,13 @@ const (
|
|||
OpX = 'x' // exec
|
||||
)
|
||||
|
||||
type Op uint8
|
||||
//type Op uint8
|
||||
|
||||
type cmdRunner struct {
|
||||
op Op
|
||||
who string
|
||||
arg string
|
||||
authCookie string
|
||||
type cmdSpec struct {
|
||||
op []byte
|
||||
who []byte
|
||||
cmd []byte
|
||||
authCookie []byte
|
||||
status int
|
||||
}
|
||||
|
||||
|
@ -95,7 +94,7 @@ func main() {
|
|||
flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen")
|
||||
flag.Parse()
|
||||
|
||||
log.SetOutput(ioutil.Discard)
|
||||
log.SetOutput(os.Stdout /*ioutil.Discard*/)
|
||||
|
||||
// Listen on TCP port 2000 on all available unicast and
|
||||
// anycast IP addresses of the local system.
|
||||
|
@ -119,85 +118,63 @@ func main() {
|
|||
// multiple connections may be served concurrently.
|
||||
go func(c hkex.Conn) (e error) {
|
||||
defer c.Close()
|
||||
var connOp *byte = nil
|
||||
ch := make(chan []byte)
|
||||
chN := 0
|
||||
eCh := make(chan error)
|
||||
|
||||
// Start a goroutine to read from our net connection
|
||||
go func(ch chan []byte, eCh chan error) {
|
||||
for {
|
||||
// try to read the data
|
||||
data := make([]byte, 512)
|
||||
chN, err = c.Read(data)
|
||||
//We use io.ReadFull() here to guarantee we consume
|
||||
//just the data we want for the cmdSpec, and no more.
|
||||
//Otherwise data will be sitting in the channel that isn't
|
||||
//passed down to the command handlers.
|
||||
var rec cmdSpec
|
||||
var len1, len2, len3, len4 uint32
|
||||
|
||||
n, err := fmt.Fscanf(c, "%d %d %d %d\n", &len1, &len2, &len3, &len4)
|
||||
if err != nil || n < 4 {
|
||||
fmt.Println("[Bad cmdSpec fmt]")
|
||||
return err
|
||||
}
|
||||
fmt.Printf(" lens:%d %d %d %d\n", len1, len2, len3, len4)
|
||||
|
||||
rec.op = make([]byte, len1, len1)
|
||||
_, err = io.ReadFull(c, rec.op)
|
||||
if err != nil {
|
||||
// send an error if it's encountered
|
||||
eCh <- err
|
||||
return
|
||||
fmt.Println("[Bad cmdSpec.op]")
|
||||
return err
|
||||
}
|
||||
// send data if we read some.
|
||||
ch <- data[0:chN]
|
||||
}
|
||||
}(ch, eCh)
|
||||
|
||||
ticker := time.Tick(time.Second / 100)
|
||||
Term:
|
||||
// continuously read from the connection
|
||||
for {
|
||||
select {
|
||||
// This case means we recieved data on the connection
|
||||
case data := <-ch:
|
||||
// Do something with the data
|
||||
fmt.Printf("Client sent %+v\n", data[0:chN])
|
||||
if connOp == nil {
|
||||
// Initial xmit - get op byte
|
||||
// Have op here and first block of data[]
|
||||
connOp = new(byte)
|
||||
*connOp = data[0]
|
||||
fmt.Printf("[* connOp '%c']\n", *connOp)
|
||||
}
|
||||
if len(data) > 1 {
|
||||
data = data[1:chN]
|
||||
chN -= 1
|
||||
rec.who = make([]byte, len2, len2)
|
||||
_, err = io.ReadFull(c, rec.who)
|
||||
if err != nil {
|
||||
fmt.Println("[Bad cmdSpec.who]")
|
||||
return err
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
// From here, one could pass all subsequent data
|
||||
// between client/server attached to an exec.Cmd,
|
||||
// as data to/from a file, etc.
|
||||
if connOp != nil && *connOp == 's' {
|
||||
rec.cmd = make([]byte, len3, len3)
|
||||
_, err = io.ReadFull(c, rec.cmd)
|
||||
if err != nil {
|
||||
fmt.Println("[Bad cmdSpec.cmd]")
|
||||
return err
|
||||
}
|
||||
|
||||
rec.authCookie = make([]byte, len4, len4)
|
||||
_, err = io.ReadFull(c, rec.authCookie)
|
||||
if err != nil {
|
||||
fmt.Println("[Bad cmdSpec.authCookie]")
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("[cmdSpec: op:%c who:%s cmd:%s auth:%s]\n",
|
||||
rec.op[0], string(rec.who), string(rec.cmd), string(rec.authCookie))
|
||||
|
||||
if rec.op[0] == 's' {
|
||||
fmt.Println("[Running shell]")
|
||||
runCmdAs("larissa", "bash -l -i", conn)
|
||||
// Returned hopefully via an EOF or exit/logout;
|
||||
// Clear current op so user can enter next, or EOF
|
||||
connOp = nil
|
||||
rec.op[0] = 0
|
||||
fmt.Println("[Exiting shell]")
|
||||
conn.Close()
|
||||
}
|
||||
if strings.Trim(string(data), "\r\n") == "exit" {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
//fmt.Printf("Client sent %s\n", string(data))
|
||||
// This case means we got an error and the goroutine has finished
|
||||
case err := <-eCh:
|
||||
// handle our error then exit for loop
|
||||
if err.Error() == "EOF" {
|
||||
fmt.Printf("[Client disconnected]\n")
|
||||
} else {
|
||||
fmt.Printf("Error reading client data! (%+v)\n", err)
|
||||
fmt.Println("[Bad cmdSpec]")
|
||||
}
|
||||
break Term
|
||||
// This will timeout on the read.
|
||||
case <-ticker:
|
||||
// do nothing? this is just so we can time out if we need to.
|
||||
// you probably don't even need to have this here unless you want
|
||||
// do something specifically on the timeout.
|
||||
}
|
||||
}
|
||||
// Shut down the connection.
|
||||
//c.Close()
|
||||
return
|
||||
}(conn)
|
||||
}
|
||||
} //endfor
|
||||
fmt.Println("[Exiting]")
|
||||
}
|
||||
|
|
21
hkexchan.go
21
hkexchan.go
|
@ -8,6 +8,7 @@ import (
|
|||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
|
||||
|
@ -29,16 +30,6 @@ const (
|
|||
HmacNoneDisallowed
|
||||
)
|
||||
|
||||
type ChanOp uint8
|
||||
|
||||
const (
|
||||
ChanOpNop = '.'
|
||||
ChanOpEcho = 'e' // For testing - echo client data to stderr
|
||||
//ChanOpFileWrite = "w"
|
||||
//ChanOpFileRead = "r"
|
||||
//ChanOpRemoteCmd = "x"
|
||||
)
|
||||
|
||||
/*TODO: HMAC derived from HKEx FA.*/
|
||||
/* Support functionality to set up encryption after a channel has
|
||||
been negotiated via hkexnet.go
|
||||
|
@ -59,7 +50,7 @@ func (hc Conn) getStream(keymat *big.Int) (ret cipher.Stream) {
|
|||
ivlen = aes.BlockSize
|
||||
iv := keymat.Bytes()[aes.BlockSize : aes.BlockSize+ivlen]
|
||||
ret = cipher.NewOFB(block, iv)
|
||||
fmt.Printf("[cipher AES_256 (%d)]\n", copts)
|
||||
log.Printf("[cipher AES_256 (%d)]\n", copts)
|
||||
break
|
||||
case CAlgTwofish128:
|
||||
key = keymat.Bytes()[0:twofish.BlockSize]
|
||||
|
@ -67,7 +58,7 @@ func (hc Conn) getStream(keymat *big.Int) (ret cipher.Stream) {
|
|||
ivlen = twofish.BlockSize
|
||||
iv := keymat.Bytes()[twofish.BlockSize : twofish.BlockSize+ivlen]
|
||||
ret = cipher.NewOFB(block, iv)
|
||||
fmt.Printf("[cipher TWOFISH_128 (%d)]\n", copts)
|
||||
log.Printf("[cipher TWOFISH_128 (%d)]\n", copts)
|
||||
break
|
||||
case CAlgBlowfish64:
|
||||
key = keymat.Bytes()[0:blowfish.BlockSize]
|
||||
|
@ -84,9 +75,10 @@ func (hc Conn) getStream(keymat *big.Int) (ret cipher.Stream) {
|
|||
// copy what's needed whereas blowfish does no such check.
|
||||
iv := keymat.Bytes()[blowfish.BlockSize : blowfish.BlockSize+ivlen]
|
||||
ret = cipher.NewOFB(block, iv)
|
||||
fmt.Printf("[cipher BLOWFISH_64 (%d)]\n", copts)
|
||||
log.Printf("[cipher BLOWFISH_64 (%d)]\n", copts)
|
||||
break
|
||||
default:
|
||||
log.Printf("[invalid cipher (%d)]\n", copts)
|
||||
fmt.Printf("DOOFUS SET A VALID CIPHER ALG (%d)\n", copts)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -94,9 +86,10 @@ func (hc Conn) getStream(keymat *big.Int) (ret cipher.Stream) {
|
|||
hopts := (hc.cipheropts >> 8) & 0xFF
|
||||
switch hopts {
|
||||
case HmacSHA256:
|
||||
fmt.Printf("[nop HmacSHA256 (%d)]\n", hopts)
|
||||
log.Printf("[nop HmacSHA256 (%d)]\n", hopts)
|
||||
break
|
||||
default:
|
||||
log.Printf("[invalid hmac (%d)]\n", hopts)
|
||||
fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
|
47
hkexnet.go
47
hkexnet.go
|
@ -39,7 +39,6 @@ type Conn struct {
|
|||
h *HerraduraKEx
|
||||
cipheropts uint32 // post-KEx cipher/hmac options
|
||||
opts uint32 // post-KEx protocol options (caller-defined)
|
||||
op uint8 // post-KEx 'op' (caller-defined)
|
||||
r cipher.Stream
|
||||
w cipher.Stream
|
||||
}
|
||||
|
@ -57,7 +56,7 @@ func (c Conn) ConnOpts() uint32 {
|
|||
// peer as part of KEx but not part of the KEx itself.
|
||||
//
|
||||
// opts - bitfields for cipher and hmac alg. to use after KEx
|
||||
func (c Conn) SetConnOpts(copts uint32) {
|
||||
func (c *Conn) SetConnOpts(copts uint32) {
|
||||
c.cipheropts = copts
|
||||
}
|
||||
|
||||
|
@ -77,31 +76,11 @@ func (c Conn) Opts() uint32 {
|
|||
// of the KEx of encryption info used by the connection.
|
||||
//
|
||||
// opts - a uint32, caller-defined
|
||||
func (c Conn) SetOpts(opts uint32) {
|
||||
func (c *Conn) SetOpts(opts uint32) {
|
||||
c.opts = opts
|
||||
}
|
||||
|
||||
// Op returns the 'op' value, which is sent to the peer
|
||||
// but is not itself part of the KEx or connection (cipher/hmac) setup.
|
||||
//
|
||||
// Consumers of this lib may use this to indicate connection-specific
|
||||
// operations not part of the KEx or encryption info used by the connection.
|
||||
func (c Conn) Op() uint8 {
|
||||
return c.op
|
||||
}
|
||||
|
||||
// SetOp sets the 'op' value, which is sent to the peer
|
||||
// but is not itself part of the KEx or connection (cipher/hmac) setup.
|
||||
//
|
||||
// Consumers of this lib may use this to indicate connection-specific
|
||||
// operations not part of the KEx or encryption info used by the connection.
|
||||
//
|
||||
// op - a uint8, caller-defined
|
||||
func (c Conn) SetOp(op uint8) {
|
||||
c.op = op
|
||||
}
|
||||
|
||||
func (c Conn) applyConnExtensions(extensions ...string) {
|
||||
func (c *Conn) applyConnExtensions(extensions ...string) {
|
||||
for _, s := range extensions {
|
||||
switch s {
|
||||
case "C_AES_256":
|
||||
|
@ -143,20 +122,20 @@ func Dial(protocol string, ipport string, extensions ...string) (hc *Conn, err e
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hc = &Conn{c: c, h: New(0, 0), cipheropts: 0, opts: 0, op: 0, r: nil, w: nil}
|
||||
hc = &Conn{c: c, h: New(0, 0), cipheropts: 0, opts: 0, r: nil, w: nil}
|
||||
|
||||
hc.applyConnExtensions(extensions...)
|
||||
|
||||
fmt.Fprintf(c, "0x%s\n%08x:%08x:%02x\n", hc.h.d.Text(16),
|
||||
hc.cipheropts, hc.opts, hc.op)
|
||||
fmt.Fprintf(c, "0x%s\n%08x:%08x\n", hc.h.d.Text(16),
|
||||
hc.cipheropts, hc.opts)
|
||||
|
||||
d := big.NewInt(0)
|
||||
_, err = fmt.Fscanln(c, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = fmt.Fscanf(c, "%08x:%08x:%02x\n",
|
||||
&hc.cipheropts, &hc.opts, &hc.op)
|
||||
_, err = fmt.Fscanf(c, "%08x:%08x\n",
|
||||
&hc.cipheropts, &hc.opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -265,15 +244,15 @@ func (hl HKExListener) Accept() (hc Conn, err error) {
|
|||
}
|
||||
log.Println("[Accepted]")
|
||||
|
||||
hc = Conn{c: c, h: New(0, 0), cipheropts: 0, opts: 0, op: 0, r: nil, w: nil}
|
||||
hc = Conn{c: c, h: New(0, 0), cipheropts: 0, opts: 0, r: nil, w: nil}
|
||||
|
||||
d := big.NewInt(0)
|
||||
_, err = fmt.Fscanln(c, d)
|
||||
if err != nil {
|
||||
return hc, err
|
||||
}
|
||||
_, err = fmt.Fscanf(c, "%08x:%08x:%02x\n",
|
||||
&hc.cipheropts, &hc.opts, &hc.op)
|
||||
_, err = fmt.Fscanf(c, "%08x:%08x\n",
|
||||
&hc.cipheropts, &hc.opts)
|
||||
if err != nil {
|
||||
return hc, err
|
||||
}
|
||||
|
@ -283,8 +262,8 @@ func (hl HKExListener) Accept() (hc Conn, err error) {
|
|||
hc.h.FA()
|
||||
log.Printf("**(s)** FA:%s\n", hc.h.fa)
|
||||
|
||||
fmt.Fprintf(c, "0x%s\n%08x:%08x:%02x\n", hc.h.d.Text(16),
|
||||
hc.cipheropts, hc.opts, hc.op)
|
||||
fmt.Fprintf(c, "0x%s\n%08x:%08x\n", hc.h.d.Text(16),
|
||||
hc.cipheropts, hc.opts)
|
||||
|
||||
hc.r = hc.getStream(hc.h.fa)
|
||||
hc.w = hc.getStream(hc.h.fa)
|
||||
|
|
Loading…
Reference in New Issue