diff --git a/Makefile b/Makefile index 0572c9a05f..d0fdb9eb2c 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,6 @@ clean: @rm -f tests/tests @rm -f *.test @rm -rf dist/* - @rm -f *.completion @printf '%s\n' '$(OK)' $(GOPASS_OUTPUT): $(GOFILES_BUILD) diff --git a/go.mod b/go.mod index e2bab10bbf..fcefc0366f 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/google/go-cmp v0.5.5 github.com/google/go-github v17.0.0+incompatible github.com/google/go-github/v33 v33.0.0 + github.com/gopasspw/pinentry v0.0.2 github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/golang-lru v0.5.4 diff --git a/go.sum b/go.sum index 122d1b4942..a9d6ebc5fe 100644 --- a/go.sum +++ b/go.sum @@ -125,6 +125,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopasspw/pinentry v0.0.2 h1:6OmkoTYMU05PmAJSIZSRjjhiQX15AstdgNa2KimH5XA= +github.com/gopasspw/pinentry v0.0.2/go.mod h1:lR1WuNI96rXXBCgM601Ima3acnX3ZSPthIAuG6lHa68= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/internal/backend/crypto/age/askpass.go b/internal/backend/crypto/age/askpass.go index 2560da9f88..b6bf0f5ce9 100644 --- a/internal/backend/crypto/age/askpass.go +++ b/internal/backend/crypto/age/askpass.go @@ -8,7 +8,7 @@ import ( "github.com/gopasspw/gopass/internal/cache" "github.com/gopasspw/gopass/internal/out" "github.com/gopasspw/gopass/pkg/debug" - "github.com/gopasspw/gopass/pkg/pinentry" + "github.com/gopasspw/pinentry" "github.com/gopasspw/gopass/pkg/pinentry/cli" ) diff --git a/internal/backend/crypto/gpg/cli/binary_others.go b/internal/backend/crypto/gpg/cli/binary_others.go index 9b720aa46f..9eb7866b8c 100644 --- a/internal/backend/crypto/gpg/cli/binary_others.go +++ b/internal/backend/crypto/gpg/cli/binary_others.go @@ -6,7 +6,7 @@ import ( "os/exec" "github.com/gopasspw/gopass/pkg/debug" - "github.com/gopasspw/gopass/pkg/pinentry/gpgconf" + "github.com/gopasspw/pinentry/gpgconf" ) func detectBinary(name string) (string, error) { diff --git a/pkg/pinentry/gpgconf/gpgconf.go b/pkg/pinentry/gpgconf/gpgconf.go deleted file mode 100644 index d39ed88bcc..0000000000 --- a/pkg/pinentry/gpgconf/gpgconf.go +++ /dev/null @@ -1,39 +0,0 @@ -package gpgconf - -import ( - "bufio" - "bytes" - "os" - "os/exec" - "strings" - - "github.com/gopasspw/gopass/pkg/debug" -) - -// Path returns the path to a GPG component -func Path(key string) (string, error) { - buf := &bytes.Buffer{} - cmd := exec.Command("gpgconf") - cmd.Stdout = buf - cmd.Stderr = os.Stderr - - debug.Log("%s %+v", cmd.Path, cmd.Args) - if err := cmd.Run(); err != nil { - return "", err - } - - key = strings.TrimSpace(strings.ToLower(key)) - sc := bufio.NewScanner(buf) - for sc.Scan() { - p := strings.Split(strings.TrimSpace(sc.Text()), ":") - if len(p) < 3 { - continue - } - if key == p[0] { - return p[2], nil - } - } - - debug.Log("key %q not found", key) - return "", nil -} diff --git a/pkg/pinentry/pinentry.go b/pkg/pinentry/pinentry.go deleted file mode 100644 index 1f2b8ba3e5..0000000000 --- a/pkg/pinentry/pinentry.go +++ /dev/null @@ -1,164 +0,0 @@ -// Package pinentry implements a cross platform pinentry client. It can be used -// to obtain credentials from the user through a simple UI application. -package pinentry - -import ( - "bufio" - "bytes" - "fmt" - "io" - "net/url" - "os" - "os/exec" - "strings" - - "github.com/gopasspw/gopass/pkg/debug" -) - -var ( - // Unescape enables unescaping of percent encoded values, - // disabled by default for backward compatibility. See #1621 - Unescape bool -) - -func init() { - if sv := os.Getenv("GOPASS_PINENTRY_UNESCAPE"); sv == "on" || sv == "true" { - Unescape = true - } -} - -// Client is a pinentry client -type Client struct { - cmd *exec.Cmd - in io.WriteCloser - out *bufio.Reader -} - -// New creates a new pinentry client -func New() (*Client, error) { - cmd := exec.Command(GetBinary()) - stdin, err := cmd.StdinPipe() - if err != nil { - return nil, err - } - - stdout, err := cmd.StdoutPipe() - if err != nil { - return nil, err - } - - br := bufio.NewReader(stdout) - if err := cmd.Start(); err != nil { - return nil, err - } - - // check welcome message - banner, _, err := br.ReadLine() - if err != nil { - return nil, err - } - if !bytes.HasPrefix(banner, []byte("OK")) { - return nil, fmt.Errorf("wrong banner: %s", banner) - } - - cl := &Client{ - cmd: cmd, - in: stdin, - out: br, - } - - return cl, nil -} - -// Close closes the client -func (c *Client) Close() { - _ = c.in.Close() -} - -// Confirm sends the confirm message -func (c *Client) Confirm() bool { - if err := c.Set("confirm", ""); err == nil { - return true - } - return false -} - -// Set sets a key -func (c *Client) Set(key, value string) error { - key = strings.ToUpper(key) - if value != "" { - value = " " + value - } - val := "SET" + key + value + "\n" - if _, err := c.in.Write([]byte(val)); err != nil { - return err - } - line, _, _ := c.out.ReadLine() - if string(line) != "OK" { - return fmt.Errorf("error: %s", line) - } - return nil -} - -// Option sets an option, e.g. "default-cancel=Abbruch" or "allow-external-password-cache" -func (c *Client) Option(value string) error { - val := "OPTION " + value + "\n" - if _, err := c.in.Write([]byte(val)); err != nil { - return err - } - line, _, _ := c.out.ReadLine() - if string(line) != "OK" { - return fmt.Errorf("error: %s", line) - } - return nil -} - -// GetPin asks for the pin -func (c *Client) GetPin() ([]byte, error) { - if _, err := c.in.Write([]byte("GETPIN\n")); err != nil { - return nil, err - } - buf, _, err := c.out.ReadLine() - if err != nil { - return nil, err - } - if bytes.HasPrefix(buf, []byte("OK")) { - return nil, nil - } - // handle status messages - for bytes.HasPrefix(buf, []byte("S ")) { - debug.Log("message: %q", string(buf)) - buf, _, err = c.out.ReadLine() - if err != nil { - return nil, err - } - } - // now there should be some data - if !bytes.HasPrefix(buf, []byte("D ")) { - return nil, fmt.Errorf("unexpected response: %s", buf) - } - - pin := make([]byte, len(buf)) - if n := copy(pin, buf); n != len(buf) { - return nil, fmt.Errorf("failed to copy pin: expected %d bytes only copied %d", len(buf), n) - } - - ok, _, err := c.out.ReadLine() - if err != nil { - return nil, err - } - if !bytes.HasPrefix(ok, []byte("OK")) { - return nil, fmt.Errorf("unexpected response: %s", ok) - } - pin = pin[2:] - - // Handle unescaping, if enabled - if bytes.Contains(pin, []byte("%")) && Unescape { - sv, err := url.QueryUnescape(string(pin)) - if err != nil { - return nil, fmt.Errorf("failed to unescape pin: %q", err) - } - pin = []byte(sv) - } - return pin, nil -} diff --git a/pkg/pinentry/pinentry_darwin.go b/pkg/pinentry/pinentry_darwin.go deleted file mode 100644 index 354e18aca7..0000000000 --- a/pkg/pinentry/pinentry_darwin.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build darwin - -package pinentry - -import "github.com/gopasspw/gopass/pkg/pinentry/gpgconf" - -// GetBinary always returns pinentry-mac -func GetBinary() string { - if p, err := gpgconf.Path("pinentry"); err == nil && p != "" { - return p - } - return "pinentry-mac" -} diff --git a/pkg/pinentry/pinentry_others.go b/pkg/pinentry/pinentry_others.go deleted file mode 100644 index dd7aef2e69..0000000000 --- a/pkg/pinentry/pinentry_others.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build !darwin,!windows - -package pinentry - -import "github.com/gopasspw/gopass/pkg/pinentry/gpgconf" - -// GetBinary returns the binary name -func GetBinary() string { - if p, err := gpgconf.Path("pinentry"); err == nil && p != "" { - return p - } - return "pinentry" -} diff --git a/pkg/pinentry/pinentry_test.go b/pkg/pinentry/pinentry_test.go deleted file mode 100644 index 9b508def43..0000000000 --- a/pkg/pinentry/pinentry_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package pinentry - -import "fmt" - -func ExampleClient() { - pi, err := New() - if err != nil { - panic(err) - } - _ = pi.Set("title", "Agent Pinentry") - _ = pi.Set("desc", "Asking for a passphrase") - _ = pi.Set("prompt", "Please enter your passphrase:") - _ = pi.Set("ok", "OK") - pin, err := pi.GetPin() - if err != nil { - panic(err) - } - fmt.Println(string(pin)) -} diff --git a/pkg/pinentry/pinentry_windows.go b/pkg/pinentry/pinentry_windows.go deleted file mode 100644 index 3a60f58962..0000000000 --- a/pkg/pinentry/pinentry_windows.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build windows - -package pinentry - -import "github.com/gopasspw/gopass/pkg/pinentry/gpgconf" - -// GetBinary always returns pinentry.exe -func GetBinary() string { - if p, err := gpgconf.Path("pinentry"); err == nil && p != "" { - return p - } - return "pinentry.exe" -} diff --git a/zsh.completion b/zsh.completion index d7ad12c21c..91430ce326 100644 --- a/zsh.completion +++ b/zsh.completion @@ -70,7 +70,7 @@ _gopass () { _gopass_complete_passwords ;; create|new) - _arguments : "--store[Which store to use]" + _arguments : "--store[Which store to use]" "--force[Force path selection]" _describe -t commands "gopass create" subcommands