crypt32: Consider alternate issuers when building chains.
This commit is contained in:
parent
1fc8c60788
commit
5e674f3580
|
@ -602,6 +602,312 @@ static BOOL CRYPT_BuildCandidateChainFromCert(HCERTCHAINENGINE hChainEngine,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Makes and returns a copy of chain, up to and including element iElement. */
|
||||||
|
static PCERT_SIMPLE_CHAIN CRYPT_CopySimpleChainToElement(
|
||||||
|
PCERT_SIMPLE_CHAIN chain, DWORD iElement)
|
||||||
|
{
|
||||||
|
PCERT_SIMPLE_CHAIN copy = CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN));
|
||||||
|
|
||||||
|
if (copy)
|
||||||
|
{
|
||||||
|
memset(copy, 0, sizeof(CERT_SIMPLE_CHAIN));
|
||||||
|
copy->cbSize = sizeof(CERT_SIMPLE_CHAIN);
|
||||||
|
copy->rgpElement =
|
||||||
|
CryptMemAlloc((iElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
|
||||||
|
if (copy->rgpElement)
|
||||||
|
{
|
||||||
|
DWORD i;
|
||||||
|
BOOL ret = TRUE;
|
||||||
|
|
||||||
|
memset(copy->rgpElement, 0,
|
||||||
|
(iElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
|
||||||
|
for (i = 0; ret && i <= iElement; i++)
|
||||||
|
{
|
||||||
|
PCERT_CHAIN_ELEMENT element =
|
||||||
|
CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
|
||||||
|
|
||||||
|
if (element)
|
||||||
|
{
|
||||||
|
memcpy(element, chain->rgpElement[i],
|
||||||
|
sizeof(CERT_CHAIN_ELEMENT));
|
||||||
|
element->pCertContext = CertDuplicateCertificateContext(
|
||||||
|
chain->rgpElement[i]->pCertContext);
|
||||||
|
/* Reset the trust status of the copied element, it'll get
|
||||||
|
* rechecked after the new chain is done.
|
||||||
|
*/
|
||||||
|
memset(&element->TrustStatus, 0, sizeof(CERT_TRUST_STATUS));
|
||||||
|
copy->rgpElement[copy->cElement++] = element;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = FALSE;
|
||||||
|
}
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
for (i = 0; i <= iElement; i++)
|
||||||
|
CryptMemFree(copy->rgpElement[i]);
|
||||||
|
CryptMemFree(copy->rgpElement);
|
||||||
|
CryptMemFree(copy);
|
||||||
|
copy = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CryptMemFree(copy);
|
||||||
|
copy = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CRYPT_FreeLowerQualityChains(PCertificateChain chain)
|
||||||
|
{
|
||||||
|
DWORD i;
|
||||||
|
|
||||||
|
for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
|
||||||
|
CertFreeCertificateChain(chain->context.rgpLowerQualityChainContext[i]);
|
||||||
|
CryptMemFree(chain->context.rgpLowerQualityChainContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CRYPT_FreeChainContext(PCertificateChain chain)
|
||||||
|
{
|
||||||
|
DWORD i;
|
||||||
|
|
||||||
|
CRYPT_FreeLowerQualityChains(chain);
|
||||||
|
for (i = 0; i < chain->context.cChain; i++)
|
||||||
|
CRYPT_FreeSimpleChain(chain->context.rgpChain[i]);
|
||||||
|
CryptMemFree(chain->context.rgpChain);
|
||||||
|
CertCloseStore(chain->world, 0);
|
||||||
|
CryptMemFree(chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Makes and returns a copy of chain, up to and including element iElement of
|
||||||
|
* simple chain iChain.
|
||||||
|
*/
|
||||||
|
static PCertificateChain CRYPT_CopyChainToElement(PCertificateChain chain,
|
||||||
|
DWORD iChain, DWORD iElement)
|
||||||
|
{
|
||||||
|
PCertificateChain copy = CryptMemAlloc(sizeof(CertificateChain));
|
||||||
|
|
||||||
|
if (copy)
|
||||||
|
{
|
||||||
|
copy->ref = 1;
|
||||||
|
copy->world = CertDuplicateStore(chain->world);
|
||||||
|
copy->context.cbSize = sizeof(CERT_CHAIN_CONTEXT);
|
||||||
|
/* Leave the trust status of the copied chain unset, it'll get
|
||||||
|
* rechecked after the new chain is done.
|
||||||
|
*/
|
||||||
|
memset(©->context.TrustStatus, 0, sizeof(CERT_TRUST_STATUS));
|
||||||
|
copy->context.cLowerQualityChainContext = 0;
|
||||||
|
copy->context.rgpLowerQualityChainContext = NULL;
|
||||||
|
copy->context.fHasRevocationFreshnessTime = FALSE;
|
||||||
|
copy->context.dwRevocationFreshnessTime = 0;
|
||||||
|
copy->context.rgpChain = CryptMemAlloc(
|
||||||
|
(iChain + 1) * sizeof(PCERT_SIMPLE_CHAIN));
|
||||||
|
if (copy->context.rgpChain)
|
||||||
|
{
|
||||||
|
BOOL ret = TRUE;
|
||||||
|
DWORD i;
|
||||||
|
|
||||||
|
memset(copy->context.rgpChain, 0,
|
||||||
|
(iChain + 1) * sizeof(PCERT_SIMPLE_CHAIN));
|
||||||
|
if (iChain)
|
||||||
|
{
|
||||||
|
for (i = 0; ret && iChain && i < iChain - 1; i++)
|
||||||
|
{
|
||||||
|
copy->context.rgpChain[i] =
|
||||||
|
CRYPT_CopySimpleChainToElement(chain->context.rgpChain[i],
|
||||||
|
chain->context.rgpChain[i]->cElement - 1);
|
||||||
|
if (!copy->context.rgpChain[i])
|
||||||
|
ret = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
i = 0;
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
copy->context.rgpChain[i] =
|
||||||
|
CRYPT_CopySimpleChainToElement(chain->context.rgpChain[i],
|
||||||
|
iElement);
|
||||||
|
if (!copy->context.rgpChain[i])
|
||||||
|
ret = FALSE;
|
||||||
|
}
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
CRYPT_FreeChainContext(copy);
|
||||||
|
copy = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
copy->context.cChain = iChain + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CryptMemFree(copy);
|
||||||
|
copy = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PCertificateChain CRYPT_BuildAlternateContextFromChain(
|
||||||
|
HCERTCHAINENGINE hChainEngine, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
|
||||||
|
PCertificateChain chain)
|
||||||
|
{
|
||||||
|
PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
|
||||||
|
PCertificateChain alternate;
|
||||||
|
|
||||||
|
TRACE("(%p, %p, %p, %p)\n", hChainEngine, pTime, hAdditionalStore, chain);
|
||||||
|
|
||||||
|
/* Always start with the last "lower quality" chain to ensure a consistent
|
||||||
|
* order of alternate creation:
|
||||||
|
*/
|
||||||
|
if (chain->context.cLowerQualityChainContext)
|
||||||
|
chain = (PCertificateChain)chain->context.rgpLowerQualityChainContext[
|
||||||
|
chain->context.cLowerQualityChainContext - 1];
|
||||||
|
/* A chain with only one element can't have any alternates */
|
||||||
|
if (chain->context.cChain <= 1 && chain->context.rgpChain[0]->cElement <= 1)
|
||||||
|
alternate = NULL;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DWORD i, j, flags;
|
||||||
|
PCCERT_CONTEXT alternateIssuer = NULL;
|
||||||
|
|
||||||
|
alternate = NULL;
|
||||||
|
for (i = 0; !alternateIssuer && i < chain->context.cChain; i++)
|
||||||
|
for (j = 0; !alternateIssuer &&
|
||||||
|
j < chain->context.rgpChain[i]->cElement - 1; j++)
|
||||||
|
{
|
||||||
|
PCCERT_CONTEXT subject =
|
||||||
|
chain->context.rgpChain[i]->rgpElement[j]->pCertContext;
|
||||||
|
PCCERT_CONTEXT prevIssuer = CertDuplicateCertificateContext(
|
||||||
|
chain->context.rgpChain[i]->rgpElement[j + 1]->pCertContext);
|
||||||
|
|
||||||
|
flags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG;
|
||||||
|
alternateIssuer = CertGetIssuerCertificateFromStore(
|
||||||
|
prevIssuer->hCertStore, subject, prevIssuer, &flags);
|
||||||
|
}
|
||||||
|
if (alternateIssuer)
|
||||||
|
{
|
||||||
|
i--;
|
||||||
|
j--;
|
||||||
|
alternate = CRYPT_CopyChainToElement(chain, i, j);
|
||||||
|
if (alternate)
|
||||||
|
{
|
||||||
|
BOOL ret = CRYPT_AddCertToSimpleChain(engine,
|
||||||
|
alternate->context.rgpChain[i], alternateIssuer);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
ret = CRYPT_BuildSimpleChain(engine, alternate->world,
|
||||||
|
alternate->context.rgpChain[i]);
|
||||||
|
if (ret)
|
||||||
|
CRYPT_CheckSimpleChain(engine,
|
||||||
|
alternate->context.rgpChain[i], pTime);
|
||||||
|
CRYPT_CombineTrustStatus(&alternate->context.TrustStatus,
|
||||||
|
&alternate->context.rgpChain[i]->TrustStatus);
|
||||||
|
}
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
CRYPT_FreeChainContext(alternate);
|
||||||
|
alternate = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TRACE("%p\n", alternate);
|
||||||
|
return alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHAIN_QUALITY_SIGNATURE_VALID 8
|
||||||
|
#define CHAIN_QUALITY_TIME_VALID 4
|
||||||
|
#define CHAIN_QUALITY_COMPLETE_CHAIN 2
|
||||||
|
#define CHAIN_QUALITY_TRUSTED_ROOT 1
|
||||||
|
|
||||||
|
#define CHAIN_QUALITY_HIGHEST \
|
||||||
|
CHAIN_QUALITY_SIGNATURE_VALID | CHAIN_QUALITY_TIME_VALID | \
|
||||||
|
CHAIN_QUALITY_COMPLETE_CHAIN | CHAIN_QUALITY_TRUSTED_ROOT
|
||||||
|
|
||||||
|
#define IS_TRUST_ERROR_SET(TrustStatus, bits) \
|
||||||
|
(TrustStatus)->dwErrorStatus & (bits)
|
||||||
|
|
||||||
|
static DWORD CRYPT_ChainQuality(PCertificateChain chain)
|
||||||
|
{
|
||||||
|
DWORD quality = CHAIN_QUALITY_HIGHEST;
|
||||||
|
|
||||||
|
if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
|
||||||
|
CERT_TRUST_IS_UNTRUSTED_ROOT))
|
||||||
|
quality &= ~CHAIN_QUALITY_TRUSTED_ROOT;
|
||||||
|
if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
|
||||||
|
CERT_TRUST_IS_PARTIAL_CHAIN))
|
||||||
|
if (chain->context.TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN)
|
||||||
|
quality &= ~CHAIN_QUALITY_COMPLETE_CHAIN;
|
||||||
|
if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
|
||||||
|
CERT_TRUST_IS_NOT_TIME_VALID | CERT_TRUST_IS_NOT_TIME_NESTED))
|
||||||
|
quality &= ~CHAIN_QUALITY_TIME_VALID;
|
||||||
|
if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
|
||||||
|
CERT_TRUST_IS_NOT_SIGNATURE_VALID))
|
||||||
|
quality &= ~CHAIN_QUALITY_SIGNATURE_VALID;
|
||||||
|
return quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chooses the highest quality chain among chain and its "lower quality"
|
||||||
|
* alternate chains. Returns the highest quality chain, with all other
|
||||||
|
* chains as lower quality chains of it.
|
||||||
|
*/
|
||||||
|
static PCertificateChain CRYPT_ChooseHighestQualityChain(
|
||||||
|
PCertificateChain chain)
|
||||||
|
{
|
||||||
|
DWORD i;
|
||||||
|
|
||||||
|
/* There are always only two chains being considered: chain, and an
|
||||||
|
* alternate at chain->rgpLowerQualityChainContext[i]. If the alternate
|
||||||
|
* has a higher quality than chain, the alternate gets assigned the lower
|
||||||
|
* quality contexts, with chain taking the alternate's place among the
|
||||||
|
* lower quality contexts.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
|
||||||
|
{
|
||||||
|
PCertificateChain alternate =
|
||||||
|
(PCertificateChain)chain->context.rgpLowerQualityChainContext[i];
|
||||||
|
|
||||||
|
if (CRYPT_ChainQuality(alternate) > CRYPT_ChainQuality(chain))
|
||||||
|
{
|
||||||
|
alternate->context.cLowerQualityChainContext =
|
||||||
|
chain->context.cLowerQualityChainContext;
|
||||||
|
alternate->context.rgpLowerQualityChainContext =
|
||||||
|
chain->context.rgpLowerQualityChainContext;
|
||||||
|
alternate->context.rgpLowerQualityChainContext[i] =
|
||||||
|
(PCCERT_CHAIN_CONTEXT)chain;
|
||||||
|
chain = alternate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL CRYPT_AddAlternateChainToChain(PCertificateChain chain,
|
||||||
|
PCertificateChain alternate)
|
||||||
|
{
|
||||||
|
BOOL ret;
|
||||||
|
|
||||||
|
if (chain->context.cLowerQualityChainContext)
|
||||||
|
chain->context.rgpLowerQualityChainContext =
|
||||||
|
CryptMemRealloc(chain->context.rgpLowerQualityChainContext,
|
||||||
|
(chain->context.cLowerQualityChainContext + 1) *
|
||||||
|
sizeof(PCCERT_CHAIN_CONTEXT));
|
||||||
|
else
|
||||||
|
chain->context.rgpLowerQualityChainContext =
|
||||||
|
CryptMemAlloc(sizeof(PCCERT_CHAIN_CONTEXT));
|
||||||
|
if (chain->context.rgpLowerQualityChainContext)
|
||||||
|
{
|
||||||
|
chain->context.rgpLowerQualityChainContext[
|
||||||
|
chain->context.cLowerQualityChainContext++] =
|
||||||
|
(PCCERT_CHAIN_CONTEXT)alternate;
|
||||||
|
ret = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = FALSE;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS {
|
typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS {
|
||||||
DWORD cbSize;
|
DWORD cbSize;
|
||||||
CERT_USAGE_MATCH RequestedUsage;
|
CERT_USAGE_MATCH RequestedUsage;
|
||||||
|
@ -647,6 +953,26 @@ BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
|
||||||
hAdditionalStore, &chain);
|
hAdditionalStore, &chain);
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
|
PCertificateChain alternate = NULL;
|
||||||
|
|
||||||
|
do {
|
||||||
|
alternate = CRYPT_BuildAlternateContextFromChain(hChainEngine,
|
||||||
|
pTime, hAdditionalStore, chain);
|
||||||
|
|
||||||
|
/* Alternate contexts are added as "lower quality" contexts of
|
||||||
|
* chain, to avoid loops in alternate chain creation.
|
||||||
|
* The highest-quality chain is chosen at the end.
|
||||||
|
*/
|
||||||
|
if (alternate)
|
||||||
|
ret = CRYPT_AddAlternateChainToChain(chain, alternate);
|
||||||
|
} while (ret && alternate);
|
||||||
|
chain = CRYPT_ChooseHighestQualityChain(chain);
|
||||||
|
if (!(dwFlags & CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS))
|
||||||
|
{
|
||||||
|
CRYPT_FreeLowerQualityChains(chain);
|
||||||
|
chain->context.cLowerQualityChainContext = 0;
|
||||||
|
chain->context.rgpLowerQualityChainContext = NULL;
|
||||||
|
}
|
||||||
if (ppChainContext)
|
if (ppChainContext)
|
||||||
*ppChainContext = (PCCERT_CHAIN_CONTEXT)chain;
|
*ppChainContext = (PCCERT_CHAIN_CONTEXT)chain;
|
||||||
else
|
else
|
||||||
|
@ -656,20 +982,6 @@ BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CRYPT_FreeChainContext(PCertificateChain chain)
|
|
||||||
{
|
|
||||||
DWORD i;
|
|
||||||
|
|
||||||
for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
|
|
||||||
CertFreeCertificateChain(chain->context.rgpLowerQualityChainContext[i]);
|
|
||||||
CryptMemFree(chain->context.rgpLowerQualityChainContext);
|
|
||||||
for (i = 0; i < chain->context.cChain; i++)
|
|
||||||
CRYPT_FreeSimpleChain(chain->context.rgpChain[i]);
|
|
||||||
CryptMemFree(chain->context.rgpChain);
|
|
||||||
CertCloseStore(chain->world, 0);
|
|
||||||
CryptMemFree(chain);
|
|
||||||
}
|
|
||||||
|
|
||||||
PCCERT_CHAIN_CONTEXT WINAPI CertDuplicateCertificateChain(
|
PCCERT_CHAIN_CONTEXT WINAPI CertDuplicateCertificateChain(
|
||||||
PCCERT_CHAIN_CONTEXT pChainContext)
|
PCCERT_CHAIN_CONTEXT pChainContext)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1589,11 +1589,11 @@ static ChainCheck chainCheck[] = {
|
||||||
{ { sizeof(chain10) / sizeof(chain10[0]), chain10 },
|
{ { sizeof(chain10) / sizeof(chain10[0]), chain10 },
|
||||||
{ { 0, CERT_TRUST_HAS_PREFERRED_ISSUER },
|
{ { 0, CERT_TRUST_HAS_PREFERRED_ISSUER },
|
||||||
{ CERT_TRUST_IS_UNTRUSTED_ROOT, 0 }, 1, simpleStatus10 },
|
{ CERT_TRUST_IS_UNTRUSTED_ROOT, 0 }, 1, simpleStatus10 },
|
||||||
TODO_ERROR | TODO_INFO },
|
TODO_INFO },
|
||||||
{ { sizeof(chain11) / sizeof(chain11[0]), chain11 },
|
{ { sizeof(chain11) / sizeof(chain11[0]), chain11 },
|
||||||
{ { 0, CERT_TRUST_HAS_PREFERRED_ISSUER },
|
{ { 0, CERT_TRUST_HAS_PREFERRED_ISSUER },
|
||||||
{ CERT_TRUST_IS_UNTRUSTED_ROOT, 0 }, 1, simpleStatus10 },
|
{ CERT_TRUST_IS_UNTRUSTED_ROOT, 0 }, 1, simpleStatus10 },
|
||||||
TODO_ERROR | TODO_INFO },
|
TODO_INFO },
|
||||||
{ { sizeof(chain12) / sizeof(chain12[0]), chain12 },
|
{ { sizeof(chain12) / sizeof(chain12[0]), chain12 },
|
||||||
{ { 0, CERT_TRUST_HAS_PREFERRED_ISSUER },
|
{ { 0, CERT_TRUST_HAS_PREFERRED_ISSUER },
|
||||||
{ CERT_TRUST_IS_UNTRUSTED_ROOT, 0 }, 1, simpleStatus12 },
|
{ CERT_TRUST_IS_UNTRUSTED_ROOT, 0 }, 1, simpleStatus12 },
|
||||||
|
|
Loading…
Reference in New Issue