|  | /* | 
|  | * Copyright 2004-2007 Juan Lang | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  | #include <assert.h> | 
|  | #include <stdarg.h> | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "wincrypt.h" | 
|  | #include "winreg.h" | 
|  | #include "winuser.h" | 
|  | #include "wine/debug.h" | 
|  | #include "wine/list.h" | 
|  | #include "crypt32_private.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(crypt); | 
|  |  | 
|  | typedef struct _WINE_HASH_TO_DELETE | 
|  | { | 
|  | BYTE        hash[20]; | 
|  | struct list entry; | 
|  | } WINE_HASH_TO_DELETE, *PWINE_HASH_TO_DELETE; | 
|  |  | 
|  | typedef struct _WINE_REGSTOREINFO | 
|  | { | 
|  | DWORD            dwOpenFlags; | 
|  | HCERTSTORE       memStore; | 
|  | HKEY             key; | 
|  | BOOL             dirty; | 
|  | CRITICAL_SECTION cs; | 
|  | struct list      certsToDelete; | 
|  | struct list      crlsToDelete; | 
|  | struct list      ctlsToDelete; | 
|  | } WINE_REGSTOREINFO, *PWINE_REGSTOREINFO; | 
|  |  | 
|  | static void CRYPT_HashToStr(const BYTE *hash, LPWSTR asciiHash) | 
|  | { | 
|  | static const WCHAR fmt[] = { '%','0','2','X',0 }; | 
|  | DWORD i; | 
|  |  | 
|  | assert(hash); | 
|  | assert(asciiHash); | 
|  |  | 
|  | for (i = 0; i < 20; i++) | 
|  | wsprintfW(asciiHash + i * 2, fmt, hash[i]); | 
|  | } | 
|  |  | 
|  | static const WCHAR CertsW[] = { 'C','e','r','t','i','f','i','c','a','t','e','s', | 
|  | 0 }; | 
|  | static const WCHAR CRLsW[] = { 'C','R','L','s',0 }; | 
|  | static const WCHAR CTLsW[] = { 'C','T','L','s',0 }; | 
|  | static const WCHAR BlobW[] = { 'B','l','o','b',0 }; | 
|  |  | 
|  | static void CRYPT_RegReadSerializedFromReg(HKEY key, DWORD contextType, | 
|  | HCERTSTORE store) | 
|  | { | 
|  | LONG rc; | 
|  | DWORD index = 0; | 
|  | WCHAR subKeyName[MAX_PATH]; | 
|  |  | 
|  | do { | 
|  | DWORD size = sizeof(subKeyName) / sizeof(WCHAR); | 
|  |  | 
|  | rc = RegEnumKeyExW(key, index++, subKeyName, &size, NULL, NULL, NULL, | 
|  | NULL); | 
|  | if (!rc) | 
|  | { | 
|  | HKEY subKey; | 
|  |  | 
|  | rc = RegOpenKeyExW(key, subKeyName, 0, KEY_READ, &subKey); | 
|  | if (!rc) | 
|  | { | 
|  | LPBYTE buf = NULL; | 
|  |  | 
|  | size = 0; | 
|  | rc = RegQueryValueExW(subKey, BlobW, NULL, NULL, NULL, &size); | 
|  | if (!rc) | 
|  | buf = CryptMemAlloc(size); | 
|  | if (buf) | 
|  | { | 
|  | rc = RegQueryValueExW(subKey, BlobW, NULL, NULL, buf, | 
|  | &size); | 
|  | if (!rc) | 
|  | { | 
|  | const void *context; | 
|  | DWORD addedType; | 
|  |  | 
|  | TRACE("Adding cert with hash %s\n", | 
|  | debugstr_w(subKeyName)); | 
|  | context = CRYPT_ReadSerializedElement(buf, size, | 
|  | contextType, &addedType); | 
|  | if (context) | 
|  | { | 
|  | const WINE_CONTEXT_INTERFACE *contextInterface; | 
|  | BYTE hash[20]; | 
|  |  | 
|  | switch (addedType) | 
|  | { | 
|  | case CERT_STORE_CERTIFICATE_CONTEXT: | 
|  | contextInterface = pCertInterface; | 
|  | break; | 
|  | case CERT_STORE_CRL_CONTEXT: | 
|  | contextInterface = pCRLInterface; | 
|  | break; | 
|  | case CERT_STORE_CTL_CONTEXT: | 
|  | contextInterface = pCTLInterface; | 
|  | break; | 
|  | default: | 
|  | contextInterface = NULL; | 
|  | } | 
|  | if (contextInterface) | 
|  | { | 
|  | size = sizeof(hash); | 
|  | if (contextInterface->getProp(context, | 
|  | CERT_HASH_PROP_ID, hash, &size)) | 
|  | { | 
|  | WCHAR asciiHash[20 * 2 + 1]; | 
|  |  | 
|  | CRYPT_HashToStr(hash, asciiHash); | 
|  | TRACE("comparing %s\n", | 
|  | debugstr_w(asciiHash)); | 
|  | TRACE("with %s\n", debugstr_w(subKeyName)); | 
|  | if (!lstrcmpW(asciiHash, subKeyName)) | 
|  | { | 
|  | TRACE("hash matches, adding\n"); | 
|  | contextInterface->addContextToStore( | 
|  | store, context, | 
|  | CERT_STORE_ADD_REPLACE_EXISTING, NULL); | 
|  | } | 
|  | else | 
|  | TRACE("hash doesn't match, ignoring\n"); | 
|  | } | 
|  | contextInterface->free(context); | 
|  | } | 
|  | } | 
|  | } | 
|  | CryptMemFree(buf); | 
|  | } | 
|  | RegCloseKey(subKey); | 
|  | } | 
|  | /* Ignore intermediate errors, continue enumerating */ | 
|  | rc = ERROR_SUCCESS; | 
|  | } | 
|  | } while (!rc); | 
|  | } | 
|  |  | 
|  | static void CRYPT_RegReadFromReg(HKEY key, HCERTSTORE store) | 
|  | { | 
|  | static const WCHAR * const subKeys[] = { CertsW, CRLsW, CTLsW }; | 
|  | static const DWORD contextFlags[] = { CERT_STORE_CERTIFICATE_CONTEXT_FLAG, | 
|  | CERT_STORE_CRL_CONTEXT_FLAG, CERT_STORE_CTL_CONTEXT_FLAG }; | 
|  | DWORD i; | 
|  |  | 
|  | for (i = 0; i < sizeof(subKeys) / sizeof(subKeys[0]); i++) | 
|  | { | 
|  | HKEY hKey; | 
|  | LONG rc; | 
|  |  | 
|  | rc = RegCreateKeyExW(key, subKeys[i], 0, NULL, 0, KEY_READ, NULL, | 
|  | &hKey, NULL); | 
|  | if (!rc) | 
|  | { | 
|  | CRYPT_RegReadSerializedFromReg(hKey, contextFlags[i], store); | 
|  | RegCloseKey(hKey); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Hash is assumed to be 20 bytes in length (a SHA-1 hash) */ | 
|  | static BOOL CRYPT_WriteSerializedToReg(HKEY key, const BYTE *hash, const BYTE *buf, | 
|  | DWORD len) | 
|  | { | 
|  | WCHAR asciiHash[20 * 2 + 1]; | 
|  | LONG rc; | 
|  | HKEY subKey; | 
|  | BOOL ret; | 
|  |  | 
|  | CRYPT_HashToStr(hash, asciiHash); | 
|  | rc = RegCreateKeyExW(key, asciiHash, 0, NULL, 0, KEY_ALL_ACCESS, NULL, | 
|  | &subKey, NULL); | 
|  | if (!rc) | 
|  | { | 
|  | rc = RegSetValueExW(subKey, BlobW, 0, REG_BINARY, buf, len); | 
|  | RegCloseKey(subKey); | 
|  | } | 
|  | if (!rc) | 
|  | ret = TRUE; | 
|  | else | 
|  | { | 
|  | SetLastError(rc); | 
|  | ret = FALSE; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_SerializeContextsToReg(HKEY key, | 
|  | const WINE_CONTEXT_INTERFACE *contextInterface, HCERTSTORE memStore) | 
|  | { | 
|  | const void *context = NULL; | 
|  | BOOL ret; | 
|  |  | 
|  | do { | 
|  | context = contextInterface->enumContextsInStore(memStore, context); | 
|  | if (context) | 
|  | { | 
|  | BYTE hash[20]; | 
|  | DWORD hashSize = sizeof(hash); | 
|  |  | 
|  | ret = contextInterface->getProp(context, CERT_HASH_PROP_ID, hash, | 
|  | &hashSize); | 
|  | if (ret) | 
|  | { | 
|  | DWORD size = 0; | 
|  | LPBYTE buf = NULL; | 
|  |  | 
|  | ret = contextInterface->serialize(context, 0, NULL, &size); | 
|  | if (size) | 
|  | buf = CryptMemAlloc(size); | 
|  | if (buf) | 
|  | { | 
|  | ret = contextInterface->serialize(context, 0, buf, &size); | 
|  | if (ret) | 
|  | ret = CRYPT_WriteSerializedToReg(key, hash, buf, size); | 
|  | } | 
|  | CryptMemFree(buf); | 
|  | } | 
|  | } | 
|  | else | 
|  | ret = TRUE; | 
|  | } while (ret && context != NULL); | 
|  | if (context) | 
|  | contextInterface->free(context); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_RegWriteToReg(PWINE_REGSTOREINFO store) | 
|  | { | 
|  | static const WCHAR * const subKeys[] = { CertsW, CRLsW, CTLsW }; | 
|  | const WINE_CONTEXT_INTERFACE * const interfaces[] = { pCertInterface, | 
|  | pCRLInterface, pCTLInterface }; | 
|  | struct list *listToDelete[] = { &store->certsToDelete, &store->crlsToDelete, | 
|  | &store->ctlsToDelete }; | 
|  | BOOL ret = TRUE; | 
|  | DWORD i; | 
|  |  | 
|  | for (i = 0; ret && i < sizeof(subKeys) / sizeof(subKeys[0]); i++) | 
|  | { | 
|  | HKEY key; | 
|  | LONG rc = RegCreateKeyExW(store->key, subKeys[i], 0, NULL, 0, | 
|  | KEY_ALL_ACCESS, NULL, &key, NULL); | 
|  |  | 
|  | if (!rc) | 
|  | { | 
|  | if (listToDelete[i]) | 
|  | { | 
|  | PWINE_HASH_TO_DELETE toDelete, next; | 
|  | WCHAR asciiHash[20 * 2 + 1]; | 
|  |  | 
|  | EnterCriticalSection(&store->cs); | 
|  | LIST_FOR_EACH_ENTRY_SAFE(toDelete, next, listToDelete[i], | 
|  | WINE_HASH_TO_DELETE, entry) | 
|  | { | 
|  | LONG rc; | 
|  |  | 
|  | CRYPT_HashToStr(toDelete->hash, asciiHash); | 
|  | TRACE("Removing %s\n", debugstr_w(asciiHash)); | 
|  | rc = RegDeleteKeyW(key, asciiHash); | 
|  | if (rc != ERROR_SUCCESS && rc != ERROR_FILE_NOT_FOUND) | 
|  | { | 
|  | SetLastError(rc); | 
|  | ret = FALSE; | 
|  | } | 
|  | list_remove(&toDelete->entry); | 
|  | CryptMemFree(toDelete); | 
|  | } | 
|  | LeaveCriticalSection(&store->cs); | 
|  | } | 
|  | ret = CRYPT_SerializeContextsToReg(key, interfaces[i], | 
|  | store->memStore); | 
|  | RegCloseKey(key); | 
|  | } | 
|  | else | 
|  | { | 
|  | SetLastError(rc); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* If force is true or the registry store is dirty, writes the contents of the | 
|  | * store to the registry. | 
|  | */ | 
|  | static BOOL CRYPT_RegFlushStore(PWINE_REGSTOREINFO store, BOOL force) | 
|  | { | 
|  | BOOL ret; | 
|  |  | 
|  | TRACE("(%p, %d)\n", store, force); | 
|  |  | 
|  | if (store->dirty || force) | 
|  | ret = CRYPT_RegWriteToReg(store); | 
|  | else | 
|  | ret = TRUE; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void WINAPI CRYPT_RegCloseStore(HCERTSTORE hCertStore, DWORD dwFlags) | 
|  | { | 
|  | PWINE_REGSTOREINFO store = hCertStore; | 
|  |  | 
|  | TRACE("(%p, %08x)\n", store, dwFlags); | 
|  | if (dwFlags) | 
|  | FIXME("Unimplemented flags: %08x\n", dwFlags); | 
|  |  | 
|  | CRYPT_RegFlushStore(store, FALSE); | 
|  | RegCloseKey(store->key); | 
|  | store->cs.DebugInfo->Spare[0] = 0; | 
|  | DeleteCriticalSection(&store->cs); | 
|  | CryptMemFree(store); | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_RegWriteContext(PWINE_REGSTOREINFO store, | 
|  | const void *context, DWORD dwFlags) | 
|  | { | 
|  | BOOL ret; | 
|  |  | 
|  | if (dwFlags & CERT_STORE_PROV_WRITE_ADD_FLAG) | 
|  | { | 
|  | store->dirty = TRUE; | 
|  | ret = TRUE; | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_RegDeleteContext(PWINE_REGSTOREINFO store, | 
|  | struct list *deleteList, const void *context, | 
|  | PCWINE_CONTEXT_INTERFACE contextInterface) | 
|  | { | 
|  | BOOL ret; | 
|  |  | 
|  | if (store->dwOpenFlags & CERT_STORE_READONLY_FLAG) | 
|  | { | 
|  | SetLastError(ERROR_ACCESS_DENIED); | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | PWINE_HASH_TO_DELETE toDelete = | 
|  | CryptMemAlloc(sizeof(WINE_HASH_TO_DELETE)); | 
|  |  | 
|  | if (toDelete) | 
|  | { | 
|  | DWORD size = sizeof(toDelete->hash); | 
|  |  | 
|  | ret = contextInterface->getProp(context, CERT_HASH_PROP_ID, | 
|  | toDelete->hash, &size); | 
|  | if (ret) | 
|  | { | 
|  | EnterCriticalSection(&store->cs); | 
|  | list_add_tail(deleteList, &toDelete->entry); | 
|  | LeaveCriticalSection(&store->cs); | 
|  | } | 
|  | else | 
|  | { | 
|  | CryptMemFree(toDelete); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | if (ret) | 
|  | store->dirty = TRUE; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_RegWriteCert(HCERTSTORE hCertStore, | 
|  | PCCERT_CONTEXT cert, DWORD dwFlags) | 
|  | { | 
|  | PWINE_REGSTOREINFO store = hCertStore; | 
|  |  | 
|  | TRACE("(%p, %p, %d)\n", hCertStore, cert, dwFlags); | 
|  |  | 
|  | return CRYPT_RegWriteContext(store, cert, dwFlags); | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_RegDeleteCert(HCERTSTORE hCertStore, | 
|  | PCCERT_CONTEXT pCertContext, DWORD dwFlags) | 
|  | { | 
|  | PWINE_REGSTOREINFO store = hCertStore; | 
|  |  | 
|  | TRACE("(%p, %p, %08x)\n", store, pCertContext, dwFlags); | 
|  |  | 
|  | return CRYPT_RegDeleteContext(store, &store->certsToDelete, pCertContext, | 
|  | pCertInterface); | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_RegWriteCRL(HCERTSTORE hCertStore, | 
|  | PCCRL_CONTEXT crl, DWORD dwFlags) | 
|  | { | 
|  | PWINE_REGSTOREINFO store = hCertStore; | 
|  |  | 
|  | TRACE("(%p, %p, %d)\n", hCertStore, crl, dwFlags); | 
|  |  | 
|  | return CRYPT_RegWriteContext(store, crl, dwFlags); | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_RegDeleteCRL(HCERTSTORE hCertStore, | 
|  | PCCRL_CONTEXT pCrlContext, DWORD dwFlags) | 
|  | { | 
|  | PWINE_REGSTOREINFO store = hCertStore; | 
|  |  | 
|  | TRACE("(%p, %p, %08x)\n", store, pCrlContext, dwFlags); | 
|  |  | 
|  | return CRYPT_RegDeleteContext(store, &store->crlsToDelete, pCrlContext, | 
|  | pCRLInterface); | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_RegWriteCTL(HCERTSTORE hCertStore, | 
|  | PCCTL_CONTEXT ctl, DWORD dwFlags) | 
|  | { | 
|  | PWINE_REGSTOREINFO store = hCertStore; | 
|  |  | 
|  | TRACE("(%p, %p, %d)\n", hCertStore, ctl, dwFlags); | 
|  |  | 
|  | return CRYPT_RegWriteContext(store, ctl, dwFlags); | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_RegDeleteCTL(HCERTSTORE hCertStore, | 
|  | PCCTL_CONTEXT pCtlContext, DWORD dwFlags) | 
|  | { | 
|  | PWINE_REGSTOREINFO store = hCertStore; | 
|  |  | 
|  | TRACE("(%p, %p, %08x)\n", store, pCtlContext, dwFlags); | 
|  |  | 
|  | return CRYPT_RegDeleteContext(store, &store->ctlsToDelete, pCtlContext, | 
|  | pCTLInterface); | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_RegControl(HCERTSTORE hCertStore, DWORD dwFlags, | 
|  | DWORD dwCtrlType, void const *pvCtrlPara) | 
|  | { | 
|  | PWINE_REGSTOREINFO store = hCertStore; | 
|  | BOOL ret; | 
|  |  | 
|  | TRACE("(%p, %08x, %d, %p)\n", hCertStore, dwFlags, dwCtrlType, | 
|  | pvCtrlPara); | 
|  |  | 
|  | switch (dwCtrlType) | 
|  | { | 
|  | case CERT_STORE_CTRL_RESYNC: | 
|  | { | 
|  | HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, | 
|  | CERT_STORE_CREATE_NEW_FLAG, NULL); | 
|  |  | 
|  | CRYPT_RegFlushStore(store, FALSE); | 
|  | CRYPT_RegReadFromReg(store->key, memStore); | 
|  | I_CertUpdateStore(store->memStore, memStore, 0, 0); | 
|  | CertCloseStore(memStore, 0); | 
|  | ret = TRUE; | 
|  | break; | 
|  | } | 
|  | case CERT_STORE_CTRL_COMMIT: | 
|  | ret = CRYPT_RegFlushStore(store, | 
|  | dwFlags & CERT_STORE_CTRL_COMMIT_FORCE_FLAG); | 
|  | break; | 
|  | case CERT_STORE_CTRL_AUTO_RESYNC: | 
|  | FIXME("CERT_STORE_CTRL_AUTO_RESYNC: stub\n"); | 
|  | ret = TRUE; | 
|  | break; | 
|  | default: | 
|  | FIXME("%d: stub\n", dwCtrlType); | 
|  | ret = FALSE; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void *regProvFuncs[] = { | 
|  | CRYPT_RegCloseStore, | 
|  | NULL, /* CERT_STORE_PROV_READ_CERT_FUNC */ | 
|  | CRYPT_RegWriteCert, | 
|  | CRYPT_RegDeleteCert, | 
|  | NULL, /* CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC */ | 
|  | NULL, /* CERT_STORE_PROV_READ_CRL_FUNC */ | 
|  | CRYPT_RegWriteCRL, | 
|  | CRYPT_RegDeleteCRL, | 
|  | NULL, /* CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC */ | 
|  | NULL, /* CERT_STORE_PROV_READ_CTL_FUNC */ | 
|  | CRYPT_RegWriteCTL, | 
|  | CRYPT_RegDeleteCTL, | 
|  | NULL, /* CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC */ | 
|  | CRYPT_RegControl, | 
|  | }; | 
|  |  | 
|  | PWINECRYPT_CERTSTORE CRYPT_RegOpenStore(HCRYPTPROV hCryptProv, DWORD dwFlags, | 
|  | const void *pvPara) | 
|  | { | 
|  | PWINECRYPT_CERTSTORE store = NULL; | 
|  |  | 
|  | TRACE("(%ld, %08x, %p)\n", hCryptProv, dwFlags, pvPara); | 
|  |  | 
|  | if (dwFlags & CERT_STORE_DELETE_FLAG) | 
|  | { | 
|  | DWORD rc = RegDeleteTreeW((HKEY)pvPara, CertsW); | 
|  |  | 
|  | if (rc == ERROR_SUCCESS || rc == ERROR_NO_MORE_ITEMS) | 
|  | rc = RegDeleteTreeW((HKEY)pvPara, CRLsW); | 
|  | if (rc == ERROR_SUCCESS || rc == ERROR_NO_MORE_ITEMS) | 
|  | rc = RegDeleteTreeW((HKEY)pvPara, CTLsW); | 
|  | if (rc == ERROR_NO_MORE_ITEMS) | 
|  | rc = ERROR_SUCCESS; | 
|  | SetLastError(rc); | 
|  | } | 
|  | else | 
|  | { | 
|  | HKEY key; | 
|  |  | 
|  | if (DuplicateHandle(GetCurrentProcess(), (HANDLE)pvPara, | 
|  | GetCurrentProcess(), (LPHANDLE)&key, | 
|  | dwFlags & CERT_STORE_READONLY_FLAG ? KEY_READ : KEY_ALL_ACCESS, | 
|  | TRUE, 0)) | 
|  | { | 
|  | PWINECRYPT_CERTSTORE memStore; | 
|  |  | 
|  | memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, hCryptProv, | 
|  | CERT_STORE_CREATE_NEW_FLAG, NULL); | 
|  | if (memStore) | 
|  | { | 
|  | PWINE_REGSTOREINFO regInfo = CryptMemAlloc( | 
|  | sizeof(WINE_REGSTOREINFO)); | 
|  |  | 
|  | if (regInfo) | 
|  | { | 
|  | CERT_STORE_PROV_INFO provInfo = { 0 }; | 
|  |  | 
|  | regInfo->dwOpenFlags = dwFlags; | 
|  | regInfo->memStore = memStore; | 
|  | regInfo->key = key; | 
|  | InitializeCriticalSection(®Info->cs); | 
|  | regInfo->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PWINE_REGSTOREINFO->cs"); | 
|  | list_init(®Info->certsToDelete); | 
|  | list_init(®Info->crlsToDelete); | 
|  | list_init(®Info->ctlsToDelete); | 
|  | CRYPT_RegReadFromReg(regInfo->key, regInfo->memStore); | 
|  | regInfo->dirty = FALSE; | 
|  | provInfo.cbSize = sizeof(provInfo); | 
|  | provInfo.cStoreProvFunc = sizeof(regProvFuncs) / | 
|  | sizeof(regProvFuncs[0]); | 
|  | provInfo.rgpvStoreProvFunc = regProvFuncs; | 
|  | provInfo.hStoreProv = regInfo; | 
|  | store = CRYPT_ProvCreateStore(dwFlags, memStore, &provInfo); | 
|  | /* Reg store doesn't need crypto provider, so close it */ | 
|  | if (hCryptProv && | 
|  | !(dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG)) | 
|  | CryptReleaseContext(hCryptProv, 0); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | TRACE("returning %p\n", store); | 
|  | return store; | 
|  | } |