Chaff packets w/sync.Mutex to allow chaff & main goroutine to both input to server.

TODO: smart chaff, mutating or mimicking recent input.
This commit is contained in:
Russ Magee 2018-05-01 02:39:45 -07:00
parent 8162707ffa
commit b8a07e9648
3 changed files with 74 additions and 7 deletions

View file

@ -391,7 +391,7 @@ func (c Conn) Read(b []byte) (n int, err error) {
// Throw away pkt if it's chaff (ie., caller to Read() won't see this data) // Throw away pkt if it's chaff (ie., caller to Read() won't see this data)
if ctrlStatOp == CSOChaff { if ctrlStatOp == CSOChaff {
log.Printf("[Chaff pkt]\n") log.Printf("[Chaff pkt, discarded]\n")
} else if ctrlStatOp == CSOTermSize { } else if ctrlStatOp == CSOTermSize {
fmt.Sscanf(string(payloadBytes), "%d %d", &c.Rows, &c.Cols) fmt.Sscanf(string(payloadBytes), "%d %d", &c.Rows, &c.Cols)
log.Printf("[TermSize pkt: rows %v cols %v]\n", c.Rows, c.Cols) log.Printf("[TermSize pkt: rows %v cols %v]\n", c.Rows, c.Cols)
@ -428,7 +428,8 @@ func (c Conn) Read(b []byte) (n int, err error) {
// //
// See go doc io.Writer // See go doc io.Writer
func (c Conn) Write(b []byte) (n int, err error) { func (c Conn) Write(b []byte) (n int, err error) {
return c.WritePacket(b, CSONone) n, err = c.WritePacket(b, CSONone)
return n, err
} }
// Write a byte slice with specified ctrlStatusOp byte // Write a byte slice with specified ctrlStatusOp byte
@ -441,6 +442,11 @@ func (c Conn) WritePacket(b []byte, op byte) (n int, err error) {
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 // Calculate hmac on payload
c.wm.Write(b) c.wm.Write(b)
hmacOut = c.wm.Sum(nil)[0:4] hmacOut = c.wm.Sum(nil)[0:4]
@ -458,13 +464,14 @@ func (c Conn) WritePacket(b []byte, op byte) (n int, err error) {
log.Printf(" ->ctext:\r\n%s\r\n", hex.Dump(wb.Bytes())) log.Printf(" ->ctext:\r\n%s\r\n", hex.Dump(wb.Bytes()))
ctrlStatOp := op ctrlStatOp := op
_ = binary.Write(c.c, binary.BigEndian, &ctrlStatOp)
//{
_ = binary.Write(c.c, binary.BigEndian, &ctrlStatOp)
// Write hmac LSB, payloadLen followed by payload // Write hmac LSB, payloadLen followed by payload
_ = binary.Write(c.c, binary.BigEndian, hmacOut) _ = binary.Write(c.c, binary.BigEndian, hmacOut)
_ = binary.Write(c.c, binary.BigEndian, payloadLen) _ = binary.Write(c.c, binary.BigEndian, payloadLen)
n, err = c.c.Write(wb.Bytes()) n, err = c.c.Write(wb.Bytes())
//}
if err != nil { if err != nil {
//panic(err) //panic(err)
log.Println(err) log.Println(err)

View file

@ -20,6 +20,7 @@ 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"
@ -192,6 +193,8 @@ 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)
@ -209,11 +212,26 @@ func main() {
panic(err) panic(err)
} }
termSzPacket := fmt.Sprintf("%d %d", rows, cols) termSzPacket := fmt.Sprintf("%d %d", rows, cols)
m.Lock()
conn.WritePacket([]byte(termSzPacket), hkexsh.CSOTermSize) conn.WritePacket([]byte(termSzPacket), hkexsh.CSOTermSize)
m.Unlock()
} }
}() }()
ch <- syscall.SIGWINCH // Initial resize. ch <- syscall.SIGWINCH // Initial resize.
// client chaffing goroutine
wg.Add(1)
go func() {
defer wg.Done()
for {
m.Lock()
conn.WritePacket([]byte("CHAFF"), hkexsh.CSOChaff)
m.Unlock()
time.Sleep(10 * time.Millisecond)
}
}()
// client writer (to server) goroutine // client writer (to server) goroutine
wg.Add(1) wg.Add(1)
go func() { go func() {
@ -221,7 +239,11 @@ func main() {
// io.Copy() expects EOF so this will // io.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)
_, outerr := func(m *sync.Mutex, conn *hkexsh.Conn, r io.Reader) (w int64, e error) {
return safeCopy(m, conn, r)
}(m, conn, os.Stdin)
if outerr != nil { if outerr != nil {
log.Println(outerr) log.Println(outerr)
if outerr.Error() != "EOF" { if outerr.Error() != "EOF" {
@ -238,3 +260,43 @@ func main() {
// Wait until both stdin and stdout goroutines finish // Wait until both stdin and stdout goroutines finish
wg.Wait() wg.Wait()
} }
func safeCopy(m *sync.Mutex, 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 {
// return wt.WriteTo(dst)
// }
// // Similarly, if the writer has a ReadFrom method, use it to do the copy.
// if rt, ok := dst.(io.ReaderFrom); ok {
// return rt.ReadFrom(src)
// }
buf := make([]byte, 32*1024)
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)
}
if ew != nil {
err = ew
break
}
if nr != nw {
err = io.ErrShortWrite
break
}
}
if er != nil {
if er != io.EOF {
err = er
}
break
}
}
return written, err
}

View file

@ -28,8 +28,6 @@ type cmdSpec struct {
who []byte who []byte
cmd []byte cmd []byte
authCookie []byte authCookie []byte
termRows []byte
termCols []byte
status int status int
} }