Update to github.com/mtrmac/gpgme v0.1.2

This fixes CVE-2020-8945 by incorporating proglottis/gpgme#23 .

Other changes included by the rebase:
- Support for gpgme_off_t (~no-op on Linux)
- Wrapping a few more GPGME functions (irrelevant if we don't call them)

Given how invasive the CVE fix is (affecting basically all binding
code), it seems safer to just update the package (and be verifiably
equivalent with upstream) than to backport and try to back out the few
other changes.

Performed by
$ go get github.com/mtrmac/gpgme@v0.1.2
$ make vendor
and manually backing out unrelated deletions of files (which Go 1.13 does
but Go 1.11, used for testing the old version, does not).

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
This commit is contained in:
Miloslav Trmač 2020-02-20 19:51:27 +01:00
parent e92e288c16
commit c48714e522
12 changed files with 432 additions and 78 deletions

1
go.mod
View File

@ -9,6 +9,7 @@ require (
github.com/docker/docker v0.0.0-20180522102801-da99009bbb11 github.com/docker/docker v0.0.0-20180522102801-da99009bbb11
github.com/dsnet/compress v0.0.1 // indirect github.com/dsnet/compress v0.0.1 // indirect
github.com/go-check/check v0.0.0-20180628173108-788fd7840127 github.com/go-check/check v0.0.0-20180628173108-788fd7840127
github.com/mtrmac/gpgme v0.1.2 // indirect
github.com/opencontainers/go-digest v1.0.0-rc1 github.com/opencontainers/go-digest v1.0.0-rc1
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6
github.com/opencontainers/image-tools v0.0.0-20170926011501-6d941547fa1d github.com/opencontainers/image-tools v0.0.0-20170926011501-6d941547fa1d

2
go.sum
View File

@ -91,6 +91,8 @@ github.com/mistifyio/go-zfs v2.1.1+incompatible h1:gAMO1HM9xBRONLHHYnu5iFsOJUiJd
github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c h1:xa+eQWKuJ9MbB9FBL/eoNvDFvveAkz2LQoz8PzX7Q/4= github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c h1:xa+eQWKuJ9MbB9FBL/eoNvDFvveAkz2LQoz8PzX7Q/4=
github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c/go.mod h1:GhAqVMEWnTcW2dxoD/SO3n2enrgWl3y6Dnx4m59GvcA= github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c/go.mod h1:GhAqVMEWnTcW2dxoD/SO3n2enrgWl3y6Dnx4m59GvcA=
github.com/mtrmac/gpgme v0.1.2 h1:dNOmvYmsrakgW7LcgiprD0yfRuQQe8/C8F6Z+zogO3s=
github.com/mtrmac/gpgme v0.1.2/go.mod h1:GYYHnGSuS7HK3zVS2n3y73y0okK/BeKzwnn5jgiVFNI=
github.com/mtrmac/image/v4 v4.0.0-20191002203927-a64d9d2717f4 h1:AE5cilZfrGtAgMg5Ed4c2Y2KczlOsMVZAK055sSq+gc= github.com/mtrmac/image/v4 v4.0.0-20191002203927-a64d9d2717f4 h1:AE5cilZfrGtAgMg5Ed4c2Y2KczlOsMVZAK055sSq+gc=
github.com/mtrmac/image/v4 v4.0.0-20191002203927-a64d9d2717f4/go.mod h1:0ASJH1YgJiX/eqFZObqepgsvIA4XjCgpyfwn9pDGafA= github.com/mtrmac/image/v4 v4.0.0-20191002203927-a64d9d2717f4/go.mod h1:0ASJH1YgJiX/eqFZObqepgsvIA4XjCgpyfwn9pDGafA=
github.com/mtrmac/image/v4 v4.0.0-20191003181245-f4c983e93262 h1:HMUEnWU3OPT09JRFQLn8VTp3GfdfiEhDMAEhkdX8QnA= github.com/mtrmac/image/v4 v4.0.0-20191003181245-f4c983e93262 h1:HMUEnWU3OPT09JRFQLn8VTp3GfdfiEhDMAEhkdX8QnA=

40
vendor/github.com/mtrmac/gpgme/.appveyor.yml generated vendored Normal file
View File

@ -0,0 +1,40 @@
---
version: 0.{build}
platform: x86
branches:
only:
- master
clone_folder: c:\gopath\src\github.com\proglottis\gpgme
environment:
GOPATH: c:\gopath
GOROOT: C:\go-x86
CGO_LDFLAGS: -LC:\gpg\lib
CGO_CFLAGS: -IC:\gpg\include
GPG_DIR: C:\gpg
install:
- nuget install 7ZipCLI -ExcludeVersion
- set PATH=%appveyor_build_folder%\7ZipCLI\tools;%PATH%
- appveyor DownloadFile https://www.gnupg.org/ftp/gcrypt/binary/gnupg-w32-2.1.20_20170403.exe -FileName gnupg-w32-2.1.20_20170403.exe
- 7z x -o%GPG_DIR% gnupg-w32-2.1.20_20170403.exe
- copy "%GPG_DIR%\lib\libgpg-error.imp" "%GPG_DIR%\lib\libgpg-error.a"
- copy "%GPG_DIR%\lib\libassuan.imp" "%GPG_DIR%\lib\libassuan.a"
- copy "%GPG_DIR%\lib\libgpgme.imp" "%GPG_DIR%\lib\libgpgme.a"
- set PATH=%GOPATH%\bin;%GOROOT%\bin;%GPG_DIR%\bin;C:\MinGW\bin;%PATH%
- C:\cygwin\bin\sed -i 's/"GPG_AGENT_INFO"/"GPG_AGENT_INFO="/;s/C.unsetenv(v)/C.putenv(v)/' %APPVEYOR_BUILD_FOLDER%\gpgme.go
test_script:
- go test -v github.com/proglottis/gpgme
build_script:
- go build -o example_decrypt.exe -i %APPVEYOR_BUILD_FOLDER%\examples\decrypt.go
- go build -o example_encrypt.exe -i %APPVEYOR_BUILD_FOLDER%\examples\encrypt.go
artifacts:
- path: example_decrypt.exe
name: decrypt example binary
- path: example_encrypt.exe
name: encrypt example binary

32
vendor/github.com/mtrmac/gpgme/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,32 @@
---
language: go
os:
- linux
- osx
- windows
dist: xenial
sudo: false
go:
- 1.11
- 1.12
- 1.13
addons:
apt:
packages:
- libgpgme11-dev
homebrew:
packages:
- gnupg
- gnupg@1.4
- gpgme
update: true
matrix:
allow_failures:
- os: windows
before_install:
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then choco install msys2; fi
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then choco install gpg4win; fi

View File

@ -50,25 +50,25 @@ func gogpgme_writefunc(handle, buffer unsafe.Pointer, size C.size_t) C.ssize_t {
} }
//export gogpgme_seekfunc //export gogpgme_seekfunc
func gogpgme_seekfunc(handle unsafe.Pointer, offset C.off_t, whence C.int) C.off_t { func gogpgme_seekfunc(handle unsafe.Pointer, offset C.gpgme_off_t, whence C.int) C.gpgme_off_t {
d := callbackLookup(uintptr(handle)).(*Data) d := callbackLookup(uintptr(handle)).(*Data)
n, err := d.s.Seek(int64(offset), int(whence)) n, err := d.s.Seek(int64(offset), int(whence))
if err != nil { if err != nil {
C.gpgme_err_set_errno(C.EIO) C.gpgme_err_set_errno(C.EIO)
return -1 return -1
} }
return C.off_t(n) return C.gpgme_off_t(n)
} }
// The Data buffer used to communicate with GPGME // The Data buffer used to communicate with GPGME
type Data struct { type Data struct {
dh C.gpgme_data_t dh C.gpgme_data_t // WARNING: Call runtime.KeepAlive(d) after ANY passing of d.dh to C
buf []byte buf []byte
cbs C.struct_gpgme_data_cbs cbs C.struct_gpgme_data_cbs
r io.Reader r io.Reader
w io.Writer w io.Writer
s io.Seeker s io.Seeker
cbc uintptr cbc uintptr // WARNING: Call runtime.KeepAlive(d) after ANY use of d.cbc in C (typically via d.dh)
} }
func newData() *Data { func newData() *Data {
@ -154,12 +154,14 @@ func (d *Data) Close() error {
callbackDelete(d.cbc) callbackDelete(d.cbc)
} }
_, err := C.gpgme_data_release(d.dh) _, err := C.gpgme_data_release(d.dh)
runtime.KeepAlive(d)
d.dh = nil d.dh = nil
return err return err
} }
func (d *Data) Write(p []byte) (int, error) { func (d *Data) Write(p []byte) (int, error) {
n, err := C.gpgme_data_write(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p))) n, err := C.gpgme_data_write(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p)))
runtime.KeepAlive(d)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -171,6 +173,7 @@ func (d *Data) Write(p []byte) (int, error) {
func (d *Data) Read(p []byte) (int, error) { func (d *Data) Read(p []byte) (int, error) {
n, err := C.gpgme_data_read(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p))) n, err := C.gpgme_data_read(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p)))
runtime.KeepAlive(d)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -181,11 +184,14 @@ func (d *Data) Read(p []byte) (int, error) {
} }
func (d *Data) Seek(offset int64, whence int) (int64, error) { func (d *Data) Seek(offset int64, whence int) (int64, error) {
n, err := C.gpgme_data_seek(d.dh, C.off_t(offset), C.int(whence)) n, err := C.gogpgme_data_seek(d.dh, C.gpgme_off_t(offset), C.int(whence))
runtime.KeepAlive(d)
return int64(n), err return int64(n), err
} }
// Name returns the associated filename if any // Name returns the associated filename if any
func (d *Data) Name() string { func (d *Data) Name() string {
return C.GoString(C.gpgme_data_get_file_name(d.dh)) res := C.GoString(C.gpgme_data_get_file_name(d.dh))
runtime.KeepAlive(d)
return res
} }

