Skip to content

Commit

Permalink
Fix reading password from stdin
Browse files Browse the repository at this point in the history
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 restic#2203
  • Loading branch information
pschultz committed Mar 26, 2019
1 parent 870e758 commit 1324e7e
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 14 deletions.
11 changes: 11 additions & 0 deletions changelog/unreleased/issue-2203
@@ -0,0 +1,11 @@
Bugfix: Fix reading passwords from stdin

Passwords for `init` and `key add` can now be read from non-terminal stdin
without having to pad the input to 1000 characters.

Additionally the password is now only read once from non-terminal stdin.

*Heads up*: If you relied on restic checking for typos, you have to do it
yourself now, before running the `init`, `key add`, or `key change` commands.

https://github.com/restic/restic/issues/2203
26 changes: 12 additions & 14 deletions cmd/restic/global.go
@@ -1,6 +1,7 @@
package main

import (
"bufio"
"context"
"fmt"
"io"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 1324e7e

Please sign in to comment.