github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/pinentry/pinentry_nix.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  //go:build darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris
     5  // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
     6  
     7  package pinentry
     8  
     9  import (
    10  	"fmt"
    11  	"os"
    12  	"path/filepath"
    13  
    14  	"os/exec"
    15  
    16  	"github.com/keybase/client/go/logger"
    17  )
    18  
    19  //
    20  // some borrowed from here:
    21  //
    22  //  https://github.com/bradfitz/camlistore/blob/master/pkg/misc/pinentry/pinentry.go
    23  //
    24  // Under the Apache 2.0 license
    25  //
    26  
    27  func canExec(s string) error {
    28  	fi, err := os.Stat(s)
    29  	if err != nil {
    30  		return err
    31  	}
    32  	mode := fi.Mode()
    33  
    34  	//
    35  	// Only consider non-directories that have at least one +x
    36  	//  bit set.
    37  	//
    38  	// TODO: Recheck this on Windows!
    39  	//   See here for lookpath: http://golang.org/src/pkg/os/exec/lp_windows.go
    40  	//
    41  	// Similar to check from exec.LookPath below
    42  	//   See here: http://golang.org/src/pkg/os/exec/lp_unix.go
    43  	//
    44  	switch {
    45  	case mode.IsDir():
    46  		return fmt.Errorf("Program '%s' is a directory", s)
    47  	case int(mode)&0111 == 0:
    48  		return fmt.Errorf("Program '%s' isn't executable", s)
    49  	default:
    50  		return nil
    51  	}
    52  }
    53  
    54  func FindPinentry(log logger.Logger) (string, error) {
    55  	if !HasWindows() {
    56  		return "", fmt.Errorf("Can't spawn gui window, not using pinentry")
    57  	}
    58  	bins := []string{
    59  		// If you install MacTools you'll wind up with this pinentry
    60  		"/usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac",
    61  	}
    62  
    63  	extraPaths := []string{}
    64  
    65  	log.Debug("+ FindPinentry()")
    66  
    67  	cmds := []string{
    68  		"pinentry-gtk-2",
    69  		"pinentry-qt4",
    70  		"pinentry",
    71  	}
    72  
    73  	checkFull := func(s string) bool {
    74  		log.Debug("| Check fullpath %s", s)
    75  		found := (canExec(s) == nil)
    76  		if found {
    77  			log.Debug("- Found: %s", s)
    78  		}
    79  		return found
    80  	}
    81  
    82  	for _, b := range bins {
    83  		if checkFull(b) {
    84  			return b, nil
    85  		}
    86  	}
    87  
    88  	path := os.Getenv("PATH")
    89  	for _, c := range cmds {
    90  		log.Debug("| Looking for %s in standard PATH %s", c, path)
    91  		fullc, err := exec.LookPath(c)
    92  		if err == nil {
    93  			log.Debug("- Found %s", fullc)
    94  			return fullc, nil
    95  		}
    96  	}
    97  
    98  	for _, ep := range extraPaths {
    99  		for _, c := range cmds {
   100  			full := filepath.Join(ep, c)
   101  			if checkFull(full) {
   102  				return full, nil
   103  			}
   104  		}
   105  	}
   106  
   107  	log.Debug("- FindPinentry: none found")
   108  	return "", fmt.Errorf("No pinentry found, checked a bunch of different places")
   109  }
   110  
   111  func (pe *Pinentry) GetTerminalName() {
   112  	// Noop on all platforms but Windows
   113  }