|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995.
//
// File: cert.c
//
// Contents:
//
// Classes:
//
// Functions:
//
// History: 09-23-97 jbanes LSA integration stuff.
// 01-05-98 jbanes Use WinVerifyTrust to validate certs.
// 03-26-99 jbanes Fix CTL support, bug #303246
//
//----------------------------------------------------------------------------
#include <stdlib.h>
#include <spbase.h>
#include <ssl2msg.h>
#include <ssl3msg.h>
#include <wincrypt.h>
#include <oidenc.h>
#include <softpub.h>
#define CERT_HEADER_CONST "certificate"
#define CERT_HEADER_LENGTH 11
#define CERT_HEADER_OFFSET 6
SP_STATUS SchGetTrustedRoots( HCERTSTORE *phClientRootStore);
BOOL WINAPI SchCreateWorldStore ( IN HCERTSTORE hRoot, IN DWORD cAdditionalStore, IN HCERTSTORE* rghAdditionalStore, OUT HCERTSTORE* phWorld);
BOOL IsCertSelfSigned(PCCERT_CONTEXT pCertContext);
// typedef struct _OIDPROVMAP
// {
// LPSTR szOid;
// DWORD dwExchSpec;
// DWORD dwCertType; // used for SSL 3.0 client auth
// } OIDPROVMAP, *POIDPROVMAP;
OIDPROVMAP g_CertTypes[] = { { szOID_RSA_RSA, SP_EXCH_RSA_PKCS1, SSL3_CERTTYPE_RSA_SIGN}, { szOID_RSA_MD2RSA, SP_EXCH_RSA_PKCS1, SSL3_CERTTYPE_RSA_SIGN}, { szOID_RSA_MD4RSA, SP_EXCH_RSA_PKCS1, SSL3_CERTTYPE_RSA_SIGN}, { szOID_RSA_MD5RSA, SP_EXCH_RSA_PKCS1, SSL3_CERTTYPE_RSA_SIGN}, { szOID_RSA_SHA1RSA, SP_EXCH_RSA_PKCS1, SSL3_CERTTYPE_RSA_SIGN}, { szOID_OIWSEC_dsa, SP_EXCH_DH_PKCS3, SSL3_CERTTYPE_DSS_SIGN}, { szOID_X957_DSA, SP_EXCH_DH_PKCS3, SSL3_CERTTYPE_DSS_SIGN}, };
DWORD g_cCertTypes = sizeof(g_CertTypes)/sizeof(OIDPROVMAP);
DWORD MapOidToKeyExch(LPSTR szOid) { DWORD i;
for(i = 0; i < g_cCertTypes; i++) { if(strcmp(szOid, g_CertTypes[i].szOid) == 0) { return g_CertTypes[i].dwExchSpec; } } return 0; }
DWORD MapOidToCertType(LPSTR szOid) { DWORD i;
for(i = 0; i < g_cCertTypes; i++) { if(strcmp(szOid, g_CertTypes[i].szOid) == 0) { return g_CertTypes[i].dwCertType; } } return 0; }
// SPLoadCertificate takes a string of encoded cert bytes
// and decodes them into the local certificate cache. It
// then returns the first certificate of the group.
SP_STATUS SPLoadCertificate( DWORD fProtocol, DWORD dwCertEncodingType, PUCHAR pCertificate, DWORD cbCertificate, PCCERT_CONTEXT *ppCertContext) { HCERTSTORE hCertStore = NULL; PCCERT_CONTEXT pCertContext = NULL;
PBYTE pbCurrentRaw; DWORD cbCurrentRaw; BOOL fLeafCert; SP_STATUS pctRet;
//
// Dereference the cert that we are replacing.
//
if(ppCertContext == NULL) { return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
if(*ppCertContext != NULL) { CertFreeCertificateContext(*ppCertContext); } *ppCertContext = NULL;
//
// Create an in-memory certificate store.
//
hCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0); if(hCertStore == NULL) { SP_LOG_RESULT(GetLastError()); return SEC_E_INSUFFICIENT_MEMORY; }
fLeafCert = TRUE; pbCurrentRaw = pCertificate; cbCurrentRaw = cbCertificate;
do {
//
// Skip to beginning of certificate.
//
if((fProtocol & SP_PROT_SSL3TLS1) && cbCurrentRaw > 3) { // SSL3 style cert chain, where the length
// of each cert is prepended.
pbCurrentRaw += 3; cbCurrentRaw -= 3; }
// Skip past the "certificate" header
if((cbCurrentRaw > (CERT_HEADER_OFFSET + CERT_HEADER_LENGTH)) && (memcmp(pbCurrentRaw + CERT_HEADER_OFFSET, CERT_HEADER_CONST, CERT_HEADER_LENGTH) == 0)) { pbCurrentRaw += CERT_HEADER_OFFSET + CERT_HEADER_LENGTH; cbCurrentRaw -= CERT_HEADER_OFFSET + CERT_HEADER_LENGTH; }
//
// Decode this certificate context.
//
if(!CertAddEncodedCertificateToStore(hCertStore, dwCertEncodingType, pbCurrentRaw, cbCurrentRaw, CERT_STORE_ADD_USE_EXISTING, &pCertContext)) { SP_LOG_RESULT(GetLastError()); pctRet = PCT_ERR_BAD_CERTIFICATE; goto cleanup; }
pbCurrentRaw += pCertContext->cbCertEncoded; if(cbCurrentRaw < pCertContext->cbCertEncoded) { pctRet = SP_LOG_RESULT(PCT_ERR_BAD_CERTIFICATE); goto cleanup; } cbCurrentRaw -= pCertContext->cbCertEncoded;
if(fLeafCert) { fLeafCert = FALSE; *ppCertContext = pCertContext; } else { CertFreeCertificateContext(pCertContext); } pCertContext = NULL;
} while(cbCurrentRaw);
pctRet = PCT_ERR_OK;
cleanup:
CertCloseStore(hCertStore, 0);
if(pctRet != PCT_ERR_OK) { if(pCertContext) { CertFreeCertificateContext(pCertContext); } if(*ppCertContext) { CertFreeCertificateContext(*ppCertContext); *ppCertContext = NULL; } }
return pctRet; }
SP_STATUS SPPublicKeyFromCert( PCCERT_CONTEXT pCert, PUBLICKEY ** ppKey, ExchSpec * pdwExchSpec) { PCERT_PUBLIC_KEY_INFO pPubKeyInfo; PUBLICKEY * pPublicKey; DWORD dwExchSpec; DWORD cbBlob; SP_STATUS pctRet;
//
// Log the subject and issuer names.
//
LogDistinguishedName(DEB_TRACE, "Subject: %s\n", pCert->pCertInfo->Subject.pbData, pCert->pCertInfo->Subject.cbData);
LogDistinguishedName(DEB_TRACE, "Issuer: %s\n", pCert->pCertInfo->Issuer.pbData, pCert->pCertInfo->Issuer.cbData);
//
// Determine type of public key embedded in the certificate.
//
pPubKeyInfo = &pCert->pCertInfo->SubjectPublicKeyInfo; if(pPubKeyInfo == NULL) { return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
dwExchSpec = MapOidToKeyExch(pPubKeyInfo->Algorithm.pszObjId);
if(dwExchSpec == 0) { return PCT_INT_UNKNOWN_CREDENTIAL; }
//
// Build public key blob from encoded public key.
//
switch(dwExchSpec) { case SP_EXCH_RSA_PKCS1: pctRet = RsaPublicKeyFromCert(pPubKeyInfo, NULL, &cbBlob); if(pctRet != PCT_ERR_OK) { return pctRet; }
pPublicKey = SPExternalAlloc(sizeof(PUBLICKEY) + cbBlob); if(pPublicKey == NULL) { return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); }
pPublicKey->pPublic = (BLOBHEADER *)(pPublicKey + 1); pPublicKey->cbPublic = cbBlob;
pctRet = RsaPublicKeyFromCert(pPubKeyInfo, pPublicKey->pPublic, &pPublicKey->cbPublic); if(pctRet != PCT_ERR_OK) { SPExternalFree(pPublicKey); return pctRet; } break;
case SP_EXCH_DH_PKCS3: pctRet = DssPublicKeyFromCert(pPubKeyInfo, NULL, &cbBlob); if(pctRet != PCT_ERR_OK) { return pctRet; }
pPublicKey = SPExternalAlloc(sizeof(PUBLICKEY) + cbBlob); if(pPublicKey == NULL) { return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); }
pPublicKey->pPublic = (BLOBHEADER *)(pPublicKey + 1); pPublicKey->cbPublic = cbBlob;
pctRet = DssPublicKeyFromCert(pPubKeyInfo, pPublicKey->pPublic, &pPublicKey->cbPublic); if(pctRet != PCT_ERR_OK) { SPExternalFree(pPublicKey); return pctRet; } break;
default: return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
//
// Set function outputs.
//
*ppKey = pPublicKey;
if(pdwExchSpec) { *pdwExchSpec = dwExchSpec; }
return PCT_ERR_OK; }
SP_STATUS SPSerializeCertificate( DWORD dwProtocol, // in
BOOL fBuildChain, // in
PBYTE * ppCertChain, // out
DWORD * pcbCertChain, // out
PCCERT_CONTEXT pCertContext, // in
DWORD dwChainingFlags) // in
{ PCCERT_CHAIN_CONTEXT pChainContext = NULL; CERT_CHAIN_PARA ChainPara; PCERT_SIMPLE_CHAIN pSimpleChain; PCCERT_CONTEXT pCurrentCert;
BOOL fSuccess = FALSE; PBYTE pbCertChain; DWORD cbCertChain; DWORD i; SP_STATUS pctRet; BOOL fImpersonating = FALSE;
SP_BEGIN("SPSerializeCertificate");
if(pcbCertChain == NULL) { SP_RETURN( SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR)); }
if(fBuildChain) { ZeroMemory(&ChainPara, sizeof(ChainPara)); ChainPara.cbSize = sizeof(ChainPara);
fImpersonating = SslImpersonateClient();
if(!(fSuccess = CertGetCertificateChain( NULL, pCertContext, NULL, NULL, &ChainPara, dwChainingFlags, NULL, &pChainContext))) { DebugLog((DEB_WARN, "Error 0x%x returned by CertGetCertificateChain!\n", GetLastError())); pChainContext = NULL; }
if(fImpersonating) { RevertToSelf(); fImpersonating = FALSE; } }
if(!fSuccess) { //
// Send the leaf certificate only.
//
// Compute size of chain.
cbCertChain = pCertContext->cbCertEncoded; if(dwProtocol & SP_PROT_SSL3TLS1) { cbCertChain += CB_SSL3_CERT_VECTOR; }
// Allocate memory for chain.
if(ppCertChain == NULL) { *pcbCertChain = cbCertChain; pctRet = PCT_ERR_OK; goto cleanup; } else if(*ppCertChain == NULL) { *ppCertChain = SPExternalAlloc(cbCertChain); if(*ppCertChain == NULL) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; } } else if(*pcbCertChain < cbCertChain) { pctRet = SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL); goto cleanup; } *pcbCertChain = cbCertChain;
// Place chain in output buffer.
pbCertChain = *ppCertChain;
if(dwProtocol & SP_PROT_SSL3TLS1) { pbCertChain[0] = MS24BOF(pCertContext->cbCertEncoded); pbCertChain[1] = MSBOF(pCertContext->cbCertEncoded); pbCertChain[2] = LSBOF(pCertContext->cbCertEncoded); pbCertChain += CB_SSL3_CERT_VECTOR; } CopyMemory(pbCertChain, pCertContext->pbCertEncoded, pCertContext->cbCertEncoded);
pctRet = PCT_ERR_OK; goto cleanup; }
//
// Compute size of chain.
//
pSimpleChain = pChainContext->rgpChain[0]; cbCertChain = 0;
for(i = 0; i < pSimpleChain->cElement; i++) { pCurrentCert = pSimpleChain->rgpElement[i]->pCertContext; if(i > 0) { // Verify that this is not a root certificate.
if(CertCompareCertificateName(pCurrentCert->dwCertEncodingType, &pCurrentCert->pCertInfo->Issuer, &pCurrentCert->pCertInfo->Subject)) { break; } }
cbCertChain += pCurrentCert->cbCertEncoded; if(dwProtocol & SP_PROT_SSL3TLS1) { cbCertChain += CB_SSL3_CERT_VECTOR; } }
//
// Allocate memory for chain.
//
if(ppCertChain == NULL) { *pcbCertChain = cbCertChain; pctRet = PCT_ERR_OK; goto cleanup; } else if(*ppCertChain == NULL) { *ppCertChain = SPExternalAlloc(cbCertChain); if(*ppCertChain == NULL) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; } } else if(*pcbCertChain < cbCertChain) { pctRet = SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL); goto cleanup; } *pcbCertChain = cbCertChain;
//
// Place chain in output buffer.
//
pbCertChain = *ppCertChain;
for(i = 0; i < pSimpleChain->cElement; i++) { pCurrentCert = pSimpleChain->rgpElement[i]->pCertContext; if(i > 0) { // Verify that this is not a root certificate.
if(CertCompareCertificateName(pCurrentCert->dwCertEncodingType, &pCurrentCert->pCertInfo->Issuer, &pCurrentCert->pCertInfo->Subject)) { break; } }
if(dwProtocol & SP_PROT_SSL3TLS1) { pbCertChain[0] = MS24BOF(pCurrentCert->cbCertEncoded); pbCertChain[1] = MSBOF(pCurrentCert->cbCertEncoded); pbCertChain[2] = LSBOF(pCurrentCert->cbCertEncoded); pbCertChain += CB_SSL3_CERT_VECTOR; } CopyMemory(pbCertChain, pCurrentCert->pbCertEncoded, pCurrentCert->cbCertEncoded); pbCertChain += pCurrentCert->cbCertEncoded; }
SP_ASSERT(*ppCertChain + cbCertChain == pbCertChain);
pctRet = PCT_ERR_OK;
cleanup:
if(pChainContext) { CertFreeCertificateChain(pChainContext); }
SP_RETURN(pctRet); }
/*****************************************************************************/ SP_STATUS ExtractIssuerNamesFromStore( HCERTSTORE hStore, // in
PBYTE pbIssuers, // out
DWORD *pcbIssuers) // in, out
{ DWORD cbCurIssuerLen = 0; DWORD cbIssuerLen = *pcbIssuers; PBYTE pbCurIssuer = pbIssuers; PCCERT_CONTEXT pCurrent = NULL; SECURITY_STATUS scRet; BOOL fIsAllowed;
// Initialize output to zero.
*pcbIssuers = 0;
while(TRUE) { pCurrent = CertEnumCertificatesInStore(hStore, pCurrent); if(pCurrent == NULL) break;
// Is this a client-auth certificate?
scRet = SPCheckKeyUsage(pCurrent, szOID_PKIX_KP_CLIENT_AUTH, FALSE, &fIsAllowed); if(scRet != SEC_E_OK) { continue; } if(!fIsAllowed) { continue; }
cbCurIssuerLen += 2 + pCurrent->pCertInfo->Subject.cbData;
// Are we writing?
if(pbIssuers) { if(cbCurIssuerLen > cbIssuerLen) { // Memory overrun
CertFreeCertificateContext(pCurrent); return SP_LOG_RESULT(PCT_INT_DATA_OVERFLOW); }
pbCurIssuer[0] = MSBOF(pCurrent->pCertInfo->Subject.cbData); pbCurIssuer[1] = LSBOF(pCurrent->pCertInfo->Subject.cbData); pbCurIssuer += 2;
CopyMemory(pbCurIssuer, pCurrent->pCertInfo->Subject.pbData, pCurrent->pCertInfo->Subject.cbData); pbCurIssuer += pCurrent->pCertInfo->Subject.cbData; } }
*pcbIssuers = cbCurIssuerLen;
return PCT_ERR_OK; }
/*****************************************************************************/ SP_STATUS GetDefaultIssuers( PBYTE pbIssuers, // out
DWORD *pcbIssuers) // in, out
{ HCERTSTORE hStore; SP_STATUS pctRet;
pctRet = SchGetTrustedRoots(&hStore); if(pctRet != PCT_ERR_OK) { return pctRet; }
pctRet = ExtractIssuerNamesFromStore(hStore, pbIssuers, pcbIssuers); if(pctRet != PCT_ERR_OK) { CertCloseStore(hStore, 0); return pctRet; }
CertCloseStore(hStore, 0); return PCT_ERR_OK; }
SP_STATUS SchGetTrustedRoots( HCERTSTORE *phClientRootStore) { HTTPSPolicyCallbackData polHttps; CERT_CHAIN_POLICY_PARA PolicyPara; CERT_CHAIN_POLICY_STATUS PolicyStatus; CERT_CHAIN_PARA ChainPara; PCCERT_CHAIN_CONTEXT pChainContext = NULL; LPSTR pszUsage;
PCCERT_CONTEXT pCertContext; HCERTSTORE hClientRootStore = 0; HCERTSTORE hRootStore = 0; HCERTSTORE hWorldStore = 0; SP_STATUS Status = SEC_E_OK; BOOL fImpersonating = FALSE;
// Open output store.
hClientRootStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0); if(hClientRootStore == NULL) { //SP_LOG_RESULT(GetLastError());
Status = SEC_E_INSUFFICIENT_MEMORY; goto cleanup; }
fImpersonating = SslImpersonateClient();
// Open root store.
hRootStore = CertOpenSystemStore(0, "ROOT"); if(hRootStore == NULL) { DebugLog((DEB_WARN, "Error 0x%x opening root store\n", GetLastError())); }
// Create world store.
if(!SchCreateWorldStore(hRootStore, 0, NULL, &hWorldStore)) { DebugLog((DEB_ERROR, "Error 0x%x creating world store\n", GetLastError())); goto cleanup; }
// Enumerate the certificates in the world store, looking
// for trusted roots. This approach will automatically take
// advantage of any CTLs that are installed on the system.
pCertContext = NULL; while(TRUE) { pCertContext = CertEnumCertificatesInStore(hWorldStore, pCertContext); if(pCertContext == NULL) break;
if(!IsCertSelfSigned(pCertContext)) { continue; }
pszUsage = szOID_PKIX_KP_CLIENT_AUTH;
ZeroMemory(&ChainPara, sizeof(ChainPara)); ChainPara.cbSize = sizeof(ChainPara); ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; ChainPara.RequestedUsage.Usage.cUsageIdentifier = 1; ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = &pszUsage;
if(!CertGetCertificateChain( NULL, pCertContext, NULL, 0, &ChainPara, 0, NULL, &pChainContext)) { SP_LOG_RESULT(GetLastError()); continue; }
// Set up validate chain structures.
ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData)); polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData); polHttps.dwAuthType = AUTHTYPE_CLIENT; polHttps.fdwChecks = 0; polHttps.pwszServerName = NULL;
ZeroMemory(&PolicyStatus, sizeof(PolicyStatus)); PolicyStatus.cbSize = sizeof(PolicyStatus);
ZeroMemory(&PolicyPara, sizeof(PolicyPara)); PolicyPara.cbSize = sizeof(PolicyPara); PolicyPara.pvExtraPolicyPara= &polHttps; PolicyPara.dwFlags = CERT_CHAIN_POLICY_IGNORE_ALL_NOT_TIME_VALID_FLAGS;
// Validate chain
if(!CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus)) { SP_LOG_RESULT(GetLastError()); CertFreeCertificateChain(pChainContext); continue; }
if(PolicyStatus.dwError) { // Certificate did not validate, move on to the next one.
CertFreeCertificateChain(pChainContext); continue; }
CertFreeCertificateChain(pChainContext);
// Add the root certificate to the list of trusted ones.
if(!CertAddCertificateContextToStore(hClientRootStore, pCertContext, CERT_STORE_ADD_USE_EXISTING, NULL)) { SP_LOG_RESULT(GetLastError()); } }
cleanup:
if(hRootStore) { CertCloseStore(hRootStore, 0); }
if(hWorldStore) { CertCloseStore(hWorldStore, 0); }
if(fImpersonating) { RevertToSelf(); }
if(Status == SEC_E_OK) { *phClientRootStore = hClientRootStore; }
return Status; }
//+---------------------------------------------------------------------------
//
// Function: ChainCreateCollectionIncludingCtlCertificates
//
// Synopsis: create a collection which includes the source store hStore and
// any CTL certificates from it
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainCreateCollectionIncludingCtlCertificates ( IN HCERTSTORE hStore, OUT HCERTSTORE* phCollection ) { BOOL fResult = FALSE; HCERTSTORE hCollection; PCCTL_CONTEXT pCtlContext = NULL; HCERTSTORE hCtlStore;
hCollection = CertOpenStore( CERT_STORE_PROV_COLLECTION, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL );
if ( hCollection == NULL ) { return( FALSE ); }
fResult = CertAddStoreToCollection( hCollection, hStore, 0, 0 );
while ( ( fResult == TRUE ) && ( ( pCtlContext = CertEnumCTLsInStore( hStore, pCtlContext ) ) != NULL ) ) { hCtlStore = CertOpenStore( CERT_STORE_PROV_MSG, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, 0, pCtlContext->hCryptMsg );
if ( hCtlStore != NULL ) { fResult = CertAddStoreToCollection( hCollection, hCtlStore, 0, 0 );
CertCloseStore( hCtlStore, 0 ); } }
if ( fResult == TRUE ) { *phCollection = hCollection; } else { CertCloseStore( hCollection, 0 ); }
return( fResult ); }
BOOL WINAPI SchCreateWorldStore ( IN HCERTSTORE hRoot, IN DWORD cAdditionalStore, IN HCERTSTORE* rghAdditionalStore, OUT HCERTSTORE* phWorld) { BOOL fResult; HCERTSTORE hWorld; HCERTSTORE hStore, hCtl; DWORD cCount;
hWorld = CertOpenStore( CERT_STORE_PROV_COLLECTION, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL );
if ( hWorld == NULL ) { return( FALSE ); }
fResult = CertAddStoreToCollection( hWorld, hRoot, 0, 0 );
for ( cCount = 0; ( cCount < cAdditionalStore ) && ( fResult == TRUE ); cCount++ ) { fResult = CertAddStoreToCollection( hWorld, rghAdditionalStore[ cCount ], 0, 0 ); }
if ( fResult == TRUE ) { hStore = CertOpenSystemStore(0, "trust"); if( hStore != NULL ) { if(ChainCreateCollectionIncludingCtlCertificates(hStore, &hCtl)) { if(!CertAddStoreToCollection( hWorld, hCtl, 0, 0 )) { DebugLog((DEB_WARN, "Error 0x%x adding CTL collection\n", GetLastError())); } CertCloseStore( hCtl, 0 ); } else { DebugLog((DEB_WARN, "Error 0x%x creating CTL collection\n", GetLastError())); } CertCloseStore( hStore, 0 ); } }
if ( fResult == TRUE ) { hStore = CertOpenSystemStore(0, "ca"); if ( hStore != NULL ) { fResult = CertAddStoreToCollection( hWorld, hStore, 0, 0 ); CertCloseStore( hStore, 0 ); } else { fResult = FALSE; } }
if ( fResult == TRUE ) { hStore = CertOpenSystemStore(0, "my"); if ( hStore != NULL ) { fResult = CertAddStoreToCollection( hWorld, hStore, 0, 0 ); CertCloseStore( hStore, 0 ); } else { fResult = FALSE; } }
if ( fResult == TRUE ) { *phWorld = hWorld; } else { CertCloseStore( hWorld, 0 ); }
return( fResult ); }
BOOL IsCertSelfSigned(PCCERT_CONTEXT pCertContext) { // Compare subject and issuer names.
if(pCertContext->pCertInfo->Subject.cbData == pCertContext->pCertInfo->Issuer.cbData) { if(memcmp(pCertContext->pCertInfo->Subject.pbData, pCertContext->pCertInfo->Issuer.pbData, pCertContext->pCertInfo->Issuer.cbData) == 0) { return TRUE; } }
return FALSE; }
SECURITY_STATUS MapWinTrustError( SECURITY_STATUS Status, SECURITY_STATUS DefaultError, DWORD dwIgnoreErrors) { if((Status == CRYPT_E_NO_REVOCATION_CHECK) && (dwIgnoreErrors & CRED_FLAG_IGNORE_NO_REVOCATION_CHECK)) { DebugLog((DEB_WARN, "MapWinTrustError: Ignoring CRYPT_E_NO_REVOCATION_CHECK\n")); Status = STATUS_SUCCESS; } if((Status == CRYPT_E_REVOCATION_OFFLINE) && (dwIgnoreErrors & CRED_FLAG_IGNORE_REVOCATION_OFFLINE)) { DebugLog((DEB_WARN, "MapWinTrustError: Ignoring CRYPT_E_REVOCATION_OFFLINE\n")); Status = STATUS_SUCCESS; }
if(HRESULT_FACILITY(Status) == FACILITY_SECURITY) { return (Status); }
switch(Status) { case ERROR_SUCCESS: return SEC_E_OK;
// Expired certificate.
case CERT_E_EXPIRED: case CERT_E_VALIDITYPERIODNESTING: return SEC_E_CERT_EXPIRED;
// Unknown CA
case CERT_E_UNTRUSTEDROOT: case CERT_E_UNTRUSTEDCA: return SEC_E_UNTRUSTED_ROOT;
// Certificate revoked.
case CERT_E_REVOKED: return CRYPT_E_REVOKED;
// Target name doesn't match name in certificate.
case CERT_E_CN_NO_MATCH: return SEC_E_WRONG_PRINCIPAL;
// Certificate contains wrong EKU.
case CERT_E_WRONG_USAGE: return SEC_E_CERT_WRONG_USAGE;
// Some other error.
default: if(DefaultError) { return DefaultError; } else { return SEC_E_CERT_UNKNOWN; } } }
NTSTATUS VerifyClientCertificate( PCCERT_CONTEXT pCertContext, DWORD dwCertFlags, DWORD dwIgnoreErrors, LPCSTR pszPolicyOID, PCCERT_CHAIN_CONTEXT *ppChainContext) // optional
{ HTTPSPolicyCallbackData polHttps; CERT_CHAIN_POLICY_PARA PolicyPara; CERT_CHAIN_POLICY_STATUS PolicyStatus; CERT_CHAIN_PARA ChainPara; PCCERT_CHAIN_CONTEXT pChainContext = NULL; DWORD Status; LPSTR pszUsage; BOOL fImpersonating = FALSE;
//
// Build certificate chain.
//
fImpersonating = SslImpersonateClient();
pszUsage = szOID_PKIX_KP_CLIENT_AUTH;
ZeroMemory(&ChainPara, sizeof(ChainPara)); ChainPara.cbSize = sizeof(ChainPara); ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; ChainPara.RequestedUsage.Usage.cUsageIdentifier = 1; ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = &pszUsage;
if(!CertGetCertificateChain( NULL, // hChainEngine
pCertContext, // pCertContext
NULL, // pTime
pCertContext->hCertStore, // hAdditionalStore
&ChainPara, // pChainPara
dwCertFlags, // dwFlags
NULL, // pvReserved
&pChainContext)) // ppChainContext
{ Status = SP_LOG_RESULT(GetLastError()); goto cleanup; }
//
// Validate certificate chain.
//
if(pszPolicyOID == CERT_CHAIN_POLICY_NT_AUTH) { ZeroMemory(&PolicyPara, sizeof(PolicyPara)); PolicyPara.cbSize = sizeof(PolicyPara); PolicyPara.dwFlags = BASIC_CONSTRAINTS_CERT_CHAIN_POLICY_END_ENTITY_FLAG; } else { ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData)); polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData); polHttps.dwAuthType = AUTHTYPE_CLIENT; polHttps.fdwChecks = 0;
ZeroMemory(&PolicyPara, sizeof(PolicyPara)); PolicyPara.cbSize = sizeof(PolicyPara); PolicyPara.pvExtraPolicyPara = &polHttps; }
ZeroMemory(&PolicyStatus, sizeof(PolicyStatus)); PolicyStatus.cbSize = sizeof(PolicyStatus);
if(!CertVerifyCertificateChainPolicy( pszPolicyOID, pChainContext, &PolicyPara, &PolicyStatus)) { Status = SP_LOG_RESULT(GetLastError()); goto cleanup; }
#if DBG
if(PolicyStatus.dwError) { DebugLog((DEB_WARN, "CertVerifyCertificateChainPolicy returned 0x%x\n", PolicyStatus.dwError)); } #endif
Status = MapWinTrustError(PolicyStatus.dwError, 0, dwIgnoreErrors);
if(Status) { DebugLog((DEB_ERROR, "MapWinTrustError returned 0x%x\n", Status)); goto cleanup; }
Status = STATUS_SUCCESS;
if(ppChainContext != NULL) { *ppChainContext = pChainContext; pChainContext = NULL; }
cleanup:
if(pChainContext) { CertFreeCertificateChain(pChainContext); }
if(fImpersonating) RevertToSelf();
return Status; }
NTSTATUS AutoVerifyServerCertificate(PSPContext pContext) { PSPCredentialGroup pCredGroup; DWORD dwCertFlags = 0; DWORD dwIgnoreErrors = 0;
if(pContext->Flags & CONTEXT_FLAG_MANUAL_CRED_VALIDATION) { return STATUS_SUCCESS; }
pCredGroup = pContext->pCredGroup; if(pCredGroup == NULL) { return SP_LOG_RESULT(SEC_E_INTERNAL_ERROR); }
if(pCredGroup->dwFlags & CRED_FLAG_REVCHECK_END_CERT) dwCertFlags |= CERT_CHAIN_REVOCATION_CHECK_END_CERT; if(pCredGroup->dwFlags & CRED_FLAG_REVCHECK_CHAIN) dwCertFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN; if(pCredGroup->dwFlags & CRED_FLAG_REVCHECK_CHAIN_EXCLUDE_ROOT) dwCertFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; if(pCredGroup->dwFlags & CRED_FLAG_IGNORE_NO_REVOCATION_CHECK) dwIgnoreErrors |= CRED_FLAG_IGNORE_NO_REVOCATION_CHECK; if(pCredGroup->dwFlags & CRED_FLAG_IGNORE_REVOCATION_OFFLINE) dwIgnoreErrors |= CRED_FLAG_IGNORE_REVOCATION_OFFLINE;
return VerifyServerCertificate(pContext, dwCertFlags, dwIgnoreErrors); }
NTSTATUS VerifyServerCertificate( PSPContext pContext, DWORD dwCertFlags, DWORD dwIgnoreErrors) { HTTPSPolicyCallbackData polHttps; CERT_CHAIN_POLICY_PARA PolicyPara; CERT_CHAIN_POLICY_STATUS PolicyStatus; CERT_CHAIN_PARA ChainPara; PCCERT_CHAIN_CONTEXT pChainContext = NULL;
#define SERVER_USAGE_COUNT 3
LPSTR rgszUsages[SERVER_USAGE_COUNT] = { szOID_PKIX_KP_SERVER_AUTH, szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE }; NTSTATUS Status; PWSTR pwszServerName = NULL; PSPCredentialGroup pCred; PCCERT_CONTEXT pCertContext; BOOL fImpersonating = FALSE;
pCred = pContext->pCredGroup; if(pCred == NULL) { return SEC_E_INTERNAL_ERROR; }
pCertContext = pContext->RipeZombie->pRemoteCert; if(pCertContext == NULL) { return SEC_E_INTERNAL_ERROR; }
//
// Build certificate chain.
//
fImpersonating = SslImpersonateClient();
ZeroMemory(&ChainPara, sizeof(ChainPara)); ChainPara.cbSize = sizeof(ChainPara); ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; ChainPara.RequestedUsage.Usage.cUsageIdentifier = SERVER_USAGE_COUNT; ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
if(!CertGetCertificateChain( NULL, // hChainEngine
pCertContext, // pCertContext
NULL, // pTime
pCertContext->hCertStore, // hAdditionalStore
&ChainPara, // pChainPara
dwCertFlags, // dwFlags
NULL, // pvReserved
&pChainContext)) // ppChainContext
{ Status = SP_LOG_RESULT(GetLastError()); goto cleanup; }
//
// Validate certificate chain.
//
if(!(pCred->dwFlags & CRED_FLAG_NO_SERVERNAME_CHECK)) { pwszServerName = pContext->RipeZombie->szCacheID;
if(pwszServerName == NULL || lstrlenW(pwszServerName) == 0) { Status = SP_LOG_RESULT(SEC_E_WRONG_PRINCIPAL); goto cleanup; } }
ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData)); polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData); polHttps.dwAuthType = AUTHTYPE_SERVER; polHttps.fdwChecks = 0; polHttps.pwszServerName = pwszServerName;
ZeroMemory(&PolicyPara, sizeof(PolicyPara)); PolicyPara.cbSize = sizeof(PolicyPara); PolicyPara.pvExtraPolicyPara = &polHttps;
ZeroMemory(&PolicyStatus, sizeof(PolicyStatus)); PolicyStatus.cbSize = sizeof(PolicyStatus);
if(!CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus)) { Status = SP_LOG_RESULT(GetLastError()); goto cleanup; }
#if DBG
if(PolicyStatus.dwError) { DebugLog((DEB_WARN, "CertVerifyCertificateChainPolicy returned 0x%x\n", PolicyStatus.dwError)); } #endif
Status = MapWinTrustError(PolicyStatus.dwError, 0, dwIgnoreErrors);
if(Status) { DebugLog((DEB_ERROR, "MapWinTrustError returned 0x%x\n", Status)); LogBogusServerCertEvent(pCertContext, pwszServerName, Status); goto cleanup; }
Status = STATUS_SUCCESS;
cleanup:
if(pChainContext) { CertFreeCertificateChain(pChainContext); }
if(fImpersonating) RevertToSelf();
return Status; }
SECURITY_STATUS SPCheckKeyUsage( PCCERT_CONTEXT pCertContext, PSTR pszUsage, BOOL fOnCertOnly, PBOOL pfIsAllowed) { PCERT_ENHKEY_USAGE pKeyUsage; DWORD cbKeyUsage; DWORD j; BOOL fFound; DWORD dwFlags = 0;
if(fOnCertOnly) { dwFlags = CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG; }
// Determine size of usage information.
if(!CertGetEnhancedKeyUsage(pCertContext, dwFlags, NULL, &cbKeyUsage)) { // No usage information exists.
*pfIsAllowed = TRUE; return SEC_E_OK; }
SafeAllocaAllocate(pKeyUsage, cbKeyUsage); if(pKeyUsage == NULL) { *pfIsAllowed = FALSE; return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); }
// Read key usage information.
if(!CertGetEnhancedKeyUsage(pCertContext, dwFlags, pKeyUsage, &cbKeyUsage)) { // No usage information exists.
SafeAllocaFree(pKeyUsage); *pfIsAllowed = TRUE; return SEC_E_OK; }
if(pKeyUsage->cUsageIdentifier == 0 && GetLastError() == CRYPT_E_NOT_FOUND) { // No usage information exists.
SafeAllocaFree(pKeyUsage); *pfIsAllowed = TRUE; return SEC_E_OK; }
// See if requested usage is in list of supported usages.
fFound = FALSE; for(j = 0; j < pKeyUsage->cUsageIdentifier; j++) { if(strcmp(pszUsage, pKeyUsage->rgpszUsageIdentifier[j]) == 0) { fFound = TRUE; break; } }
SafeAllocaFree(pKeyUsage);
if(!fFound) { // Usage extensions found, but doesn't list ours.
*pfIsAllowed = FALSE; } else { *pfIsAllowed = TRUE; }
return SEC_E_OK; }
|