diff options
author | Jakob Unterwurzacher | 2022-01-03 15:18:59 +0100 |
---|---|---|
committer | Jakob Unterwurzacher | 2022-01-03 15:18:59 +0100 |
commit | 4b251f3ce1f0a0472ed10a00aeef70c69ba03a5d (patch) | |
tree | a93b7b37d5b3118decd8a52f2db2e76d09d82b70 /internal/readpassword/read.go | |
parent | 1eaf1211a259a38cdf3e7dad2e00e140409bef9a (diff) |
readpassword: bubble up errors instead of exiting the process
This allows cleanups to happen in the caller, like removing
the control socket.
Fixes https://github.com/rfjakob/gocryptfs/issues/634
Diffstat (limited to 'internal/readpassword/read.go')
-rw-r--r-- | internal/readpassword/read.go | 75 |
1 files changed, 38 insertions, 37 deletions
diff --git a/internal/readpassword/read.go b/internal/readpassword/read.go index c0dce43..498d09b 100644 --- a/internal/readpassword/read.go +++ b/internal/readpassword/read.go @@ -11,7 +11,6 @@ import ( "golang.org/x/crypto/ssh/terminal" - "github.com/rfjakob/gocryptfs/v2/internal/exitcodes" "github.com/rfjakob/gocryptfs/v2/internal/tlog" ) @@ -22,7 +21,7 @@ const ( // Once tries to get a password from the user, either from the terminal, extpass, passfile // or stdin. Leave "prompt" empty to use the default "Password: " prompt. -func Once(extpass []string, passfile []string, prompt string) []byte { +func Once(extpass []string, passfile []string, prompt string) ([]byte, error) { if len(passfile) != 0 { return readPassFileConcatenate(passfile) } @@ -40,7 +39,7 @@ func Once(extpass []string, passfile []string, prompt string) []byte { // Twice is the same as Once but will prompt twice if we get the password from // the terminal. -func Twice(extpass []string, passfile []string) []byte { +func Twice(extpass []string, passfile []string) ([]byte, error) { if len(passfile) != 0 { return readPassFileConcatenate(passfile) } @@ -50,54 +49,59 @@ func Twice(extpass []string, passfile []string) []byte { if !terminal.IsTerminal(int(os.Stdin.Fd())) { return readPasswordStdin("Password") } - p1 := readPasswordTerminal("Password: ") - p2 := readPasswordTerminal("Repeat: ") + p1, err := readPasswordTerminal("Password: ") + if err != nil { + return nil, err + } + p2, err := readPasswordTerminal("Repeat: ") + if err != nil { + return nil, err + } if !bytes.Equal(p1, p2) { - tlog.Fatal.Println("Passwords do not match") - os.Exit(exitcodes.ReadPassword) + return nil, fmt.Errorf("Passwords do not match") } // Wipe the password duplicate from memory for i := range p2 { p2[i] = 0 } - return p1 + return p1, nil } // readPasswordTerminal reads a line from the terminal. // Exits on read error or empty result. -func readPasswordTerminal(prompt string) []byte { +func readPasswordTerminal(prompt string) ([]byte, error) { fd := int(os.Stdin.Fd()) fmt.Fprintf(os.Stderr, prompt) // terminal.ReadPassword removes the trailing newline p, err := terminal.ReadPassword(fd) if err != nil { - tlog.Fatal.Printf("Could not read password from terminal: %v\n", err) - os.Exit(exitcodes.ReadPassword) + return nil, fmt.Errorf("Could not read password from terminal: %v\n", err) } fmt.Fprintf(os.Stderr, "\n") if len(p) == 0 { - tlog.Fatal.Println("Password is empty") - os.Exit(exitcodes.PasswordEmpty) + return nil, fmt.Errorf("Password is empty") } - return p + return p, nil } // readPasswordStdin reads a line from stdin. // It exits with a fatal error on read error or empty result. -func readPasswordStdin(prompt string) []byte { +func readPasswordStdin(prompt string) ([]byte, error) { tlog.Info.Printf("Reading %s from stdin", prompt) - p := readLineUnbuffered(os.Stdin) + p, err := readLineUnbuffered(os.Stdin) + if err != nil { + return nil, err + } if len(p) == 0 { - tlog.Fatal.Printf("Got empty %s from stdin", prompt) - os.Exit(exitcodes.ReadPassword) + return nil, fmt.Errorf("Got empty %s from stdin", prompt) } - return p + return p, nil } // readPasswordExtpass executes the "extpass" program and returns the first line // of the output. // Exits on read error or empty result. -func readPasswordExtpass(extpass []string) []byte { +func readPasswordExtpass(extpass []string) ([]byte, error) { var parts []string if len(extpass) == 1 { parts = strings.Split(extpass[0], " ") @@ -109,50 +113,47 @@ func readPasswordExtpass(extpass []string) []byte { cmd.Stderr = os.Stderr pipe, err := cmd.StdoutPipe() if err != nil { - tlog.Fatal.Printf("extpass pipe setup failed: %v", err) - os.Exit(exitcodes.ReadPassword) + return nil, fmt.Errorf("extpass pipe setup failed: %v", err) } err = cmd.Start() if err != nil { - tlog.Fatal.Printf("extpass cmd start failed: %v", err) - os.Exit(exitcodes.ReadPassword) + return nil, fmt.Errorf("extpass cmd start failed: %v", err) + } + p, err := readLineUnbuffered(pipe) + if err != nil { + return nil, err } - p := readLineUnbuffered(pipe) pipe.Close() err = cmd.Wait() if err != nil { - tlog.Fatal.Printf("extpass program returned an error: %v", err) - os.Exit(exitcodes.ReadPassword) + return nil, fmt.Errorf("extpass program returned an error: %v", err) } if len(p) == 0 { - tlog.Fatal.Println("extpass: password is empty") - os.Exit(exitcodes.ReadPassword) + return nil, fmt.Errorf("extpass: password is empty") } - return p + return p, nil } // readLineUnbuffered reads single bytes from "r" util it gets "\n" or EOF. // The returned string does NOT contain the trailing "\n". -func readLineUnbuffered(r io.Reader) (l []byte) { +func readLineUnbuffered(r io.Reader) (l []byte, err error) { b := make([]byte, 1) for { if len(l) > maxPasswordLen { - tlog.Fatal.Printf("fatal: maximum password length of %d bytes exceeded", maxPasswordLen) - os.Exit(exitcodes.ReadPassword) + return nil, fmt.Errorf("fatal: maximum password length of %d bytes exceeded", maxPasswordLen) } n, err := r.Read(b) if err == io.EOF { - return l + return l, nil } if err != nil { - tlog.Fatal.Printf("readLineUnbuffered: %v", err) - os.Exit(exitcodes.ReadPassword) + return nil, fmt.Errorf("readLineUnbuffered: %v", err) } if n == 0 { continue } if b[0] == '\n' { - return l + return l, nil } l = append(l, b...) } |