mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
cmd/tailscale/cli, util/winutil/authenticode: flesh out authenticode support
Previously, tailscale upgrade was doing the bare minimum for checking authenticode signatures via `WinVerifyTrustEx`. This is fine, but we can do better: * WinVerifyTrustEx verifies that the binary's signature is valid, but it doesn't determine *whose* signature is valid; tailscale upgrade should also ensure that the binary is actually signed *by us*. * I added the ability to check the signatures of MSI files. * In future PRs I will be adding diagnostic logging that lists details about every module (ie, DLL) loaded into our process. As part of that metadata, I want to be able to extract information about who signed the binaries. This code is modelled on some C++ I wrote for Firefox back in the day. See https://searchfox.org/mozilla-central/rev/27e4816536c891d85d63695025f2549fd7976392/toolkit/xre/dllservices/mozglue/Authenticode.cpp for reference. Fixes #8284 Signed-off-by: Aaron Klotz <aaron@tailscale.com>
This commit is contained in:
parent
ec9213a627
commit
7adf15f90e
@ -6,33 +6,15 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"unsafe"
|
"tailscale.com/util/winutil/authenticode"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
verifyAuthenticode = verifyAuthenticodeWindows
|
verifyAuthenticode = verifyTailscale
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyAuthenticodeWindows(path string) error {
|
const certSubjectTailscale = "Tailscale Inc."
|
||||||
path16, err := windows.UTF16PtrFromString(path)
|
|
||||||
if err != nil {
|
func verifyTailscale(path string) error {
|
||||||
return err
|
return authenticode.Verify(path, certSubjectTailscale)
|
||||||
}
|
|
||||||
data := &windows.WinTrustData{
|
|
||||||
Size: uint32(unsafe.Sizeof(windows.WinTrustData{})),
|
|
||||||
UIChoice: windows.WTD_UI_NONE,
|
|
||||||
RevocationChecks: windows.WTD_REVOKE_WHOLECHAIN, // Full revocation checking, as this is called with network connectivity.
|
|
||||||
UnionChoice: windows.WTD_CHOICE_FILE,
|
|
||||||
StateAction: windows.WTD_STATEACTION_VERIFY,
|
|
||||||
FileOrCatalogOrBlobOrSgnrOrCert: unsafe.Pointer(&windows.WinTrustFileInfo{
|
|
||||||
Size: uint32(unsafe.Sizeof(windows.WinTrustFileInfo{})),
|
|
||||||
FilePath: path16,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
err = windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
|
|
||||||
data.StateAction = windows.WTD_STATEACTION_CLOSE
|
|
||||||
windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
|
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
|
||||||
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
|
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
|
||||||
L github.com/coreos/go-iptables/iptables from tailscale.com/util/linuxfw
|
L github.com/coreos/go-iptables/iptables from tailscale.com/util/linuxfw
|
||||||
|
W 💣 github.com/dblohm7/wingoes from tailscale.com/util/winutil/authenticode
|
||||||
|
W 💣 github.com/dblohm7/wingoes/pe from tailscale.com/util/winutil/authenticode
|
||||||
github.com/fxamacker/cbor/v2 from tailscale.com/tka
|
github.com/fxamacker/cbor/v2 from tailscale.com/tka
|
||||||
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
|
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
|
||||||
L github.com/google/nftables from tailscale.com/util/linuxfw
|
L github.com/google/nftables from tailscale.com/util/linuxfw
|
||||||
@ -66,7 +68,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
tailscale.com/atomicfile from tailscale.com/ipn+
|
tailscale.com/atomicfile from tailscale.com/ipn+
|
||||||
tailscale.com/client/tailscale from tailscale.com/cmd/tailscale/cli+
|
tailscale.com/client/tailscale from tailscale.com/cmd/tailscale/cli+
|
||||||
tailscale.com/client/tailscale/apitype from tailscale.com/cmd/tailscale/cli+
|
tailscale.com/client/tailscale/apitype from tailscale.com/cmd/tailscale/cli+
|
||||||
💣 tailscale.com/cmd/tailscale/cli from tailscale.com/cmd/tailscale
|
tailscale.com/cmd/tailscale/cli from tailscale.com/cmd/tailscale
|
||||||
tailscale.com/control/controlbase from tailscale.com/control/controlhttp
|
tailscale.com/control/controlbase from tailscale.com/control/controlhttp
|
||||||
tailscale.com/control/controlhttp from tailscale.com/cmd/tailscale/cli
|
tailscale.com/control/controlhttp from tailscale.com/cmd/tailscale/cli
|
||||||
tailscale.com/control/controlknobs from tailscale.com/net/portmapper
|
tailscale.com/control/controlknobs from tailscale.com/net/portmapper
|
||||||
@ -144,6 +146,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
tailscale.com/util/singleflight from tailscale.com/net/dnscache
|
tailscale.com/util/singleflight from tailscale.com/net/dnscache
|
||||||
tailscale.com/util/slicesx from tailscale.com/net/dnscache+
|
tailscale.com/util/slicesx from tailscale.com/net/dnscache+
|
||||||
💣 tailscale.com/util/winutil from tailscale.com/hostinfo+
|
💣 tailscale.com/util/winutil from tailscale.com/hostinfo+
|
||||||
|
W 💣 tailscale.com/util/winutil/authenticode from tailscale.com/cmd/tailscale/cli
|
||||||
tailscale.com/version from tailscale.com/cmd/tailscale/cli+
|
tailscale.com/version from tailscale.com/cmd/tailscale/cli+
|
||||||
tailscale.com/version/distro from tailscale.com/cmd/tailscale/cli+
|
tailscale.com/version/distro from tailscale.com/cmd/tailscale/cli+
|
||||||
tailscale.com/wgengine/capture from tailscale.com/cmd/tailscale/cli
|
tailscale.com/wgengine/capture from tailscale.com/cmd/tailscale/cli
|
||||||
@ -194,7 +197,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
bytes from bufio+
|
bytes from bufio+
|
||||||
compress/flate from compress/gzip+
|
compress/flate from compress/gzip+
|
||||||
compress/gzip from net/http
|
compress/gzip from net/http
|
||||||
compress/zlib from image/png
|
compress/zlib from image/png+
|
||||||
container/list from crypto/tls+
|
container/list from crypto/tls+
|
||||||
context from crypto/tls+
|
context from crypto/tls+
|
||||||
crypto from crypto/ecdsa+
|
crypto from crypto/ecdsa+
|
||||||
@ -219,6 +222,8 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
crypto/x509 from crypto/tls+
|
crypto/x509 from crypto/tls+
|
||||||
crypto/x509/pkix from crypto/x509+
|
crypto/x509/pkix from crypto/x509+
|
||||||
database/sql/driver from github.com/google/uuid
|
database/sql/driver from github.com/google/uuid
|
||||||
|
W debug/dwarf from debug/pe
|
||||||
|
W debug/pe from github.com/dblohm7/wingoes/pe
|
||||||
embed from tailscale.com/cmd/tailscale/cli+
|
embed from tailscale.com/cmd/tailscale/cli+
|
||||||
encoding from encoding/json+
|
encoding from encoding/json+
|
||||||
encoding/asn1 from crypto/x509+
|
encoding/asn1 from crypto/x509+
|
||||||
|
2
go.mod
2
go.mod
@ -18,7 +18,7 @@ require (
|
|||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
||||||
github.com/creack/pty v1.1.18
|
github.com/creack/pty v1.1.18
|
||||||
github.com/dave/jennifer v1.6.1
|
github.com/dave/jennifer v1.6.1
|
||||||
github.com/dblohm7/wingoes v0.0.0-20230426155039-111c8c3b57c8
|
github.com/dblohm7/wingoes v0.0.0-20230801195049-ed8077baf0cd
|
||||||
github.com/dsnet/try v0.0.3
|
github.com/dsnet/try v0.0.3
|
||||||
github.com/evanw/esbuild v0.14.53
|
github.com/evanw/esbuild v0.14.53
|
||||||
github.com/frankban/quicktest v1.14.5
|
github.com/frankban/quicktest v1.14.5
|
||||||
|
4
go.sum
4
go.sum
@ -240,8 +240,8 @@ github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dblohm7/wingoes v0.0.0-20230426155039-111c8c3b57c8 h1:vtIE3GO4hKplR58aTRx3yLPqAbfWyoyYrE8PXUv0Prw=
|
github.com/dblohm7/wingoes v0.0.0-20230801195049-ed8077baf0cd h1:zYVpYS5d3Uf04vVCJuzqpOCwQQIzJibtOx8ivt7zt2Q=
|
||||||
github.com/dblohm7/wingoes v0.0.0-20230426155039-111c8c3b57c8/go.mod h1:6NCrWM5jRefaG7iN0iMShPalLsljHWBh9v1zxM2f8Xs=
|
github.com/dblohm7/wingoes v0.0.0-20230801195049-ed8077baf0cd/go.mod h1:6NCrWM5jRefaG7iN0iMShPalLsljHWBh9v1zxM2f8Xs=
|
||||||
github.com/denis-tingaikin/go-header v0.4.3 h1:tEaZKAlqql6SKCY++utLmkPLd6K8IBM20Ha7UVm+mtU=
|
github.com/denis-tingaikin/go-header v0.4.3 h1:tEaZKAlqql6SKCY++utLmkPLd6K8IBM20Ha7UVm+mtU=
|
||||||
github.com/denis-tingaikin/go-header v0.4.3/go.mod h1:0wOCWuN71D5qIgE2nz9KrKmuYBAC2Mra5RassOIQ2/c=
|
github.com/denis-tingaikin/go-header v0.4.3/go.mod h1:0wOCWuN71D5qIgE2nz9KrKmuYBAC2Mra5RassOIQ2/c=
|
||||||
github.com/denis-tingajkin/go-header v0.3.1/go.mod h1:sq/2IxMhaZX+RRcgHfCRx/m0M5na0fBt4/CRe7Lrji0=
|
github.com/denis-tingajkin/go-header v0.3.1/go.mod h1:sq/2IxMhaZX+RRcgHfCRx/m0M5na0fBt4/CRe7Lrji0=
|
||||||
|
512
util/winutil/authenticode/authenticode_windows.go
Normal file
512
util/winutil/authenticode/authenticode_windows.go
Normal file
@ -0,0 +1,512 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package authenticode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/dblohm7/wingoes"
|
||||||
|
"github.com/dblohm7/wingoes/pe"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrSigNotFound is returned if no authenticode signature could be found.
|
||||||
|
ErrSigNotFound = errors.New("authenticode signature not found")
|
||||||
|
// ErrUnexpectedCertSubject is wrapped with the actual cert subject and
|
||||||
|
// returned when the binary is signed by a different subject than expected.
|
||||||
|
ErrUnexpectedCertSubject = errors.New("unexpected cert subject")
|
||||||
|
errCertSubjectNotFound = errors.New("cert subject not found")
|
||||||
|
errCertSubjectDecodeLenMismatch = errors.New("length mismatch while decoding cert subject")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_CERT_STRONG_SIGN_OID_INFO_CHOICE = 2
|
||||||
|
_CMSG_SIGNER_CERT_INFO_PARAM = 7
|
||||||
|
_MSI_INVALID_HASH_IS_FATAL = 1
|
||||||
|
_TRUST_E_NOSIGNATURE = wingoes.HRESULT(-((0x800B0100 ^ 0xFFFFFFFF) + 1))
|
||||||
|
)
|
||||||
|
|
||||||
|
// Verify performs authenticode verification on the file at path, and also
|
||||||
|
// ensures that expectedCertSubject was the entity who signed it. path may point
|
||||||
|
// to either a PE binary or an MSI package. ErrSigNotFound is returned if no
|
||||||
|
// signature is found.
|
||||||
|
func Verify(path string, expectedCertSubject string) error {
|
||||||
|
path16, err := windows.UTF16PtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var subject string
|
||||||
|
if strings.EqualFold(filepath.Ext(path), ".msi") {
|
||||||
|
subject, err = verifyMSI(path16)
|
||||||
|
} else {
|
||||||
|
subject, _, err = queryPE(path16, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if subject != expectedCertSubject {
|
||||||
|
return fmt.Errorf("%w %q", ErrUnexpectedCertSubject, subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SigProvenance indicates whether an authenticode signature was embedded within
|
||||||
|
// the file itself, or the signature applies to an associated catalog file.
|
||||||
|
type SigProvenance int
|
||||||
|
|
||||||
|
const (
|
||||||
|
SigProvUnknown = SigProvenance(iota)
|
||||||
|
SigProvEmbedded
|
||||||
|
SigProvCatalog
|
||||||
|
)
|
||||||
|
|
||||||
|
// QueryCertSubject obtains the subject associated with the certificate used to
|
||||||
|
// sign the PE binary located at path. When err == nil, it also returns the
|
||||||
|
// provenance of that signature. ErrSigNotFound is returned if no signature
|
||||||
|
// is found. Note that this function does *not* validate the chain of trust; use
|
||||||
|
// Verify for that purpose!
|
||||||
|
func QueryCertSubject(path string) (certSubject string, provenance SigProvenance, err error) {
|
||||||
|
path16, err := windows.UTF16PtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", SigProvUnknown, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryPE(path16, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryPE(utf16Path *uint16, verify bool) (string, SigProvenance, error) {
|
||||||
|
certSubject, err := queryEmbeddedCertSubject(utf16Path, verify)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case err == ErrSigNotFound:
|
||||||
|
// Try looking for the signature in a catalog file.
|
||||||
|
default:
|
||||||
|
return certSubject, SigProvEmbedded, err
|
||||||
|
}
|
||||||
|
|
||||||
|
certSubject, err = queryCatalogCertSubject(utf16Path, verify)
|
||||||
|
switch {
|
||||||
|
case err == ErrSigNotFound:
|
||||||
|
return "", SigProvUnknown, err
|
||||||
|
default:
|
||||||
|
return certSubject, SigProvCatalog, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CertSubjectError struct {
|
||||||
|
Err error
|
||||||
|
Subject string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CertSubjectError) Error() string {
|
||||||
|
if e == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
if e.Subject == "" {
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("cert subject %q: %v", e.Subject, e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CertSubjectError) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyMSI(path *uint16) (string, error) {
|
||||||
|
var certCtx *windows.CertContext
|
||||||
|
hr := msiGetFileSignatureInformation(path, _MSI_INVALID_HASH_IS_FATAL, &certCtx, nil, nil)
|
||||||
|
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||||
|
if e == wingoes.ErrorFromHRESULT(_TRUST_E_NOSIGNATURE) {
|
||||||
|
return "", ErrSigNotFound
|
||||||
|
}
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
defer windows.CertFreeCertificateContext(certCtx)
|
||||||
|
|
||||||
|
return certSubjectFromCertContext(certCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func certSubjectFromCertContext(certCtx *windows.CertContext) (string, error) {
|
||||||
|
desiredLen := windows.CertGetNameString(
|
||||||
|
certCtx,
|
||||||
|
windows.CERT_NAME_SIMPLE_DISPLAY_TYPE,
|
||||||
|
0,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if desiredLen <= 1 {
|
||||||
|
return "", errCertSubjectNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]uint16, desiredLen)
|
||||||
|
actualLen := windows.CertGetNameString(
|
||||||
|
certCtx,
|
||||||
|
windows.CERT_NAME_SIMPLE_DISPLAY_TYPE,
|
||||||
|
0,
|
||||||
|
nil,
|
||||||
|
&buf[0],
|
||||||
|
desiredLen,
|
||||||
|
)
|
||||||
|
if actualLen != desiredLen {
|
||||||
|
return "", errCertSubjectDecodeLenMismatch
|
||||||
|
}
|
||||||
|
|
||||||
|
return windows.UTF16ToString(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type objectQuery struct {
|
||||||
|
certStore windows.Handle
|
||||||
|
cryptMsg windows.Handle
|
||||||
|
encodingType uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func newObjectQuery(utf16Path *uint16) (*objectQuery, error) {
|
||||||
|
var oq objectQuery
|
||||||
|
if err := windows.CryptQueryObject(
|
||||||
|
windows.CERT_QUERY_OBJECT_FILE,
|
||||||
|
unsafe.Pointer(utf16Path),
|
||||||
|
windows.CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
|
||||||
|
windows.CERT_QUERY_FORMAT_FLAG_BINARY,
|
||||||
|
0,
|
||||||
|
&oq.encodingType,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
&oq.certStore,
|
||||||
|
&oq.cryptMsg,
|
||||||
|
nil,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &oq, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oq *objectQuery) Close() error {
|
||||||
|
if oq.certStore != 0 {
|
||||||
|
if err := windows.CertCloseStore(oq.certStore, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
oq.certStore = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if oq.cryptMsg != 0 {
|
||||||
|
if err := cryptMsgClose(oq.cryptMsg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
oq.cryptMsg = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oq *objectQuery) certSubject() (string, error) {
|
||||||
|
var certInfoLen uint32
|
||||||
|
if err := cryptMsgGetParam(
|
||||||
|
oq.cryptMsg,
|
||||||
|
_CMSG_SIGNER_CERT_INFO_PARAM,
|
||||||
|
0,
|
||||||
|
unsafe.Pointer(nil),
|
||||||
|
&certInfoLen,
|
||||||
|
); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, certInfoLen)
|
||||||
|
if err := cryptMsgGetParam(
|
||||||
|
oq.cryptMsg,
|
||||||
|
_CMSG_SIGNER_CERT_INFO_PARAM,
|
||||||
|
0,
|
||||||
|
unsafe.Pointer(&buf[0]),
|
||||||
|
&certInfoLen,
|
||||||
|
); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
certInfo := (*windows.CertInfo)(unsafe.Pointer(&buf[0]))
|
||||||
|
certCtx, err := windows.CertFindCertificateInStore(
|
||||||
|
oq.certStore,
|
||||||
|
oq.encodingType,
|
||||||
|
0,
|
||||||
|
windows.CERT_FIND_SUBJECT_CERT,
|
||||||
|
unsafe.Pointer(certInfo),
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer windows.CertFreeCertificateContext(certCtx)
|
||||||
|
|
||||||
|
return certSubjectFromCertContext(certCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractCertBlob(hfile windows.Handle) ([]byte, error) {
|
||||||
|
pef, err := pe.NewPEFromFileHandle(hfile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer pef.Close()
|
||||||
|
|
||||||
|
certsAny, err := pef.DataDirectoryEntry(pe.IMAGE_DIRECTORY_ENTRY_SECURITY)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
certs, ok := certsAny.([]pe.AuthenticodeCert)
|
||||||
|
if !ok || len(certs) == 0 {
|
||||||
|
return nil, ErrSigNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cert := range certs {
|
||||||
|
if cert.Revision() != pe.WIN_CERT_REVISION_2_0 || cert.Type() != pe.WIN_CERT_TYPE_PKCS_SIGNED_DATA {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return cert.Data(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrSigNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
type _HCRYPTPROV windows.Handle
|
||||||
|
|
||||||
|
type _CRYPT_VERIFY_MESSAGE_PARA struct {
|
||||||
|
CBSize uint32
|
||||||
|
MsgAndCertEncodingType uint32
|
||||||
|
HCryptProv _HCRYPTPROV
|
||||||
|
FNGetSignerCertificate uintptr
|
||||||
|
GetArg uintptr
|
||||||
|
StrongSignPara *windows.CertStrongSignPara
|
||||||
|
}
|
||||||
|
|
||||||
|
func querySubjectFromBlob(blob []byte) (string, error) {
|
||||||
|
para := _CRYPT_VERIFY_MESSAGE_PARA{
|
||||||
|
CBSize: uint32(unsafe.Sizeof(_CRYPT_VERIFY_MESSAGE_PARA{})),
|
||||||
|
MsgAndCertEncodingType: windows.X509_ASN_ENCODING | windows.PKCS_7_ASN_ENCODING,
|
||||||
|
}
|
||||||
|
|
||||||
|
var certCtx *windows.CertContext
|
||||||
|
if err := cryptVerifyMessageSignature(¶, 0, &blob[0], uint32(len(blob)), nil, nil, &certCtx); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer windows.CertFreeCertificateContext(certCtx)
|
||||||
|
|
||||||
|
return certSubjectFromCertContext(certCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryEmbeddedCertSubject(utf16Path *uint16, verify bool) (string, error) {
|
||||||
|
peBinary, err := windows.CreateFile(
|
||||||
|
utf16Path,
|
||||||
|
windows.GENERIC_READ,
|
||||||
|
windows.FILE_SHARE_READ,
|
||||||
|
nil,
|
||||||
|
windows.OPEN_EXISTING,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer windows.CloseHandle(peBinary)
|
||||||
|
|
||||||
|
blob, err := extractCertBlob(peBinary)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
certSubj, err := querySubjectFromBlob(blob)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !verify {
|
||||||
|
return certSubj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
wintrustArg := unsafe.Pointer(&windows.WinTrustFileInfo{
|
||||||
|
Size: uint32(unsafe.Sizeof(windows.WinTrustFileInfo{})),
|
||||||
|
FilePath: utf16Path,
|
||||||
|
File: peBinary,
|
||||||
|
})
|
||||||
|
if err := verifyTrust(windows.WTD_CHOICE_FILE, wintrustArg); err != nil {
|
||||||
|
// We might still want to know who the cert subject claims to be
|
||||||
|
// even if the validation has failed (eg for troubleshooting purposes),
|
||||||
|
// so we return a CertSubjectError.
|
||||||
|
return "", &CertSubjectError{Err: err, Subject: certSubj}
|
||||||
|
}
|
||||||
|
|
||||||
|
return certSubj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_BCRYPT_SHA256_ALGORITHM = &([]uint16{'S', 'H', 'A', '2', '5', '6', 0})[0]
|
||||||
|
_OID_CERT_STRONG_SIGN_OS_1 = &([]byte("1.3.6.1.4.1.311.72.1.1\x00"))[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
type _HCATADMIN windows.Handle
|
||||||
|
type _HCATINFO windows.Handle
|
||||||
|
|
||||||
|
type _CATALOG_INFO struct {
|
||||||
|
size uint32
|
||||||
|
catalogFile [windows.MAX_PATH]uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type _WINTRUST_CATALOG_INFO struct {
|
||||||
|
size uint32
|
||||||
|
catalogVersion uint32
|
||||||
|
catalogFilePath *uint16
|
||||||
|
memberTag *uint16
|
||||||
|
memberFilePath *uint16
|
||||||
|
memberFile windows.Handle
|
||||||
|
pCalculatedFileHash *byte
|
||||||
|
cbCalculatedFileHash uint32
|
||||||
|
catalogContext uintptr
|
||||||
|
catAdmin _HCATADMIN
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryCatalogCertSubject(utf16Path *uint16, verify bool) (string, error) {
|
||||||
|
var catAdmin _HCATADMIN
|
||||||
|
policy := windows.CertStrongSignPara{
|
||||||
|
Size: uint32(unsafe.Sizeof(windows.CertStrongSignPara{})),
|
||||||
|
InfoChoice: _CERT_STRONG_SIGN_OID_INFO_CHOICE,
|
||||||
|
InfoOrSerializedInfoOrOID: unsafe.Pointer(_OID_CERT_STRONG_SIGN_OS_1),
|
||||||
|
}
|
||||||
|
if err := cryptCATAdminAcquireContext2(
|
||||||
|
&catAdmin,
|
||||||
|
nil,
|
||||||
|
_BCRYPT_SHA256_ALGORITHM,
|
||||||
|
&policy,
|
||||||
|
0,
|
||||||
|
); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer cryptCATAdminReleaseContext(catAdmin, 0)
|
||||||
|
|
||||||
|
// We use windows.CreateFile instead of standard library facilities because:
|
||||||
|
// 1. Subsequent API calls directly utilize the file's Win32 HANDLE;
|
||||||
|
// 2. We're going to be hashing the contents of this file, so we want to
|
||||||
|
// provide a sequential-scan hint to the kernel.
|
||||||
|
memberFile, err := windows.CreateFile(
|
||||||
|
utf16Path,
|
||||||
|
windows.GENERIC_READ,
|
||||||
|
windows.FILE_SHARE_READ,
|
||||||
|
nil,
|
||||||
|
windows.OPEN_EXISTING,
|
||||||
|
windows.FILE_FLAG_SEQUENTIAL_SCAN,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer windows.CloseHandle(memberFile)
|
||||||
|
|
||||||
|
var hashLen uint32
|
||||||
|
if err := cryptCATAdminCalcHashFromFileHandle2(
|
||||||
|
catAdmin,
|
||||||
|
memberFile,
|
||||||
|
&hashLen,
|
||||||
|
nil,
|
||||||
|
0,
|
||||||
|
); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
hashBuf := make([]byte, hashLen)
|
||||||
|
if err := cryptCATAdminCalcHashFromFileHandle2(
|
||||||
|
catAdmin,
|
||||||
|
memberFile,
|
||||||
|
&hashLen,
|
||||||
|
&hashBuf[0],
|
||||||
|
0,
|
||||||
|
); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
catInfoCtx, err := cryptCATAdminEnumCatalogFromHash(
|
||||||
|
catAdmin,
|
||||||
|
&hashBuf[0],
|
||||||
|
hashLen,
|
||||||
|
0,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
if err == windows.ERROR_NOT_FOUND {
|
||||||
|
err = ErrSigNotFound
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer cryptCATAdminReleaseCatalogContext(catAdmin, catInfoCtx, 0)
|
||||||
|
|
||||||
|
catInfo := _CATALOG_INFO{
|
||||||
|
size: uint32(unsafe.Sizeof(_CATALOG_INFO{})),
|
||||||
|
}
|
||||||
|
if err := cryptCATAdminCatalogInfoFromContext(catInfoCtx, &catInfo, 0); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
oq, err := newObjectQuery(&catInfo.catalogFile[0])
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer oq.Close()
|
||||||
|
|
||||||
|
certSubj, err := oq.certSubject()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !verify {
|
||||||
|
return certSubj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// memberTag is required to be formatted this way.
|
||||||
|
hbh := strings.ToUpper(hex.EncodeToString(hashBuf))
|
||||||
|
memberTag, err := windows.UTF16PtrFromString(hbh)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
wintrustArg := unsafe.Pointer(&_WINTRUST_CATALOG_INFO{
|
||||||
|
size: uint32(unsafe.Sizeof(_WINTRUST_CATALOG_INFO{})),
|
||||||
|
catalogFilePath: &catInfo.catalogFile[0],
|
||||||
|
memberTag: memberTag,
|
||||||
|
memberFilePath: utf16Path,
|
||||||
|
memberFile: memberFile,
|
||||||
|
catAdmin: catAdmin,
|
||||||
|
})
|
||||||
|
if err := verifyTrust(windows.WTD_CHOICE_CATALOG, wintrustArg); err != nil {
|
||||||
|
// We might still want to know who the cert subject claims to be
|
||||||
|
// even if the validation has failed (eg for troubleshooting purposes),
|
||||||
|
// so we return a CertSubjectError.
|
||||||
|
return "", &CertSubjectError{Err: err, Subject: certSubj}
|
||||||
|
}
|
||||||
|
|
||||||
|
return certSubj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyTrust(infoType uint32, info unsafe.Pointer) error {
|
||||||
|
data := &windows.WinTrustData{
|
||||||
|
Size: uint32(unsafe.Sizeof(windows.WinTrustData{})),
|
||||||
|
UIChoice: windows.WTD_UI_NONE,
|
||||||
|
RevocationChecks: windows.WTD_REVOKE_WHOLECHAIN, // Full revocation checking, as this is called with network connectivity.
|
||||||
|
UnionChoice: infoType,
|
||||||
|
StateAction: windows.WTD_STATEACTION_VERIFY,
|
||||||
|
FileOrCatalogOrBlobOrSgnrOrCert: info,
|
||||||
|
}
|
||||||
|
err := windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
|
||||||
|
|
||||||
|
data.StateAction = windows.WTD_STATEACTION_CLOSE
|
||||||
|
windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
18
util/winutil/authenticode/mksyscall.go
Normal file
18
util/winutil/authenticode/mksyscall.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package authenticode
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go mksyscall.go
|
||||||
|
//go:generate go run golang.org/x/tools/cmd/goimports -w zsyscall_windows.go
|
||||||
|
|
||||||
|
//sys cryptCATAdminAcquireContext2(hCatAdmin *_HCATADMIN, pgSubsystem *windows.GUID, hashAlgorithm *uint16, strongHashPolicy *windows.CertStrongSignPara, flags uint32) (err error) [int32(failretval)==0] = wintrust.CryptCATAdminAcquireContext2
|
||||||
|
//sys cryptCATAdminCalcHashFromFileHandle2(hCatAdmin _HCATADMIN, file windows.Handle, pcbHash *uint32, pbHash *byte, flags uint32) (err error) [int32(failretval)==0] = wintrust.CryptCATAdminCalcHashFromFileHandle2
|
||||||
|
//sys cryptCATAdminCatalogInfoFromContext(hCatInfo _HCATINFO, catInfo *_CATALOG_INFO, flags uint32) (err error) [int32(failretval)==0] = wintrust.CryptCATCatalogInfoFromContext
|
||||||
|
//sys cryptCATAdminEnumCatalogFromHash(hCatAdmin _HCATADMIN, pbHash *byte, cbHash uint32, flags uint32, prevCatInfo *_HCATINFO) (ret _HCATINFO, err error) [ret==0] = wintrust.CryptCATAdminEnumCatalogFromHash
|
||||||
|
//sys cryptCATAdminReleaseCatalogContext(hCatAdmin _HCATADMIN, hCatInfo _HCATINFO, flags uint32) (err error) [int32(failretval)==0] = wintrust.CryptCATAdminReleaseCatalogContext
|
||||||
|
//sys cryptCATAdminReleaseContext(hCatAdmin _HCATADMIN, flags uint32) (err error) [int32(failretval)==0] = wintrust.CryptCATAdminReleaseContext
|
||||||
|
//sys cryptMsgClose(cryptMsg windows.Handle) (err error) [int32(failretval)==0] = crypt32.CryptMsgClose
|
||||||
|
//sys cryptMsgGetParam(cryptMsg windows.Handle, paramType uint32, index uint32, data unsafe.Pointer, dataLen *uint32) (err error) [int32(failretval)==0] = crypt32.CryptMsgGetParam
|
||||||
|
//sys cryptVerifyMessageSignature(pVerifyPara *_CRYPT_VERIFY_MESSAGE_PARA, signerIndex uint32, pbSignedBlob *byte, cbSignedBlob uint32, pbDecoded *byte, pdbDecoded *uint32, ppSignerCert **windows.CertContext) (err error) [int32(failretval)==0] = crypt32.CryptVerifyMessageSignature
|
||||||
|
//sys msiGetFileSignatureInformation(signedObjectPath *uint16, flags uint32, certCtx **windows.CertContext, pbHashData *byte, cbHashData *uint32) (ret wingoes.HRESULT) = msi.MsiGetFileSignatureInformationW
|
135
util/winutil/authenticode/zsyscall_windows.go
Normal file
135
util/winutil/authenticode/zsyscall_windows.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// Code generated by 'go generate'; DO NOT EDIT.
|
||||||
|
|
||||||
|
package authenticode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/dblohm7/wingoes"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
errERROR_EINVAL error = syscall.EINVAL
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return errERROR_EINVAL
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
// TODO: add more here, after collecting data on the common
|
||||||
|
// error values see on Windows. (perhaps when running
|
||||||
|
// all.bat?)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modcrypt32 = windows.NewLazySystemDLL("crypt32.dll")
|
||||||
|
modmsi = windows.NewLazySystemDLL("msi.dll")
|
||||||
|
modwintrust = windows.NewLazySystemDLL("wintrust.dll")
|
||||||
|
|
||||||
|
procCryptMsgClose = modcrypt32.NewProc("CryptMsgClose")
|
||||||
|
procCryptMsgGetParam = modcrypt32.NewProc("CryptMsgGetParam")
|
||||||
|
procCryptVerifyMessageSignature = modcrypt32.NewProc("CryptVerifyMessageSignature")
|
||||||
|
procMsiGetFileSignatureInformationW = modmsi.NewProc("MsiGetFileSignatureInformationW")
|
||||||
|
procCryptCATAdminAcquireContext2 = modwintrust.NewProc("CryptCATAdminAcquireContext2")
|
||||||
|
procCryptCATAdminCalcHashFromFileHandle2 = modwintrust.NewProc("CryptCATAdminCalcHashFromFileHandle2")
|
||||||
|
procCryptCATAdminEnumCatalogFromHash = modwintrust.NewProc("CryptCATAdminEnumCatalogFromHash")
|
||||||
|
procCryptCATAdminReleaseCatalogContext = modwintrust.NewProc("CryptCATAdminReleaseCatalogContext")
|
||||||
|
procCryptCATAdminReleaseContext = modwintrust.NewProc("CryptCATAdminReleaseContext")
|
||||||
|
procCryptCATCatalogInfoFromContext = modwintrust.NewProc("CryptCATCatalogInfoFromContext")
|
||||||
|
)
|
||||||
|
|
||||||
|
func cryptMsgClose(cryptMsg windows.Handle) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procCryptMsgClose.Addr(), 1, uintptr(cryptMsg), 0, 0)
|
||||||
|
if int32(r1) == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cryptMsgGetParam(cryptMsg windows.Handle, paramType uint32, index uint32, data unsafe.Pointer, dataLen *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procCryptMsgGetParam.Addr(), 5, uintptr(cryptMsg), uintptr(paramType), uintptr(index), uintptr(data), uintptr(unsafe.Pointer(dataLen)), 0)
|
||||||
|
if int32(r1) == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cryptVerifyMessageSignature(pVerifyPara *_CRYPT_VERIFY_MESSAGE_PARA, signerIndex uint32, pbSignedBlob *byte, cbSignedBlob uint32, pbDecoded *byte, pdbDecoded *uint32, ppSignerCert **windows.CertContext) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall9(procCryptVerifyMessageSignature.Addr(), 7, uintptr(unsafe.Pointer(pVerifyPara)), uintptr(signerIndex), uintptr(unsafe.Pointer(pbSignedBlob)), uintptr(cbSignedBlob), uintptr(unsafe.Pointer(pbDecoded)), uintptr(unsafe.Pointer(pdbDecoded)), uintptr(unsafe.Pointer(ppSignerCert)), 0, 0)
|
||||||
|
if int32(r1) == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func msiGetFileSignatureInformation(signedObjectPath *uint16, flags uint32, certCtx **windows.CertContext, pbHashData *byte, cbHashData *uint32) (ret wingoes.HRESULT) {
|
||||||
|
r0, _, _ := syscall.Syscall6(procMsiGetFileSignatureInformationW.Addr(), 5, uintptr(unsafe.Pointer(signedObjectPath)), uintptr(flags), uintptr(unsafe.Pointer(certCtx)), uintptr(unsafe.Pointer(pbHashData)), uintptr(unsafe.Pointer(cbHashData)), 0)
|
||||||
|
ret = wingoes.HRESULT(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cryptCATAdminAcquireContext2(hCatAdmin *_HCATADMIN, pgSubsystem *windows.GUID, hashAlgorithm *uint16, strongHashPolicy *windows.CertStrongSignPara, flags uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procCryptCATAdminAcquireContext2.Addr(), 5, uintptr(unsafe.Pointer(hCatAdmin)), uintptr(unsafe.Pointer(pgSubsystem)), uintptr(unsafe.Pointer(hashAlgorithm)), uintptr(unsafe.Pointer(strongHashPolicy)), uintptr(flags), 0)
|
||||||
|
if int32(r1) == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cryptCATAdminCalcHashFromFileHandle2(hCatAdmin _HCATADMIN, file windows.Handle, pcbHash *uint32, pbHash *byte, flags uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procCryptCATAdminCalcHashFromFileHandle2.Addr(), 5, uintptr(hCatAdmin), uintptr(file), uintptr(unsafe.Pointer(pcbHash)), uintptr(unsafe.Pointer(pbHash)), uintptr(flags), 0)
|
||||||
|
if int32(r1) == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cryptCATAdminEnumCatalogFromHash(hCatAdmin _HCATADMIN, pbHash *byte, cbHash uint32, flags uint32, prevCatInfo *_HCATINFO) (ret _HCATINFO, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall6(procCryptCATAdminEnumCatalogFromHash.Addr(), 5, uintptr(hCatAdmin), uintptr(unsafe.Pointer(pbHash)), uintptr(cbHash), uintptr(flags), uintptr(unsafe.Pointer(prevCatInfo)), 0)
|
||||||
|
ret = _HCATINFO(r0)
|
||||||
|
if ret == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cryptCATAdminReleaseCatalogContext(hCatAdmin _HCATADMIN, hCatInfo _HCATINFO, flags uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procCryptCATAdminReleaseCatalogContext.Addr(), 3, uintptr(hCatAdmin), uintptr(hCatInfo), uintptr(flags))
|
||||||
|
if int32(r1) == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cryptCATAdminReleaseContext(hCatAdmin _HCATADMIN, flags uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procCryptCATAdminReleaseContext.Addr(), 2, uintptr(hCatAdmin), uintptr(flags), 0)
|
||||||
|
if int32(r1) == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cryptCATAdminCatalogInfoFromContext(hCatInfo _HCATINFO, catInfo *_CATALOG_INFO, flags uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procCryptCATCatalogInfoFromContext.Addr(), 3, uintptr(hCatInfo), uintptr(unsafe.Pointer(catInfo)), uintptr(flags))
|
||||||
|
if int32(r1) == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user