mirror of https://gogs.blitter.com/RLabs/xs
- got term resizing working (client SIGWINCH signals -> server_pty(rows,cols)
This commit is contained in:
parent
50f0433579
commit
8162707ffa
43
hkexnet.go
43
hkexnet.go
|
@ -29,19 +29,28 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
csoNone = iota // No error, normal packet
|
CSONone = iota // No error, normal packet
|
||||||
csoHmacInvalid // HMAC mismatch detect on remote end
|
CSOHmacInvalid // HMAC mismatch detected on remote end
|
||||||
csoChaff // This packet is a dummy, do not process beyond decryption
|
CSOTermSize // set term size (rows:cols)
|
||||||
|
CSOChaff // Dummy packet, do not pass beyond decryption
|
||||||
)
|
)
|
||||||
|
|
||||||
/*---------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
type WinSize struct {
|
||||||
|
Rows uint16
|
||||||
|
Cols uint16
|
||||||
|
}
|
||||||
|
|
||||||
// Conn is a HKex connection - a drop-in replacement for net.Conn
|
// Conn is a HKex connection - a drop-in replacement for net.Conn
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
c net.Conn // which also implements io.Reader, io.Writer, ...
|
c net.Conn // which also implements io.Reader, io.Writer, ...
|
||||||
h *HerraduraKEx
|
h *HerraduraKEx
|
||||||
cipheropts uint32 // post-KEx cipher/hmac options
|
cipheropts uint32 // post-KEx cipher/hmac options
|
||||||
opts uint32 // post-KEx protocol options (caller-defined)
|
opts uint32 // post-KEx protocol options (caller-defined)
|
||||||
|
WinCh chan WinSize
|
||||||
|
Rows uint16
|
||||||
|
Cols uint16
|
||||||
r cipher.Stream //read cipherStream
|
r cipher.Stream //read cipherStream
|
||||||
rm hash.Hash
|
rm hash.Hash
|
||||||
w cipher.Stream //write cipherStream
|
w cipher.Stream //write cipherStream
|
||||||
|
@ -262,7 +271,8 @@ func (hl HKExListener) Accept() (hc Conn, err error) {
|
||||||
}
|
}
|
||||||
log.Println("[Accepted]")
|
log.Println("[Accepted]")
|
||||||
|
|
||||||
hc = Conn{c: c, h: New(0, 0), dBuf: new(bytes.Buffer)}
|
hc = Conn{c: c, h: New(0, 0), WinCh: make(chan WinSize, 1),
|
||||||
|
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
|
||||||
|
@ -311,9 +321,10 @@ func (c Conn) Read(b []byte) (n int, err error) {
|
||||||
var hmacIn [4]uint8
|
var hmacIn [4]uint8
|
||||||
var payloadLen uint32
|
var payloadLen uint32
|
||||||
|
|
||||||
// Read ctrl/status opcode (csoHmacInvalid on hmac mismatch)
|
// Read ctrl/status opcode (CSOHmacInvalid on hmac mismatch)
|
||||||
err = binary.Read(c.c, binary.BigEndian, &ctrlStatOp)
|
err = binary.Read(c.c, binary.BigEndian, &ctrlStatOp)
|
||||||
if ctrlStatOp == csoHmacInvalid {
|
log.Printf("[ctrlStatOp: %v]\n", ctrlStatOp)
|
||||||
|
if ctrlStatOp == CSOHmacInvalid {
|
||||||
// Other side indicated channel tampering, close channel
|
// Other side indicated channel tampering, close channel
|
||||||
c.Close()
|
c.Close()
|
||||||
return 1, errors.New("** ALERT - remote end detected HMAC mismatch - possible channel tampering **")
|
return 1, errors.New("** ALERT - remote end detected HMAC mismatch - possible channel tampering **")
|
||||||
|
@ -379,8 +390,12 @@ 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]\n")
|
||||||
|
} else if ctrlStatOp == CSOTermSize {
|
||||||
|
fmt.Sscanf(string(payloadBytes), "%d %d", &c.Rows, &c.Cols)
|
||||||
|
log.Printf("[TermSize pkt: rows %v cols %v]\n", c.Rows, c.Cols)
|
||||||
|
c.WinCh <- WinSize{c.Rows, c.Cols}
|
||||||
} else {
|
} else {
|
||||||
c.dBuf.Write(payloadBytes)
|
c.dBuf.Write(payloadBytes)
|
||||||
//log.Printf("c.dBuf: %s\n", hex.Dump(c.dBuf.Bytes()))
|
//log.Printf("c.dBuf: %s\n", hex.Dump(c.dBuf.Bytes()))
|
||||||
|
@ -394,7 +409,7 @@ func (c Conn) Read(b []byte) (n int, err error) {
|
||||||
// Log alert if hmac didn't match, corrupted channel
|
// Log alert if hmac didn't match, corrupted channel
|
||||||
if !bytes.Equal(hTmp, []byte(hmacIn[0:])) /*|| hmacIn[0] > 0xf8*/ {
|
if !bytes.Equal(hTmp, []byte(hmacIn[0:])) /*|| hmacIn[0] > 0xf8*/ {
|
||||||
fmt.Println("** ALERT - detected HMAC mismatch, possible channel tampering **")
|
fmt.Println("** ALERT - detected HMAC mismatch, possible channel tampering **")
|
||||||
_, _ = c.c.Write([]byte{csoHmacInvalid})
|
_, _ = c.c.Write([]byte{CSOHmacInvalid})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,6 +428,11 @@ 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a byte slice with specified ctrlStatusOp byte
|
||||||
|
func (c Conn) WritePacket(b []byte, op byte) (n int, err error) {
|
||||||
//log.Printf("[Encrypting...]\r\n")
|
//log.Printf("[Encrypting...]\r\n")
|
||||||
var hmacOut []uint8
|
var hmacOut []uint8
|
||||||
var payloadLen uint32
|
var payloadLen uint32
|
||||||
|
@ -437,8 +457,7 @@ func (c Conn) Write(b []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()))
|
||||||
|
|
||||||
var ctrlStatOp byte
|
ctrlStatOp := op
|
||||||
ctrlStatOp = csoNone
|
|
||||||
_ = 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
|
||||||
|
|
|
@ -14,9 +14,12 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
"os/user"
|
"os/user"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
hkexsh "blitter.com/go/hkexsh"
|
hkexsh "blitter.com/go/hkexsh"
|
||||||
isatty "github.com/mattn/go-isatty"
|
isatty "github.com/mattn/go-isatty"
|
||||||
|
@ -30,6 +33,23 @@ type cmdSpec struct {
|
||||||
status int
|
status int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get terminal size using 'stty' command
|
||||||
|
// (Most portable btwn Linux and MSYS/win32, but
|
||||||
|
// TODO: remove external dep on 'stty' utility)
|
||||||
|
func getTermSize() (rows int, cols int, err error) {
|
||||||
|
cmd := exec.Command("stty", "size")
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
out, err := cmd.Output()
|
||||||
|
//fmt.Printf("out: %#v\n", string(out))
|
||||||
|
//fmt.Printf("err: %#v\n", err)
|
||||||
|
|
||||||
|
fmt.Sscanf(string(out), "%d %d\n", &rows, &cols)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Demo of a simple client that dials up to a simple test server to
|
// Demo of a simple client that dials up to a simple test server to
|
||||||
// send data.
|
// send data.
|
||||||
//
|
//
|
||||||
|
@ -77,6 +97,9 @@ func main() {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
// From this point on, conn is a secure encrypted channel
|
// From this point on, conn is a secure encrypted channel
|
||||||
|
|
||||||
|
rows := 0
|
||||||
|
cols := 0
|
||||||
|
|
||||||
// Set stdin in raw mode if it's an interactive session
|
// Set stdin in raw mode if it's an interactive session
|
||||||
// TODO: send flag to server side indicating this
|
// TODO: send flag to server side indicating this
|
||||||
// affects shell command used
|
// affects shell command used
|
||||||
|
@ -131,7 +154,9 @@ func main() {
|
||||||
authCookie: []byte(authCookie),
|
authCookie: []byte(authCookie),
|
||||||
status: 0}
|
status: 0}
|
||||||
|
|
||||||
_, err = fmt.Fprintf(conn, "%d %d %d %d\n", len(rec.op), len(rec.who), len(rec.cmd), len(rec.authCookie))
|
_, err = fmt.Fprintf(conn, "%d %d %d %d\n",
|
||||||
|
len(rec.op), len(rec.who), len(rec.cmd), len(rec.authCookie))
|
||||||
|
|
||||||
_, err = conn.Write(rec.op)
|
_, err = conn.Write(rec.op)
|
||||||
_, err = conn.Write(rec.who)
|
_, err = conn.Write(rec.who)
|
||||||
_, err = conn.Write(rec.cmd)
|
_, err = conn.Write(rec.cmd)
|
||||||
|
@ -168,6 +193,27 @@ func main() {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if isInteractive {
|
if isInteractive {
|
||||||
|
// Handle pty resizes (notify server side)
|
||||||
|
ch := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(ch, syscall.SIGWINCH)
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
for range ch {
|
||||||
|
// Query client's term size so we can communicate it to server
|
||||||
|
// pty after interactive session starts
|
||||||
|
rows, cols, err = getTermSize()
|
||||||
|
log.Printf("[rows %v cols %v]\n", rows, cols)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
termSzPacket := fmt.Sprintf("%d %d", rows, cols)
|
||||||
|
conn.WritePacket([]byte(termSzPacket), hkexsh.CSOTermSize)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
ch <- syscall.SIGWINCH // Initial resize.
|
||||||
|
|
||||||
// client writer (to server) goroutine
|
// client writer (to server) goroutine
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
|
|
@ -28,6 +28,8 @@ type cmdSpec struct {
|
||||||
who []byte
|
who []byte
|
||||||
cmd []byte
|
cmd []byte
|
||||||
authCookie []byte
|
authCookie []byte
|
||||||
|
termRows []byte
|
||||||
|
termCols []byte
|
||||||
status int
|
status int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +121,15 @@ func runShellAs(who string, cmd string, interactive bool, conn hkexsh.Conn) (err
|
||||||
}
|
}
|
||||||
// Make sure to close the pty at the end.
|
// Make sure to close the pty at the end.
|
||||||
defer func() { _ = ptmx.Close() }() // Best effort.
|
defer func() { _ = ptmx.Close() }() // Best effort.
|
||||||
|
|
||||||
|
// Watch for term resizes
|
||||||
|
go func() {
|
||||||
|
for sz := range conn.WinCh {
|
||||||
|
log.Printf("[Setting term size to: %v %v]\n", sz.Rows, sz.Cols)
|
||||||
|
pty.Setsize(ptmx, &pty.Winsize{Rows: sz.Rows, Cols: sz.Cols})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Copy stdin to the pty.. (bgnd goroutine)
|
// Copy stdin to the pty.. (bgnd goroutine)
|
||||||
go func() {
|
go func() {
|
||||||
_, _ = io.Copy(ptmx, conn)
|
_, _ = io.Copy(ptmx, conn)
|
||||||
|
@ -171,8 +182,8 @@ func main() {
|
||||||
// Wait for a connection.
|
// Wait for a connection.
|
||||||
conn, err := l.Accept()
|
conn, err := l.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Accept() got error(%v), hanging up.\n", err)
|
log.Printf("Accept() got error(%v), hanging up.\n", err)
|
||||||
conn.Close()
|
conn.Close()
|
||||||
//log.Fatal(err)
|
//log.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
log.Println("Accepted client")
|
log.Println("Accepted client")
|
||||||
|
|
Loading…
Reference in New Issue