2646 lines
67 KiB
C
2646 lines
67 KiB
C
/*++
|
||
|
||
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
|