Skip to content

Commit

Permalink
Add restore handler for password input
Browse files Browse the repository at this point in the history
This commit restores the terminal state in case the program is
interrupted while being in password read mode. This ensures the terminal
remains usable, also if the password input is being cancelled.

Signed-off-by: phoenix <[email protected]>
  • Loading branch information
grisu48 committed Aug 15, 2023
1 parent b70b0c4 commit 91a353f
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 2 deletions.
4 changes: 2 additions & 2 deletions pkg/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import (
"path/filepath"
"strings"

"github.com/containers/common/pkg/util"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/pkg/docker/config"
"github.com/containers/image/v5/pkg/sysregistriesv2"
"github.com/containers/image/v5/types"
"github.com/sirupsen/logrus"
terminal "golang.org/x/term"
)

// ErrNewCredentialsInvalid means that the new user-provided credentials are
Expand Down Expand Up @@ -269,7 +269,7 @@ func getUserAndPass(opts *LoginOptions, password, userFromAuthFile string) (user
}
if password == "" {
fmt.Fprint(opts.Stdout, "Password: ")
pass, err := terminal.ReadPassword(int(os.Stdin.Fd()))
pass, err := util.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return "", "", fmt.Errorf("reading password: %w", err)
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package util

import (
"bytes"
"errors"
"fmt"
"os"
"os/exec"
Expand All @@ -19,6 +20,10 @@ const (
UnknownPackage = "Unknown"
)

var (
ErrInterrupt = errors.New("Interrupt")
)

// Note: This function is copied from containers/podman libpod/util.go
// Please see https://github.com/containers/common/pull/1460
func queryPackageVersion(cmdArg ...string) string {
Expand Down
44 changes: 44 additions & 0 deletions pkg/util/util_supported.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import (
"errors"
"fmt"
"os"
"os/signal"
"path/filepath"
"sync"
"syscall"

"github.com/containers/storage/pkg/homedir"
"github.com/containers/storage/pkg/unshare"
"github.com/sirupsen/logrus"
terminal "golang.org/x/term"
)

var (
Expand Down Expand Up @@ -89,3 +91,45 @@ func GetRuntimeDir() (string, error) {
}
return rootlessRuntimeDir, nil
}

// ReadPassword reads a password from the terminal without echo.
func ReadPassword(fd int) ([]byte, error) {
// Store and restore the terminal status on interruptions to
// avoid that the terminal remains in the password state
// This is necessary as for https://github.com/golang/go/issues/31180

oldState, err := terminal.GetState(fd)
if err != nil {
return make([]byte, 0), err
}

type Buffer struct {
Buffer []byte
Error error
}
errorChannel := make(chan Buffer, 1)

// SIGINT and SIGTERM restore the terminal, otherwise the no-echo mode would remain intact
interruptChannel := make(chan os.Signal, 1)
signal.Notify(interruptChannel, syscall.SIGINT, syscall.SIGTERM)
defer func() {
signal.Stop(interruptChannel)
close(interruptChannel)
}()
go func() {
for range interruptChannel {
if oldState != nil {
_ = terminal.Restore(fd, oldState)
}
errorChannel <- Buffer{Buffer: make([]byte, 0), Error: ErrInterrupt}
}
}()

go func() {
buf, err := terminal.ReadPassword(fd)
errorChannel <- Buffer{Buffer: buf, Error: err}
}()

buf := <-errorChannel
return buf.Buffer, buf.Error
}
15 changes: 15 additions & 0 deletions pkg/util/util_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,24 @@ package util

import (
"errors"

terminal "golang.org/x/term"
)

// getRuntimeDir returns the runtime directory
func GetRuntimeDir() (string, error) {
return "", errors.New("this function is not implemented for windows")
}

// ReadPassword reads a password from the terminal.
func ReadPassword(fd int) ([]byte, error) {
oldState, err := terminal.GetState(fd)
if err != nil {
return make([]byte, 0), err
}
buf, err := terminal.ReadPassword(fd)
if oldState != nil {
_ = terminal.Restore(fd, oldState)
}
return buf, err
}

0 comments on commit 91a353f

Please sign in to comment.