3
vendor/github.com/mtrmac/gpgme/go.mod generated vendored Normal file
View File

@ -0,0 +1,3 @@
module github.com/mtrmac/gpgme
go 1.11

View File

@ -8,6 +8,28 @@ void gogpgme_set_passphrase_cb(gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, uintpt
gpgme_set_passphrase_cb(ctx, cb, (void *)handle); gpgme_set_passphrase_cb(ctx, cb, (void *)handle);
} }
gpgme_off_t gogpgme_data_seek(gpgme_data_t dh, gpgme_off_t offset, int whence) {
return gpgme_data_seek(dh, offset, whence);
}
gpgme_error_t gogpgme_op_assuan_transact_ext(
gpgme_ctx_t ctx,
char* cmd,
uintptr_t data_h,
uintptr_t inquiry_h,
uintptr_t status_h,
gpgme_error_t *operr
){
return gpgme_op_assuan_transact_ext(
ctx,
cmd,
(gpgme_assuan_data_cb_t) gogpgme_assuan_data_callback, (void *)data_h,
(gpgme_assuan_inquire_cb_t) gogpgme_assuan_inquiry_callback, (void *)inquiry_h,
(gpgme_assuan_status_cb_t) gogpgme_assuan_status_callback, (void *)status_h,
operr
);
}
unsigned int key_revoked(gpgme_key_t k) { unsigned int key_revoked(gpgme_key_t k) {
return k->revoked; return k->revoked;
} }

