.hkexsh_id file supports multiple authtokens (multi remote hosts, aliases for same remote host)

This commit is contained in:
Russ Magee 2018-09-14 11:58:10 -07:00
parent d9b34fa631
commit e02764bf4b
4 changed files with 68 additions and 19 deletions

View file

@ -20,9 +20,10 @@ Features
- (IN PROGRESS) auth tokens to allow scripted hkexsh/hkexcp use - (IN PROGRESS) auth tokens to allow scripted hkexsh/hkexcp use
* ~/.hkexsh_id file with multiple (host:token) entries * ~/.hkexsh_id file with multiple (host:token) entries
(Currently only one supported - need to support multiple lines for (Currently only one supported - need to support multiple lines for
multiple dest servers; also consider client sending host/ip used multiple dest servers; client sends host/ip used to connect to server,
to connect to server, so it can ensure the auth token matches that so multihomed servers can still be specified, with separate entries
used as servers can potentially be reached by multiple hostnames/IPs) stored in both server/client ~/.hkexsh_id files. Use connhost here to
match against proper entry.)
- hktun - tunnelling - multiple tunnel sessions co-existing w/shell sessions - hktun - tunnelling - multiple tunnel sessions co-existing w/shell sessions
Alternate transports for hkexsh.Conn - HTTP-mimicking traffic, ICMP, ... ? Alternate transports for hkexsh.Conn - HTTP-mimicking traffic, ICMP, ... ?

View file

