d86acfc94f
* Remove DEL characters from password input iTerm password manager sends \x03\0x7f before sending a password from its password manager to make sure the password is not being echoed to the screen. Unfortunately, vault login does not handle the Space DEL sequence, causing the login to fail when using the password manager. This patch uses a simple method to delete the sequence if present anywhere in the string, although it is strictly only needed at the start of input. * Simplify iTerm handling to only remove iTerm prefix The logic now only removes the two byte prefix sent in by iTerm instead of trying to remove all deletes in the string. This has been tested to work with the iTerm password manager. As a small correction, the byte sequence is \x20\x7f. The earlier commit message incorrectly stated it was \x03\x7f.
70 lines
1.7 KiB
Go
70 lines
1.7 KiB
Go
// password is a package for reading a password securely from a terminal.
|
|
// The code in this package disables echo in the terminal so that the
|
|
// password is not echoed back in plaintext to the user.
|
|
package password
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"os/signal"
|
|
"strings"
|
|
)
|
|
|
|
var ErrInterrupted = errors.New("interrupted")
|
|
|
|
// Read reads the password from the given os.File. The password
|
|
// will not be echoed back to the user. Ctrl-C will automatically return
|
|
// from this function with a blank string and an ErrInterrupted.
|
|
func Read(f *os.File) (string, error) {
|
|
ch := make(chan os.Signal, 1)
|
|
signal.Notify(ch, os.Interrupt)
|
|
defer signal.Stop(ch)
|
|
|
|
// Run the actual read in a go-routine so that we can still detect signals
|
|
var result string
|
|
var resultErr error
|
|
doneCh := make(chan struct{})
|
|
go func() {
|
|
defer close(doneCh)
|
|
result, resultErr = read(f)
|
|
}()
|
|
|
|
// Wait on either the read to finish or the signal to come through
|
|
select {
|
|
case <-ch:
|
|
return "", ErrInterrupted
|
|
case <-doneCh:
|
|
return removeiTermDelete(result), resultErr
|
|
}
|
|
}
|
|
|
|
func readline(f *os.File) (string, error) {
|
|
var buf [1]byte
|
|
resultBuf := make([]byte, 0, 64)
|
|
for {
|
|
n, err := f.Read(buf[:])
|
|
if err != nil && err != io.EOF {
|
|
return "", err
|
|
}
|
|
if n == 0 || buf[0] == '\n' || buf[0] == '\r' {
|
|
break
|
|
}
|
|
|
|
// ASCII code 3 is what is sent for a Ctrl-C while reading raw.
|
|
// If we see that, then get the interrupt. We have to do this here
|
|
// because terminals in raw mode won't catch it at the shell level.
|
|
if buf[0] == 3 {
|
|
return "", ErrInterrupted
|
|
}
|
|
|
|
resultBuf = append(resultBuf, buf[0])
|
|
}
|
|
|
|
return string(resultBuf), nil
|
|
}
|
|
|
|
func removeiTermDelete(input string) string {
|
|
return strings.TrimPrefix(input, "\x20\x7f")
|
|
}
|