View File

@ -6,12 +6,24 @@
#include <gpgme.h> #include <gpgme.h>
/* GPGME_VERSION_NUMBER was introduced in 1.4.0 */
#if !defined(GPGME_VERSION_NUMBER) || GPGME_VERSION_NUMBER < 0x010402
typedef off_t gpgme_off_t; /* Introduced in 1.4.2 */
#endif
extern ssize_t gogpgme_readfunc(void *handle, void *buffer, size_t size); extern ssize_t gogpgme_readfunc(void *handle, void *buffer, size_t size);
extern ssize_t gogpgme_writefunc(void *handle, void *buffer, size_t size); extern ssize_t gogpgme_writefunc(void *handle, void *buffer, size_t size);
extern off_t gogpgme_seekfunc(void *handle, off_t offset, int whence); extern off_t gogpgme_seekfunc(void *handle, off_t offset, int whence);
extern gpgme_error_t gogpgme_passfunc(void *hook, char *uid_hint, char *passphrase_info, int prev_was_bad, int fd); extern gpgme_error_t gogpgme_passfunc(void *hook, char *uid_hint, char *passphrase_info, int prev_was_bad, int fd);
extern gpgme_error_t gogpgme_data_new_from_cbs(gpgme_data_t *dh, gpgme_data_cbs_t cbs, uintptr_t handle); extern gpgme_error_t gogpgme_data_new_from_cbs(gpgme_data_t *dh, gpgme_data_cbs_t cbs, uintptr_t handle);
extern void gogpgme_set_passphrase_cb(gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, uintptr_t handle); extern void gogpgme_set_passphrase_cb(gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, uintptr_t handle);
extern gpgme_off_t gogpgme_data_seek(gpgme_data_t dh, gpgme_off_t offset, int whence);
extern gpgme_error_t gogpgme_op_assuan_transact_ext(gpgme_ctx_t ctx, char *cmd, uintptr_t data_h, uintptr_t inquiry_h , uintptr_t status_h, gpgme_error_t *operr);
extern gpgme_error_t gogpgme_assuan_data_callback(void *opaque, void* data, size_t datalen );
extern gpgme_error_t gogpgme_assuan_inquiry_callback(void *opaque, char* name, char* args);
extern gpgme_error_t gogpgme_assuan_status_callback(void *opaque, char* status, char* args);
extern unsigned int key_revoked(gpgme_key_t k); extern unsigned int key_revoked(gpgme_key_t k);
extern unsigned int key_expired(gpgme_key_t k); extern unsigned int key_expired(gpgme_key_t k);

View File

