Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use temporary files, instead of stdin/stdout, when calling gpg #1229

Merged
merged 1 commit into from
May 26, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 105 additions & 38 deletions internal/chezmoi/gpgencryption.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package chezmoi

import (
"bytes"
"os"
"os/exec"
"path/filepath"
"runtime"

"github.com/rs/zerolog/log"

Expand All @@ -21,69 +22,135 @@ type GPGEncryption struct {

// Decrypt implements Encyrption.Decrypt.
func (e *GPGEncryption) Decrypt(ciphertext []byte) ([]byte, error) {
args := append([]string{"--decrypt"}, e.Args...)
//nolint:gosec
cmd := exec.Command(e.Command, args...)
cmd.Stdin = bytes.NewReader(ciphertext)
cmd.Stderr = os.Stderr
return chezmoilog.LogCmdOutput(log.Logger, cmd)
var plaintext []byte
if err := withPrivateTempDir(func(tempDir string) error {
ciphertextFilename := filepath.Join(tempDir, "ciphertext"+e.EncryptedSuffix())
if err := os.WriteFile(ciphertextFilename, ciphertext, 0o600); err != nil {
return err
}
plaintextFilename := filepath.Join(tempDir, "plaintext")

args := e.decryptArgs(plaintextFilename, ciphertextFilename)
if err := e.run(args); err != nil {
return err
}

var err error
plaintext, err = os.ReadFile(plaintextFilename)
return err
}); err != nil {
return nil, err
}
return plaintext, nil
}

// DecryptToFile implements Encryption.DecryptToFile.
func (e *GPGEncryption) DecryptToFile(plaintextFilename string, ciphertext []byte) error {
args := append([]string{
"--decrypt",
"--output", plaintextFilename,
"--yes",
}, e.Args...)
//nolint:gosec
cmd := exec.Command(e.Command, args...)
cmd.Stdin = bytes.NewReader(ciphertext)
cmd.Stderr = os.Stderr
return chezmoilog.LogCmdRun(log.Logger, cmd)
return withPrivateTempDir(func(tempDir string) error {
ciphertextFilename := filepath.Join(tempDir, "ciphertext"+e.EncryptedSuffix())
if err := os.WriteFile(ciphertextFilename, ciphertext, 0o600); err != nil {
return err
}
args := e.decryptArgs(plaintextFilename, ciphertextFilename)
return e.run(args)
})
}

// Encrypt implements Encryption.Encrypt.
func (e *GPGEncryption) Encrypt(plaintext []byte) ([]byte, error) {
args := append(e.encryptArgs(), e.Args...)
//nolint:gosec
cmd := exec.Command(e.Command, args...)
cmd.Stdin = bytes.NewReader(plaintext)
cmd.Stderr = os.Stderr
return chezmoilog.LogCmdOutput(log.Logger, cmd)
var ciphertext []byte
if err := withPrivateTempDir(func(tempDir string) error {
plaintextFilename := filepath.Join(tempDir, "plaintext")
if err := os.WriteFile(plaintextFilename, plaintext, 0o600); err != nil {
return err
}
ciphertextFilename := filepath.Join(tempDir, "ciphertext"+e.EncryptedSuffix())

args := e.encryptArgs(plaintextFilename, ciphertextFilename)
if err := e.run(args); err != nil {
return err
}

var err error
ciphertext, err = os.ReadFile(ciphertextFilename)
return err
}); err != nil {
return nil, err
}
return ciphertext, nil
}

// EncryptFile implements Encryption.EncryptFile.
func (e *GPGEncryption) EncryptFile(plaintextFilename string) (ciphertext []byte, err error) {
f, err := os.Open(plaintextFilename)
if err != nil {
func (e *GPGEncryption) EncryptFile(plaintextFilename string) ([]byte, error) {
var ciphertext []byte
if err := withPrivateTempDir(func(tempDir string) error {
ciphertextFilename := filepath.Join(tempDir, "ciphertext"+e.EncryptedSuffix())

args := e.encryptArgs(plaintextFilename, ciphertextFilename)
if err := e.run(args); err != nil {
return err
}

var err error
ciphertext, err = os.ReadFile(ciphertextFilename)
return err
}); err != nil {
return nil, err
}
defer f.Close()
args := append(e.encryptArgs(), e.Args...)
//nolint:gosec
cmd := exec.Command(e.Command, args...)
cmd.Stdin = f
cmd.Stderr = os.Stderr
return chezmoilog.LogCmdOutput(log.Logger, cmd)
return ciphertext, nil
}

// EncryptedSuffix implements Encryption.EncryptedSuffix.
func (e *GPGEncryption) EncryptedSuffix() string {
return e.Suffix
}

func (e *GPGEncryption) encryptArgs() []string {
func (e *GPGEncryption) decryptArgs(plaintextFilename, ciphertextFilename string) []string {
args := []string{"--output", plaintextFilename}
args = append(args, e.Args...)
args = append(args, "--decrypt", ciphertextFilename)
return args
}

func (e *GPGEncryption) encryptArgs(plaintextFilename, ciphertextFilename string) []string {
args := []string{
"--armor",
"--output", ciphertextFilename,
}
if e.Symmetric {
args = append(args, "--symmetric")
} else {
} else if e.Recipient != "" {
args = append(args, "--recipient", e.Recipient)
}
args = append(args, e.Args...)
if !e.Symmetric {
args = append(args, "--encrypt")
if e.Recipient != "" {
args = append(args, "--recipient", e.Recipient)
}
}
args = append(args, plaintextFilename)
return args
}

func (e *GPGEncryption) run(args []string) error {
//nolint:gosec
cmd := exec.Command(e.Command, args...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return chezmoilog.LogCmdRun(log.Logger, cmd)
}

// withPrivateTempDir creates a private temporary and calls f.
func withPrivateTempDir(f func(tempDir string) error) error {
tempDir, err := os.MkdirTemp("", "chezmoi-encryption")
if err != nil {
return err
}
defer os.RemoveAll(tempDir)
if runtime.GOOS != "windows" {
if err := os.Chmod(tempDir, 0o700); err != nil {
return err
}
}

return f(tempDir)
}