mirror of https://gogs.blitter.com/RLabs/xs
-Converted exit status to uint32 (0-255: UNIX exit codes), above for OOB (out-of-band) status
-Failed auth for shell logins now returns extended code CSEBadAuth to client
This commit is contained in:
parent
8a24fb113f
commit
9ff35a69fe
|
@ -34,6 +34,16 @@ import (
|
|||
"blitter.com/go/hkexsh/herradurakex"
|
||||
)
|
||||
|
||||
// const CSExtendedCode - extended (>255 UNIX exit status) codes
|
||||
// This indicate channel-related or internal errors
|
||||
const (
|
||||
CSEBadAuth = 1024 // failed login
|
||||
CSETruncCSO // No CSOExitStatus in payload
|
||||
CSEStillOpen // Channel closed unexpectedly
|
||||
CSEExecFail // cmd.Start() (exec) failed
|
||||
CSEPtyExecFail // pty.Start() (exec w/pty) failed
|
||||
)
|
||||
|
||||
const (
|
||||
CSONone = iota // No error, normal packet
|
||||
CSOHmacInvalid // HMAC mismatch detected on remote end
|
||||
|
@ -77,7 +87,7 @@ type (
|
|||
|
||||
chaff ChaffConfig
|
||||
|
||||
closeStat *uint8 // close status
|
||||
closeStat *uint32 // close status (CSOExitStatus)
|
||||
r cipher.Stream //read cipherStream
|
||||
rm hash.Hash
|
||||
w cipher.Stream //write cipherStream
|
||||
|
@ -86,11 +96,11 @@ type (
|
|||
}
|
||||
)
|
||||
|
||||
func (hc Conn) GetStatus() uint8 {
|
||||
func (hc Conn) GetStatus() uint32 {
|
||||
return *hc.closeStat
|
||||
}
|
||||
|
||||
func (hc *Conn) SetStatus(stat uint8) {
|
||||
func (hc *Conn) SetStatus(stat uint32) {
|
||||
*hc.closeStat = stat
|
||||
//fmt.Println("closeStat:", *hc.closeStat)
|
||||
log.Println("closeStat:", *hc.closeStat)
|
||||
|
@ -177,7 +187,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{m: &sync.Mutex{}, c: c, closeStat: new(uint8), h: hkex.New(0, 0), dBuf: new(bytes.Buffer)}
|
||||
hc = &Conn{m: &sync.Mutex{}, c: c, closeStat: new(uint32), h: hkex.New(0, 0), dBuf: new(bytes.Buffer)}
|
||||
hc.applyConnExtensions(extensions...)
|
||||
|
||||
// Send hkexnet.Conn parameters to remote side
|
||||
|
@ -205,14 +215,16 @@ func Dial(protocol string, ipport string, extensions ...string) (hc *Conn, err e
|
|||
|
||||
hc.r, hc.rm, err = hc.getStream(hc.h.FA())
|
||||
hc.w, hc.wm, err = hc.getStream(hc.h.FA())
|
||||
*hc.closeStat = 99 // open or prematurely-closed status
|
||||
*hc.closeStat = CSEStillOpen // open or prematurely-closed status
|
||||
return
|
||||
}
|
||||
|
||||
// Close a hkex.Conn
|
||||
func (hc *Conn) Close() (err error) {
|
||||
hc.DisableChaff()
|
||||
hc.WritePacket([]byte{byte(*hc.closeStat)}, CSOExitStatus)
|
||||
s := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(s, *hc.closeStat)
|
||||
hc.WritePacket(s, CSOExitStatus)
|
||||
err = hc.c.Close()
|
||||
log.Println("[Conn Closing]")
|
||||
return
|
||||
|
@ -308,13 +320,13 @@ func (hl *HKExListener) Accept() (hc Conn, err error) {
|
|||
// Open raw Conn c
|
||||
c, err := hl.l.Accept()
|
||||
if err != nil {
|
||||
hc := Conn{m: &sync.Mutex{}, c: nil, h: nil, closeStat: new(uint8), cipheropts: 0, opts: 0,
|
||||
hc := Conn{m: &sync.Mutex{}, c: nil, h: nil, closeStat: new(uint32), cipheropts: 0, opts: 0,
|
||||
r: nil, w: nil}
|
||||
return hc, err
|
||||
}
|
||||
log.Println("[Accepted]")
|
||||
|
||||
hc = Conn{m: &sync.Mutex{}, c: c, h: hkex.New(0, 0), closeStat: new(uint8), WinCh: make(chan WinSize, 1),
|
||||
hc = Conn{m: &sync.Mutex{}, c: c, h: hkex.New(0, 0), closeStat: new(uint32), WinCh: make(chan WinSize, 1),
|
||||
dBuf: new(bytes.Buffer)}
|
||||
|
||||
// Read in hkexnet.Conn parameters over raw Conn c
|
||||
|
@ -438,7 +450,7 @@ func (hc Conn) Read(b []byte) (n int, err error) {
|
|||
hc.WinCh <- WinSize{hc.Rows, hc.Cols}
|
||||
} else if ctrlStatOp == CSOExitStatus {
|
||||
if len(payloadBytes) > 0 {
|
||||
hc.SetStatus(payloadBytes[0])
|
||||
hc.SetStatus(binary.BigEndian.Uint32(payloadBytes))
|
||||
//!// If remote end is closing with an error, reply we're closing ours
|
||||
//!if hc.GetStatus() != 0 {
|
||||
//! log.Print("CSOExitStatus:", hc.GetStatus())
|
||||
|
@ -446,7 +458,7 @@ func (hc Conn) Read(b []byte) (n int, err error) {
|
|||
//!}
|
||||
} else {
|
||||
log.Println("[truncated payload, cannot determine CSOExitStatus]")
|
||||
*hc.closeStat = 98
|
||||
*hc.closeStat = CSETruncCSO
|
||||
}
|
||||
} else {
|
||||
hc.dBuf.Write(payloadBytes)
|
||||
|
@ -458,7 +470,7 @@ func (hc Conn) Read(b []byte) (n int, err error) {
|
|||
hTmp := hc.rm.Sum(nil)[0:4]
|
||||
log.Printf("<%04x) HMAC:(i)%s (c)%02x\r\n", decryptN, hex.EncodeToString([]byte(hmacIn[0:])), hTmp)
|
||||
|
||||
if *hc.closeStat > 90 {
|
||||
if *hc.closeStat == CSETruncCSO {
|
||||
log.Println("[cannot verify HMAC]")
|
||||
} else {
|
||||
// Log alert if hmac didn't match, corrupted channel
|
||||
|
|
|
@ -9,6 +9,7 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -34,7 +35,7 @@ type cmdSpec struct {
|
|||
who []byte
|
||||
cmd []byte
|
||||
authCookie []byte
|
||||
status int // UNIX exit status is uint8, but os.Exit() wants int
|
||||
status uint32 // exit status (0-255 is std UNIX status)
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -98,7 +99,7 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other
|
|||
}
|
||||
|
||||
// doCopyMode begins a secure hkexsh local<->remote file copy operation.
|
||||
func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *cmdSpec) (err error, exitStatus int) {
|
||||
func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *cmdSpec) (err error, exitStatus uint32) {
|
||||
if remoteDest {
|
||||
fmt.Println("local files:", files, "remote filepath:", string(rec.cmd))
|
||||
|
||||
|
@ -161,7 +162,7 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *cmdSpec)
|
|||
// defined for both Unix and Windows and in both cases has
|
||||
// an ExitStatus() method with the same signature.
|
||||
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
exitStatus = status.ExitStatus()
|
||||
exitStatus = uint32(status.ExitStatus())
|
||||
log.Printf("Exit Status: %d", exitStatus)
|
||||
fmt.Print(stdErrBuffer)
|
||||
}
|
||||
|
@ -169,7 +170,9 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *cmdSpec)
|
|||
}
|
||||
//fmt.Println("*** client->server cp finished ***")
|
||||
// Signal other end transfer is complete
|
||||
conn.WritePacket([]byte{byte( /*255*/ rec.status)}, hkexnet.CSOExitStatus)
|
||||
s := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(s, rec.status)
|
||||
conn.WritePacket(s, hkexnet.CSOExitStatus)
|
||||
_, _ = conn.Read(nil /*ackByte*/)
|
||||
}
|
||||
} else {
|
||||
|
@ -209,7 +212,7 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *cmdSpec)
|
|||
// defined for both Unix and Windows and in both cases has
|
||||
// an ExitStatus() method with the same signature.
|
||||
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
exitStatus = status.ExitStatus()
|
||||
exitStatus = uint32(status.ExitStatus())
|
||||
log.Printf("Exit Status: %d", exitStatus)
|
||||
}
|
||||
}
|
||||
|
@ -217,7 +220,7 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, rec *cmdSpec)
|
|||
// return local status, if nonzero;
|
||||
// otherwise, return remote status if nonzero
|
||||
if exitStatus == 0 {
|
||||
exitStatus = int(conn.GetStatus())
|
||||
exitStatus = uint32(conn.GetStatus())
|
||||
}
|
||||
//fmt.Println("*** server->client cp finished ***")
|
||||
}
|
||||
|
@ -250,7 +253,7 @@ func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State,
|
|||
}
|
||||
}
|
||||
|
||||
rec.status = int(conn.GetStatus())
|
||||
rec.status = uint32(conn.GetStatus())
|
||||
log.Println("rec.status:", rec.status)
|
||||
|
||||
if isInteractive {
|
||||
|
@ -282,7 +285,7 @@ func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State,
|
|||
log.Println(outerr)
|
||||
fmt.Println(outerr)
|
||||
_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
|
||||
os.Exit(255)
|
||||
os.Exit(254)
|
||||
}
|
||||
log.Println("[Sent EOF]")
|
||||
}()
|
||||
|
@ -549,7 +552,15 @@ func main() {
|
|||
}
|
||||
|
||||
if rec.status != 0 {
|
||||
fmt.Fprintln(os.Stderr, "Remote end exited with status:", rec.status)
|
||||
fmt.Fprint(os.Stderr, "Remote end ")
|
||||
if rec.status == hkexnet.CSEBadAuth {
|
||||
// shell exit status can't hold CSEBadAuth (uint32)
|
||||
rec.status = 255
|
||||
fmt.Fprintln(os.Stderr, "replied: bad auth")
|
||||
} else {
|
||||
fmt.Fprintln(os.Stderr, "exited with status:", rec.status)
|
||||
}
|
||||
os.Exit(rec.status)
|
||||
|
||||
}
|
||||
os.Exit(int(rec.status))
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -40,7 +41,7 @@ type cmdSpec struct {
|
|||
|
||||
/* -------------------------------------------------------------- */
|
||||
// Perform a client->server copy
|
||||
func runClientToServerCopyAs(who string, conn hkexnet.Conn, fpath string, chaffing bool) (err error, exitStatus int) {
|
||||
func runClientToServerCopyAs(who string, conn hkexnet.Conn, fpath string, chaffing bool) (err error, exitStatus uint32) {
|
||||
u, _ := user.Lookup(who)
|
||||
var uid, gid uint32
|
||||
fmt.Sscanf(u.Uid, "%d", &uid)
|
||||
|
@ -99,7 +100,7 @@ func runClientToServerCopyAs(who string, conn hkexnet.Conn, fpath string, chaffi
|
|||
err = c.Start() // returns immediately
|
||||
if err != nil {
|
||||
log.Printf("Command finished with error: %v", err)
|
||||
return err, 253 // !?
|
||||
return err, hkexnet.CSEExecFail // !?
|
||||
} else {
|
||||
if err := c.Wait(); err != nil {
|
||||
//fmt.Println("*** c.Wait() done ***")
|
||||
|
@ -111,7 +112,7 @@ func runClientToServerCopyAs(who string, conn hkexnet.Conn, fpath string, chaffi
|
|||
// defined for both Unix and Windows and in both cases has
|
||||
// an ExitStatus() method with the same signature.
|
||||
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
exitStatus = status.ExitStatus()
|
||||
exitStatus = uint32(status.ExitStatus())
|
||||
log.Printf("Exit Status: %d", exitStatus)
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +123,7 @@ func runClientToServerCopyAs(who string, conn hkexnet.Conn, fpath string, chaffi
|
|||
}
|
||||
|
||||
// Perform a server->client copy
|
||||
func runServerToClientCopyAs(who string, conn hkexnet.Conn, srcPath string, chaffing bool) (err error, exitStatus int) {
|
||||
func runServerToClientCopyAs(who string, conn hkexnet.Conn, srcPath string, chaffing bool) (err error, exitStatus uint32) {
|
||||
u, _ := user.Lookup(who)
|
||||
var uid, gid uint32
|
||||
fmt.Sscanf(u.Uid, "%d", &uid)
|
||||
|
@ -179,7 +180,7 @@ func runServerToClientCopyAs(who string, conn hkexnet.Conn, srcPath string, chaf
|
|||
err = c.Start() // returns immediately
|
||||
if err != nil {
|
||||
log.Printf("Command finished with error: %v", err)
|
||||
return err, 253 // !?
|
||||
return err, hkexnet.CSEExecFail // !?
|
||||
} else {
|
||||
if err := c.Wait(); err != nil {
|
||||
//fmt.Println("*** c.Wait() done ***")
|
||||
|
@ -191,7 +192,7 @@ func runServerToClientCopyAs(who string, conn hkexnet.Conn, srcPath string, chaf
|
|||
// defined for both Unix and Windows and in both cases has
|
||||
// an ExitStatus() method with the same signature.
|
||||
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
exitStatus = status.ExitStatus()
|
||||
exitStatus = uint32(status.ExitStatus())
|
||||
log.Printf("Exit Status: %d", exitStatus)
|
||||
// TODO: send stdErrBuffer to client via specific packet
|
||||
// type so it can inform user
|
||||
|
@ -209,7 +210,7 @@ func runServerToClientCopyAs(who string, conn hkexnet.Conn, srcPath string, chaf
|
|||
// Run a command (via default shell) as a specific user
|
||||
//
|
||||
// Uses ptys to support commands which expect a terminal.
|
||||
func runShellAs(who string, cmd string, interactive bool, conn hkexnet.Conn, chaffing bool) (err error, exitStatus int) {
|
||||
func runShellAs(who string, cmd string, interactive bool, conn hkexnet.Conn, chaffing bool) (err error, exitStatus uint32) {
|
||||
var wg sync.WaitGroup
|
||||
u, _ := user.Lookup(who)
|
||||
var uid, gid uint32
|
||||
|
@ -248,7 +249,7 @@ func runShellAs(who string, cmd string, interactive bool, conn hkexnet.Conn, cha
|
|||
// Start the command with a pty.
|
||||
ptmx, err := pty.Start(c) // returns immediately with ptmx file
|
||||
if err != nil {
|
||||
return err, 0
|
||||
return err, hkexnet.CSEPtyExecFail
|
||||
}
|
||||
// Make sure to close the pty at the end.
|
||||
defer func() { _ = ptmx.Close() }() // Best effort.
|
||||
|
@ -312,11 +313,11 @@ func runShellAs(who string, cmd string, interactive bool, conn hkexnet.Conn, cha
|
|||
// defined for both Unix and Windows and in both cases has
|
||||
// an ExitStatus() method with the same signature.
|
||||
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
exitStatus = status.ExitStatus()
|
||||
exitStatus = uint32(status.ExitStatus())
|
||||
log.Printf("Exit Status: %d", exitStatus)
|
||||
}
|
||||
}
|
||||
conn.SetStatus(uint8(exitStatus))
|
||||
conn.SetStatus(exitStatus)
|
||||
}
|
||||
wg.Wait() // Wait on pty->stdout completion to client
|
||||
}
|
||||
|
@ -446,6 +447,7 @@ func main() {
|
|||
rec.op[0], string(rec.who), string(rec.cmd))
|
||||
|
||||
valid, allowedCmds := hkexsh.AuthUser(string(rec.who), string(rec.authCookie), "/etc/hkexsh.passwd")
|
||||
|
||||
// Security scrub
|
||||
for i := range rec.authCookie {
|
||||
rec.authCookie[i] = 0
|
||||
|
@ -454,6 +456,14 @@ func main() {
|
|||
|
||||
if !valid {
|
||||
log.Println("Invalid user", string(rec.who))
|
||||
|
||||
// Signal other end auth failed
|
||||
rec.status = hkexnet.CSEBadAuth
|
||||
hc.SetStatus(hkexnet.CSEBadAuth)
|
||||
s := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(s, hkexnet.CSEBadAuth)
|
||||
hc.WritePacket(s, hkexnet.CSOExitStatus)
|
||||
|
||||
hc.Write([]byte(rejectUserMsg()))
|
||||
return
|
||||
}
|
||||
|
@ -474,7 +484,7 @@ func main() {
|
|||
log.Printf("[Error spawning cmd for %s@%s]\n", rec.who, hname)
|
||||
} else {
|
||||
log.Printf("[Command completed for %s@%s, status %d]\n", rec.who, hname, cmdStatus)
|
||||
hc.SetStatus(uint8(cmdStatus))
|
||||
hc.SetStatus(cmdStatus)
|
||||
}
|
||||
} else if rec.op[0] == 's' {
|
||||
// Interactive session
|
||||
|
@ -494,7 +504,7 @@ func main() {
|
|||
log.Printf("[Error spawning shell for %s@%s]\n", rec.who, hname)
|
||||
} else {
|
||||
log.Printf("[Shell completed for %s@%s, status %d]\n", rec.who, hname, cmdStatus)
|
||||
hc.SetStatus(uint8(cmdStatus))
|
||||
hc.SetStatus(cmdStatus)
|
||||
}
|
||||
} else if rec.op[0] == 'D' {
|
||||
// File copy (destination) operation - client copy to server
|
||||
|
@ -511,7 +521,7 @@ func main() {
|
|||
} else {
|
||||
log.Printf("[Command completed for %s@%s, status %d]\n", rec.who, hname, cmdStatus)
|
||||
}
|
||||
hc.SetStatus(uint8(cmdStatus))
|
||||
hc.SetStatus(cmdStatus)
|
||||
} else if rec.op[0] == 'S' {
|
||||
// File copy (src) operation - server copy to client
|
||||
log.Printf("[Server->Client copy]\n")
|
||||
|
@ -528,9 +538,11 @@ func main() {
|
|||
} else {
|
||||
log.Printf("[Command completed for %s@%s, status %d]\n", rec.who, hname, cmdStatus)
|
||||
}
|
||||
hc.SetStatus(uint8(cmdStatus))
|
||||
hc.SetStatus(cmdStatus)
|
||||
// Signal other end transfer is complete
|
||||
hc.WritePacket([]byte{byte( /*255*/ cmdStatus)}, hkexnet.CSOExitStatus)
|
||||
s := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(s, cmdStatus)
|
||||
hc.WritePacket(s, hkexnet.CSOExitStatus)
|
||||
//fmt.Println("Waiting for EOF from other end.")
|
||||
_, _ = hc.Read(nil /*ackByte*/)
|
||||
//fmt.Println("Got remote end ack.")
|
||||
|
|
Loading…
Reference in New Issue