//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1995 - 1999 // // File: oidfunc.cpp // // Contents: Cryptographic Object ID (OID) Functions // // Functions: I_CryptOIDFuncDllMain // CryptInitOIDFunctionSet // CryptInstallOIDFunctionAddress // // CryptSetOIDFunctionValue // CryptGetOIDFunctionValue // CryptRegisterOIDFunction // CryptUnregisterOIDFunction // CryptRegisterDefaultOIDFunction // CryptUnregisterDefaultOIDFunction // CryptEnumOIDFunction // // CryptGetOIDFunctionAddress // CryptGetDefaultOIDDllList // CryptGetDefaultOIDFunctionAddress // CryptFreeOIDFunctionAddress // // Comments: // For the CryptGetOIDFunctionAddress we search the installed // const and str lists without // entering the critical section. The adds which are within // the critical section update the list pointers in the proper // order to allow list searching without locking. // // However, registry loads are done with OIDFunc // locked. // // HOLDING OID LOCK WHILE DOING A LoadLibrary() or FreeLibrary() // MAY LEAD TO DEADLOCK !!! // // // History: 07-Nov-96 philh created // 09-Aug-98 philh changed to NOT hold OID lock when calling // LoadLibrary() or FreeLibrary(). //-------------------------------------------------------------------------- #include "global.hxx" #include #ifdef STATIC #undef STATIC #endif #define STATIC #define LEN_ALIGN(Len) ((Len + 7) & ~7) #define CONST_OID_STR_PREFIX_CHAR '#' //+------------------------------------------------------------------------- // OID Element Type Definitions //-------------------------------------------------------------------------- #define CONST_OID_TYPE 1 #define STR_OID_TYPE 2 #define DLL_OID_TYPE 3 //+------------------------------------------------------------------------- // Dll and Procedure Element Definitions //-------------------------------------------------------------------------- typedef struct _DLL_ELEMENT DLL_ELEMENT, *PDLL_ELEMENT; typedef struct _DLL_PROC_ELEMENT DLL_PROC_ELEMENT, *PDLL_PROC_ELEMENT; struct _DLL_ELEMENT { DWORD dwOIDType; PDLL_ELEMENT pNext; LPWSTR pwszDll; // expanded HMODULE hDll; DWORD dwRefCnt; BOOL fLoaded; PDLL_PROC_ELEMENT pProcHead; LPFNCANUNLOADNOW pfnDllCanUnloadNow; // The following are used to defer the freeing of Dlls until after waiting // at least one FREE_DLL_TIMEOUT. DWORD dwFreeCnt; // 0, 1 or 2. PDLL_ELEMENT pFreeNext; PDLL_ELEMENT pFreePrev; }; struct _DLL_PROC_ELEMENT { PDLL_PROC_ELEMENT pNext; PDLL_ELEMENT pDll; LPSTR pszName; void *pvAddr; // NULL'ed when Dll is unloaded }; // Linked list of all the Dlls. All proc elements are on one of the Dll // element's proc list. static PDLL_ELEMENT pDllHead; // Linked list of Dlls waiting to be freed. static PDLL_ELEMENT pFreeDllHead; // Count of elements in the above list static DWORD dwFreeDllCnt; // When nonzero, a FreeDll callback has been registered. static LONG lFreeDll; static HANDLE hFreeDllRegWaitFor; static HMODULE hFreeDllLibModule; // Crypt32.dll hInst static HMODULE hOidInfoInst; // 15 seconds #define FREE_DLL_TIMEOUT 15000 //+------------------------------------------------------------------------- // Installed OID Element Definitions //-------------------------------------------------------------------------- typedef struct _CONST_OID_FUNC_ELEMENT CONST_OID_FUNC_ELEMENT, *PCONST_OID_FUNC_ELEMENT; struct _CONST_OID_FUNC_ELEMENT { DWORD dwOIDType; DWORD dwEncodingType; PCONST_OID_FUNC_ELEMENT pNext; DWORD_PTR dwLowOID; DWORD_PTR dwHighOID; HMODULE hDll; void **rgpvFuncAddr; }; typedef struct _STR_OID_FUNC_ELEMENT STR_OID_FUNC_ELEMENT, *PSTR_OID_FUNC_ELEMENT; struct _STR_OID_FUNC_ELEMENT { DWORD dwOIDType; DWORD dwEncodingType; PSTR_OID_FUNC_ELEMENT pNext; LPSTR pszOID; HMODULE hDll; void *pvFuncAddr; }; //+------------------------------------------------------------------------- // Registry OID Element Definitions //-------------------------------------------------------------------------- typedef struct _REG_OID_FUNC_ELEMENT REG_OID_FUNC_ELEMENT, *PREG_OID_FUNC_ELEMENT; struct _REG_OID_FUNC_ELEMENT { DWORD dwEncodingType; PREG_OID_FUNC_ELEMENT pNext; union { DWORD_PTR dwOID; LPSTR pszOID; }; PDLL_PROC_ELEMENT pDllProc; }; //+------------------------------------------------------------------------- // Default registry DLL list Element Definitions //-------------------------------------------------------------------------- typedef struct _DEFAULT_REG_ELEMENT DEFAULT_REG_ELEMENT, *PDEFAULT_REG_ELEMENT; struct _DEFAULT_REG_ELEMENT { DWORD dwEncodingType; PDEFAULT_REG_ELEMENT pNext; LPWSTR pwszDllList; DWORD cchDllList; DWORD cDll; LPWSTR *rgpwszDll; PDLL_PROC_ELEMENT *rgpDllProc; }; //+------------------------------------------------------------------------- // Function Set Definition //-------------------------------------------------------------------------- typedef struct _FUNC_SET FUNC_SET, *PFUNC_SET; struct _FUNC_SET { PFUNC_SET pNext; LPSTR pszFuncName; PCONST_OID_FUNC_ELEMENT pConstOIDFuncHead; PCONST_OID_FUNC_ELEMENT pConstOIDFuncTail; PSTR_OID_FUNC_ELEMENT pStrOIDFuncHead; PSTR_OID_FUNC_ELEMENT pStrOIDFuncTail; // Following are updated with OIDFunc locked BOOL fRegLoaded; PREG_OID_FUNC_ELEMENT pRegBeforeOIDFuncHead; PREG_OID_FUNC_ELEMENT pRegAfterOIDFuncHead; PDEFAULT_REG_ELEMENT pDefaultRegHead; }; // Linked list of all the function sets static PFUNC_SET pFuncSetHead; // Used to protect the adding of function sets and elements to function sets. // Protects the pDllHead list and registry loads. static CRITICAL_SECTION OIDFuncCriticalSection; //+------------------------------------------------------------------------- // OIDFunc lock and unlock functions //-------------------------------------------------------------------------- static inline void LockOIDFunc() { EnterCriticalSection(&OIDFuncCriticalSection); } static inline void UnlockOIDFunc() { LeaveCriticalSection(&OIDFuncCriticalSection); } //+------------------------------------------------------------------------- // First try to get the EncodingType from the lower 16 bits. If 0, get // from the upper 16 bits. //-------------------------------------------------------------------------- static inline DWORD GetEncodingType( IN DWORD dwEncodingType ) { return (dwEncodingType & CERT_ENCODING_TYPE_MASK) ? (dwEncodingType & CERT_ENCODING_TYPE_MASK) : (dwEncodingType & CMSG_ENCODING_TYPE_MASK) >> 16; } //+------------------------------------------------------------------------- // Duplicate the Dll library's handle // // Upon entry/exit OIDFunc must NOT be locked!! //-------------------------------------------------------------------------- static HMODULE DuplicateLibrary( IN HMODULE hDll ) { if (hDll) { WCHAR wszModule[_MAX_PATH + 1]; if (0 == GetModuleFileNameU(hDll, wszModule, _MAX_PATH)) goto GetModuleFileNameError; wszModule[_MAX_PATH] = L'\0'; if (NULL == (hDll = LoadLibraryExU(wszModule, NULL, 0))) goto LoadLibraryError; } CommonReturn: return hDll; ErrorReturn: hDll = NULL; goto CommonReturn; TRACE_ERROR(GetModuleFileNameError) TRACE_ERROR(LoadLibraryError) } //+------------------------------------------------------------------------- // Add one or more functions with a constant OID. The constant OIDs are // monotonically increasing. // // Upon entry, pFuncSet hasn't been added to the searched pFuncSetHead list. // // Upon entry/exit OIDFunc must NOT be locked!! //-------------------------------------------------------------------------- STATIC BOOL AddConstOIDFunc( IN HMODULE hDll, IN DWORD dwEncodingType, IN OUT PFUNC_SET pFuncSet, IN DWORD cFuncEntry, IN const CRYPT_OID_FUNC_ENTRY rgFuncEntry[] ) { PCONST_OID_FUNC_ELEMENT pEle; DWORD cbEle; void **ppvFuncAddr; cbEle = sizeof(CONST_OID_FUNC_ELEMENT) + cFuncEntry * sizeof(void *); if (NULL == (pEle = (PCONST_OID_FUNC_ELEMENT) PkiZeroAlloc(cbEle))) return FALSE; pEle->dwOIDType = CONST_OID_TYPE; pEle->dwEncodingType = dwEncodingType; pEle->pNext = NULL; pEle->dwLowOID = (DWORD_PTR) rgFuncEntry[0].pszOID; pEle->dwHighOID = pEle->dwLowOID + cFuncEntry - 1; pEle->hDll = DuplicateLibrary(hDll); ppvFuncAddr = (void **) (((BYTE *) pEle) + sizeof(CONST_OID_FUNC_ELEMENT)); pEle->rgpvFuncAddr = ppvFuncAddr; for (DWORD i = 0; i < cFuncEntry; i++, ppvFuncAddr++) *ppvFuncAddr = rgFuncEntry[i].pvFuncAddr; if (pFuncSet->pConstOIDFuncTail) pFuncSet->pConstOIDFuncTail->pNext = pEle; else pFuncSet->pConstOIDFuncHead = pEle; pFuncSet->pConstOIDFuncTail = pEle; return TRUE; } //+------------------------------------------------------------------------- // Add single function with a string OID. // // Upon entry, pFuncSet hasn't been added to the searched pFuncSetHead list. // // Upon entry/exit OIDFunc must NOT be locked!! //-------------------------------------------------------------------------- STATIC BOOL AddStrOIDFunc( IN HMODULE hDll, IN DWORD dwEncodingType, IN OUT PFUNC_SET pFuncSet, IN const CRYPT_OID_FUNC_ENTRY *pFuncEntry ) { PSTR_OID_FUNC_ELEMENT pEle; DWORD cbEle; DWORD cchOID; LPSTR psz; cchOID = strlen(pFuncEntry->pszOID) + 1; cbEle = sizeof(STR_OID_FUNC_ELEMENT) + cchOID; if (NULL == (pEle = (PSTR_OID_FUNC_ELEMENT) PkiZeroAlloc(cbEle))) return FALSE; pEle->dwOIDType = STR_OID_TYPE; pEle->dwEncodingType = dwEncodingType; pEle->pNext = NULL; psz = (LPSTR) (((BYTE *) pEle) + sizeof(STR_OID_FUNC_ELEMENT)); pEle->pszOID = psz; memcpy(psz, pFuncEntry->pszOID, cchOID); pEle->hDll = DuplicateLibrary(hDll); pEle->pvFuncAddr = pFuncEntry->pvFuncAddr; if (pFuncSet->pStrOIDFuncTail) pFuncSet->pStrOIDFuncTail->pNext = pEle; else pFuncSet->pStrOIDFuncHead = pEle; pFuncSet->pStrOIDFuncTail = pEle; return TRUE; } //+------------------------------------------------------------------------- // Free the constant or string function elements // // Upon entry/exit OIDFunc must NOT be locked!! //-------------------------------------------------------------------------- STATIC void FreeFuncSetConstAndStrElements( IN OUT PFUNC_SET pFuncSet ) { PCONST_OID_FUNC_ELEMENT pConstEle; PSTR_OID_FUNC_ELEMENT pStrEle; pConstEle = pFuncSet->pConstOIDFuncHead; while (pConstEle) { PCONST_OID_FUNC_ELEMENT pNextEle = pConstEle->pNext; if (pConstEle->hDll) FreeLibrary(pConstEle->hDll); PkiFree(pConstEle); pConstEle = pNextEle; } pStrEle = pFuncSet->pStrOIDFuncHead; while (pStrEle) { PSTR_OID_FUNC_ELEMENT pNextEle = pStrEle->pNext; if (pStrEle->hDll) FreeLibrary(pStrEle->hDll); PkiFree(pStrEle); pStrEle = pNextEle; } } //+------------------------------------------------------------------------- // Free the function set and its elements // // Upon entry/exit OIDFunc must NOT be locked!! //-------------------------------------------------------------------------- STATIC void FreeFuncSet( IN OUT PFUNC_SET pFuncSet ) { PREG_OID_FUNC_ELEMENT pRegEle; PDEFAULT_REG_ELEMENT pDefaultReg; FreeFuncSetConstAndStrElements(pFuncSet); pRegEle = pFuncSet->pRegBeforeOIDFuncHead; while (pRegEle) { PREG_OID_FUNC_ELEMENT pNextEle = pRegEle->pNext; PkiFree(pRegEle); pRegEle = pNextEle; } pRegEle = pFuncSet->pRegAfterOIDFuncHead; while (pRegEle) { PREG_OID_FUNC_ELEMENT pNextEle = pRegEle->pNext; PkiFree(pRegEle); pRegEle = pNextEle; } pDefaultReg = pFuncSet->pDefaultRegHead; while (pDefaultReg) { PDEFAULT_REG_ELEMENT pNext = pDefaultReg->pNext; PkiFree(pDefaultReg); pDefaultReg = pNext; } PkiFree(pFuncSet); } //+------------------------------------------------------------------------- // Free the Dll and its proc elements // // Upon entry/exit OIDFunc must NOT be locked!! //-------------------------------------------------------------------------- STATIC void FreeDll( IN OUT PDLL_ELEMENT pDll ) { PDLL_PROC_ELEMENT pProcEle; pProcEle = pDll->pProcHead; while (pProcEle) { PDLL_PROC_ELEMENT pNextEle = pProcEle->pNext; PkiFree(pProcEle); pProcEle = pNextEle; } if (pDll->fLoaded) { assert(pDll->hDll); FreeLibrary(pDll->hDll); } PkiFree(pDll); } //+------------------------------------------------------------------------- // Dll initialization //-------------------------------------------------------------------------- BOOL WINAPI I_CryptOIDFuncDllMain( HMODULE hInst, ULONG ulReason, LPVOID lpReserved) { BOOL fRet = TRUE; switch (ulReason) { case DLL_PROCESS_ATTACH: fRet = Pki_InitializeCriticalSection(&OIDFuncCriticalSection); hOidInfoInst = hInst; break; case DLL_PROCESS_DETACH: // Do interlock to guard against a potential race condition with // the RegWaitFor callback thread. We doing this without doing // a LockOIDFunc(). if (InterlockedExchange(&lFreeDll, 0)) { assert(hFreeDllRegWaitFor); hFreeDllLibModule = NULL; ILS_UnregisterWait(hFreeDllRegWaitFor); hFreeDllRegWaitFor = NULL; } while (pFuncSetHead) { PFUNC_SET pFuncSet = pFuncSetHead; pFuncSetHead = pFuncSet->pNext; FreeFuncSet(pFuncSet); } while (pDllHead) { PDLL_ELEMENT pDll = pDllHead; pDllHead = pDll->pNext; FreeDll(pDll); } DeleteCriticalSection(&OIDFuncCriticalSection); break; case DLL_THREAD_DETACH: default: break; } return fRet; } //+------------------------------------------------------------------------- // Initialize and return handle to the OID function set identified by its // function name. // // If the set already exists, a handle to the existing set is returned. //-------------------------------------------------------------------------- HCRYPTOIDFUNCSET WINAPI CryptInitOIDFunctionSet( IN LPCSTR pszFuncName, IN DWORD dwFlags ) { PFUNC_SET pFuncSet; LockOIDFunc(); // See if the set already exists for (pFuncSet = pFuncSetHead; pFuncSet; pFuncSet = pFuncSet->pNext) { if (0 == strcmp(pszFuncName, pFuncSet->pszFuncName)) break; } if (NULL == pFuncSet) { // Allocate and initialize a new set DWORD cchFuncName = strlen(pszFuncName) + 1; if (pFuncSet = (PFUNC_SET) PkiZeroAlloc( sizeof(FUNC_SET) + cchFuncName)) { LPSTR psz = (LPSTR) (((BYTE *) pFuncSet) + sizeof(FUNC_SET)); pFuncSet->pszFuncName = psz; memcpy(psz, pszFuncName, cchFuncName); pFuncSet->pNext = pFuncSetHead; pFuncSetHead = pFuncSet; } } UnlockOIDFunc(); return (HCRYPTOIDFUNCSET) pFuncSet; } //+------------------------------------------------------------------------- // Install a set of callable OID function addresses. // // By default the functions are installed at end of the list. // Set CRYPT_INSTALL_OID_FUNC_BEFORE_FLAG to install at beginning of list. // // hModule should be updated with the hModule passed to DllMain to prevent // the Dll containing the function addresses from being unloaded by // CryptGetOIDFuncAddress/CryptFreeOIDFunctionAddress. This would be the // case when the Dll has also regsvr32'ed OID functions via // CryptRegisterOIDFunction. // // DEFAULT functions are installed by setting rgFuncEntry[].pszOID = // CRYPT_DEFAULT_OID. //-------------------------------------------------------------------------- BOOL WINAPI CryptInstallOIDFunctionAddress( IN HMODULE hModule, // hModule passed to DllMain IN DWORD dwEncodingType, IN LPCSTR pszFuncName, IN DWORD cFuncEntry, IN const CRYPT_OID_FUNC_ENTRY rgFuncEntry[], IN DWORD dwFlags ) { BOOL fResult; PFUNC_SET pFuncSet; FUNC_SET AddFuncSet; memset(&AddFuncSet, 0, sizeof(AddFuncSet)); int ConstFirst = -1; int ConstLast = 0; DWORD_PTR dwOID; DWORD_PTR dwLastOID = 0; DWORD i; dwEncodingType = GetEncodingType(dwEncodingType); if (NULL == (pFuncSet = (PFUNC_SET) CryptInitOIDFunctionSet( pszFuncName, 0))) return FALSE; // Don't need to hold lock while updating local copy of AddFuncSet. for (i = 0; i < cFuncEntry; i++) { if (0xFFFF >= (dwOID = (DWORD_PTR) rgFuncEntry[i].pszOID)) { if (ConstFirst < 0) ConstFirst = i; else if (dwOID != dwLastOID + 1) { if (!AddConstOIDFunc( hModule, dwEncodingType, &AddFuncSet, ConstLast - ConstFirst + 1, &rgFuncEntry[ConstFirst] )) goto AddConstOIDFuncError; ConstFirst = i; } ConstLast = i; dwLastOID = dwOID; } else { if (ConstFirst >= 0) { if (!AddConstOIDFunc( hModule, dwEncodingType, &AddFuncSet, ConstLast - ConstFirst + 1, &rgFuncEntry[ConstFirst] )) goto AddConstOIDFuncError; ConstFirst = -1; } if (!AddStrOIDFunc( hModule, dwEncodingType, &AddFuncSet, &rgFuncEntry[i] )) goto AddStrOIDFuncError; } } if (ConstFirst >= 0) { if (!AddConstOIDFunc( hModule, dwEncodingType, &AddFuncSet, ConstLast - ConstFirst + 1, &rgFuncEntry[ConstFirst] )) goto AddConstOIDFuncError; } // NOTE::: // // Since the get function accesses the lists without entering the critical // section, the following pointers must be updated in the correct // order. Note, Get doesn't access the tail. LockOIDFunc(); if (AddFuncSet.pConstOIDFuncHead) { if (NULL == pFuncSet->pConstOIDFuncHead) { pFuncSet->pConstOIDFuncHead = AddFuncSet.pConstOIDFuncHead; pFuncSet->pConstOIDFuncTail = AddFuncSet.pConstOIDFuncTail; } else if (dwFlags & CRYPT_INSTALL_OID_FUNC_BEFORE_FLAG) { AddFuncSet.pConstOIDFuncTail->pNext = pFuncSet->pConstOIDFuncHead; pFuncSet->pConstOIDFuncHead = AddFuncSet.pConstOIDFuncHead; } else { pFuncSet->pConstOIDFuncTail->pNext = AddFuncSet.pConstOIDFuncHead; pFuncSet->pConstOIDFuncTail = AddFuncSet.pConstOIDFuncTail; } } if (AddFuncSet.pStrOIDFuncHead) { if (NULL == pFuncSet->pStrOIDFuncHead) { pFuncSet->pStrOIDFuncHead = AddFuncSet.pStrOIDFuncHead; pFuncSet->pStrOIDFuncTail = AddFuncSet.pStrOIDFuncTail; } else if (dwFlags & CRYPT_INSTALL_OID_FUNC_BEFORE_FLAG) { AddFuncSet.pStrOIDFuncTail->pNext = pFuncSet->pStrOIDFuncHead; pFuncSet->pStrOIDFuncHead = AddFuncSet.pStrOIDFuncHead; } else { pFuncSet->pStrOIDFuncTail->pNext = AddFuncSet.pStrOIDFuncHead; pFuncSet->pStrOIDFuncTail = AddFuncSet.pStrOIDFuncTail; } } UnlockOIDFunc(); fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; FreeFuncSetConstAndStrElements(&AddFuncSet); goto CommonReturn; TRACE_ERROR(AddConstOIDFuncError) TRACE_ERROR(AddStrOIDFuncError) } STATIC LPSTR EncodingTypeToRegName( IN DWORD dwEncodingType ) { LPSTR pszRegName; DWORD cchRegName; char szEncodingTypeValue[33]; dwEncodingType = GetEncodingType(dwEncodingType); _ltoa(dwEncodingType, szEncodingTypeValue, 10); cchRegName = strlen(CRYPT_OID_REG_ENCODING_TYPE_PREFIX) + strlen(szEncodingTypeValue) + 1; if (pszRegName = (LPSTR) PkiNonzeroAlloc(cchRegName)) { strcpy(pszRegName, CRYPT_OID_REG_ENCODING_TYPE_PREFIX); strcat(pszRegName, szEncodingTypeValue); } return pszRegName; } // Returns FALSE for an invalid EncodingType reg name STATIC BOOL RegNameToEncodingType( IN LPCSTR pszRegEncodingType, OUT DWORD *pdwEncodingType ) { BOOL fResult = FALSE; DWORD dwEncodingType = 0; const DWORD cchPrefix = strlen(CRYPT_OID_REG_ENCODING_TYPE_PREFIX); if (pszRegEncodingType && (DWORD) strlen(pszRegEncodingType) >= cchPrefix && 2 == CompareStringA(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, pszRegEncodingType, cchPrefix, CRYPT_OID_REG_ENCODING_TYPE_PREFIX, cchPrefix)) { long lEncodingType; lEncodingType = atol(pszRegEncodingType + cchPrefix); if (lEncodingType >= 0 && lEncodingType <= 0xFFFF) { dwEncodingType = (DWORD) lEncodingType; fResult = TRUE; } } *pdwEncodingType = dwEncodingType; return fResult; } STATIC LPSTR FormatOIDFuncRegName( IN DWORD dwEncodingType, IN LPCSTR pszFuncName, IN LPCSTR pszOID ) { LPSTR pszRegEncodingType; LPSTR pszRegName; DWORD cchRegName; char szOID[34]; if (pszOID == NULL) { SetLastError((DWORD) E_INVALIDARG); return NULL; } if (NULL == (pszRegEncodingType = EncodingTypeToRegName(dwEncodingType))) return NULL; if ((DWORD_PTR) pszOID <= 0xFFFF) { szOID[0] = CONST_OID_STR_PREFIX_CHAR; _ltoa((long) ((DWORD_PTR)pszOID), szOID + 1, 10); pszOID = szOID; } cchRegName = strlen(CRYPT_OID_REGPATH "\\") + strlen(pszRegEncodingType) + 1 + strlen(pszFuncName) + 1 + strlen(pszOID) + 1; if (pszRegName = (LPSTR) PkiNonzeroAlloc(cchRegName)) { strcpy(pszRegName, CRYPT_OID_REGPATH "\\"); strcat(pszRegName, pszRegEncodingType); strcat(pszRegName, "\\"); strcat(pszRegName, pszFuncName); strcat(pszRegName, "\\"); strcat(pszRegName, pszOID); } PkiFree(pszRegEncodingType); return pszRegName; } //+------------------------------------------------------------------------- // Set the value for the specified encoding type, function name, OID and // value name. // // See RegSetValueEx for the possible value types. // // String types are UNICODE. // // If pbValueData == NULL and cbValueData == 0, deletes the value. //-------------------------------------------------------------------------- BOOL WINAPI CryptSetOIDFunctionValue( IN DWORD dwEncodingType, IN LPCSTR pszFuncName, IN LPCSTR pszOID, IN LPCWSTR pwszValueName, IN DWORD dwValueType, IN const BYTE *pbValueData, IN DWORD cbValueData ) { BOOL fResult; LONG lStatus; LPSTR pszRegName = NULL; HKEY hKey = NULL; DWORD dwDisposition; if (NULL == (pszRegName = FormatOIDFuncRegName( dwEncodingType, pszFuncName, pszOID))) goto FormatRegNameError; if (ERROR_SUCCESS != (lStatus = RegCreateKeyExA( HKEY_LOCAL_MACHINE, pszRegName, 0, // dwReserved NULL, // lpClass REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, // lpSecurityAttributes &hKey, &dwDisposition))) goto RegCreateKeyError; if (NULL == pbValueData && 0 == cbValueData) { if (ERROR_SUCCESS != (lStatus = RegDeleteValueU( hKey, pwszValueName))) goto RegDeleteValueError; } else { if (ERROR_SUCCESS != (lStatus = RegSetValueExU( hKey, pwszValueName, 0, // dwReserved dwValueType, pbValueData, cbValueData))) goto RegSetValueError; } fResult = TRUE; CommonReturn: if (pszRegName) PkiFree(pszRegName); if (hKey) RegCloseKey(hKey); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(FormatRegNameError) SET_ERROR_VAR(RegCreateKeyError, lStatus) SET_ERROR_VAR(RegDeleteValueError, lStatus) SET_ERROR_VAR(RegSetValueError, lStatus) } //+------------------------------------------------------------------------- // Get the value for the specified encoding type, function name, OID and // value name. // // See RegEnumValue for the possible value types. // // String types are UNICODE. //-------------------------------------------------------------------------- BOOL WINAPI CryptGetOIDFunctionValue( IN DWORD dwEncodingType, IN LPCSTR pszFuncName, IN LPCSTR pszOID, IN LPCWSTR pwszValueName, OUT DWORD *pdwValueType, OUT BYTE *pbValueData, IN OUT DWORD *pcbValueData ) { BOOL fResult; LONG lStatus; LPSTR pszRegName = NULL; HKEY hKey = NULL; if (NULL == (pszRegName = FormatOIDFuncRegName( dwEncodingType, pszFuncName, pszOID))) goto FormatRegNameError; if (ERROR_SUCCESS != (lStatus = RegOpenKeyExA( HKEY_LOCAL_MACHINE, pszRegName, 0, // dwReserved KEY_READ, &hKey))) { if (ERROR_FILE_NOT_FOUND == lStatus) { // Inhibit error tracing SetLastError((DWORD) lStatus); goto ErrorReturn; } goto RegOpenKeyError; } if (ERROR_SUCCESS != (lStatus = RegQueryValueExU( hKey, pwszValueName, NULL, // lpdwReserved pdwValueType, pbValueData, pcbValueData))) goto RegQueryValueError; fResult = TRUE; CommonReturn: if (pszRegName) PkiFree(pszRegName); if (hKey) RegCloseKey(hKey); return fResult; ErrorReturn: *pdwValueType = 0; *pcbValueData = 0; fResult = FALSE; goto CommonReturn; TRACE_ERROR(FormatRegNameError) SET_ERROR_VAR(RegOpenKeyError, lStatus) SET_ERROR_VAR(RegQueryValueError, lStatus) } //+------------------------------------------------------------------------- // Register the Dll containing the function to be called for the specified // encoding type, function name and OID. // // pwszDll may contain environment-variable strings // which are ExpandEnvironmentStrings()'ed before loading the Dll. // // In addition to registering the DLL, you may override the // name of the function to be called. For example, // pszFuncName = "CryptDllEncodeObject", // pszOverrideFuncName = "MyEncodeXyz". // This allows a Dll to export multiple OID functions for the same // function name without needing to interpose its own OID dispatcher function. //-------------------------------------------------------------------------- BOOL WINAPI CryptRegisterOIDFunction( IN DWORD dwEncodingType, IN LPCSTR pszFuncName, IN LPCSTR pszOID, IN OPTIONAL LPCWSTR pwszDll, IN OPTIONAL LPCSTR pszOverrideFuncName ) { BOOL fResult; LPWSTR pwszOverrideFuncName = NULL; if (pwszDll) { if (!CryptSetOIDFunctionValue( dwEncodingType, pszFuncName, pszOID, CRYPT_OID_REG_DLL_VALUE_NAME, REG_SZ, (BYTE *) pwszDll, (wcslen(pwszDll) + 1) * sizeof(WCHAR))) goto SetDllError; } if (pszOverrideFuncName) { if (NULL == (pwszOverrideFuncName = MkWStr( (LPSTR) pszOverrideFuncName))) goto MkWStrError; if (!CryptSetOIDFunctionValue( dwEncodingType, pszFuncName, pszOID, CRYPT_OID_REG_FUNC_NAME_VALUE_NAME, REG_SZ, (BYTE *) pwszOverrideFuncName, (wcslen(pwszOverrideFuncName) + 1) * sizeof(WCHAR))) goto SetFuncNameError; } fResult = TRUE; CommonReturn: if (pwszOverrideFuncName) FreeWStr(pwszOverrideFuncName); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(SetDllError) TRACE_ERROR(SetFuncNameError) TRACE_ERROR(MkWStrError) } //+------------------------------------------------------------------------- // Unregister the Dll containing the function to be called for the specified // encoding type, function name and OID. //-------------------------------------------------------------------------- BOOL WINAPI CryptUnregisterOIDFunction( IN DWORD dwEncodingType, IN LPCSTR pszFuncName, IN LPCSTR pszOID ) { BOOL fResult; LONG lStatus; LPSTR pszRegName = NULL; LPSTR pszRegOID; HKEY hKey = NULL; if (NULL == (pszRegName = FormatOIDFuncRegName( dwEncodingType, pszFuncName, pszOID))) goto FormatRegNameError; // Separate off the OID component of the RegName. Its the // last component of the name. pszRegOID = pszRegName + strlen(pszRegName); while (*pszRegOID != '\\') pszRegOID--; *pszRegOID++ = '\0'; if (ERROR_SUCCESS != (lStatus = RegOpenKeyExA( HKEY_LOCAL_MACHINE, pszRegName, 0, // dwReserved KEY_WRITE, &hKey))) goto RegOpenKeyError; if (ERROR_SUCCESS != (lStatus = RegDeleteKeyA( hKey, pszRegOID))) goto RegDeleteKeyError; fResult = TRUE; CommonReturn: if (pszRegName) PkiFree(pszRegName); if (hKey) RegCloseKey(hKey); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(FormatRegNameError) SET_ERROR_VAR(RegOpenKeyError, lStatus) SET_ERROR_VAR(RegDeleteKeyError, lStatus) } STATIC BOOL GetDefaultDllList( IN DWORD dwEncodingType, IN LPCSTR pszFuncName, OUT LPWSTR pwszList, IN OUT DWORD *pcchList ) { BOOL fResult; DWORD dwType; DWORD cchList; DWORD cbList; cchList = *pcchList; if (pwszList) { if (cchList < 3) goto InvalidArg; else // make room for two extra null terminators cchList -= 2; } else cchList = 0; cbList = cchList * sizeof(WCHAR); fResult = CryptGetOIDFunctionValue( dwEncodingType, pszFuncName, CRYPT_DEFAULT_OID, CRYPT_OID_REG_DLL_VALUE_NAME, &dwType, (BYTE *) pwszList, &cbList); cchList = cbList / sizeof(WCHAR); if (!fResult) { if (ERROR_FILE_NOT_FOUND != GetLastError()) { if (cchList) cchList += 2; goto GetOIDFunctionValueError; } cchList = 0; } else if (!(REG_MULTI_SZ == dwType || REG_SZ == dwType || REG_EXPAND_SZ == dwType)) goto BadDefaultListRegType; if (pwszList) { // Ensure the list has two null terminators pwszList[cchList++] = L'\0'; pwszList[cchList++] = L'\0'; } else { if (cchList == 0) cchList = 3; else cchList += 2; } fResult = TRUE; CommonReturn: *pcchList = cchList; return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(GetOIDFunctionValueError) SET_ERROR(InvalidArg, E_INVALIDARG) SET_ERROR(BadDefaultListRegType, E_INVALIDARG) } // Remove any entries following the first empty string. STATIC DWORD AdjustDefaultListLength( IN LPCWSTR pwszList ) { LPCWSTR pwsz = pwszList; DWORD cch; while (cch = wcslen(pwsz)) pwsz += cch + 1; return (DWORD)(pwsz - pwszList) + 1; } //+------------------------------------------------------------------------- // Register the Dll containing the default function to be called for the // specified encoding type and function name. // // Unlike CryptRegisterOIDFunction, you can't override the function name // needing to be exported by the Dll. // // The Dll is inserted before the entry specified by dwIndex. // dwIndex == 0, inserts at the beginning. // dwIndex == CRYPT_REGISTER_LAST_INDEX, appends at the end. // // pwszDll may contain environment-variable strings // which are ExpandEnvironmentStrings()'ed before loading the Dll. //-------------------------------------------------------------------------- BOOL WINAPI CryptRegisterDefaultOIDFunction( IN DWORD dwEncodingType, IN LPCSTR pszFuncName, IN DWORD dwIndex, IN LPCWSTR pwszDll ) { BOOL fResult; LPWSTR pwszDllList; // _alloca'ed DWORD cchDllList; DWORD cchDll; LPWSTR pwsz, pwszInsert, pwszSrc, pwszDest; DWORD cch, cchRemain; if (NULL == pwszDll || L'\0' == *pwszDll) goto InvalidArg; cchDll = wcslen(pwszDll) + 1; if (!GetDefaultDllList( dwEncodingType, pszFuncName, NULL, // pwszDllList &cchDllList)) goto GetDefaultDllListError; __try { pwszDllList = (LPWSTR) _alloca((cchDllList + cchDll) * sizeof(WCHAR)); } __except(EXCEPTION_EXECUTE_HANDLER) { goto OutOfMemory; } if (!GetDefaultDllList( dwEncodingType, pszFuncName, pwszDllList, &cchDllList)) goto GetDefaultDllListError; // Remove entries following the first empty entry assert(AdjustDefaultListLength(pwszDllList) <= cchDllList); cchDllList = AdjustDefaultListLength(pwszDllList); // Check if the Dll already exists in the list pwsz = pwszDllList; while (cch = wcslen(pwsz)) { if (0 == _wcsicmp(pwsz, pwszDll)) goto DllExistsError; pwsz += cch + 1; } // Find the Null terminated DLL in the DllList to insert before. // We insert before the dwIndex. pwszInsert = pwszDllList; while (dwIndex-- && 0 != (cch = wcslen(pwszInsert))) pwszInsert += cch + 1; // Before inserting, we need to move all the remaining entries in the // existing DllList. // // Note, there must be at least the final zero terminator at // pwszDllList[cchDllList - 1]. assert(pwszInsert < pwszDllList + cchDllList); if (pwszInsert >= pwszDllList + cchDllList) goto BadRegMultiSzError; cchRemain = (DWORD)((pwszDllList + cchDllList) - pwszInsert); assert(cchRemain); pwszSrc = pwszDllList + cchDllList - 1; pwszDest = pwszSrc + cchDll; while (cchRemain--) *pwszDest-- = *pwszSrc--; assert(pwszSrc + 1 == pwszInsert); // Insert the pwszDll memcpy(pwszInsert, pwszDll, cchDll * sizeof(WCHAR)); if (!CryptSetOIDFunctionValue( dwEncodingType, pszFuncName, CRYPT_DEFAULT_OID, CRYPT_OID_REG_DLL_VALUE_NAME, REG_MULTI_SZ, (BYTE *) pwszDllList, (cchDllList + cchDll) * sizeof(WCHAR))) goto SetDllListError; fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) SET_ERROR(OutOfMemory, E_OUTOFMEMORY) SET_ERROR(DllExistsError, ERROR_FILE_EXISTS) SET_ERROR(BadRegMultiSzError, E_INVALIDARG) TRACE_ERROR(GetDefaultDllListError) TRACE_ERROR(SetDllListError) } BOOL WINAPI CryptUnregisterDefaultOIDFunction( IN DWORD dwEncodingType, IN LPCSTR pszFuncName, IN LPCWSTR pwszDll ) { BOOL fResult; LPWSTR pwszDllList; // _alloca'ed DWORD cchDllList; DWORD cchDll; LPWSTR pwszDelete, pwszMove; DWORD cchDelete, cchRemain; if (NULL == pwszDll || L'\0' == *pwszDll) goto InvalidArg; cchDll = wcslen(pwszDll) + 1; if (!GetDefaultDllList( dwEncodingType, pszFuncName, NULL, // pwszDllList &cchDllList)) goto GetDefaultDllListError; __try { pwszDllList = (LPWSTR) _alloca(cchDllList * sizeof(WCHAR)); } __except(EXCEPTION_EXECUTE_HANDLER) { goto OutOfMemory; } if (!GetDefaultDllList( dwEncodingType, pszFuncName, pwszDllList, &cchDllList)) goto GetDefaultDllListError; // Remove entries following the first empty entry assert(AdjustDefaultListLength(pwszDllList) <= cchDllList); cchDllList = AdjustDefaultListLength(pwszDllList); // Search the DllList for a match pwszDelete = pwszDllList; while (cchDelete = wcslen(pwszDelete)) { if (0 == _wcsicmp(pwszDll, pwszDelete)) break; pwszDelete += cchDelete + 1; } if (0 == cchDelete) goto DllNotFound; cchDelete++; assert(cchDelete == cchDll); // Move all the Dll entries that follow. // // Note, there must be at least the final zero terminator at // pwszDllList[cchDllList - 1]. pwszMove = pwszDelete + cchDelete; assert(pwszMove < pwszDllList + cchDllList); if (pwszMove >= pwszDllList + cchDllList) goto BadRegMultiSzError; cchRemain = (DWORD)((pwszDllList + cchDllList) - pwszMove); assert(cchRemain); while (cchRemain--) *pwszDelete++ = *pwszMove++; if (!CryptSetOIDFunctionValue( dwEncodingType, pszFuncName, CRYPT_DEFAULT_OID, CRYPT_OID_REG_DLL_VALUE_NAME, REG_MULTI_SZ, (BYTE *) pwszDllList, (cchDllList - cchDelete) * sizeof(WCHAR))) goto SetDllListError; fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) SET_ERROR(DllNotFound, ERROR_FILE_NOT_FOUND) SET_ERROR(OutOfMemory, E_OUTOFMEMORY) SET_ERROR(BadRegMultiSzError, E_INVALIDARG) TRACE_ERROR(GetDefaultDllListError) TRACE_ERROR(SetDllListError) } #define MAX_SUBKEY_LEN 128 STATIC HKEY GetNextRegSubKey( IN HKEY hKey, IN OUT DWORD *piSubKey, IN LPCSTR pszFuncNameMatch, OUT char szSubKeyName[MAX_SUBKEY_LEN] ) { HKEY hSubKey; if (pszFuncNameMatch && *pszFuncNameMatch) { if ((*piSubKey)++ > 0 || strlen(pszFuncNameMatch) >= MAX_SUBKEY_LEN) return NULL; strcpy(szSubKeyName, pszFuncNameMatch); } else { if (ERROR_SUCCESS != RegEnumKeyA( hKey, (*piSubKey)++, szSubKeyName, MAX_SUBKEY_LEN)) return NULL; } if (ERROR_SUCCESS == RegOpenKeyExA( hKey, szSubKeyName, 0, // dwReserved KEY_READ, &hSubKey)) return hSubKey; else return NULL; } STATIC BOOL GetRegValues( IN HKEY hKey, OUT void **ppvAlloc, OUT DWORD *pcValue, OUT DWORD **ppdwValueType, OUT LPWSTR **pppwszValueName, OUT BYTE ***pppbValueData, OUT DWORD **ppcbValueData ) { BOOL fResult; LONG lStatus; void *pvAlloc = NULL; DWORD cValue; DWORD iValue; DWORD cchMaxName; DWORD cbMaxData; DWORD cbAlignData = 0; DWORD *pdwValueType; LPWSTR *ppwszValueName; BYTE **ppbValueData; DWORD *pcbValueData; LPWSTR pwszName; BYTE *pbData; if (ERROR_SUCCESS != (lStatus = RegQueryInfoKeyU( hKey, NULL, NULL, NULL, NULL, NULL, NULL, &cValue, &cchMaxName, &cbMaxData, NULL, NULL ))) goto RegQueryInfoKeyError; // Do a single allocation for all the arrays, value names and // value data. Update the array pointers. if (cValue > 0) { BYTE *pbAlloc; DWORD cbAlloc; // Include NULL terminator for the name and align the data length // Also, include two NULL terminators to be added for the data. // Ensures REG_MULTI_SZ is always NULL terminated. cchMaxName++; if (4 > cbMaxData) cbMaxData = 4; cbAlignData = LEN_ALIGN(cbMaxData + 2 * sizeof(WCHAR)); cbAlloc = (sizeof(DWORD) + sizeof(LPWSTR) + sizeof(BYTE *) + sizeof(DWORD) + cchMaxName * sizeof(WCHAR) + cbAlignData) * cValue; if (NULL == (pvAlloc = PkiNonzeroAlloc(cbAlloc))) goto OutOfMemory; pbAlloc = (BYTE *) pvAlloc; ppwszValueName = (LPWSTR *) pbAlloc; pbAlloc += sizeof(LPWSTR) * cValue; ppbValueData = (BYTE **) pbAlloc; pbAlloc += sizeof(BYTE *) * cValue; pdwValueType = (DWORD *) pbAlloc; pbAlloc += sizeof(DWORD) * cValue; pcbValueData = (DWORD *) pbAlloc; pbAlloc += sizeof(DWORD) * cValue; pbData = pbAlloc; pbAlloc += cbAlignData * cValue; pwszName = (LPWSTR) pbAlloc; assert(((BYTE *) pvAlloc) + cbAlloc == pbAlloc + (cchMaxName * sizeof(WCHAR)) * cValue); } else { ppwszValueName = NULL; ppbValueData = NULL; pdwValueType = NULL; pcbValueData = NULL; pbData = NULL; pwszName = NULL; } for (iValue = 0; iValue < cValue; iValue++, pwszName += cchMaxName, pbData += cbAlignData) { DWORD cchName = cchMaxName; DWORD cbData = cbMaxData; DWORD dwType; if (ERROR_SUCCESS != (lStatus = RegEnumValueU( hKey, iValue, pwszName, &cchName, NULL, // pdwReserved &dwType, pbData, &cbData ))) goto RegEnumValueError; // Ensure the data has two NULL terminators for REG_MULTI_SZ // Note cbAlignData >= cbMaxData + 2 * sizeof(WCHAR) memset(pbData + cbData, 0, 2 * sizeof(WCHAR)); pdwValueType[iValue] = dwType; ppwszValueName[iValue] = pwszName; ppbValueData[iValue] = pbData; pcbValueData[iValue] = cbData; } fResult = TRUE; CommonReturn: *ppvAlloc = pvAlloc; *pcValue = cValue; *ppdwValueType = pdwValueType; *pppwszValueName = ppwszValueName; *pppbValueData = ppbValueData; *ppcbValueData = pcbValueData; return fResult; ErrorReturn: if (pvAlloc) { PkiFree(pvAlloc); pvAlloc = NULL; } cValue = 0; pdwValueType = NULL; ppwszValueName = NULL; ppbValueData = NULL; pcbValueData = NULL; fResult = FALSE; goto CommonReturn; TRACE_ERROR(OutOfMemory) SET_ERROR_VAR(RegQueryInfoKeyError, lStatus) SET_ERROR_VAR(RegEnumValueError, lStatus) } //+------------------------------------------------------------------------- // Enumerate the OID functions identified by their encoding type, // function name and OID. // // pfnEnumOIDFunc is called for each registry key matching the input // parameters. Setting dwEncodingType to CRYPT_MATCH_ANY_ENCODING_TYPE matches // any. Setting pszFuncName or pszOID to NULL matches any. // // Set pszOID == CRYPT_DEFAULT_OID to restrict the enumeration to only the // DEFAULT functions // // String types are UNICODE. //-------------------------------------------------------------------------- BOOL WINAPI CryptEnumOIDFunction( IN DWORD dwEncodingType, IN LPCSTR pszFuncName, IN LPCSTR pszOID, IN DWORD dwFlags, IN void *pvArg, IN PFN_CRYPT_ENUM_OID_FUNC pfnEnumOIDFunc ) { HKEY hRegKey; LPSTR pszEncodingType = NULL; char szOID[34]; if (CRYPT_MATCH_ANY_ENCODING_TYPE != dwEncodingType) { dwEncodingType = GetEncodingType(dwEncodingType); if (NULL == (pszEncodingType = EncodingTypeToRegName(dwEncodingType))) return FALSE; } if (pszOID && (DWORD_PTR) pszOID <= 0xFFFF) { szOID[0] = CONST_OID_STR_PREFIX_CHAR; _ltoa((DWORD) ((DWORD_PTR)pszOID), szOID + 1, 10); pszOID = szOID; } if (ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, CRYPT_OID_REGPATH, 0, // dwReserved KEY_READ, &hRegKey)) { // Enumerate and optionally match encoding type HKEY hEncodingTypeKey; DWORD iEncodingType = 0; char szRegEncodingType[MAX_SUBKEY_LEN]; while (hEncodingTypeKey = GetNextRegSubKey(hRegKey, &iEncodingType, pszEncodingType, szRegEncodingType)) { // Convert the EncodingType string and validate DWORD dwRegEncodingType; if (RegNameToEncodingType(szRegEncodingType, &dwRegEncodingType)) { // Enumerate and optionally match FuncName, for example, // ("CryptDllEncodeObject") HKEY hFuncName; DWORD iFuncName = 0; char szRegFuncName[MAX_SUBKEY_LEN]; while (hFuncName = GetNextRegSubKey(hEncodingTypeKey, &iFuncName, pszFuncName, szRegFuncName)) { // Enumerate and optionally match OID string ("1.2.3.4") HKEY hOID; DWORD iOID = 0; char szRegOID[MAX_SUBKEY_LEN]; while (hOID = GetNextRegSubKey(hFuncName, &iOID, pszOID, szRegOID)) { // Read and allocate the registry values void *pvAlloc; DWORD cValue; DWORD *pdwValueType; LPWSTR *ppwszValueName; BYTE **ppbValueData; DWORD *pcbValueData; if (GetRegValues( hOID, &pvAlloc, &cValue, &pdwValueType, &ppwszValueName, &ppbValueData, &pcbValueData)) { pfnEnumOIDFunc( dwRegEncodingType, szRegFuncName, szRegOID, cValue, pdwValueType, (LPCWSTR *) ppwszValueName, (const BYTE **) ppbValueData, pcbValueData, pvArg); if (pvAlloc) PkiFree(pvAlloc); } RegCloseKey(hOID); } RegCloseKey(hFuncName); } } RegCloseKey(hEncodingTypeKey); } RegCloseKey(hRegKey); } if (pszEncodingType) PkiFree(pszEncodingType); return TRUE; } //+========================================================================= // Registry and Dll Load Functions //========================================================================== // Note, returned Dll element isn't AddRef'ed STATIC PDLL_ELEMENT FindDll( IN LPCWSTR pwszDll // not expanded ) { LPWSTR pwszExpandDll; // _alloca'ed WCHAR rgch[4]; DWORD cchDll; PDLL_ELEMENT pDll; if (0 == (cchDll = ExpandEnvironmentStringsU( pwszDll, rgch, // lpszDest, NON_NULL for win95 sizeof(rgch)/sizeof(rgch[0])))) // cchDest return NULL; __try { pwszExpandDll = (LPWSTR) _alloca(cchDll * sizeof(WCHAR)); } __except(EXCEPTION_EXECUTE_HANDLER) { return NULL; } if (0 == ExpandEnvironmentStringsU( pwszDll, pwszExpandDll, cchDll)) return NULL; LockOIDFunc(); // Check if we already have an entry for (pDll = pDllHead; pDll; pDll = pDll->pNext) { if (0 == _wcsicmp(pwszExpandDll, pDll->pwszDll)) break; } if (NULL == pDll) { // Need to create a new DLL entry and add to our list if (pDll = (PDLL_ELEMENT) PkiZeroAlloc( sizeof(DLL_ELEMENT) + cchDll * sizeof(WCHAR))) { LPWSTR pwszEleDll; pDll->dwOIDType = DLL_OID_TYPE; pwszEleDll = (LPWSTR) ((BYTE *) pDll + sizeof(DLL_ELEMENT)); memcpy(pwszEleDll, pwszExpandDll, cchDll * sizeof(WCHAR)); pDll->pwszDll = pwszEleDll; pDll->pNext = pDllHead; pDllHead = pDll; } } UnlockOIDFunc(); return pDll; } // Upon entry/exit OIDFunc is locked STATIC PDLL_PROC_ELEMENT AddDllProc( IN LPCSTR pszFuncName, IN LPCWSTR pwszDll ) { PDLL_PROC_ELEMENT pProcEle = NULL; PDLL_ELEMENT pDll; DWORD cchFuncName; DWORD cbEle; LPSTR psz; cchFuncName = strlen(pszFuncName) + 1; cbEle = sizeof(DLL_PROC_ELEMENT) + cchFuncName; if (NULL == (pProcEle = (PDLL_PROC_ELEMENT) PkiZeroAlloc(cbEle))) goto OutOfMemory; if (NULL == (pDll = FindDll(pwszDll))) goto FindDllError; pProcEle->pNext = pDll->pProcHead; pDll->pProcHead = pProcEle; pProcEle->pDll = pDll; psz = (LPSTR) ((BYTE *) pProcEle + sizeof(DLL_PROC_ELEMENT)); memcpy(psz, pszFuncName, cchFuncName); pProcEle->pszName = psz; pProcEle->pvAddr = NULL; CommonReturn: return pProcEle; ErrorReturn: PkiFree(pProcEle); pProcEle = NULL; goto CommonReturn; TRACE_ERROR(OutOfMemory) TRACE_ERROR(FindDllError) } // Upon entry/exit OIDFunc is locked STATIC void AddRegOIDFunc( IN DWORD dwEncodingType, IN OUT PFUNC_SET pFuncSet, IN LPCSTR pszFuncName, IN LPCSTR pszOID, IN LPCWSTR pwszDll, IN DWORD dwCryptFlags ) { PREG_OID_FUNC_ELEMENT pOIDEle = NULL; PDLL_PROC_ELEMENT pProcEle; // not allocated, doesn't need to be free'ed DWORD cchOID; DWORD cbEle; LPSTR psz; if (0xFFFF < (DWORD_PTR) pszOID) cchOID = strlen(pszOID) + 1; else cchOID = 0; cbEle = sizeof(REG_OID_FUNC_ELEMENT) + cchOID; if (NULL == (pOIDEle = (PREG_OID_FUNC_ELEMENT) PkiZeroAlloc(cbEle))) goto OutOfMemory; if (NULL == (pProcEle = AddDllProc(pszFuncName, pwszDll))) goto AddDllProcError; pOIDEle->dwEncodingType = dwEncodingType; if (dwCryptFlags & CRYPT_INSTALL_OID_FUNC_BEFORE_FLAG) { pOIDEle->pNext = pFuncSet->pRegBeforeOIDFuncHead; pFuncSet->pRegBeforeOIDFuncHead = pOIDEle; } else { pOIDEle->pNext = pFuncSet->pRegAfterOIDFuncHead; pFuncSet->pRegAfterOIDFuncHead = pOIDEle; } if (cchOID) { psz = (LPSTR) ((BYTE *) pOIDEle + sizeof(REG_OID_FUNC_ELEMENT)); memcpy(psz, pszOID, cchOID); pOIDEle->pszOID = psz; } else pOIDEle->dwOID = (DWORD_PTR) pszOID; pOIDEle->pDllProc = pProcEle; CommonReturn: return; ErrorReturn: PkiFree(pOIDEle); goto CommonReturn; TRACE_ERROR(OutOfMemory) TRACE_ERROR(AddDllProcError) } // Upon entry/exit OIDFunc is locked STATIC void AddDefaultDllList( IN DWORD dwEncodingType, IN OUT PFUNC_SET pFuncSet, IN LPCWSTR pwszInDllList, IN DWORD cchInDllList ) { LPWSTR pwszDllList; // _alloca'ed LPWSTR pwsz; DWORD cchDllList; DWORD cchDll; DWORD cDll; DWORD i; PDEFAULT_REG_ELEMENT pEle = NULL; DWORD cbEle; LPWSTR *ppwszEleDll; PDLL_PROC_ELEMENT *ppEleDllProc; LPWSTR pwszEleDllList; // Ensure cchDllList has 2 terminating NULL characters assert(cchInDllList && pwszInDllList); cchDllList = cchInDllList + 2; __try { pwszDllList = (LPWSTR) _alloca(cchDllList * sizeof(WCHAR)); } __except(EXCEPTION_EXECUTE_HANDLER) { goto OutOfMemory; } memcpy(pwszDllList, pwszInDllList, cchInDllList * sizeof(WCHAR)); pwszDllList[cchInDllList] = L'\0'; pwszDllList[cchInDllList + 1] = L'\0'; // Get count of null terminated Dlls cDll = 0; for (pwsz = pwszDllList; 0 != (cchDll = wcslen(pwsz)); pwsz += cchDll + 1) cDll++; if (0 == cDll) goto NoDll; cbEle = sizeof(DEFAULT_REG_ELEMENT) + cDll * sizeof(LPWSTR) + cDll * sizeof(PDLL_PROC_ELEMENT) + cchDllList * sizeof(WCHAR) ; if (NULL == (pEle = (PDEFAULT_REG_ELEMENT) PkiZeroAlloc(cbEle))) goto OutOfMemory; ppwszEleDll = (LPWSTR *) ((BYTE *) pEle + sizeof(DEFAULT_REG_ELEMENT)); ppEleDllProc = (PDLL_PROC_ELEMENT *) ((BYTE *) ppwszEleDll + cDll * sizeof(LPWSTR)); pwszEleDllList = (LPWSTR) ((BYTE *) ppEleDllProc + cDll * sizeof(PDLL_PROC_ELEMENT)); assert((BYTE *) pwszEleDllList + cchDllList * sizeof(WCHAR) == (BYTE *) pEle + cbEle); pEle->dwEncodingType = dwEncodingType; // pEle->pNext = memcpy(pwszEleDllList, pwszDllList, cchDllList * sizeof(WCHAR)); pEle->pwszDllList = pwszEleDllList; pEle->cchDllList = cchDllList; pEle->cDll = cDll; pEle->rgpwszDll = ppwszEleDll; pEle->rgpDllProc = ppEleDllProc; for (pwsz = pwszEleDllList, i = 0; 0 != (cchDll = wcslen(pwsz)); pwsz += cchDll + 1, i++) { ppwszEleDll[i] = pwsz; if (NULL == (ppEleDllProc[i] = AddDllProc( pFuncSet->pszFuncName, pwsz))) goto AddDllProcError; } assert (i == cDll); pEle->pNext = pFuncSet->pDefaultRegHead; pFuncSet->pDefaultRegHead = pEle; CommonReturn: return; ErrorReturn: PkiFree(pEle); goto CommonReturn; TRACE_ERROR(NoDll); TRACE_ERROR(OutOfMemory); TRACE_ERROR(AddDllProcError); } //+------------------------------------------------------------------------- // Called by CryptEnumOIDFunction to enumerate through all the // registered OID functions. // // Called with OIDFunc locked //-------------------------------------------------------------------------- STATIC BOOL WINAPI EnumRegFuncCallback( IN DWORD dwEncodingType, IN LPCSTR pszFuncName, IN LPCSTR pszOID, IN DWORD cValue, IN const DWORD rgdwValueType[], IN LPCWSTR const rgpwszValueName[], IN const BYTE * const rgpbValueData[], IN const DWORD rgcbValueData[], IN void *pvArg ) { PFUNC_SET pFuncSet = (PFUNC_SET) pvArg; BOOL fDefaultDllList = FALSE; LPCWSTR pwszDll = NULL; // not allocated DWORD cchDll = 0; LPCWSTR pwszOverrideFuncName = NULL; // not allocated DWORD dwCryptFlags = 0; assert(pFuncSet); if (CONST_OID_STR_PREFIX_CHAR == *pszOID) { // Convert "#" string to its corresponding constant OID value pszOID = (LPCSTR)(DWORD_PTR) atol(pszOID + 1); if (0xFFFF < (DWORD_PTR) pszOID) // Invalid OID. Skip it. goto InvalidOID; } else if (0 == _stricmp(CRYPT_DEFAULT_OID, pszOID)) fDefaultDllList = TRUE; while (cValue--) { LPCWSTR pwszValueName = rgpwszValueName[cValue]; DWORD dwValueType = rgdwValueType[cValue]; const BYTE *pbValueData = rgpbValueData[cValue]; DWORD cbValueData = rgcbValueData[cValue]; if (0 == _wcsicmp(pwszValueName, CRYPT_OID_REG_DLL_VALUE_NAME)) { if (REG_SZ == dwValueType || REG_EXPAND_SZ == dwValueType || (fDefaultDllList && REG_MULTI_SZ == dwValueType)) { pwszDll = (LPCWSTR) pbValueData; cchDll = cbValueData / sizeof(WCHAR); } else // Invalid "Dll" value. goto InvalidDll; } else if (0 == _wcsicmp(pwszValueName, CRYPT_OID_REG_FUNC_NAME_VALUE_NAME)) { if (REG_SZ == dwValueType) { LPCWSTR pwszValue = (LPCWSTR) pbValueData; if (L'\0' != *pwszValue) pwszOverrideFuncName = pwszValue; } else // Invalid "FuncName" value. goto InvalidFuncName; } else if (0 == _wcsicmp(pwszValueName, CRYPT_OID_REG_FLAGS_VALUE_NAME)) { if (REG_DWORD == dwValueType && cbValueData >= sizeof(dwCryptFlags)) memcpy(&dwCryptFlags, pbValueData, sizeof(dwCryptFlags)); // else // Ignore invalid CryptFlags value type } } if (0 == cchDll || L'\0' == *pwszDll) goto NoDll; if (fDefaultDllList) AddDefaultDllList( dwEncodingType, pFuncSet, pwszDll, cchDll ); else { BYTE rgb[_MAX_PATH]; if (pwszOverrideFuncName) { if (!MkMBStr(rgb, _MAX_PATH, pwszOverrideFuncName, (LPSTR *) &pszFuncName)) goto MkMBStrError; } AddRegOIDFunc( dwEncodingType, pFuncSet, pszFuncName, pszOID, pwszDll, dwCryptFlags ); if (pwszOverrideFuncName) FreeMBStr(rgb, (LPSTR) pszFuncName); } CommonReturn: return TRUE; ErrorReturn: goto CommonReturn; TRACE_ERROR(InvalidOID) TRACE_ERROR(InvalidDll) TRACE_ERROR(InvalidFuncName) TRACE_ERROR(NoDll) TRACE_ERROR(MkMBStrError) } STATIC void LoadRegFunc( IN OUT PFUNC_SET pFuncSet ) { LockOIDFunc(); if (pFuncSet->fRegLoaded) goto CommonReturn; CryptEnumOIDFunction( CRYPT_MATCH_ANY_ENCODING_TYPE, pFuncSet->pszFuncName, NULL, // pszOID 0, // dwFlags (void *) pFuncSet, // pvArg EnumRegFuncCallback ); pFuncSet->fRegLoaded = TRUE; CommonReturn: UnlockOIDFunc(); return; } // Upon entry/exit OIDFunc is locked STATIC void RemoveFreeDll( IN PDLL_ELEMENT pDll ) { // Remove Dll from free list if (pDll->pFreeNext) pDll->pFreeNext->pFreePrev = pDll->pFreePrev; if (pDll->pFreePrev) pDll->pFreePrev->pFreeNext = pDll->pFreeNext; else if (pDll == pFreeDllHead) pFreeDllHead = pDll->pFreeNext; // else // Not on any list pDll->pFreeNext = NULL; pDll->pFreePrev = NULL; assert(dwFreeDllCnt); if (dwFreeDllCnt) dwFreeDllCnt--; } // Upon entry/exit OIDFunc is locked STATIC void AddRefDll( IN PDLL_ELEMENT pDll ) { pDll->dwRefCnt++; if (pDll->dwFreeCnt) { pDll->dwFreeCnt = 0; RemoveFreeDll(pDll); } } // Note, MUST NOT HOLD OID LOCK WHILE CALLING FreeLibrary()!! // // Therefore, will put the Dll's to be freed on a list while holding the // OID LOCK. After releasing the OID LOCK, will iterate through the // list and call FreeLibrary(). STATIC VOID NTAPI FreeDllWaitForCallback( PVOID Context, BOOLEAN fWaitOrTimedOut // ??? ) { PDLL_ELEMENT pFreeDll; HMODULE *phFreeLibrary = NULL; // _alloca'ed DWORD cFreeLibrary = 0; LockOIDFunc(); if (dwFreeDllCnt) { DWORD dwOrigFreeDllCnt = dwFreeDllCnt; __try { phFreeLibrary = (HMODULE *) _alloca( dwOrigFreeDllCnt * sizeof(HMODULE)); } __except(EXCEPTION_EXECUTE_HANDLER) { goto OutOfMemory; } pFreeDll = pFreeDllHead; assert(pFreeDll); while (pFreeDll) { PDLL_ELEMENT pDll = pFreeDll; pFreeDll = pFreeDll->pFreeNext; assert(pDll->dwFreeCnt); if (0 == --pDll->dwFreeCnt) { RemoveFreeDll(pDll); assert(pDll->fLoaded); if (!pDll->fLoaded) continue; if (NULL == pDll->pfnDllCanUnloadNow || S_OK == pDll->pfnDllCanUnloadNow()) { assert(cFreeLibrary < dwOrigFreeDllCnt); if (cFreeLibrary < dwOrigFreeDllCnt) { PDLL_PROC_ELEMENT pEle; // Loop and NULL all proc addresses for (pEle = pDll->pProcHead; pEle; pEle = pEle->pNext) pEle->pvAddr = NULL; pDll->pfnDllCanUnloadNow = NULL; // Add to array to be freed after releasing lock!! assert(pDll->hDll); phFreeLibrary[cFreeLibrary++] = pDll->hDll; pDll->hDll = NULL; pDll->fLoaded = FALSE; } } } } } else { assert(NULL == pFreeDllHead); } if (NULL == pFreeDllHead) { assert(0 == dwFreeDllCnt); // Do interlock to guard against a potential race condition at // PROCESS_DETACH. Note, PROCESS_DETACH doesn't do a LockOIDFunc(). if (InterlockedExchange(&lFreeDll, 0)) { HANDLE hRegWaitFor; HMODULE hDllLibModule; hRegWaitFor = hFreeDllRegWaitFor; hFreeDllRegWaitFor = NULL; hDllLibModule = hFreeDllLibModule; hFreeDllLibModule = NULL; UnlockOIDFunc(); while (cFreeLibrary--) FreeLibrary(phFreeLibrary[cFreeLibrary]); assert(hRegWaitFor); ILS_ExitWait(hRegWaitFor, hDllLibModule); assert(FALSE); return; } } CommonReturn: UnlockOIDFunc(); while (cFreeLibrary--) FreeLibrary(phFreeLibrary[cFreeLibrary]); return; ErrorReturn: goto CommonReturn; TRACE_ERROR(OutOfMemory); } STATIC void ReleaseDll( IN PDLL_ELEMENT pDll ) { LockOIDFunc(); assert(pDll->dwRefCnt); if (0 == --pDll->dwRefCnt) { assert(pDll->fLoaded); if (!pDll->fLoaded) goto CommonReturn; assert(0 == pDll->dwFreeCnt); if (pDll->dwFreeCnt) goto CommonReturn; if (0 == lFreeDll) { assert(NULL == hFreeDllRegWaitFor); assert(NULL == hFreeDllLibModule); // Inhibit crypt32.dll from being unloaded until this thread // exits. hFreeDllLibModule = DuplicateLibrary(hOidInfoInst); if (!ILS_RegisterWaitForSingleObject( &hFreeDllRegWaitFor, NULL, // hObject FreeDllWaitForCallback, NULL, // Context FREE_DLL_TIMEOUT, 0 // dwFlags )) { hFreeDllRegWaitFor = NULL; if (hFreeDllLibModule) { FreeLibrary(hFreeDllLibModule); hFreeDllLibModule = NULL; } goto RegisterWaitForError; } lFreeDll = 1; } assert(NULL == pDll->pFreeNext); assert(NULL == pDll->pFreePrev); pDll->dwFreeCnt = 2; if (pFreeDllHead) { pFreeDllHead->pFreePrev = pDll; pDll->pFreeNext = pFreeDllHead; } pFreeDllHead = pDll; dwFreeDllCnt++; } CommonReturn: UnlockOIDFunc(); return; ErrorReturn: goto CommonReturn; TRACE_ERROR(RegisterWaitForError) } // Upon entry/exit OIDFunc must NOT be locked!! STATIC BOOL LoadDll( IN PDLL_ELEMENT pDll ) { BOOL fResult; HMODULE hDll = NULL; LPFNCANUNLOADNOW pfnDllCanUnloadNow = NULL; LockOIDFunc(); if (pDll->fLoaded) AddRefDll(pDll); else { UnlockOIDFunc(); // NO LoadLibrary() or GetProcAddress() while holding OID lock!! hDll = LoadLibraryExU(pDll->pwszDll, NULL, 0); if (hDll) pfnDllCanUnloadNow = (LPFNCANUNLOADNOW) GetProcAddress( hDll, "DllCanUnloadNow"); LockOIDFunc(); AddRefDll(pDll); if (!pDll->fLoaded) { assert(1 == pDll->dwRefCnt); assert(0 == pDll->dwFreeCnt); assert(pDll->pwszDll); assert(NULL == pDll->hDll); if (NULL == (pDll->hDll = hDll)) { pDll->dwRefCnt = 0; goto LoadLibraryError; } hDll = NULL; pDll->fLoaded = TRUE; assert(NULL == pDll->pfnDllCanUnloadNow); pDll->pfnDllCanUnloadNow = pfnDllCanUnloadNow; } } fResult = TRUE; CommonReturn: UnlockOIDFunc(); if (hDll) { // Dll was loaded by another thread. DWORD dwErr = GetLastError(); FreeLibrary(hDll); SetLastError(dwErr); } return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(LoadLibraryError); } // Upon entry/exit OIDFunc must NOT be locked!! STATIC BOOL GetDllProcAddr( IN PDLL_PROC_ELEMENT pEle, OUT void **ppvFuncAddr, OUT HCRYPTOIDFUNCADDR *phFuncAddr ) { BOOL fResult; void *pvAddr; PDLL_ELEMENT pDll; LockOIDFunc(); pDll = pEle->pDll; assert(pDll); if (pvAddr = pEle->pvAddr) AddRefDll(pDll); else { UnlockOIDFunc(); // NO LoadLibrary() or GetProcAddress() while holding OID lock!! fResult = LoadDll(pDll); if (fResult) { assert(pDll->hDll); pvAddr = GetProcAddress(pDll->hDll, pEle->pszName); } LockOIDFunc(); if (!fResult) goto LoadDllError; if (pvAddr) pEle->pvAddr = pvAddr; else { ReleaseDll(pDll); goto GetProcAddressError; } } fResult = TRUE; CommonReturn: *ppvFuncAddr = pvAddr; *phFuncAddr = (HCRYPTOIDFUNCADDR) pDll; UnlockOIDFunc(); return fResult; ErrorReturn: fResult = FALSE; pDll = NULL; pvAddr = NULL; goto CommonReturn; TRACE_ERROR(LoadDllError) TRACE_ERROR(GetProcAddressError) } // Upon entry/exit OIDFunc must NOT be locked!! STATIC BOOL GetRegOIDFunctionAddress( IN PREG_OID_FUNC_ELEMENT pRegEle, IN DWORD dwEncodingType, IN LPCSTR pszOID, OUT void **ppvFuncAddr, OUT HCRYPTOIDFUNCADDR *phFuncAddr ) { for (; pRegEle; pRegEle = pRegEle->pNext) { if (dwEncodingType != pRegEle->dwEncodingType) continue; if (0xFFFF >= (DWORD_PTR) pszOID) { if (pszOID != pRegEle->pszOID) continue; } else { if (0xFFFF >= (DWORD_PTR) pRegEle->pszOID || 0 != _stricmp(pszOID, pRegEle->pszOID)) continue; } return GetDllProcAddr( pRegEle->pDllProc, ppvFuncAddr, phFuncAddr ); } *ppvFuncAddr = NULL; *phFuncAddr = NULL; return FALSE; } // Upon entry/exit OIDFunc must NOT be locked!! STATIC BOOL GetDefaultRegOIDFunctionAddress( IN PFUNC_SET pFuncSet, IN DWORD dwEncodingType, IN LPCWSTR pwszDll, OUT void **ppvFuncAddr, OUT HCRYPTOIDFUNCADDR *phFuncAddr ) { PDEFAULT_REG_ELEMENT pRegEle = pFuncSet->pDefaultRegHead; PDLL_ELEMENT pDll; for (; pRegEle; pRegEle = pRegEle->pNext) { if (dwEncodingType != pRegEle->dwEncodingType) continue; for (DWORD i = 0; i < pRegEle->cDll; i++) { if (0 == _wcsicmp(pwszDll, pRegEle->rgpwszDll[i])) return GetDllProcAddr( pRegEle->rgpDllProc[i], ppvFuncAddr, phFuncAddr ); } } if (pDll = FindDll(pwszDll)) { if (LoadDll(pDll)) { if (*ppvFuncAddr = GetProcAddress(pDll->hDll, pFuncSet->pszFuncName)) { *phFuncAddr = (HCRYPTOIDFUNCADDR) pDll; return TRUE; } else ReleaseDll(pDll); } } *ppvFuncAddr = NULL; *phFuncAddr = NULL; return FALSE; } //+------------------------------------------------------------------------- // Search the list of installed functions for an OID and EncodingType match. // If not found, search the registry. // // For success, returns TRUE with *ppvFuncAddr updated with the function's // address and *phFuncAddr updated with the function address's handle. // The function's handle is AddRef'ed. CryptFreeOIDFunctionAddress needs to // be called to release it. // // For a registry match, the Dll containing the function is loaded. // // By default, both the registered and installed function lists are searched. // Set CRYPT_GET_INSTALLED_OID_FUNC_FLAG to only search the installed list // of functions. This flag would be set by a registered function to get // the address of a pre-installed function it was replacing. For example, // the registered function might handle a new special case and call the // pre-installed function to handle the remaining cases. //-------------------------------------------------------------------------- BOOL WINAPI CryptGetOIDFunctionAddress( IN HCRYPTOIDFUNCSET hFuncSet, IN DWORD dwEncodingType, IN LPCSTR pszOID, IN DWORD dwFlags, OUT void **ppvFuncAddr, OUT HCRYPTOIDFUNCADDR *phFuncAddr ) { PFUNC_SET pFuncSet = (PFUNC_SET) hFuncSet; DWORD_PTR dwOID; dwEncodingType = GetEncodingType(dwEncodingType); if (0xFFFF < (DWORD_PTR) pszOID && CONST_OID_STR_PREFIX_CHAR == *pszOID) { // Convert "#" string to its corresponding constant OID value pszOID = (LPCSTR)(DWORD_PTR) atol(pszOID + 1); if (0xFFFF < (DWORD_PTR) pszOID) { SetLastError((DWORD) E_INVALIDARG); *ppvFuncAddr = NULL; *phFuncAddr = NULL; return FALSE; } } if (!pFuncSet->fRegLoaded) LoadRegFunc(pFuncSet); if (0 == (dwFlags & CRYPT_GET_INSTALLED_OID_FUNC_FLAG) && pFuncSet->pRegBeforeOIDFuncHead) { if (GetRegOIDFunctionAddress( pFuncSet->pRegBeforeOIDFuncHead, dwEncodingType, pszOID, ppvFuncAddr, phFuncAddr )) return TRUE; } if (0xFFFF >= (dwOID = (DWORD_PTR) pszOID)) { PCONST_OID_FUNC_ELEMENT pConstEle = pFuncSet->pConstOIDFuncHead; while (pConstEle) { if (dwEncodingType == pConstEle->dwEncodingType && dwOID >= pConstEle->dwLowOID && dwOID <= pConstEle->dwHighOID) { *ppvFuncAddr = pConstEle->rgpvFuncAddr[ dwOID - pConstEle->dwLowOID]; *phFuncAddr = (HCRYPTOIDFUNCADDR) pConstEle; return TRUE; } pConstEle = pConstEle->pNext; } } else { PSTR_OID_FUNC_ELEMENT pStrEle = pFuncSet->pStrOIDFuncHead; while (pStrEle) { if (dwEncodingType == pStrEle->dwEncodingType && 0 == _stricmp(pszOID, pStrEle->pszOID)) { *ppvFuncAddr = pStrEle->pvFuncAddr; *phFuncAddr = (HCRYPTOIDFUNCADDR) pStrEle; return TRUE; } pStrEle = pStrEle->pNext; } } if (0 == (dwFlags & CRYPT_GET_INSTALLED_OID_FUNC_FLAG) && pFuncSet->pRegAfterOIDFuncHead) { if (GetRegOIDFunctionAddress( pFuncSet->pRegAfterOIDFuncHead, dwEncodingType, pszOID, ppvFuncAddr, phFuncAddr )) return TRUE; } SetLastError((DWORD) ERROR_FILE_NOT_FOUND); *ppvFuncAddr = NULL; *phFuncAddr = NULL; return FALSE; } //+------------------------------------------------------------------------- // Get the list of registered default Dll entries for the specified // function set and encoding type. // // The returned list consists of none, one or more null terminated Dll file // names. The list is terminated with an empty (L"\0") Dll file name. // For example: L"first.dll" L"\0" L"second.dll" L"\0" L"\0" //-------------------------------------------------------------------------- BOOL WINAPI CryptGetDefaultOIDDllList( IN HCRYPTOIDFUNCSET hFuncSet, IN DWORD dwEncodingType, OUT LPWSTR pwszDllList, IN OUT DWORD *pcchDllList ) { BOOL fResult; PFUNC_SET pFuncSet = (PFUNC_SET) hFuncSet; PDEFAULT_REG_ELEMENT pRegEle; DWORD cchRegDllList = 2; LPWSTR pwszRegDllList = L"\0\0"; if (!pFuncSet->fRegLoaded) LoadRegFunc(pFuncSet); dwEncodingType = GetEncodingType(dwEncodingType); pRegEle = pFuncSet->pDefaultRegHead; for (; pRegEle; pRegEle = pRegEle->pNext) { if (dwEncodingType == pRegEle->dwEncodingType) { cchRegDllList = pRegEle->cchDllList; assert(cchRegDllList >= 2); pwszRegDllList = pRegEle->pwszDllList; break; } } fResult = TRUE; if (pwszDllList) { if (cchRegDllList > *pcchDllList) { SetLastError((DWORD) ERROR_MORE_DATA); fResult = FALSE; } else memcpy(pwszDllList, pwszRegDllList, cchRegDllList * sizeof(WCHAR)); } *pcchDllList = cchRegDllList; return fResult; } //+------------------------------------------------------------------------- // Either: get the first or next installed DEFAULT function OR // load the Dll containing the DEFAULT function. // // If pwszDll is NULL, search the list of installed DEFAULT functions. // *phFuncAddr must be set to NULL to get the first installed function. // Successive installed functions are returned by setting *phFuncAddr // to the hFuncAddr returned by the previous call. // // If pwszDll is NULL, the input *phFuncAddr // is always CryptFreeOIDFunctionAddress'ed by this function, even for // an error. // // If pwszDll isn't NULL, then, attempts to load the Dll and the DEFAULT // function. *phFuncAddr is ignored upon entry and isn't // CryptFreeOIDFunctionAddress'ed. // // For success, returns TRUE with *ppvFuncAddr updated with the function's // address and *phFuncAddr updated with the function address's handle. // The function's handle is AddRef'ed. CryptFreeOIDFunctionAddress needs to // be called to release it or CryptGetDefaultOIDFunctionAddress can also // be called for a NULL pwszDll. //-------------------------------------------------------------------------- BOOL WINAPI CryptGetDefaultOIDFunctionAddress( IN HCRYPTOIDFUNCSET hFuncSet, IN DWORD dwEncodingType, IN OPTIONAL LPCWSTR pwszDll, IN DWORD dwFlags, OUT void **ppvFuncAddr, IN OUT HCRYPTOIDFUNCADDR *phFuncAddr ) { PFUNC_SET pFuncSet = (PFUNC_SET) hFuncSet; if (!pFuncSet->fRegLoaded) LoadRegFunc(pFuncSet); dwEncodingType = GetEncodingType(dwEncodingType); if (NULL == pwszDll) { // Get from installed list PSTR_OID_FUNC_ELEMENT pStrEle = (PSTR_OID_FUNC_ELEMENT) *phFuncAddr; if (pStrEle && STR_OID_TYPE == pStrEle->dwOIDType) pStrEle = pStrEle->pNext; else pStrEle = pFuncSet->pStrOIDFuncHead; while (pStrEle) { if (dwEncodingType == pStrEle->dwEncodingType && 0 == _stricmp(CRYPT_DEFAULT_OID, pStrEle->pszOID)) { *ppvFuncAddr = pStrEle->pvFuncAddr; *phFuncAddr = (HCRYPTOIDFUNCADDR) pStrEle; return TRUE; } pStrEle = pStrEle->pNext; } SetLastError(ERROR_FILE_NOT_FOUND); *ppvFuncAddr = NULL; *phFuncAddr = NULL; return FALSE; } else return GetDefaultRegOIDFunctionAddress( pFuncSet, dwEncodingType, pwszDll, ppvFuncAddr, phFuncAddr); } //+------------------------------------------------------------------------- // Releases the handle AddRef'ed and returned by CryptGetOIDFunctionAddress // or CryptGetDefaultOIDFunctionAddress. // // If a Dll was loaded for the function its unloaded. However, before doing // the unload, the DllCanUnloadNow function exported by the loaded Dll is // called. It should return S_FALSE to inhibit the unload or S_TRUE to enable // the unload. If the Dll doesn't export DllCanUnloadNow, the Dll is unloaded. // // DllCanUnloadNow has the following signature: // STDAPI DllCanUnloadNow(void); //-------------------------------------------------------------------------- BOOL WINAPI CryptFreeOIDFunctionAddress( IN HCRYPTOIDFUNCADDR hFuncAddr, IN DWORD dwFlags ) { PDLL_ELEMENT pDll = (PDLL_ELEMENT) hFuncAddr; if (pDll && DLL_OID_TYPE == pDll->dwOIDType) { DWORD dwErr = GetLastError(); ReleaseDll(pDll); SetLastError(dwErr); } return TRUE; }