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