github.com/StackExchange/blackbox/v2@v2.0.1-0.20220331193400-d84e904973ab/pkg/crypters/gnupg/gnupg.go (about)

     1  package gnupg
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"log"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  
    11  	"github.com/StackExchange/blackbox/v2/pkg/bblog"
    12  	"github.com/StackExchange/blackbox/v2/pkg/bbutil"
    13  	"github.com/StackExchange/blackbox/v2/pkg/crypters"
    14  )
    15  
    16  var pluginName = "GnuPG"
    17  
    18  func init() {
    19  	crypters.Register(pluginName, 100, registerNew)
    20  }
    21  
    22  // CrypterHandle is the handle
    23  type CrypterHandle struct {
    24  	GPGCmd   string // "gpg2" or "gpg"
    25  	logErr   *log.Logger
    26  	logDebug *log.Logger
    27  }
    28  
    29  func registerNew(debug bool) (crypters.Crypter, error) {
    30  
    31  	crypt := &CrypterHandle{
    32  		logErr:   bblog.GetErr(),
    33  		logDebug: bblog.GetDebug(debug),
    34  	}
    35  
    36  	// Which binary to use?
    37  	path, err := exec.LookPath("gpg2")
    38  	if err != nil {
    39  		path, err = exec.LookPath("gpg")
    40  		if err != nil {
    41  			path = "gpg2"
    42  		}
    43  	}
    44  	crypt.GPGCmd = path
    45  
    46  	return crypt, nil
    47  }
    48  
    49  // Name returns my name.
    50  func (crypt CrypterHandle) Name() string {
    51  	return pluginName
    52  }
    53  
    54  // Decrypt name+".gpg", possibly overwriting name.
    55  func (crypt CrypterHandle) Decrypt(filename string, umask int, overwrite bool) error {
    56  
    57  	a := []string{
    58  		"--use-agent",
    59  		"-q",
    60  		"--decrypt",
    61  		"-o", filename,
    62  	}
    63  	if overwrite {
    64  		a = append(a, "--yes")
    65  	}
    66  	a = append(a, filename+".gpg")
    67  
    68  	oldumask := bbutil.Umask(umask)
    69  	err := bbutil.RunBash(crypt.GPGCmd, a...)
    70  	bbutil.Umask(oldumask)
    71  	return err
    72  }
    73  
    74  // Cat returns the plaintext or, if it is missing, the decrypted cyphertext.
    75  func (crypt CrypterHandle) Cat(filename string) ([]byte, error) {
    76  
    77  	a := []string{
    78  		"--use-agent",
    79  		"-q",
    80  		"--decrypt",
    81  	}
    82  
    83  	// TODO(tlim): This assumes the entire gpg file fits in memory. If
    84  	// this becomes a problem, re-implement this using exec Cmd.StdinPipe()
    85  	// and feed the input in chunks.
    86  	in, err := ioutil.ReadFile(filename + ".gpg")
    87  	if err != nil {
    88  
    89  		if os.IsNotExist(err) {
    90  			// Encrypted file doesn't exit? Return the plaintext.
    91  			return ioutil.ReadFile(filename)
    92  		}
    93  
    94  		return nil, err
    95  	}
    96  
    97  	return bbutil.RunBashInputOutput(in, crypt.GPGCmd, a...)
    98  }
    99  
   100  // Encrypt name, overwriting name+".gpg"
   101  func (crypt CrypterHandle) Encrypt(filename string, umask int, receivers []string) (string, error) {
   102  	var err error
   103  
   104  	crypt.logDebug.Printf("Encrypt(%q, %d, %q)", filename, umask, receivers)
   105  	encrypted := filename + ".gpg"
   106  	a := []string{
   107  		"--use-agent",
   108  		"--yes",
   109  		"--trust-model=always",
   110  		"--encrypt",
   111  		"-o", encrypted,
   112  	}
   113  	for _, f := range receivers {
   114  		a = append(a, "-r", f)
   115  	}
   116  	a = append(a, "--encrypt")
   117  	a = append(a, filename)
   118  	//err = bbutil.RunBash("ls", "-la")
   119  
   120  	oldumask := bbutil.Umask(umask)
   121  	crypt.logDebug.Printf("Args = %q", a)
   122  	err = bbutil.RunBash(crypt.GPGCmd, a...)
   123  	bbutil.Umask(oldumask)
   124  
   125  	return encrypted, err
   126  }
   127  
   128  // AddNewKey extracts keyname from sourcedir's GnuPG chain to destdir keychain.
   129  // It returns a list of files that may have changed.
   130  func (crypt CrypterHandle) AddNewKey(keyname, repobasedir, sourcedir, destdir string) ([]string, error) {
   131  
   132  	// $GPG --homedir="$2" --export -a "$KEYNAME" >"$pubkeyfile"
   133  	args := []string{
   134  		"--export",
   135  		"-a",
   136  	}
   137  	if sourcedir != "" {
   138  		args = append(args, "--homedir", sourcedir)
   139  	}
   140  	args = append(args, keyname)
   141  	crypt.logDebug.Printf("ADDNEWKEY: Extracting key=%v: gpg, %v\n", keyname, args)
   142  	pubkey, err := bbutil.RunBashOutput("gpg", args...)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	if len(pubkey) == 0 {
   147  		return nil, fmt.Errorf("Nothing found when %q exported from %q", keyname, sourcedir)
   148  	}
   149  
   150  	// $GPG --no-permission-warning --homedir="$KEYRINGDIR" --import "$pubkeyfile"
   151  	args = []string{
   152  		"--no-permission-warning",
   153  		"--homedir", destdir,
   154  		"--import",
   155  	}
   156  	crypt.logDebug.Printf("ADDNEWKEY: Importing: gpg %v\n", args)
   157  	// fmt.Printf("DEBUG: crypter ADD %q", args)
   158  	err = bbutil.RunBashInput(pubkey, "gpg", args...)
   159  	if err != nil {
   160  		return nil, fmt.Errorf("AddNewKey failed: %w", err)
   161  	}
   162  
   163  	// Suggest: ${pubring_path} trustdb.gpg  blackbox-admins.txt
   164  	var changed []string
   165  
   166  	// Prefix each file with the relative path to it.
   167  	prefix, err := filepath.Rel(repobasedir, destdir)
   168  	if err != nil {
   169  		//fmt.Printf("FAIL (%v) (%v) (%v)\n", repobasedir, destdir, err)
   170  		prefix = destdir
   171  	}
   172  	for _, file := range []string{"pubring.gpg", "pubring.kbx", "trustdb.gpg"} {
   173  		path := filepath.Join(destdir, file)
   174  		if bbutil.FileExistsOrProblem(path) {
   175  			changed = append(changed, filepath.Join(prefix, file))
   176  		}
   177  	}
   178  	return changed, nil
   179  }