diff options
Diffstat (limited to 'src/mscng/x509vfy.c')
-rw-r--r-- | src/mscng/x509vfy.c | 1215 |
1 files changed, 1215 insertions, 0 deletions
diff --git a/src/mscng/x509vfy.c b/src/mscng/x509vfy.c new file mode 100644 index 00000000..8940ba3d --- /dev/null +++ b/src/mscng/x509vfy.c @@ -0,0 +1,1215 @@ +/* + * XML Security Library (http://www.aleksey.com/xmlsec). + * + * + * This is free software; see Copyright file in the source + * distribution for preciese wording. + * + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. + */ +/** + * SECTION:x509vfy + * @Short_description: X509 certificates verification support functions for Microsoft Cryptography API: Next Generation (CNG). + * @Stability: Private + * + */ + +#include "globals.h" + +#ifndef XMLSEC_NO_X509 + +#include <string.h> + +#include <windows.h> + +#include <xmlsec/xmlsec.h> +#include <xmlsec/xmltree.h> +#include <xmlsec/keys.h> +#include <xmlsec/keyinfo.h> +#include <xmlsec/keysmngr.h> +#include <xmlsec/base64.h> +#include <xmlsec/bn.h> +#include <xmlsec/errors.h> + +#include <xmlsec/mscng/crypto.h> +#include <xmlsec/mscng/x509.h> + +typedef struct _xmlSecMSCngX509StoreCtx xmlSecMSCngX509StoreCtx, + *xmlSecMSCngX509StoreCtxPtr; +struct _xmlSecMSCngX509StoreCtx { + HCERTSTORE trusted; + HCERTSTORE trustedMemStore; + HCERTSTORE untrusted; + HCERTSTORE untrustedMemStore; +}; + +#define xmlSecMSCngX509StoreGetCtx(store) \ + ((xmlSecMSCngX509StoreCtxPtr)(((xmlSecByte*)(store)) + \ + sizeof(xmlSecKeyDataStoreKlass))) +#define xmlSecMSCngX509StoreSize \ + (sizeof(xmlSecKeyDataStoreKlass) + sizeof(xmlSecMSCngX509StoreCtx)) + +static void +xmlSecMSCngX509StoreFinalize(xmlSecKeyDataStorePtr store) { + xmlSecMSCngX509StoreCtxPtr ctx; + int ret; + + xmlSecAssert(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId)); + ctx = xmlSecMSCngX509StoreGetCtx(store); + xmlSecAssert(ctx != NULL); + + if(ctx->trusted != NULL) { + ret = CertCloseStore(ctx->trusted, CERT_CLOSE_STORE_CHECK_FLAG); + if(ret == FALSE) { + xmlSecMSCngLastError("CertCloseStore", xmlSecKeyDataStoreGetName(store)); + } + } + + if(ctx->trustedMemStore != NULL) { + ret = CertCloseStore(ctx->trustedMemStore, CERT_CLOSE_STORE_CHECK_FLAG); + if(ret == FALSE) { + xmlSecMSCngLastError("CertCloseStore", xmlSecKeyDataStoreGetName(store)); + } + } + + if(ctx->untrusted != NULL) { + ret = CertCloseStore(ctx->untrusted, CERT_CLOSE_STORE_CHECK_FLAG); + if(ret == FALSE) { + xmlSecMSCngLastError("CertCloseStore", xmlSecKeyDataStoreGetName(store)); + } + } + + if(ctx->untrustedMemStore != NULL) { + ret = CertCloseStore(ctx->untrustedMemStore, CERT_CLOSE_STORE_CHECK_FLAG); + if(ret == FALSE) { + xmlSecMSCngLastError("CertCloseStore", xmlSecKeyDataStoreGetName(store)); + } + } + + memset(ctx, 0, sizeof(xmlSecMSCngX509StoreCtx)); +} + +/** + * xmlSecMSCngX509StoreAdoptKeyStore: + * @store: the pointer to X509 key data store klass. + * @keyStore: the pointer to keys store. + * + * Adds @keyStore to the list of key stores. + * + * Returns: 0 on success or a negative value if an error occurs. + */ +int +xmlSecMSCngX509StoreAdoptKeyStore(xmlSecKeyDataStorePtr store, HCERTSTORE keyStore) { + xmlSecMSCngX509StoreCtxPtr ctx; + int ret; + + xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); + xmlSecAssert2(keyStore != NULL, -1); + + ctx = xmlSecMSCngX509StoreGetCtx(store); + xmlSecAssert2(ctx != NULL, -1); + xmlSecAssert2(ctx->trusted != NULL, -1); + + ret = CertAddStoreToCollection(ctx->trusted, keyStore, CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 2); + if(ret != TRUE) { + xmlSecMSCngLastError("CertAddStoreToCollection", + xmlSecKeyDataStoreGetName(store)); + return(-1); + } + + return(0); +} + +/** + * xmlSecMSCngX509StoreAdoptTrustedStore: + * @store: the pointer to X509 key data store klass. + * @trustedStore: the pointer to certs store. + * + * Adds @trustedStore to the list of trusted certs stores. + * + * Returns: 0 on success or a negative value if an error occurs. + */ +int +xmlSecMSCngX509StoreAdoptTrustedStore(xmlSecKeyDataStorePtr store, HCERTSTORE trustedStore) { + xmlSecMSCngX509StoreCtxPtr ctx; + int ret; + + xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); + xmlSecAssert2( trustedStore != NULL, -1); + + ctx = xmlSecMSCngX509StoreGetCtx(store); + xmlSecAssert2(ctx != NULL, -1); + xmlSecAssert2(ctx->trusted != NULL, -1); + + ret = CertAddStoreToCollection(ctx->trusted , trustedStore , CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG , 3); + if(ret == FALSE) { + xmlSecMSCngLastError("CertAddStoreToCollection", + xmlSecKeyDataStoreGetName(store)); + return(-1); + } + + return(0); +} + +/** + * xmlSecMSCngX509StoreAdoptUntrustedStore: + * @store: the pointer to X509 key data store klass. + * @untrustedStore: the pointer to certs store. + * + * Adds @trustedStore to the list of untrusted certs stores. + * + * Returns: 0 on success or a negative value if an error occurs. + */ +int +xmlSecMSCngX509StoreAdoptUntrustedStore(xmlSecKeyDataStorePtr store, HCERTSTORE untrustedStore) { + xmlSecMSCngX509StoreCtxPtr ctx; + int ret; + + xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); + xmlSecAssert2(untrustedStore != NULL, -1); + + ctx = xmlSecMSCngX509StoreGetCtx(store); + xmlSecAssert2(ctx != NULL, -1); + xmlSecAssert2(ctx->untrusted != NULL, -1); + + ret = CertAddStoreToCollection(ctx->untrusted, untrustedStore, CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG , 2); + if(ret == FALSE) { + xmlSecMSCngLastError("CertAddStoreToCollection", + xmlSecKeyDataStoreGetName(store)); + return(-1); + } + + return(0); +} + +static int +xmlSecMSCngX509StoreInitialize(xmlSecKeyDataStorePtr store) { + int ret; + xmlSecMSCngX509StoreCtxPtr ctx; + + xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); + ctx = xmlSecMSCngX509StoreGetCtx(store); + xmlSecAssert2(ctx != NULL, -1); + + memset(ctx, 0, sizeof(xmlSecMSCngX509StoreCtx)); + + /* create a trusted store that will be a collection of other stores */ + ctx->trusted = CertOpenStore( + CERT_STORE_PROV_COLLECTION, + 0, + 0, + 0, + NULL); + if(ctx->trusted == NULL) { + xmlSecMSCngLastError("CertOpenStore", xmlSecKeyDataStoreGetName(store)); + return(-1); + } + + /* create an actual trusted store */ + ctx->trustedMemStore = CertOpenStore( + CERT_STORE_PROV_MEMORY, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, + CERT_STORE_CREATE_NEW_FLAG, + NULL); + if(ctx->trustedMemStore == NULL) { + xmlSecMSCngLastError("CertOpenStore", xmlSecKeyDataStoreGetName(store)); + xmlSecMSCngX509StoreFinalize(store); + return(-1); + } + + /* add the store to the trusted collection */ + ret = CertAddStoreToCollection( + ctx->trusted, + ctx->trustedMemStore, + CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, + 1); + if(ret == 0) { + xmlSecMSCngLastError("CertAddStoreToCollection", xmlSecKeyDataStoreGetName(store)); + xmlSecMSCngX509StoreFinalize(store); + return(-1); + } + + /* create an untrusted store that will be a collection of other stores */ + ctx->untrusted = CertOpenStore( + CERT_STORE_PROV_COLLECTION, + 0, + 0, + 0, + NULL); + if(ctx->untrusted == NULL) { + xmlSecMSCngLastError("CertOpenStore", xmlSecKeyDataStoreGetName(store)); + xmlSecMSCngX509StoreFinalize(store); + return(-1); + } + + /* create an actual untrusted store */ + ctx->untrustedMemStore = CertOpenStore( + CERT_STORE_PROV_MEMORY, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, + CERT_STORE_CREATE_NEW_FLAG, + NULL); + if(ctx->untrustedMemStore == NULL) { + xmlSecMSCngLastError("CertOpenStore", xmlSecKeyDataStoreGetName(store)); + xmlSecMSCngX509StoreFinalize(store); + return(-1); + } + + /* add the store to the untrusted collection */ + ret = CertAddStoreToCollection( + ctx->untrusted, + ctx->untrustedMemStore, + CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, + 1); + if(ret == 0) { + xmlSecMSCngLastError("CertAddStoreToCollection", xmlSecKeyDataStoreGetName(store)); + xmlSecMSCngX509StoreFinalize(store); + return(-1); + } + + return(0); +} + +static xmlSecKeyDataStoreKlass xmlSecMSCngX509StoreKlass = { + sizeof(xmlSecKeyDataStoreKlass), + xmlSecMSCngX509StoreSize, + + /* data */ + xmlSecNameX509Store, /* const xmlChar* name; */ + + /* constructors/destructor */ + xmlSecMSCngX509StoreInitialize, /* xmlSecKeyDataStoreInitializeMethod initialize; */ + xmlSecMSCngX509StoreFinalize, /* xmlSecKeyDataStoreFinalizeMethod finalize; */ + + /* reserved for the future */ + NULL, /* void* reserved0; */ + NULL, /* void* reserved1; */ +}; + +/** + * xmlSecMSCngX509StoreGetKlass: + * + * The MSCng X509 certificates key data store klass. + * + * Returns: pointer to MSCng X509 certificates key data store klass. + */ +xmlSecKeyDataStoreId +xmlSecMSCngX509StoreGetKlass(void) { + return(&xmlSecMSCngX509StoreKlass); +} + +/** + * xmlSecMSCngX509StoreAdoptCert: + * @store: the pointer to X509 key data store klass. + * @cert: the pointer to PCCERT_CONTEXT X509 certificate. + * @type: the certificate type (trusted/untrusted). + * + * Adds trusted (root) or untrusted certificate to the store. + * + * Returns: 0 on success or a negative value if an error occurs. + */ +int +xmlSecMSCngX509StoreAdoptCert(xmlSecKeyDataStorePtr store, PCCERT_CONTEXT pCert, xmlSecKeyDataType type) { + xmlSecMSCngX509StoreCtxPtr ctx; + HCERTSTORE hCertStore; + int ret; + + xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); + xmlSecAssert2(pCert != NULL, -1); + + ctx = xmlSecMSCngX509StoreGetCtx(store); + xmlSecAssert2(ctx != NULL, -1); + xmlSecAssert2(ctx->trusted != NULL, -1); + + if(type == xmlSecKeyDataTypeTrusted) { + hCertStore = ctx->trusted; + } else if(type == xmlSecKeyDataTypeNone) { + hCertStore = ctx->untrusted; + } else { + xmlSecNotImplementedError(NULL); + return(-1); + } + + xmlSecAssert2(hCertStore != NULL, -1); + ret = CertAddCertificateContextToStore( + hCertStore, + pCert, + CERT_STORE_ADD_ALWAYS, + NULL); + if(ret == FALSE) { + xmlSecMSCngLastError("CertAddCertificateContextToStore", xmlSecKeyDataStoreGetName(store)); + return(-1); + } + + return(0); +} + +/** + * xmlSecMSCngCheckRevocation: + * @store: may contain a CRL + * @cert: the certificate that is revoked (or not) + * + * Checks if @cert is in the CRL of @store. + * + * Returns: 0 on success or a negative value if an errors occurs. + */ +static int +xmlSecMSCngCheckRevocation(HCERTSTORE store, PCCERT_CONTEXT cert) { + PCCRL_CONTEXT crlCtx = NULL; + PCRL_ENTRY crlEntry = NULL; + int ret; + + xmlSecAssert2(store != NULL, -1); + xmlSecAssert2(cert != NULL, -1); + + while((crlCtx = CertEnumCRLsInStore(store, crlCtx)) != NULL) { + ret = CertFindCertificateInCRL(cert, + crlCtx, + 0, + NULL, + &crlEntry); + if(ret == 0) { + continue; + } + if(crlEntry == NULL) { + continue; + } + + xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, NULL, + "cert found in CRL"); + return(-1); + } + + return(0); +} + +/** + * xmlSecMSCngX509StoreContainsCert: + * @store: the certificate store + * @subject: the name of the subject or issuer to find + * @cert: the certificate + * + * Determines if cert is found in store. + * + * Returns: 1 and 0 if find does or does not succeed, or a negative value if an + * error occurs. + */ +static int +xmlSecMSCngX509StoreContainsCert(HCERTSTORE store, CERT_NAME_BLOB* name, + PCCERT_CONTEXT cert) +{ + PCCERT_CONTEXT issuerCert = NULL; + DWORD flags; + int ret; + + xmlSecAssert2(store != NULL, -1); + xmlSecAssert2(name != NULL, -1); + xmlSecAssert2(cert != NULL, -1); + + issuerCert = CertFindCertificateInStore(store, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, + CERT_FIND_SUBJECT_NAME, + name, + NULL); + if(issuerCert != NULL) { + flags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG; + ret = CertVerifySubjectCertificateContext(cert, + issuerCert, + &flags); + if(ret == 0) { + xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, + NULL, + "CertVerifySubjectCertificateContext"); + CertFreeCertificateContext(issuerCert); + return(-1); + } + CertFreeCertificateContext(issuerCert); + return(1); + } + + return(0); +} + +static int +xmlSecMSCngVerifyCertTime(PCCERT_CONTEXT cert, LPFILETIME time) { + xmlSecAssert2(cert != NULL, -1); + xmlSecAssert2(cert->pCertInfo != NULL, -1); + xmlSecAssert2(time != NULL, -1); + + if(CompareFileTime(&cert->pCertInfo->NotBefore, time) == 1) { + xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, + NULL, + "CompareFileTime"); + return(-1); + } + + if(CompareFileTime(&cert->pCertInfo->NotAfter, time) == -1) { + xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, + NULL, + "CompareFileTime"); + return(-1); + } + + return(0); +} + +/** + * xmlSecMSCngX509StoreVerifyCertificateOwn: + * @cert: the certificate to verify. + * @time: pointer to FILETIME that we are interested in + * @trustedStore: trusted certificates added via xmlSecMSCngX509StoreAdoptCert(). + * @certStore: the untrusted certificates stack. + * @store: key data store, name used for error reporting only. + * + * Verifies @cert based on trustedStore (ignoring system trusted certificates). + * + * Returns: 0 on success or a negative value if an error occurs. + */ +static int +xmlSecMSCngX509StoreVerifyCertificateOwn(PCCERT_CONTEXT cert, + FILETIME* time, HCERTSTORE trustedStore, HCERTSTORE untrustedStore, HCERTSTORE certStore, + xmlSecKeyDataStorePtr store) { + PCCERT_CONTEXT issuerCert = NULL; + DWORD flags; + int ret; + + xmlSecAssert2(cert != NULL, -1); + xmlSecAssert2(trustedStore != NULL, -1); + xmlSecAssert2(certStore != NULL, -1); + xmlSecAssert2(store != NULL, -1); + + ret = xmlSecMSCngVerifyCertTime(cert, time); + if(ret < 0) { + xmlSecInternalError("xmlSecMSCngVerifyCertTime", + xmlSecKeyDataStoreGetName(store)); + return(-1); + } + + ret = xmlSecMSCngCheckRevocation(certStore, cert); + if(ret < 0) { + xmlSecInternalError("xmlSecMSCngCheckRevocation", + xmlSecKeyDataStoreGetName(store)); + return(-1); + } + + /* does trustedStore contain cert directly? */ + ret = xmlSecMSCngX509StoreContainsCert(trustedStore, + &cert->pCertInfo->Subject, cert); + if(ret < 0) { + xmlSecInternalError("xmlSecMSCngX509StoreContainsCert", + xmlSecKeyDataStoreGetName(store)); + return(-1); + } + if(ret == 1) { + return(0); + } + + /* does trustedStore contain the issuer cert? */ + ret = xmlSecMSCngX509StoreContainsCert(trustedStore, + &cert->pCertInfo->Issuer, cert); + if(ret < 0) { + xmlSecInternalError("xmlSecMSCngX509StoreContainsCert", + xmlSecKeyDataStoreGetName(store)); + return(-1); + } + if(ret == 1) { + return(0); + } + + /* is cert self-signed? no recursion in that case */ + if(CertCompareCertificateName(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + &cert->pCertInfo->Subject, + &cert->pCertInfo->Issuer)) { + return(-1); + } + + /* the same checks recursively for the issuer cert in certStore */ + issuerCert = CertFindCertificateInStore(certStore, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, + CERT_FIND_SUBJECT_NAME, + &cert->pCertInfo->Issuer, + NULL); + if(issuerCert != NULL) { + flags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG; + ret = CertVerifySubjectCertificateContext(cert, issuerCert, &flags); + if(ret == 0) { + xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, + xmlSecKeyDataStoreGetName(store), + "CertVerifySubjectCertificateContext"); + CertFreeCertificateContext(issuerCert); + return(-1); + } + + ret = xmlSecMSCngX509StoreVerifyCertificateOwn(issuerCert, time, + trustedStore, untrustedStore, certStore, store); + if(ret < 0) { + xmlSecInternalError("xmlSecMSCngX509StoreVerifyCertificateOwn", xmlSecKeyDataStoreGetName(store)); + CertFreeCertificateContext(issuerCert); + return(-1); + } + CertFreeCertificateContext(issuerCert); + return(0); + } + + /* the same checks recursively for the issuer cert in untrustedStore */ + issuerCert = CertFindCertificateInStore(untrustedStore, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, + CERT_FIND_SUBJECT_NAME, + &cert->pCertInfo->Issuer, + NULL); + if(issuerCert != NULL) { + flags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG; + ret = CertVerifySubjectCertificateContext(cert, issuerCert, &flags); + if(ret == 0) { + xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, + xmlSecKeyDataStoreGetName(store), + "CertVerifySubjectCertificateContext"); + CertFreeCertificateContext(issuerCert); + return(-1); + } + + ret = xmlSecMSCngX509StoreVerifyCertificateOwn(issuerCert, time, + trustedStore, untrustedStore, certStore, store); + if(ret < 0) { + xmlSecInternalError("xmlSecMSCngX509StoreVerifyCertificateOwn", xmlSecKeyDataStoreGetName(store)); + CertFreeCertificateContext(issuerCert); + return(-1); + } + CertFreeCertificateContext(issuerCert); + return(0); + } + + return(-1); +} + +/** + * xmlSecMSCngX509StoreVerifyCertificateSystem: + * @cert: the certificate we check + * @time: pointer to FILETIME that we are interested in + * @untrustedStore: untrusted certificates added via API + * @docStore: untrusted certificates/CRLs extracted from a document + * + * Verifies @cert based on system trusted certificates. + * + * Returns: 0 on success or a negative value if an error occurs. + */ +static int +xmlSecMSCngX509StoreVerifyCertificateSystem(PCCERT_CONTEXT cert, + FILETIME* time, HCERTSTORE untrustedStore, HCERTSTORE docStore) { + PCCERT_CHAIN_CONTEXT pChainContext = NULL; + CERT_CHAIN_PARA chainPara; + HCERTSTORE chainStore = NULL; + int res = -1; + int ret; + + /* initialize data structures */ + memset(&chainPara, 0, sizeof(CERT_CHAIN_PARA)); + chainPara.cbSize = sizeof(CERT_CHAIN_PARA); + + /* create additional store for CertGetCertificateChain() */ + chainStore = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0, 0, NULL); + if(chainStore == NULL) { + xmlSecMSCngLastError("CertOpenStore", NULL); + goto end; + } + + ret = CertAddStoreToCollection(chainStore, docStore, 0, 0); + if(ret == FALSE) { + xmlSecMSCngLastError("CertAddStoreToCollection", NULL); + goto end; + } + + ret = CertAddStoreToCollection(chainStore, untrustedStore, 0, 0); + if(ret == FALSE) { + xmlSecMSCngLastError("CertAddStoreToCollection", NULL); + goto end; + } + + /* build a chain using CertGetCertificateChain + and the certificate retrieved */ + ret = CertGetCertificateChain(NULL, cert, time, chainStore, &chainPara, + CERT_CHAIN_REVOCATION_CHECK_CHAIN, NULL, &pChainContext); + if(ret == FALSE) { + xmlSecMSCngLastError("CertGetCertificateChain", NULL); + goto end; + } + + if (pChainContext->TrustStatus.dwErrorStatus == CERT_TRUST_REVOCATION_STATUS_UNKNOWN) { + CertFreeCertificateChain(pChainContext); + pChainContext = NULL; + ret = CertGetCertificateChain(NULL, cert, time, chainStore, &chainPara, + CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, NULL, + &pChainContext); + if(ret == FALSE) { + xmlSecMSCngLastError("CertGetCertificateChain", NULL); + goto end; + } + } + + if(pChainContext->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR) { + res = 0; + } + +end: + if(pChainContext != NULL) { + CertFreeCertificateChain(pChainContext); + } + + if(chainStore != NULL) { + CertCloseStore(chainStore, 0); + } + + return (res); +} + +/** + * xmlSecMSCngUnixTimeToFileTime: + * + * Converts time_t into FILETIME timestamp. See xmlSecMSCngX509CertGetTime() + * for details. + */ +static int +xmlSecMSCngUnixTimeToFileTime(time_t in, LPFILETIME out) { + /* 64-bit value */ + LONGLONG ll; + + xmlSecAssert2(out != NULL, -1); + + /* seconds -> 100 nanoseconds */ + /* 1970-01-01 epoch -> 1601-01-01 epoch */ + ll = Int32x32To64(in, 10000000) + 116444736000000000; + out->dwLowDateTime = (DWORD)ll; + out->dwHighDateTime = ll >> 32; + + return(0); +} + +/** + * xmlSecMSCngX509StoreVerifyCertificate: + * @store: the pointer to X509 certificate context store klass. + * @cert: the certificate to verify. + * @certStore: the untrusted certificates stack. + * @keyInfoCtx: the pointer to <dsig:KeyInfo/> element processing context. + * + * Verifies @cert. + * + * Returns: 0 on success or a negative value if an error occurs. + */ +static int +xmlSecMSCngX509StoreVerifyCertificate(xmlSecKeyDataStorePtr store, + PCCERT_CONTEXT cert, HCERTSTORE certStore, xmlSecKeyInfoCtx* keyInfoCtx) { + xmlSecMSCngX509StoreCtxPtr ctx; + FILETIME fTime; + int ret; + + xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); + xmlSecAssert2(cert != NULL, -1); + xmlSecAssert2(cert->pCertInfo != NULL, -1); + xmlSecAssert2(certStore != NULL, -1); + xmlSecAssert2(keyInfoCtx != NULL, -1); + + ctx = xmlSecMSCngX509StoreGetCtx(store); + xmlSecAssert2(ctx != NULL, -1); + xmlSecAssert2(ctx->trusted != NULL, -1); + + if(keyInfoCtx->certsVerificationTime > 0) { + xmlSecMSCngUnixTimeToFileTime(keyInfoCtx->certsVerificationTime, + &fTime); + } else { + /* current time */ + GetSystemTimeAsFileTime(&fTime); + } + + /* verify based on the own trusted certificates */ + ret = xmlSecMSCngX509StoreVerifyCertificateOwn(cert, + &fTime, ctx->trusted, ctx->untrusted, certStore, store); + if(ret >= 0) { + return(0); + } + + /* verify based on the system certificates */ + ret = xmlSecMSCngX509StoreVerifyCertificateSystem(cert, + &fTime, ctx->untrusted, certStore); + if(ret >= 0) { + return(0); + } + + return(-1); +} + +/** + * xmlSecMSCngX509StoreVerify: + * @store: the pointer to X509 certificate context store klass. + * @certs: the untrusted certificates stack. + * @keyInfoCtx: the pointer to <dsig:KeyInfo/> element processing context. + * + * Verifies @certs list. + * + * Returns: pointer to the first verified certificate from @certs. + */ +PCCERT_CONTEXT +xmlSecMSCngX509StoreVerify(xmlSecKeyDataStorePtr store, HCERTSTORE certs, + xmlSecKeyInfoCtx* keyInfoCtx) { + PCCERT_CONTEXT cert = NULL; + int ret; + + xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), NULL); + xmlSecAssert2(certs != NULL, NULL); + xmlSecAssert2(keyInfoCtx != NULL, NULL); + + while((cert = CertEnumCertificatesInStore(certs, cert)) != NULL) { + PCCERT_CONTEXT foundCert = NULL; + int skip = 0; + xmlSecAssert2(cert->pCertInfo != NULL, NULL); + + /* is cert the issuer of a certificate in certs? if so, skip it */ + do { + foundCert = CertFindCertificateInStore(certs, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, + CERT_FIND_ISSUER_NAME, + &(cert->pCertInfo->Subject), + foundCert); + /* don't skip self-signed certificates */ + if((foundCert != NULL) && + !CertCompareCertificateName(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + &(foundCert->pCertInfo->Subject), + &(foundCert->pCertInfo->Issuer))) { + skip = 1; + } + } while(skip == 0 && foundCert != NULL); + if(foundCert != NULL) { + CertFreeCertificateContext(foundCert); + } + if(skip == 0) { + if((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS) != 0) { + return(cert); + } + + /* need to actually verify the certificate */ + ret = xmlSecMSCngX509StoreVerifyCertificate(store, cert, certs, keyInfoCtx); + if(ret == 0) { + return(cert); + } + } + } + + return(NULL); +} + +static LPTSTR +xmlSecMSCngX509GetCertName(const xmlChar* name) { + xmlChar* copy; + xmlChar* p; + LPTSTR res; + + xmlSecAssert2(name != 0, NULL); + + /* emailAddress= results in an error, E= does not, so replace the former */ + copy = xmlStrdup(name); + if(copy == NULL) { + xmlSecStrdupError(name, NULL); + return(NULL); + } + + while((p = (xmlChar*)xmlStrstr(copy, BAD_CAST "emailAddress=")) != NULL) { + memcpy(p, " E=", 13); + } + + res = xmlSecWin32ConvertUtf8ToTstr(copy); + if(res == NULL) { + xmlSecInternalError("xmlSecWin32ConvertUtf8ToTstr", NULL); + xmlFree(copy); + return(NULL); + } + + xmlFree(copy); + + return(res); +} + +static BYTE* +xmlSecMSCngCertStrToName(DWORD dwCertEncodingType, LPTSTR pszX500, DWORD dwStrType, DWORD* len) { + BYTE* str = NULL; + LPCTSTR ppszError = NULL; + + xmlSecAssert2(pszX500 != NULL, NULL); + xmlSecAssert2(len != NULL, NULL); + + if (!CertStrToName(dwCertEncodingType, pszX500, dwStrType, + NULL, NULL, len, &ppszError)) { + /* this might not be an error, string might just not exist */ + return(NULL); + } + + str = (BYTE *)xmlMalloc(sizeof(TCHAR) * ((*len) + 1)); + if(str == NULL) { + xmlSecMallocError(sizeof(TCHAR) * ((*len) + 1), NULL); + return(NULL); + } + memset(str, 0, (*len) + 1); + + if (!CertStrToName(dwCertEncodingType, pszX500, dwStrType, + NULL, str, len, NULL)) { + xmlSecMSCngLastError("CertStrToName", NULL); + xmlFree(str); + return(NULL); + } + + return(str); +} + +static PCCERT_CONTEXT +xmlSecMSCngX509FindCertByIssuer(HCERTSTORE store, LPTSTR wcIssuer, + xmlSecBnPtr issuerSerialBn, DWORD dwCertEncodingType) { + xmlSecAssert2(store != NULL, NULL); + xmlSecAssert2(wcIssuer != NULL, NULL); + xmlSecAssert2(issuerSerialBn != NULL, NULL); + + PCCERT_CONTEXT res = NULL; + CERT_INFO certInfo; + BYTE* bdata; + DWORD len; + + + xmlSecAssert2(store != NULL, NULL); + xmlSecAssert2(wcIssuer != NULL, NULL); + xmlSecAssert2(issuerSerialBn != NULL, NULL); + + certInfo.SerialNumber.cbData = xmlSecBnGetSize(issuerSerialBn); + certInfo.SerialNumber.pbData = xmlSecBnGetData(issuerSerialBn); + + + /* CASE 1: UTF8, DN */ + if (NULL == res) { + bdata = xmlSecMSCngCertStrToName(dwCertEncodingType, + wcIssuer, + CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG | CERT_OID_NAME_STR, + &len); + if(bdata != NULL) { + certInfo.Issuer.cbData = len; + certInfo.Issuer.pbData = bdata; + + res = CertFindCertificateInStore(store, + dwCertEncodingType, + 0, + CERT_FIND_SUBJECT_CERT, + &certInfo, + NULL); + xmlFree(bdata); + } + } + + /* CASE 2: UTF8, REVERSE DN */ + if (NULL == res) { + bdata = xmlSecMSCngCertStrToName(dwCertEncodingType, + wcIssuer, + CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG | CERT_OID_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, + &len); + if(bdata != NULL) { + certInfo.Issuer.cbData = len; + certInfo.Issuer.pbData = bdata; + + res = CertFindCertificateInStore(store, + dwCertEncodingType, + 0, + CERT_FIND_SUBJECT_CERT, + &certInfo, + NULL); + xmlFree(bdata); + } + } + + /* CASE 3: UNICODE, DN */ + if (NULL == res) { + bdata = xmlSecMSCngCertStrToName(dwCertEncodingType, + wcIssuer, + CERT_OID_NAME_STR, + &len); + if(bdata != NULL) { + certInfo.Issuer.cbData = len; + certInfo.Issuer.pbData = bdata; + + res = CertFindCertificateInStore(store, + dwCertEncodingType, + 0, + CERT_FIND_SUBJECT_CERT, + &certInfo, + NULL); + xmlFree(bdata); + } + } + + /* CASE 4: UNICODE, REVERSE DN */ + if (NULL == res) { + bdata = xmlSecMSCngCertStrToName(dwCertEncodingType, + wcIssuer, + CERT_OID_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, + &len); + if(bdata != NULL) { + certInfo.Issuer.cbData = len; + certInfo.Issuer.pbData = bdata; + + res = CertFindCertificateInStore(store, + dwCertEncodingType, + 0, + CERT_FIND_SUBJECT_CERT, + &certInfo, + NULL); + xmlFree(bdata); + } + } + + return (res); +} + +static PCCERT_CONTEXT +xmlSecMSCngX509FindCert(HCERTSTORE store, xmlChar* subjectName, + xmlChar* issuerName, xmlChar* issuerSerial, xmlChar* ski) { + PCCERT_CONTEXT cert; + int ret; + + xmlSecAssert2(store != 0, NULL); + + if(subjectName != NULL) { + LPTSTR wcSubjectName; + + wcSubjectName = xmlSecMSCngX509GetCertName(subjectName); + if(wcSubjectName == NULL) { + xmlSecInternalError("xmlSecMSCngX509GetCertName", NULL); + return(NULL); + } + + cert = xmlSecMSCngX509FindCertBySubject(store, wcSubjectName, + PKCS_7_ASN_ENCODING | X509_ASN_ENCODING); + xmlFree(wcSubjectName); + + return(cert); + } + + if(issuerName != NULL && issuerSerial != NULL) { + xmlSecBn issuerSerialBn; + LPTSTR wcIssuerName = NULL; + + ret = xmlSecBnInitialize(&issuerSerialBn, 0); + if(ret < 0) { + xmlSecInternalError("xmlSecBnInitialize", NULL); + return(NULL); + } + + ret = xmlSecBnFromDecString(&issuerSerialBn, issuerSerial); + if(ret < 0) { + xmlSecInternalError("xmlSecBnFromDecString", NULL); + xmlSecBnFinalize(&issuerSerialBn); + return(NULL); + } + + /* xmlSecMSCngX509FindCertByIssuer() wants this in the opposite order */ + ret = xmlSecBnReverse(&issuerSerialBn); + if(ret < 0) { + xmlSecInternalError("xmlSecBnReverse", NULL); + xmlSecBnFinalize(&issuerSerialBn); + return(NULL); + } + + wcIssuerName = xmlSecMSCngX509GetCertName(issuerName); + if(wcIssuerName == NULL) { + xmlSecInternalError("xmlSecMSCngX509GetCertName", NULL); + xmlSecBnFinalize(&issuerSerialBn); + return(NULL); + } + + cert = xmlSecMSCngX509FindCertByIssuer(store, wcIssuerName, + &issuerSerialBn, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING); + xmlFree(wcIssuerName); + xmlSecBnFinalize(&issuerSerialBn); + + return(cert); + } + + if(ski != NULL) { + CRYPT_HASH_BLOB blob; + xmlChar* binSki; + int binSkiLen; + + binSki = xmlStrdup(ski); + if(binSki == NULL) { + xmlSecStrdupError(ski, NULL); + return (NULL); + } + + /* base64 decode "in place" */ + binSkiLen = xmlSecBase64Decode(binSki, (xmlSecByte*)binSki, xmlStrlen(binSki)); + if(binSkiLen < 0) { + xmlSecInternalError("xmlSecBase64Decode", NULL); + xmlFree(binSki); + return(NULL); + } + + blob.pbData = binSki; + blob.cbData = binSkiLen; + cert = CertFindCertificateInStore(store, + PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, + 0, + CERT_FIND_KEY_IDENTIFIER, + &blob, + NULL); + xmlFree(binSki); + + return(cert); + } + + return(NULL); +} + +/** + * xmlSecMSCngX509StoreFindCert: + * @store: the pointer to X509 key data store klass. + * @subjectName: the desired certificate name. + * @issuerName: the desired certificate issuer name. + * @issuerSerial: the desired certificate issuer serial number. + * @ski: the desired certificate SKI. + * @keyInfoCtx: the pointer to <dsig:KeyInfo/> element processing context. + * + * Searches @store for a certificate that matches given criteria. + * + * Returns: pointer to found certificate or NULL if certificate is not found + * or an error occurs. + */ +PCCERT_CONTEXT +xmlSecMSCngX509StoreFindCert(xmlSecKeyDataStorePtr store, xmlChar *subjectName, + xmlChar *issuerName, xmlChar *issuerSerial, xmlChar *ski, + xmlSecKeyInfoCtx* keyInfoCtx) { + xmlSecMSCngX509StoreCtxPtr ctx; + PCCERT_CONTEXT cert = NULL; + + xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), NULL); + xmlSecAssert2(keyInfoCtx != NULL, NULL); + + ctx = xmlSecMSCngX509StoreGetCtx(store); + xmlSecAssert2(ctx != NULL, NULL); + + /* search untrusted certs store */ + if(ctx->untrusted != NULL) { + cert = xmlSecMSCngX509FindCert(ctx->untrusted, subjectName, + issuerName, issuerSerial, ski); + } + + /* search trusted certs store */ + if(cert == NULL && ctx->trusted != NULL) { + cert = xmlSecMSCngX509FindCert(ctx->trusted, subjectName, + issuerName, issuerSerial, ski); + } + + return(cert); +} + +/** + * xmlSecMSCngX509FindCertBySubject: + * @store: the pointer to certs store + * @wcSubject: the cert subject (Unicode) + * @dwCertEncodingType: the cert encoding type + * + * Searches for a cert with given @subject in the @store + * + * Returns: cert handle on success or NULL otherwise + */ +PCCERT_CONTEXT +xmlSecMSCngX509FindCertBySubject(HCERTSTORE store, LPTSTR wcSubject, + DWORD dwCertEncodingType) { + PCCERT_CONTEXT res = NULL; + CERT_NAME_BLOB cnb; + BYTE* bdata; + DWORD len; + + xmlSecAssert2(store != NULL, NULL); + xmlSecAssert2(wcSubject != NULL, NULL); + + /* CASE 1: UTF8, DN */ + if(res == NULL) { + bdata = xmlSecMSCngCertStrToName(dwCertEncodingType, + wcSubject, + CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG | CERT_OID_NAME_STR, + &len); + if(bdata != NULL) { + cnb.cbData = len; + cnb.pbData = bdata; + + res = CertFindCertificateInStore(store, + dwCertEncodingType, + 0, + CERT_FIND_SUBJECT_NAME, + &cnb, + NULL); + xmlFree(bdata); + } + } + + /* CASE 2: UTF8, REVERSE DN */ + if(res == NULL) { + bdata = xmlSecMSCngCertStrToName(dwCertEncodingType, + wcSubject, + CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG | CERT_OID_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, + &len); + if(bdata != NULL) { + cnb.cbData = len; + cnb.pbData = bdata; + + res = CertFindCertificateInStore(store, + dwCertEncodingType, + 0, + CERT_FIND_SUBJECT_NAME, + &cnb, + NULL); + xmlFree(bdata); + } + } + + /* CASE 3: UNICODE, DN */ + if(res == NULL) { + bdata = xmlSecMSCngCertStrToName(dwCertEncodingType, + wcSubject, + CERT_OID_NAME_STR, + &len); + if(bdata != NULL) { + cnb.cbData = len; + cnb.pbData = bdata; + + res = CertFindCertificateInStore(store, + dwCertEncodingType, + 0, + CERT_FIND_SUBJECT_NAME, + &cnb, + NULL); + xmlFree(bdata); + } + } + + /* CASE 4: UNICODE, REVERSE DN */ + if(res == NULL) { + bdata = xmlSecMSCngCertStrToName(dwCertEncodingType, + wcSubject, + CERT_OID_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, + &len); + if(bdata != NULL) { + cnb.cbData = len; + cnb.pbData = bdata; + + res = CertFindCertificateInStore(store, + dwCertEncodingType, + 0, + CERT_FIND_SUBJECT_NAME, + &cnb, + NULL); + xmlFree(bdata); + } + } + + return(res); +} + +#endif /* XMLSEC_NO_X509 */ |