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"
 | 
						"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 New Issue