mirror of
https://gogs.blitter.com/RLabs/xs
synced 2024-08-14 10:26:42 +00:00
Locking in WritePacket() apparently working, client and server-side chaffing functional
This commit is contained in:
parent
6d606bbbd9
commit
a49a5d4cc2
3 changed files with 100 additions and 72 deletions
131
hkexnet.go
131
hkexnet.go
|
@ -23,6 +23,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -52,12 +53,17 @@ type Conn struct {
|
||||||
WinCh chan WinSize
|
WinCh chan WinSize
|
||||||
Rows uint16
|
Rows uint16
|
||||||
Cols uint16
|
Cols uint16
|
||||||
Rwmut sync.Mutex
|
|
||||||
r cipher.Stream //read cipherStream
|
Rwmut *sync.Mutex
|
||||||
rm hash.Hash
|
chaff bool
|
||||||
w cipher.Stream //write cipherStream
|
chaffMsecsMin int //msecs min interval
|
||||||
wm hash.Hash
|
chaffMsecsMax int //msecs max interval
|
||||||
dBuf *bytes.Buffer //decrypt buffer for Read()
|
|
||||||
|
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
|
// 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
|
return nil, err
|
||||||
}
|
}
|
||||||
// Init hkexnet.Conn hc over net.Conn c
|
// 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...)
|
hc.applyConnExtensions(extensions...)
|
||||||
|
|
||||||
// Send hkexnet.Conn parameters to remote side
|
// Send hkexnet.Conn parameters to remote side
|
||||||
|
@ -268,13 +274,15 @@ func (hl HKExListener) Accept() (hc Conn, err error) {
|
||||||
// Open raw Conn c
|
// Open raw Conn c
|
||||||
c, err := hl.l.Accept()
|
c, err := hl.l.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Conn{c: nil, h: nil, cipheropts: 0, opts: 0,
|
hc := Conn{c: nil, h: nil, cipheropts: 0, opts: 0, Rwmut: &sync.Mutex{},
|
||||||
r: nil, w: nil}, err
|
r: nil, w: nil}
|
||||||
|
return hc, err
|
||||||
}
|
}
|
||||||
log.Println("[Accepted]")
|
log.Println("[Accepted]")
|
||||||
|
|
||||||
hc = Conn{c: c, h: New(0, 0), WinCh: make(chan WinSize, 1),
|
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
|
// Read in hkexnet.Conn parameters over raw Conn c
|
||||||
// d is value for Herradura key exchange
|
// 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)
|
log.Println("unexpected Read() err:", err)
|
||||||
} else {
|
} else {
|
||||||
log.Println("[Client hung up]")
|
log.Println("[Client hung up]")
|
||||||
|
// TODO: Stop chaff if active
|
||||||
}
|
}
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -440,40 +449,44 @@ func (c Conn) WritePacket(b []byte, op byte) (n int, err error) {
|
||||||
var hmacOut []uint8
|
var hmacOut []uint8
|
||||||
var payloadLen uint32
|
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
|
// Calculate hmac on payload
|
||||||
//if payloadLen == 2 && string(b) == "`1" {
|
c.wm.Write(b)
|
||||||
// op = CSOChaff
|
hmacOut = c.wm.Sum(nil)[0:4]
|
||||||
//}
|
|
||||||
|
|
||||||
// Calculate hmac on payload
|
log.Printf(" (%04x> HMAC(o):%s\r\n", payloadLen, hex.EncodeToString(hmacOut))
|
||||||
c.wm.Write(b)
|
|
||||||
hmacOut = c.wm.Sum(nil)[0:4]
|
|
||||||
|
|
||||||
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
|
ctrlStatOp := op
|
||||||
// The StreamWriter acts like a pipe, forwarding whatever is
|
|
||||||
// written to it through the cipher, encrypting as it goes
|
err = binary.Write(c.c, binary.BigEndian, &ctrlStatOp)
|
||||||
ws := &cipher.StreamWriter{S: c.w, W: &wb}
|
if err == nil {
|
||||||
_, err = ws.Write(b)
|
// Write hmac LSB, payloadLen followed by payload
|
||||||
if err != nil {
|
err = binary.Write(c.c, binary.BigEndian, hmacOut)
|
||||||
panic(err)
|
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 {
|
if err != nil {
|
||||||
//panic(err)
|
//panic(err)
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
@ -481,12 +494,42 @@ func (c Conn) WritePacket(b []byte, op byte) (n int, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// hkexsh.Copy() is a modified version of io.Copy() with locking,
|
func (c *Conn) Chaff(enable bool, msecsMin int, msecsMax int, szMax int) {
|
||||||
// on a passed-in mutex, around the actual call to Write() to permit
|
c.chaff = enable
|
||||||
// multiple producers to write hkexsh buffers to the same destination.
|
c.chaffMsecsMin = msecsMin //move these to params of chaffHelper() ?
|
||||||
//
|
c.chaffMsecsMax = msecsMax
|
||||||
// (Used to generate chaff during sessions)
|
|
||||||
func Copy(m *sync.Mutex, dst io.Writer, src io.Reader) (written int64, err error) {
|
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.
|
// // If the reader has a WriteTo method, use it to do the copy.
|
||||||
// // Avoids an allocation and a copy.
|
// // Avoids an allocation and a copy.
|
||||||
// if wt, ok := src.(io.WriterTo); ok {
|
// 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 {
|
for {
|
||||||
nr, er := src.Read(buf)
|
nr, er := src.Read(buf)
|
||||||
if nr > 0 {
|
if nr > 0 {
|
||||||
m.Lock()
|
|
||||||
nw, ew := dst.Write(buf[0:nr])
|
nw, ew := dst.Write(buf[0:nr])
|
||||||
m.Unlock()
|
|
||||||
if nw > 0 {
|
if nw > 0 {
|
||||||
written += int64(nw)
|
written += int64(nw)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
@ -21,7 +20,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
|
|
||||||
hkexsh "blitter.com/go/hkexsh"
|
hkexsh "blitter.com/go/hkexsh"
|
||||||
isatty "github.com/mattn/go-isatty"
|
isatty "github.com/mattn/go-isatty"
|
||||||
|
@ -194,8 +192,6 @@ func main() {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
//m := &sync.Mutex{}
|
|
||||||
|
|
||||||
if isInteractive {
|
if isInteractive {
|
||||||
// Handle pty resizes (notify server side)
|
// Handle pty resizes (notify server side)
|
||||||
ch := make(chan os.Signal, 1)
|
ch := make(chan os.Signal, 1)
|
||||||
|
@ -213,40 +209,22 @@ func main() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
termSzPacket := fmt.Sprintf("%d %d", rows, cols)
|
termSzPacket := fmt.Sprintf("%d %d", rows, cols)
|
||||||
conn.Rwmut.Lock()
|
|
||||||
conn.WritePacket([]byte(termSzPacket), hkexsh.CSOTermSize)
|
conn.WritePacket([]byte(termSzPacket), hkexsh.CSOTermSize)
|
||||||
conn.Rwmut.Unlock()
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ch <- syscall.SIGWINCH // Initial resize.
|
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
|
// client writer (to server) goroutine
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
// io.Copy() expects EOF so this will
|
// Copy() expects EOF so this will
|
||||||
// exit with outerr == nil
|
// exit with outerr == nil
|
||||||
//!_, outerr := io.Copy(conn, os.Stdin)
|
//!_, 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) {
|
_, 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)
|
}(conn, os.Stdin)
|
||||||
|
|
||||||
if outerr != nil {
|
if outerr != nil {
|
||||||
|
@ -258,6 +236,8 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Println("[Sent EOF]")
|
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
|
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)
|
// Copy stdin to the pty.. (bgnd goroutine)
|
||||||
go func() {
|
go func() {
|
||||||
_, _ = io.Copy(ptmx, conn)
|
_, _ = hkexsh.Copy(ptmx, conn)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// ..and the pty to stdout.
|
// ..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.
|
//err = c.Run() // returns when c finishes.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue