WIP: server->client copy primitively functional; TODO client->server copy

This commit is contained in:
Russ Magee 2018-08-24 18:50:45 -07:00
parent 0b9b8b8320
commit 7867f84b87
2 changed files with 124 additions and 30 deletions

View file

@ -19,6 +19,7 @@ import (
"runtime" "runtime"
"strings" "strings"
"sync" "sync"
"syscall"
hkexsh "blitter.com/go/hkexsh" hkexsh "blitter.com/go/hkexsh"
"blitter.com/go/hkexsh/hkexnet" "blitter.com/go/hkexsh/hkexnet"
@ -101,18 +102,102 @@ func parseNonSwitchArgs(a []string, dp string) (user, host, port, path string, i
} }
// doCopyMode begins a secure hkexsh local<->remote file copy operation. // doCopyMode begins a secure hkexsh local<->remote file copy operation.
func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, recurs bool, rec *cmdSpec) { func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, recurs bool, rec *cmdSpec) (err error, exitStatus int) {
// TODO: Bring in runShellAs(), stripped down, from hkexshd
// and build either side of tar pipeline: names?
// runTarSrc(), runTarSink() ?
if remoteDest { if remoteDest {
fmt.Println("local files:", files, "remote filepath:", string(rec.cmd)) fmt.Println("local files:", files, "remote filepath:", string(rec.cmd))
fmt.Fprintf(conn, "copyMode remoteDest TODO\n") fmt.Fprintf(conn, "copyMode remoteDest ...\n")
var c *exec.Cmd
//os.Clearenv()
//os.Setenv("HOME", u.HomeDir)
//os.Setenv("TERM", "vt102") // TODO: server or client option?
cmdName := "/bin/tar"
cmdArgs := []string{"-cz", "-f", "/dev/stdout", files}
fmt.Printf("[%v %v]\n", cmdName, cmdArgs)
// NOTE the lack of quotes around --xform option's sed expression.
// When args are passed in exec() format, no quoting is required
// (as this isn't input from a shell) (right? -rlm 20180823)
//cmdArgs := []string{"-xvz", "-C", files, `--xform=s#.*/\(.*\)#\1#`}
c = exec.Command(cmdName, cmdArgs...)
c.Stdout = conn
// Start the command (no pty)
err = c.Start() // returns immediately
if err != nil {
fmt.Println(err)
//log.Fatal(err)
} else {
if err = c.Wait(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
// The program has exited with an exit code != 0
// This works on both Unix and Windows. Although package
// syscall is generally platform dependent, WaitStatus is
// 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()
log.Printf("Exit Status: %d", exitStatus)
}
}
}
fmt.Println("*** client->server cp finished ***")
}
} else { } else {
fmt.Println("remote filepath:", string(rec.cmd), "local files:", files) fmt.Println("remote filepath:", string(rec.cmd), "local files:", files)
fmt.Fprintf(conn, "copyMode localDest TODO\n") fmt.Fprintf(conn, "copyMode localDest ...\n")
var c *exec.Cmd
//os.Clearenv()
//os.Setenv("HOME", u.HomeDir)
//os.Setenv("TERM", "vt102") // TODO: server or client option?
cmdName := "/bin/tar"
destPath := files
//if path.IsAbs(files) {
// destPath := files
//} else {
// destPath := strings.Join({os.Getenv("PWD"),files}, os.PathSeparator)
//}
cmdArgs := []string{"-xvz", "-C", destPath}
fmt.Printf("[%v %v]\n", cmdName, cmdArgs)
// NOTE the lack of quotes around --xform option's sed expression.
// When args are passed in exec() format, no quoting is required
// (as this isn't input from a shell) (right? -rlm 20180823)
//cmdArgs := []string{"-xvz", "-C", destPath, `--xform=s#.*/\(.*\)#\1#`}
c = exec.Command(cmdName, cmdArgs...)
c.Stdin = conn
c.Stdout = os.Stdout
c.Stderr = os.Stderr
// Start the command (no pty)
err = c.Start() // returns immediately
if err != nil {
fmt.Println(err)
//log.Fatal(err)
} else {
if err = c.Wait(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
// The program has exited with an exit code != 0
// This works on both Unix and Windows. Although package
// syscall is generally platform dependent, WaitStatus is
// 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()
log.Printf("Exit Status: %d", exitStatus)
} }
} }
}
fmt.Println("*** server->client cp finished ***")
}
}
return
}
// doShellMode begins an hkexsh shell session (one-shot command or interactive). // doShellMode begins an hkexsh shell session (one-shot command or interactive).
func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State, rec *cmdSpec) { func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State, rec *cmdSpec) {
@ -256,7 +341,7 @@ func main() {
// -else flatten otherArgs into space-delim list => copySrc // -else flatten otherArgs into space-delim list => copySrc
if pathIsDest { if pathIsDest {
if len(otherArgs) == 0 { if len(otherArgs) == 0 {
log.Fatal("ERROR: Must specify at least one src path for copy") log.Fatal("ERROR: Must specify at least one dest path for copy")
} else { } else {
for _, v := range otherArgs { for _, v := range otherArgs {
copySrc = append(copySrc, ' ') copySrc = append(copySrc, ' ')
@ -267,7 +352,7 @@ func main() {
} }
} else { } else {
if len(otherArgs) == 0 { if len(otherArgs) == 0 {
log.Fatal("ERROR: Must specify dest path for copy") log.Fatal("ERROR: Must specify src path for copy")
} else if len(otherArgs) == 1 { } else if len(otherArgs) == 1 {
copyDst = otherArgs[0] copyDst = otherArgs[0]
if strings.Contains(copyDst, "*") || strings.Contains(copyDst, "?") { if strings.Contains(copyDst, "*") || strings.Contains(copyDst, "?") {

View file

@ -61,7 +61,8 @@ func runClientToServerCopyAs(who string, conn hkexnet.Conn, destPath string, cha
// NOTE the lack of quotes around --xform option's sed expression. // NOTE the lack of quotes around --xform option's sed expression.
// When args are passed in exec() format, no quoting is required // When args are passed in exec() format, no quoting is required
// (as this isn't input from a shell) (right? -rlm 20180823) // (as this isn't input from a shell) (right? -rlm 20180823)
cmdArgs := []string{"-xzv", "-C", destPath, `--xform=s#.*/\(.*\)#\1#`} cmdArgs := []string{"-xvz", "-C", destPath}
//cmdArgs := []string{"-xvz", "-C", destPath, `--xform=s#.*/\(.*\)#\1#`}
c = exec.Command(cmdName, cmdArgs...) c = exec.Command(cmdName, cmdArgs...)
//If os.Clearenv() isn't called by server above these will be seen in the //If os.Clearenv() isn't called by server above these will be seen in the
@ -71,8 +72,14 @@ func runClientToServerCopyAs(who string, conn hkexnet.Conn, destPath string, cha
c.SysProcAttr = &syscall.SysProcAttr{} c.SysProcAttr = &syscall.SysProcAttr{}
c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid} c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
c.Stdin = conn c.Stdin = conn
c.Stdout = conn //c.Stdout = conn
c.Stderr = conn //c.Stderr = conn
if chaffing {
conn.EnableChaff()
}
defer conn.DisableChaff()
defer conn.ShutdownChaff()
// Start the command (no pty) // Start the command (no pty)
log.Printf("[%v %v]\n", cmdName, cmdArgs) log.Printf("[%v %v]\n", cmdName, cmdArgs)
@ -81,12 +88,6 @@ func runClientToServerCopyAs(who string, conn hkexnet.Conn, destPath string, cha
log.Printf("Command finished with error: %v", err) log.Printf("Command finished with error: %v", err)
return err, 253 // !? return err, 253 // !?
} else { } else {
if chaffing {
conn.EnableChaff()
}
defer conn.DisableChaff()
defer conn.ShutdownChaff()
if err := c.Wait(); err != nil { if err := c.Wait(); err != nil {
fmt.Println("*** c.Wait() done ***") fmt.Println("*** c.Wait() done ***")
if exiterr, ok := err.(*exec.ExitError); ok { if exiterr, ok := err.(*exec.ExitError); ok {
@ -102,9 +103,10 @@ func runClientToServerCopyAs(who string, conn hkexnet.Conn, destPath string, cha
} }
} }
} }
} fmt.Println("*** client->server cp finished ***")
return return
} }
}
// Perform a server->client copy // 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 int) {
@ -136,10 +138,16 @@ func runServerToClientCopyAs(who string, conn hkexnet.Conn, srcPath string, chaf
c.Dir = u.HomeDir c.Dir = u.HomeDir
c.SysProcAttr = &syscall.SysProcAttr{} c.SysProcAttr = &syscall.SysProcAttr{}
c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid} c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
c.Stdin = conn
c.Stdout = conn c.Stdout = conn
c.Stderr = conn c.Stderr = conn
if chaffing {
conn.EnableChaff()
}
//defer conn.Close()
defer conn.DisableChaff()
defer conn.ShutdownChaff()
// Start the command (no pty) // Start the command (no pty)
log.Printf("[%v %v]\n", cmdName, cmdArgs) log.Printf("[%v %v]\n", cmdName, cmdArgs)
err = c.Start() // returns immediately err = c.Start() // returns immediately
@ -147,12 +155,6 @@ func runServerToClientCopyAs(who string, conn hkexnet.Conn, srcPath string, chaf
log.Printf("Command finished with error: %v", err) log.Printf("Command finished with error: %v", err)
return err, 253 // !? return err, 253 // !?
} else { } else {
if chaffing {
conn.EnableChaff()
}
defer conn.DisableChaff()
defer conn.ShutdownChaff()
if err := c.Wait(); err != nil { if err := c.Wait(); err != nil {
fmt.Println("*** c.Wait() done ***") fmt.Println("*** c.Wait() done ***")
if exiterr, ok := err.(*exec.ExitError); ok { if exiterr, ok := err.(*exec.ExitError); ok {
@ -168,9 +170,10 @@ func runServerToClientCopyAs(who string, conn hkexnet.Conn, srcPath string, chaf
} }
} }
} }
} fmt.Println("*** server->client cp finished ***")
return return
} }
}
// Run a command (via default shell) as a specific user // Run a command (via default shell) as a specific user
// //
@ -467,10 +470,13 @@ func main() {
hname := strings.Split(addr.String(), ":")[0] hname := strings.Split(addr.String(), ":")[0]
log.Printf("[Running copy for [%s@%s]]\n", rec.who, hname) log.Printf("[Running copy for [%s@%s]]\n", rec.who, hname)
runErr, cmdStatus := runClientToServerCopyAs(string(rec.who), hc, string(rec.cmd), chaffEnabled) runErr, cmdStatus := runClientToServerCopyAs(string(rec.who), hc, string(rec.cmd), chaffEnabled)
// Returned hopefully via an EOF or exit/logout;
// Clear current op so user can enter next, or EOF
rec.op[0] = 0
if runErr != nil { if runErr != nil {
log.Printf("[Error spawning shell for %s@%s]\n", rec.who, hname) log.Printf("[Error spawning cp for %s@%s]\n", rec.who, hname)
} else { } else {
log.Printf("[Shell completed for %s@%s, status %d]\n", rec.who, hname, cmdStatus) log.Printf("[Command completed for %s@%s, status %d]\n", rec.who, hname, cmdStatus)
hc.SetStatus(uint8(cmdStatus)) hc.SetStatus(uint8(cmdStatus))
} }
} else if rec.op[0] == 'S' { } else if rec.op[0] == 'S' {
@ -482,10 +488,13 @@ func main() {
hname := strings.Split(addr.String(), ":")[0] hname := strings.Split(addr.String(), ":")[0]
log.Printf("[Running copy for [%s@%s]]\n", rec.who, hname) log.Printf("[Running copy for [%s@%s]]\n", rec.who, hname)
runErr, cmdStatus := runServerToClientCopyAs(string(rec.who), hc, string(rec.cmd), chaffEnabled) runErr, cmdStatus := runServerToClientCopyAs(string(rec.who), hc, string(rec.cmd), chaffEnabled)
// Returned hopefully via an EOF or exit/logout;
// Clear current op so user can enter next, or EOF
rec.op[0] = 0
if runErr != nil { if runErr != nil {
log.Printf("[Error spawning shell for %s@%s]\n", rec.who, hname) log.Printf("[Error spawning cp for %s@%s]\n", rec.who, hname)
} else { } else {
log.Printf("[Shell completed for %s@%s, status %d]\n", rec.who, hname, cmdStatus) log.Printf("[Command completed for %s@%s, status %d]\n", rec.who, hname, cmdStatus)
hc.SetStatus(uint8(cmdStatus)) hc.SetStatus(uint8(cmdStatus))
} }
} else { } else {