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 }