windows-nt/Source/XPSP1/NT/base/cluster/service/cp/crypto.c
2020-09-26 16:20:57 +08:00

2646 lines
67 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
crypto.c
Abstract:
Interfaces for registering and deregistering crypto checkpoint
handlers.
Author:
Jeff Spelman (jeffspel) 11/10/1998
Revision History:
Charlie Wickham (charlwi) 7/7/00
added "how this works" section
--*/
#include "cpp.h"
#include "wincrypt.h"
#if 0
How crypto checkpointing works
Crypto checkpointing allows a crypto key container to be associated with a
resource. When that resource is moved to another node in the cluster, the key
container is constructed/updated from the checkpoint information on the previous
hosting node. The key container is not replicated; it only appears on a node if
the resource is moved to that node. Keys in the container must be exportable.
The user identifies the crypto key container by passing in a string of the form
"Type\Name\Key" where Type is CSP Provider type, Name is the provider name, and
Key is the key container name.
Checkpoints are added in CpckAddCryptoCheckpoint. Checkpoint information is
stored in two places: in the CryptoSync key under the resource key and in a
datafile on the quorum drive. A new key, called CryptoSync, is created under
the resource key. In this key are values that are of the form 00000001,
00000002, etc. The data associated with the value is the string identifying
the crypto key container. The datafile contains all the information to restore
the crypto key container on another node in the cluster, i.e., a header
(CRYPTO_KEY_FILE_DATA), the signature and exchange keys if they exist, and the
security descriptor associated with key container.
Upon receiving the control code, the cluster service cracks the string into its
component parts and stores the data in a CRYPTO_KEY_INFO structure. The
CryptoSync key is opened/created and a check is made to see if the checkpoint
already exists. If not, a new ID is found and the checkpoint is saved to a file
on the quorum disk.
When the resource is moved to another node, the FM calls CpckReplicateCryptoKeys
to restore the keys on that node. This routine reads the file and creates the
key container, imports the keys and sets the security descr. on the container.
Delete cleans up registry entry and file.
#endif
//
// Local type and structure definitions
//
typedef struct _CPCK_ADD_CONTEXT {
BOOL fFound;
BYTE *pbInfo;
DWORD cbInfo;
} CPCK_ADD_CONTEXT, *PCPCK_ADD_CONTEXT;
typedef struct _CPCK_DEL_CONTEXT {
DWORD dwId;
BYTE *pbInfo;
DWORD cbInfo;
} CPCK_DEL_CONTEXT, *PCPCK_DEL_CONTEXT;
typedef struct _CPCK_GET_CONTEXT {
DWORD cCheckpoints;
BOOL fNeedMoreData;
DWORD cbAvailable;
DWORD cbRequired;
BYTE *pbOutput;
} CPCK_GET_CONTEXT, *PCPCK_GET_CONTEXT;
// struct for Crypto Key information
typedef struct _CRYPTO_KEY_INFO {
DWORD dwVersion;
DWORD dwProvType;
LPWSTR pwszProvName;
LPWSTR pwszContainer;
} CRYPTO_KEY_INFO, *PCRYPTO_KEY_INFO;
// current version for the CRYPTO_KEY_INFO struct
#define CRYPTO_KEY_INFO_VERSION 1
// struct for key data when writing and reading from files
#define SALT_SIZE 16
#define IV_SIZE 8
typedef struct _CRYPTO_KEY_FILE_DATA {
DWORD dwVersion;
DWORD cbSig;
DWORD cbExch;
DWORD cbSecDescr;
struct _CRYPTO_KEY_FILE_INITIALIZATION_DATA {
BYTE rgbSigIV[IV_SIZE];
BYTE rgbExchIV[IV_SIZE];
BYTE rgbSalt[SALT_SIZE];
};
} CRYPTO_KEY_FILE_DATA, *PCRYPTO_KEY_FILE_DATA;
// current version for the CRYPTO_KEY_INFO struct
#define CRYPTO_KEY_FILE_DATA_VERSION 1
//
// Local function prototypes
//
BOOL
CpckReplicateCallback(
IN LPWSTR ValueName,
IN LPVOID ValueData,
IN DWORD ValueType,
IN DWORD ValueSize,
IN PFM_RESOURCE Resource
);
BOOL
CpckAddCheckpointCallback(
IN LPWSTR ValueName,
IN LPVOID ValueData,
IN DWORD ValueType,
IN DWORD ValueSize,
IN PCPCK_ADD_CONTEXT Context
);
BOOL
CpckDeleteCheckpointCallback(
IN LPWSTR ValueName,
IN LPVOID ValueData,
IN DWORD ValueType,
IN DWORD ValueSize,
IN PCPCK_DEL_CONTEXT Context
);
BOOL
CpckGetCheckpointsCallback(
IN LPWSTR ValueName,
IN LPVOID ValueData,
IN DWORD ValueType,
IN DWORD ValueSize,
IN PCPCK_GET_CONTEXT Context
);
DWORD
CpckInstallKeyContainer(
IN HCRYPTPROV hProv,
IN LPWSTR FileName
);
DWORD
CpckCheckpoint(
IN PFM_RESOURCE Resource,
IN HCRYPTPROV hProv,
IN DWORD dwId,
IN CRYPTO_KEY_INFO *pCryptoKeyInfo
);
CL_NODE_ID
CppGetQuorumNodeId(
VOID
);
DWORD
CpckReplicateCryptoKeys(
IN PFM_RESOURCE Resource
)
/*++
Routine Description:
Restores any crypto key checkpoints for this resource.
Arguments:
Resource - Supplies the resource.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
DWORD Status;
HDMKEY ResourceKey;
HDMKEY CryptoSyncKey;
//
// Open up the resource's key
//
ResourceKey = DmOpenKey(DmResourcesKey,
OmObjectId(Resource),
KEY_READ);
CL_ASSERT(ResourceKey != NULL);
//
// Open up the CryptoSync key
//
CryptoSyncKey = DmOpenKey(ResourceKey,
L"CryptoSync",
KEY_READ);
DmCloseKey(ResourceKey);
if (CryptoSyncKey != NULL) {
DmEnumValues(CryptoSyncKey,
CpckReplicateCallback,
Resource);
DmCloseKey(CryptoSyncKey);
}
return(ERROR_SUCCESS);
} // CpckReplicateCryptoKeys
void
FreeCryptoKeyInfo(
IN OUT CRYPTO_KEY_INFO *pCryptoKeyInfo
)
/*++
Routine Description:
Frees the string pointers in the structure.
Arguments:
CryptoKeyInfo - Pointer to the CRYPTO_KEY_INFO structure which
--*/
{
if (NULL != pCryptoKeyInfo)
{
if (NULL != pCryptoKeyInfo->pwszProvName)
{
LocalFree(pCryptoKeyInfo->pwszProvName);
pCryptoKeyInfo->pwszProvName = NULL;
}
if (NULL != pCryptoKeyInfo->pwszContainer)
{
LocalFree(pCryptoKeyInfo->pwszContainer);
pCryptoKeyInfo->pwszContainer = NULL;
}
}
} // FreeCryptoKeyInfo
DWORD
CpckValueToCryptoKeyInfo(
OUT CRYPTO_KEY_INFO *pCryptoKeyInfo,
IN LPVOID ValueData,
IN DWORD ValueSize
)
/*++
Routine Description:
Converts from a binary blob into a CryptoKeyInfo structure. Basically
this just does some value and pointer assignments.
Arguments:
CryptoKeyInfo - Pointer to the CRYPTO_KEY_INFO structure which is filled in
ValueData - Supplies the value data (this is the binary blob)
ValueSize - Supplies the size of ValueData
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
DWORD *pdw;
WCHAR *pwsz = (WCHAR*)ValueData;
DWORD cb = sizeof(DWORD) * 2;
DWORD cwch;
DWORD i;
DWORD Status = ERROR_SUCCESS;
// make sure the length is OK
if (ValueSize < sizeof(WCHAR) * 3)
{
Status = ERROR_INVALID_PARAMETER;
goto Ret;
}
// first is the Provider type
for (i = 0; i < (ValueSize - 3) / sizeof(WCHAR); i++)
{
if (L'\\' == pwsz[i])
{
pwsz[i] = L'\0';
break;
}
}
if ((ValueSize - 3) / sizeof(WCHAR) == i)
{
Status = ERROR_INVALID_PARAMETER;
goto Ret;
}
pCryptoKeyInfo->dwProvType = _wtoi(pwsz);
pwsz[i] = L'\\';
cwch = i;
// grab the provider name pointer
for (i = i + 1; i < (ValueSize - 2) / sizeof(WCHAR); i++)
{
if (L'\\' == pwsz[i])
{
pwsz[i] = L'\0';
break;
}
}
if ((ValueSize - 2) / sizeof(WCHAR) == i)
{
Status = ERROR_INVALID_PARAMETER;
goto Ret;
}
cb = (wcslen(&pwsz[cwch + 1]) + 1) * sizeof(WCHAR);
if (NULL == (pCryptoKeyInfo->pwszProvName =
(WCHAR*)LocalAlloc(LMEM_ZEROINIT, cb)))
{
Status = ERROR_NOT_ENOUGH_MEMORY;
goto Ret;
}
wcscpy(pCryptoKeyInfo->pwszProvName, &pwsz[cwch + 1]);
pwsz[i] = L'\\';
cwch = i;
// grab the container name pointer
cb = (wcslen(&pwsz[cwch + 1]) + 1) * sizeof(WCHAR);
if (NULL == (pCryptoKeyInfo->pwszContainer =
(WCHAR*)LocalAlloc(LMEM_ZEROINIT, cb)))
{
Status = ERROR_NOT_ENOUGH_MEMORY;
goto Ret;
}
wcscpy(pCryptoKeyInfo->pwszContainer, &pwsz[cwch + 1]);
Ret:
return (Status);
} // CpckValueToCryptoKeyInfo
DWORD
CpckOpenCryptoKeyContainer(
IN CRYPTO_KEY_INFO *pCryptoKeyInfo,
IN BOOL fCreate,
OUT HCRYPTPROV *phProv
)
/*++
Routine Description:
Opens a crypto key container (always uses CRYPT_MACHINE_KEYSET). Checks
for either a signature and/or an exchange key and that they are
exportable.
Arguments:
pCryptKeyInfo - Supplies the information for opening the container
fCreate - Flag indicating if container is to be created
phProv - The resulting Crypto Provider handle
Return Value:
ERROR_SUCCESS if succeeds
Crypto Error code if it fails
--*/
{
BOOLEAN WasEnabled;
HCRYPTKEY hSigKey = 0;
HCRYPTKEY hExchKey = 0;
DWORD dwPermissions;
DWORD cbPermissions;
DWORD Status = 0;
//
// Attempt to open the specified crypto key container.
//
if (!CryptAcquireContextW(phProv,
pCryptoKeyInfo->pwszContainer,
pCryptoKeyInfo->pwszProvName,
pCryptoKeyInfo->dwProvType,
CRYPT_MACHINE_KEYSET))
{
if (fCreate)
{
//
// if unable to open then create the container
//
if (!CryptAcquireContextW(phProv,
pCryptoKeyInfo->pwszContainer,
pCryptoKeyInfo->pwszProvName,
pCryptoKeyInfo->dwProvType,
CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET))
{
Status = GetLastError();
}
}
else
{
Status = GetLastError();
}
}
// if failed then try with BACKUP/RESTORE privilege
if (0 != Status)
{
//
//
Status = ClRtlEnableThreadPrivilege(SE_RESTORE_PRIVILEGE,
&WasEnabled);
if ( Status != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL,
"[CPCK] CpckOpenCryptoKeyContainer failed to enable thread privilege %1!d!...\n",
Status);
goto Ret;
}
if (!CryptAcquireContextW(phProv,
pCryptoKeyInfo->pwszContainer,
pCryptoKeyInfo->pwszProvName,
pCryptoKeyInfo->dwProvType,
CRYPT_MACHINE_KEYSET))
{
if (fCreate)
{
//
// if unable to open then create the container
//
if (!CryptAcquireContextW(phProv,
pCryptoKeyInfo->pwszContainer,
pCryptoKeyInfo->pwszProvName,
pCryptoKeyInfo->dwProvType,
CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET))
{
Status = GetLastError();
}
}
else
{
Status = GetLastError();
}
}
ClRtlRestoreThreadPrivilege(SE_RESTORE_PRIVILEGE,
WasEnabled);
}
if ((0 == Status) && (!fCreate))
{
// check if there is a sig key
if (CryptGetUserKey(*phProv, AT_SIGNATURE, &hSigKey))
{
// check if key is exportable
cbPermissions = sizeof(DWORD);
if (!CryptGetKeyParam(hSigKey,
KP_PERMISSIONS,
(BYTE*)&dwPermissions,
&cbPermissions,
0))
{
Status = GetLastError();
goto Ret;
}
if (!(dwPermissions & CRYPT_EXPORT))
{
Status = (DWORD)NTE_BAD_KEY;
goto Ret;
}
}
// check if there is an exchange key
if (CryptGetUserKey(*phProv, AT_KEYEXCHANGE, &hExchKey))
{
// check if key is exportable
cbPermissions = sizeof(DWORD);
if (!CryptGetKeyParam(hExchKey,
KP_PERMISSIONS,
(BYTE*)&dwPermissions,
&cbPermissions,
0))
{
Status = GetLastError();
goto Ret;
}
if (!(dwPermissions & CRYPT_EXPORT))
{
Status = (DWORD)NTE_BAD_KEY;
goto Ret;
}
}
}
Ret:
if (hSigKey)
CryptDestroyKey(hSigKey);
if (hExchKey)
CryptDestroyKey(hExchKey);
return Status;
} // CpckOpenCryptoKeyContainer
BOOL
CpckReplicateCallback(
IN LPWSTR ValueName,
IN LPVOID ValueData,
IN DWORD ValueType,
IN DWORD ValueSize,
IN PFM_RESOURCE Resource
)
/*++
Routine Description:
Value enumeration callback for watching a resource's crypto key
checkpoints.
Arguments:
ValueName - Supplies the name of the value (this is the checkpoint ID)
ValueData - Supplies the value data (this is the registry crypto key info)
ValueType - Supplies the value type (must be REG_BINARY)
ValueSize - Supplies the size of ValueData
Resource - Supplies the resource this value is a crypto key checkpoint for
Return Value:
TRUE to continue enumeration
--*/
{
DWORD Id;
DWORD Status;
WCHAR TempFile[MAX_PATH];
CRYPTO_KEY_INFO CryptoKeyInfo;
HCRYPTPROV hProv = 0;
BOOL fRet = TRUE;
memset(&CryptoKeyInfo, 0, sizeof(CryptoKeyInfo));
Id = wcstol(ValueName, NULL, 16); // skip past the 'Crypto' prefix
if (Id == 0) {
ClRtlLogPrint(LOG_UNUSUAL,
"[CPCK] CpckReplicateCallback invalid checkpoint ID %1!ws! for resource %2!ws!\n",
ValueName,
OmObjectName(Resource));
goto Ret;
}
//
// convert from binary blob into a Crypto Key Info structure
//
Status = CpckValueToCryptoKeyInfo(&CryptoKeyInfo,
ValueData,
ValueSize);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_UNUSUAL,
"[CPCK] CpckReplicateCallback invalid crypto info %1!ws! for resource %2!ws!\n",
ValueName,
OmObjectName(Resource));
goto Ret;
}
Status = CpckOpenCryptoKeyContainer(&CryptoKeyInfo,
TRUE,
&hProv);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_UNUSUAL,
"[CPCK] CpckReplicateCallback CryptAcquireContext failed for %1!ws! %2!ws! with %3!d! for resource %4!ws!\n",
CryptoKeyInfo.pwszContainer,
CryptoKeyInfo.pwszProvName,
Status,
OmObjectName(Resource));
goto Ret;
}
ClRtlLogPrint(LOG_NOISE,
"[CPCK] CpckReplicateCallback retrieving crypto id %1!lx! for resource %2!ws\n",
Id,
OmObjectName(Resource));
//
// See if there is any checkpoint data for this ID.
//
Status = DmCreateTempFileName(TempFile);
if (Status != ERROR_SUCCESS) {
CL_UNEXPECTED_ERROR( Status );
}
Status = CpGetDataFile(Resource,
Id,
TempFile,
TRUE);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_UNUSUAL,
"[CPCK] CpckReplicateCallback - CpGetDataFile for id %1!lx! resource %2!ws! failed %3!d!\n",
Id,
OmObjectName(Resource),
Status);
} else {
//
// Finally install the checkpointed file into the registry.
//
Status = CpckInstallKeyContainer(hProv, TempFile);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_CRITICAL,
"[CPCK] CpckReplicateCallback: could not restore temp file %1!ws! to container %2!ws! error %3!d!\n",
TempFile,
CryptoKeyInfo.pwszContainer,
Status);
// Log the event for crypto key failure
CsLogEventData2(LOG_CRITICAL,
CP_CRYPTO_CKPT_RESTORE_FAILED,
sizeof(Status),
&Status,
OmObjectName(Resource),
CryptoKeyInfo.pwszContainer);
}
}
DeleteFile(TempFile);
//
// watcher for crypto keys is not currently available or needed
//
Ret:
FreeCryptoKeyInfo(&CryptoKeyInfo);
if (hProv)
CryptReleaseContext(hProv, 0);
return fRet;
} // CpckReplicateCallback
DWORD
CpckAddCryptoCheckpoint(
IN PFM_RESOURCE Resource,
IN PVOID InBuffer,
IN DWORD InBufferSize
)
/*++
Routine Description:
Adds a new crypto key checkpoint to a resource's list.
Arguments:
Resource - supplies the resource the crypto key checkpoint should be added to.
InBuffer - Supplies the crypto key information (always CRYPT_MACHINE_KEYSET)
InBufferSize - Supplies the length of InBuffer
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
HDMKEY SyncKey;
CPCK_ADD_CONTEXT Context;
HDMKEY ResourceKey = NULL;
HDMKEY CryptoSyncKey = NULL;
DWORD Disposition;
DWORD Id;
WCHAR IdName[9];
DWORD Status;
CLUSTER_RESOURCE_STATE State;
BOOLEAN WasEnabled;
DWORD Count=60;
CRYPTO_KEY_INFO CryptoKeyInfo;
HCRYPTPROV hProv = 0;
memset(&CryptoKeyInfo, 0, sizeof(CryptoKeyInfo));
//
// convert from binary blob into a Crypto Key Info structure
//
Status = CpckValueToCryptoKeyInfo(&CryptoKeyInfo,
InBuffer,
InBufferSize);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_UNUSUAL,
"[CPCK] CpckAddCryptoCheckpoint: invalid crypto info for resource %1!ws!\n",
OmObjectName(Resource));
goto Ret;
}
Status = CpckOpenCryptoKeyContainer(&CryptoKeyInfo,
FALSE,
&hProv);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_UNUSUAL,
"[CPCK] CpckAddCryptoCheckpoint: open key container failed for "
"container %1!ws! (provider: %2!ws!) with %3!d! for resource %4!ws!\n",
CryptoKeyInfo.pwszContainer,
CryptoKeyInfo.pwszProvName,
Status,
OmObjectName(Resource));
goto Ret;
}
//
// Open up the resource's key
//
ResourceKey = DmOpenKey(DmResourcesKey,
OmObjectId(Resource),
KEY_READ);
if( ResourceKey == NULL ) {
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[CPCK] CpckAddCryptoCheckpoint: couldn't open Resource key for %1!ws! error %2!d!\n",
OmObjectName(Resource),
Status);
goto Ret;
}
//
// Open up the CryptoSync key
//
CryptoSyncKey = DmCreateKey(ResourceKey,
L"CryptoSync",
0,
KEY_READ | KEY_WRITE,
NULL,
&Disposition);
DmCloseKey(ResourceKey);
if (CryptoSyncKey == NULL) {
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[CPCK] CpckAddCryptoCheckpoint: couldn't create CryptoSync key for "
"resource %1!ws! error %2!d!\n",
OmObjectName(Resource),
Status);
goto Ret;
}
if (Disposition == REG_OPENED_EXISTING_KEY) {
//
// Enumerate all the other values to make sure this key is
// not already registered.
//
Context.fFound = FALSE;
Context.pbInfo = InBuffer;
Context.cbInfo = InBufferSize;
DmEnumValues(CryptoSyncKey,
CpckAddCheckpointCallback,
&Context);
if (Context.fFound) {
//
// This checkpoint already exists.
//
ClRtlLogPrint(LOG_UNUSUAL,
"[CPCK] CpckAddCryptoCheckpoint: failing attempt to add duplicate "
"checkpoint for resource %1!ws!, container %2!ws! (provider: %3!ws!)\n",
OmObjectName(Resource),
CryptoKeyInfo.pwszContainer,
CryptoKeyInfo.pwszProvName);
Status = ERROR_ALREADY_EXISTS;
goto Ret;
}
//
// Now we need to find a unique checkpoint ID for this registry subtree.
// Start at 1 and keep trying value names until we get to one that does
// not already exist.
//
for (Id=1; ; Id++) {
DWORD dwType;
DWORD cbData;
wsprintfW(IdName,L"%08lx",Id);
cbData = 0;
Status = DmQueryValue(CryptoSyncKey,
IdName,
&dwType,
NULL,
&cbData);
if (Status == ERROR_FILE_NOT_FOUND) {
//
// Found a free ID.
//
break;
}
}
} else {
//
// The crypto sync reg key was just created, so this must be the only checkpoint
// that exists.
//
Id = 1;
wsprintfW(IdName, L"%08lx",Id);
}
ClRtlLogPrint(LOG_NOISE,
"[CPCK] CpckAddCryptoCheckpoint: creating new checkpoint id %1!d! "
"for resource %2!ws!, container %3!ws! (provider: %4!ws!)\n",
Id,
OmObjectName(Resource),
CryptoKeyInfo.pwszContainer,
CryptoKeyInfo.pwszProvName);
Status = DmSetValue(CryptoSyncKey,
IdName,
REG_BINARY,
(CONST BYTE *)InBuffer,
InBufferSize);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_CRITICAL,
"[CPCK] CpckAddCryptoCheckpoint: failed to create new checkpoint id %1!d! "
"for resource %2!ws!, container %3!ws! (provider: %4!ws!)\n",
Id,
OmObjectName(Resource),
CryptoKeyInfo.pwszContainer,
CryptoKeyInfo.pwszProvName);
goto Ret;
}
RetryCheckpoint:
//
// Take the initial checkpoint
//
Status = CpckCheckpoint(Resource,
hProv,
Id,
&CryptoKeyInfo);
// this may fail due to quorum resource being offline. We could do one of
// two things here, wait for quorum resource to come online or retry. We
// retry as this may be called from the online routines of a resource and
// we dont want to add any circular waits.
if ((Status == ERROR_ACCESS_DENIED) ||
(Status == ERROR_INVALID_FUNCTION) ||
(Status == ERROR_NOT_READY) ||
(Status == RPC_X_INVALID_PIPE_OPERATION) ||
(Status == ERROR_BUSY) ||
(Status == ERROR_SWAPERROR))
{
if (Count--)
{
Sleep(1000);
goto RetryCheckpoint;
}
#if DBG
else
{
if (IsDebuggerPresent())
DebugBreak();
}
#endif
}
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_CRITICAL,
"[CPCK] CpckAddCryptoCheckpoint: failed to take initial checkpoint for "
"resource %1!ws!, container %2!ws! (provider: %3!ws!), error %4!d!\n",
OmObjectName(Resource),
CryptoKeyInfo.pwszContainer,
CryptoKeyInfo.pwszProvName,
Status);
goto Ret;
}
Ret:
FreeCryptoKeyInfo(&CryptoKeyInfo);
if (hProv)
CryptReleaseContext(hProv, 0);
if (CryptoSyncKey)
DmCloseKey(CryptoSyncKey);
return(Status);
} // CpckAddCryptoCheckpoint
BOOL
CpckAddCheckpointCallback(
IN LPWSTR ValueName,
IN LPVOID ValueData,
IN DWORD ValueType,
IN DWORD ValueSize,
IN PCPCK_ADD_CONTEXT Context
)
/*++
Routine Description:
Value enumeration callback for adding a new registry
checkpoint subtrees. This is only used to see if the specified
registry subtree is already being watched.
Arguments:
ValueName - Supplies the name of the value (this is the checkpoint ID)
ValueData - Supplies the value data (this is the crypto key information)
ValueType - Supplies the value type (must be REG_BINARY)
ValueSize - Supplies the size of ValueData
Context - Supplies the callback context
Return Value:
TRUE to continue enumeration
FALSE if a match is found and enumeration should be stopped
--*/
{
if (memcmp(ValueData, Context->pbInfo, Context->cbInfo) == 0) {
//
// Found a match
//
Context->fFound = TRUE;
return(FALSE);
}
return(TRUE);
} // CpckAddCheckpointCallback
DWORD
CpckDeleteCryptoCheckpoint(
IN PFM_RESOURCE Resource,
IN PVOID InBuffer,
IN DWORD InBufferSize
)
/*++
Routine Description:
Removes a crypto key checkpoint from a resource's list.
Arguments:
Resource - supplies the resource the registry checkpoint should be added to.
InBuffer - Supplies the crypto key information (always CRYPT_MACHINE_KEYSET)
InBufferSize - Supplies the length of InBuffer
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
CPCK_DEL_CONTEXT Context;
HDMKEY ResourceKey;
HDMKEY CryptoSyncKey;
DWORD Status;
WCHAR ValueId[9];
LPWSTR pszFileName=NULL;
LPWSTR pszDirectoryName=NULL;
CLUSTER_RESOURCE_STATE State;
//
// Open up the resource's key
//
ResourceKey = DmOpenKey(DmResourcesKey,
OmObjectId(Resource),
KEY_READ);
CL_ASSERT(ResourceKey != NULL);
//
// Open up the CryptoSync key
//
CryptoSyncKey = DmOpenKey(ResourceKey,
L"CryptoSync",
KEY_READ | KEY_WRITE);
DmCloseKey(ResourceKey);
if (CryptoSyncKey == NULL) {
Status = GetLastError();
ClRtlLogPrint(LOG_NOISE,
"[CPCK] CpckDeleteCryptoCheckpoint - couldn't open CryptoSync key error %1!d!\n",
Status);
return(Status);
}
//
// Enumerate all the values to find this one
//
Context.dwId = 0;
Context.pbInfo = InBuffer;
Context.cbInfo = InBufferSize;
DmEnumValues(CryptoSyncKey,
CpckDeleteCheckpointCallback,
&Context);
if (Context.dwId == 0) {
//
// The specified tree was not found.
//
DmCloseKey(CryptoSyncKey);
return(ERROR_FILE_NOT_FOUND);
}
wsprintfW(ValueId,L"%08lx",Context.dwId);
Status = DmDeleteValue(CryptoSyncKey, ValueId);
DmCloseKey(CryptoSyncKey);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_CRITICAL,
"[CPCK] CpckDeleteCryptoCheckpoint - couldn't delete value %1!ws! error %2!d!\n",
ValueId,
Status);
return(Status);
}
//delete the file corresponding to this checkpoint
Status = CpckDeleteCryptoFile(Resource, Context.dwId, NULL);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_CRITICAL,
"[CPCK] CpckDeleteCryptoCheckpoint - couldn't delete checkpoint file , error %1!d!\n",
Status);
return(Status);
}
return(Status);
} // CpckDeleteCryptoCheckpoint
DWORD
CpckRemoveResourceCheckpoints(
IN PFM_RESOURCE Resource
)
/*++
Routine Description:
This is called when a resource is deleted to remove all the checkpoints
and the related stuff in the registry.
Arguments:
Resource - supplies the resource
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
DWORD Status;
//delete all the checkpoints corresponding to this resource
Status = CpckDeleteCryptoFile(Resource, 0, NULL);
if (Status != ERROR_SUCCESS)
{
ClRtlLogPrint(LOG_CRITICAL,
"[CPCK] CpckRemoveResourceCheckpoints, CpckDeleteCheckpointFile failed %1!d!\n",
Status);
goto FnExit;
}
FnExit:
return(Status);
} // CpckRemoveResourceCheckpoints
BOOL
CpckDeleteCheckpointCallback(
IN LPWSTR ValueName,
IN LPVOID ValueData,
IN DWORD ValueType,
IN DWORD ValueSize,
IN PCPCK_DEL_CONTEXT Context
)
/*++
Routine Description:
Value enumeration callback for deleting an old registry
checkpoint subtrees.
Arguments:
ValueName - Supplies the name of the value (this is the checkpoint ID)
ValueData - Supplies the value data (this is the crypto info)
ValueType - Supplies the value type (must be REG_BINARY)
ValueSize - Supplies the size of ValueData
Context - Supplies the callback context
Return Value:
TRUE to continue enumeration
FALSE if a match is found and enumeration should be stopped
--*/
{
if (memcmp(ValueData, Context->pbInfo, Context->cbInfo) == 0) {
//
// Found a match
//
Context->dwId = wcstol(ValueName, NULL, 16); // skip past the 'Crypto' prefix
return(FALSE);
}
return(TRUE);
} // CpckDeleteCheckpointCallback
DWORD
CpckGetCryptoCheckpoints(
IN PFM_RESOURCE Resource,
OUT PUCHAR OutBuffer,
IN DWORD OutBufferSize,
OUT LPDWORD BytesReturned,
OUT LPDWORD Required
)
/*++
Routine Description:
Retrieves a list of the resource's crypto checkpoints
Arguments:
Resource - Supplies the resource whose crypto checkpoints should be retrieved.
OutBuffer - Supplies a pointer to the output buffer.
OutBufferSize - Supplies the size (in bytes) of the output buffer.
BytesReturned - Returns the number of bytes written to the output buffer.
Required - Returns the number of bytes required. (if the output buffer was insufficient)
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
CPCK_GET_CONTEXT Context;
HDMKEY ResourceKey;
HDMKEY CryptoSyncKey;
DWORD Status;
*BytesReturned = 0;
*Required = 0;
//
// Open up the resource's key
//
ResourceKey = DmOpenKey(DmResourcesKey,
OmObjectId(Resource),
KEY_READ);
CL_ASSERT(ResourceKey != NULL);
//
// Open up the CryptoSync key
//
CryptoSyncKey = DmOpenKey(ResourceKey,
L"CryptoSync",
KEY_READ | KEY_WRITE);
DmCloseKey(ResourceKey);
if (CryptoSyncKey == NULL) {
//
// No reg sync key, therefore there are no subtrees
//
return(ERROR_SUCCESS);
}
Context.cCheckpoints = 0;
ZeroMemory(OutBuffer, OutBufferSize);
Context.cbRequired = sizeof(WCHAR);
if (OutBufferSize < sizeof(WCHAR) * 2)
{
Context.fNeedMoreData = TRUE;
Context.cbAvailable = 0;
*BytesReturned = 0;
}
else
{
Context.fNeedMoreData = FALSE;
Context.cbAvailable = OutBufferSize - sizeof(WCHAR);
Context.pbOutput = (BYTE*)(OutBuffer);
*BytesReturned = sizeof(WCHAR) * 2;
}
DmEnumValues(CryptoSyncKey,
CpckGetCheckpointsCallback,
&Context);
DmCloseKey(CryptoSyncKey);
if ((0 != Context.cCheckpoints) && Context.fNeedMoreData) {
Status = ERROR_MORE_DATA;
} else {
Status = ERROR_SUCCESS;
}
if ( 0 == Context.cCheckpoints ) {
*BytesReturned = 0;
*Required = 0;
} else {
if ( Context.fNeedMoreData ) {
*Required = Context.cbRequired;
} else {
*BytesReturned = (DWORD)(Context.pbOutput - OutBuffer);
}
}
return(Status);
} // CpckGetCryptoCheckpoints
BOOL
CpckGetCheckpointsCallback(
IN LPWSTR ValueName,
IN LPVOID ValueData,
IN DWORD ValueType,
IN DWORD ValueSize,
IN PCPCK_GET_CONTEXT Context
)
/*++
Routine Description:
Value enumeration callback for retrieving all of a resource's
checkpoint subtrees.
Arguments:
ValueName - Supplies the name of the value (this is the checkpoint ID)
ValueData - Supplies the value data (this is the crypto info)
ValueType - Supplies the value type (must be REG_BINARY)
ValueSize - Supplies the size of ValueData
Context - Supplies the callback context
Return Value:
TRUE to continue enumeration
--*/
{
Context->cbRequired += ValueSize;
Context->cCheckpoints++;
if (Context->cbAvailable >= ValueSize) {
CopyMemory(Context->pbOutput, ValueData, ValueSize);
Context->pbOutput += ValueSize;
Context->cbAvailable -= ValueSize;
} else {
Context->fNeedMoreData = TRUE;
}
return(TRUE);
} // CpckGetCheckpointsCallback
DWORD
CpckGenSymKey(
IN HCRYPTPROV hProv,
IN BYTE *pbSalt,
IN BYTE *pbIV,
OUT HCRYPTKEY *phSymKey
)
/*++
Routine Description:
Generate a session key based on the specified Salt and IV.
Arguments:
hProv - Handle to the crypto provider (key container)
pbSalt - Salt value
pbIV - IV value
phSymKey - Resulting symmetric key (CALG_RC2)
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
HCRYPTHASH hHash = 0;
DWORD cbPassword = 0;
DWORD Status;
if (!CryptCreateHash(hProv,
CALG_SHA1,
0,
0,
&hHash))
{
Status = GetLastError();
goto Ret;
}
if (!CryptHashData(hHash,
pbSalt,
SALT_SIZE,
0))
{
Status = GetLastError();
goto Ret;
}
// derive the key from the hash
if (!CryptDeriveKey(hProv,
CALG_RC2,
hHash,
0,
phSymKey))
{
Status = GetLastError();
goto Ret;
}
// set the IV on the key
if (!CryptSetKeyParam(*phSymKey,
KP_IV,
pbIV,
0))
{
Status = GetLastError();
goto Ret;
}
Status = ERROR_SUCCESS;
Ret:
if (hHash)
CryptDestroyHash(hHash);
return (Status);
} // CpckGenSymKey
DWORD
CpckExportPrivateKey(
IN HCRYPTPROV hProv,
IN HCRYPTKEY hKey,
IN BYTE *pbIV,
IN BYTE *pbSalt,
OUT BYTE *pbExportedKey,
OUT DWORD *pcbExportedKey
)
/*++
Routine Description:
Exports the private key data.
Arguments:
hProv - Handle to the crypto provider (key container)
hKey - Handle to the key to export
pbIV - IV for the symmetric key
pbSalt - Salt to generate symmetric key
pbExportedKey - Supplies the buffer the key is to be exported into
pcbExportedKey - Supplies the length of the buffer, if pbExportedKey is
NULL then this will be the length of the key to export
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
HCRYPTKEY hSymKey = 0;
DWORD Status;
// create the symmetric key to encrypt the private key with
Status = CpckGenSymKey(hProv,
pbSalt,
pbIV,
&hSymKey);
if (0 != Status)
{
goto Ret;
}
// Export the key
if (!CryptExportKey(hKey,
hSymKey,
PRIVATEKEYBLOB,
0,
pbExportedKey,
pcbExportedKey))
{
Status = GetLastError();
goto Ret;
}
Status = ERROR_SUCCESS;
Ret:
if (hSymKey)
CryptDestroyKey(hSymKey);
return (Status);
} // CpckExportPrivateKey
DWORD
CpckGetKeyContainerSecDescr(
IN HCRYPTPROV hProv,
OUT PSECURITY_DESCRIPTOR *ppSecDescr,
OUT DWORD *pcbSecDescr
)
/*++
Routine Description:
Gets the key container security descriptor so that when replicated
the same descriptor may be set on the replicated key.
Arguments:
hProv - Handle to the crypto provider (key container)
ppSecDescr - Pointer to buffer holding security descriptor
pcbSecDescr - Pointer to the length of the returned security descriptor
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
PTOKEN_PRIVILEGES pPrevPriv = NULL;
DWORD cbPrevPriv = 0;
BYTE rgbNewPriv[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];
PTOKEN_PRIVILEGES pNewPriv = (PTOKEN_PRIVILEGES)rgbNewPriv;
SECURITY_DESCRIPTOR_CONTROL Control;
DWORD dwRevision;
HANDLE hToken = 0;
PSECURITY_DESCRIPTOR pNewSD = NULL;
DWORD dw;
DWORD dwFlags;
DWORD Status = ERROR_SUCCESS;
// check if there is a thread token
if (FALSE == OpenThreadToken(GetCurrentThread(),
MAXIMUM_ALLOWED,
TRUE,
&hToken))
{
// get the process token
if (FALSE == OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken))
{
Status = GetLastError();
goto Ret;
}
}
// set up the new priviledge state
memset(rgbNewPriv, 0, sizeof(rgbNewPriv));
pNewPriv->PrivilegeCount = 1;
if(!LookupPrivilegeValueW(NULL, SE_SECURITY_NAME,
&(pNewPriv->Privileges[0].Luid)))
{
goto Ret;
}
pNewPriv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// get the length of the previous state
AdjustTokenPrivileges(hToken,
FALSE,
(PTOKEN_PRIVILEGES)pNewPriv,
sizeof(dw),
(PTOKEN_PRIVILEGES)&dw,
&cbPrevPriv);
// alloc for the previous state
if (NULL == (pPrevPriv = (PTOKEN_PRIVILEGES)LocalAlloc(LMEM_ZEROINIT,
cbPrevPriv)))
{
Status = ERROR_NOT_ENOUGH_MEMORY;
goto Ret;
}
// adjust the priviledge and get the previous state
if (!AdjustTokenPrivileges(hToken,
FALSE,
pNewPriv,
cbPrevPriv,
(PTOKEN_PRIVILEGES)pPrevPriv,
&cbPrevPriv))
{
Status = GetLastError();
goto Ret;
}
dwFlags = OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION |
SACL_SECURITY_INFORMATION ;
// get the security descriptor
if (CryptGetProvParam(hProv,
PP_KEYSET_SEC_DESCR,
NULL,
pcbSecDescr,
dwFlags))
{
if (NULL != (*ppSecDescr =
(PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_ZEROINIT,
*pcbSecDescr)))
{
if (!CryptGetProvParam(hProv,
PP_KEYSET_SEC_DESCR,
(BYTE*)(*ppSecDescr),
pcbSecDescr,
dwFlags))
{
Status = GetLastError();
}
}
else
{
Status = ERROR_NOT_ENOUGH_MEMORY;
}
}
else
{
Status = GetLastError();
}
// adjust the priviledge to the previous state
if (!AdjustTokenPrivileges(hToken,
FALSE,
pPrevPriv,
0,
NULL,
NULL))
{
Status = GetLastError();
goto Ret;
}
if (ERROR_SUCCESS != Status)
{
goto Ret;
}
// ge the control on the security descriptor to check if self relative
if (!GetSecurityDescriptorControl(*ppSecDescr,
&Control,
&dwRevision))
{
Status = GetLastError();
goto Ret;
}
// if not self relative then make a self relative copy
if (!(SE_SELF_RELATIVE & Control))
{
if (NULL == (pNewSD =
(PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_ZEROINIT,
*pcbSecDescr)))
{
Status = ERROR_NOT_ENOUGH_MEMORY;
goto Ret;
}
if (!MakeSelfRelativeSD(*ppSecDescr,
pNewSD,
pcbSecDescr))
{
Status = GetLastError();
goto Ret;
}
LocalFree(*ppSecDescr);
*ppSecDescr = (BYTE*)pNewSD;
pNewSD = NULL;
}
Status = ERROR_SUCCESS;
Ret:
if (pPrevPriv)
LocalFree(pPrevPriv);
if (pNewSD)
LocalFree(pNewSD);
if (hToken)
CloseHandle(hToken);
return Status;
} // CpckGetKeyContainerSecDescr
DWORD
CpckStoreKeyContainer(
IN HCRYPTPROV hProv,
IN CRYPTO_KEY_INFO *pCryptoKeyInfo,
IN LPWSTR TempFile
)
/*++
Routine Description:
Writes the key container associated with the provider handle to a
the specified file.
Arguments:
hProv - Handle to the crypto provider (key container)
pCryptoKeyInfo - Crypto key information (password if given)
TempFile - Supplies the file which the key data is to be written to
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
CRYPTO_KEY_FILE_DATA KeyFileData;
HCRYPTKEY hSigKey = 0;
HCRYPTKEY hExchKey = 0;
BYTE *pb = NULL;
DWORD cb = 0;
DWORD dwBytesWritten;
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD dwPermissions = 0;
DWORD cbPermissions ;
PSECURITY_DESCRIPTOR pSecDescr = NULL;
DWORD cbSecDescr;
DWORD Status;
memset(&KeyFileData, 0, sizeof(KeyFileData));
// generate the necessary random data for salt and IVs.
// calls with the sig IV buffer but this will fill the
// exch IV and Salt as well since the buffer len is 32
if (!CryptGenRandom(hProv,
sizeof(struct _CRYPTO_KEY_FILE_INITIALIZATION_DATA),
KeyFileData.rgbSigIV))
{
Status = GetLastError();
goto Ret;
}
KeyFileData.dwVersion = CRYPTO_KEY_FILE_DATA_VERSION;
// calculate the length of key data
cb = sizeof(KeyFileData);
// get the self relative security descriptor
Status = CpckGetKeyContainerSecDescr(hProv,
&pSecDescr,
&cbSecDescr);
if (ERROR_SUCCESS != Status)
{
goto Ret;
}
cb += cbSecDescr;
// get sig key length if necessary
if (CryptGetUserKey(hProv, AT_SIGNATURE, &hSigKey))
{
// check if key is exportable
cbPermissions = sizeof(DWORD);
if (!CryptGetKeyParam(hSigKey,
KP_PERMISSIONS,
(BYTE*)&dwPermissions,
&cbPermissions,
0))
{
Status = GetLastError();
goto Ret;
}
if (!(dwPermissions & CRYPT_EXPORT))
{
Status = (DWORD)NTE_BAD_KEY;
goto Ret;
}
// get the sig key length
Status = CpckExportPrivateKey(hProv,
hSigKey,
KeyFileData.rgbSigIV,
KeyFileData.rgbSalt,
NULL,
&(KeyFileData.cbSig));
if (0 != Status)
{
goto Ret;
}
cb += KeyFileData.cbSig;
}
// get key exchange key length if necessary
if (CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hExchKey))
{
// check if key is exportable
dwPermissions = 0;
cbPermissions = sizeof(DWORD);
if (!CryptGetKeyParam(hExchKey,
KP_PERMISSIONS,
(BYTE*)&dwPermissions,
&cbPermissions,
0))
{
Status = GetLastError();
goto Ret;
}
if (!(dwPermissions & CRYPT_EXPORT))
{
Status = (DWORD)NTE_BAD_KEY;
goto Ret;
}
// get the exchange key length
Status = CpckExportPrivateKey(hProv,
hExchKey,
KeyFileData.rgbExchIV,
KeyFileData.rgbSalt,
NULL,
&(KeyFileData.cbExch));
if (0 != Status)
{
goto Ret;
}
cb += KeyFileData.cbExch;
}
// allocate space for the keys
if (NULL == (pb = LocalAlloc(LMEM_ZEROINIT, cb)))
{
Status = ERROR_NOT_ENOUGH_MEMORY;
goto Ret;
}
// copy the key file data into the pb
cb = sizeof(KeyFileData);
// copy the signature key
if (0 != hSigKey)
{
Status = CpckExportPrivateKey(hProv,
hSigKey,
KeyFileData.rgbSigIV,
KeyFileData.rgbSalt,
pb + cb,
&(KeyFileData.cbSig));
if (0 != Status)
{
goto Ret;
}
cb += KeyFileData.cbSig;
}
// copy the key exchange key
if (0 != hExchKey)
{
Status = CpckExportPrivateKey(hProv,
hExchKey,
KeyFileData.rgbExchIV,
KeyFileData.rgbSalt,
pb + cb,
&(KeyFileData.cbExch));
if (0 != Status)
{
goto Ret;
}
cb += KeyFileData.cbExch;
}
// copy the security descriptor
CopyMemory(pb + cb, (BYTE*)pSecDescr, cbSecDescr);
cb += cbSecDescr;
// copy the lengths
CopyMemory(pb, &KeyFileData, sizeof(KeyFileData));
// write the buffer to the file
hFile = CreateFileW(TempFile,
GENERIC_WRITE,
0,
NULL,
OPEN_ALWAYS,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
Status = GetLastError();
goto Ret;
}
if (!WriteFile(hFile, pb, cb, &dwBytesWritten, NULL))
{
Status = GetLastError();
goto Ret;
}
Status = ERROR_SUCCESS;
Ret:
if (pSecDescr)
LocalFree(pSecDescr);
if(INVALID_HANDLE_VALUE != hFile)
CloseHandle(hFile);
if (pb)
LocalFree(pb);
if (hSigKey)
CryptDestroyKey(hSigKey);
if (hExchKey)
CryptDestroyKey(hExchKey);
return (Status);
} // CpckStoreKeyContainer
DWORD
CpckSaveCheckpointToFile(
IN HCRYPTPROV hProv,
IN CRYPTO_KEY_INFO *pCryptoKeyInfo,
IN LPWSTR TempFile)
/*++
Routine Description:
have DM create a temp file and call the routine that exports the keys and
writes the checkpoint file.
Arguments:
hProv - Handle to the crypto provider (key container)
pCryptoKeyInfo - Crypto key information (password if given)
TempFile - Supplies the file which the key data is to be written to
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
DWORD Status;
Status = DmCreateTempFileName(TempFile);
if (Status != ERROR_SUCCESS) {
CL_UNEXPECTED_ERROR( Status );
TempFile[0] = L'\0';
return(Status);
}
// put the key information into the file
Status = CpckStoreKeyContainer(hProv, pCryptoKeyInfo, TempFile);
if (Status != ERROR_SUCCESS)
{
ClRtlLogPrint(LOG_UNUSUAL,
"[CPCK] CpckSaveCheckpointToFile failed to get store container %1!ws! %2!ws! to file %3!ws! error %4!d!\n",
pCryptoKeyInfo->pwszContainer,
pCryptoKeyInfo->pwszProvName,
TempFile,
Status);
CL_LOGFAILURE(Status);
DeleteFile(TempFile);
TempFile[0] = L'\0';
}
return(Status);
} // CpckSaveCheckpointToFile
DWORD
CpckCheckpoint(
IN PFM_RESOURCE Resource,
IN HCRYPTPROV hProv,
IN DWORD dwId,
IN CRYPTO_KEY_INFO *pCryptoKeyInfo
)
/*++
Routine Description:
Takes a checkpoint of the specified crypto key.
Arguments:
Resource - Supplies the resource this is a checkpoint for.
hKey - Supplies the crypto info to checkpoint
dwId - Supplies the checkpoint ID.
KeyName - Supplies the name of the registry key.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
DWORD Status;
WCHAR TempFile[MAX_PATH];
Status = CpckSaveCheckpointToFile(hProv, pCryptoKeyInfo, TempFile);
if (Status == ERROR_SUCCESS)
{
//
// Got a file with the right bits in it. Checkpoint the
// file.
//
Status = CpSaveDataFile(Resource,
dwId,
TempFile,
TRUE);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_CRITICAL,
"[CPCK] CpckCheckpoint - CpSaveData failed %1!d!\n",
Status);
}
}
//if the file was created, delete it
if (TempFile[0] != L'\0')
DeleteFile(TempFile);
return(Status);
} // CpckCheckpoint
DWORD
CpckSetKeyContainerSecDescr(
IN HCRYPTPROV hProv,
IN BYTE *pbSecDescr,
IN DWORD cbSecDescr
)
/*++
Routine Description:
Sets the key container security descriptor.
Arguments:
hProv - Handle to the crypto provider (key container)
pbSecDescr - Buffer holding security descriptor
cbSecDescr - Length of the security descriptor
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
PTOKEN_PRIVILEGES pPrevPriv = NULL;
DWORD cbPrevPriv = 0;
BYTE rgbNewPriv[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];
PTOKEN_PRIVILEGES pNewPriv = (PTOKEN_PRIVILEGES)rgbNewPriv;
HANDLE hToken = 0;
DWORD dw;
DWORD dwFlags;
DWORD Status = ERROR_SUCCESS;
// check if there is a thread token
if (FALSE == OpenThreadToken(GetCurrentThread(),
MAXIMUM_ALLOWED,
TRUE,
&hToken))
{
// get the process token
if (FALSE == OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken))
{
Status = GetLastError();
goto Ret;
}
}
// set up the new priviledge state
memset(rgbNewPriv, 0, sizeof(rgbNewPriv));
pNewPriv->PrivilegeCount = 1;
if(!LookupPrivilegeValueW(NULL, SE_SECURITY_NAME,
&(pNewPriv->Privileges[0].Luid)))
{
goto Ret;
}
pNewPriv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// get the length of the previous state
AdjustTokenPrivileges(hToken,
FALSE,
(PTOKEN_PRIVILEGES)pNewPriv,
sizeof(dw),
(PTOKEN_PRIVILEGES)&dw,
&cbPrevPriv);
// alloc for the previous state
if (NULL == (pPrevPriv = (PTOKEN_PRIVILEGES)LocalAlloc(LMEM_ZEROINIT,
cbPrevPriv)))
{
Status = ERROR_NOT_ENOUGH_MEMORY;
goto Ret;
}
// adjust the priviledge and get the previous state
AdjustTokenPrivileges(hToken,
FALSE,
pNewPriv,
cbPrevPriv,
(PTOKEN_PRIVILEGES)pPrevPriv,
&cbPrevPriv);
dwFlags = OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION |
SACL_SECURITY_INFORMATION ;
// get the security descriptor
if (!CryptSetProvParam(hProv,
PP_KEYSET_SEC_DESCR,
pbSecDescr,
dwFlags))
{
Status = GetLastError();
}
// adjust the priviledge to the previous state
if (!AdjustTokenPrivileges(hToken,
FALSE,
pPrevPriv,
0,
NULL,
NULL))
{
Status = GetLastError();
goto Ret;
}
if (ERROR_SUCCESS != Status)
{
goto Ret;
}
Status = ERROR_SUCCESS;
Ret:
if (pPrevPriv)
LocalFree(pPrevPriv);
if (hToken)
CloseHandle(hToken);
return Status;
} // CpckSetKeyContainerSecDescr
DWORD
CpckImportPrivateKey(
IN HCRYPTPROV hProv,
IN BYTE *pbIV,
IN BYTE *pbSalt,
IN BYTE *pbKey,
IN DWORD cbKey
)
/*++
Routine Description:
Exports the private key data.
Arguments:
hProv - Handle to the crypto provider (key container)
hKey - Handle to the key to export
pbIV - IV for the symmetric key
pbSalt - Salt to generate symmetric key
pbKey - Supplies the buffer the key is in
cbKey - Supplies the length of the key buffer
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
BOOLEAN WasEnabled;
HCRYPTKEY hSymKey = 0;
HCRYPTKEY hKey = 0;
DWORD Status = ERROR_SUCCESS;
// create the symmetric key to encrypt the private key with
Status = CpckGenSymKey(hProv,
pbSalt,
pbIV,
&hSymKey);
if (0 != Status)
{
goto Ret;
}
// Import the key
if (!CryptImportKey(hProv,
pbKey,
cbKey,
hSymKey,
CRYPT_EXPORTABLE,
&hKey))
{
// if failed then try with BACKUP/RESTORE
ClRtlEnableThreadPrivilege(SE_RESTORE_PRIVILEGE,
&WasEnabled);
if (!CryptImportKey(hProv,
pbKey,
cbKey,
hSymKey,
CRYPT_EXPORTABLE,
&hKey))
{
Status = GetLastError();
}
ClRtlRestoreThreadPrivilege(SE_RESTORE_PRIVILEGE,
WasEnabled);
}
Ret:
if (hSymKey)
CryptDestroyKey(hSymKey);
if (hKey)
CryptDestroyKey(hKey);
return (Status);
} // CpckImportPrivateKey
DWORD
CpckInstallKeyContainer(
IN HCRYPTPROV hProv,
IN LPWSTR FileName
)
/*++
Routine Description:
Installs new crypto key information from a specified file.
Arguments:
hProv - Supplies the provider handle where FileName will be installed to.
FileName - The name of the file from which to read the crypto key info
to install.
Return Value:
ERROR_SUCCESS if the installation completed successfully
Win32 error code otherwise.
--*/
{
HANDLE hMap = NULL;
BYTE *pbFile = NULL;
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD cbFile = 0;
DWORD *pdwVersion;
CRYPTO_KEY_FILE_DATA *pKeyFileData;
DWORD Status;
// read the key data from the file
hFile = CreateFileW(FileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
Status = GetLastError();
goto Ret;
}
if (0xFFFFFFFF == (cbFile = GetFileSize(hFile, NULL)))
{
Status = GetLastError();
goto Ret;
}
if (sizeof(CRYPTO_KEY_FILE_DATA) > cbFile)
{
Status = ERROR_FILE_INVALID;
goto Ret;
}
if (NULL == (hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY,
0, 0, NULL)))
{
Status = GetLastError();
goto Ret;
}
if (NULL == (pbFile = (BYTE*)MapViewOfFile(hMap, FILE_MAP_READ,
0, 0, 0 )))
{
Status = GetLastError();
goto Ret;
}
// get the length information out of the file
pKeyFileData = (CRYPTO_KEY_FILE_DATA*)pbFile;
if (CRYPTO_KEY_FILE_DATA_VERSION != pKeyFileData->dwVersion)
{
Status = ERROR_FILE_INVALID;
goto Ret;
}
if ((sizeof(CRYPTO_KEY_FILE_DATA) + pKeyFileData->cbSig +
pKeyFileData->cbExch) > cbFile)
{
Status = ERROR_FILE_INVALID;
goto Ret;
}
if (pKeyFileData->cbSig)
{
// import the sig key if there is one
Status = CpckImportPrivateKey(hProv,
pKeyFileData->rgbSigIV,
pKeyFileData->rgbSalt,
pbFile + sizeof(CRYPTO_KEY_FILE_DATA),
pKeyFileData->cbSig);
if (0 != Status)
{
goto Ret;
}
}
if (pKeyFileData->cbExch)
{
// import the exch key if there is one
Status = CpckImportPrivateKey(hProv,
pKeyFileData->rgbExchIV,
pKeyFileData->rgbSalt,
pbFile + sizeof(CRYPTO_KEY_FILE_DATA) +
pKeyFileData->cbSig,
pKeyFileData->cbExch);
if (0 != Status)
{
goto Ret;
}
}
Status = CpckSetKeyContainerSecDescr(hProv,
pbFile + sizeof(CRYPTO_KEY_FILE_DATA) +
pKeyFileData->cbSig + pKeyFileData->cbExch,
pKeyFileData->cbSecDescr);
if (ERROR_SUCCESS != Status)
{
goto Ret;
}
Status = ERROR_SUCCESS;
Ret:
if(pbFile)
UnmapViewOfFile(pbFile);
if(hMap)
CloseHandle(hMap);
if(INVALID_HANDLE_VALUE != hFile)
CloseHandle(hFile);
return(Status);
} // CpckInstallKeyContainer
DWORD
CpckDeleteFile(
IN PFM_RESOURCE Resource,
IN DWORD dwCheckpointId,
IN OPTIONAL LPCWSTR lpszQuorumPath
)
/*++
Routine Description:
Gets the file corresponding to the checkpoint id relative
to the supplied path and deletes it.
Arguments:
PFM_RESOURCE - Supplies the pointer to the resource.
dwCheckpointId - The checkpoint id to be deleted. If 0, all
checkpoints are deleted.
lpszQuorumPath - If specified, the checkpoint file relative
to this path is deleted.
Return Value:
ERROR_SUCCESS if the completed successfully
Win32 error code otherwise.
--*/
{
DWORD Status;
LPWSTR pszFileName=NULL;
LPWSTR pszDirectoryName=NULL;
Status = CppGetCheckpointFile(Resource, dwCheckpointId,
&pszDirectoryName, &pszFileName, lpszQuorumPath, TRUE);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_CRITICAL,
"[CPCK] CpckDeleteFile- couldnt get checkpoint file name, error %1!d!\n",
Status);
goto FnExit;
}
if (!DeleteFileW(pszFileName))
{
Status = GetLastError();
ClRtlLogPrint(LOG_CRITICAL,
"[CPCK] CpckDeleteFile - couldn't delete the file, error %1!d!\n",
Status);
goto FnExit;
}
//
// Now try and delete the directory.
//
if (!RemoveDirectoryW(pszDirectoryName))
{
//if there is a failure, we still return success
//because it may not be possible to delete a directory
//when it is not empty
ClRtlLogPrint(LOG_UNUSUAL,
"[CPCK] CpckDeleteFile- unable to remove directory %1!ws!, error %2!d!\n",
pszDirectoryName,
GetLastError());
}
FnExit:
if (pszFileName)
LocalFree(pszFileName);
if (pszDirectoryName)
LocalFree(pszDirectoryName);
return(Status);
} // CpckDeleteFile
BOOL
CpckRemoveCheckpointFileCallback(
IN LPWSTR ValueName,
IN LPVOID ValueData,
IN DWORD ValueType,
IN DWORD ValueSize,
IN PCP_CALLBACK_CONTEXT Context
)
/*++
Routine Description:
Registry value enumeration callback used when the quorum resource
is changing. Deletes the specified checkpoint file from the old
quorum directory.
Arguments:
ValueName - Supplies the name of the value (this is the checkpoint ID)
ValueData - Supplies the value data (this is the crypto info)
ValueType - Supplies the value type (must be REG_BINARY)
ValueSize - Supplies the size of ValueData
Context - Supplies the quorum change context (old path and resource)
Return Value:
TRUE to continue enumeration
--*/
{
DWORD Status;
DWORD Id;
Id = wcstol(ValueName, NULL, 16);
if (Id == 0) {
ClRtlLogPrint(LOG_UNUSUAL,
"[CPCK] CpckRemoveCheckpointFileCallback invalid checkpoint ID %1!ws! for resource %2!ws!\n",
ValueName,
OmObjectName(Context->Resource));
return(TRUE);
}
Status = CpckDeleteFile(Context->Resource, Id, Context->lpszPathName);
return(TRUE);
} // CpckRemoveCheckpointFileCallback
DWORD
CpckDeleteCheckpointFile(
IN PFM_RESOURCE Resource,
IN DWORD dwCheckpointId,
IN OPTIONAL LPCWSTR lpszQuorumPath
)
/*++
Routine Description:
Deletes the checkpoint file corresponding the resource.
This node must be the owner of the quorum resource
Arguments:
PFM_RESOURCE - Supplies the pointer to the resource.
dwCheckpointId - The checkpoint id to be deleted. If 0, all
checkpoints are deleted.
lpszQuorumPath - If specified, the checkpoint file relative
to this path is deleted.
Return Value:
ERROR_SUCCESS if completed successfully
Win32 error code otherwise.
--*/
{
DWORD Status;
HDMKEY ResourceKey;
HDMKEY CryptoSyncKey;
CP_CALLBACK_CONTEXT Context;
if (dwCheckpointId)
{
Status = CpckDeleteFile(Resource, dwCheckpointId, lpszQuorumPath);
}
else
{
HDMKEY ResourceKey;
HDMKEY CryptoSyncKey;
CP_CALLBACK_CONTEXT Context;
//delete all checkpoints corresponding to this resource
//
// Open up the resource's key
//
ResourceKey = DmOpenKey(DmResourcesKey,
OmObjectId(Resource),
KEY_READ);
CL_ASSERT(ResourceKey != NULL);
//
// Open up the CryptoSync key
//
CryptoSyncKey = DmOpenKey(ResourceKey,
L"CryptoSync",
KEY_READ | KEY_WRITE);
DmCloseKey(ResourceKey);
if (CryptoSyncKey == NULL)
{
Status = GetLastError();
ClRtlLogPrint(LOG_NOISE,
"[CPCK] CpckDeleteCheckpointFile- couldn't open CryptoSync key error %1!d!\n",
Status);
goto FnExit;
}
Context.lpszPathName = lpszQuorumPath;
Context.Resource = Resource;
//
// Enumerate all the values and delete them one by one.
//
DmEnumValues(CryptoSyncKey,
CpckRemoveCheckpointFileCallback,
&Context);
}
FnExit:
return(Status);
} // CpckDeleteCheckpointFile
DWORD
CpckDeleteCryptoFile(
IN PFM_RESOURCE Resource,
IN DWORD dwCheckpointId,
IN OPTIONAL LPCWSTR lpszQuorumPath
)
/*++
Routine Description:
This function removes the checkpoint file correspoinding to the
checkpoint id for a given resource from the given directory.
Arguments:
Resource - Supplies the resource associated with this data.
dwCheckpointId - Supplies the unique checkpoint ID describing this data. The caller is responsible
for ensuring the uniqueness of the checkpoint ID.
lpszQuorumPath - Supplies the path of the cluster files on a quorum device.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
CL_NODE_ID OwnerNode;
DWORD Status;
do {
OwnerNode = CppGetQuorumNodeId();
ClRtlLogPrint(LOG_NOISE,
"[CPCK] CpckDeleteCryptoFile: removing checkpoint file for id %1!d! at quorum node %2!d!\n",
dwCheckpointId,
OwnerNode);
if (OwnerNode == NmLocalNodeId)
{
Status = CpckDeleteCheckpointFile(Resource, dwCheckpointId, lpszQuorumPath);
}
else
{
Status = CpDeleteCryptoCheckpoint(Session[OwnerNode],
OmObjectId(Resource),
dwCheckpointId,
lpszQuorumPath);
//talking to an old server, cant perform this function
//ignore the error
if (Status == RPC_S_PROCNUM_OUT_OF_RANGE)
Status = ERROR_SUCCESS;
}
if (Status == ERROR_HOST_NODE_NOT_RESOURCE_OWNER) {
//
// This node no longer owns the quorum resource, retry.
//
ClRtlLogPrint(LOG_UNUSUAL,
"[CPCK] CpckDeleteCryptoFile: quorum owner %1!d! no longer owner\n",
OwnerNode);
}
} while ( Status == ERROR_HOST_NODE_NOT_RESOURCE_OWNER );
return(Status);
} // CpckDeleteCryptoFile