.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
* ~/.hkexsh_id file with multiple (host:token) entries
(Currently only one supported - need to support multiple lines for
multiple dest servers; also consider client sending host/ip used
to connect to server, so it can ensure the auth token matches that
used as servers can potentially be reached by multiple hostnames/IPs)
multiple dest servers; client sends host/ip used to connect to server,
so multihomed servers can still be specified, with separate entries
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
Alternate transports for hkexsh.Conn - HTTP-mimicking traffic, ICMP, ... ?

View file

@ -17,6 +17,7 @@ import (
"log"
"os/user"
"runtime"
"strings"
"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) {
auth = strings.TrimSpace(auth)
u, ue := user.Lookup(username)
if ue != nil {
return false
@ -80,8 +82,25 @@ func AuthUserByToken(username string, connhostname string, auth string) (valid b
return false
}
if string(b) == auth {
r := csv.NewReader(bytes.NewReader(b))
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
}

View file

@ -320,7 +320,8 @@ func rejectUserMsg() string {
func main() {
version := "0.2pre (NO WARRANTY)"
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 shellMode bool // if true act as shell, else file copier
var cAlg string
@ -358,7 +359,8 @@ func main() {
// hkexsh accepts a command (-x) but not
// a srcpath (-r) or dstpath (-t)
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
flag.Usage = UsageShell
} else {
@ -445,20 +447,44 @@ func main() {
log.SetOutput(ioutil.Discard)
}
if aopt && gopt {
fmt.Fprintln(os.Stderr,
"Error: use -g first to generate an authtoken,",
" then -a to login using it.")
os.Exit(1)
}
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)
//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 {
// We must make the decision about interactivity before Dial()
// as it affects chaffing behaviour. 20180805
if aopt {
if gopt {
op = []byte{'A'}
chaffFreqMin = 2
chaffFreqMax = 10

View file

@ -12,6 +12,7 @@ import (
"crypto/rand"
"encoding/binary"
"encoding/hex"
"errors"
"flag"
"fmt"
"io"
@ -106,7 +107,8 @@ func runClientToServerCopyAs(who, ttype string, conn hkexnet.Conn, fpath string,
// an ExitStatus() method with the same signature.
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
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]
log.Printf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname)
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)
// Returned hopefully via an EOF or exit/logout;
// 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
rec.SetOp([]byte{0})
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 {
log.Printf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)
}
fmt.Println("cmdStatus:", cmdStatus)
hc.SetStatus(cmdStatus)
} else if rec.Op()[0] == 'S' {
// File copy (src) operation - server copy to client