Added -u (user), -x (exec cmd) options, -d (dbg) for logging; detection of "-x -" for

stdin/pipeline commands.
This commit is contained in:
Russ Magee 2018-01-21 15:46:40 -08:00
parent 39a0890346
commit 6fd8ac1519
2 changed files with 77 additions and 42 deletions

View File

@ -7,6 +7,8 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"os/user"
"strings"
"sync" "sync"
hkex "blitter.com/herradurakex" hkex "blitter.com/herradurakex"
@ -37,18 +39,27 @@ type cmdSpec struct {
func main() { func main() {
var wg sync.WaitGroup var wg sync.WaitGroup
var dbg bool
var cAlg string var cAlg string
var hAlg string var hAlg string
var server string var server string
var cmdStr string
var altUser string
isInteractive := false isInteractive := false
flag.StringVar(&cAlg, "c", "C_AES_256", "cipher [\"C_AES_256\" | \"C_TWOFISH_128\" | \"C_BLOWFISH_64\"]") flag.StringVar(&cAlg, "c", "C_AES_256", "cipher [\"C_AES_256\" | \"C_TWOFISH_128\" | \"C_BLOWFISH_64\"]")
flag.StringVar(&hAlg, "h", "H_SHA256", "hmac [\"H_SHA256\"]") flag.StringVar(&hAlg, "h", "H_SHA256", "hmac [\"H_SHA256\"]")
flag.StringVar(&server, "s", "localhost:2000", "server hostname/address[:port]") flag.StringVar(&server, "s", "localhost:2000", "server hostname/address[:port]")
flag.StringVar(&cmdStr, "x", "", "command to run (default empty - interactive shell)")
flag.StringVar(&altUser, "u", "", "specify alternate user")
flag.BoolVar(&dbg, "d", false, "debug logging")
flag.Parse() flag.Parse()
//log.SetOutput(os.Stdout) if dbg {
log.SetOutput(ioutil.Discard) log.SetOutput(os.Stdout)
} else {
log.SetOutput(ioutil.Discard)
}
conn, err := hkex.Dial("tcp", server, cAlg, hAlg) conn, err := hkex.Dial("tcp", server, cAlg, hAlg)
if err != nil { if err != nil {
@ -58,8 +69,9 @@ func main() {
defer conn.Close() defer conn.Close()
// 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
// affects shell command used
if isatty.IsTerminal(os.Stdin.Fd()) { if isatty.IsTerminal(os.Stdin.Fd()) {
isInteractive = true
oldState, err := MakeRaw(int(os.Stdin.Fd())) oldState, err := MakeRaw(int(os.Stdin.Fd()))
if err != nil { if err != nil {
panic(err) panic(err)
@ -69,9 +81,33 @@ func main() {
fmt.Println("NOT A TTY") fmt.Println("NOT A TTY")
} }
rec := &cmdSpec{op: []byte{'s'}, var uname string
who: []byte("ABCD"), if len(altUser) == 0 {
cmd: []byte("EFGH"), u, _ := user.Current()
uname = u.Username
} else {
uname = altUser
}
var op []byte
if len(cmdStr) == 0 {
op = []byte{'s'}
isInteractive = true
} else if cmdStr == "-" {
op = []byte{'c'}
cmdStdin, err := ioutil.ReadAll(os.Stdin)
if err != nil {
panic(err)
}
cmdStr = strings.Trim(string(cmdStdin), "\r\n")
} else {
op = []byte{'c'}
}
rec := &cmdSpec{
op: op,
who: []byte(uname),
cmd: []byte(cmdStr),
authCookie: []byte("99"), authCookie: []byte("99"),
status: 0} status: 0}
@ -109,23 +145,25 @@ func main() {
} }
}() }()
// client writer (to server) goroutine if isInteractive {
wg.Add(1) // client writer (to server) goroutine
go func() { wg.Add(1)
defer wg.Done() go func() {
defer wg.Done()
// io.Copy() expects EOF so this will // io.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)
if outerr != nil { if outerr != nil {
if outerr.Error() != "EOF" { if outerr.Error() != "EOF" {
fmt.Println(outerr) fmt.Println(outerr)
os.Exit(2) os.Exit(2)
}
} }
} log.Println("[Sent EOF]")
log.Println("[Sent EOF]") wg.Done() // client hung up, close WaitGroup to exit client
wg.Done() // client hung up, close WaitGroup to exit client }()
}() }
// Wait until both stdin and stdout goroutines finish // Wait until both stdin and stdout goroutines finish
wg.Wait() wg.Wait()

View File

@ -4,6 +4,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"os" "os"
"os/exec" "os/exec"
@ -15,24 +16,6 @@ import (
"github.com/kr/pty" "github.com/kr/pty"
) )
// Unused, probably obsolete. Once interactive session
// and piped I/O one-shot commands are working reconsider
// how Op might be used
const (
OpR = 'r' // read(file) (binary mode)
OpW = 'w' // (over)write
OpA = 'a' // append
OpRm = 'd' // rm
OpRmD = 'D' // rmdir (rm -rf)
OpM = 'm' // mkdir (-p)
OpN = 'n' // re(n)ame (mv)
OpCm = 'c' // chmod
OpCo = 'C' // chown
OpX = 'x' // exec
)
//type Op uint8
type cmdSpec struct { type cmdSpec struct {
op []byte op []byte
who []byte who []byte
@ -89,12 +72,18 @@ func runCmdAs(who string, cmd string, conn hkex.Conn) (err error) {
// Listener and Conns. The KEx and encrypt/decrypt is done within the type. // Listener and Conns. The KEx and encrypt/decrypt is done within the type.
// Compare to 'serverp.go' in this directory to see the equivalence. // Compare to 'serverp.go' in this directory to see the equivalence.
func main() { func main() {
var dbg bool
var laddr string var laddr string
flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen") flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen")
flag.BoolVar(&dbg, "d", false, "debug logging")
flag.Parse() flag.Parse()
log.SetOutput(os.Stdout /*ioutil.Discard*/) if dbg {
log.SetOutput(os.Stdout)
} else {
log.SetOutput(ioutil.Discard)
}
// Listen on TCP port 2000 on all available unicast and // Listen on TCP port 2000 on all available unicast and
// anycast IP addresses of the local system. // anycast IP addresses of the local system.
@ -163,9 +152,17 @@ func main() {
fmt.Printf("[cmdSpec: op:%c who:%s cmd:%s auth:%s]\n", fmt.Printf("[cmdSpec: op:%c who:%s cmd:%s auth:%s]\n",
rec.op[0], string(rec.who), string(rec.cmd), string(rec.authCookie)) rec.op[0], string(rec.who), string(rec.cmd), string(rec.authCookie))
if rec.op[0] == 's' { if rec.op[0] == 'c' {
// Non-interactive command
fmt.Println("[Running command]")
runCmdAs(string(rec.who), string(rec.cmd), conn)
// Returned hopefully via an EOF or exit/logout;
// Clear current op so user can enter next, or EOF
rec.op[0] = 0
fmt.Println("[Command complete]")
} else if rec.op[0] == 's' {
fmt.Println("[Running shell]") fmt.Println("[Running shell]")
runCmdAs("larissa", "bash -l -i", conn) runCmdAs(string(rec.who), "bash -l -i", conn)
// Returned hopefully via an EOF or exit/logout; // Returned hopefully via an EOF or exit/logout;
// Clear current op so user can enter next, or EOF // Clear current op so user can enter next, or EOF
rec.op[0] = 0 rec.op[0] = 0