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
|