From 5715517e29bb101c8441174edf7a6d17e688caca Mon Sep 17 00:00:00 2001 From: Peter Schultz Date: Tue, 26 Mar 2019 16:14:40 +0100 Subject: [PATCH] Fix reading password from stdin Reading the password from non-terminal stdin used io.ReadFull with a byte slice of length 1000. We are now using a Scanner to read one line of input, independent of its length. Additionally, if stdin is not a terminal, the password is read only once instead of twice (in an effort to detect typos). Fixes #2203 Signed-off-by: Peter Schultz --- changelog/unreleased/issue-2203 | 6 ++++++ cmd/restic/global.go | 26 ++++++++++++-------------- 2 files changed, 18 insertions(+), 14 deletions(-) create mode 100644 changelog/unreleased/issue-2203 diff --git a/changelog/unreleased/issue-2203 b/changelog/unreleased/issue-2203 new file mode 100644 index 000000000..1229c108c --- /dev/null +++ b/changelog/unreleased/issue-2203 @@ -0,0 +1,6 @@ +Bugfix: Fix reading passwords from stdin + +Passwords for the `init`, `key add`, and `key passwd` commands can now be read from +non-terminal stdin. + +https://github.com/restic/restic/issues/2203 diff --git a/cmd/restic/global.go b/cmd/restic/global.go index 57d4e8d8c..87d461296 100644 --- a/cmd/restic/global.go +++ b/cmd/restic/global.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "context" "fmt" "io" @@ -273,15 +274,10 @@ func resolvePassword(opts GlobalOptions) (string, error) { // readPassword reads the password from the given reader directly. func readPassword(in io.Reader) (password string, err error) { - buf := make([]byte, 1000) - n, err := io.ReadFull(in, buf) - buf = buf[:n] + sc := bufio.NewScanner(in) + sc.Scan() - if err != nil && errors.Cause(err) != io.ErrUnexpectedEOF { - return "", errors.Wrap(err, "ReadFull") - } - - return strings.TrimRight(string(buf), "\r\n"), nil + return sc.Text(), errors.Wrap(err, "Scan") } // readPasswordTerminal reads the password from the given reader which must be a @@ -336,13 +332,15 @@ func ReadPasswordTwice(gopts GlobalOptions, prompt1, prompt2 string) (string, er if err != nil { return "", err } - pw2, err := ReadPassword(gopts, prompt2) - if err != nil { - return "", err - } + if stdinIsTerminal() { + pw2, err := ReadPassword(gopts, prompt2) + if err != nil { + return "", err + } - if pw1 != pw2 { - return "", errors.Fatal("passwords do not match") + if pw1 != pw2 { + return "", errors.Fatal("passwords do not match") + } } return pw1, nil