@ -7,7 +7,6 @@ package gpgme
// #include <gpgme.h> // #include <gpgme.h>
// #include "go_gpgme.h" // #include "go_gpgme.h"
import "C" import "C"
import ( import (
"fmt" "fmt"
"io" "io"
@ -48,9 +47,8 @@ const (
ProtocolAssuan Protocol = C.GPGME_PROTOCOL_ASSUAN ProtocolAssuan Protocol = C.GPGME_PROTOCOL_ASSUAN
ProtocolG13 Protocol = C.GPGME_PROTOCOL_G13 ProtocolG13 Protocol = C.GPGME_PROTOCOL_G13
ProtocolUIServer Protocol = C.GPGME_PROTOCOL_UISERVER ProtocolUIServer Protocol = C.GPGME_PROTOCOL_UISERVER
// ProtocolSpawn Protocol = C.GPGME_PROTOCOL_SPAWN // Unavailable in 1.4.3 ProtocolDefault Protocol = C.GPGME_PROTOCOL_DEFAULT
ProtocolDefault Protocol = C.GPGME_PROTOCOL_DEFAULT ProtocolUnknown Protocol = C.GPGME_PROTOCOL_UNKNOWN
ProtocolUnknown Protocol = C.GPGME_PROTOCOL_UNKNOWN
) )
type PinEntryMode int type PinEntryMode int
@ -70,7 +68,6 @@ const (
EncryptNoEncryptTo EncryptFlag = C.GPGME_ENCRYPT_NO_ENCRYPT_TO EncryptNoEncryptTo EncryptFlag = C.GPGME_ENCRYPT_NO_ENCRYPT_TO
EncryptPrepare EncryptFlag = C.GPGME_ENCRYPT_PREPARE EncryptPrepare EncryptFlag = C.GPGME_ENCRYPT_PREPARE
EncryptExceptSign EncryptFlag = C.GPGME_ENCRYPT_EXPECT_SIGN EncryptExceptSign EncryptFlag = C.GPGME_ENCRYPT_EXPECT_SIGN
// EncryptNoCompress EncryptFlag = C.GPGME_ENCRYPT_NO_COMPRESS // Unavailable in 1.4.3
) )
type HashAlgo int type HashAlgo int
@ -84,7 +81,6 @@ const (
KeyListModeExtern KeyListMode = C.GPGME_KEYLIST_MODE_EXTERN KeyListModeExtern KeyListMode = C.GPGME_KEYLIST_MODE_EXTERN
KeyListModeSigs KeyListMode = C.GPGME_KEYLIST_MODE_SIGS KeyListModeSigs KeyListMode = C.GPGME_KEYLIST_MODE_SIGS
KeyListModeSigNotations KeyListMode = C.GPGME_KEYLIST_MODE_SIG_NOTATIONS KeyListModeSigNotations KeyListMode = C.GPGME_KEYLIST_MODE_SIG_NOTATIONS
// KeyListModeWithSecret KeyListMode = C.GPGME_KEYLIST_MODE_WITH_SECRET // Unavailable in 1.4.3
KeyListModeEphemeral KeyListMode = C.GPGME_KEYLIST_MODE_EPHEMERAL KeyListModeEphemeral KeyListMode = C.GPGME_KEYLIST_MODE_EPHEMERAL
KeyListModeModeValidate KeyListMode = C.GPGME_KEYLIST_MODE_VALIDATE KeyListModeModeValidate KeyListMode = C.GPGME_KEYLIST_MODE_VALIDATE
) )
@ -168,39 +164,60 @@ func EngineCheckVersion(p Protocol) error {
} }
type EngineInfo struct { type EngineInfo struct {
info C.gpgme_engine_info_t next *EngineInfo
protocol Protocol
fileName string
homeDir string
version string
requiredVersion string
}
func copyEngineInfo(info C.gpgme_engine_info_t) *EngineInfo {
res := &EngineInfo{
next: nil,
protocol: Protocol(info.protocol),
fileName: C.GoString(info.file_name),
homeDir: C.GoString(info.home_dir),
version: C.GoString(info.version),
requiredVersion: C.GoString(info.req_version),
}
if info.next != nil {
res.next = copyEngineInfo(info.next)
}
return res
} }
func (e *EngineInfo) Next() *EngineInfo { func (e *EngineInfo) Next() *EngineInfo {
if e.info.next == nil { return e.next
return nil
}
return &EngineInfo{info: e.info.next}
} }
func (e *EngineInfo) Protocol() Protocol { func (e *EngineInfo) Protocol() Protocol {
return Protocol(e.info.protocol) return e.protocol
} }
func (e *EngineInfo) FileName() string { func (e *EngineInfo) FileName() string {
return C.GoString(e.info.file_name) return e.fileName
} }
func (e *EngineInfo) Version() string { func (e *EngineInfo) Version() string {
return C.GoString(e.info.version) return e.version
} }
func (e *EngineInfo) RequiredVersion() string { func (e *EngineInfo) RequiredVersion() string {
return C.GoString(e.info.req_version) return e.requiredVersion
} }
func (e *EngineInfo) HomeDir() string { func (e *EngineInfo) HomeDir() string {
return C.GoString(e.info.home_dir) return e.homeDir
} }
func GetEngineInfo() (*EngineInfo, error) { func GetEngineInfo() (*EngineInfo, error) {
info := &EngineInfo{} var cInfo C.gpgme_engine_info_t
return info, handleError(C.gpgme_get_engine_info(&info.info)) err := handleError(C.gpgme_get_engine_info(&cInfo))
if err != nil {
return nil, err
}
return copyEngineInfo(cInfo), nil // It is up to the caller not to invalidate cInfo concurrently until this is done.
} }
func SetEngineInfo(proto Protocol, fileName, homeDir string) error { func SetEngineInfo(proto Protocol, fileName, homeDir string) error {
@ -261,9 +278,9 @@ type Context struct {
KeyError error KeyError error
callback Callback callback Callback
cbc uintptr cbc uintptr // WARNING: Call runtime.KeepAlive(c) after ANY use of c.cbc in C (typically via c.ctx)
ctx C.gpgme_ctx_t ctx C.gpgme_ctx_t // WARNING: Call runtime.KeepAlive(c) after ANY passing of c.ctx to C
} }
func New() (*Context, error) { func New() (*Context, error) {
@ -281,49 +298,68 @@ func (c *Context) Release() {
callbackDelete(c.cbc) callbackDelete(c.cbc)
} }
C.gpgme_release(c.ctx) C.gpgme_release(c.ctx)
runtime.KeepAlive(c)
c.ctx = nil c.ctx = nil
} }
func (c *Context) SetArmor(yes bool) { func (c *Context) SetArmor(yes bool) {
C.gpgme_set_armor(c.ctx, cbool(yes)) C.gpgme_set_armor(c.ctx, cbool(yes))
runtime.KeepAlive(c)
} }
func (c *Context) Armor() bool { func (c *Context) Armor() bool {
return C.gpgme_get_armor(c.ctx) != 0 res := C.gpgme_get_armor(c.ctx) != 0
runtime.KeepAlive(c)
return res
} }
func (c *Context) SetTextMode(yes bool) { func (c *Context) SetTextMode(yes bool) {
C.gpgme_set_textmode(c.ctx, cbool(yes)) C.gpgme_set_textmode(c.ctx, cbool(yes))
runtime.KeepAlive(c)
} }
func (c *Context) TextMode() bool { func (c *Context) TextMode() bool {
return C.gpgme_get_textmode(c.ctx) != 0 res := C.gpgme_get_textmode(c.ctx) != 0
runtime.KeepAlive(c)
return res
} }
func (c *Context) SetProtocol(p Protocol) error { func (c *Context) SetProtocol(p Protocol) error {
return handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p))) err := handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p)))
runtime.KeepAlive(c)
return err
} }
func (c *Context) Protocol() Protocol { func (c *Context) Protocol() Protocol {
return Protocol(C.gpgme_get_protocol(c.ctx)) res := Protocol(C.gpgme_get_protocol(c.ctx))
runtime.KeepAlive(c)
return res
} }
func (c *Context) SetKeyListMode(m KeyListMode) error { func (c *Context) SetKeyListMode(m KeyListMode) error {
return handleError(C.gpgme_set_keylist_mode(c.ctx, C.gpgme_keylist_mode_t(m))) err := handleError(C.gpgme_set_keylist_mode(c.ctx, C.gpgme_keylist_mode_t(m)))
runtime.KeepAlive(c)
return err
} }
func (c *Context) KeyListMode() KeyListMode { func (c *Context) KeyListMode() KeyListMode {
return KeyListMode(C.gpgme_get_keylist_mode(c.ctx)) res := KeyListMode(C.gpgme_get_keylist_mode(c.ctx))
runtime.KeepAlive(c)
return res
} }
// Unavailable in 1.3.2: // Unavailable in 1.3.2:
// func (c *Context) SetPinEntryMode(m PinEntryMode) error { // func (c *Context) SetPinEntryMode(m PinEntryMode) error {
// return handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m))) // err := handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m)))
// runtime.KeepAlive(c)
// return err
// } // }
// Unavailable in 1.3.2: // Unavailable in 1.3.2:
// func (c *Context) PinEntryMode() PinEntryMode { // func (c *Context) PinEntryMode() PinEntryMode {
// return PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx)) // res := PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx))
// runtime.KeepAlive(c)
// return res
// } // }
func (c *Context) SetCallback(callback Callback) error { func (c *Context) SetCallback(callback Callback) error {
@ -340,11 +376,17 @@ func (c *Context) SetCallback(callback Callback) error {
c.cbc = 0 c.cbc = 0
_, err = C.gogpgme_set_passphrase_cb(c.ctx, nil, 0) _, err = C.gogpgme_set_passphrase_cb(c.ctx, nil, 0)
} }
runtime.KeepAlive(c)
return err return err
} }
func (c *Context) EngineInfo() *EngineInfo { func (c *Context) EngineInfo() *EngineInfo {
return &EngineInfo{info: C.gpgme_ctx_get_engine_info(c.ctx)} cInfo := C.gpgme_ctx_get_engine_info(c.ctx)
runtime.KeepAlive(c)
// NOTE: c must be live as long as we are accessing cInfo.
res := copyEngineInfo(cInfo)
runtime.KeepAlive(c) // for accesses to cInfo
return res
} }
func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error { func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error {
@ -357,19 +399,23 @@ func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error
chome = C.CString(homeDir) chome = C.CString(homeDir)
defer C.free(unsafe.Pointer(chome)) defer C.free(unsafe.Pointer(chome))
} }
return handleError(C.gpgme_ctx_set_engine_info(c.ctx, C.gpgme_protocol_t(proto), cfn, chome)) err := handleError(C.gpgme_ctx_set_engine_info(c.ctx, C.gpgme_protocol_t(proto), cfn, chome))
runtime.KeepAlive(c)
return err
} }
func (c *Context) KeyListStart(pattern string, secretOnly bool) error { func (c *Context) KeyListStart(pattern string, secretOnly bool) error {
cpattern := C.CString(pattern) cpattern := C.CString(pattern)
defer C.free(unsafe.Pointer(cpattern)) defer C.free(unsafe.Pointer(cpattern))
err := C.gpgme_op_keylist_start(c.ctx, cpattern, cbool(secretOnly)) err := handleError(C.gpgme_op_keylist_start(c.ctx, cpattern, cbool(secretOnly)))
return handleError(err) runtime.KeepAlive(c)
return err
} }
func (c *Context) KeyListNext() bool { func (c *Context) KeyListNext() bool {
c.Key = newKey() c.Key = newKey()
err := handleError(C.gpgme_op_keylist_next(c.ctx, &c.Key.k)) err := handleError(C.gpgme_op_keylist_next(c.ctx, &c.Key.k))
runtime.KeepAlive(c) // implies runtime.KeepAlive(c.Key)
if err != nil { if err != nil {
if e, ok := err.(Error); ok && e.Code() == ErrorEOF { if e, ok := err.(Error); ok && e.Code() == ErrorEOF {
c.KeyError = nil c.KeyError = nil
@ -383,7 +429,9 @@ func (c *Context) KeyListNext() bool {
} }
func (c *Context) KeyListEnd() error { func (c *Context) KeyListEnd() error {
return handleError(C.gpgme_op_keylist_end(c.ctx)) err := handleError(C.gpgme_op_keylist_end(c.ctx))
runtime.KeepAlive(c)
return err
} }
func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) { func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) {
@ -391,7 +439,11 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) {
cfpr := C.CString(fingerprint) cfpr := C.CString(fingerprint)
defer C.free(unsafe.Pointer(cfpr)) defer C.free(unsafe.Pointer(cfpr))
err := handleError(C.gpgme_get_key(c.ctx, cfpr, &key.k, cbool(secret))) err := handleError(C.gpgme_get_key(c.ctx, cfpr, &key.k, cbool(secret)))
if e, ok := err.(Error); key.k == nil && ok && e.Code() == ErrorEOF { runtime.KeepAlive(c)
runtime.KeepAlive(key)
keyKIsNil := key.k == nil
runtime.KeepAlive(key)
if e, ok := err.(Error); keyKIsNil && ok && e.Code() == ErrorEOF {
return nil, fmt.Errorf("key %q not found", fingerprint) return nil, fmt.Errorf("key %q not found", fingerprint)
} }
if err != nil { if err != nil {
@ -401,11 +453,19 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) {
} }
func (c *Context) Decrypt(ciphertext, plaintext *Data) error { func (c *Context) Decrypt(ciphertext, plaintext *Data) error {
return handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh)) err := handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh))
runtime.KeepAlive(c)
runtime.KeepAlive(ciphertext)
runtime.KeepAlive(plaintext)
return err
} }
func (c *Context) DecryptVerify(ciphertext, plaintext *Data) error { func (c *Context) DecryptVerify(ciphertext, plaintext *Data) error {
return handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh)) err := handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh))
runtime.KeepAlive(c)
runtime.KeepAlive(ciphertext)
runtime.KeepAlive(plaintext)
return err
} }
type Signature struct { type Signature struct {
@ -432,10 +492,20 @@ func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, err
plainPtr = plain.dh plainPtr = plain.dh
} }
err := handleError(C.gpgme_op_verify(c.ctx, sig.dh, signedTextPtr, plainPtr)) err := handleError(C.gpgme_op_verify(c.ctx, sig.dh, signedTextPtr, plainPtr))
runtime.KeepAlive(c)
runtime.KeepAlive(sig)
if signedText != nil {
runtime.KeepAlive(signedText)
}
if plain != nil {
runtime.KeepAlive(plain)
}
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
res := C.gpgme_op_verify_result(c.ctx) res := C.gpgme_op_verify_result(c.ctx)
runtime.KeepAlive(c)
// NOTE: c must be live as long as we are accessing res.
sigs := []Signature{} sigs := []Signature{}
for s := res.signatures; s != nil; s = s.next { for s := res.signatures; s != nil; s = s.next {
sig := Signature{ sig := Signature{
@ -455,7 +525,9 @@ func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, err
} }
sigs = append(sigs, sig) sigs = append(sigs, sig)
} }
return C.GoString(res.file_name), sigs, nil fileName := C.GoString(res.file_name)
runtime.KeepAlive(c) // for all accesses to res above
return fileName, sigs, nil
} }
func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphertext *Data) error { func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphertext *Data) error {
@ -467,18 +539,116 @@ func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphe
*ptr = recipients[i].k *ptr = recipients[i].k
} }
err := C.gpgme_op_encrypt(c.ctx, (*C.gpgme_key_t)(recp), C.gpgme_encrypt_flags_t(flags), plaintext.dh, ciphertext.dh) err := C.gpgme_op_encrypt(c.ctx, (*C.gpgme_key_t)(recp), C.gpgme_encrypt_flags_t(flags), plaintext.dh, ciphertext.dh)
runtime.KeepAlive(c)
runtime.KeepAlive(recipients)
runtime.KeepAlive(plaintext)
runtime.KeepAlive(ciphertext)
return handleError(err) return handleError(err)
} }
func (c *Context) Sign(signers []*Key, plain, sig *Data, mode SigMode) error { func (c *Context) Sign(signers []*Key, plain, sig *Data, mode SigMode) error {
C.gpgme_signers_clear(c.ctx) C.gpgme_signers_clear(c.ctx)
runtime.KeepAlive(c)
for _, k := range signers { for _, k := range signers {
if err := handleError(C.gpgme_signers_add(c.ctx, k.k)); err != nil { err := handleError(C.gpgme_signers_add(c.ctx, k.k))
runtime.KeepAlive(c)
runtime.KeepAlive(k)
if err != nil {
C.gpgme_signers_clear(c.ctx) C.gpgme_signers_clear(c.ctx)
runtime.KeepAlive(c)
return err return err
} }
} }
return handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode))) err := handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode)))
runtime.KeepAlive(c)
runtime.KeepAlive(plain)
runtime.KeepAlive(sig)
return err
}
type AssuanDataCallback func(data []byte) error
type AssuanInquireCallback func(name, args string) error
type AssuanStatusCallback func(status, args string) error
// AssuanSend sends a raw Assuan command to gpg-agent
func (c *Context) AssuanSend(
cmd string,
data AssuanDataCallback,
inquiry AssuanInquireCallback,
status AssuanStatusCallback,
) error {
var operr C.gpgme_error_t
dataPtr := callbackAdd(&data)
inquiryPtr := callbackAdd(&inquiry)
statusPtr := callbackAdd(&status)
cmdCStr := C.CString(cmd)
defer C.free(unsafe.Pointer(cmdCStr))
err := C.gogpgme_op_assuan_transact_ext(
c.ctx,
cmdCStr,
C.uintptr_t(dataPtr),
C.uintptr_t(inquiryPtr),
C.uintptr_t(statusPtr),
&operr,
)
runtime.KeepAlive(c)
if handleError(operr) != nil {
return handleError(operr)
}
return handleError(err)
}
//export gogpgme_assuan_data_callback
func gogpgme_assuan_data_callback(handle unsafe.Pointer, data unsafe.Pointer, datalen C.size_t) C.gpgme_error_t {
c := callbackLookup(uintptr(handle)).(*AssuanDataCallback)
if *c == nil {
return 0
}
(*c)(C.GoBytes(data, C.int(datalen)))
return 0
}
//export gogpgme_assuan_inquiry_callback
func gogpgme_assuan_inquiry_callback(handle unsafe.Pointer, cName *C.char, cArgs *C.char) C.gpgme_error_t {
name := C.GoString(cName)
args := C.GoString(cArgs)
c := callbackLookup(uintptr(handle)).(*AssuanInquireCallback)
if *c == nil {
return 0
}
(*c)(name, args)
return 0
}
//export gogpgme_assuan_status_callback
func gogpgme_assuan_status_callback(handle unsafe.Pointer, cStatus *C.char, cArgs *C.char) C.gpgme_error_t {
status := C.GoString(cStatus)
args := C.GoString(cArgs)
c := callbackLookup(uintptr(handle)).(*AssuanStatusCallback)
if *c == nil {
return 0
}
(*c)(status, args)
return 0
}
// ExportModeFlags defines how keys are exported from Export
type ExportModeFlags uint
const (
ExportModeExtern ExportModeFlags = C.GPGME_EXPORT_MODE_EXTERN
ExportModeMinimal ExportModeFlags = C.GPGME_EXPORT_MODE_MINIMAL
)
func (c *Context) Export(pattern string, mode ExportModeFlags, data *Data) error {
pat := C.CString(pattern)
defer C.free(unsafe.Pointer(pat))
err := handleError(C.gpgme_op_export(c.ctx, pat, C.gpgme_export_mode_t(mode), data.dh))
runtime.KeepAlive(c)
runtime.KeepAlive(data)
return err
} }
// ImportStatusFlags describes the type of ImportStatus.Status. The C API in gpgme.h simply uses "unsigned". // ImportStatusFlags describes the type of ImportStatus.Status. The C API in gpgme.h simply uses "unsigned".
@ -517,10 +687,14 @@ type ImportResult struct {
func (c *Context) Import(keyData *Data) (*ImportResult, error) { func (c *Context) Import(keyData *Data) (*ImportResult, error) {
err := handleError(C.gpgme_op_import(c.ctx, keyData.dh)) err := handleError(C.gpgme_op_import(c.ctx, keyData.dh))
runtime.KeepAlive(c)
runtime.KeepAlive(keyData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
res := C.gpgme_op_import_result(c.ctx) res := C.gpgme_op_import_result(c.ctx)
runtime.KeepAlive(c)
// NOTE: c must be live as long as we are accessing res.
imports := []ImportStatus{} imports := []ImportStatus{}
for s := res.imports; s != nil; s = s.next { for s := res.imports; s != nil; s = s.next {
imports = append(imports, ImportStatus{ imports = append(imports, ImportStatus{
@ -529,7 +703,7 @@ func (c *Context) Import(keyData *Data) (*ImportResult, error) {
Status: ImportStatusFlags(s.status), Status: ImportStatusFlags(s.status),
}) })
} }
return &ImportResult{ importResult := &ImportResult{
Considered: int(res.considered), Considered: int(res.considered),
NoUserID: int(res.no_user_id), NoUserID: int(res.no_user_id),
Imported: int(res.imported), Imported: int(res.imported),
@ -544,11 +718,13 @@ func (c *Context) Import(keyData *Data) (*ImportResult, error) {
SecretUnchanged: int(res.secret_unchanged), SecretUnchanged: int(res.secret_unchanged),
NotImported: int(res.not_imported), NotImported: int(res.not_imported),
Imports: imports, Imports: imports,
}, nil }
runtime.KeepAlive(c) // for all accesses to res above
return importResult, nil
} }
type Key struct { type Key struct {
k C.gpgme_key_t k C.gpgme_key_t // WARNING: Call Runtime.KeepAlive(k) after ANY passing of k.k to C
} }
func newKey() *Key { func newKey() *Key {
@ -559,85 +735,122 @@ func newKey() *Key {
func (k *Key) Release() { func (k *Key) Release() {
C.gpgme_key_release(k.k) C.gpgme_key_release(k.k)
runtime.KeepAlive(k)
k.k = nil k.k = nil
} }
func (k *Key) Revoked() bool { func (k *Key) Revoked() bool {
return C.key_revoked(k.k) != 0 res := C.key_revoked(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) Expired() bool { func (k *Key) Expired() bool {
return C.key_expired(k.k) != 0 res := C.key_expired(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) Disabled() bool { func (k *Key) Disabled() bool {
return C.key_disabled(k.k) != 0 res := C.key_disabled(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) Invalid() bool { func (k *Key) Invalid() bool {
return C.key_invalid(k.k) != 0 res := C.key_invalid(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) CanEncrypt() bool { func (k *Key) CanEncrypt() bool {
return C.key_can_encrypt(k.k) != 0 res := C.key_can_encrypt(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) CanSign() bool { func (k *Key) CanSign() bool {
return C.key_can_sign(k.k) != 0 res := C.key_can_sign(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) CanCertify() bool { func (k *Key) CanCertify() bool {
return C.key_can_certify(k.k) != 0 res := C.key_can_certify(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) Secret() bool { func (k *Key) Secret() bool {
return C.key_secret(k.k) != 0 res := C.key_secret(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) CanAuthenticate() bool { func (k *Key) CanAuthenticate() bool {
return C.key_can_authenticate(k.k) != 0 res := C.key_can_authenticate(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) IsQualified() bool { func (k *Key) IsQualified() bool {
return C.key_is_qualified(k.k) != 0 res := C.key_is_qualified(k.k) != 0
runtime.KeepAlive(k)
return res
} }
func (k *Key) Protocol() Protocol { func (k *Key) Protocol() Protocol {
return Protocol(k.k.protocol) res := Protocol(k.k.protocol)
runtime.KeepAlive(k)
return res
} }
func (k *Key) IssuerSerial() string { func (k *Key) IssuerSerial() string {
return C.GoString(k.k.issuer_serial) res := C.GoString(k.k.issuer_serial)
runtime.KeepAlive(k)
return res
} }
func (k *Key) IssuerName() string { func (k *Key) IssuerName() string {
return C.GoString(k.k.issuer_name) res := C.GoString(k.k.issuer_name)
runtime.KeepAlive(k)
return res
} }
func (k *Key) ChainID() string { func (k *Key) ChainID() string {
return C.GoString(k.k.chain_id) res := C.GoString(k.k.chain_id)
runtime.KeepAlive(k)
return res
} }
func (k *Key) OwnerTrust() Validity { func (k *Key) OwnerTrust() Validity {
return Validity(k.k.owner_trust) res := Validity(k.k.owner_trust)
runtime.KeepAlive(k)
return res
} }
func (k *Key) SubKeys() *SubKey { func (k *Key) SubKeys() *SubKey {
if k.k.subkeys == nil { subKeys := k.k.subkeys
runtime.KeepAlive(k)
if subKeys == nil {
return nil return nil
} }
return &SubKey{k: k.k.subkeys, parent: k} return &SubKey{k: subKeys, parent: k} // The parent: k reference ensures subKeys remains valid
} }
func (k *Key) UserIDs() *UserID { func (k *Key) UserIDs() *UserID {
if k.k.uids == nil { uids := k.k.uids
runtime.KeepAlive(k)
if uids == nil {
return nil return nil
} }
return &UserID{u: k.k.uids, parent: k} return &UserID{u: uids, parent: k} // The parent: k reference ensures uids remains valid
} }
func (k *Key) KeyListMode() KeyListMode { func (k *Key) KeyListMode() KeyListMode {
return KeyListMode(k.k.keylist_mode) res := KeyListMode(k.k.keylist_mode)
runtime.KeepAlive(k)
return res
} }
type SubKey struct { type SubKey struct {
@ -737,12 +950,3 @@ func (u *UserID) Comment() string {
func (u *UserID) Email() string { func (u *UserID) Email() string {
return C.GoString(u.u.email) return C.GoString(u.u.email)
} }
// This is somewhat of a horrible hack. We need to unset GPG_AGENT_INFO so that gpgme does not pass --use-agent to GPG.
// os.Unsetenv should be enough, but that only calls the underlying C library (which gpgme uses) if cgo is involved
// - and cgo can't be used in tests. So, provide this helper for test initialization.
func unsetenvGPGAgentInfo() {
v := C.CString("GPG_AGENT_INFO")
defer C.free(unsafe.Pointer(v))
C.unsetenv(v)
}

18
vendor/github.com/mtrmac/gpgme/unset_agent_info.go generated vendored Normal file
View File

@ -0,0 +1,18 @@
// +build !windows
package gpgme
// #include <stdlib.h>
import "C"
import (
"unsafe"
)
// This is somewhat of a horrible hack. We need to unset GPG_AGENT_INFO so that gpgme does not pass --use-agent to GPG.
// os.Unsetenv should be enough, but that only calls the underlying C library (which gpgme uses) if cgo is involved
// - and cgo can't be used in tests. So, provide this helper for test initialization.
func unsetenvGPGAgentInfo() {
v := C.CString("GPG_AGENT_INFO")
defer C.free(unsafe.Pointer(v))
C.unsetenv(v)
}

View File

@ -0,0 +1,14 @@
package gpgme
// #include <stdlib.h>
import "C"
import (
"unsafe"
)
// unsetenv is not available in mingw
func unsetenvGPGAgentInfo() {
v := C.CString("GPG_AGENT_INFO=")
defer C.free(unsafe.Pointer(v))
C.putenv(v)
}

2
vendor/modules.txt vendored
View File

@ -189,7 +189,7 @@ github.com/mattn/go-isatty
github.com/mattn/go-shellwords github.com/mattn/go-shellwords
# github.com/mistifyio/go-zfs v2.1.1+incompatible # github.com/mistifyio/go-zfs v2.1.1+incompatible
github.com/mistifyio/go-zfs github.com/mistifyio/go-zfs
# github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c # github.com/mtrmac/gpgme v0.1.2
github.com/mtrmac/gpgme github.com/mtrmac/gpgme
# github.com/opencontainers/go-digest v1.0.0-rc1 # github.com/opencontainers/go-digest v1.0.0-rc1
github.com/opencontainers/go-digest github.com/opencontainers/go-digest