mirror of
https://gogs.blitter.com/RLabs/xs
synced 2024-08-14 10:26:42 +00:00
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
2 changed files with 86 additions and 2 deletions
|
@ -45,6 +45,7 @@ func main() {
|
||||||
var server string
|
var server string
|
||||||
var cmdStr string
|
var cmdStr string
|
||||||
var altUser string
|
var altUser string
|
||||||
|
var authCookie 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\"]")
|
||||||
|
@ -52,6 +53,7 @@ func main() {
|
||||||
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(&cmdStr, "x", "", "command to run (default empty - interactive shell)")
|
||||||
flag.StringVar(&altUser, "u", "", "specify alternate user")
|
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.BoolVar(&dbg, "d", false, "debug logging")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
@ -78,7 +80,7 @@ func main() {
|
||||||
}
|
}
|
||||||
defer func() { _ = Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.
|
defer func() { _ = Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("NOT A TTY")
|
log.Println("NOT A TTY")
|
||||||
}
|
}
|
||||||
|
|
||||||
var uname string
|
var uname string
|
||||||
|
@ -104,11 +106,20 @@ func main() {
|
||||||
op = []byte{'c'}
|
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{
|
rec := &cmdSpec{
|
||||||
op: op,
|
op: op,
|
||||||
who: []byte(uname),
|
who: []byte(uname),
|
||||||
cmd: []byte(cmdStr),
|
cmd: []byte(cmdStr),
|
||||||
authCookie: []byte("99"),
|
authCookie: []byte(authCookie),
|
||||||
status: 0}
|
status: 0}
|
||||||
|
|
||||||
_, err = fmt.Fprintf(conn, "%d %d %d %d\n", len(rec.op), len(rec.who), len(rec.cmd), len(rec.authCookie))
|
_, 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 {
|
func Restore(fd int, state *State) error {
|
||||||
return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
|
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
|
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
|
// Demo of a simple server that listens and spawns goroutines for each
|
||||||
// connecting client. Note this code is identical to standard tcp
|
// connecting client. Note this code is identical to standard tcp
|
||||||
// server code, save for declaring 'hkex' rather than 'net'
|
// 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",
|
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))
|
||||||
|
|
||||||
|
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' {
|
if rec.op[0] == 'c' {
|
||||||
// Non-interactive command
|
// Non-interactive command
|
||||||
fmt.Println("[Running command]")
|
fmt.Println("[Running command]")
|
||||||
|
|
Loading…
Reference in a new issue