mirror of https://gogs.blitter.com/RLabs/xs
Brought in ReadPassword from ssh/terminal, enabling entry of authCookie w/o term
echo. TODO: consider methods of securing authCookie in auth file (salt+hash etc.)
This commit is contained in:
parent
59337db7e3
commit
4d9ea3cbe1
|
@ -45,6 +45,7 @@ func main() {
|
|||
var server string
|
||||
var cmdStr string
|
||||
var altUser string
|
||||
var authCookie string
|
||||
isInteractive := false
|
||||
|
||||
flag.StringVar(&cAlg, "c", "C_AES_256", "cipher [\"C_AES_256\" | \"C_TWOFISH_128\" | \"C_BLOWFISH_64\"]")
|
||||
|
@ -52,6 +53,7 @@ func main() {
|
|||
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.StringVar(&authCookie, "a", "", "auth cookie (MultiCheese3999(tm) 2FA cookie")
|
||||
flag.BoolVar(&dbg, "d", false, "debug logging")
|
||||
flag.Parse()
|
||||
|
||||
|
@ -78,7 +80,7 @@ func main() {
|
|||
}
|
||||
defer func() { _ = Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.
|
||||
} else {
|
||||
fmt.Println("NOT A TTY")
|
||||
log.Println("NOT A TTY")
|
||||
}
|
||||
|
||||
var uname string
|
||||
|
@ -104,11 +106,20 @@ func main() {
|
|||
op = []byte{'c'}
|
||||
}
|
||||
|
||||
if len(authCookie) == 0 {
|
||||
fmt.Printf("Gimme cookie:")
|
||||
ab, err := ReadPassword(int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
authCookie = string(ab)
|
||||
}
|
||||
|
||||
rec := &cmdSpec{
|
||||
op: op,
|
||||
who: []byte(uname),
|
||||
cmd: []byte(cmdStr),
|
||||
authCookie: []byte("99"),
|
||||
authCookie: []byte(authCookie),
|
||||
status: 0}
|
||||
|
||||
_, err = fmt.Fprintf(conn, "%d %d %d %d\n", len(rec.op), len(rec.who), len(rec.cmd), len(rec.authCookie))
|
||||
|
@ -229,3 +240,63 @@ func GetState(fd int) (*State, error) {
|
|||
func Restore(fd int, state *State) error {
|
||||
return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
|
||||
}
|
||||
|
||||
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||
// is commonly used for inputting passwords and other sensitive data. The slice
|
||||
// returned does not include the \n.
|
||||
func ReadPassword(fd int) ([]byte, error) {
|
||||
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newState := *termios
|
||||
newState.Lflag &^= unix.ECHO
|
||||
newState.Lflag |= unix.ICANON | unix.ISIG
|
||||
newState.Iflag |= unix.ICRNL
|
||||
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
unix.IoctlSetTermios(fd, ioctlWriteTermios, termios)
|
||||
}()
|
||||
|
||||
return readPasswordLine(passwordReader(fd))
|
||||
}
|
||||
|
||||
// passwordReader is an io.Reader that reads from a specific file descriptor.
|
||||
type passwordReader int
|
||||
|
||||
func (r passwordReader) Read(buf []byte) (int, error) {
|
||||
return unix.Read(int(r), buf)
|
||||
}
|
||||
|
||||
// readPasswordLine reads from reader until it finds \n or io.EOF.
|
||||
// The slice returned does not include the \n.
|
||||
// readPasswordLine also ignores any \r it finds.
|
||||
func readPasswordLine(reader io.Reader) ([]byte, error) {
|
||||
var buf [1]byte
|
||||
var ret []byte
|
||||
|
||||
for {
|
||||
n, err := reader.Read(buf[:])
|
||||
if n > 0 {
|
||||
switch buf[0] {
|
||||
case '\n':
|
||||
return ret, nil
|
||||
case '\r':
|
||||
// remove \r from passwords on Windows
|
||||
default:
|
||||
ret = append(ret, buf[0])
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF && len(ret) > 0 {
|
||||
return ret, nil
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,6 +108,11 @@ func runShellAs(who string, cmd string, interactive bool, conn hkex.Conn) (err e
|
|||
return
|
||||
}
|
||||
|
||||
func rejectUserMsg() string {
|
||||
// TODO: Use Shakespeare insult generator. :p
|
||||
return "Invalid user\r\n"
|
||||
}
|
||||
|
||||
// Demo of a simple server that listens and spawns goroutines for each
|
||||
// connecting client. Note this code is identical to standard tcp
|
||||
// server code, save for declaring 'hkex' rather than 'net'
|
||||
|
@ -194,6 +199,14 @@ func main() {
|
|||
fmt.Printf("[cmdSpec: op:%c who:%s cmd:%s auth:%s]\n",
|
||||
rec.op[0], string(rec.who), string(rec.cmd), string(rec.authCookie))
|
||||
|
||||
valid, allowedCmds := hkex.AuthUser(string(rec.who), string(rec.authCookie), "/etc/hkexsh.passwd")
|
||||
if !valid {
|
||||
log.Println("Invalid user", string(rec.who))
|
||||
c.Write([]byte(rejectUserMsg()))
|
||||
return
|
||||
}
|
||||
log.Printf("[allowedCmds:%s]\n", allowedCmds)
|
||||
|
||||
if rec.op[0] == 'c' {
|
||||
// Non-interactive command
|
||||
fmt.Println("[Running command]")
|
||||
|
|
Loading…
Reference in New Issue