Implement registry stores.
This commit is contained in:
parent
60cae6e289
commit
5ef9d88cab
|
@ -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 \
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -3,7 +3,7 @@ TOPOBJDIR = ../../..
|
|||
SRCDIR = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
TESTDLL = crypt32.dll
|
||||
IMPORTS = crypt32 kernel32
|
||||
IMPORTS = crypt32 advapi32 kernel32
|
||||
|
||||
CTESTS = \
|
||||
cert.c \
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue