mirror of https://gogs.blitter.com/RLabs/xs
Locking in WritePacket() apparently working, client and server-side chaffing functional
This commit is contained in:
parent
6d606bbbd9
commit
a49a5d4cc2
131
hkexnet.go
131
hkexnet.go
|
@ -23,6 +23,7 @@ import (
|
|||
"io"
|
||||
"log"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -52,12 +53,17 @@ type Conn struct {
|
|||
WinCh chan WinSize
|
||||
Rows uint16
|
||||
Cols uint16
|
||||
Rwmut sync.Mutex
|
||||
r cipher.Stream //read cipherStream
|
||||
rm hash.Hash
|
||||
w cipher.Stream //write cipherStream
|
||||
wm hash.Hash
|
||||
dBuf *bytes.Buffer //decrypt buffer for Read()
|
||||
|
||||
Rwmut *sync.Mutex
|
||||
chaff bool
|
||||
chaffMsecsMin int //msecs min interval
|
||||
chaffMsecsMax int //msecs max interval
|
||||
|
||||
r cipher.Stream //read cipherStream
|
||||
rm hash.Hash
|
||||
w cipher.Stream //write cipherStream
|
||||
wm hash.Hash
|
||||
dBuf *bytes.Buffer //decrypt buffer for Read()
|
||||
}
|
||||
|
||||
// ConnOpts returns the cipher/hmac options value, which is sent to the
|
||||
|
@ -141,7 +147,7 @@ func Dial(protocol string, ipport string, extensions ...string) (hc *Conn, err e
|
|||
return nil, err
|
||||
}
|
||||
// Init hkexnet.Conn hc over net.Conn c
|
||||
hc = &Conn{c: c, h: New(0, 0), dBuf: new(bytes.Buffer)}
|
||||
hc = &Conn{c: c, h: New(0, 0), Rwmut: &sync.Mutex{}, dBuf: new(bytes.Buffer)}
|
||||
hc.applyConnExtensions(extensions...)
|
||||
|
||||
// Send hkexnet.Conn parameters to remote side
|
||||
|
@ -268,13 +274,15 @@ func (hl HKExListener) Accept() (hc Conn, err error) {
|
|||
// Open raw Conn c
|
||||
c, err := hl.l.Accept()
|
||||
if err != nil {
|
||||
return Conn{c: nil, h: nil, cipheropts: 0, opts: 0,
|
||||
r: nil, w: nil}, err
|
||||
hc := Conn{c: nil, h: nil, cipheropts: 0, opts: 0, Rwmut: &sync.Mutex{},
|
||||
r: nil, w: nil}
|
||||
return hc, err
|
||||
}
|
||||
log.Println("[Accepted]")
|
||||
|
||||
hc = Conn{c: c, h: New(0, 0), WinCh: make(chan WinSize, 1),
|
||||
dBuf: new(bytes.Buffer)}
|
||||
Rwmut: &sync.Mutex{},
|
||||
dBuf: new(bytes.Buffer)}
|
||||
|
||||
// Read in hkexnet.Conn parameters over raw Conn c
|
||||
// d is value for Herradura key exchange
|
||||
|
@ -341,6 +349,7 @@ func (c Conn) Read(b []byte) (n int, err error) {
|
|||
log.Println("unexpected Read() err:", err)
|
||||
} else {
|
||||
log.Println("[Client hung up]")
|
||||
// TODO: Stop chaff if active
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
@ -440,40 +449,44 @@ func (c Conn) WritePacket(b []byte, op byte) (n int, err error) {
|
|||
var hmacOut []uint8
|
||||
var payloadLen uint32
|
||||
|
||||
log.Printf(" :>ptext:\r\n%s\r\n", hex.Dump(b))
|
||||
c.Rwmut.Lock()
|
||||
{
|
||||
log.Printf(" :>ptext:\r\n%s\r\n", hex.Dump(b))
|
||||
|
||||
payloadLen = uint32(len(b))
|
||||
payloadLen = uint32(len(b))
|
||||
|
||||
// Testing: '`1' will trigger a chaff packet
|
||||
//if payloadLen == 2 && string(b) == "`1" {
|
||||
// op = CSOChaff
|
||||
//}
|
||||
// Calculate hmac on payload
|
||||
c.wm.Write(b)
|
||||
hmacOut = c.wm.Sum(nil)[0:4]
|
||||
|
||||
// Calculate hmac on payload
|
||||
c.wm.Write(b)
|
||||
hmacOut = c.wm.Sum(nil)[0:4]
|
||||
log.Printf(" (%04x> HMAC(o):%s\r\n", payloadLen, hex.EncodeToString(hmacOut))
|
||||
|
||||
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: c.w, W: &wb}
|
||||
_, err = ws.Write(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Printf(" ->ctext:\r\n%s\r\n", hex.Dump(wb.Bytes()))
|
||||
|
||||
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: c.w, W: &wb}
|
||||
_, err = ws.Write(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
ctrlStatOp := op
|
||||
|
||||
err = binary.Write(c.c, binary.BigEndian, &ctrlStatOp)
|
||||
if err == nil {
|
||||
// Write hmac LSB, payloadLen followed by payload
|
||||
err = binary.Write(c.c, binary.BigEndian, hmacOut)
|
||||
if err == nil {
|
||||
err = binary.Write(c.c, binary.BigEndian, payloadLen)
|
||||
if err == nil {
|
||||
n, err = c.c.Write(wb.Bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Printf(" ->ctext:\r\n%s\r\n", hex.Dump(wb.Bytes()))
|
||||
c.Rwmut.Unlock()
|
||||
|
||||
ctrlStatOp := op
|
||||
|
||||
//{
|
||||
_ = binary.Write(c.c, binary.BigEndian, &ctrlStatOp)
|
||||
// Write hmac LSB, payloadLen followed by payload
|
||||
_ = binary.Write(c.c, binary.BigEndian, hmacOut)
|
||||
_ = binary.Write(c.c, binary.BigEndian, payloadLen)
|
||||
n, err = c.c.Write(wb.Bytes())
|
||||
//}
|
||||
if err != nil {
|
||||
//panic(err)
|
||||
log.Println(err)
|
||||
|
@ -481,12 +494,42 @@ func (c Conn) WritePacket(b []byte, op byte) (n int, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// hkexsh.Copy() is a modified version of io.Copy() with locking,
|
||||
// on a passed-in mutex, around the actual call to Write() to permit
|
||||
// multiple producers to write hkexsh buffers to the same destination.
|
||||
//
|
||||
// (Used to generate chaff during sessions)
|
||||
func Copy(m *sync.Mutex, dst io.Writer, src io.Reader) (written int64, err error) {
|
||||
func (c *Conn) Chaff(enable bool, msecsMin int, msecsMax int, szMax int) {
|
||||
c.chaff = enable
|
||||
c.chaffMsecsMin = msecsMin //move these to params of chaffHelper() ?
|
||||
c.chaffMsecsMax = msecsMax
|
||||
|
||||
if enable {
|
||||
log.Println("Chaffing ENABLED")
|
||||
c.chaffHelper(szMax)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper routine to spawn a chaffing goroutine for each Conn
|
||||
// TODO: if/when server->client chaffing is added, server must
|
||||
// todo: ensure this is turned off on client hangup
|
||||
func (c *Conn) chaffHelper(szMax int) {
|
||||
go func() {
|
||||
for {
|
||||
var nextDuration int
|
||||
if c.chaff {
|
||||
chaff := make([]byte, rand.Intn(szMax))
|
||||
min := c.chaffMsecsMin
|
||||
nextDuration = rand.Intn(c.chaffMsecsMax-min) + min
|
||||
_, _ = rand.Read(chaff)
|
||||
_, err := c.WritePacket(chaff, CSOChaff)
|
||||
if err != nil {
|
||||
log.Println("[ *** error writing chaff - end chaffing *** ]")
|
||||
break
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Duration(nextDuration) * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// hkexsh.Copy() is a modified version of io.Copy()
|
||||
func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
|
||||
// // If the reader has a WriteTo method, use it to do the copy.
|
||||
// // Avoids an allocation and a copy.
|
||||
// if wt, ok := src.(io.WriterTo); ok {
|
||||
|
@ -501,9 +544,7 @@ func Copy(m *sync.Mutex, dst io.Writer, src io.Reader) (written int64, err error
|
|||
for {
|
||||
nr, er := src.Read(buf)
|
||||
if nr > 0 {
|
||||
m.Lock()
|
||||
nw, ew := dst.Write(buf[0:nr])
|
||||
m.Unlock()
|
||||
if nw > 0 {
|
||||
written += int64(nw)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
|
@ -21,7 +20,6 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
hkexsh "blitter.com/go/hkexsh"
|
||||
isatty "github.com/mattn/go-isatty"
|
||||
|
@ -194,8 +192,6 @@ func main() {
|
|||
}
|
||||
}()
|
||||
|
||||
//m := &sync.Mutex{}
|
||||
|
||||
if isInteractive {
|
||||
// Handle pty resizes (notify server side)
|
||||
ch := make(chan os.Signal, 1)
|
||||
|
@ -213,40 +209,22 @@ func main() {
|
|||
panic(err)
|
||||
}
|
||||
termSzPacket := fmt.Sprintf("%d %d", rows, cols)
|
||||
conn.Rwmut.Lock()
|
||||
conn.WritePacket([]byte(termSzPacket), hkexsh.CSOTermSize)
|
||||
conn.Rwmut.Unlock()
|
||||
}
|
||||
}()
|
||||
ch <- syscall.SIGWINCH // Initial resize.
|
||||
|
||||
// client chaffing goroutine
|
||||
// TODO: Consider making this a feature of hkexsh.Conn itself
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
chaff := make([]byte, rand.Intn(512))
|
||||
nextDurationMin := 1000 //ms
|
||||
nextDuration := rand.Intn(5000-nextDurationMin) + nextDurationMin
|
||||
_, _ = rand.Read(chaff)
|
||||
conn.Rwmut.Lock()
|
||||
conn.WritePacket(chaff, hkexsh.CSOChaff)
|
||||
conn.Rwmut.Unlock()
|
||||
time.Sleep(time.Duration(nextDuration) * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
// client writer (to server) goroutine
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
// io.Copy() expects EOF so this will
|
||||
// Copy() expects EOF so this will
|
||||
// exit with outerr == nil
|
||||
//!_, outerr := io.Copy(conn, os.Stdin)
|
||||
conn.Chaff(true, 100, 500, 32) // enable client->server chaffing
|
||||
_, outerr := func(conn *hkexsh.Conn, r io.Reader) (w int64, e error) {
|
||||
return hkexsh.Copy(&conn.Rwmut, conn, r)
|
||||
return hkexsh.Copy(conn, r)
|
||||
}(conn, os.Stdin)
|
||||
|
||||
if outerr != nil {
|
||||
|
@ -258,6 +236,8 @@ func main() {
|
|||
}
|
||||
}
|
||||
log.Println("[Sent EOF]")
|
||||
//FIXME: regression circa. April 30 2018 on 'exit' from client,
|
||||
//fixme: Enter/RETURN required prior to actua client exit
|
||||
wg.Done() // client hung up, close WaitGroup to exit client
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -130,10 +130,17 @@ func runShellAs(who string, cmd string, interactive bool, conn hkexsh.Conn) (err
|
|||
|
||||
// Copy stdin to the pty.. (bgnd goroutine)
|
||||
go func() {
|
||||
_, _ = io.Copy(ptmx, conn)
|
||||
_, _ = hkexsh.Copy(ptmx, conn)
|
||||
}()
|
||||
|
||||
// ..and the pty to stdout.
|
||||
_, _ = io.Copy(conn, ptmx)
|
||||
// --(FIXME: server->client chaffing can't work here as-is, since we
|
||||
// --pty.Start()ed the command above, and that command has no
|
||||
// --knowledge of another thread which would do chaffing.
|
||||
// --Modify pty somehow to slave the command through hkexsh.Copy() ?
|
||||
conn.Chaff(true, 100, 500, 32)
|
||||
_, _ = hkexsh.Copy(conn, ptmx)
|
||||
//_, _ = io.Copy(conn, ptmx)
|
||||
|
||||
//err = c.Run() // returns when c finishes.
|
||||
|
||||
|
|
Loading…
Reference in New Issue