Implement registry stores.
This commit is contained in:
parent
60cae6e289
commit
5ef9d88cab
|
@ -5,7 +5,7 @@ SRCDIR = @srcdir@
|
||||||
VPATH = @srcdir@
|
VPATH = @srcdir@
|
||||||
MODULE = crypt32.dll
|
MODULE = crypt32.dll
|
||||||
IMPORTLIB = libcrypt32.$(IMPLIBEXT)
|
IMPORTLIB = libcrypt32.$(IMPLIBEXT)
|
||||||
IMPORTS = advapi32 kernel32 ntdll
|
IMPORTS = user32 advapi32 kernel32 ntdll
|
||||||
|
|
||||||
C_SRCS = \
|
C_SRCS = \
|
||||||
cert.c \
|
cert.c \
|
||||||
|
|
|
@ -27,10 +27,12 @@
|
||||||
* registering and enumerating physical stores and locations.)
|
* registering and enumerating physical stores and locations.)
|
||||||
* - Many flags, options and whatnot are unimplemented.
|
* - Many flags, options and whatnot are unimplemented.
|
||||||
*/
|
*/
|
||||||
|
#include <assert.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include "windef.h"
|
#include "windef.h"
|
||||||
#include "winbase.h"
|
#include "winbase.h"
|
||||||
#include "winreg.h"
|
#include "winreg.h"
|
||||||
|
#include "winuser.h"
|
||||||
#include "wincrypt.h"
|
#include "wincrypt.h"
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
#include "wine/list.h"
|
#include "wine/list.h"
|
||||||
|
@ -234,6 +236,29 @@ typedef struct _WINE_MEMSTORE
|
||||||
struct list certs;
|
struct list certs;
|
||||||
} WINE_MEMSTORE, *PWINE_MEMSTORE;
|
} 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
|
typedef struct _WINE_STORE_LIST_ENTRY
|
||||||
{
|
{
|
||||||
PWINECRYPT_CERTSTORE store;
|
PWINECRYPT_CERTSTORE store;
|
||||||
|
@ -806,6 +831,570 @@ static WINECRYPT_CERTSTORE *CRYPT_CollectionOpenStore(HCRYPTPROV hCryptProv,
|
||||||
return (PWINECRYPT_CERTSTORE)store;
|
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)
|
static void WINAPI CRYPT_DummyCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
|
||||||
{
|
{
|
||||||
HeapFree(GetProcessHeap(), 0, (PWINECRYPT_CERTSTORE)hCertStore);
|
HeapFree(GetProcessHeap(), 0, (PWINECRYPT_CERTSTORE)hCertStore);
|
||||||
|
@ -877,10 +1466,12 @@ HCERTSTORE WINAPI CertOpenStore(LPCSTR lpszStoreProvider,
|
||||||
case (int)CERT_STORE_PROV_MEMORY:
|
case (int)CERT_STORE_PROV_MEMORY:
|
||||||
openFunc = CRYPT_MemOpenStore;
|
openFunc = CRYPT_MemOpenStore;
|
||||||
break;
|
break;
|
||||||
|
case (int)CERT_STORE_PROV_REG:
|
||||||
|
openFunc = CRYPT_RegOpenStore;
|
||||||
|
break;
|
||||||
case (int)CERT_STORE_PROV_COLLECTION:
|
case (int)CERT_STORE_PROV_COLLECTION:
|
||||||
openFunc = CRYPT_CollectionOpenStore;
|
openFunc = CRYPT_CollectionOpenStore;
|
||||||
break;
|
break;
|
||||||
case (int)CERT_STORE_PROV_REG:
|
|
||||||
case (int)CERT_STORE_PROV_SYSTEM_A:
|
case (int)CERT_STORE_PROV_SYSTEM_A:
|
||||||
case (int)CERT_STORE_PROV_SYSTEM_W:
|
case (int)CERT_STORE_PROV_SYSTEM_W:
|
||||||
openFunc = CRYPT_DummyOpenStore;
|
openFunc = CRYPT_DummyOpenStore;
|
||||||
|
@ -1640,9 +2231,24 @@ BOOL WINAPI CertCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
|
||||||
BOOL WINAPI CertControlStore(HCERTSTORE hCertStore, DWORD dwFlags,
|
BOOL WINAPI CertControlStore(HCERTSTORE hCertStore, DWORD dwFlags,
|
||||||
DWORD dwCtrlType, void const *pvCtrlPara)
|
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);
|
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,
|
BOOL WINAPI CertGetCRLContextProperty(PCCRL_CONTEXT pCRLContext,
|
||||||
|
|
|
@ -3,7 +3,7 @@ TOPOBJDIR = ../../..
|
||||||
SRCDIR = @srcdir@
|
SRCDIR = @srcdir@
|
||||||
VPATH = @srcdir@
|
VPATH = @srcdir@
|
||||||
TESTDLL = crypt32.dll
|
TESTDLL = crypt32.dll
|
||||||
IMPORTS = crypt32 kernel32
|
IMPORTS = crypt32 advapi32 kernel32
|
||||||
|
|
||||||
CTESTS = \
|
CTESTS = \
|
||||||
cert.c \
|
cert.c \
|
||||||
|
|
|
@ -585,6 +585,329 @@ static void testCollectionStore(void)
|
||||||
CertCloseStore(store1, 0);
|
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)
|
static void testCertProperties(void)
|
||||||
{
|
{
|
||||||
PCCERT_CONTEXT context = CertCreateCertificateContext(X509_ASN_ENCODING,
|
PCCERT_CONTEXT context = CertCreateCertificateContext(X509_ASN_ENCODING,
|
||||||
|
@ -862,6 +1185,7 @@ START_TEST(cert)
|
||||||
/* various combinations of CertOpenStore */
|
/* various combinations of CertOpenStore */
|
||||||
testMemStore();
|
testMemStore();
|
||||||
testCollectionStore();
|
testCollectionStore();
|
||||||
|
testRegStore();
|
||||||
|
|
||||||
testCertProperties();
|
testCertProperties();
|
||||||
testAddSerialized();
|
testAddSerialized();
|
||||||
|
|
Loading…
Reference in New Issue