@ -17,6 +17,7 @@ import (
"log" "log"
"os/user" "os/user"
"runtime" "runtime"
"strings"
"github.com/jameskeane/bcrypt" "github.com/jameskeane/bcrypt"
) )
@ -69,6 +70,7 @@ func AuthUserByPasswd(username string, auth string, fname string) (valid bool, a
} }
func AuthUserByToken(username string, connhostname string, auth string) (valid bool) { func AuthUserByToken(username string, connhostname string, auth string) (valid bool) {
auth = strings.TrimSpace(auth)
u, ue := user.Lookup(username) u, ue := user.Lookup(username)
if ue != nil { if ue != nil {
return false return false
@ -80,8 +82,25 @@ func AuthUserByToken(username string, connhostname string, auth string) (valid b
return false return false
} }
if string(b) == auth { r := csv.NewReader(bytes.NewReader(b))
return true
r.Comma = ':'
r.Comment = '#'
r.FieldsPerRecord = 2 // connhost:authtoken
for {
record, err := r.Read()
if err == io.EOF {
return false
}
record[0] = strings.TrimSpace(record[0])
record[1] = strings.TrimSpace(record[1])
fmt.Println("auth:", auth, "record:",
strings.Join([]string{record[0], record[1]}, ":"))
if (connhostname == record[0]) &&
(auth == strings.Join([]string{record[0], record[1]}, ":")) {
return true
}
} }
return return
} }

View file

@ -320,7 +320,8 @@ func rejectUserMsg() string {
func main() { func main() {
version := "0.2pre (NO WARRANTY)" version := "0.2pre (NO WARRANTY)"
var vopt bool var vopt bool
var aopt bool var aopt bool //login using authToken
var gopt bool //login via password, asking server to generate authToken
var dbg bool var dbg bool
var shellMode bool // if true act as shell, else file copier var shellMode bool // if true act as shell, else file copier
var cAlg string var cAlg string
@ -358,7 +359,8 @@ func main() {
// hkexsh accepts a command (-x) but not // hkexsh accepts a command (-x) but not
// a srcpath (-r) or dstpath (-t) // a srcpath (-r) or dstpath (-t)
flag.StringVar(&cmdStr, "x", "", "`command` to run (if not specified run interactive shell)") flag.StringVar(&cmdStr, "x", "", "`command` to run (if not specified run interactive shell)")
flag.BoolVar(&aopt, "a", false, "return autologin token from server") flag.BoolVar(&aopt, "a", false, "login using auth token")
flag.BoolVar(&gopt, "g", false, "ask server to generate authtoken")
shellMode = true shellMode = true
flag.Usage = UsageShell flag.Usage = UsageShell
} else { } else {
@ -445,20 +447,44 @@ func main() {
log.SetOutput(ioutil.Discard) log.SetOutput(ioutil.Discard)
} }
// See if we can log in via an auth token if aopt && gopt {
u, _ := user.Current() fmt.Fprintln(os.Stderr,
ab, aerr := ioutil.ReadFile(fmt.Sprintf("%s/.hkexsh_id", u.HomeDir)) "Error: use -g first to generate an authtoken,",
if aerr == nil { " then -a to login using it.")
authCookie = string(ab) os.Exit(1)
// Security scrub }
ab = nil
runtime.GC() if !gopt {
// See if we can log in via an auth token
u, _ := user.Current()
ab, aerr := ioutil.ReadFile(fmt.Sprintf("%s/.hkexsh_id", u.HomeDir))
if aerr == nil {
//authCookie = string(ab)
idx := strings.Index(string(ab), remoteHost)
if idx >= 0 {
ab = ab[idx:]
} else {
fmt.Fprintln(os.Stderr, "ERROR: no matching authtoken")
os.Exit(1)
}
entries := strings.SplitN(string(ab), "\n", -1)
//if len(entries) > 0 {
fmt.Println("entries[0]:", entries[0])
authCookie = strings.TrimSpace(entries[0])
//} else {
// fmt.Fprintln(os.Stderr, "ERROR: no matching authtoken")
// os.Exit(1)
//}
// Security scrub
ab = nil
runtime.GC()
}
} }
if shellMode { if shellMode {
// We must make the decision about interactivity before Dial() // We must make the decision about interactivity before Dial()
// as it affects chaffing behaviour. 20180805 // as it affects chaffing behaviour. 20180805
if aopt { if gopt {
op = []byte{'A'} op = []byte{'A'}
chaffFreqMin = 2 chaffFreqMin = 2
chaffFreqMax = 10 chaffFreqMax = 10

View file

@ -12,6 +12,7 @@ import (
"crypto/rand" "crypto/rand"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"errors"
"flag" "flag"
"fmt" "fmt"
"io" "io"
@ -106,7 +107,8 @@ func runClientToServerCopyAs(who, ttype string, conn hkexnet.Conn, fpath string,
// an ExitStatus() method with the same signature. // an ExitStatus() method with the same signature.
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
exitStatus = uint32(status.ExitStatus()) exitStatus = uint32(status.ExitStatus())
log.Printf("Exit Status: %d", exitStatus) err = errors.New("cmd returned nonzero status")
fmt.Printf("Exit Status: %d\n", exitStatus)
} }
} }
} }
@ -495,7 +497,7 @@ func main() {
hname := strings.Split(addr.String(), ":")[0] hname := strings.Split(addr.String(), ":")[0]
log.Printf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname) log.Printf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname)
token := GenAuthToken(string(rec.Who()), string(rec.ConnHost())) token := GenAuthToken(string(rec.Who()), string(rec.ConnHost()))
tokenCmd := fmt.Sprintf("echo \"%s\" | tee ~/.hkexsh_id", token) tokenCmd := fmt.Sprintf("echo \"%s\" | tee -a ~/.hkexsh_id", token)
runErr, cmdStatus := runShellAs(string(rec.Who()), string(rec.TermType()), tokenCmd, false, hc, chaffEnabled) runErr, cmdStatus := runShellAs(string(rec.Who()), string(rec.TermType()), tokenCmd, false, hc, chaffEnabled)
// 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
@ -554,10 +556,11 @@ func main() {
// Clear current op so user can enter next, or EOF // Clear current op so user can enter next, or EOF
rec.SetOp([]byte{0}) rec.SetOp([]byte{0})
if runErr != nil { if runErr != nil {
log.Printf("[Error spawning cp for %s@%s]\n", rec.Who(), hname) log.Printf("[Error running cp for %s@%s]\n", rec.Who(), hname)
} else { } else {
log.Printf("[Command 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)
} }
fmt.Println("cmdStatus:", cmdStatus)
hc.SetStatus(cmdStatus) hc.SetStatus(cmdStatus)
} else if rec.Op()[0] == 'S' { } else if rec.Op()[0] == 'S' {
// File copy (src) operation - server copy to client // File copy (src) operation - server copy to client