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"
"strings"
"sync"
"syscall"
hkexsh "blitter.com/go/hkexsh"
"blitter.com/go/hkexsh/hkexnet"
@ -101,17 +102,101 @@ func parseNonSwitchArgs(a []string, dp string) (user, host, port, path string, i
}
// doCopyMode begins a secure hkexsh local<->remote file copy operation.
func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, recurs bool, rec *cmdSpec) {
// TODO: Bring in runShellAs(), stripped down, from hkexshd
// and build either side of tar pipeline: names?
// runTarSrc(), runTarSink() ?
func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, recurs bool, rec *cmdSpec) (err error, exitStatus int) {
if remoteDest {
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 {
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).
@ -256,7 +341,7 @@ func main() {
// -else flatten otherArgs into space-delim list => copySrc
if pathIsDest {
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 {
for _, v := range otherArgs {
copySrc = append(copySrc, ' ')
@ -267,7 +352,7 @@ func main() {
}
} else {
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 {
copyDst = otherArgs[0]
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.
// When args are passed in exec() format, no quoting is required
// (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...)
//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.Credential = &syscall.Credential{Uid: uid, Gid: gid}
c.Stdin = conn
c.Stdout = conn
c.Stderr = conn
//c.Stdout = conn
//c.Stderr = conn
if chaffing {
conn.EnableChaff()
}
defer conn.DisableChaff()
defer conn.ShutdownChaff()
// Start the command (no pty)
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)
return err, 253 // !?
} else {
if chaffing {
conn.EnableChaff()
}
defer conn.DisableChaff()
defer conn.ShutdownChaff()
if err := c.Wait(); err != nil {
fmt.Println("*** c.Wait() done ***")
if exiterr, ok := err.(*exec.ExitError); ok {
@ -102,8 +103,9 @@ func runClientToServerCopyAs(who string, conn hkexnet.Conn, destPath string, cha
}
}
}
fmt.Println("*** client->server cp finished ***")
return
}
return
}
// Perform a server->client copy
@ -136,10 +138,16 @@ func runServerToClientCopyAs(who string, conn hkexnet.Conn, srcPath string, chaf
c.Dir = u.HomeDir
c.SysProcAttr = &syscall.SysProcAttr{}
c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
c.Stdin = conn
c.Stdout = conn
c.Stderr = conn
if chaffing {
conn.EnableChaff()
}
//defer conn.Close()
defer conn.DisableChaff()
defer conn.ShutdownChaff()
// Start the command (no pty)
log.Printf("[%v %v]\n", cmdName, cmdArgs)
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)
return err, 253 // !?
} else {
if chaffing {
conn.EnableChaff()
}
defer conn.DisableChaff()
defer conn.ShutdownChaff()
if err := c.Wait(); err != nil {
fmt.Println("*** c.Wait() done ***")
if exiterr, ok := err.(*exec.ExitError); ok {
@ -168,8 +170,9 @@ 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
@ -467,10 +470,13 @@ func main() {
hname := strings.Split(addr.String(), ":")[0]
log.Printf("[Running copy for [%s@%s]]\n", rec.who, hname)
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 {
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 {
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))
}
} else if rec.op[0] == 'S' {
@ -482,10 +488,13 @@ func main() {
hname := strings.Split(addr.String(), ":")[0]
log.Printf("[Running copy for [%s@%s]]\n", rec.who, hname)
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 {
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 {
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))
}
} else {