//+------------------------------------------------------------------------- // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1996 - 1999 // // File: msctl.cpp // // Contents: Default version of CertDllVerifyCTLUsage. // // Default implementation: // - If CtlStores are specified, then, only those stores are // searched to find a CTL with the specified usage and optional // ListIdentifier. Otherwise, the "Trust" system store is // searched to find a CTL. // - If CERT_VERIFY_TRUSTED_SIGNERS_FLAG is set, then, only the // SignerStores are searched to find the certificate // corresponding to the signer's issuer and serial number. // Otherwise, the CTL message's store, SignerStores, // "Trust" system store, "CA" system store, "ROOT" and "SPC" // system stores are searched to find the signer's certificate. // In either case, the public key in the found // certificate is used to verify the CTL's signature. // - If the CTL has a NextUpdate and // CERT_VERIFY_NO_TIME_CHECK_FLAG isn't set, then its // verified for time validity. // - If the CTL is time invalid, then, attempts to // get a time valid version. Uses either the CTL's // NextUpdateLocation property or CTL's NextUpdateLocation // extension or searches the signer's info for a // NextUpdateLocation attribute. The NextUpdateLocation // is encoded as a GeneralNames. Any non-URL name choices are // skipped. // // Functions: DllMain // DllRegisterServer // DllUnregisterServer // CertDllVerifyCTLUsage // // History: 29-Apr-97 philh created // 09-Oct-97 kirtd simplification, use CryptGetTimeValidObject //-------------------------------------------------------------------------- #include #include #define MSCTL_TIMEOUT 15000 //+------------------------------------------------------------------------- // Default stores searched to find a CTL or signer //-------------------------------------------------------------------------- // The CTL stores must be at the beginning. CTL stores are opened as // READ/WRITE. Remaining stores are opened READONLY. // // CTL stores are also searched for signers. static const struct { LPCWSTR pwszStore; DWORD dwFlags; } rgDefaultStoreInfo[] = { L"TRUST", CERT_SYSTEM_STORE_CURRENT_USER, L"CA", CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT", CERT_SYSTEM_STORE_CURRENT_USER, L"SPC", CERT_SYSTEM_STORE_LOCAL_MACHINE }; #define NUM_DEFAULT_STORES (sizeof(rgDefaultStoreInfo) / \ sizeof(rgDefaultStoreInfo[0])) #define NUM_DEFAULT_CTL_STORES 1 #define NUM_DEFAULT_SIGNER_STORES NUM_DEFAULT_STORES //+------------------------------------------------------------------------- // The following HCERTSTORE handles once opened, remain open until // ProcessDetach //-------------------------------------------------------------------------- static HCERTSTORE rghDefaultStore[NUM_DEFAULT_STORES]; static BOOL fOpenedDefaultStores; extern CRITICAL_SECTION MSCtlDefaultStoresCriticalSection; //+------------------------------------------------------------------------- // Close the default stores that might have been opened //-------------------------------------------------------------------------- void MSCtlCloseDefaultStores() { if (fOpenedDefaultStores) { DWORD i; for (i = 0; i < NUM_DEFAULT_STORES; i++) { HCERTSTORE hStore = rghDefaultStore[i]; if (hStore) CertCloseStore(hStore, 0); } fOpenedDefaultStores = FALSE; } } //+------------------------------------------------------------------------- // Returns TRUE if the CTL is still time valid. // // A CTL without a NextUpdate is considered time valid. //-------------------------------------------------------------------------- static BOOL IsTimeValidCtl( IN LPFILETIME pTimeToVerify, IN PCCTL_CONTEXT pCtl ) { PCTL_INFO pCtlInfo = pCtl->pCtlInfo; // Note, NextUpdate is optional. When not present, its set to 0 if ((0 == pCtlInfo->NextUpdate.dwLowDateTime && 0 == pCtlInfo->NextUpdate.dwHighDateTime) || CompareFileTime(&pCtlInfo->NextUpdate, pTimeToVerify) >= 0) return TRUE; else return FALSE; } //+------------------------------------------------------------------------- // Local functions called by CertDllVerifyCTLUsage //-------------------------------------------------------------------------- static void MSCtlOpenDefaultStores(); static BOOL VerifyCtl( IN PCCTL_CONTEXT pCtl, IN DWORD dwFlags, IN PCTL_VERIFY_USAGE_PARA pVerifyUsagePara, OUT PCCERT_CONTEXT *ppSigner, OUT DWORD *pdwSignerIndex ); static BOOL GetTimeValidCtl( IN LPFILETIME pCurrentTime, IN PCCTL_CONTEXT pCtl, IN DWORD dwFlags, IN PCTL_VERIFY_USAGE_PARA pVerifyUsagePara, OUT PCCTL_CONTEXT *ppValidCtl, IN OUT PCCERT_CONTEXT *ppSigner, IN OUT DWORD *pdwSignerIndex ); static PCCTL_CONTEXT ReplaceCtl( IN HCERTSTORE hStore, IN PCCTL_CONTEXT pOrigCtl, IN PCCTL_CONTEXT pValidCtl ); static BOOL CompareCtlUsage( IN DWORD dwFindFlags, IN PCTL_FIND_USAGE_PARA pPara, IN PCCTL_CONTEXT pCtl ) { PCTL_INFO pInfo = pCtl->pCtlInfo; if ((CTL_FIND_SAME_USAGE_FLAG & dwFindFlags) && pPara->SubjectUsage.cUsageIdentifier != pInfo->SubjectUsage.cUsageIdentifier) return FALSE; if (pPara->SubjectUsage.cUsageIdentifier) { DWORD cId1 = pPara->SubjectUsage.cUsageIdentifier; LPSTR *ppszId1 = pPara->SubjectUsage.rgpszUsageIdentifier; for ( ; cId1 > 0; cId1--, ppszId1++) { DWORD cId2 = pInfo->SubjectUsage.cUsageIdentifier; LPSTR *ppszId2 = pInfo->SubjectUsage.rgpszUsageIdentifier; for ( ; cId2 > 0; cId2--, ppszId2++) { if (0 == strcmp(*ppszId1, *ppszId2)) break; } if (0 == cId2) return FALSE; } } if (pPara->ListIdentifier.cbData) { DWORD cb = pPara->ListIdentifier.cbData; if (CTL_FIND_NO_LIST_ID_CBDATA == cb) cb = 0; if (cb != pInfo->ListIdentifier.cbData) return FALSE; if (0 != cb && 0 != memcmp(pPara->ListIdentifier.pbData, pInfo->ListIdentifier.pbData, cb)) return FALSE; } return TRUE; } //+------------------------------------------------------------------------- // Default version of CertDllVerifyCTLUsage //-------------------------------------------------------------------------- BOOL WINAPI CertDllVerifyCTLUsage( IN DWORD dwEncodingType, IN DWORD dwSubjectType, IN void *pvSubject, IN PCTL_USAGE pSubjectUsage, IN DWORD dwFlags, IN OPTIONAL PCTL_VERIFY_USAGE_PARA pVerifyUsagePara, IN OUT PCTL_VERIFY_USAGE_STATUS pVerifyUsageStatus ) { BOOL fResult = FALSE; DWORD dwError = (DWORD) CRYPT_E_NO_VERIFY_USAGE_CHECK; DWORD cCtlStore; HCERTSTORE *phCtlStore; // not allocated or reference counted FILETIME CurrentTime; CTL_FIND_USAGE_PARA FindUsagePara; DWORD dwFindFlags; PCCTL_CONTEXT pValidCtl; PCCERT_CONTEXT pSigner; PCTL_ENTRY pEntry; DWORD dwSignerIndex; assert(NULL == pVerifyUsagePara || pVerifyUsagePara->cbSize >= sizeof(CTL_VERIFY_USAGE_PARA)); assert(pVerifyUsageStatus && pVerifyUsageStatus->cbSize >= sizeof(CTL_VERIFY_USAGE_STATUS)); if (pVerifyUsagePara && pVerifyUsagePara->cCtlStore > 0) { cCtlStore = pVerifyUsagePara->cCtlStore; phCtlStore = pVerifyUsagePara->rghCtlStore; } else { MSCtlOpenDefaultStores(); dwFlags &= ~CERT_VERIFY_INHIBIT_CTL_UPDATE_FLAG; cCtlStore = NUM_DEFAULT_CTL_STORES; phCtlStore = rghDefaultStore; } // Get current time to be used to determine if CTLs are time valid { SYSTEMTIME SystemTime; GetSystemTime(&SystemTime); SystemTimeToFileTime(&SystemTime, &CurrentTime); } memset(&FindUsagePara, 0, sizeof(FindUsagePara)); FindUsagePara.cbSize = sizeof(FindUsagePara); dwFindFlags = 0; if (pSubjectUsage) { FindUsagePara.SubjectUsage = *pSubjectUsage; if (0 == (CERT_VERIFY_ALLOW_MORE_USAGE_FLAG & dwFlags)) dwFindFlags = CTL_FIND_SAME_USAGE_FLAG; } if (pVerifyUsagePara) FindUsagePara.ListIdentifier = pVerifyUsagePara->ListIdentifier; for ( ; ( cCtlStore > 0 ) && ( dwError != 0 ); cCtlStore--, phCtlStore++) { HCERTSTORE hCtlStore = *phCtlStore; PCCTL_CONTEXT pCtl; if (NULL == hCtlStore) continue; pCtl = NULL; while ( ( pCtl = CertFindCTLInStore( hCtlStore, dwEncodingType, dwFindFlags, CTL_FIND_USAGE, &FindUsagePara, pCtl ) ) ) { pValidCtl = NULL; pSigner = NULL; pEntry = NULL; dwSignerIndex = 0; if ( ( fResult = VerifyCtl( pCtl, dwFlags, pVerifyUsagePara, &pSigner, &dwSignerIndex ) ) == TRUE ) { if ( !( dwFlags & CERT_VERIFY_NO_TIME_CHECK_FLAG ) && ( IsTimeValidCtl( &CurrentTime, pCtl ) == FALSE ) ) { fResult = GetTimeValidCtl( &CurrentTime, pCtl, dwFlags, pVerifyUsagePara, &pValidCtl, &pSigner, &dwSignerIndex ); if ( fResult == TRUE ) { if ( !( dwFlags & CERT_VERIFY_INHIBIT_CTL_UPDATE_FLAG ) ) { pValidCtl = ReplaceCtl( hCtlStore, pCtl, pValidCtl ); pVerifyUsageStatus->dwFlags |= CERT_VERIFY_UPDATED_CTL_FLAG; } fResult = CompareCtlUsage( dwFindFlags, &FindUsagePara, pValidCtl ); } else { dwError = (DWORD) CRYPT_E_VERIFY_USAGE_OFFLINE; } } if ( fResult == TRUE ) { PCCTL_CONTEXT pCtlToUse; if ( pValidCtl != NULL ) { pCtlToUse = CertDuplicateCTLContext( pValidCtl ); } else { pCtlToUse = CertDuplicateCTLContext( pCtl ); } pEntry = CertFindSubjectInCTL( dwEncodingType, dwSubjectType, pvSubject, pCtlToUse, 0 ); if ( pEntry != NULL ) { pVerifyUsageStatus->dwCtlEntryIndex = (DWORD)(pEntry - pCtlToUse->pCtlInfo->rgCTLEntry); if ( pVerifyUsageStatus->ppCtl != NULL ) { *pVerifyUsageStatus->ppCtl = pCtlToUse; } else { CertFreeCTLContext( pCtlToUse ); } pVerifyUsageStatus->dwSignerIndex = dwSignerIndex; if ( pVerifyUsageStatus->ppSigner != NULL ) { *pVerifyUsageStatus->ppSigner = CertDuplicateCertificateContext( pSigner ); } dwError = 0; } else { dwError = (DWORD) CRYPT_E_NOT_IN_CTL; CertFreeCTLContext( pCtlToUse ); } } } else { dwError = (DWORD) CRYPT_E_NO_TRUSTED_SIGNER; } if ( pValidCtl != NULL ) { CertFreeCTLContext( pValidCtl ); } if ( pSigner != NULL ) { CertFreeCertificateContext( pSigner ); } if ( dwError == 0 ) { CertFreeCTLContext(pCtl); break; } } } if ( dwError != 0 ) { fResult = FALSE; } pVerifyUsageStatus->dwError = dwError; SetLastError( dwError ); return fResult; } //+========================================================================= // Open default stores functions //========================================================================== static const CRYPT_OID_FUNC_ENTRY UsageFuncTable[] = { CRYPT_DEFAULT_OID, CertDllVerifyCTLUsage }; #define USAGE_FUNC_COUNT (sizeof(UsageFuncTable) / sizeof(UsageFuncTable[0])) //+------------------------------------------------------------------------- // Open the default stores used to find the CTL or signer. Also, install // ourself so we aren't unloaded. // // Open and install are only done once. //-------------------------------------------------------------------------- static void MSCtlOpenDefaultStores() { if (fOpenedDefaultStores) return; assert(NUM_DEFAULT_STORES >= NUM_DEFAULT_CTL_STORES); assert(NUM_DEFAULT_STORES >= NUM_DEFAULT_SIGNER_STORES); EnterCriticalSection(&MSCtlDefaultStoresCriticalSection); if (!fOpenedDefaultStores) { DWORD i; for (i = 0; i < NUM_DEFAULT_STORES; i++) { DWORD dwFlags; dwFlags = rgDefaultStoreInfo[i].dwFlags; if (i >= NUM_DEFAULT_CTL_STORES) dwFlags |= CERT_STORE_READONLY_FLAG; rghDefaultStore[i] = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, 0, // dwEncodingType 0, // hCryptProv dwFlags, (const void *) rgDefaultStoreInfo[i].pwszStore ); } fOpenedDefaultStores = TRUE; } LeaveCriticalSection(&MSCtlDefaultStoresCriticalSection); } //+========================================================================= // Verify and replace CTL functions //========================================================================== //+------------------------------------------------------------------------- // Verifies the signature of the CTL. //-------------------------------------------------------------------------- static BOOL VerifyCtl( IN PCCTL_CONTEXT pCtl, IN DWORD dwFlags, IN PCTL_VERIFY_USAGE_PARA pVerifyUsagePara, OUT PCCERT_CONTEXT *ppSigner, OUT DWORD *pdwSignerIndex ) { BOOL fResult; DWORD cParaStore; HCERTSTORE *phParaStore; // not allocated or reference counted DWORD cStore; HCERTSTORE *phStore = NULL; HCERTSTORE *phAllocStore = NULL; DWORD dwGetFlags; if (pVerifyUsagePara) { cParaStore = pVerifyUsagePara->cSignerStore; phParaStore = pVerifyUsagePara->rghSignerStore; } else { cParaStore = 0; phParaStore = NULL; } if (dwFlags & CERT_VERIFY_TRUSTED_SIGNERS_FLAG) { cStore = cParaStore; phStore = phParaStore; dwGetFlags = CMSG_TRUSTED_SIGNER_FLAG; } else { MSCtlOpenDefaultStores(); if (cParaStore) { cStore = cParaStore + NUM_DEFAULT_SIGNER_STORES; if (NULL == (phAllocStore = (HCERTSTORE *) PkiNonzeroAlloc( cStore * sizeof(HCERTSTORE)))) goto OutOfMemory; phStore = phAllocStore; memcpy(phStore, phParaStore, cParaStore * sizeof(HCERTSTORE)); memcpy(&phStore[cParaStore], rghDefaultStore, NUM_DEFAULT_SIGNER_STORES * sizeof(HCERTSTORE)); } else { cStore = NUM_DEFAULT_SIGNER_STORES; phStore = rghDefaultStore; } dwGetFlags = 0; } fResult = CryptMsgGetAndVerifySigner( pCtl->hCryptMsg, cStore, phStore, dwGetFlags, ppSigner, pdwSignerIndex); CommonReturn: PkiFree(phAllocStore); return fResult; ErrorReturn: *ppSigner = NULL; *pdwSignerIndex = 0; fResult = FALSE; goto CommonReturn; SET_ERROR(OutOfMemory, E_OUTOFMEMORY) } //+------------------------------------------------------------------------- // Replaces the CTL in the store. Copies over any original properties. //-------------------------------------------------------------------------- static PCCTL_CONTEXT ReplaceCtl( IN HCERTSTORE hStore, IN PCCTL_CONTEXT pOrigCtl, IN PCCTL_CONTEXT pValidCtl ) { PCCTL_CONTEXT pNewCtl; if (CertAddCTLContextToStore( hStore, pValidCtl, CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, &pNewCtl)) CertFreeCTLContext(pValidCtl); else pNewCtl = pValidCtl; return pNewCtl; } //+========================================================================= // Get time valid CTL via URL obtained from old CTL's NextUpdateLocation // property, extension or signer attribute. //========================================================================== static BOOL GetTimeValidCtl( IN LPFILETIME pCurrentTime, IN PCCTL_CONTEXT pCtl, IN DWORD dwFlags, IN PCTL_VERIFY_USAGE_PARA pVerifyUsagePara, OUT PCCTL_CONTEXT *ppValidCtl, IN OUT PCCERT_CONTEXT *ppSigner, IN OUT DWORD *pdwSignerIndex ) { BOOL fResult; *ppValidCtl = NULL; fResult = CryptGetTimeValidObject( TIME_VALID_OID_GET_CTL, (LPVOID)pCtl, *ppSigner, pCurrentTime, 0, MSCTL_TIMEOUT, (LPVOID *)ppValidCtl, NULL, NULL ); if ( fResult == FALSE ) { fResult = CryptGetTimeValidObject( TIME_VALID_OID_GET_CTL, (LPVOID)pCtl, *ppSigner, pCurrentTime, CRYPT_DONT_VERIFY_SIGNATURE, MSCTL_TIMEOUT, (LPVOID *)ppValidCtl, NULL, NULL ); if ( fResult == TRUE ) { DWORD dwSignerIndex = *pdwSignerIndex; PCCERT_CONTEXT pSigner = *ppSigner; fResult = VerifyCtl( *ppValidCtl, dwFlags, pVerifyUsagePara, ppSigner, pdwSignerIndex ); if ( fResult == TRUE ) { CertFreeCertificateContext( pSigner ); } else { *pdwSignerIndex = dwSignerIndex; *ppSigner = pSigner; CertFreeCTLContext( *ppValidCtl ); SetLastError( (DWORD) CRYPT_E_NO_TRUSTED_SIGNER ); } } } return( fResult ); }