From: Paul Gofman pgofman@codeweavers.com
--- dlls/crypt32/crypt32_private.h | 5 +- dlls/crypt32/regstore.c | 6 +- dlls/crypt32/rootstore.c | 145 ++++++++++++++------------------- 3 files changed, 69 insertions(+), 87 deletions(-)
diff --git a/dlls/crypt32/crypt32_private.h b/dlls/crypt32/crypt32_private.h index 827594d8489..1f0ec39d200 100644 --- a/dlls/crypt32/crypt32_private.h +++ b/dlls/crypt32/crypt32_private.h @@ -349,10 +349,11 @@ WINECRYPT_CERTSTORE *CRYPT_FileNameOpenStoreW(HCRYPTPROV hCryptProv, DWORD dwFlags, const void *pvPara);
void CRYPT_ImportSystemRootCertsToReg(void); -BOOL CRYPT_SerializeContextsToReg(HKEY key, DWORD flags, const WINE_CONTEXT_INTERFACE *contextInterface, - HCERTSTORE memStore); +BOOL CRYPT_SerializeContextToReg(HKEY key, DWORD flags, const WINE_CONTEXT_INTERFACE *context_iface, + const void *context); void CRYPT_RegReadSerializedFromReg(HKEY key, DWORD contextType, HCERTSTORE store, DWORD disposition); +void CRYPT_RegDeleteFromReg(HKEY key, const BYTE *sha1_hash);
DWORD CRYPT_IsCertificateSelfSigned(const CERT_CONTEXT *cert);
diff --git a/dlls/crypt32/regstore.c b/dlls/crypt32/regstore.c index 43ffa106c76..fbc2909dd2c 100644 --- a/dlls/crypt32/regstore.c +++ b/dlls/crypt32/regstore.c @@ -197,7 +197,7 @@ static BOOL CRYPT_WriteSerializedToReg(HKEY key, DWORD flags, const BYTE *hash, return ret; }
-static BOOL CRYPT_SerializeContextToReg(HKEY key, DWORD flags, const WINE_CONTEXT_INTERFACE *context_iface, +BOOL CRYPT_SerializeContextToReg(HKEY key, DWORD flags, const WINE_CONTEXT_INTERFACE *context_iface, const void *context) { BYTE hash[20]; @@ -226,7 +226,7 @@ static BOOL CRYPT_SerializeContextToReg(HKEY key, DWORD flags, const WINE_CONTEX return ret; }
-BOOL CRYPT_SerializeContextsToReg(HKEY key, DWORD flags, +static BOOL CRYPT_SerializeContextsToReg(HKEY key, DWORD flags, const WINE_CONTEXT_INTERFACE *contextInterface, HCERTSTORE memStore) { const void *context = NULL; @@ -241,7 +241,7 @@ BOOL CRYPT_SerializeContextsToReg(HKEY key, DWORD flags, return ret; }
-static void CRYPT_RegDeleteFromReg(HKEY key, const BYTE *sha1_hash) +void CRYPT_RegDeleteFromReg(HKEY key, const BYTE *sha1_hash) { WCHAR hash[20 * 2 + 1];
diff --git a/dlls/crypt32/rootstore.c b/dlls/crypt32/rootstore.c index 6d049af2edf..bf4804783e2 100644 --- a/dlls/crypt32/rootstore.c +++ b/dlls/crypt32/rootstore.c @@ -58,6 +58,7 @@ static const char *trust_status_to_str(DWORD status) { CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT, "excluded name constraint" }, { CERT_TRUST_IS_OFFLINE_REVOCATION, "revocation server offline" }, { CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY, "no issuance policy" }, + { CERT_TRUST_IS_PARTIAL_CHAIN, "partial chain" }, }; static char buf[1024]; int i, pos = 0; @@ -98,7 +99,7 @@ static const char *get_cert_common_name(PCCERT_CONTEXT cert) return name; }
-static void check_and_store_certs( HCERTSTORE cached, HCERTSTORE new, HCERTSTORE to ) +static void check_and_store_certs( HCERTSTORE cached, HKEY key ) { DWORD root_count = 0; CERT_CHAIN_ENGINE_CONFIG chainEngineConfig = { sizeof(chainEngineConfig), 0 }; @@ -107,19 +108,29 @@ static void check_and_store_certs( HCERTSTORE cached, HCERTSTORE new, HCERTSTORE
TRACE("\n");
- CertDuplicateStore( to ); - if (!(engine = CRYPT_CreateChainEngine( to, CERT_SYSTEM_STORE_CURRENT_USER, &chainEngineConfig ))) + if (!(engine = CRYPT_CreateChainEngine( cached, CERT_SYSTEM_STORE_CURRENT_USER, &chainEngineConfig ))) return;
- while ((cert = CertEnumCertificatesInStore( new, cert ))) + while ((cert = CertEnumCertificatesInStore( cached, cert ))) { const DWORD allowed_errors = CERT_TRUST_IS_UNTRUSTED_ROOT | CERT_TRUST_IS_NOT_VALID_FOR_USAGE | CERT_TRUST_INVALID_BASIC_CONSTRAINTS | CERT_TRUST_IS_NOT_TIME_VALID; CERT_CHAIN_PARA chainPara = { sizeof(chainPara), { 0 } }; PCCERT_CHAIN_CONTEXT chain; + DWORD size; + int is_new; BOOL ret;
- ret = CertGetCertificateChain( engine, cert, NULL, cached, &chainPara, CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL, NULL, &chain ); + size = sizeof(is_new); + if (!CertGetCertificateContextProperty( cert, CERT_FIRST_USER_PROP_ID, &is_new, &size )) + { + ERR( "CERT_FIRST_USER_PROP_ID property absent for cert %p.\n", cert ); + continue; + } + if (!is_new) + continue; + + ret = CertGetCertificateChain( engine, cert, NULL, NULL, &chainPara, CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL, NULL, &chain ); if (!ret) { TRACE( "rejecting %s: chain creation failed.\n", get_cert_common_name( cert )); @@ -140,8 +151,13 @@ static void check_and_store_certs( HCERTSTORE cached, HCERTSTORE new, HCERTSTORE if (chain->TrustStatus.dwErrorStatus & ~allowed_errors) TRACE( "rejecting %s: %s\n", get_cert_common_name(cert), trust_status_to_str( chain->TrustStatus.dwErrorStatus & ~CERT_TRUST_IS_UNTRUSTED_ROOT )); - else if (CertAddCertificateContextToStore( to, cert, CERT_STORE_ADD_NEW, NULL )) + else + { + /* Clear custom property so it is not serialized and seen by apps. */ + CertSetCertificateContextProperty( cert, CERT_FIRST_USER_PROP_ID, 0, NULL ); + CRYPT_SerializeContextToReg( key, 0, pCertInterface, cert ); root_count++; + } CertFreeCertificateChain(chain); } CertFreeCertificateChainEngine( engine ); @@ -575,26 +591,32 @@ static const struct CONST_BLOB { { rootcertauthority2011, sizeof(rootcertauthority2011) }, };
-static void add_ms_root_certs(HCERTSTORE to, HCERTSTORE cached) +static void add_ms_root_certs(HKEY key, HCERTSTORE cached) { PCCERT_CONTEXT cert, existing; DWORD i; + int is_new; + const CRYPT_DATA_BLOB exists_blob = { sizeof(is_new), (BYTE *)&is_new };
TRACE("\n");
for (i = 0; i < ARRAY_SIZE(msRootCerts); i++) { - if (!CertAddEncodedCertificateToStore(to, X509_ASN_ENCODING, - msRootCerts[i].pb, msRootCerts[i].cb, CERT_STORE_ADD_NEW, &cert)) + if (!(cert = CertCreateCertificateContext(X509_ASN_ENCODING, msRootCerts[i].pb, msRootCerts[i].cb))) { WARN("adding root cert %ld failed: %08lx\n", i, GetLastError()); continue; } if ((existing = CertFindCertificateInStore(cached, X509_ASN_ENCODING, 0, CERT_FIND_EXISTING, cert, NULL))) { - CertDeleteCertificateFromStore(existing); + is_new = 0; + CertSetCertificateContextProperty(existing, CERT_FIRST_USER_PROP_ID, 0, &exists_blob); CertFreeCertificateContext(existing); } + else + { + CRYPT_SerializeContextToReg(key, 0, pCertInterface, cert); + } CertFreeCertificateContext(cert); } } @@ -604,9 +626,8 @@ static void add_ms_root_certs(HCERTSTORE to, HCERTSTORE cached) * adding redundant certificates, e.g. when both a certificate bundle and * individual certificates exist in the same directory. */ -static void read_trusted_roots_from_known_locations(HCERTSTORE store, HCERTSTORE cached, BOOL *delete) +static void sync_trusted_roots_from_known_locations( HKEY key, HCERTSTORE cached ) { - HCERTSTORE new; DWORD needed, size; struct enum_root_certs_params params = { NULL, 2048, &needed }; HCRYPTPROV prov; @@ -614,17 +635,11 @@ static void read_trusted_roots_from_known_locations(HCERTSTORE store, HCERTSTORE BYTE hashval[20]; DWORD hashlen; CRYPT_HASH_BLOB hash_blob = { sizeof(hashval), hashval }; - CRYPT_DATA_BLOB exists_blob = { 0, NULL }; + int is_new; + CRYPT_DATA_BLOB exists_blob = { sizeof(is_new), (BYTE *)&is_new }; PCCERT_CONTEXT cert, existing; - unsigned int existing_count = 0, new_count = 0; - unsigned int cached_count = 0; - - new = CertOpenStore( CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL ); - if (!new) return; - - existing = NULL; - while ((existing = CertEnumCertificatesInStore( cached, existing ))) - ++cached_count; + unsigned int existing_count = 0, new_count = 0, deleted_count = 0; + BYTE sha1_hash[20];
CryptAcquireContextW( &prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ); params.buffer = CryptMemAlloc( params.size ); @@ -649,6 +664,7 @@ static void read_trusted_roots_from_known_locations(HCERTSTORE store, HCERTSTORE * is found among host imports. */ if (!CertGetCertificateContextProperty( existing, CERT_FIRST_USER_PROP_ID, NULL, &size )) { + is_new = 0; if (!CertSetCertificateContextProperty( existing, CERT_FIRST_USER_PROP_ID, 0, &exists_blob )) ERR( "Failed to set property.\n" ); ++existing_count; @@ -656,9 +672,9 @@ static void read_trusted_roots_from_known_locations(HCERTSTORE store, HCERTSTORE CertFreeCertificateContext( existing ); continue; } - CertAddEncodedCertificateToStore( new, X509_ASN_ENCODING, params.buffer, needed, CERT_STORE_ADD_ALWAYS, &cert ); - /* Add to cached so we can catch duplicates and check_and_store_certs() has the full chains. */ - CertAddCertificateContextToStore( cached, cert, CERT_STORE_ADD_ALWAYS, NULL ); + + CertAddEncodedCertificateToStore( cached, X509_ASN_ENCODING, params.buffer, needed, CERT_STORE_ADD_ALWAYS, &cert ); + is_new = 1; if (!CertSetCertificateContextProperty( cert, CERT_FIRST_USER_PROP_ID, 0, &exists_blob )) ERR("Failed to set property.\n"); CertFreeCertificateContext( cert ); @@ -667,61 +683,35 @@ static void read_trusted_roots_from_known_locations(HCERTSTORE store, HCERTSTORE CryptMemFree( params.buffer ); CryptReleaseContext( prov, 0 );
- if (existing_count < cached_count) + cert = NULL; + while ((cert = CertEnumCertificatesInStore( cached, cert ))) { - /* Some certs were removed on host. Clean up the cache and add all the certificates so cert chains - * get revalidated. The certs present on host are now in 'cached' store and are marked with - * CERT_FIRST_USER_PROP_ID property. */ - TRACE( "Some keys were removed, reimporting, cached %u, existing %u, new %u.\n", - cached_count, existing_count, new_count ); - *delete = TRUE; - existing_count = 0; - existing = NULL; - while ((existing = CertEnumCertificatesInStore( cached, existing ))) - { - if (!CertGetCertificateContextProperty( existing, CERT_FIRST_USER_PROP_ID, NULL, &size )) - continue; - CertAddCertificateContextToStore( new, existing, CERT_STORE_ADD_NEW, NULL ); - ++new_count; - } - } - if (new_count) - { - /* Clear custom property so it is not serialized and seen by apps. */ + if (CertGetCertificateContextProperty( cert, CERT_FIRST_USER_PROP_ID, NULL, &size )) + continue; + + size = sizeof(sha1_hash); + CertGetCertificateContextProperty( cert, CERT_HASH_PROP_ID, sha1_hash, &size ); + ++deleted_count; + CRYPT_RegDeleteFromReg( key, sha1_hash ); + /* Delete from cached so deleted certs do not participate in chain verification. */ + CertDeleteCertificateFromStore( cert ); + /* Restart enumeration as it is broken by deleting cert from store. */ + CertFreeCertificateContext( cert ); cert = NULL; - while ((cert = CertEnumCertificatesInStore( new, cert ))) - CertSetCertificateContextProperty( cert, CERT_FIRST_USER_PROP_ID, 0, NULL ); - check_and_store_certs( cached, new, store ); }
- CertCloseStore( new, 0 ); - TRACE( "existing %u, new %u.\n", existing_count, new_count ); -} - -static HCERTSTORE create_root_store(HCERTSTORE cached, BOOL *delete) -{ - HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, - X509_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); - - - *delete = FALSE; - if (memStore) - { - add_ms_root_certs(memStore, cached); - read_trusted_roots_from_known_locations(memStore, cached, delete); - } + if (new_count) + check_and_store_certs( cached, key );
- TRACE("returning %p\n", memStore); - return memStore; + TRACE( "existing %u, deleted %u, new %u.\n", existing_count, deleted_count, new_count ); }
void CRYPT_ImportSystemRootCertsToReg(void) { - HCERTSTORE store = NULL, reg = NULL; + HCERTSTORE cached = NULL; HKEY key = NULL; LONG rc; HANDLE hsem; - BOOL delete;
static BOOL root_certs_imported = FALSE;
@@ -746,27 +736,18 @@ void CRYPT_ImportSystemRootCertsToReg(void) if (rc) goto done;
- if (!(reg = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL))) + if (!(cached = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL))) { ERR("Failed to create memory store.\n"); goto done; } - CRYPT_RegReadSerializedFromReg(key, CERT_STORE_CERTIFICATE_CONTEXT_FLAG, reg, CERT_STORE_ADD_ALWAYS); - - if (!(store = create_root_store(reg, &delete))) - { - ERR("Failed to create root store\n"); - goto done; - } - if (delete && RegDeleteTreeW(key, NULL)) - ERR("Error deleting key.\n"); - if (!CRYPT_SerializeContextsToReg(key, 0, pCertInterface, store)) - ERR("Failed to import system certs into registry, %08lx\n", GetLastError()); + CRYPT_RegReadSerializedFromReg(key, CERT_STORE_CERTIFICATE_CONTEXT_FLAG, cached, CERT_STORE_ADD_ALWAYS); + add_ms_root_certs(key, cached); + sync_trusted_roots_from_known_locations(key, cached);
done: RegCloseKey(key); - CertCloseStore(store, 0); - CertCloseStore(reg, 0); + CertCloseStore(cached, 0); root_certs_imported = TRUE; ReleaseSemaphore(hsem, 1, NULL); CloseHandle(hsem);