From 2a8cac388bc74fb497a2fae743941986057f14cb Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Fri, 19 Nov 2021 15:39:03 +0800 Subject: [PATCH 1/3] add option to disable PIN cache --- capi/wincapi.go | 106 ++++++++++++++++++++++++++++++++++++++++++++++-- main.go | 4 ++ 2 files changed, 106 insertions(+), 4 deletions(-) diff --git a/capi/wincapi.go b/capi/wincapi.go index ac530d8..f6d8a02 100644 --- a/capi/wincapi.go +++ b/capi/wincapi.go @@ -18,15 +18,33 @@ const ( ALG_ECDSA_SHA256 = "1.2.840.10045.4.3.2" ALG_ECDSA_SHA384 = "1.2.840.10045.4.3.3" ALG_ECDSA_SHA512 = "1.2.840.10045.4.3.4" + + NCRYPT_PIN_PROPERTY = "SmartCardPin" +) + +const ( + AT_KEYEXCHANGE = uint32(1) + AT_SIGNATURE = uint32(2) + CERT_NCRYPT_KEY_SPEC = uint32(0xFFFFFFFF) + + X509_ASN_ENCODING = 0x1 + PKCS_7_ASN_ENCODING = 0x10000 + CRYPT_ACQUIRE_CACHE_FLAG = uint32(0x00000001) + CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG = uint32(0x00040000) ) var ( modcrypt32 = syscall.NewLazyDLL("crypt32.dll") + modncrypt = syscall.NewLazyDLL("ncrypt.dll") procCryptSignMessage = modcrypt32.NewProc("CryptSignMessage") procCertDuplicateCertificateContext = modcrypt32.NewProc("CertDuplicateCertificateContext") procCertGetCertificateContextProperty = modcrypt32.NewProc("CertGetCertificateContextProperty") + procCryptAcquireCertificatePrivateKey = modcrypt32.NewProc("CryptAcquireCertificatePrivateKey") + procNCryptSetProperty = modncrypt.NewProc("NCryptSetProperty") ) +var disablePINCache = true + type cryptoapiBlob struct { DataSize uint32 Data uintptr @@ -104,6 +122,64 @@ func certGetCertificateContextProperty(context *syscall.CertContext, dwPropId ui return int(r0) } +func nCryptSetPropertyString(hObject uintptr, pszProperty string, pbInput string, dwFlags uint32) (err error) { + + pszPropertyPtr, _ := syscall.UTF16PtrFromString(pszProperty) + + dataPtr := uintptr(0) + dataSize := uint32(0) + if pbInput != "" { + stringPtr, _ := syscall.UTF16PtrFromString(pbInput) + dataPtr = uintptr(unsafe.Pointer(stringPtr)) + dataSize = uint32(len(pbInput)) + } + dataSizePtr := uintptr(unsafe.Pointer(&dataSize)) + + r0, _, e1 := syscall.Syscall6(procNCryptSetProperty.Addr(), 5, + hObject, + uintptr(unsafe.Pointer(pszPropertyPtr)), + dataPtr, + dataSizePtr, + uintptr(dwFlags), + 0, + ) + + if r0 != 0 { + return e1 + } + + if e1 != syscall.Errno(0) { + return e1 + } + return nil +} + +func cryptAcquireCertificatePrivateKey(certContext uintptr, flags uint32) (provContext uintptr, err error) { + pvParameters := uint32(0) + phCryptProvOrNCryptKey := uintptr(0) + pdwKeySpec := 0 // Can be 0, AT_KEYEXCHANGE, AT_SIGNATURE, or CERT_NCRYPT_KEY_SPEC + pfCallerFreeProvOrNCryptKey := false + + r0, _, e1 := syscall.Syscall6(procCryptAcquireCertificatePrivateKey.Addr(), 6, + certContext, + uintptr(flags), + uintptr(unsafe.Pointer(&pvParameters)), + uintptr(unsafe.Pointer(&phCryptProvOrNCryptKey)), + uintptr(unsafe.Pointer(&pdwKeySpec)), + uintptr(unsafe.Pointer(&pfCallerFreeProvOrNCryptKey)), + ) + + if r0 == 0 { + return 0, fmt.Errorf("r0 was 0") + } + + if e1 != syscall.Errno(0) { + return 0, e1 + } + + return phCryptProvOrNCryptKey, nil +} + type Certificate struct { certContext uintptr *x509.Certificate @@ -185,10 +261,18 @@ func LoadUserCerts() ([]*Certificate, error) { } func Sign(alg string, cert *Certificate, data []byte) (*pkcs7.PKCS7, error) { - const ( - X509_ASN_ENCODING = 0x1 - PKCS_7_ASN_ENCODING = 0x10000 - ) + var nCryptHandle uintptr + + if disablePINCache { + var err error + // Acquire a handle for the private key attached to this certificate + acquireFlags := uint32(CRYPT_ACQUIRE_CACHE_FLAG | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG) + nCryptHandle, err = cryptAcquireCertificatePrivateKey(cert.certContext, acquireFlags) + if err != nil { + return nil, err + } + } + algptr, err := syscall.BytePtrFromString(alg) if err != nil { return nil, err @@ -203,5 +287,19 @@ func Sign(alg string, cert *Certificate, data []byte) (*pkcs7.PKCS7, error) { if err != nil { return nil, err } + + if disablePINCache && nCryptHandle != 0 { + // Set the PIN to NULL so we are prompted again + err = nCryptSetPropertyString(nCryptHandle, NCRYPT_PIN_PROPERTY, "", 0) + if err != nil { + return nil, fmt.Errorf("Could not set NCRYPT_PIN_PROPERTY: %v\n", err) + } + } + return pkcs7.Parse(sign) } + +func SetPINCache(b bool) { + fmt.Printf("Setting PIN Cache to %v\n", b) + disablePINCache = b +} diff --git a/main.go b/main.go index 69f6ebc..f888572 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ package main import ( "context" "flag" + "github.com/buptczq/WinCryptSSHAgent/capi" "os" "os/signal" "path/filepath" @@ -36,6 +37,7 @@ var applications = []app.Application{ var installHVService = flag.Bool("i", false, "Install Hyper-V Guest Communication Services") var disableCapi = flag.Bool("disable-capi", false, "Disable Windows Crypto API") +var disablePINCache = flag.Bool("disable-pin-cache", false, "Clear the Smart Card PIN Cache after each operation") func installService() { if !utils.IsAdmin() { @@ -122,6 +124,8 @@ func main() { // context ctx, cancel := context.WithCancel(context.Background()) + capi.SetPINCache(*disablePINCache) + // agent var ag agent.Agent if hvClient { From 6f1f814e7807264eec224b484621f5b383646ea7 Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Fri, 19 Nov 2021 15:41:55 +0800 Subject: [PATCH 2/3] rename function and remove debug message --- capi/wincapi.go | 3 +-- main.go | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/capi/wincapi.go b/capi/wincapi.go index f6d8a02..78b5b85 100644 --- a/capi/wincapi.go +++ b/capi/wincapi.go @@ -299,7 +299,6 @@ func Sign(alg string, cert *Certificate, data []byte) (*pkcs7.PKCS7, error) { return pkcs7.Parse(sign) } -func SetPINCache(b bool) { - fmt.Printf("Setting PIN Cache to %v\n", b) +func SetDisablePINCache(b bool) { disablePINCache = b } diff --git a/main.go b/main.go index f888572..31b60c9 100644 --- a/main.go +++ b/main.go @@ -124,7 +124,7 @@ func main() { // context ctx, cancel := context.WithCancel(context.Background()) - capi.SetPINCache(*disablePINCache) + capi.SetDisablePINCache(*disablePINCache) // agent var ag agent.Agent From 53431b75a6ec216813f9dbe5fd2b6f65b9da619f Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Wed, 24 Nov 2021 08:22:47 +0800 Subject: [PATCH 3/3] add comments --- capi/wincapi.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/capi/wincapi.go b/capi/wincapi.go index 78b5b85..2d5c8aa 100644 --- a/capi/wincapi.go +++ b/capi/wincapi.go @@ -122,6 +122,7 @@ func certGetCertificateContextProperty(context *syscall.CertContext, dwPropId ui return int(r0) } +// nCryptSetPropertyString sets a string value for a named property for a CNG key storage object. func nCryptSetPropertyString(hObject uintptr, pszProperty string, pbInput string, dwFlags uint32) (err error) { pszPropertyPtr, _ := syscall.UTF16PtrFromString(pszProperty) @@ -154,6 +155,8 @@ func nCryptSetPropertyString(hObject uintptr, pszProperty string, pbInput string return nil } +// cryptAcquireCertificatePrivateKey obtains the private key for a certificateContext, returning a CNG NCRYPT_KEY_HANDLE +// or a HCRYPTPROV depending on the flags given. func cryptAcquireCertificatePrivateKey(certContext uintptr, flags uint32) (provContext uintptr, err error) { pvParameters := uint32(0) phCryptProvOrNCryptKey := uintptr(0)