//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1995 - 1999 // // File: ctlfunc.cpp // // Contents: Certificate Verify CTL Usage Dispatch Functions // // Functions: I_CertCTLUsageFuncDllMain // CertVerifyCTLUsage // // History: 29-Apr-97 philh created //-------------------------------------------------------------------------- #include "global.hxx" #include static HCRYPTOIDFUNCSET hUsageFuncSet; typedef BOOL (WINAPI *PFN_CERT_DLL_VERIFY_CTL_USAGE)( 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 ); //+------------------------------------------------------------------------- // Dll initialization //-------------------------------------------------------------------------- BOOL WINAPI I_CertCTLUsageFuncDllMain( HMODULE hModule, ULONG ulReason, LPVOID lpReserved) { BOOL fRet; switch (ulReason) { case DLL_PROCESS_ATTACH: if (NULL == (hUsageFuncSet = CryptInitOIDFunctionSet( CRYPT_OID_VERIFY_CTL_USAGE_FUNC, 0))) // dwFlags goto CryptInitOIDFunctionSetError; break; case DLL_PROCESS_DETACH: case DLL_THREAD_DETACH: default: break; } fRet = TRUE; CommonReturn: return fRet; ErrorReturn: fRet = FALSE; goto CommonReturn; TRACE_ERROR(CryptInitOIDFunctionSetError) } static void ZeroUsageStatus( IN OUT PCTL_VERIFY_USAGE_STATUS pUsageStatus) { pUsageStatus->dwError = 0; pUsageStatus->dwFlags = 0; if (pUsageStatus->ppCtl) *pUsageStatus->ppCtl = NULL; pUsageStatus->dwCtlEntryIndex = 0; if (pUsageStatus->ppSigner) *pUsageStatus->ppSigner = NULL; pUsageStatus->dwSignerIndex = 0; } //+------------------------------------------------------------------------- // Remember the "most interesting" error //-------------------------------------------------------------------------- static void UpdateUsageError( IN DWORD dwNewError, IN OUT DWORD *pdwError ) { if (0 != dwNewError) { DWORD dwError = *pdwError; if ((DWORD) CRYPT_E_NOT_IN_CTL == dwNewError || (DWORD) CRYPT_E_NO_VERIFY_USAGE_DLL == dwError || ((DWORD) CRYPT_E_NOT_IN_CTL != dwError && (DWORD) CRYPT_E_NO_TRUSTED_SIGNER != dwError && (DWORD) CRYPT_E_NO_VERIFY_USAGE_CHECK != dwNewError)) *pdwError = dwNewError; } } static BOOL VerifyDefaultUsage( 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; DWORD dwError = (DWORD) CRYPT_E_NO_VERIFY_USAGE_DLL; LPWSTR pwszDllList; // _alloca'ed DWORD cchDllList; DWORD cchDll; void *pvFuncAddr; HCRYPTOIDFUNCADDR hFuncAddr; // Iterate through the installed default functions. // Setting pwszDll to NULL searches the installed list. Setting // hFuncAddr to NULL starts the search at the beginning. hFuncAddr = NULL; while (CryptGetDefaultOIDFunctionAddress( hUsageFuncSet, dwEncodingType, NULL, // pwszDll 0, // dwFlags &pvFuncAddr, &hFuncAddr)) { ZeroUsageStatus(pVerifyUsageStatus); fResult = ((PFN_CERT_DLL_VERIFY_CTL_USAGE) pvFuncAddr)( dwEncodingType, dwSubjectType, pvSubject, pSubjectUsage, dwFlags, pVerifyUsagePara, pVerifyUsageStatus); if (fResult) { CryptFreeOIDFunctionAddress(hFuncAddr, 0); goto CommonReturn; } else // Unable to verify usage for this installed // function. However, remember any "interesting" // errors. UpdateUsageError(pVerifyUsageStatus->dwError, &dwError); } if (!CryptGetDefaultOIDDllList( hUsageFuncSet, dwEncodingType, NULL, // pszDllList &cchDllList)) goto GetDllListError; __try { pwszDllList = (LPWSTR) _alloca(cchDllList * sizeof(WCHAR)); } __except(EXCEPTION_EXECUTE_HANDLER) { goto OutOfMemory; } if (!CryptGetDefaultOIDDllList( hUsageFuncSet, dwEncodingType, pwszDllList, &cchDllList)) goto GetDllListError; for (; 0 != (cchDll = wcslen(pwszDllList)); pwszDllList += cchDll + 1) { if (CryptGetDefaultOIDFunctionAddress( hUsageFuncSet, dwEncodingType, pwszDllList, 0, // dwFlags &pvFuncAddr, &hFuncAddr)) { ZeroUsageStatus(pVerifyUsageStatus); fResult = ((PFN_CERT_DLL_VERIFY_CTL_USAGE) pvFuncAddr)( dwEncodingType, dwSubjectType, pvSubject, pSubjectUsage, dwFlags, pVerifyUsagePara, pVerifyUsageStatus); CryptFreeOIDFunctionAddress(hFuncAddr, 0); if (fResult) goto CommonReturn; else // Unable to verify usage for this registered // function. However, remember any "interesting" // errors. UpdateUsageError(pVerifyUsageStatus->dwError, &dwError); } } goto ErrorReturn; CommonReturn: return fResult; ErrorReturn: pVerifyUsageStatus->dwError = dwError; fResult = FALSE; goto CommonReturn; TRACE_ERROR(GetDllListError) TRACE_ERROR(OutOfMemory) } static BOOL VerifyOIDUsage( 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; HCRYPTOIDFUNCADDR hFuncAddr; PVOID pvFuncAddr; if (pSubjectUsage && pSubjectUsage->cUsageIdentifier > 0 && CryptGetOIDFunctionAddress( hUsageFuncSet, dwEncodingType, pSubjectUsage->rgpszUsageIdentifier[0], 0, // dwFlags &pvFuncAddr, &hFuncAddr)) { ZeroUsageStatus(pVerifyUsageStatus); fResult = ((PFN_CERT_DLL_VERIFY_CTL_USAGE) pvFuncAddr)( dwEncodingType, dwSubjectType, pvSubject, pSubjectUsage, dwFlags, pVerifyUsagePara, pVerifyUsageStatus); CryptFreeOIDFunctionAddress(hFuncAddr, 0); } else { pVerifyUsageStatus->dwError = (DWORD) CRYPT_E_NO_VERIFY_USAGE_DLL; fResult = FALSE; } return fResult; } //+------------------------------------------------------------------------- // Verify that a subject is trusted for the specified usage by finding a // signed and time valid CTL with the usage identifiers and containing the // the subject. A subject can be identified by either its certificate context // or any identifier such as its SHA1 hash. // // See CertFindSubjectInCTL for definition of dwSubjectType and pvSubject // parameters. // // Via pVerifyUsagePara, the caller can specify the stores to be searched // to find the CTL. The caller can also specify the stores containing // acceptable CTL signers. By setting the ListIdentifier, the caller // can also restrict to a particular signer CTL list. // // Via pVerifyUsageStatus, the CTL containing the subject, the subject's // index into the CTL's array of entries, and the signer of the CTL // are returned. If the caller is interested, ppCtl and ppSigner can be set // to NULL. Returned contexts must be freed via the store's free context APIs. // // If the CERT_VERIFY_INHIBIT_CTL_UPDATE_FLAG isn't set, then, a time // invalid CTL in one of the CtlStores may be replaced. When replaced, the // CERT_VERIFY_UPDATED_CTL_FLAG is set in pVerifyUsageStatus->dwFlags. // // If the CERT_VERIFY_TRUSTED_SIGNERS_FLAG is set, then, only the // SignerStores specified in pVerifyUsageStatus are searched to find // the signer. Otherwise, the SignerStores provide additional sources // to find the signer's certificate. // // If CERT_VERIFY_NO_TIME_CHECK_FLAG is set, then, the CTLs aren't checked // for time validity. // // If CERT_VERIFY_ALLOW_MORE_USAGE_FLAG is set, then, the CTL may contain // additional usage identifiers than specified by pSubjectUsage. Otherwise, // the found CTL will contain the same usage identifers and no more. // // CertVerifyCTLUsage will be implemented as a dispatcher to OID installable // functions. First, it will try to find an OID function matching the first // usage object identifier in the pUsage sequence. Next, it will dispatch // to the default CertDllVerifyCTLUsage functions. // // If the subject is trusted for the specified usage, then, TRUE is // returned. Otherwise, FALSE is returned with dwError set to one of the // following: // CRYPT_E_NO_VERIFY_USAGE_DLL // CRYPT_E_NO_VERIFY_USAGE_CHECK // CRYPT_E_VERIFY_USAGE_OFFLINE // CRYPT_E_NOT_IN_CTL // CRYPT_E_NO_TRUSTED_SIGNER //-------------------------------------------------------------------------- BOOL WINAPI CertVerifyCTLUsage( 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; assert(NULL == pVerifyUsagePara || pVerifyUsagePara->cbSize >= sizeof(CTL_VERIFY_USAGE_PARA)); assert(pVerifyUsageStatus && pVerifyUsageStatus->cbSize >= sizeof(CTL_VERIFY_USAGE_STATUS)); if (pVerifyUsagePara && pVerifyUsagePara->cbSize < sizeof(CTL_VERIFY_USAGE_PARA)) goto InvalidArg; if (NULL == pVerifyUsageStatus || pVerifyUsageStatus->cbSize < sizeof(CTL_VERIFY_USAGE_STATUS)) goto InvalidArg; fResult = VerifyOIDUsage( dwEncodingType, dwSubjectType, pvSubject, pSubjectUsage, dwFlags, pVerifyUsagePara, pVerifyUsageStatus); if (!fResult) { DWORD dwError = pVerifyUsageStatus->dwError; fResult = VerifyDefaultUsage( dwEncodingType, dwSubjectType, pvSubject, pSubjectUsage, dwFlags, pVerifyUsagePara, pVerifyUsageStatus); if (!fResult) { UpdateUsageError(pVerifyUsageStatus->dwError, &dwError); ZeroUsageStatus(pVerifyUsageStatus); pVerifyUsageStatus->dwError = dwError; SetLastError(dwError); } } CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) }