Implement registry stores.

This commit is contained in:
Juan Lang 2005-09-07 13:25:56 +00:00 committed by Alexandre Julliard
parent 60cae6e289
commit 5ef9d88cab
4 changed files with 935 additions and 5 deletions

View File

@ -5,7 +5,7 @@ SRCDIR = @srcdir@
VPATH = @srcdir@
MODULE = crypt32.dll
IMPORTLIB = libcrypt32.$(IMPLIBEXT)
IMPORTS = advapi32 kernel32 ntdll
IMPORTS = user32 advapi32 kernel32 ntdll
C_SRCS = \
cert.c \

View File

@ -27,10 +27,12 @@
* registering and enumerating physical stores and locations.)
* - Many flags, options and whatnot are unimplemented.
*/
#include <assert.h>
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "winuser.h"
#include "wincrypt.h"
#include "wine/debug.h"
#include "wine/list.h"
@ -234,6 +236,29 @@ typedef struct _WINE_MEMSTORE
struct list certs;
} WINE_MEMSTORE, *PWINE_MEMSTORE;
typedef struct _WINE_HASH_TO_DELETE
{
BYTE hash[20];
struct list entry;
} WINE_HASH_TO_DELETE, *PWINE_HASH_TO_DELETE;
/* Returned by a reg store during enumeration. */
typedef struct _WINE_REG_CERT_CONTEXT
{
WINE_CERT_CONTEXT_REF cert;
PWINE_CERT_CONTEXT_REF childContext;
} WINE_REG_CERT_CONTEXT, *PWINE_REG_CERT_CONTEXT;
typedef struct _WINE_REGSTORE
{
WINECRYPT_CERTSTORE hdr;
PWINECRYPT_CERTSTORE memStore;
HKEY key;
BOOL dirty;
CRITICAL_SECTION cs;
struct list certsToDelete;
} WINE_REGSTORE, *PWINE_REGSTORE;
typedef struct _WINE_STORE_LIST_ENTRY
{
PWINECRYPT_CERTSTORE store;
@ -806,6 +831,570 @@ static WINECRYPT_CERTSTORE *CRYPT_CollectionOpenStore(HCRYPTPROV hCryptProv,
return (PWINECRYPT_CERTSTORE)store;
}
static void CRYPT_HashToStr(LPBYTE 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(PWINE_REGSTORE store, HKEY key,
DWORD contextType)
{
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 = HeapAlloc(GetProcessHeap(), 0, 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 = &gCertInterface;
break;
case CERT_STORE_CRL_CONTEXT:
contextInterface = &gCRLInterface;
break;
case CERT_STORE_CTL_CONTEXT:
contextInterface = &gCTLInterface;
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);
}
}
}
}
}
HeapFree(GetProcessHeap(), 0, buf);
}
RegCloseKey(subKey);
}
/* Ignore intermediate errors, continue enumerating */
rc = ERROR_SUCCESS;
}
} while (!rc);
}
static void CRYPT_RegReadFromReg(PWINE_REGSTORE store)
{
static const WCHAR *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 key;
LONG rc;
rc = RegCreateKeyExW(store->key, subKeys[i], 0, NULL, 0, KEY_READ, NULL,
&key, NULL);
if (!rc)
{
CRYPT_RegReadSerializedFromReg(store, key, contextFlags[i]);
RegCloseKey(key);
}
}
}
/* Hash is assumed to be 20 bytes in length (a SHA-1 hash) */
static BOOL CRYPT_WriteSerializedToReg(HKEY key, LPBYTE hash, LPBYTE 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 = HeapAlloc(GetProcessHeap(), 0, size);
if (buf)
{
ret = contextInterface->serialize(context, 0, buf, &size);
if (ret)
ret = CRYPT_WriteSerializedToReg(key, hash, buf, size);
}
HeapFree(GetProcessHeap(), 0, buf);
}
}
else
ret = TRUE;
} while (ret && context != NULL);
if (context)
contextInterface->free(context);
return ret;
}
static BOOL CRYPT_RegWriteToReg(PWINE_REGSTORE store)
{
static const WCHAR *subKeys[] = { CertsW, CRLsW, CTLsW };
static const WINE_CONTEXT_INTERFACE *interfaces[] = { &gCertInterface,
&gCRLInterface, &gCTLInterface };
struct list *listToDelete[] = { &store->certsToDelete, NULL, NULL };
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);
HeapFree(GetProcessHeap(), 0, 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_REGSTORE store, BOOL force)
{
BOOL ret;
if (store->dirty || force)
ret = CRYPT_RegWriteToReg(store);
else
ret = TRUE;
return ret;
}
static void WINAPI CRYPT_RegCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
{
PWINE_REGSTORE store = (PWINE_REGSTORE)hCertStore;
TRACE("(%p, %08lx)\n", store, dwFlags);
if (dwFlags)
FIXME("Unimplemented flags: %08lx\n", dwFlags);
CRYPT_RegFlushStore(store, FALSE);
/* certsToDelete should already be cleared by this point */
store->memStore->closeStore(store->memStore, 0);
RegCloseKey(store->key);
DeleteCriticalSection(&store->cs);
HeapFree(GetProcessHeap(), 0, store);
}
static BOOL WINAPI CRYPT_RegAddCert(HCERTSTORE hCertStore, PCCERT_CONTEXT cert,
DWORD dwAddDisposition)
{
PWINE_REGSTORE store = (PWINE_REGSTORE)hCertStore;
BOOL ret;
TRACE("(%p, %p, %ld)\n", hCertStore, cert, dwAddDisposition);
if (store->hdr.dwOpenFlags & CERT_STORE_READONLY_FLAG)
{
SetLastError(ERROR_ACCESS_DENIED);
ret = FALSE;
}
else
{
ret = store->memStore->addCert(store->memStore, cert, dwAddDisposition);
if (ret)
store->dirty = TRUE;
}
return ret;
}
static PWINE_CERT_CONTEXT_REF CRYPT_RegCreateCertRef(
PWINE_CERT_CONTEXT context, HCERTSTORE store)
{
PWINE_REG_CERT_CONTEXT ret = HeapAlloc(GetProcessHeap(), 0,
sizeof(WINE_REG_CERT_CONTEXT));
if (ret)
{
CRYPT_InitCertRef((PWINE_CERT_CONTEXT_REF)ret, context, store);
ret->childContext = NULL;
}
return (PWINE_CERT_CONTEXT_REF)ret;
}
static PWINE_CERT_CONTEXT_REF CRYPT_RegEnumCert(PWINECRYPT_CERTSTORE store,
PWINE_CERT_CONTEXT_REF pPrev)
{
PWINE_REGSTORE rs = (PWINE_REGSTORE)store;
PWINE_CERT_CONTEXT_REF child;
PWINE_REG_CERT_CONTEXT prev = (PWINE_REG_CERT_CONTEXT)pPrev, ret = NULL;
TRACE("(%p, %p)\n", store, pPrev);
if (pPrev)
{
child = rs->memStore->enumCert(rs->memStore, prev->childContext);
if (child)
{
ret = (PWINE_REG_CERT_CONTEXT)pPrev;
memcpy(&ret->cert, child, sizeof(WINE_CERT_CONTEXT_REF));
ret->cert.cert.hCertStore = (HCERTSTORE)store;
InterlockedIncrement(&ret->cert.context->ref);
ret->childContext = child;
}
}
else
{
child = rs->memStore->enumCert(rs->memStore, NULL);
if (child)
{
ret = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_REG_CERT_CONTEXT));
if (ret)
{
memcpy(&ret->cert, child, sizeof(WINE_CERT_CONTEXT_REF));
ret->cert.cert.hCertStore = (HCERTSTORE)store;
InterlockedIncrement(&ret->cert.context->ref);
ret->childContext = child;
}
else
CertFreeCertificateContext((PCCERT_CONTEXT)child);
}
}
return (PWINE_CERT_CONTEXT_REF)ret;
}
static BOOL WINAPI CRYPT_RegDeleteCert(HCERTSTORE hCertStore,
PCCERT_CONTEXT pCertContext, DWORD dwFlags)
{
PWINE_REGSTORE store = (PWINE_REGSTORE)hCertStore;
BOOL ret;
TRACE("(%p, %p, %08lx)\n", store, pCertContext, dwFlags);
if (store->hdr.dwOpenFlags & CERT_STORE_READONLY_FLAG)
{
SetLastError(ERROR_ACCESS_DENIED);
ret = FALSE;
}
else
{
PWINE_HASH_TO_DELETE toDelete =
HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_HASH_TO_DELETE));
if (toDelete)
{
DWORD size = sizeof(toDelete->hash);
ret = CertGetCertificateContextProperty(pCertContext,
CERT_HASH_PROP_ID, toDelete->hash, &size);
if (ret)
{
list_init(&toDelete->entry);
EnterCriticalSection(&store->cs);
list_add_tail(&store->certsToDelete, &toDelete->entry);
LeaveCriticalSection(&store->cs);
ret = store->memStore->deleteCert(store->memStore, pCertContext,
dwFlags);
}
else
HeapFree(GetProcessHeap(), 0, toDelete);
}
else
ret = FALSE;
if (ret)
store->dirty = TRUE;
}
return ret;
}
static void CRYPT_RegFreeCert(PWINE_CERT_CONTEXT_REF ref)
{
PWINE_REG_CERT_CONTEXT context = (PWINE_REG_CERT_CONTEXT)ref;
TRACE("(%p)\n", ref);
if (context->childContext)
CertFreeCertificateContext((PCCERT_CONTEXT)context->childContext);
}
static BOOL WINAPI CRYPT_RegControl(HCERTSTORE hCertStore, DWORD dwFlags,
DWORD dwCtrlType, void const *pvCtrlPara)
{
PWINE_REGSTORE store = (PWINE_REGSTORE)hCertStore;
BOOL ret;
switch (dwCtrlType)
{
case CERT_STORE_CTRL_RESYNC:
CRYPT_RegFlushStore(store, FALSE);
store->memStore->closeStore(store->memStore, 0);
store->memStore = CRYPT_MemOpenStore(store->hdr.cryptProv,
store->hdr.dwOpenFlags, NULL);
if (store->memStore)
{
CRYPT_RegReadFromReg(store);
ret = TRUE;
}
else
ret = FALSE;
break;
case CERT_STORE_CTRL_COMMIT:
ret = CRYPT_RegFlushStore(store,
dwFlags & CERT_STORE_CTRL_COMMIT_FORCE_FLAG);
break;
default:
FIXME("%ld: stub\n", dwCtrlType);
ret = FALSE;
}
return ret;
}
/* Copied from shlwapi's SHDeleteKeyW, and reformatted to match this file. */
static DWORD CRYPT_RecurseDeleteKey(HKEY hKey, LPCWSTR lpszSubKey)
{
DWORD dwRet, dwKeyCount = 0, dwMaxSubkeyLen = 0, dwSize, i;
WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
HKEY hSubKey = 0;
TRACE("(hkey=%p,%s)\n", hKey, debugstr_w(lpszSubKey));
dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
if (!dwRet)
{
/* Find how many subkeys there are */
dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount,
&dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
if (!dwRet)
{
dwMaxSubkeyLen++;
if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR))
{
/* Name too big: alloc a buffer for it */
lpszName = HeapAlloc(GetProcessHeap(), 0,
dwMaxSubkeyLen*sizeof(WCHAR));
}
if (!lpszName)
dwRet = ERROR_NOT_ENOUGH_MEMORY;
else
{
/* Recursively delete all the subkeys */
for (i = 0; i < dwKeyCount && !dwRet; i++)
{
dwSize = dwMaxSubkeyLen;
dwRet = RegEnumKeyExW(hSubKey, i, lpszName, &dwSize, NULL,
NULL, NULL, NULL);
if (!dwRet)
dwRet = CRYPT_RecurseDeleteKey(hSubKey, lpszName);
}
if (lpszName != szNameBuf)
{
/* Free buffer if allocated */
HeapFree(GetProcessHeap(), 0, lpszName);
}
}
}
RegCloseKey(hSubKey);
if (!dwRet)
dwRet = RegDeleteKeyW(hKey, lpszSubKey);
}
return dwRet;
}
static WINECRYPT_CERTSTORE *CRYPT_RegOpenStore(HCRYPTPROV hCryptProv,
DWORD dwFlags, const void *pvPara)
{
PWINE_REGSTORE store = NULL;
TRACE("(%ld, %08lx, %p)\n", hCryptProv, dwFlags, pvPara);
if (dwFlags & CERT_STORE_DELETE_FLAG)
{
DWORD rc = CRYPT_RecurseDeleteKey((HKEY)pvPara, CertsW);
if (rc == ERROR_SUCCESS || rc == ERROR_NO_MORE_ITEMS)
rc = CRYPT_RecurseDeleteKey((HKEY)pvPara, CRLsW);
if (rc == ERROR_SUCCESS || rc == ERROR_NO_MORE_ITEMS)
rc = CRYPT_RecurseDeleteKey((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 = CRYPT_MemOpenStore(hCryptProv, dwFlags, NULL);
if (memStore)
{
store = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(WINE_REGSTORE));
if (store)
{
CRYPT_InitStore(&store->hdr, hCryptProv, dwFlags,
StoreTypeReg);
store->hdr.closeStore = CRYPT_RegCloseStore;
store->hdr.addCert = CRYPT_RegAddCert;
store->hdr.createCertRef = CRYPT_RegCreateCertRef;
store->hdr.enumCert = CRYPT_RegEnumCert;
store->hdr.deleteCert = CRYPT_RegDeleteCert;
store->hdr.freeCert = CRYPT_RegFreeCert;
store->hdr.control = CRYPT_RegControl;
store->memStore = memStore;
store->key = key;
InitializeCriticalSection(&store->cs);
list_init(&store->certsToDelete);
CRYPT_RegReadFromReg(store);
store->dirty = FALSE;
}
}
}
}
TRACE("returning %p\n", store);
return (WINECRYPT_CERTSTORE *)store;
}
static void WINAPI CRYPT_DummyCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
{
HeapFree(GetProcessHeap(), 0, (PWINECRYPT_CERTSTORE)hCertStore);
@ -877,10 +1466,12 @@ HCERTSTORE WINAPI CertOpenStore(LPCSTR lpszStoreProvider,
case (int)CERT_STORE_PROV_MEMORY:
openFunc = CRYPT_MemOpenStore;
break;
case (int)CERT_STORE_PROV_REG:
openFunc = CRYPT_RegOpenStore;
break;
case (int)CERT_STORE_PROV_COLLECTION:
openFunc = CRYPT_CollectionOpenStore;
break;
case (int)CERT_STORE_PROV_REG:
case (int)CERT_STORE_PROV_SYSTEM_A:
case (int)CERT_STORE_PROV_SYSTEM_W:
openFunc = CRYPT_DummyOpenStore;
@ -1640,9 +2231,24 @@ BOOL WINAPI CertCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
BOOL WINAPI CertControlStore(HCERTSTORE hCertStore, DWORD dwFlags,
DWORD dwCtrlType, void const *pvCtrlPara)
{
FIXME("(%p, %08lx, %ld, %p): stub\n", hCertStore, dwFlags, dwCtrlType,
WINECRYPT_CERTSTORE *hcs = (WINECRYPT_CERTSTORE *)hCertStore;
BOOL ret;
TRACE("(%p, %08lx, %ld, %p)\n", hCertStore, dwFlags, dwCtrlType,
pvCtrlPara);
return TRUE;
if (!hcs)
ret = FALSE;
else if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
ret = FALSE;
else
{
if (hcs->control)
ret = hcs->control(hCertStore, dwFlags, dwCtrlType, pvCtrlPara);
else
ret = TRUE;
}
return ret;
}
BOOL WINAPI CertGetCRLContextProperty(PCCRL_CONTEXT pCRLContext,

View File

@ -3,7 +3,7 @@ TOPOBJDIR = ../../..
SRCDIR = @srcdir@
VPATH = @srcdir@
TESTDLL = crypt32.dll
IMPORTS = crypt32 kernel32
IMPORTS = crypt32 advapi32 kernel32
CTESTS = \
cert.c \

View File

@ -585,6 +585,329 @@ static void testCollectionStore(void)
CertCloseStore(store1, 0);
}
/* Looks for the property with ID propID in the buffer buf. Returns a pointer
* to its header if found, NULL if not.
*/
static const struct CertPropIDHeader *findPropID(const BYTE *buf, DWORD size,
DWORD propID)
{
const struct CertPropIDHeader *ret = NULL;
BOOL failed = FALSE;
while (size && !ret && !failed)
{
if (size < sizeof(struct CertPropIDHeader))
failed = TRUE;
else
{
const struct CertPropIDHeader *hdr =
(const struct CertPropIDHeader *)buf;
size -= sizeof(struct CertPropIDHeader);
buf += sizeof(struct CertPropIDHeader);
if (size < hdr->cb)
failed = TRUE;
else if (hdr->propID == propID)
ret = hdr;
else
{
buf += hdr->cb;
size -= hdr->cb;
}
}
}
return ret;
}
typedef DWORD (WINAPI *SHDeleteKeyAFunc)(HKEY, LPCSTR);
static void testRegStore(void)
{
static const char tempKey[] = "Software\\Wine\\CryptTemp";
HCERTSTORE store;
LONG rc;
HKEY key = NULL;
DWORD disp;
store = CertOpenStore(CERT_STORE_PROV_REG, 0, 0, 0, NULL);
ok(!store && GetLastError() == ERROR_INVALID_HANDLE,
"Expected ERROR_INVALID_HANDLE, got %ld\n", GetLastError());
store = CertOpenStore(CERT_STORE_PROV_REG, 0, 0, 0, key);
ok(!store && GetLastError() == ERROR_INVALID_HANDLE,
"Expected ERROR_INVALID_HANDLE, got %ld\n", GetLastError());
/* Opening up any old key works.. */
key = HKEY_CURRENT_USER;
store = CertOpenStore(CERT_STORE_PROV_REG, 0, 0, 0, key);
/* Not sure if this is a bug in DuplicateHandle, marking todo_wine for now
*/
todo_wine ok(store != 0, "CertOpenStore failed: %08lx\n", GetLastError());
CertCloseStore(store, 0);
rc = RegCreateKeyExA(HKEY_CURRENT_USER, tempKey, 0, NULL, 0, KEY_ALL_ACCESS,
NULL, &key, NULL);
ok(!rc, "RegCreateKeyExA failed: %ld\n", rc);
if (key)
{
BOOL ret;
BYTE hash[20];
DWORD size, i;
static const char certificates[] = "Certificates\\";
char subKeyName[sizeof(certificates) + 20 * 2 + 1], *ptr;
HKEY subKey;
PCCERT_CONTEXT context;
store = CertOpenStore(CERT_STORE_PROV_REG, 0, 0, 0, key);
ok(store != 0, "CertOpenStore failed: %08lx\n", GetLastError());
/* Add a certificate. It isn't persisted right away, since it's only
* added to the cache..
*/
ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
bigCert2, sizeof(bigCert2) - 1, CERT_STORE_ADD_ALWAYS, NULL);
ok(ret, "CertAddEncodedCertificateToStore failed: %08lx\n",
GetLastError());
/* so flush the cache to force a commit.. */
ret = CertControlStore(store, 0, CERT_STORE_CTRL_COMMIT, NULL);
ok(ret, "CertControlStore failed: %08lx\n", GetLastError());
/* and check that the expected subkey was written. */
size = sizeof(hash);
ret = CryptHashCertificate(0, 0, 0, bigCert2, sizeof(bigCert2) - 1,
hash, &size);
ok(ret, "CryptHashCertificate failed: %ld\n", GetLastError());
strcpy(subKeyName, certificates);
for (i = 0, ptr = subKeyName + sizeof(certificates) - 1; i < size;
i++, ptr += 2)
sprintf(ptr, "%02X", hash[i]);
rc = RegCreateKeyExA(key, subKeyName, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
&subKey, NULL);
ok(!rc, "RegCreateKeyExA failed: %ld\n", rc);
if (subKey)
{
LPBYTE buf;
size = 0;
RegQueryValueExA(subKey, "Blob", NULL, NULL, NULL, &size);
buf = HeapAlloc(GetProcessHeap(), 0, size);
if (buf)
{
rc = RegQueryValueExA(subKey, "Blob", NULL, NULL, buf, &size);
ok(!rc, "RegQueryValueExA failed: %ld\n", rc);
if (!rc)
{
const struct CertPropIDHeader *hdr;
/* Both the hash and the cert should be present */
hdr = findPropID(buf, size, CERT_CERT_PROP_ID);
ok(hdr != NULL, "Expected to find a cert property\n");
if (hdr)
{
ok(hdr->cb == sizeof(bigCert2) - 1,
"Unexpected size %ld of cert property, expected %d\n",
hdr->cb, sizeof(bigCert2) - 1);
ok(!memcmp((BYTE *)hdr + sizeof(*hdr), bigCert2,
hdr->cb), "Unexpected cert in cert property\n");
}
hdr = findPropID(buf, size, CERT_HASH_PROP_ID);
ok(hdr != NULL, "Expected to find a hash property\n");
if (hdr)
{
ok(hdr->cb == sizeof(hash),
"Unexpected size %ld of hash property, expected %d\n",
hdr->cb, sizeof(hash));
ok(!memcmp((BYTE *)hdr + sizeof(*hdr), hash,
hdr->cb), "Unexpected hash in cert property\n");
}
}
HeapFree(GetProcessHeap(), 0, buf);
}
RegCloseKey(subKey);
}
/* Remove the existing context */
context = CertEnumCertificatesInStore(store, NULL);
ok(context != NULL, "Expected a cert context\n");
if (context)
{
CertDeleteCertificateFromStore(context);
CertFreeCertificateContext(context);
}
ret = CertControlStore(store, 0, CERT_STORE_CTRL_COMMIT, NULL);
ok(ret, "CertControlStore failed: %08lx\n", GetLastError());
/* Add a serialized cert with a bogus hash directly to the registry */
memset(hash, 0, sizeof(hash));
strcpy(subKeyName, certificates);
for (i = 0, ptr = subKeyName + sizeof(certificates) - 1;
i < sizeof(hash); i++, ptr += 2)
sprintf(ptr, "%02X", hash[i]);
rc = RegCreateKeyExA(key, subKeyName, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
&subKey, NULL);
ok(!rc, "RegCreateKeyExA failed: %ld\n", rc);
if (subKey)
{
BYTE buf[sizeof(struct CertPropIDHeader) * 2 + sizeof(hash) +
sizeof(bigCert) - 1], *ptr;
DWORD certCount = 0;
struct CertPropIDHeader *hdr;
hdr = (struct CertPropIDHeader *)buf;
hdr->propID = CERT_HASH_PROP_ID;
hdr->unknown1 = 1;
hdr->cb = sizeof(hash);
ptr = buf + sizeof(*hdr);
memcpy(ptr, hash, sizeof(hash));
ptr += sizeof(hash);
hdr = (struct CertPropIDHeader *)ptr;
hdr->propID = CERT_CERT_PROP_ID;
hdr->unknown1 = 1;
hdr->cb = sizeof(bigCert) - 1;
ptr += sizeof(*hdr);
memcpy(ptr, bigCert, sizeof(bigCert) - 1);
rc = RegSetValueExA(subKey, "Blob", 0, REG_BINARY, buf,
sizeof(buf));
ok(!rc, "RegSetValueExA failed: %ld\n", rc);
ret = CertControlStore(store, 0, CERT_STORE_CTRL_RESYNC, NULL);
ok(ret, "CertControlStore failed: %08lx\n", GetLastError());
/* Make sure the bogus hash cert gets loaded. */
certCount = 0;
context = NULL;
do {
context = CertEnumCertificatesInStore(store, context);
if (context)
certCount++;
} while (context != NULL);
ok(certCount == 1, "Expected 1 certificates, got %ld\n", certCount);
RegCloseKey(subKey);
}
/* Add another serialized cert directly to the registry, this time
* under the correct key name (named with the correct hash value).
*/
size = sizeof(hash);
ret = CryptHashCertificate(0, 0, 0, bigCert2,
sizeof(bigCert2) - 1, hash, &size);
ok(ret, "CryptHashCertificate failed: %ld\n", GetLastError());
strcpy(subKeyName, certificates);
for (i = 0, ptr = subKeyName + sizeof(certificates) - 1;
i < sizeof(hash); i++, ptr += 2)
sprintf(ptr, "%02X", hash[i]);
rc = RegCreateKeyExA(key, subKeyName, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
&subKey, NULL);
ok(!rc, "RegCreateKeyExA failed: %ld\n", rc);
if (subKey)
{
BYTE buf[sizeof(struct CertPropIDHeader) * 2 + sizeof(hash) +
sizeof(bigCert2) - 1], *ptr;
DWORD certCount = 0;
PCCERT_CONTEXT context;
struct CertPropIDHeader *hdr;
/* First try with a bogus hash... */
hdr = (struct CertPropIDHeader *)buf;
hdr->propID = CERT_HASH_PROP_ID;
hdr->unknown1 = 1;
hdr->cb = sizeof(hash);
ptr = buf + sizeof(*hdr);
memset(ptr, 0, sizeof(hash));
ptr += sizeof(hash);
hdr = (struct CertPropIDHeader *)ptr;
hdr->propID = CERT_CERT_PROP_ID;
hdr->unknown1 = 1;
hdr->cb = sizeof(bigCert2) - 1;
ptr += sizeof(*hdr);
memcpy(ptr, bigCert2, sizeof(bigCert2) - 1);
rc = RegSetValueExA(subKey, "Blob", 0, REG_BINARY, buf,
sizeof(buf));
ok(!rc, "RegSetValueExA failed: %ld\n", rc);
ret = CertControlStore(store, 0, CERT_STORE_CTRL_RESYNC, NULL);
ok(ret, "CertControlStore failed: %08lx\n", GetLastError());
/* and make sure just one cert still gets loaded. */
certCount = 0;
context = NULL;
do {
context = CertEnumCertificatesInStore(store, context);
if (context)
certCount++;
} while (context != NULL);
ok(certCount == 1, "Expected 1 certificates, got %ld\n", certCount);
/* Try again with the correct hash... */
ptr = buf + sizeof(*hdr);
memcpy(ptr, hash, sizeof(hash));
rc = RegSetValueExA(subKey, "Blob", 0, REG_BINARY, buf,
sizeof(buf));
ok(!rc, "RegSetValueExA failed: %ld\n", rc);
ret = CertControlStore(store, 0, CERT_STORE_CTRL_RESYNC, NULL);
ok(ret, "CertControlStore failed: %08lx\n", GetLastError());
/* and make sure two certs get loaded. */
certCount = 0;
context = NULL;
do {
context = CertEnumCertificatesInStore(store, context);
if (context)
certCount++;
} while (context != NULL);
ok(certCount == 2, "Expected 2 certificates, got %ld\n", certCount);
RegCloseKey(subKey);
}
CertCloseStore(store, 0);
/* Is delete allowed on a reg store? */
store = CertOpenStore(CERT_STORE_PROV_REG, 0, 0,
CERT_STORE_DELETE_FLAG, key);
ok(store == NULL, "Expected NULL return from CERT_STORE_DELETE_FLAG\n");
ok(GetLastError() == 0, "CertOpenStore failed: %08lx\n",
GetLastError());
RegCloseKey(key);
}
/* The CertOpenStore with CERT_STORE_DELETE_FLAG above will delete the
* contents of the key, but not the key itself.
*/
rc = RegCreateKeyExA(HKEY_CURRENT_USER, tempKey, 0, NULL, 0, KEY_ALL_ACCESS,
NULL, &key, &disp);
ok(!rc, "RegCreateKeyExA failed: %ld\n", rc);
ok(disp == REG_OPENED_EXISTING_KEY,
"Expected REG_OPENED_EXISTING_KEY, got %ld\n", disp);
if (!rc)
{
RegCloseKey(key);
rc = RegDeleteKeyA(HKEY_CURRENT_USER, tempKey);
/* There seems to be a bug in the registry code, not sure if it's a
* race condition in the recurse delete key implementation here, or if
* it's elsewhere in wine. Marking todo_wine for now.
*/
todo_wine ok(!rc, "RegDeleteKeyA failed: %ld\n", rc);
if (rc)
{
HMODULE shlwapi = LoadLibraryA("shlwapi");
/* Use shlwapi's SHDeleteKeyA to _really_ blow away the key,
* otherwise subsequent tests will fail.
*/
if (shlwapi)
{
SHDeleteKeyAFunc pSHDeleteKeyA =
(SHDeleteKeyAFunc)GetProcAddress(shlwapi, "SHDeleteKeyA");
if (pSHDeleteKeyA)
pSHDeleteKeyA(HKEY_CURRENT_USER, tempKey);
FreeLibrary(shlwapi);
}
}
}
}
static void testCertProperties(void)
{
PCCERT_CONTEXT context = CertCreateCertificateContext(X509_ASN_ENCODING,
@ -862,6 +1185,7 @@ START_TEST(cert)
/* various combinations of CertOpenStore */
testMemStore();
testCollectionStore();
testRegStore();
testCertProperties();
testAddSerialized();