1443 lines
38 KiB
C
1443 lines
38 KiB
C
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// 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_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 + strlen(CERT_HEADER_CONST))) &&
|
||
|
(memcmp(pbCurrentRaw + CERT_HEADER_OFFSET, CERT_HEADER_CONST, strlen(CERT_HEADER_CONST)) == 0))
|
||
|
{
|
||
|
pbCurrentRaw += CERT_HEADER_OFFSET + strlen(CERT_HEADER_CONST);
|
||
|
cbCurrentRaw -= CERT_HEADER_OFFSET + strlen(CERT_HEADER_CONST);
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// 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;
|
||
|
DWORD 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
SP_STATUS
|
||
|
MapWinTrustError(DWORD Status, DWORD 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;
|
||
|
|
||
|
// 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 };
|
||
|
|
||
|
DWORD 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;
|
||
|
}
|
||
|
|
||
|
pKeyUsage = SPExternalAlloc(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.
|
||
|
SPExternalFree(pKeyUsage);
|
||
|
*pfIsAllowed = TRUE;
|
||
|
return SEC_E_OK;
|
||
|
}
|
||
|
|
||
|
if(pKeyUsage->cUsageIdentifier == 0 && GetLastError() == CRYPT_E_NOT_FOUND)
|
||
|
{
|
||
|
// No usage information exists.
|
||
|
SPExternalFree(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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SPExternalFree(pKeyUsage);
|
||
|
|
||
|
if(!fFound)
|
||
|
{
|
||
|
// Usage extensions found, but doesn't list ours.
|
||
|
*pfIsAllowed = FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*pfIsAllowed = TRUE;
|
||
|
}
|
||
|
|
||
|
return SEC_E_OK;
|
||
|
}
|
||
|
|