| 1 | /*
|
|---|
| 2 | * Copyright 2006 Juan Lang
|
|---|
| 3 | *
|
|---|
| 4 | * This library is free software; you can redistribute it and/or
|
|---|
| 5 | * modify it under the terms of the GNU Lesser General Public
|
|---|
| 6 | * License as published by the Free Software Foundation; either
|
|---|
| 7 | * version 2.1 of the License, or (at your option) any later version.
|
|---|
| 8 | *
|
|---|
| 9 | * This library is distributed in the hope that it will be useful,
|
|---|
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|---|
| 12 | * Lesser General Public License for more details.
|
|---|
| 13 | *
|
|---|
| 14 | * You should have received a copy of the GNU Lesser General Public
|
|---|
| 15 | * License along with this library; if not, write to the Free Software
|
|---|
| 16 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|---|
| 17 | *
|
|---|
| 18 | */
|
|---|
| 19 | #include <stdarg.h>
|
|---|
| 20 | #include <string.h>
|
|---|
| 21 | #define NONAMELESSUNION
|
|---|
| 22 | #include "windef.h"
|
|---|
| 23 | #include "winbase.h"
|
|---|
| 24 | #define CERT_CHAIN_PARA_HAS_EXTRA_FIELDS
|
|---|
| 25 | #define CERT_REVOCATION_PARA_HAS_EXTRA_FIELDS
|
|---|
| 26 | #include "winerror.h"
|
|---|
| 27 | #include "wincrypt.h"
|
|---|
| 28 | #include "wine/debug.h"
|
|---|
| 29 | #include "wine/unicode.h"
|
|---|
| 30 | #include "crypt32_private.h"
|
|---|
| 31 |
|
|---|
| 32 | WINE_DEFAULT_DEBUG_CHANNEL(crypt);
|
|---|
| 33 |
|
|---|
| 34 | #define DEFAULT_CYCLE_MODULUS 7
|
|---|
| 35 |
|
|---|
| 36 | static HCERTCHAINENGINE CRYPT_defaultChainEngine;
|
|---|
| 37 |
|
|---|
| 38 | /* This represents a subset of a certificate chain engine: it doesn't include
|
|---|
| 39 | * the "hOther" store described by MSDN, because I'm not sure how that's used.
|
|---|
| 40 | * It also doesn't include the "hTrust" store, because I don't yet implement
|
|---|
| 41 | * CTLs or complex certificate chains.
|
|---|
| 42 | */
|
|---|
| 43 | typedef struct _CertificateChainEngine
|
|---|
| 44 | {
|
|---|
| 45 | LONG ref;
|
|---|
| 46 | HCERTSTORE hRoot;
|
|---|
| 47 | HCERTSTORE hWorld;
|
|---|
| 48 | DWORD dwFlags;
|
|---|
| 49 | DWORD dwUrlRetrievalTimeout;
|
|---|
| 50 | DWORD MaximumCachedCertificates;
|
|---|
| 51 | DWORD CycleDetectionModulus;
|
|---|
| 52 | } CertificateChainEngine, *PCertificateChainEngine;
|
|---|
| 53 |
|
|---|
| 54 | static inline void CRYPT_AddStoresToCollection(HCERTSTORE collection,
|
|---|
| 55 | DWORD cStores, HCERTSTORE *stores)
|
|---|
| 56 | {
|
|---|
| 57 | DWORD i;
|
|---|
| 58 |
|
|---|
| 59 | for (i = 0; i < cStores; i++)
|
|---|
| 60 | CertAddStoreToCollection(collection, stores[i], 0, 0);
|
|---|
| 61 | }
|
|---|
| 62 |
|
|---|
| 63 | static inline void CRYPT_CloseStores(DWORD cStores, HCERTSTORE *stores)
|
|---|
| 64 | {
|
|---|
| 65 | DWORD i;
|
|---|
| 66 |
|
|---|
| 67 | for (i = 0; i < cStores; i++)
|
|---|
| 68 | CertCloseStore(stores[i], 0);
|
|---|
| 69 | }
|
|---|
| 70 |
|
|---|
| 71 | static const WCHAR rootW[] = { 'R','o','o','t',0 };
|
|---|
| 72 |
|
|---|
| 73 | static BOOL CRYPT_CheckRestrictedRoot(HCERTSTORE store)
|
|---|
| 74 | {
|
|---|
| 75 | BOOL ret = TRUE;
|
|---|
| 76 |
|
|---|
| 77 | if (store)
|
|---|
| 78 | {
|
|---|
| 79 | HCERTSTORE rootStore = CertOpenSystemStoreW(0, rootW);
|
|---|
| 80 | PCCERT_CONTEXT cert = NULL, check;
|
|---|
| 81 | BYTE hash[20];
|
|---|
| 82 | DWORD size;
|
|---|
| 83 |
|
|---|
| 84 | do {
|
|---|
| 85 | cert = CertEnumCertificatesInStore(store, cert);
|
|---|
| 86 | if (cert)
|
|---|
| 87 | {
|
|---|
| 88 | size = sizeof(hash);
|
|---|
| 89 |
|
|---|
| 90 | ret = CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID,
|
|---|
| 91 | hash, &size);
|
|---|
| 92 | if (ret)
|
|---|
| 93 | {
|
|---|
| 94 | CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
|
|---|
| 95 |
|
|---|
| 96 | check = CertFindCertificateInStore(rootStore,
|
|---|
| 97 | cert->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH, &blob,
|
|---|
| 98 | NULL);
|
|---|
| 99 | if (!check)
|
|---|
| 100 | ret = FALSE;
|
|---|
| 101 | else
|
|---|
| 102 | CertFreeCertificateContext(check);
|
|---|
| 103 | }
|
|---|
| 104 | }
|
|---|
| 105 | } while (ret && cert);
|
|---|
| 106 | if (cert)
|
|---|
| 107 | CertFreeCertificateContext(cert);
|
|---|
| 108 | CertCloseStore(rootStore, 0);
|
|---|
| 109 | }
|
|---|
| 110 | return ret;
|
|---|
| 111 | }
|
|---|
| 112 |
|
|---|
| 113 | HCERTCHAINENGINE CRYPT_CreateChainEngine(HCERTSTORE root,
|
|---|
| 114 | PCERT_CHAIN_ENGINE_CONFIG pConfig)
|
|---|
| 115 | {
|
|---|
| 116 | static const WCHAR caW[] = { 'C','A',0 };
|
|---|
| 117 | static const WCHAR myW[] = { 'M','y',0 };
|
|---|
| 118 | static const WCHAR trustW[] = { 'T','r','u','s','t',0 };
|
|---|
| 119 | PCertificateChainEngine engine =
|
|---|
| 120 | CryptMemAlloc(sizeof(CertificateChainEngine));
|
|---|
| 121 |
|
|---|
| 122 | if (engine)
|
|---|
| 123 | {
|
|---|
| 124 | HCERTSTORE worldStores[4];
|
|---|
| 125 |
|
|---|
| 126 | engine->ref = 1;
|
|---|
| 127 | engine->hRoot = root;
|
|---|
| 128 | engine->hWorld = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
|
|---|
| 129 | CERT_STORE_CREATE_NEW_FLAG, NULL);
|
|---|
| 130 | worldStores[0] = CertDuplicateStore(engine->hRoot);
|
|---|
| 131 | worldStores[1] = CertOpenSystemStoreW(0, caW);
|
|---|
| 132 | worldStores[2] = CertOpenSystemStoreW(0, myW);
|
|---|
| 133 | worldStores[3] = CertOpenSystemStoreW(0, trustW);
|
|---|
| 134 | CRYPT_AddStoresToCollection(engine->hWorld,
|
|---|
| 135 | sizeof(worldStores) / sizeof(worldStores[0]), worldStores);
|
|---|
| 136 | CRYPT_AddStoresToCollection(engine->hWorld,
|
|---|
| 137 | pConfig->cAdditionalStore, pConfig->rghAdditionalStore);
|
|---|
| 138 | CRYPT_CloseStores(sizeof(worldStores) / sizeof(worldStores[0]),
|
|---|
| 139 | worldStores);
|
|---|
| 140 | engine->dwFlags = pConfig->dwFlags;
|
|---|
| 141 | engine->dwUrlRetrievalTimeout = pConfig->dwUrlRetrievalTimeout;
|
|---|
| 142 | engine->MaximumCachedCertificates =
|
|---|
| 143 | pConfig->MaximumCachedCertificates;
|
|---|
| 144 | if (pConfig->CycleDetectionModulus)
|
|---|
| 145 | engine->CycleDetectionModulus = pConfig->CycleDetectionModulus;
|
|---|
| 146 | else
|
|---|
| 147 | engine->CycleDetectionModulus = DEFAULT_CYCLE_MODULUS;
|
|---|
| 148 | }
|
|---|
| 149 | return (HCERTCHAINENGINE)engine;
|
|---|
| 150 | }
|
|---|
| 151 |
|
|---|
| 152 | BOOL WINAPI CertCreateCertificateChainEngine(PCERT_CHAIN_ENGINE_CONFIG pConfig,
|
|---|
| 153 | HCERTCHAINENGINE *phChainEngine)
|
|---|
| 154 | {
|
|---|
| 155 | BOOL ret;
|
|---|
| 156 |
|
|---|
| 157 | TRACE("(%p, %p)\n", pConfig, phChainEngine);
|
|---|
| 158 |
|
|---|
| 159 | if (pConfig->cbSize != sizeof(*pConfig))
|
|---|
| 160 | {
|
|---|
| 161 | SetLastError(E_INVALIDARG);
|
|---|
| 162 | return FALSE;
|
|---|
| 163 | }
|
|---|
| 164 | *phChainEngine = NULL;
|
|---|
| 165 | ret = CRYPT_CheckRestrictedRoot(pConfig->hRestrictedRoot);
|
|---|
| 166 | if (ret)
|
|---|
| 167 | {
|
|---|
| 168 | HCERTSTORE root;
|
|---|
| 169 | HCERTCHAINENGINE engine;
|
|---|
| 170 |
|
|---|
| 171 | if (pConfig->hRestrictedRoot)
|
|---|
| 172 | root = CertDuplicateStore(pConfig->hRestrictedRoot);
|
|---|
| 173 | else
|
|---|
| 174 | root = CertOpenSystemStoreW(0, rootW);
|
|---|
| 175 | engine = CRYPT_CreateChainEngine(root, pConfig);
|
|---|
| 176 | if (engine)
|
|---|
| 177 | {
|
|---|
| 178 | *phChainEngine = engine;
|
|---|
| 179 | ret = TRUE;
|
|---|
| 180 | }
|
|---|
| 181 | else
|
|---|
| 182 | ret = FALSE;
|
|---|
| 183 | }
|
|---|
| 184 | return ret;
|
|---|
| 185 | }
|
|---|
| 186 |
|
|---|
| 187 | VOID WINAPI CertFreeCertificateChainEngine(HCERTCHAINENGINE hChainEngine)
|
|---|
| 188 | {
|
|---|
| 189 | PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
|
|---|
| 190 |
|
|---|
| 191 | TRACE("(%p)\n", hChainEngine);
|
|---|
| 192 |
|
|---|
| 193 | if (engine && InterlockedDecrement(&engine->ref) == 0)
|
|---|
| 194 | {
|
|---|
| 195 | CertCloseStore(engine->hWorld, 0);
|
|---|
| 196 | CertCloseStore(engine->hRoot, 0);
|
|---|
| 197 | CryptMemFree(engine);
|
|---|
| 198 | }
|
|---|
| 199 | }
|
|---|
| 200 |
|
|---|
| 201 | static HCERTCHAINENGINE CRYPT_GetDefaultChainEngine(void)
|
|---|
| 202 | {
|
|---|
| 203 | if (!CRYPT_defaultChainEngine)
|
|---|
| 204 | {
|
|---|
| 205 | CERT_CHAIN_ENGINE_CONFIG config = { 0 };
|
|---|
| 206 | HCERTCHAINENGINE engine;
|
|---|
| 207 |
|
|---|
| 208 | config.cbSize = sizeof(config);
|
|---|
| 209 | CertCreateCertificateChainEngine(&config, &engine);
|
|---|
| 210 | InterlockedCompareExchangePointer(&CRYPT_defaultChainEngine, engine,
|
|---|
| 211 | NULL);
|
|---|
| 212 | if (CRYPT_defaultChainEngine != engine)
|
|---|
| 213 | CertFreeCertificateChainEngine(engine);
|
|---|
| 214 | }
|
|---|
| 215 | return CRYPT_defaultChainEngine;
|
|---|
| 216 | }
|
|---|
| 217 |
|
|---|
| 218 | void default_chain_engine_free(void)
|
|---|
| 219 | {
|
|---|
| 220 | CertFreeCertificateChainEngine(CRYPT_defaultChainEngine);
|
|---|
| 221 | }
|
|---|
| 222 |
|
|---|
| 223 | typedef struct _CertificateChain
|
|---|
| 224 | {
|
|---|
| 225 | CERT_CHAIN_CONTEXT context;
|
|---|
| 226 | HCERTSTORE world;
|
|---|
| 227 | LONG ref;
|
|---|
| 228 | } CertificateChain, *PCertificateChain;
|
|---|
| 229 |
|
|---|
| 230 | static inline BOOL CRYPT_IsCertificateSelfSigned(PCCERT_CONTEXT cert)
|
|---|
| 231 | {
|
|---|
| 232 | return CertCompareCertificateName(cert->dwCertEncodingType,
|
|---|
| 233 | &cert->pCertInfo->Subject, &cert->pCertInfo->Issuer);
|
|---|
| 234 | }
|
|---|
| 235 |
|
|---|
| 236 | static void CRYPT_FreeChainElement(PCERT_CHAIN_ELEMENT element)
|
|---|
| 237 | {
|
|---|
| 238 | CertFreeCertificateContext(element->pCertContext);
|
|---|
| 239 | CryptMemFree(element);
|
|---|
| 240 | }
|
|---|
| 241 |
|
|---|
| 242 | static void CRYPT_CheckSimpleChainForCycles(PCERT_SIMPLE_CHAIN chain)
|
|---|
| 243 | {
|
|---|
| 244 | DWORD i, j, cyclicCertIndex = 0;
|
|---|
| 245 |
|
|---|
| 246 | /* O(n^2) - I don't think there's a faster way */
|
|---|
| 247 | for (i = 0; !cyclicCertIndex && i < chain->cElement; i++)
|
|---|
| 248 | for (j = i + 1; !cyclicCertIndex && j < chain->cElement; j++)
|
|---|
| 249 | if (CertCompareCertificate(X509_ASN_ENCODING,
|
|---|
| 250 | chain->rgpElement[i]->pCertContext->pCertInfo,
|
|---|
| 251 | chain->rgpElement[j]->pCertContext->pCertInfo))
|
|---|
| 252 | cyclicCertIndex = j;
|
|---|
| 253 | if (cyclicCertIndex)
|
|---|
| 254 | {
|
|---|
| 255 | chain->rgpElement[cyclicCertIndex]->TrustStatus.dwErrorStatus
|
|---|
| 256 | |= CERT_TRUST_IS_CYCLIC | CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
|
|---|
| 257 | /* Release remaining certs */
|
|---|
| 258 | for (i = cyclicCertIndex + 1; i < chain->cElement; i++)
|
|---|
| 259 | CRYPT_FreeChainElement(chain->rgpElement[i]);
|
|---|
| 260 | /* Truncate chain */
|
|---|
| 261 | chain->cElement = cyclicCertIndex + 1;
|
|---|
| 262 | }
|
|---|
| 263 | }
|
|---|
| 264 |
|
|---|
| 265 | /* Checks whether the chain is cyclic by examining the last element's status */
|
|---|
| 266 | static inline BOOL CRYPT_IsSimpleChainCyclic(PCERT_SIMPLE_CHAIN chain)
|
|---|
| 267 | {
|
|---|
| 268 | if (chain->cElement)
|
|---|
| 269 | return chain->rgpElement[chain->cElement - 1]->TrustStatus.dwErrorStatus
|
|---|
| 270 | & CERT_TRUST_IS_CYCLIC;
|
|---|
| 271 | else
|
|---|
| 272 | return FALSE;
|
|---|
| 273 | }
|
|---|
| 274 |
|
|---|
| 275 | static inline void CRYPT_CombineTrustStatus(CERT_TRUST_STATUS *chainStatus,
|
|---|
| 276 | CERT_TRUST_STATUS *elementStatus)
|
|---|
| 277 | {
|
|---|
| 278 | /* Any error that applies to an element also applies to a chain.. */
|
|---|
| 279 | chainStatus->dwErrorStatus |= elementStatus->dwErrorStatus;
|
|---|
| 280 | /* but the bottom nibble of an element's info status doesn't apply to the
|
|---|
| 281 | * chain.
|
|---|
| 282 | */
|
|---|
| 283 | chainStatus->dwInfoStatus |= (elementStatus->dwInfoStatus & 0xfffffff0);
|
|---|
| 284 | }
|
|---|
| 285 |
|
|---|
| 286 | static BOOL CRYPT_AddCertToSimpleChain(PCertificateChainEngine engine,
|
|---|
| 287 | PCERT_SIMPLE_CHAIN chain, PCCERT_CONTEXT cert, DWORD subjectInfoStatus)
|
|---|
| 288 | {
|
|---|
| 289 | BOOL ret = FALSE;
|
|---|
| 290 | PCERT_CHAIN_ELEMENT element = CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
|
|---|
| 291 |
|
|---|
| 292 | if (element)
|
|---|
| 293 | {
|
|---|
| 294 | if (!chain->cElement)
|
|---|
| 295 | chain->rgpElement = CryptMemAlloc(sizeof(PCERT_CHAIN_ELEMENT));
|
|---|
| 296 | else
|
|---|
| 297 | chain->rgpElement = CryptMemRealloc(chain->rgpElement,
|
|---|
| 298 | (chain->cElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
|
|---|
| 299 | if (chain->rgpElement)
|
|---|
| 300 | {
|
|---|
| 301 | chain->rgpElement[chain->cElement++] = element;
|
|---|
| 302 | memset(element, 0, sizeof(CERT_CHAIN_ELEMENT));
|
|---|
| 303 | element->cbSize = sizeof(CERT_CHAIN_ELEMENT);
|
|---|
| 304 | element->pCertContext = CertDuplicateCertificateContext(cert);
|
|---|
| 305 | if (chain->cElement > 1)
|
|---|
| 306 | chain->rgpElement[chain->cElement - 2]->TrustStatus.dwInfoStatus
|
|---|
| 307 | = subjectInfoStatus;
|
|---|
| 308 | /* FIXME: initialize the rest of element */
|
|---|
| 309 | if (!(chain->cElement % engine->CycleDetectionModulus))
|
|---|
| 310 | CRYPT_CheckSimpleChainForCycles(chain);
|
|---|
| 311 | CRYPT_CombineTrustStatus(&chain->TrustStatus,
|
|---|
| 312 | &element->TrustStatus);
|
|---|
| 313 | ret = TRUE;
|
|---|
| 314 | }
|
|---|
| 315 | else
|
|---|
| 316 | CryptMemFree(element);
|
|---|
| 317 | }
|
|---|
| 318 | return ret;
|
|---|
| 319 | }
|
|---|
| 320 |
|
|---|
| 321 | static void CRYPT_FreeSimpleChain(PCERT_SIMPLE_CHAIN chain)
|
|---|
| 322 | {
|
|---|
| 323 | DWORD i;
|
|---|
| 324 |
|
|---|
| 325 | for (i = 0; i < chain->cElement; i++)
|
|---|
| 326 | CRYPT_FreeChainElement(chain->rgpElement[i]);
|
|---|
| 327 | CryptMemFree(chain->rgpElement);
|
|---|
| 328 | CryptMemFree(chain);
|
|---|
| 329 | }
|
|---|
| 330 |
|
|---|
| 331 | static void CRYPT_CheckTrustedStatus(HCERTSTORE hRoot,
|
|---|
| 332 | PCERT_CHAIN_ELEMENT rootElement)
|
|---|
| 333 | {
|
|---|
| 334 | BYTE hash[20];
|
|---|
| 335 | DWORD size = sizeof(hash);
|
|---|
| 336 | CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
|
|---|
| 337 | PCCERT_CONTEXT trustedRoot;
|
|---|
| 338 |
|
|---|
| 339 | CertGetCertificateContextProperty(rootElement->pCertContext,
|
|---|
| 340 | CERT_HASH_PROP_ID, hash, &size);
|
|---|
| 341 | trustedRoot = CertFindCertificateInStore(hRoot,
|
|---|
| 342 | rootElement->pCertContext->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH,
|
|---|
| 343 | &blob, NULL);
|
|---|
| 344 | if (!trustedRoot)
|
|---|
| 345 | rootElement->TrustStatus.dwErrorStatus |=
|
|---|
| 346 | CERT_TRUST_IS_UNTRUSTED_ROOT;
|
|---|
| 347 | else
|
|---|
| 348 | CertFreeCertificateContext(trustedRoot);
|
|---|
| 349 | }
|
|---|
| 350 |
|
|---|
| 351 | static void CRYPT_CheckRootCert(HCERTCHAINENGINE hRoot,
|
|---|
| 352 | PCERT_CHAIN_ELEMENT rootElement)
|
|---|
| 353 | {
|
|---|
| 354 | PCCERT_CONTEXT root = rootElement->pCertContext;
|
|---|
| 355 |
|
|---|
| 356 | if (!CryptVerifyCertificateSignatureEx(0, root->dwCertEncodingType,
|
|---|
| 357 | CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, (void *)root,
|
|---|
| 358 | CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)root, 0, NULL))
|
|---|
| 359 | {
|
|---|
| 360 | TRACE("Last certificate's signature is invalid\n");
|
|---|
| 361 | rootElement->TrustStatus.dwErrorStatus |=
|
|---|
| 362 | CERT_TRUST_IS_NOT_SIGNATURE_VALID;
|
|---|
| 363 | }
|
|---|
| 364 | CRYPT_CheckTrustedStatus((HCERTSTORE)hRoot, rootElement);
|
|---|
| 365 | }
|
|---|
| 366 |
|
|---|
| 367 | /* Decodes a cert's basic constraints extension (either szOID_BASIC_CONSTRAINTS
|
|---|
| 368 | * or szOID_BASIC_CONSTRAINTS2, whichever is present) into a
|
|---|
| 369 | * CERT_BASIC_CONSTRAINTS2_INFO. If it neither extension is present, sets
|
|---|
| 370 | * constraints->fCA to defaultIfNotSpecified.
|
|---|
| 371 | * Returns FALSE if the extension is present but couldn't be decoded.
|
|---|
| 372 | */
|
|---|
| 373 | static BOOL CRYPT_DecodeBasicConstraints(PCCERT_CONTEXT cert,
|
|---|
| 374 | CERT_BASIC_CONSTRAINTS2_INFO *constraints, BOOL defaultIfNotSpecified)
|
|---|
| 375 | {
|
|---|
| 376 | BOOL ret = TRUE;
|
|---|
| 377 | PCERT_EXTENSION ext = CertFindExtension(szOID_BASIC_CONSTRAINTS,
|
|---|
| 378 | cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
|
|---|
| 379 |
|
|---|
| 380 | constraints->fPathLenConstraint = FALSE;
|
|---|
| 381 | if (ext)
|
|---|
| 382 | {
|
|---|
| 383 | CERT_BASIC_CONSTRAINTS_INFO *info;
|
|---|
| 384 | DWORD size = 0;
|
|---|
| 385 |
|
|---|
| 386 | ret = CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS,
|
|---|
| 387 | ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG,
|
|---|
| 388 | NULL, (LPBYTE)&info, &size);
|
|---|
| 389 | if (ret)
|
|---|
| 390 | {
|
|---|
| 391 | if (info->SubjectType.cbData == 1)
|
|---|
| 392 | constraints->fCA =
|
|---|
| 393 | info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG;
|
|---|
| 394 | LocalFree((HANDLE)info);
|
|---|
| 395 | }
|
|---|
| 396 | }
|
|---|
| 397 | else
|
|---|
| 398 | {
|
|---|
| 399 | ext = CertFindExtension(szOID_BASIC_CONSTRAINTS2,
|
|---|
| 400 | cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
|
|---|
| 401 | if (ext)
|
|---|
| 402 | {
|
|---|
| 403 | DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO);
|
|---|
| 404 |
|
|---|
| 405 | ret = CryptDecodeObjectEx(X509_ASN_ENCODING,
|
|---|
| 406 | szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData,
|
|---|
| 407 | 0, NULL, constraints, &size);
|
|---|
| 408 | }
|
|---|
| 409 | else
|
|---|
| 410 | constraints->fCA = defaultIfNotSpecified;
|
|---|
| 411 | }
|
|---|
| 412 | return ret;
|
|---|
| 413 | }
|
|---|
| 414 |
|
|---|
| 415 | /* Checks element's basic constraints to see if it can act as a CA, with
|
|---|
| 416 | * remainingCAs CAs left in this chain. Updates chainConstraints with the
|
|---|
| 417 | * element's constraints, if:
|
|---|
| 418 | * 1. chainConstraints doesn't have a path length constraint, or
|
|---|
| 419 | * 2. element's path length constraint is smaller than chainConstraints's
|
|---|
| 420 | * Sets *pathLengthConstraintViolated to TRUE if a path length violation
|
|---|
| 421 | * occurs.
|
|---|
| 422 | * Returns TRUE if the element can be a CA, and the length of the remaining
|
|---|
| 423 | * chain is valid.
|
|---|
| 424 | */
|
|---|
| 425 | static BOOL CRYPT_CheckBasicConstraintsForCA(PCCERT_CONTEXT cert,
|
|---|
| 426 | CERT_BASIC_CONSTRAINTS2_INFO *chainConstraints, DWORD remainingCAs,
|
|---|
| 427 | BOOL *pathLengthConstraintViolated)
|
|---|
| 428 | {
|
|---|
| 429 | BOOL validBasicConstraints;
|
|---|
| 430 | CERT_BASIC_CONSTRAINTS2_INFO constraints;
|
|---|
| 431 |
|
|---|
| 432 | if ((validBasicConstraints = CRYPT_DecodeBasicConstraints(cert,
|
|---|
| 433 | &constraints, TRUE)) != NULL)
|
|---|
| 434 | {
|
|---|
| 435 | if (!constraints.fCA)
|
|---|
| 436 | {
|
|---|
| 437 | TRACE("chain element %d can't be a CA\n", remainingCAs + 1);
|
|---|
| 438 | validBasicConstraints = FALSE;
|
|---|
| 439 | }
|
|---|
| 440 | else if (constraints.fPathLenConstraint)
|
|---|
| 441 | {
|
|---|
| 442 | /* If the element has path length constraints, they apply to the
|
|---|
| 443 | * entire remaining chain.
|
|---|
| 444 | */
|
|---|
| 445 | if (!chainConstraints->fPathLenConstraint ||
|
|---|
| 446 | constraints.dwPathLenConstraint <
|
|---|
| 447 | chainConstraints->dwPathLenConstraint)
|
|---|
| 448 | {
|
|---|
| 449 | TRACE("setting path length constraint to %d\n",
|
|---|
| 450 | chainConstraints->dwPathLenConstraint);
|
|---|
| 451 | chainConstraints->fPathLenConstraint = TRUE;
|
|---|
| 452 | chainConstraints->dwPathLenConstraint =
|
|---|
| 453 | constraints.dwPathLenConstraint;
|
|---|
| 454 | }
|
|---|
| 455 | }
|
|---|
| 456 | }
|
|---|
| 457 | if (chainConstraints->fPathLenConstraint &&
|
|---|
| 458 | remainingCAs > chainConstraints->dwPathLenConstraint)
|
|---|
| 459 | {
|
|---|
| 460 | TRACE("remaining CAs %d exceed max path length %d\n", remainingCAs,
|
|---|
| 461 | chainConstraints->dwPathLenConstraint);
|
|---|
| 462 | validBasicConstraints = FALSE;
|
|---|
| 463 | *pathLengthConstraintViolated = TRUE;
|
|---|
| 464 | }
|
|---|
| 465 | return validBasicConstraints;
|
|---|
| 466 | }
|
|---|
| 467 |
|
|---|
| 468 | static BOOL url_matches(LPCWSTR constraint, LPCWSTR name,
|
|---|
| 469 | DWORD *trustErrorStatus)
|
|---|
| 470 | {
|
|---|
| 471 | BOOL match = FALSE;
|
|---|
| 472 |
|
|---|
| 473 | TRACE("%s, %s\n", debugstr_w(constraint), debugstr_w(name));
|
|---|
| 474 |
|
|---|
| 475 | if (!constraint)
|
|---|
| 476 | *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
|
|---|
| 477 | else if (!name)
|
|---|
| 478 | ; /* no match */
|
|---|
| 479 | else if (constraint[0] == '.')
|
|---|
| 480 | {
|
|---|
| 481 | if (lstrlenW(name) > lstrlenW(constraint))
|
|---|
| 482 | match = !lstrcmpiW(name + lstrlenW(name) - lstrlenW(constraint),
|
|---|
| 483 | constraint);
|
|---|
| 484 | }
|
|---|
| 485 | else
|
|---|
| 486 | match = !lstrcmpiW(constraint, name);
|
|---|
| 487 | return match;
|
|---|
| 488 | }
|
|---|
| 489 |
|
|---|
| 490 | static BOOL rfc822_name_matches(LPCWSTR constraint, LPCWSTR name,
|
|---|
| 491 | DWORD *trustErrorStatus)
|
|---|
| 492 | {
|
|---|
| 493 | BOOL match = FALSE;
|
|---|
| 494 | LPCWSTR at;
|
|---|
| 495 |
|
|---|
| 496 | TRACE("%s, %s\n", debugstr_w(constraint), debugstr_w(name));
|
|---|
| 497 |
|
|---|
| 498 | if (!constraint)
|
|---|
| 499 | *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
|
|---|
| 500 | else if (!name)
|
|---|
| 501 | ; /* no match */
|
|---|
| 502 | else if ((at = strchrW(constraint, '@')) != NULL)
|
|---|
| 503 | match = !lstrcmpiW(constraint, name);
|
|---|
| 504 | else
|
|---|
| 505 | {
|
|---|
| 506 | if ((at = strchrW(name, '@')) != NULL)
|
|---|
| 507 | match = url_matches(constraint, at + 1, trustErrorStatus);
|
|---|
| 508 | else
|
|---|
| 509 | match = !lstrcmpiW(constraint, name);
|
|---|
| 510 | }
|
|---|
| 511 | return match;
|
|---|
| 512 | }
|
|---|
| 513 |
|
|---|
| 514 | static BOOL dns_name_matches(LPCWSTR constraint, LPCWSTR name,
|
|---|
| 515 | DWORD *trustErrorStatus)
|
|---|
| 516 | {
|
|---|
| 517 | BOOL match = FALSE;
|
|---|
| 518 |
|
|---|
| 519 | TRACE("%s, %s\n", debugstr_w(constraint), debugstr_w(name));
|
|---|
| 520 |
|
|---|
| 521 | if (!constraint)
|
|---|
| 522 | *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
|
|---|
| 523 | else if (!name)
|
|---|
| 524 | ; /* no match */
|
|---|
| 525 | else if (lstrlenW(name) >= lstrlenW(constraint))
|
|---|
| 526 | match = !lstrcmpiW(name + lstrlenW(name) - lstrlenW(constraint),
|
|---|
| 527 | constraint);
|
|---|
| 528 | /* else: name is too short, no match */
|
|---|
| 529 |
|
|---|
| 530 | return match;
|
|---|
| 531 | }
|
|---|
| 532 |
|
|---|
| 533 | static BOOL ip_address_matches(const CRYPT_DATA_BLOB *constraint,
|
|---|
| 534 | const CRYPT_DATA_BLOB *name, DWORD *trustErrorStatus)
|
|---|
| 535 | {
|
|---|
| 536 | BOOL match = FALSE;
|
|---|
| 537 |
|
|---|
| 538 | TRACE("(%d, %p), (%d, %p)\n", constraint->cbData, constraint->pbData,
|
|---|
| 539 | name->cbData, name->pbData);
|
|---|
| 540 |
|
|---|
| 541 | if (constraint->cbData != sizeof(DWORD) * 2)
|
|---|
| 542 | *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
|
|---|
| 543 | else if (name->cbData == sizeof(DWORD))
|
|---|
| 544 | {
|
|---|
| 545 | DWORD subnet, mask, addr;
|
|---|
| 546 |
|
|---|
| 547 | memcpy(&subnet, constraint->pbData, sizeof(subnet));
|
|---|
| 548 | memcpy(&mask, constraint->pbData + sizeof(subnet), sizeof(mask));
|
|---|
| 549 | memcpy(&addr, name->pbData, sizeof(addr));
|
|---|
| 550 | /* These are really in big-endian order, but for equality matching we
|
|---|
| 551 | * don't need to swap to host order
|
|---|
| 552 | */
|
|---|
| 553 | match = (subnet & mask) == (addr & mask);
|
|---|
| 554 | }
|
|---|
| 555 | /* else: name is wrong size, no match */
|
|---|
| 556 |
|
|---|
| 557 | return match;
|
|---|
| 558 | }
|
|---|
| 559 |
|
|---|
| 560 | static void CRYPT_FindMatchingNameEntry(const CERT_ALT_NAME_ENTRY *constraint,
|
|---|
| 561 | const CERT_ALT_NAME_INFO *subjectName, DWORD *trustErrorStatus,
|
|---|
| 562 | DWORD errorIfFound, DWORD errorIfNotFound)
|
|---|
| 563 | {
|
|---|
| 564 | DWORD i;
|
|---|
| 565 | BOOL match = FALSE;
|
|---|
| 566 |
|
|---|
| 567 | for (i = 0; i < subjectName->cAltEntry; i++)
|
|---|
| 568 | {
|
|---|
| 569 | if (subjectName->rgAltEntry[i].dwAltNameChoice ==
|
|---|
| 570 | constraint->dwAltNameChoice)
|
|---|
| 571 | {
|
|---|
| 572 | switch (constraint->dwAltNameChoice)
|
|---|
| 573 | {
|
|---|
| 574 | case CERT_ALT_NAME_RFC822_NAME:
|
|---|
| 575 | match = rfc822_name_matches(constraint->u.pwszURL,
|
|---|
| 576 | subjectName->rgAltEntry[i].u.pwszURL, trustErrorStatus);
|
|---|
| 577 | break;
|
|---|
| 578 | case CERT_ALT_NAME_DNS_NAME:
|
|---|
| 579 | match = dns_name_matches(constraint->u.pwszURL,
|
|---|
| 580 | subjectName->rgAltEntry[i].u.pwszURL, trustErrorStatus);
|
|---|
| 581 | break;
|
|---|
| 582 | case CERT_ALT_NAME_URL:
|
|---|
| 583 | match = url_matches(constraint->u.pwszURL,
|
|---|
| 584 | subjectName->rgAltEntry[i].u.pwszURL, trustErrorStatus);
|
|---|
| 585 | break;
|
|---|
| 586 | case CERT_ALT_NAME_IP_ADDRESS:
|
|---|
| 587 | match = ip_address_matches(&constraint->u.IPAddress,
|
|---|
| 588 | &subjectName->rgAltEntry[i].u.IPAddress, trustErrorStatus);
|
|---|
| 589 | break;
|
|---|
| 590 | case CERT_ALT_NAME_DIRECTORY_NAME:
|
|---|
| 591 | default:
|
|---|
| 592 | ERR("name choice %d unsupported in this context\n",
|
|---|
| 593 | constraint->dwAltNameChoice);
|
|---|
| 594 | *trustErrorStatus |=
|
|---|
| 595 | CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
|
|---|
| 596 | }
|
|---|
| 597 | }
|
|---|
| 598 | }
|
|---|
| 599 | *trustErrorStatus |= match ? errorIfFound : errorIfNotFound;
|
|---|
| 600 | }
|
|---|
| 601 |
|
|---|
| 602 | static void CRYPT_CheckNameConstraints(
|
|---|
| 603 | const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, const CERT_INFO *cert,
|
|---|
| 604 | DWORD *trustErrorStatus)
|
|---|
| 605 | {
|
|---|
| 606 | /* If there aren't any existing constraints, don't bother checking */
|
|---|
| 607 | if (nameConstraints->cPermittedSubtree || nameConstraints->cExcludedSubtree)
|
|---|
| 608 | {
|
|---|
| 609 | CERT_EXTENSION *ext;
|
|---|
| 610 |
|
|---|
| 611 | if ((ext = CertFindExtension(szOID_SUBJECT_ALT_NAME, cert->cExtension,
|
|---|
| 612 | cert->rgExtension)) != NULL)
|
|---|
| 613 | {
|
|---|
| 614 | CERT_ALT_NAME_INFO *subjectName;
|
|---|
| 615 | DWORD size;
|
|---|
| 616 |
|
|---|
| 617 | if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
|
|---|
| 618 | ext->Value.pbData, ext->Value.cbData,
|
|---|
| 619 | CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
|
|---|
| 620 | &subjectName, &size))
|
|---|
| 621 | {
|
|---|
| 622 | DWORD i;
|
|---|
| 623 |
|
|---|
| 624 | for (i = 0; i < nameConstraints->cExcludedSubtree; i++)
|
|---|
| 625 | CRYPT_FindMatchingNameEntry(
|
|---|
| 626 | &nameConstraints->rgExcludedSubtree[i].Base, subjectName,
|
|---|
| 627 | trustErrorStatus,
|
|---|
| 628 | CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT, 0);
|
|---|
| 629 | for (i = 0; i < nameConstraints->cPermittedSubtree; i++)
|
|---|
| 630 | CRYPT_FindMatchingNameEntry(
|
|---|
| 631 | &nameConstraints->rgPermittedSubtree[i].Base, subjectName,
|
|---|
| 632 | trustErrorStatus,
|
|---|
| 633 | 0, CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT);
|
|---|
| 634 | LocalFree((HANDLE)subjectName);
|
|---|
| 635 | }
|
|---|
| 636 | }
|
|---|
| 637 | else
|
|---|
| 638 | {
|
|---|
| 639 | if (nameConstraints->cPermittedSubtree)
|
|---|
| 640 | *trustErrorStatus |=
|
|---|
| 641 | CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT;
|
|---|
| 642 | if (nameConstraints->cExcludedSubtree)
|
|---|
| 643 | *trustErrorStatus |=
|
|---|
| 644 | CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT;
|
|---|
| 645 | }
|
|---|
| 646 | }
|
|---|
| 647 | }
|
|---|
| 648 |
|
|---|
| 649 | /* Gets cert's name constraints, if any. Free with LocalFree. */
|
|---|
| 650 | static CERT_NAME_CONSTRAINTS_INFO *CRYPT_GetNameConstraints(CERT_INFO *cert)
|
|---|
| 651 | {
|
|---|
| 652 | CERT_NAME_CONSTRAINTS_INFO *info = NULL;
|
|---|
| 653 |
|
|---|
| 654 | CERT_EXTENSION *ext;
|
|---|
| 655 |
|
|---|
| 656 | if ((ext = CertFindExtension(szOID_NAME_CONSTRAINTS, cert->cExtension,
|
|---|
| 657 | cert->rgExtension)) != NULL)
|
|---|
| 658 | {
|
|---|
| 659 | DWORD size;
|
|---|
| 660 |
|
|---|
| 661 | CryptDecodeObjectEx(X509_ASN_ENCODING, X509_NAME_CONSTRAINTS,
|
|---|
| 662 | ext->Value.pbData, ext->Value.cbData,
|
|---|
| 663 | CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, &info,
|
|---|
| 664 | &size);
|
|---|
| 665 | }
|
|---|
| 666 | return info;
|
|---|
| 667 | }
|
|---|
| 668 |
|
|---|
| 669 | static void CRYPT_CheckChainNameConstraints(PCERT_SIMPLE_CHAIN chain)
|
|---|
| 670 | {
|
|---|
| 671 | int i, j;
|
|---|
| 672 |
|
|---|
| 673 | /* Microsoft's implementation appears to violate RFC 3280: according to
|
|---|
| 674 | * MSDN, the various CERT_TRUST_*_NAME_CONSTRAINT errors are set if a CA's
|
|---|
| 675 | * name constraint is violated in the end cert. According to RFC 3280,
|
|---|
| 676 | * the constraints should be checked against every subsequent certificate
|
|---|
| 677 | * in the chain, not just the end cert.
|
|---|
| 678 | * Microsoft's implementation also sets the name constraint errors on the
|
|---|
| 679 | * certs whose constraints were violated, not on the certs that violated
|
|---|
| 680 | * them.
|
|---|
| 681 | * In order to be error-compatible with Microsoft's implementation, while
|
|---|
| 682 | * still adhering to RFC 3280, I use a O(n ^ 2) algorithm to check name
|
|---|
| 683 | * constraints.
|
|---|
| 684 | */
|
|---|
| 685 | for (i = chain->cElement - 1; i > 0; i--)
|
|---|
| 686 | {
|
|---|
| 687 | CERT_NAME_CONSTRAINTS_INFO *nameConstraints;
|
|---|
| 688 |
|
|---|
| 689 | if ((nameConstraints = CRYPT_GetNameConstraints(
|
|---|
| 690 | chain->rgpElement[i]->pCertContext->pCertInfo)) != NULL)
|
|---|
| 691 | {
|
|---|
| 692 | for (j = i - 1; j >= 0; j--)
|
|---|
| 693 | {
|
|---|
| 694 | DWORD errorStatus = 0;
|
|---|
| 695 |
|
|---|
| 696 | /* According to RFC 3280, self-signed certs don't have name
|
|---|
| 697 | * constraints checked unless they're the end cert.
|
|---|
| 698 | */
|
|---|
| 699 | if (j == 0 || !CRYPT_IsCertificateSelfSigned(
|
|---|
| 700 | chain->rgpElement[j]->pCertContext))
|
|---|
| 701 | {
|
|---|
| 702 | CRYPT_CheckNameConstraints(nameConstraints,
|
|---|
| 703 | chain->rgpElement[i]->pCertContext->pCertInfo,
|
|---|
| 704 | &errorStatus);
|
|---|
| 705 | chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
|
|---|
| 706 | errorStatus;
|
|---|
| 707 | }
|
|---|
| 708 | }
|
|---|
| 709 | LocalFree((HANDLE)nameConstraints);
|
|---|
| 710 | }
|
|---|
| 711 | }
|
|---|
| 712 | }
|
|---|
| 713 |
|
|---|
| 714 | static void CRYPT_CheckSimpleChain(PCertificateChainEngine engine,
|
|---|
| 715 | PCERT_SIMPLE_CHAIN chain, LPFILETIME time)
|
|---|
| 716 | {
|
|---|
| 717 | PCERT_CHAIN_ELEMENT rootElement = chain->rgpElement[chain->cElement - 1];
|
|---|
| 718 | int i;
|
|---|
| 719 | BOOL pathLengthConstraintViolated = FALSE;
|
|---|
| 720 | CERT_BASIC_CONSTRAINTS2_INFO constraints = { TRUE, FALSE, 0 };
|
|---|
| 721 |
|
|---|
| 722 | for (i = chain->cElement - 1; i >= 0; i--)
|
|---|
| 723 | {
|
|---|
| 724 | if (CertVerifyTimeValidity(time,
|
|---|
| 725 | chain->rgpElement[i]->pCertContext->pCertInfo) != 0)
|
|---|
| 726 | chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
|
|---|
| 727 | CERT_TRUST_IS_NOT_TIME_VALID;
|
|---|
| 728 | if (i != 0)
|
|---|
| 729 | {
|
|---|
| 730 | /* Check the signature of the cert this issued */
|
|---|
| 731 | if (!CryptVerifyCertificateSignatureEx(0, X509_ASN_ENCODING,
|
|---|
| 732 | CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
|
|---|
| 733 | (void *)chain->rgpElement[i - 1]->pCertContext,
|
|---|
| 734 | CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT,
|
|---|
| 735 | (void *)chain->rgpElement[i]->pCertContext, 0, NULL))
|
|---|
| 736 | chain->rgpElement[i - 1]->TrustStatus.dwErrorStatus |=
|
|---|
| 737 | CERT_TRUST_IS_NOT_SIGNATURE_VALID;
|
|---|
| 738 | /* Once a path length constraint has been violated, every remaining
|
|---|
| 739 | * CA cert's basic constraints is considered invalid.
|
|---|
| 740 | */
|
|---|
| 741 | if (pathLengthConstraintViolated)
|
|---|
| 742 | chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
|
|---|
| 743 | CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
|
|---|
| 744 | else if (!CRYPT_CheckBasicConstraintsForCA(
|
|---|
| 745 | chain->rgpElement[i]->pCertContext, &constraints, i - 1,
|
|---|
| 746 | &pathLengthConstraintViolated))
|
|---|
| 747 | chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
|
|---|
| 748 | CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
|
|---|
| 749 | else if (constraints.fPathLenConstraint &&
|
|---|
| 750 | constraints.dwPathLenConstraint)
|
|---|
| 751 | {
|
|---|
| 752 | /* This one's valid - decrement max length */
|
|---|
| 753 | constraints.dwPathLenConstraint--;
|
|---|
| 754 | }
|
|---|
| 755 | }
|
|---|
| 756 | if (CRYPT_IsSimpleChainCyclic(chain))
|
|---|
| 757 | {
|
|---|
| 758 | /* If the chain is cyclic, then the path length constraints
|
|---|
| 759 | * are violated, because the chain is infinitely long.
|
|---|
| 760 | */
|
|---|
| 761 | pathLengthConstraintViolated = TRUE;
|
|---|
| 762 | chain->TrustStatus.dwErrorStatus |=
|
|---|
| 763 | CERT_TRUST_IS_PARTIAL_CHAIN |
|
|---|
| 764 | CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
|
|---|
| 765 | }
|
|---|
| 766 | /* FIXME: check valid usages */
|
|---|
| 767 | CRYPT_CombineTrustStatus(&chain->TrustStatus,
|
|---|
| 768 | &chain->rgpElement[i]->TrustStatus);
|
|---|
| 769 | }
|
|---|
| 770 | CRYPT_CheckChainNameConstraints(chain);
|
|---|
| 771 | if (CRYPT_IsCertificateSelfSigned(rootElement->pCertContext))
|
|---|
| 772 | {
|
|---|
| 773 | rootElement->TrustStatus.dwInfoStatus |=
|
|---|
| 774 | CERT_TRUST_IS_SELF_SIGNED | CERT_TRUST_HAS_NAME_MATCH_ISSUER;
|
|---|
| 775 | CRYPT_CheckRootCert((HCERTCHAINENGINE)engine->hRoot, rootElement);
|
|---|
| 776 | }
|
|---|
| 777 | CRYPT_CombineTrustStatus(&chain->TrustStatus, &rootElement->TrustStatus);
|
|---|
| 778 | }
|
|---|
| 779 |
|
|---|
| 780 | static PCCERT_CONTEXT CRYPT_GetIssuer(HCERTSTORE store, PCCERT_CONTEXT subject,
|
|---|
| 781 | PCCERT_CONTEXT prevIssuer, DWORD *infoStatus)
|
|---|
| 782 | {
|
|---|
| 783 | PCCERT_CONTEXT issuer = NULL;
|
|---|
| 784 | PCERT_EXTENSION ext;
|
|---|
| 785 | DWORD size;
|
|---|
| 786 |
|
|---|
| 787 | *infoStatus = 0;
|
|---|
| 788 | if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER,
|
|---|
| 789 | subject->pCertInfo->cExtension, subject->pCertInfo->rgExtension)) != NULL)
|
|---|
| 790 | {
|
|---|
| 791 | CERT_AUTHORITY_KEY_ID_INFO *info;
|
|---|
| 792 | BOOL ret;
|
|---|
| 793 |
|
|---|
| 794 | ret = CryptDecodeObjectEx(subject->dwCertEncodingType,
|
|---|
| 795 | X509_AUTHORITY_KEY_ID, ext->Value.pbData, ext->Value.cbData,
|
|---|
| 796 | CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
|
|---|
| 797 | &info, &size);
|
|---|
| 798 | if (ret)
|
|---|
| 799 | {
|
|---|
| 800 | CERT_ID id;
|
|---|
| 801 |
|
|---|
| 802 | if (info->CertIssuer.cbData && info->CertSerialNumber.cbData)
|
|---|
| 803 | {
|
|---|
| 804 | id.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
|
|---|
| 805 | memcpy(&id.u.IssuerSerialNumber.Issuer, &info->CertIssuer,
|
|---|
| 806 | sizeof(CERT_NAME_BLOB));
|
|---|
| 807 | memcpy(&id.u.IssuerSerialNumber.SerialNumber,
|
|---|
| 808 | &info->CertSerialNumber, sizeof(CRYPT_INTEGER_BLOB));
|
|---|
| 809 | issuer = CertFindCertificateInStore(store,
|
|---|
| 810 | subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
|
|---|
| 811 | prevIssuer);
|
|---|
| 812 | if (issuer)
|
|---|
| 813 | *infoStatus = CERT_TRUST_HAS_EXACT_MATCH_ISSUER;
|
|---|
| 814 | }
|
|---|
| 815 | else if (info->KeyId.cbData)
|
|---|
| 816 | {
|
|---|
| 817 | id.dwIdChoice = CERT_ID_KEY_IDENTIFIER;
|
|---|
| 818 | memcpy(&id.u.KeyId, &info->KeyId, sizeof(CRYPT_HASH_BLOB));
|
|---|
| 819 | issuer = CertFindCertificateInStore(store,
|
|---|
| 820 | subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
|
|---|
| 821 | prevIssuer);
|
|---|
| 822 | if (issuer)
|
|---|
| 823 | *infoStatus = CERT_TRUST_HAS_KEY_MATCH_ISSUER;
|
|---|
| 824 | }
|
|---|
| 825 | LocalFree((HANDLE)info);
|
|---|
| 826 | }
|
|---|
| 827 | }
|
|---|
| 828 | else if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER2,
|
|---|
| 829 | subject->pCertInfo->cExtension, subject->pCertInfo->rgExtension)) != NULL)
|
|---|
| 830 | {
|
|---|
| 831 | CERT_AUTHORITY_KEY_ID2_INFO *info;
|
|---|
| 832 | BOOL ret;
|
|---|
| 833 |
|
|---|
| 834 | ret = CryptDecodeObjectEx(subject->dwCertEncodingType,
|
|---|
| 835 | X509_AUTHORITY_KEY_ID2, ext->Value.pbData, ext->Value.cbData,
|
|---|
| 836 | CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
|
|---|
| 837 | &info, &size);
|
|---|
| 838 | if (ret)
|
|---|
| 839 | {
|
|---|
| 840 | CERT_ID id;
|
|---|
| 841 |
|
|---|
| 842 | if (info->AuthorityCertIssuer.cAltEntry &&
|
|---|
| 843 | info->AuthorityCertSerialNumber.cbData)
|
|---|
| 844 | {
|
|---|
| 845 | PCERT_ALT_NAME_ENTRY directoryName = NULL;
|
|---|
| 846 | DWORD i;
|
|---|
| 847 |
|
|---|
| 848 | for (i = 0; !directoryName &&
|
|---|
| 849 | i < info->AuthorityCertIssuer.cAltEntry; i++)
|
|---|
| 850 | if (info->AuthorityCertIssuer.rgAltEntry[i].dwAltNameChoice
|
|---|
| 851 | == CERT_ALT_NAME_DIRECTORY_NAME)
|
|---|
| 852 | directoryName =
|
|---|
| 853 | &info->AuthorityCertIssuer.rgAltEntry[i];
|
|---|
| 854 | if (directoryName)
|
|---|
| 855 | {
|
|---|
| 856 | id.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
|
|---|
| 857 | memcpy(&id.u.IssuerSerialNumber.Issuer,
|
|---|
| 858 | &directoryName->u.DirectoryName, sizeof(CERT_NAME_BLOB));
|
|---|
| 859 | memcpy(&id.u.IssuerSerialNumber.SerialNumber,
|
|---|
| 860 | &info->AuthorityCertSerialNumber,
|
|---|
| 861 | sizeof(CRYPT_INTEGER_BLOB));
|
|---|
| 862 | issuer = CertFindCertificateInStore(store,
|
|---|
| 863 | subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
|
|---|
| 864 | prevIssuer);
|
|---|
| 865 | if (issuer)
|
|---|
| 866 | *infoStatus = CERT_TRUST_HAS_EXACT_MATCH_ISSUER;
|
|---|
| 867 | }
|
|---|
| 868 | else
|
|---|
| 869 | FIXME("no supported name type in authority key id2\n");
|
|---|
| 870 | }
|
|---|
| 871 | else if (info->KeyId.cbData)
|
|---|
| 872 | {
|
|---|
| 873 | id.dwIdChoice = CERT_ID_KEY_IDENTIFIER;
|
|---|
| 874 | memcpy(&id.u.KeyId, &info->KeyId, sizeof(CRYPT_HASH_BLOB));
|
|---|
| 875 | issuer = CertFindCertificateInStore(store,
|
|---|
| 876 | subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
|
|---|
| 877 | prevIssuer);
|
|---|
| 878 | if (issuer)
|
|---|
| 879 | *infoStatus = CERT_TRUST_HAS_KEY_MATCH_ISSUER;
|
|---|
| 880 | }
|
|---|
| 881 | LocalFree((HANDLE)info);
|
|---|
| 882 | }
|
|---|
| 883 | }
|
|---|
| 884 | else
|
|---|
| 885 | {
|
|---|
| 886 | issuer = CertFindCertificateInStore(store,
|
|---|
| 887 | subject->dwCertEncodingType, 0, CERT_FIND_SUBJECT_NAME,
|
|---|
| 888 | &subject->pCertInfo->Issuer, prevIssuer);
|
|---|
| 889 | if (issuer)
|
|---|
| 890 | *infoStatus = CERT_TRUST_HAS_NAME_MATCH_ISSUER;
|
|---|
| 891 | }
|
|---|
| 892 | return issuer;
|
|---|
| 893 | }
|
|---|
| 894 |
|
|---|
| 895 | /* Builds a simple chain by finding an issuer for the last cert in the chain,
|
|---|
| 896 | * until reaching a self-signed cert, or until no issuer can be found.
|
|---|
| 897 | */
|
|---|
| 898 | static BOOL CRYPT_BuildSimpleChain(PCertificateChainEngine engine,
|
|---|
| 899 | HCERTSTORE world, PCERT_SIMPLE_CHAIN chain)
|
|---|
| 900 | {
|
|---|
| 901 | BOOL ret = TRUE;
|
|---|
| 902 | PCCERT_CONTEXT cert = chain->rgpElement[chain->cElement - 1]->pCertContext;
|
|---|
| 903 |
|
|---|
| 904 | while (ret && !CRYPT_IsSimpleChainCyclic(chain) &&
|
|---|
| 905 | !CRYPT_IsCertificateSelfSigned(cert))
|
|---|
| 906 | {
|
|---|
| 907 | DWORD infoStatus;
|
|---|
| 908 | PCCERT_CONTEXT issuer = CRYPT_GetIssuer(world, cert, NULL, &infoStatus);
|
|---|
| 909 |
|
|---|
| 910 | if (issuer)
|
|---|
| 911 | {
|
|---|
| 912 | ret = CRYPT_AddCertToSimpleChain(engine, chain, issuer, infoStatus);
|
|---|
| 913 | /* CRYPT_AddCertToSimpleChain add-ref's the issuer, so free it to
|
|---|
| 914 | * close the enumeration that found it
|
|---|
| 915 | */
|
|---|
| 916 | CertFreeCertificateContext(issuer);
|
|---|
| 917 | cert = issuer;
|
|---|
| 918 | }
|
|---|
| 919 | else
|
|---|
| 920 | {
|
|---|
| 921 | TRACE("Couldn't find issuer, halting chain creation\n");
|
|---|
| 922 | chain->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_PARTIAL_CHAIN;
|
|---|
| 923 | break;
|
|---|
| 924 | }
|
|---|
| 925 | }
|
|---|
| 926 | return ret;
|
|---|
| 927 | }
|
|---|
| 928 |
|
|---|
| 929 | static BOOL CRYPT_GetSimpleChainForCert(PCertificateChainEngine engine,
|
|---|
| 930 | HCERTSTORE world, PCCERT_CONTEXT cert, LPFILETIME pTime,
|
|---|
| 931 | PCERT_SIMPLE_CHAIN *ppChain)
|
|---|
| 932 | {
|
|---|
| 933 | BOOL ret = FALSE;
|
|---|
| 934 | PCERT_SIMPLE_CHAIN chain;
|
|---|
| 935 |
|
|---|
| 936 | TRACE("(%p, %p, %p, %p)\n", engine, world, cert, pTime);
|
|---|
| 937 |
|
|---|
| 938 | chain = CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN));
|
|---|
| 939 | if (chain)
|
|---|
| 940 | {
|
|---|
| 941 | memset(chain, 0, sizeof(CERT_SIMPLE_CHAIN));
|
|---|
| 942 | chain->cbSize = sizeof(CERT_SIMPLE_CHAIN);
|
|---|
| 943 | ret = CRYPT_AddCertToSimpleChain(engine, chain, cert, 0);
|
|---|
| 944 | if (ret)
|
|---|
| 945 | {
|
|---|
| 946 | ret = CRYPT_BuildSimpleChain(engine, world, chain);
|
|---|
| 947 | if (ret)
|
|---|
| 948 | CRYPT_CheckSimpleChain(engine, chain, pTime);
|
|---|
| 949 | }
|
|---|
| 950 | if (!ret)
|
|---|
| 951 | {
|
|---|
| 952 | CRYPT_FreeSimpleChain(chain);
|
|---|
| 953 | chain = NULL;
|
|---|
| 954 | }
|
|---|
| 955 | *ppChain = chain;
|
|---|
| 956 | }
|
|---|
| 957 | return ret;
|
|---|
| 958 | }
|
|---|
| 959 |
|
|---|
| 960 | static BOOL CRYPT_BuildCandidateChainFromCert(HCERTCHAINENGINE hChainEngine,
|
|---|
| 961 | PCCERT_CONTEXT cert, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
|
|---|
| 962 | PCertificateChain *ppChain)
|
|---|
| 963 | {
|
|---|
| 964 | PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
|
|---|
| 965 | PCERT_SIMPLE_CHAIN simpleChain = NULL;
|
|---|
| 966 | HCERTSTORE world;
|
|---|
| 967 | BOOL ret;
|
|---|
| 968 |
|
|---|
| 969 | world = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
|
|---|
| 970 | CERT_STORE_CREATE_NEW_FLAG, NULL);
|
|---|
| 971 | CertAddStoreToCollection(world, engine->hWorld, 0, 0);
|
|---|
| 972 | if (hAdditionalStore)
|
|---|
| 973 | CertAddStoreToCollection(world, hAdditionalStore, 0, 0);
|
|---|
| 974 | /* FIXME: only simple chains are supported for now, as CTLs aren't
|
|---|
| 975 | * supported yet.
|
|---|
| 976 | */
|
|---|
| 977 | if ((ret = CRYPT_GetSimpleChainForCert(engine, world, cert, pTime,
|
|---|
| 978 | &simpleChain)) != NULL)
|
|---|
| 979 | {
|
|---|
| 980 | PCertificateChain chain = CryptMemAlloc(sizeof(CertificateChain));
|
|---|
| 981 |
|
|---|
| 982 | if (chain)
|
|---|
| 983 | {
|
|---|
| 984 | chain->ref = 1;
|
|---|
| 985 | chain->world = world;
|
|---|
| 986 | chain->context.cbSize = sizeof(CERT_CHAIN_CONTEXT);
|
|---|
| 987 | chain->context.TrustStatus = simpleChain->TrustStatus;
|
|---|
| 988 | chain->context.cChain = 1;
|
|---|
| 989 | chain->context.rgpChain = CryptMemAlloc(sizeof(PCERT_SIMPLE_CHAIN));
|
|---|
| 990 | chain->context.rgpChain[0] = simpleChain;
|
|---|
| 991 | chain->context.cLowerQualityChainContext = 0;
|
|---|
| 992 | chain->context.rgpLowerQualityChainContext = NULL;
|
|---|
| 993 | chain->context.fHasRevocationFreshnessTime = FALSE;
|
|---|
| 994 | chain->context.dwRevocationFreshnessTime = 0;
|
|---|
| 995 | }
|
|---|
| 996 | else
|
|---|
| 997 | ret = FALSE;
|
|---|
| 998 | *ppChain = chain;
|
|---|
| 999 | }
|
|---|
| 1000 | return ret;
|
|---|
| 1001 | }
|
|---|
| 1002 |
|
|---|
| 1003 | /* Makes and returns a copy of chain, up to and including element iElement. */
|
|---|
| 1004 | static PCERT_SIMPLE_CHAIN CRYPT_CopySimpleChainToElement(
|
|---|
| 1005 | PCERT_SIMPLE_CHAIN chain, DWORD iElement)
|
|---|
| 1006 | {
|
|---|
| 1007 | PCERT_SIMPLE_CHAIN copy = CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN));
|
|---|
| 1008 |
|
|---|
| 1009 | if (copy)
|
|---|
| 1010 | {
|
|---|
| 1011 | memset(copy, 0, sizeof(CERT_SIMPLE_CHAIN));
|
|---|
| 1012 | copy->cbSize = sizeof(CERT_SIMPLE_CHAIN);
|
|---|
| 1013 | copy->rgpElement =
|
|---|
| 1014 | CryptMemAlloc((iElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
|
|---|
| 1015 | if (copy->rgpElement)
|
|---|
| 1016 | {
|
|---|
| 1017 | DWORD i;
|
|---|
| 1018 | BOOL ret = TRUE;
|
|---|
| 1019 |
|
|---|
| 1020 | memset(copy->rgpElement, 0,
|
|---|
| 1021 | (iElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
|
|---|
| 1022 | for (i = 0; ret && i <= iElement; i++)
|
|---|
| 1023 | {
|
|---|
| 1024 | PCERT_CHAIN_ELEMENT element =
|
|---|
| 1025 | CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
|
|---|
| 1026 |
|
|---|
| 1027 | if (element)
|
|---|
| 1028 | {
|
|---|
| 1029 | *element = *chain->rgpElement[i];
|
|---|
| 1030 | element->pCertContext = CertDuplicateCertificateContext(
|
|---|
| 1031 | chain->rgpElement[i]->pCertContext);
|
|---|
| 1032 | /* Reset the trust status of the copied element, it'll get
|
|---|
| 1033 | * rechecked after the new chain is done.
|
|---|
| 1034 | */
|
|---|
| 1035 | memset(&element->TrustStatus, 0, sizeof(CERT_TRUST_STATUS));
|
|---|
| 1036 | copy->rgpElement[copy->cElement++] = element;
|
|---|
| 1037 | }
|
|---|
| 1038 | else
|
|---|
| 1039 | ret = FALSE;
|
|---|
| 1040 | }
|
|---|
| 1041 | if (!ret)
|
|---|
| 1042 | {
|
|---|
| 1043 | for (i = 0; i <= iElement; i++)
|
|---|
| 1044 | CryptMemFree(copy->rgpElement[i]);
|
|---|
| 1045 | CryptMemFree(copy->rgpElement);
|
|---|
| 1046 | CryptMemFree(copy);
|
|---|
| 1047 | copy = NULL;
|
|---|
| 1048 | }
|
|---|
| 1049 | }
|
|---|
| 1050 | else
|
|---|
| 1051 | {
|
|---|
| 1052 | CryptMemFree(copy);
|
|---|
| 1053 | copy = NULL;
|
|---|
| 1054 | }
|
|---|
| 1055 | }
|
|---|
| 1056 | return copy;
|
|---|
| 1057 | }
|
|---|
| 1058 |
|
|---|
| 1059 | static void CRYPT_FreeLowerQualityChains(PCertificateChain chain)
|
|---|
| 1060 | {
|
|---|
| 1061 | DWORD i;
|
|---|
| 1062 |
|
|---|
| 1063 | for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
|
|---|
| 1064 | CertFreeCertificateChain(chain->context.rgpLowerQualityChainContext[i]);
|
|---|
| 1065 | CryptMemFree(chain->context.rgpLowerQualityChainContext);
|
|---|
| 1066 | chain->context.cLowerQualityChainContext = 0;
|
|---|
| 1067 | chain->context.rgpLowerQualityChainContext = NULL;
|
|---|
| 1068 | }
|
|---|
| 1069 |
|
|---|
| 1070 | static void CRYPT_FreeChainContext(PCertificateChain chain)
|
|---|
| 1071 | {
|
|---|
| 1072 | DWORD i;
|
|---|
| 1073 |
|
|---|
| 1074 | CRYPT_FreeLowerQualityChains(chain);
|
|---|
| 1075 | for (i = 0; i < chain->context.cChain; i++)
|
|---|
| 1076 | CRYPT_FreeSimpleChain(chain->context.rgpChain[i]);
|
|---|
| 1077 | CryptMemFree(chain->context.rgpChain);
|
|---|
| 1078 | CertCloseStore(chain->world, 0);
|
|---|
| 1079 | CryptMemFree(chain);
|
|---|
| 1080 | }
|
|---|
| 1081 |
|
|---|
| 1082 | /* Makes and returns a copy of chain, up to and including element iElement of
|
|---|
| 1083 | * simple chain iChain.
|
|---|
| 1084 | */
|
|---|
| 1085 | static PCertificateChain CRYPT_CopyChainToElement(PCertificateChain chain,
|
|---|
| 1086 | DWORD iChain, DWORD iElement)
|
|---|
| 1087 | {
|
|---|
| 1088 | PCertificateChain copy = CryptMemAlloc(sizeof(CertificateChain));
|
|---|
| 1089 |
|
|---|
| 1090 | if (copy)
|
|---|
| 1091 | {
|
|---|
| 1092 | copy->ref = 1;
|
|---|
| 1093 | copy->world = CertDuplicateStore(chain->world);
|
|---|
| 1094 | copy->context.cbSize = sizeof(CERT_CHAIN_CONTEXT);
|
|---|
| 1095 | /* Leave the trust status of the copied chain unset, it'll get
|
|---|
| 1096 | * rechecked after the new chain is done.
|
|---|
| 1097 | */
|
|---|
| 1098 | memset(©->context.TrustStatus, 0, sizeof(CERT_TRUST_STATUS));
|
|---|
| 1099 | copy->context.cLowerQualityChainContext = 0;
|
|---|
| 1100 | copy->context.rgpLowerQualityChainContext = NULL;
|
|---|
| 1101 | copy->context.fHasRevocationFreshnessTime = FALSE;
|
|---|
| 1102 | copy->context.dwRevocationFreshnessTime = 0;
|
|---|
| 1103 | copy->context.rgpChain = CryptMemAlloc(
|
|---|
| 1104 | (iChain + 1) * sizeof(PCERT_SIMPLE_CHAIN));
|
|---|
| 1105 | if (copy->context.rgpChain)
|
|---|
| 1106 | {
|
|---|
| 1107 | BOOL ret = TRUE;
|
|---|
| 1108 | DWORD i;
|
|---|
| 1109 |
|
|---|
| 1110 | memset(copy->context.rgpChain, 0,
|
|---|
| 1111 | (iChain + 1) * sizeof(PCERT_SIMPLE_CHAIN));
|
|---|
| 1112 | if (iChain)
|
|---|
| 1113 | {
|
|---|
| 1114 | for (i = 0; ret && iChain && i < iChain - 1; i++)
|
|---|
| 1115 | {
|
|---|
| 1116 | copy->context.rgpChain[i] =
|
|---|
| 1117 | CRYPT_CopySimpleChainToElement(chain->context.rgpChain[i],
|
|---|
| 1118 | chain->context.rgpChain[i]->cElement - 1);
|
|---|
| 1119 | if (!copy->context.rgpChain[i])
|
|---|
| 1120 | ret = FALSE;
|
|---|
| 1121 | }
|
|---|
| 1122 | }
|
|---|
| 1123 | else
|
|---|
| 1124 | i = 0;
|
|---|
| 1125 | if (ret)
|
|---|
| 1126 | {
|
|---|
| 1127 | copy->context.rgpChain[i] =
|
|---|
| 1128 | CRYPT_CopySimpleChainToElement(chain->context.rgpChain[i],
|
|---|
| 1129 | iElement);
|
|---|
| 1130 | if (!copy->context.rgpChain[i])
|
|---|
| 1131 | ret = FALSE;
|
|---|
| 1132 | }
|
|---|
| 1133 | if (!ret)
|
|---|
| 1134 | {
|
|---|
| 1135 | CRYPT_FreeChainContext(copy);
|
|---|
| 1136 | copy = NULL;
|
|---|
| 1137 | }
|
|---|
| 1138 | else
|
|---|
| 1139 | copy->context.cChain = iChain + 1;
|
|---|
| 1140 | }
|
|---|
| 1141 | else
|
|---|
| 1142 | {
|
|---|
| 1143 | CryptMemFree(copy);
|
|---|
| 1144 | copy = NULL;
|
|---|
| 1145 | }
|
|---|
| 1146 | }
|
|---|
| 1147 | return copy;
|
|---|
| 1148 | }
|
|---|
| 1149 |
|
|---|
| 1150 | static PCertificateChain CRYPT_BuildAlternateContextFromChain(
|
|---|
| 1151 | HCERTCHAINENGINE hChainEngine, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
|
|---|
| 1152 | PCertificateChain chain)
|
|---|
| 1153 | {
|
|---|
| 1154 | PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
|
|---|
| 1155 | PCertificateChain alternate;
|
|---|
| 1156 |
|
|---|
| 1157 | TRACE("(%p, %p, %p, %p)\n", hChainEngine, pTime, hAdditionalStore, chain);
|
|---|
| 1158 |
|
|---|
| 1159 | /* Always start with the last "lower quality" chain to ensure a consistent
|
|---|
| 1160 | * order of alternate creation:
|
|---|
| 1161 | */
|
|---|
| 1162 | if (chain->context.cLowerQualityChainContext)
|
|---|
| 1163 | chain = (PCertificateChain)chain->context.rgpLowerQualityChainContext[
|
|---|
| 1164 | chain->context.cLowerQualityChainContext - 1];
|
|---|
| 1165 | /* A chain with only one element can't have any alternates */
|
|---|
| 1166 | if (chain->context.cChain <= 1 && chain->context.rgpChain[0]->cElement <= 1)
|
|---|
| 1167 | alternate = NULL;
|
|---|
| 1168 | else
|
|---|
| 1169 | {
|
|---|
| 1170 | DWORD i, j, infoStatus;
|
|---|
| 1171 | PCCERT_CONTEXT alternateIssuer = NULL;
|
|---|
| 1172 |
|
|---|
| 1173 | alternate = NULL;
|
|---|
| 1174 | for (i = 0; !alternateIssuer && i < chain->context.cChain; i++)
|
|---|
| 1175 | for (j = 0; !alternateIssuer &&
|
|---|
| 1176 | j < chain->context.rgpChain[i]->cElement - 1; j++)
|
|---|
| 1177 | {
|
|---|
| 1178 | PCCERT_CONTEXT subject =
|
|---|
| 1179 | chain->context.rgpChain[i]->rgpElement[j]->pCertContext;
|
|---|
| 1180 | PCCERT_CONTEXT prevIssuer = CertDuplicateCertificateContext(
|
|---|
| 1181 | chain->context.rgpChain[i]->rgpElement[j + 1]->pCertContext);
|
|---|
| 1182 |
|
|---|
| 1183 | alternateIssuer = CRYPT_GetIssuer(prevIssuer->hCertStore,
|
|---|
| 1184 | subject, prevIssuer, &infoStatus);
|
|---|
| 1185 | }
|
|---|
| 1186 | if (alternateIssuer)
|
|---|
| 1187 | {
|
|---|
| 1188 | i--;
|
|---|
| 1189 | j--;
|
|---|
| 1190 | alternate = CRYPT_CopyChainToElement(chain, i, j);
|
|---|
| 1191 | if (alternate)
|
|---|
| 1192 | {
|
|---|
| 1193 | BOOL ret = CRYPT_AddCertToSimpleChain(engine,
|
|---|
| 1194 | alternate->context.rgpChain[i], alternateIssuer, infoStatus);
|
|---|
| 1195 |
|
|---|
| 1196 | /* CRYPT_AddCertToSimpleChain add-ref's the issuer, so free it
|
|---|
| 1197 | * to close the enumeration that found it
|
|---|
| 1198 | */
|
|---|
| 1199 | CertFreeCertificateContext(alternateIssuer);
|
|---|
| 1200 | if (ret)
|
|---|
| 1201 | {
|
|---|
| 1202 | ret = CRYPT_BuildSimpleChain(engine, alternate->world,
|
|---|
| 1203 | alternate->context.rgpChain[i]);
|
|---|
| 1204 | if (ret)
|
|---|
| 1205 | CRYPT_CheckSimpleChain(engine,
|
|---|
| 1206 | alternate->context.rgpChain[i], pTime);
|
|---|
| 1207 | CRYPT_CombineTrustStatus(&alternate->context.TrustStatus,
|
|---|
| 1208 | &alternate->context.rgpChain[i]->TrustStatus);
|
|---|
| 1209 | }
|
|---|
| 1210 | if (!ret)
|
|---|
| 1211 | {
|
|---|
| 1212 | CRYPT_FreeChainContext(alternate);
|
|---|
| 1213 | alternate = NULL;
|
|---|
| 1214 | }
|
|---|
| 1215 | }
|
|---|
| 1216 | }
|
|---|
| 1217 | }
|
|---|
| 1218 | TRACE("%p\n", alternate);
|
|---|
| 1219 | return alternate;
|
|---|
| 1220 | }
|
|---|
| 1221 |
|
|---|
| 1222 | #define CHAIN_QUALITY_SIGNATURE_VALID 8
|
|---|
| 1223 | #define CHAIN_QUALITY_TIME_VALID 4
|
|---|
| 1224 | #define CHAIN_QUALITY_COMPLETE_CHAIN 2
|
|---|
| 1225 | #define CHAIN_QUALITY_TRUSTED_ROOT 1
|
|---|
| 1226 |
|
|---|
| 1227 | #define CHAIN_QUALITY_HIGHEST \
|
|---|
| 1228 | CHAIN_QUALITY_SIGNATURE_VALID | CHAIN_QUALITY_TIME_VALID | \
|
|---|
| 1229 | CHAIN_QUALITY_COMPLETE_CHAIN | CHAIN_QUALITY_TRUSTED_ROOT
|
|---|
| 1230 |
|
|---|
| 1231 | #define IS_TRUST_ERROR_SET(TrustStatus, bits) \
|
|---|
| 1232 | (TrustStatus)->dwErrorStatus & (bits)
|
|---|
| 1233 |
|
|---|
| 1234 | static DWORD CRYPT_ChainQuality(PCertificateChain chain)
|
|---|
| 1235 | {
|
|---|
| 1236 | DWORD quality = CHAIN_QUALITY_HIGHEST;
|
|---|
| 1237 |
|
|---|
| 1238 | if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
|
|---|
| 1239 | CERT_TRUST_IS_UNTRUSTED_ROOT))
|
|---|
| 1240 | quality &= ~CHAIN_QUALITY_TRUSTED_ROOT;
|
|---|
| 1241 | if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
|
|---|
| 1242 | CERT_TRUST_IS_PARTIAL_CHAIN))
|
|---|
| 1243 | if (chain->context.TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN)
|
|---|
| 1244 | quality &= ~CHAIN_QUALITY_COMPLETE_CHAIN;
|
|---|
| 1245 | if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
|
|---|
| 1246 | CERT_TRUST_IS_NOT_TIME_VALID | CERT_TRUST_IS_NOT_TIME_NESTED))
|
|---|
| 1247 | quality &= ~CHAIN_QUALITY_TIME_VALID;
|
|---|
| 1248 | if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
|
|---|
| 1249 | CERT_TRUST_IS_NOT_SIGNATURE_VALID))
|
|---|
| 1250 | quality &= ~CHAIN_QUALITY_SIGNATURE_VALID;
|
|---|
| 1251 | return quality;
|
|---|
| 1252 | }
|
|---|
| 1253 |
|
|---|
| 1254 | /* Chooses the highest quality chain among chain and its "lower quality"
|
|---|
| 1255 | * alternate chains. Returns the highest quality chain, with all other
|
|---|
| 1256 | * chains as lower quality chains of it.
|
|---|
| 1257 | */
|
|---|
| 1258 | static PCertificateChain CRYPT_ChooseHighestQualityChain(
|
|---|
| 1259 | PCertificateChain chain)
|
|---|
| 1260 | {
|
|---|
| 1261 | DWORD i;
|
|---|
| 1262 |
|
|---|
| 1263 | /* There are always only two chains being considered: chain, and an
|
|---|
| 1264 | * alternate at chain->rgpLowerQualityChainContext[i]. If the alternate
|
|---|
| 1265 | * has a higher quality than chain, the alternate gets assigned the lower
|
|---|
| 1266 | * quality contexts, with chain taking the alternate's place among the
|
|---|
| 1267 | * lower quality contexts.
|
|---|
| 1268 | */
|
|---|
| 1269 | for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
|
|---|
| 1270 | {
|
|---|
| 1271 | PCertificateChain alternate =
|
|---|
| 1272 | (PCertificateChain)chain->context.rgpLowerQualityChainContext[i];
|
|---|
| 1273 |
|
|---|
| 1274 | if (CRYPT_ChainQuality(alternate) > CRYPT_ChainQuality(chain))
|
|---|
| 1275 | {
|
|---|
| 1276 | alternate->context.cLowerQualityChainContext =
|
|---|
| 1277 | chain->context.cLowerQualityChainContext;
|
|---|
| 1278 | alternate->context.rgpLowerQualityChainContext =
|
|---|
| 1279 | chain->context.rgpLowerQualityChainContext;
|
|---|
| 1280 | alternate->context.rgpLowerQualityChainContext[i] =
|
|---|
| 1281 | (PCCERT_CHAIN_CONTEXT)chain;
|
|---|
| 1282 | chain->context.cLowerQualityChainContext = 0;
|
|---|
| 1283 | chain->context.rgpLowerQualityChainContext = NULL;
|
|---|
| 1284 | chain = alternate;
|
|---|
| 1285 | }
|
|---|
| 1286 | }
|
|---|
| 1287 | return chain;
|
|---|
| 1288 | }
|
|---|
| 1289 |
|
|---|
| 1290 | static BOOL CRYPT_AddAlternateChainToChain(PCertificateChain chain,
|
|---|
| 1291 | PCertificateChain alternate)
|
|---|
| 1292 | {
|
|---|
| 1293 | BOOL ret;
|
|---|
| 1294 |
|
|---|
| 1295 | if (chain->context.cLowerQualityChainContext)
|
|---|
| 1296 | chain->context.rgpLowerQualityChainContext =
|
|---|
| 1297 | CryptMemRealloc(chain->context.rgpLowerQualityChainContext,
|
|---|
| 1298 | (chain->context.cLowerQualityChainContext + 1) *
|
|---|
| 1299 | sizeof(PCCERT_CHAIN_CONTEXT));
|
|---|
| 1300 | else
|
|---|
| 1301 | chain->context.rgpLowerQualityChainContext =
|
|---|
| 1302 | CryptMemAlloc(sizeof(PCCERT_CHAIN_CONTEXT));
|
|---|
| 1303 | if (chain->context.rgpLowerQualityChainContext)
|
|---|
| 1304 | {
|
|---|
| 1305 | chain->context.rgpLowerQualityChainContext[
|
|---|
| 1306 | chain->context.cLowerQualityChainContext++] =
|
|---|
| 1307 | (PCCERT_CHAIN_CONTEXT)alternate;
|
|---|
| 1308 | ret = TRUE;
|
|---|
| 1309 | }
|
|---|
| 1310 | else
|
|---|
| 1311 | ret = FALSE;
|
|---|
| 1312 | return ret;
|
|---|
| 1313 | }
|
|---|
| 1314 |
|
|---|
| 1315 | static PCERT_CHAIN_ELEMENT CRYPT_FindIthElementInChain(
|
|---|
| 1316 | PCERT_CHAIN_CONTEXT chain, DWORD i)
|
|---|
| 1317 | {
|
|---|
| 1318 | DWORD j, iElement;
|
|---|
| 1319 | PCERT_CHAIN_ELEMENT element = NULL;
|
|---|
| 1320 |
|
|---|
| 1321 | for (j = 0, iElement = 0; !element && j < chain->cChain; j++)
|
|---|
| 1322 | {
|
|---|
| 1323 | if (iElement + chain->rgpChain[j]->cElement < i)
|
|---|
| 1324 | iElement += chain->rgpChain[j]->cElement;
|
|---|
| 1325 | else
|
|---|
| 1326 | element = chain->rgpChain[j]->rgpElement[i - iElement];
|
|---|
| 1327 | }
|
|---|
| 1328 | return element;
|
|---|
| 1329 | }
|
|---|
| 1330 |
|
|---|
| 1331 | typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS {
|
|---|
| 1332 | DWORD cbSize;
|
|---|
| 1333 | CERT_USAGE_MATCH RequestedUsage;
|
|---|
| 1334 | } CERT_CHAIN_PARA_NO_EXTRA_FIELDS, *PCERT_CHAIN_PARA_NO_EXTRA_FIELDS;
|
|---|
| 1335 |
|
|---|
| 1336 | static void CRYPT_VerifyChainRevocation(PCERT_CHAIN_CONTEXT chain,
|
|---|
| 1337 | LPFILETIME pTime, PCERT_CHAIN_PARA pChainPara, DWORD chainFlags)
|
|---|
| 1338 | {
|
|---|
| 1339 | DWORD cContext;
|
|---|
| 1340 |
|
|---|
| 1341 | if (chainFlags & CERT_CHAIN_REVOCATION_CHECK_END_CERT)
|
|---|
| 1342 | cContext = 1;
|
|---|
| 1343 | else if ((chainFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN) ||
|
|---|
| 1344 | (chainFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT))
|
|---|
| 1345 | {
|
|---|
| 1346 | DWORD i;
|
|---|
| 1347 |
|
|---|
| 1348 | for (i = 0, cContext = 0; i < chain->cChain; i++)
|
|---|
| 1349 | {
|
|---|
| 1350 | if (i < chain->cChain - 1 ||
|
|---|
| 1351 | chainFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN)
|
|---|
| 1352 | cContext += chain->rgpChain[i]->cElement;
|
|---|
| 1353 | else
|
|---|
| 1354 | cContext += chain->rgpChain[i]->cElement - 1;
|
|---|
| 1355 | }
|
|---|
| 1356 | }
|
|---|
| 1357 | else
|
|---|
| 1358 | cContext = 0;
|
|---|
| 1359 | if (cContext)
|
|---|
| 1360 | {
|
|---|
| 1361 | PCCERT_CONTEXT *contexts =
|
|---|
| 1362 | CryptMemAlloc(cContext * sizeof(PCCERT_CONTEXT *));
|
|---|
| 1363 |
|
|---|
| 1364 | if (contexts)
|
|---|
| 1365 | {
|
|---|
| 1366 | DWORD i, j, iContext, revocationFlags;
|
|---|
| 1367 | CERT_REVOCATION_PARA revocationPara = { sizeof(revocationPara), 0 };
|
|---|
| 1368 | CERT_REVOCATION_STATUS revocationStatus =
|
|---|
| 1369 | { sizeof(revocationStatus), 0 };
|
|---|
| 1370 | BOOL ret;
|
|---|
| 1371 |
|
|---|
| 1372 | for (i = 0, iContext = 0; iContext < cContext && i < chain->cChain;
|
|---|
| 1373 | i++)
|
|---|
| 1374 | {
|
|---|
| 1375 | for (j = 0; iContext < cContext &&
|
|---|
| 1376 | j < chain->rgpChain[i]->cElement; j++)
|
|---|
| 1377 | contexts[iContext++] =
|
|---|
| 1378 | chain->rgpChain[i]->rgpElement[j]->pCertContext;
|
|---|
| 1379 | }
|
|---|
| 1380 | revocationFlags = CERT_VERIFY_REV_CHAIN_FLAG;
|
|---|
| 1381 | if (chainFlags & CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY)
|
|---|
| 1382 | revocationFlags |= CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION;
|
|---|
| 1383 | if (chainFlags & CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT)
|
|---|
| 1384 | revocationFlags |= CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG;
|
|---|
| 1385 | revocationPara.pftTimeToUse = pTime;
|
|---|
| 1386 | if (pChainPara->cbSize == sizeof(CERT_CHAIN_PARA))
|
|---|
| 1387 | {
|
|---|
| 1388 | revocationPara.dwUrlRetrievalTimeout =
|
|---|
| 1389 | pChainPara->dwUrlRetrievalTimeout;
|
|---|
| 1390 | revocationPara.fCheckFreshnessTime =
|
|---|
| 1391 | pChainPara->fCheckRevocationFreshnessTime;
|
|---|
| 1392 | revocationPara.dwFreshnessTime =
|
|---|
| 1393 | pChainPara->dwRevocationFreshnessTime;
|
|---|
| 1394 | }
|
|---|
| 1395 | ret = CertVerifyRevocation(X509_ASN_ENCODING,
|
|---|
| 1396 | CERT_CONTEXT_REVOCATION_TYPE, cContext, (void **)contexts,
|
|---|
| 1397 | revocationFlags, &revocationPara, &revocationStatus);
|
|---|
| 1398 | if (!ret)
|
|---|
| 1399 | {
|
|---|
| 1400 | PCERT_CHAIN_ELEMENT element =
|
|---|
| 1401 | CRYPT_FindIthElementInChain(chain, revocationStatus.dwIndex);
|
|---|
| 1402 | DWORD error;
|
|---|
| 1403 |
|
|---|
| 1404 | switch (revocationStatus.dwError)
|
|---|
| 1405 | {
|
|---|
| 1406 | case CRYPT_E_NO_REVOCATION_CHECK:
|
|---|
| 1407 | case CRYPT_E_NO_REVOCATION_DLL:
|
|---|
| 1408 | case CRYPT_E_NOT_IN_REVOCATION_DATABASE:
|
|---|
| 1409 | error = CERT_TRUST_REVOCATION_STATUS_UNKNOWN;
|
|---|
| 1410 | break;
|
|---|
| 1411 | case CRYPT_E_REVOCATION_OFFLINE:
|
|---|
| 1412 | error = CERT_TRUST_IS_OFFLINE_REVOCATION;
|
|---|
| 1413 | break;
|
|---|
| 1414 | case CRYPT_E_REVOKED:
|
|---|
| 1415 | error = CERT_TRUST_IS_REVOKED;
|
|---|
| 1416 | break;
|
|---|
| 1417 | default:
|
|---|
| 1418 | WARN("unmapped error %08x\n", revocationStatus.dwError);
|
|---|
| 1419 | error = 0;
|
|---|
| 1420 | }
|
|---|
| 1421 | if (element)
|
|---|
| 1422 | {
|
|---|
| 1423 | /* FIXME: set element's pRevocationInfo member */
|
|---|
| 1424 | element->TrustStatus.dwErrorStatus |= error;
|
|---|
| 1425 | }
|
|---|
| 1426 | chain->TrustStatus.dwErrorStatus |= error;
|
|---|
| 1427 | }
|
|---|
| 1428 | CryptMemFree(contexts);
|
|---|
| 1429 | }
|
|---|
| 1430 | }
|
|---|
| 1431 | }
|
|---|
| 1432 |
|
|---|
| 1433 | BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
|
|---|
| 1434 | PCCERT_CONTEXT pCertContext, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
|
|---|
| 1435 | PCERT_CHAIN_PARA pChainPara, DWORD dwFlags, LPVOID pvReserved,
|
|---|
| 1436 | PCCERT_CHAIN_CONTEXT* ppChainContext)
|
|---|
| 1437 | {
|
|---|
| 1438 | BOOL ret;
|
|---|
| 1439 | PCertificateChain chain = NULL;
|
|---|
| 1440 |
|
|---|
| 1441 | TRACE("(%p, %p, %p, %p, %p, %08x, %p, %p)\n", hChainEngine, pCertContext,
|
|---|
| 1442 | pTime, hAdditionalStore, pChainPara, dwFlags, pvReserved, ppChainContext);
|
|---|
| 1443 |
|
|---|
| 1444 | if (ppChainContext)
|
|---|
| 1445 | *ppChainContext = NULL;
|
|---|
| 1446 | if (!pChainPara)
|
|---|
| 1447 | {
|
|---|
| 1448 | SetLastError(E_INVALIDARG);
|
|---|
| 1449 | return FALSE;
|
|---|
| 1450 | }
|
|---|
| 1451 | if (!pCertContext->pCertInfo->SignatureAlgorithm.pszObjId)
|
|---|
| 1452 | {
|
|---|
| 1453 | SetLastError(ERROR_INVALID_DATA);
|
|---|
| 1454 | return FALSE;
|
|---|
| 1455 | }
|
|---|
| 1456 | if (pChainPara->cbSize != sizeof(CERT_CHAIN_PARA_NO_EXTRA_FIELDS) &&
|
|---|
| 1457 | pChainPara->cbSize != sizeof(CERT_CHAIN_PARA))
|
|---|
| 1458 | {
|
|---|
| 1459 | SetLastError(E_INVALIDARG);
|
|---|
| 1460 | return FALSE;
|
|---|
| 1461 | }
|
|---|
| 1462 | if (!hChainEngine)
|
|---|
| 1463 | hChainEngine = CRYPT_GetDefaultChainEngine();
|
|---|
| 1464 | /* FIXME: what about HCCE_LOCAL_MACHINE? */
|
|---|
| 1465 | ret = CRYPT_BuildCandidateChainFromCert(hChainEngine, pCertContext, pTime,
|
|---|
| 1466 | hAdditionalStore, &chain);
|
|---|
| 1467 | if (ret)
|
|---|
| 1468 | {
|
|---|
| 1469 | PCertificateChain alternate = NULL;
|
|---|
| 1470 | PCERT_CHAIN_CONTEXT pChain;
|
|---|
| 1471 |
|
|---|
| 1472 | do {
|
|---|
| 1473 | alternate = CRYPT_BuildAlternateContextFromChain(hChainEngine,
|
|---|
| 1474 | pTime, hAdditionalStore, chain);
|
|---|
| 1475 |
|
|---|
| 1476 | /* Alternate contexts are added as "lower quality" contexts of
|
|---|
| 1477 | * chain, to avoid loops in alternate chain creation.
|
|---|
| 1478 | * The highest-quality chain is chosen at the end.
|
|---|
| 1479 | */
|
|---|
| 1480 | if (alternate)
|
|---|
| 1481 | ret = CRYPT_AddAlternateChainToChain(chain, alternate);
|
|---|
| 1482 | } while (ret && alternate);
|
|---|
| 1483 | chain = CRYPT_ChooseHighestQualityChain(chain);
|
|---|
| 1484 | if (!(dwFlags & CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS))
|
|---|
| 1485 | CRYPT_FreeLowerQualityChains(chain);
|
|---|
| 1486 | pChain = (PCERT_CHAIN_CONTEXT)chain;
|
|---|
| 1487 | CRYPT_VerifyChainRevocation(pChain, pTime, pChainPara, dwFlags);
|
|---|
| 1488 | if (ppChainContext)
|
|---|
| 1489 | *ppChainContext = pChain;
|
|---|
| 1490 | else
|
|---|
| 1491 | CertFreeCertificateChain(pChain);
|
|---|
| 1492 | }
|
|---|
| 1493 | TRACE("returning %d\n", ret);
|
|---|
| 1494 | return ret;
|
|---|
| 1495 | }
|
|---|
| 1496 |
|
|---|
| 1497 | PCCERT_CHAIN_CONTEXT WINAPI CertDuplicateCertificateChain(
|
|---|
| 1498 | PCCERT_CHAIN_CONTEXT pChainContext)
|
|---|
| 1499 | {
|
|---|
| 1500 | PCertificateChain chain = (PCertificateChain)pChainContext;
|
|---|
| 1501 |
|
|---|
| 1502 | TRACE("(%p)\n", pChainContext);
|
|---|
| 1503 |
|
|---|
| 1504 | if (chain)
|
|---|
| 1505 | InterlockedIncrement(&chain->ref);
|
|---|
| 1506 | return pChainContext;
|
|---|
| 1507 | }
|
|---|
| 1508 |
|
|---|
| 1509 | VOID WINAPI CertFreeCertificateChain(PCCERT_CHAIN_CONTEXT pChainContext)
|
|---|
| 1510 | {
|
|---|
| 1511 | PCertificateChain chain = (PCertificateChain)pChainContext;
|
|---|
| 1512 |
|
|---|
| 1513 | TRACE("(%p)\n", pChainContext);
|
|---|
| 1514 |
|
|---|
| 1515 | if (chain)
|
|---|
| 1516 | {
|
|---|
| 1517 | if (InterlockedDecrement(&chain->ref) == 0)
|
|---|
| 1518 | CRYPT_FreeChainContext(chain);
|
|---|
| 1519 | }
|
|---|
| 1520 | }
|
|---|
| 1521 |
|
|---|
| 1522 | static void find_element_with_error(PCCERT_CHAIN_CONTEXT chain, DWORD error,
|
|---|
| 1523 | LONG *iChain, LONG *iElement)
|
|---|
| 1524 | {
|
|---|
| 1525 | DWORD i, j;
|
|---|
| 1526 |
|
|---|
| 1527 | for (i = 0; i < chain->cChain; i++)
|
|---|
| 1528 | for (j = 0; j < chain->rgpChain[i]->cElement; j++)
|
|---|
| 1529 | if (chain->rgpChain[i]->rgpElement[j]->TrustStatus.dwErrorStatus &
|
|---|
| 1530 | error)
|
|---|
| 1531 | {
|
|---|
| 1532 | *iChain = i;
|
|---|
| 1533 | *iElement = j;
|
|---|
| 1534 | return;
|
|---|
| 1535 | }
|
|---|
| 1536 | }
|
|---|
| 1537 |
|
|---|
| 1538 | static BOOL WINAPI verify_base_policy(LPCSTR szPolicyOID,
|
|---|
| 1539 | PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|---|
| 1540 | PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
|
|---|
| 1541 | {
|
|---|
| 1542 | pPolicyStatus->lChainIndex = pPolicyStatus->lElementIndex = -1;
|
|---|
| 1543 | if (pChainContext->TrustStatus.dwErrorStatus &
|
|---|
| 1544 | CERT_TRUST_IS_NOT_SIGNATURE_VALID)
|
|---|
| 1545 | {
|
|---|
| 1546 | pPolicyStatus->dwError = TRUST_E_CERT_SIGNATURE;
|
|---|
| 1547 | find_element_with_error(pChainContext,
|
|---|
| 1548 | CERT_TRUST_IS_NOT_SIGNATURE_VALID, &pPolicyStatus->lChainIndex,
|
|---|
| 1549 | &pPolicyStatus->lElementIndex);
|
|---|
| 1550 | }
|
|---|
| 1551 | else if (pChainContext->TrustStatus.dwErrorStatus &
|
|---|
| 1552 | CERT_TRUST_IS_UNTRUSTED_ROOT)
|
|---|
| 1553 | {
|
|---|
| 1554 | pPolicyStatus->dwError = CERT_E_UNTRUSTEDROOT;
|
|---|
| 1555 | find_element_with_error(pChainContext,
|
|---|
| 1556 | CERT_TRUST_IS_UNTRUSTED_ROOT, &pPolicyStatus->lChainIndex,
|
|---|
| 1557 | &pPolicyStatus->lElementIndex);
|
|---|
| 1558 | }
|
|---|
| 1559 | else if (pChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_CYCLIC)
|
|---|
| 1560 | {
|
|---|
| 1561 | pPolicyStatus->dwError = CERT_E_CHAINING;
|
|---|
| 1562 | find_element_with_error(pChainContext, CERT_TRUST_IS_CYCLIC,
|
|---|
| 1563 | &pPolicyStatus->lChainIndex, &pPolicyStatus->lElementIndex);
|
|---|
| 1564 | /* For a cyclic chain, which element is a cycle isn't meaningful */
|
|---|
| 1565 | pPolicyStatus->lElementIndex = -1;
|
|---|
| 1566 | }
|
|---|
| 1567 | else
|
|---|
| 1568 | pPolicyStatus->dwError = NO_ERROR;
|
|---|
| 1569 | return TRUE;
|
|---|
| 1570 | }
|
|---|
| 1571 |
|
|---|
| 1572 | static BYTE msTestPubKey1[] = {
|
|---|
| 1573 | 0x30,0x47,0x02,0x40,0x81,0x55,0x22,0xb9,0x8a,0xa4,0x6f,0xed,0xd6,0xe7,0xd9,
|
|---|
| 1574 | 0x66,0x0f,0x55,0xbc,0xd7,0xcd,0xd5,0xbc,0x4e,0x40,0x02,0x21,0xa2,0xb1,0xf7,
|
|---|
| 1575 | 0x87,0x30,0x85,0x5e,0xd2,0xf2,0x44,0xb9,0xdc,0x9b,0x75,0xb6,0xfb,0x46,0x5f,
|
|---|
| 1576 | 0x42,0xb6,0x9d,0x23,0x36,0x0b,0xde,0x54,0x0f,0xcd,0xbd,0x1f,0x99,0x2a,0x10,
|
|---|
| 1577 | 0x58,0x11,0xcb,0x40,0xcb,0xb5,0xa7,0x41,0x02,0x03,0x01,0x00,0x01 };
|
|---|
| 1578 | static BYTE msTestPubKey2[] = {
|
|---|
| 1579 | 0x30,0x47,0x02,0x40,0x9c,0x50,0x05,0x1d,0xe2,0x0e,0x4c,0x53,0xd8,0xd9,0xb5,
|
|---|
| 1580 | 0xe5,0xfd,0xe9,0xe3,0xad,0x83,0x4b,0x80,0x08,0xd9,0xdc,0xe8,0xe8,0x35,0xf8,
|
|---|
| 1581 | 0x11,0xf1,0xe9,0x9b,0x03,0x7a,0x65,0x64,0x76,0x35,0xce,0x38,0x2c,0xf2,0xb6,
|
|---|
| 1582 | 0x71,0x9e,0x06,0xd9,0xbf,0xbb,0x31,0x69,0xa3,0xf6,0x30,0xa0,0x78,0x7b,0x18,
|
|---|
| 1583 | 0xdd,0x50,0x4d,0x79,0x1e,0xeb,0x61,0xc1,0x02,0x03,0x01,0x00,0x01 };
|
|---|
| 1584 |
|
|---|
| 1585 | static BOOL WINAPI verify_authenticode_policy(LPCSTR szPolicyOID,
|
|---|
| 1586 | PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|---|
| 1587 | PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
|
|---|
| 1588 | {
|
|---|
| 1589 | BOOL ret = verify_base_policy(szPolicyOID, pChainContext, pPolicyPara,
|
|---|
| 1590 | pPolicyStatus);
|
|---|
| 1591 |
|
|---|
| 1592 | if (ret && pPolicyStatus->dwError == CERT_E_UNTRUSTEDROOT)
|
|---|
| 1593 | {
|
|---|
| 1594 | CERT_PUBLIC_KEY_INFO msPubKey = { { 0 } };
|
|---|
| 1595 | BOOL isMSTestRoot = FALSE;
|
|---|
| 1596 | PCCERT_CONTEXT failingCert =
|
|---|
| 1597 | pChainContext->rgpChain[pPolicyStatus->lChainIndex]->
|
|---|
| 1598 | rgpElement[pPolicyStatus->lElementIndex]->pCertContext;
|
|---|
| 1599 | DWORD i;
|
|---|
| 1600 | CRYPT_DATA_BLOB keyBlobs[] = {
|
|---|
| 1601 | { sizeof(msTestPubKey1), msTestPubKey1 },
|
|---|
| 1602 | { sizeof(msTestPubKey2), msTestPubKey2 },
|
|---|
| 1603 | };
|
|---|
| 1604 |
|
|---|
| 1605 | /* Check whether the root is an MS test root */
|
|---|
| 1606 | for (i = 0; !isMSTestRoot && i < sizeof(keyBlobs) / sizeof(keyBlobs[0]);
|
|---|
| 1607 | i++)
|
|---|
| 1608 | {
|
|---|
| 1609 | msPubKey.PublicKey.cbData = keyBlobs[i].cbData;
|
|---|
| 1610 | msPubKey.PublicKey.pbData = keyBlobs[i].pbData;
|
|---|
| 1611 | if (CertComparePublicKeyInfo(
|
|---|
| 1612 | X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|---|
| 1613 | &failingCert->pCertInfo->SubjectPublicKeyInfo, &msPubKey))
|
|---|
| 1614 | isMSTestRoot = TRUE;
|
|---|
| 1615 | }
|
|---|
| 1616 | if (isMSTestRoot)
|
|---|
| 1617 | pPolicyStatus->dwError = CERT_E_UNTRUSTEDTESTROOT;
|
|---|
| 1618 | }
|
|---|
| 1619 | return ret;
|
|---|
| 1620 | }
|
|---|
| 1621 |
|
|---|
| 1622 | static BOOL WINAPI verify_basic_constraints_policy(LPCSTR szPolicyOID,
|
|---|
| 1623 | PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|---|
| 1624 | PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
|
|---|
| 1625 | {
|
|---|
| 1626 | pPolicyStatus->lChainIndex = pPolicyStatus->lElementIndex = -1;
|
|---|
| 1627 | if (pChainContext->TrustStatus.dwErrorStatus &
|
|---|
| 1628 | CERT_TRUST_INVALID_BASIC_CONSTRAINTS)
|
|---|
| 1629 | {
|
|---|
| 1630 | pPolicyStatus->dwError = TRUST_E_BASIC_CONSTRAINTS;
|
|---|
| 1631 | find_element_with_error(pChainContext,
|
|---|
| 1632 | CERT_TRUST_INVALID_BASIC_CONSTRAINTS, &pPolicyStatus->lChainIndex,
|
|---|
| 1633 | &pPolicyStatus->lElementIndex);
|
|---|
| 1634 | }
|
|---|
| 1635 | else
|
|---|
| 1636 | pPolicyStatus->dwError = NO_ERROR;
|
|---|
| 1637 | return TRUE;
|
|---|
| 1638 | }
|
|---|
| 1639 |
|
|---|
| 1640 | static BYTE msPubKey1[] = {
|
|---|
| 1641 | 0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,0xdf,0x08,0xba,0xe3,0x3f,0x6e,
|
|---|
| 1642 | 0x64,0x9b,0xf5,0x89,0xaf,0x28,0x96,0x4a,0x07,0x8f,0x1b,0x2e,0x8b,0x3e,0x1d,
|
|---|
| 1643 | 0xfc,0xb8,0x80,0x69,0xa3,0xa1,0xce,0xdb,0xdf,0xb0,0x8e,0x6c,0x89,0x76,0x29,
|
|---|
| 1644 | 0x4f,0xca,0x60,0x35,0x39,0xad,0x72,0x32,0xe0,0x0b,0xae,0x29,0x3d,0x4c,0x16,
|
|---|
| 1645 | 0xd9,0x4b,0x3c,0x9d,0xda,0xc5,0xd3,0xd1,0x09,0xc9,0x2c,0x6f,0xa6,0xc2,0x60,
|
|---|
| 1646 | 0x53,0x45,0xdd,0x4b,0xd1,0x55,0xcd,0x03,0x1c,0xd2,0x59,0x56,0x24,0xf3,0xe5,
|
|---|
| 1647 | 0x78,0xd8,0x07,0xcc,0xd8,0xb3,0x1f,0x90,0x3f,0xc0,0x1a,0x71,0x50,0x1d,0x2d,
|
|---|
| 1648 | 0xa7,0x12,0x08,0x6d,0x7c,0xb0,0x86,0x6c,0xc7,0xba,0x85,0x32,0x07,0xe1,0x61,
|
|---|
| 1649 | 0x6f,0xaf,0x03,0xc5,0x6d,0xe5,0xd6,0xa1,0x8f,0x36,0xf6,0xc1,0x0b,0xd1,0x3e,
|
|---|
| 1650 | 0x69,0x97,0x48,0x72,0xc9,0x7f,0xa4,0xc8,0xc2,0x4a,0x4c,0x7e,0xa1,0xd1,0x94,
|
|---|
| 1651 | 0xa6,0xd7,0xdc,0xeb,0x05,0x46,0x2e,0xb8,0x18,0xb4,0x57,0x1d,0x86,0x49,0xdb,
|
|---|
| 1652 | 0x69,0x4a,0x2c,0x21,0xf5,0x5e,0x0f,0x54,0x2d,0x5a,0x43,0xa9,0x7a,0x7e,0x6a,
|
|---|
| 1653 | 0x8e,0x50,0x4d,0x25,0x57,0xa1,0xbf,0x1b,0x15,0x05,0x43,0x7b,0x2c,0x05,0x8d,
|
|---|
| 1654 | 0xbd,0x3d,0x03,0x8c,0x93,0x22,0x7d,0x63,0xea,0x0a,0x57,0x05,0x06,0x0a,0xdb,
|
|---|
| 1655 | 0x61,0x98,0x65,0x2d,0x47,0x49,0xa8,0xe7,0xe6,0x56,0x75,0x5c,0xb8,0x64,0x08,
|
|---|
| 1656 | 0x63,0xa9,0x30,0x40,0x66,0xb2,0xf9,0xb6,0xe3,0x34,0xe8,0x67,0x30,0xe1,0x43,
|
|---|
| 1657 | 0x0b,0x87,0xff,0xc9,0xbe,0x72,0x10,0x5e,0x23,0xf0,0x9b,0xa7,0x48,0x65,0xbf,
|
|---|
| 1658 | 0x09,0x88,0x7b,0xcd,0x72,0xbc,0x2e,0x79,0x9b,0x7b,0x02,0x03,0x01,0x00,0x01 };
|
|---|
| 1659 | static BYTE msPubKey2[] = {
|
|---|
| 1660 | 0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,0xa9,0x02,0xbd,0xc1,0x70,0xe6,
|
|---|
| 1661 | 0x3b,0xf2,0x4e,0x1b,0x28,0x9f,0x97,0x78,0x5e,0x30,0xea,0xa2,0xa9,0x8d,0x25,
|
|---|
| 1662 | 0x5f,0xf8,0xfe,0x95,0x4c,0xa3,0xb7,0xfe,0x9d,0xa2,0x20,0x3e,0x7c,0x51,0xa2,
|
|---|
| 1663 | 0x9b,0xa2,0x8f,0x60,0x32,0x6b,0xd1,0x42,0x64,0x79,0xee,0xac,0x76,0xc9,0x54,
|
|---|
| 1664 | 0xda,0xf2,0xeb,0x9c,0x86,0x1c,0x8f,0x9f,0x84,0x66,0xb3,0xc5,0x6b,0x7a,0x62,
|
|---|
| 1665 | 0x23,0xd6,0x1d,0x3c,0xde,0x0f,0x01,0x92,0xe8,0x96,0xc4,0xbf,0x2d,0x66,0x9a,
|
|---|
| 1666 | 0x9a,0x68,0x26,0x99,0xd0,0x3a,0x2c,0xbf,0x0c,0xb5,0x58,0x26,0xc1,0x46,0xe7,
|
|---|
| 1667 | 0x0a,0x3e,0x38,0x96,0x2c,0xa9,0x28,0x39,0xa8,0xec,0x49,0x83,0x42,0xe3,0x84,
|
|---|
| 1668 | 0x0f,0xbb,0x9a,0x6c,0x55,0x61,0xac,0x82,0x7c,0xa1,0x60,0x2d,0x77,0x4c,0xe9,
|
|---|
| 1669 | 0x99,0xb4,0x64,0x3b,0x9a,0x50,0x1c,0x31,0x08,0x24,0x14,0x9f,0xa9,0xe7,0x91,
|
|---|
| 1670 | 0x2b,0x18,0xe6,0x3d,0x98,0x63,0x14,0x60,0x58,0x05,0x65,0x9f,0x1d,0x37,0x52,
|
|---|
| 1671 | 0x87,0xf7,0xa7,0xef,0x94,0x02,0xc6,0x1b,0xd3,0xbf,0x55,0x45,0xb3,0x89,0x80,
|
|---|
| 1672 | 0xbf,0x3a,0xec,0x54,0x94,0x4e,0xae,0xfd,0xa7,0x7a,0x6d,0x74,0x4e,0xaf,0x18,
|
|---|
| 1673 | 0xcc,0x96,0x09,0x28,0x21,0x00,0x57,0x90,0x60,0x69,0x37,0xbb,0x4b,0x12,0x07,
|
|---|
| 1674 | 0x3c,0x56,0xff,0x5b,0xfb,0xa4,0x66,0x0a,0x08,0xa6,0xd2,0x81,0x56,0x57,0xef,
|
|---|
| 1675 | 0xb6,0x3b,0x5e,0x16,0x81,0x77,0x04,0xda,0xf6,0xbe,0xae,0x80,0x95,0xfe,0xb0,
|
|---|
| 1676 | 0xcd,0x7f,0xd6,0xa7,0x1a,0x72,0x5c,0x3c,0xca,0xbc,0xf0,0x08,0xa3,0x22,0x30,
|
|---|
| 1677 | 0xb3,0x06,0x85,0xc9,0xb3,0x20,0x77,0x13,0x85,0xdf,0x02,0x03,0x01,0x00,0x01 };
|
|---|
| 1678 | static BYTE msPubKey3[] = {
|
|---|
| 1679 | 0x30,0x82,0x02,0x0a,0x02,0x82,0x02,0x01,0x00,0xf3,0x5d,0xfa,0x80,0x67,0xd4,
|
|---|
| 1680 | 0x5a,0xa7,0xa9,0x0c,0x2c,0x90,0x20,0xd0,0x35,0x08,0x3c,0x75,0x84,0xcd,0xb7,
|
|---|
| 1681 | 0x07,0x89,0x9c,0x89,0xda,0xde,0xce,0xc3,0x60,0xfa,0x91,0x68,0x5a,0x9e,0x94,
|
|---|
| 1682 | 0x71,0x29,0x18,0x76,0x7c,0xc2,0xe0,0xc8,0x25,0x76,0x94,0x0e,0x58,0xfa,0x04,
|
|---|
| 1683 | 0x34,0x36,0xe6,0xdf,0xaf,0xf7,0x80,0xba,0xe9,0x58,0x0b,0x2b,0x93,0xe5,0x9d,
|
|---|
| 1684 | 0x05,0xe3,0x77,0x22,0x91,0xf7,0x34,0x64,0x3c,0x22,0x91,0x1d,0x5e,0xe1,0x09,
|
|---|
| 1685 | 0x90,0xbc,0x14,0xfe,0xfc,0x75,0x58,0x19,0xe1,0x79,0xb7,0x07,0x92,0xa3,0xae,
|
|---|
| 1686 | 0x88,0x59,0x08,0xd8,0x9f,0x07,0xca,0x03,0x58,0xfc,0x68,0x29,0x6d,0x32,0xd7,
|
|---|
| 1687 | 0xd2,0xa8,0xcb,0x4b,0xfc,0xe1,0x0b,0x48,0x32,0x4f,0xe6,0xeb,0xb8,0xad,0x4f,
|
|---|
| 1688 | 0xe4,0x5c,0x6f,0x13,0x94,0x99,0xdb,0x95,0xd5,0x75,0xdb,0xa8,0x1a,0xb7,0x94,
|
|---|
| 1689 | 0x91,0xb4,0x77,0x5b,0xf5,0x48,0x0c,0x8f,0x6a,0x79,0x7d,0x14,0x70,0x04,0x7d,
|
|---|
| 1690 | 0x6d,0xaf,0x90,0xf5,0xda,0x70,0xd8,0x47,0xb7,0xbf,0x9b,0x2f,0x6c,0xe7,0x05,
|
|---|
| 1691 | 0xb7,0xe1,0x11,0x60,0xac,0x79,0x91,0x14,0x7c,0xc5,0xd6,0xa6,0xe4,0xe1,0x7e,
|
|---|
| 1692 | 0xd5,0xc3,0x7e,0xe5,0x92,0xd2,0x3c,0x00,0xb5,0x36,0x82,0xde,0x79,0xe1,0x6d,
|
|---|
| 1693 | 0xf3,0xb5,0x6e,0xf8,0x9f,0x33,0xc9,0xcb,0x52,0x7d,0x73,0x98,0x36,0xdb,0x8b,
|
|---|
| 1694 | 0xa1,0x6b,0xa2,0x95,0x97,0x9b,0xa3,0xde,0xc2,0x4d,0x26,0xff,0x06,0x96,0x67,
|
|---|
| 1695 | 0x25,0x06,0xc8,0xe7,0xac,0xe4,0xee,0x12,0x33,0x95,0x31,0x99,0xc8,0x35,0x08,
|
|---|
| 1696 | 0x4e,0x34,0xca,0x79,0x53,0xd5,0xb5,0xbe,0x63,0x32,0x59,0x40,0x36,0xc0,0xa5,
|
|---|
| 1697 | 0x4e,0x04,0x4d,0x3d,0xdb,0x5b,0x07,0x33,0xe4,0x58,0xbf,0xef,0x3f,0x53,0x64,
|
|---|
| 1698 | 0xd8,0x42,0x59,0x35,0x57,0xfd,0x0f,0x45,0x7c,0x24,0x04,0x4d,0x9e,0xd6,0x38,
|
|---|
| 1699 | 0x74,0x11,0x97,0x22,0x90,0xce,0x68,0x44,0x74,0x92,0x6f,0xd5,0x4b,0x6f,0xb0,
|
|---|
| 1700 | 0x86,0xe3,0xc7,0x36,0x42,0xa0,0xd0,0xfc,0xc1,0xc0,0x5a,0xf9,0xa3,0x61,0xb9,
|
|---|
| 1701 | 0x30,0x47,0x71,0x96,0x0a,0x16,0xb0,0x91,0xc0,0x42,0x95,0xef,0x10,0x7f,0x28,
|
|---|
| 1702 | 0x6a,0xe3,0x2a,0x1f,0xb1,0xe4,0xcd,0x03,0x3f,0x77,0x71,0x04,0xc7,0x20,0xfc,
|
|---|
| 1703 | 0x49,0x0f,0x1d,0x45,0x88,0xa4,0xd7,0xcb,0x7e,0x88,0xad,0x8e,0x2d,0xec,0x45,
|
|---|
| 1704 | 0xdb,0xc4,0x51,0x04,0xc9,0x2a,0xfc,0xec,0x86,0x9e,0x9a,0x11,0x97,0x5b,0xde,
|
|---|
| 1705 | 0xce,0x53,0x88,0xe6,0xe2,0xb7,0xfd,0xac,0x95,0xc2,0x28,0x40,0xdb,0xef,0x04,
|
|---|
| 1706 | 0x90,0xdf,0x81,0x33,0x39,0xd9,0xb2,0x45,0xa5,0x23,0x87,0x06,0xa5,0x55,0x89,
|
|---|
| 1707 | 0x31,0xbb,0x06,0x2d,0x60,0x0e,0x41,0x18,0x7d,0x1f,0x2e,0xb5,0x97,0xcb,0x11,
|
|---|
| 1708 | 0xeb,0x15,0xd5,0x24,0xa5,0x94,0xef,0x15,0x14,0x89,0xfd,0x4b,0x73,0xfa,0x32,
|
|---|
| 1709 | 0x5b,0xfc,0xd1,0x33,0x00,0xf9,0x59,0x62,0x70,0x07,0x32,0xea,0x2e,0xab,0x40,
|
|---|
| 1710 | 0x2d,0x7b,0xca,0xdd,0x21,0x67,0x1b,0x30,0x99,0x8f,0x16,0xaa,0x23,0xa8,0x41,
|
|---|
| 1711 | 0xd1,0xb0,0x6e,0x11,0x9b,0x36,0xc4,0xde,0x40,0x74,0x9c,0xe1,0x58,0x65,0xc1,
|
|---|
| 1712 | 0x60,0x1e,0x7a,0x5b,0x38,0xc8,0x8f,0xbb,0x04,0x26,0x7c,0xd4,0x16,0x40,0xe5,
|
|---|
| 1713 | 0xb6,0x6b,0x6c,0xaa,0x86,0xfd,0x00,0xbf,0xce,0xc1,0x35,0x02,0x03,0x01,0x00,
|
|---|
| 1714 | 0x01 };
|
|---|
| 1715 |
|
|---|
| 1716 | static BOOL WINAPI verify_ms_root_policy(LPCSTR szPolicyOID,
|
|---|
| 1717 | PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|---|
| 1718 | PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
|
|---|
| 1719 | {
|
|---|
| 1720 | BOOL ret = verify_base_policy(szPolicyOID, pChainContext, pPolicyPara,
|
|---|
| 1721 | pPolicyStatus);
|
|---|
| 1722 |
|
|---|
| 1723 | if (ret && !pPolicyStatus->dwError)
|
|---|
| 1724 | {
|
|---|
| 1725 | CERT_PUBLIC_KEY_INFO msPubKey = { { 0 } };
|
|---|
| 1726 | BOOL isMSRoot = FALSE;
|
|---|
| 1727 | DWORD i;
|
|---|
| 1728 | CRYPT_DATA_BLOB keyBlobs[] = {
|
|---|
| 1729 | { sizeof(msPubKey1), msPubKey1 },
|
|---|
| 1730 | { sizeof(msPubKey2), msPubKey2 },
|
|---|
| 1731 | { sizeof(msPubKey3), msPubKey3 },
|
|---|
| 1732 | };
|
|---|
| 1733 | PCERT_SIMPLE_CHAIN rootChain =
|
|---|
| 1734 | pChainContext->rgpChain[pChainContext->cChain -1 ];
|
|---|
| 1735 | PCCERT_CONTEXT root =
|
|---|
| 1736 | rootChain->rgpElement[rootChain->cElement - 1]->pCertContext;
|
|---|
| 1737 |
|
|---|
| 1738 | for (i = 0; !isMSRoot && i < sizeof(keyBlobs) / sizeof(keyBlobs[0]);
|
|---|
| 1739 | i++)
|
|---|
| 1740 | {
|
|---|
| 1741 | msPubKey.PublicKey.cbData = keyBlobs[i].cbData;
|
|---|
| 1742 | msPubKey.PublicKey.pbData = keyBlobs[i].pbData;
|
|---|
| 1743 | if (CertComparePublicKeyInfo(
|
|---|
| 1744 | X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|---|
| 1745 | &root->pCertInfo->SubjectPublicKeyInfo, &msPubKey))
|
|---|
| 1746 | isMSRoot = TRUE;
|
|---|
| 1747 | }
|
|---|
| 1748 | if (isMSRoot)
|
|---|
| 1749 | pPolicyStatus->lChainIndex = pPolicyStatus->lElementIndex = 0;
|
|---|
| 1750 | }
|
|---|
| 1751 | return ret;
|
|---|
| 1752 | }
|
|---|
| 1753 |
|
|---|
| 1754 | typedef BOOL (*WINAPI CertVerifyCertificateChainPolicyFunc)(LPCSTR szPolicyOID,
|
|---|
| 1755 | PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|---|
| 1756 | PCERT_CHAIN_POLICY_STATUS pPolicyStatus);
|
|---|
| 1757 |
|
|---|
| 1758 | BOOL WINAPI CertVerifyCertificateChainPolicy(LPCSTR szPolicyOID,
|
|---|
| 1759 | PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|---|
| 1760 | PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
|
|---|
| 1761 | {
|
|---|
| 1762 | static HCRYPTOIDFUNCSET set = NULL;
|
|---|
| 1763 | BOOL ret = FALSE;
|
|---|
| 1764 | CertVerifyCertificateChainPolicyFunc verifyPolicy = NULL;
|
|---|
| 1765 | HCRYPTOIDFUNCADDR hFunc = NULL;
|
|---|
| 1766 |
|
|---|
| 1767 | TRACE("(%s, %p, %p, %p)\n", debugstr_a(szPolicyOID), pChainContext,
|
|---|
| 1768 | pPolicyPara, pPolicyStatus);
|
|---|
| 1769 |
|
|---|
| 1770 | if (!HIWORD(szPolicyOID))
|
|---|
| 1771 | {
|
|---|
| 1772 | switch (LOWORD(szPolicyOID))
|
|---|
| 1773 | {
|
|---|
| 1774 | case LOWORD(CERT_CHAIN_POLICY_BASE):
|
|---|
| 1775 | verifyPolicy = verify_base_policy;
|
|---|
| 1776 | break;
|
|---|
| 1777 | case LOWORD(CERT_CHAIN_POLICY_AUTHENTICODE):
|
|---|
| 1778 | verifyPolicy = verify_authenticode_policy;
|
|---|
| 1779 | break;
|
|---|
| 1780 | case LOWORD(CERT_CHAIN_POLICY_BASIC_CONSTRAINTS):
|
|---|
| 1781 | verifyPolicy = verify_basic_constraints_policy;
|
|---|
| 1782 | break;
|
|---|
| 1783 | case LOWORD(CERT_CHAIN_POLICY_MICROSOFT_ROOT):
|
|---|
| 1784 | verifyPolicy = verify_ms_root_policy;
|
|---|
| 1785 | break;
|
|---|
| 1786 | default:
|
|---|
| 1787 | FIXME("unimplemented for %d\n", LOWORD(szPolicyOID));
|
|---|
| 1788 | }
|
|---|
| 1789 | }
|
|---|
| 1790 | if (!verifyPolicy)
|
|---|
| 1791 | {
|
|---|
| 1792 | if (!set)
|
|---|
| 1793 | set = CryptInitOIDFunctionSet(
|
|---|
| 1794 | CRYPT_OID_VERIFY_CERTIFICATE_CHAIN_POLICY_FUNC, 0);
|
|---|
| 1795 | CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, szPolicyOID, 0,
|
|---|
| 1796 | (void **)&verifyPolicy, &hFunc);
|
|---|
| 1797 | }
|
|---|
| 1798 | if (verifyPolicy)
|
|---|
| 1799 | ret = verifyPolicy(szPolicyOID, pChainContext, pPolicyPara,
|
|---|
| 1800 | pPolicyStatus);
|
|---|
| 1801 | if (hFunc)
|
|---|
| 1802 | CryptFreeOIDFunctionAddress(hFunc, 0);
|
|---|
| 1803 | return ret;
|
|---|
| 1804 | }
|
|---|