Vendor out the keybase pinentry
This commit is contained in:
parent
f60fcfd408
commit
e0cb8a9187
|
@ -6,8 +6,8 @@ import (
|
|||
"errors"
|
||||
|
||||
"github.com/keybase/client/go/logger"
|
||||
"github.com/keybase/client/go/pinentry"
|
||||
"github.com/keybase/client/go/protocol/keybase1"
|
||||
pinentry "github.com/quexten/goldwarden/agent/systemauth/pinentry/keybase-pinentry"
|
||||
)
|
||||
|
||||
func GetPassword(title string, description string) (string, error) {
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2015, Keybase
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of keybase nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,219 @@
|
|||
// Copyright 2015 Keybase, Inc. All rights reserved. Use of
|
||||
// this source code is governed by the included BSD license.
|
||||
|
||||
package pinentry
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/keybase/client/go/logger"
|
||||
keybase1 "github.com/keybase/client/go/protocol/keybase1"
|
||||
)
|
||||
|
||||
//
|
||||
// some borrowed from here:
|
||||
//
|
||||
// https://github.com/bradfitz/camlistore/blob/master/pkg/misc/pinentry/pinentry.go
|
||||
//
|
||||
// Under the Apache 2.0 license
|
||||
//
|
||||
|
||||
type Pinentry struct {
|
||||
initRes *error
|
||||
path string
|
||||
term string
|
||||
tty string
|
||||
prog string
|
||||
log logger.Logger
|
||||
}
|
||||
|
||||
func New(envprog string, log logger.Logger, tty string) *Pinentry {
|
||||
return &Pinentry{
|
||||
prog: envprog,
|
||||
log: log,
|
||||
tty: tty,
|
||||
}
|
||||
}
|
||||
|
||||
func (pe *Pinentry) Init() (error, error) {
|
||||
if pe.initRes != nil {
|
||||
return *pe.initRes, nil
|
||||
}
|
||||
err, fatalerr := pe.FindProgram()
|
||||
if err == nil {
|
||||
pe.GetTerminalName()
|
||||
}
|
||||
pe.term = os.Getenv("TERM")
|
||||
pe.initRes = &err
|
||||
return err, fatalerr
|
||||
}
|
||||
|
||||
func (pe *Pinentry) SetInitError(e error) {
|
||||
pe.initRes = &e
|
||||
}
|
||||
|
||||
func (pe *Pinentry) FindProgram() (error, error) {
|
||||
prog := pe.prog
|
||||
var err, fatalerr error
|
||||
if len(prog) > 0 {
|
||||
if err = canExec(prog); err == nil {
|
||||
pe.path = prog
|
||||
} else {
|
||||
err = fmt.Errorf("Can't execute given pinentry program '%s': %s",
|
||||
prog, err)
|
||||
fatalerr = err
|
||||
}
|
||||
} else if prog, err = FindPinentry(pe.log); err == nil {
|
||||
pe.path = prog
|
||||
}
|
||||
return err, fatalerr
|
||||
}
|
||||
|
||||
func (pe *Pinentry) Get(arg keybase1.SecretEntryArg) (res *keybase1.SecretEntryRes, err error) {
|
||||
|
||||
pe.log.Debug("+ Pinentry::Get()")
|
||||
|
||||
// Do a lazy initialization
|
||||
if err, _ = pe.Init(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
inst := pinentryInstance{parent: pe}
|
||||
defer inst.Close()
|
||||
|
||||
if err = inst.Init(); err != nil {
|
||||
// We probably shouldn't try to use this thing again if we failed
|
||||
// to set it up.
|
||||
pe.SetInitError(err)
|
||||
return
|
||||
}
|
||||
res, err = inst.Run(arg)
|
||||
pe.log.Debug("- Pinentry::Get() -> %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
func (pi *pinentryInstance) Close() {
|
||||
pi.stdin.Close()
|
||||
pi.cmd.Wait()
|
||||
}
|
||||
|
||||
type pinentryInstance struct {
|
||||
parent *Pinentry
|
||||
cmd *exec.Cmd
|
||||
stdout io.ReadCloser
|
||||
stdin io.WriteCloser
|
||||
br *bufio.Reader
|
||||
}
|
||||
|
||||
func (pi *pinentryInstance) Set(cmd, val string, errp *error) {
|
||||
if val == "" {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(pi.stdin, "%s %s\n", cmd, val)
|
||||
line, _, err := pi.br.ReadLine()
|
||||
if err != nil {
|
||||
*errp = err
|
||||
return
|
||||
}
|
||||
if string(line) != "OK" {
|
||||
*errp = fmt.Errorf("Response to " + cmd + " was " + string(line))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (pi *pinentryInstance) Init() (err error) {
|
||||
parent := pi.parent
|
||||
|
||||
parent.log.Debug("+ pinentryInstance::Init()")
|
||||
|
||||
pi.cmd = exec.Command(parent.path)
|
||||
pi.stdin, _ = pi.cmd.StdinPipe()
|
||||
pi.stdout, _ = pi.cmd.StdoutPipe()
|
||||
|
||||
if err = pi.cmd.Start(); err != nil {
|
||||
parent.log.Warning("unexpected error running pinentry (%s): %s", parent.path, err)
|
||||
return
|
||||
}
|
||||
|
||||
pi.br = bufio.NewReader(pi.stdout)
|
||||
lineb, _, err := pi.br.ReadLine()
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Failed to get getpin greeting: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
line := string(lineb)
|
||||
if !strings.HasPrefix(line, "OK") {
|
||||
err = fmt.Errorf("getpin greeting didn't say 'OK', said: %q", line)
|
||||
return
|
||||
}
|
||||
|
||||
if len(parent.tty) > 0 {
|
||||
parent.log.Debug("setting ttyname to %s", parent.tty)
|
||||
pi.Set("OPTION", "ttyname="+parent.tty, &err)
|
||||
if err != nil {
|
||||
parent.log.Debug("error setting ttyname: %s", err)
|
||||
}
|
||||
}
|
||||
if len(parent.term) > 0 {
|
||||
parent.log.Debug("setting ttytype to %s", parent.term)
|
||||
pi.Set("OPTION", "ttytype="+parent.term, &err)
|
||||
if err != nil {
|
||||
parent.log.Debug("error setting ttytype: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
parent.log.Debug("- pinentryInstance::Init() -> %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
func descEncode(s string) string {
|
||||
s = strings.Replace(s, "%", "%%", -1)
|
||||
s = strings.Replace(s, "\n", "%0A", -1)
|
||||
return s
|
||||
}
|
||||
|
||||
func resDecode(s string) string {
|
||||
s = strings.Replace(s, "%25", "%", -1)
|
||||
return s
|
||||
}
|
||||
|
||||
func (pi *pinentryInstance) Run(arg keybase1.SecretEntryArg) (res *keybase1.SecretEntryRes, err error) {
|
||||
|
||||
pi.Set("SETPROMPT", arg.Prompt, &err)
|
||||
pi.Set("SETDESC", descEncode(arg.Desc), &err)
|
||||
pi.Set("SETOK", arg.Ok, &err)
|
||||
pi.Set("SETCANCEL", arg.Cancel, &err)
|
||||
pi.Set("SETERROR", arg.Err, &err)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(pi.stdin, "GETPIN\n")
|
||||
var lineb []byte
|
||||
lineb, _, err = pi.br.ReadLine()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Failed to read line after GETPIN: %v", err)
|
||||
return
|
||||
}
|
||||
line := string(lineb)
|
||||
switch {
|
||||
case strings.HasPrefix(line, "D "):
|
||||
res = &keybase1.SecretEntryRes{Text: resDecode(line[2:])}
|
||||
case strings.HasPrefix(line, "ERR 83886179 canceled") || strings.HasPrefix(line, "ERR 83886179 Operation cancelled"):
|
||||
res = &keybase1.SecretEntryRes{Canceled: true}
|
||||
case line == "OK":
|
||||
res = &keybase1.SecretEntryRes{}
|
||||
default:
|
||||
return nil, fmt.Errorf("GETPIN response didn't start with D; got %q", line)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright 2015 Keybase, Inc. All rights reserved. Use of
|
||||
// this source code is governed by the included BSD license.
|
||||
|
||||
//go:build darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris
|
||||
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
|
||||
|
||||
package pinentry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/keybase/client/go/logger"
|
||||
)
|
||||
|
||||
//
|
||||
// some borrowed from here:
|
||||
//
|
||||
// https://github.com/bradfitz/camlistore/blob/master/pkg/misc/pinentry/pinentry.go
|
||||
//
|
||||
// Under the Apache 2.0 license
|
||||
//
|
||||
|
||||
func canExec(s string) error {
|
||||
fi, err := os.Stat(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mode := fi.Mode()
|
||||
|
||||
//
|
||||
// Only consider non-directories that have at least one +x
|
||||
// bit set.
|
||||
//
|
||||
// TODO: Recheck this on windows!
|
||||
// See here for lookpath: http://golang.org/src/pkg/os/exec/lp_windows.go
|
||||
//
|
||||
// Similar to check from exec.LookPath below
|
||||
// See here: http://golang.org/src/pkg/os/exec/lp_unix.go
|
||||
//
|
||||
if mode.IsDir() {
|
||||
return fmt.Errorf("Program '%s' is a directory", s)
|
||||
} else if int(mode)&0111 == 0 {
|
||||
return fmt.Errorf("Program '%s' isn't executable", s)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func FindPinentry(log logger.Logger) (string, error) {
|
||||
if !HasWindows() {
|
||||
return "", fmt.Errorf("Can't spawn gui window, not using pinentry")
|
||||
}
|
||||
bins := []string{
|
||||
// If you install MacTools you'll wind up with this pinentry
|
||||
"/usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac",
|
||||
}
|
||||
|
||||
extraPaths := []string{}
|
||||
|
||||
log.Debug("+ FindPinentry()")
|
||||
|
||||
cmds := []string{
|
||||
"pinentry-gtk-2",
|
||||
"pinentry-qt4",
|
||||
"pinentry",
|
||||
}
|
||||
|
||||
checkFull := func(s string) bool {
|
||||
log.Debug("| Check fullpath %s", s)
|
||||
found := (canExec(s) == nil)
|
||||
if found {
|
||||
log.Debug("- Found: %s", s)
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
for _, b := range bins {
|
||||
if checkFull(b) {
|
||||
return b, nil
|
||||
}
|
||||
}
|
||||
|
||||
path := os.Getenv("PATH")
|
||||
for _, c := range cmds {
|
||||
log.Debug("| Looking for %s in standard PATH %s", c, path)
|
||||
fullc, err := exec.LookPath(c)
|
||||
if err == nil {
|
||||
log.Debug("- Found %s", fullc)
|
||||
return fullc, nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, ep := range extraPaths {
|
||||
for _, c := range cmds {
|
||||
full := filepath.Join(ep, c)
|
||||
if checkFull(full) {
|
||||
return full, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug("- FindPinentry: none found")
|
||||
return "", fmt.Errorf("No pinentry found, checked a bunch of different places")
|
||||
}
|
||||
|
||||
func (pe *Pinentry) GetTerminalName() {
|
||||
// Noop on all platforms but windows
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2015 Keybase, Inc. All rights reserved. Use of
|
||||
// this source code is governed by the included BSD license.
|
||||
|
||||
//go:build !darwin
|
||||
// +build !darwin
|
||||
|
||||
package pinentry
|
||||
|
||||
type pinentrySecretStoreInfo struct{}
|
||||
|
||||
func (pi *pinentryInstance) useSecretStore(useSecretStore bool) (pinentrySecretStoreInfo, error) {
|
||||
return pinentrySecretStoreInfo{}, nil
|
||||
}
|
||||
|
||||
func (pi *pinentryInstance) shouldStoreSecret(info pinentrySecretStoreInfo) bool {
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
// Copyright 2015 Keybase, Inc. All rights reserved. Use of
|
||||
// this source code is governed by the included BSD license.
|
||||
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package pinentry
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/keybase/go-keychain"
|
||||
)
|
||||
|
||||
const (
|
||||
// pinentryServiceName is the service name that pinentry uses
|
||||
// when storing into the Keychain.
|
||||
pinentryServiceName = "GnuPG"
|
||||
// accountNameByteLength is how many random bytes to use to
|
||||
// generate the account name. 32 bytes of randomness is more
|
||||
// than enough to make the account name unpredictable.
|
||||
accountNameByteLength = 32
|
||||
)
|
||||
|
||||
type pinentrySecretStoreInfo string
|
||||
|
||||
func (pi *pinentryInstance) useSecretStore(useSecretStore bool) (pinentrySecretStoreInfo, error) {
|
||||
if !useSecretStore {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Make account name unpredictable to make it infeasible for
|
||||
// an attacker to guess (and thus sniff the passphrase). See
|
||||
// https://github.com/keybase/client/issues/484#issuecomment-114313867
|
||||
// .
|
||||
var accountNameBytes [accountNameByteLength]byte
|
||||
n, err := rand.Read(accountNameBytes[:])
|
||||
if n != accountNameByteLength {
|
||||
return "", fmt.Errorf("Unexpected random byte count %d", n)
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
accountName := "keybase-" + hex.EncodeToString(accountNameBytes[:])
|
||||
|
||||
// This will cause a "Save in Keychain" checkbox to appear in
|
||||
// the pinentry dialog. If checked, pinentry will then save
|
||||
// the entered passphrase into the keychain with the service
|
||||
// name "GnuPG" and the account name equal to the passed-in
|
||||
// cache-id option value.
|
||||
pi.Set("OPTION", "cache-id "+accountName, &err)
|
||||
if err != nil {
|
||||
// It's possible that the pinentry being used doesn't support
|
||||
// this option. So just return instead of causing a fatal
|
||||
// error.
|
||||
pi.parent.log.Debug("| Error setting pinentry cache-id OPTION: %s", err)
|
||||
pi.parent.log.Debug("| Not using secret store as a result.")
|
||||
return "", nil
|
||||
}
|
||||
return pinentrySecretStoreInfo(accountName), err
|
||||
}
|
||||
|
||||
func (pi *pinentryInstance) shouldStoreSecret(info pinentrySecretStoreInfo) bool {
|
||||
if len(info) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// We just want to know when the user did check the "Save in
|
||||
// Keychain" checkbox, so remove whatever pinentry put into
|
||||
// the keychain, and infer the state of the checkbox from the
|
||||
// error (since there will be no error if an entry was found
|
||||
// and deleted).
|
||||
//
|
||||
// This is a bit of a hack -- this may cause a dialog to pop
|
||||
// up saying that the client wants to access the user's
|
||||
// keychain. But this will do for now until we write our own
|
||||
// pinentry.
|
||||
query := keychain.NewItem()
|
||||
query.SetSecClass(keychain.SecClassGenericPassword)
|
||||
query.SetService(pinentryServiceName)
|
||||
query.SetAccount(string(info))
|
||||
query.SetMatchLimit(keychain.MatchLimitOne)
|
||||
|
||||
// We need to query and delete by item reference because the
|
||||
// OSX keychain API only allows us to delete unowned items
|
||||
// this way.
|
||||
query.SetReturnRef(true)
|
||||
ref, err := keychain.QueryItemRef(query)
|
||||
if err != nil {
|
||||
// Default to false if there was an error.
|
||||
return false
|
||||
}
|
||||
if ref == nil {
|
||||
// If not found, return false.
|
||||
return false
|
||||
}
|
||||
|
||||
defer keychain.Release(ref)
|
||||
|
||||
err = keychain.DeleteItemRef(ref)
|
||||
if err != nil {
|
||||
// Default to false if there was an error deleting.
|
||||
return false
|
||||
}
|
||||
|
||||
// Entry was found and deleted.
|
||||
return true
|
||||
}
|
||||
|
||||
func HasWindows() bool {
|
||||
// We aren't in an ssh connection, so we can probably spawn a window.
|
||||
return len(os.Getenv("SSH_CONNECTION")) == 0
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright 2015 Keybase, Inc. All rights reserved. Use of
|
||||
// this source code is governed by the included BSD license.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package pinentry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/keybase/client/go/logger"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
func HasWindows() bool {
|
||||
// We're assuming you aren't using windows remotely.
|
||||
return true
|
||||
}
|
||||
|
||||
// LookPath searches for an executable binary named file
|
||||
// in the directories named by the PATH environment variable.
|
||||
// If file contains a slash, it is tried directly and the PATH is not consulted.
|
||||
|
||||
func canExec(s string) error {
|
||||
if strings.IndexAny(s, `:\/`) == -1 {
|
||||
s += string(filepath.Separator)
|
||||
}
|
||||
_, err := exec.LookPath(s)
|
||||
return err
|
||||
}
|
||||
|
||||
func FindPinentry(log logger.Logger) (string, error) {
|
||||
|
||||
// // If you install GPG you'll wind up with this pinentry
|
||||
// C:\Program Files (x86)\GNU\GnuPG\pinentry-gtk-2.exe
|
||||
// C:\Program Files (x86)\GNU\GnuPG\pinentry-qt4.exe
|
||||
// C:\Program Files (x86)\GNU\GnuPG\pinentry-w32.exe
|
||||
// C:\Program Files (x86)\GNU\GnuPG\pinentry.exe
|
||||
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Wow6432Node\GNU\GnuPG`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
k, err = registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\GNU\GnuPG`, registry.QUERY_VALUE)
|
||||
}
|
||||
if err != nil {
|
||||
log.Debug("- FindPinentry: can't open registry")
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
installDir, _, err := k.GetStringValue("Install Directory")
|
||||
if err != nil {
|
||||
log.Debug("- FindPinentry: can't get string from registry")
|
||||
}
|
||||
|
||||
extraPaths := []string{}
|
||||
|
||||
log.Debug("+ FindPinentry()")
|
||||
|
||||
cmds := []string{
|
||||
"pinentry-gtk-2.exe",
|
||||
"pinentry-qt4.exe",
|
||||
"pinentry-w32.exe",
|
||||
"pinentry.exe",
|
||||
}
|
||||
|
||||
// First, look where the registry points
|
||||
for _, c := range cmds {
|
||||
full := filepath.Join(installDir, c)
|
||||
log.Debug("| (registry) Looking for %s", full)
|
||||
_, err := exec.LookPath(full)
|
||||
if err == nil {
|
||||
return full, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Look in program files, just in case
|
||||
extraPaths = append(extraPaths, os.Getenv("ProgramFiles"))
|
||||
extraPaths = append(extraPaths, os.Getenv("ProgramFiles(x86)"))
|
||||
|
||||
for _, ep := range extraPaths {
|
||||
for _, c := range cmds {
|
||||
full := filepath.Join(ep, "GNU", "GnuPG", c)
|
||||
log.Debug("| Looking for %s", full)
|
||||
_, err := exec.LookPath(full)
|
||||
if err == nil {
|
||||
return full, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, ep := range extraPaths {
|
||||
for _, c := range cmds {
|
||||
full := filepath.Join(ep, "Gpg4win", "bin", c)
|
||||
log.Debug("| Looking for %s", full)
|
||||
_, err := exec.LookPath(full)
|
||||
if err == nil {
|
||||
return full, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug("- FindPinentry: none found")
|
||||
return "", fmt.Errorf("No pinentry found, checked a bunch of different places")
|
||||
}
|
||||
|
||||
func (pe *Pinentry) GetTerminalName() {
|
||||
pe.tty = "windows"
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2015 Keybase, Inc. All rights reserved. Use of
|
||||
// this source code is governed by the included BSD license.
|
||||
|
||||
//go:build dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris
|
||||
// +build dragonfly freebsd linux nacl netbsd openbsd solaris
|
||||
|
||||
package pinentry
|
||||
|
||||
import "os"
|
||||
|
||||
func HasWindows() bool {
|
||||
//If there is a DISPLAY then we can spawn a window to it.
|
||||
return len(os.Getenv("DISPLAY")) > 0
|
||||
}
|
3
go.mod
3
go.mod
|
@ -11,6 +11,7 @@ require (
|
|||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/icza/gox v0.0.0-20230924165045-adcb03233bb5
|
||||
github.com/keybase/client/go v0.0.0-20240202160538-668db6be75e4
|
||||
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6
|
||||
github.com/lox/go-touchid v0.0.0-20170712105233-619cc8e578d0
|
||||
github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
|
@ -20,6 +21,7 @@ require (
|
|||
github.com/vmihailenco/msgpack/v5 v5.4.1
|
||||
golang.org/x/crypto v0.17.0
|
||||
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848
|
||||
golang.org/x/sys v0.15.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -32,7 +34,6 @@ require (
|
|||
github.com/keybase/go-logging v0.0.0-20231213204715-4b3ff33ba5b6 // indirect
|
||||
github.com/keybase/msgpackzip v0.0.0-20221220225959-4abf538d2b9c // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
2
go.sum
2
go.sum
|
@ -41,6 +41,8 @@ github.com/keybase/go-framed-msgpack-rpc v0.0.0-20230103225103-1f052922b096 h1:r
|
|||
github.com/keybase/go-framed-msgpack-rpc v0.0.0-20230103225103-1f052922b096/go.mod h1:XO67nMjltHJ8OsBWnFiDU1F67wR+rtJB21NXtb1TKyA=
|
||||
github.com/keybase/go-jsonw v0.0.0-20200325173637-df90f282c233 h1:zLk+cB/0ShMCBcgBOXYgellLZiZahXFicJleKyrlqiM=
|
||||
github.com/keybase/go-jsonw v0.0.0-20200325173637-df90f282c233/go.mod h1:lofKQwj13L0/7ji5VYaY0257JDlQE2BRRf+rI2Vk1rU=
|
||||
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
|
||||
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
|
||||
github.com/keybase/go-logging v0.0.0-20231213204715-4b3ff33ba5b6 h1:H4IvZdHXpeK963LgCMbTcEviEal4891UGf2iOqOGL94=
|
||||
github.com/keybase/go-logging v0.0.0-20231213204715-4b3ff33ba5b6/go.mod h1:0yOEB+QF1Ega1Cr7oMKb3yUAc3C9/eg6fBHB5HLP7AA=
|
||||
github.com/keybase/msgpackzip v0.0.0-20221220225959-4abf538d2b9c h1:PRG2AXSelSy7MiDI+PwJR2QSqI1N3OybRUutsMiHtpo=
|
||||
|
|
Loading…
Reference in New Issue