You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1838 lines
46 KiB
1838 lines
46 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
nmutil.c
|
|
|
|
Abstract:
|
|
|
|
Miscellaneous utility routines for the Node Manager component.
|
|
|
|
Author:
|
|
|
|
Mike Massa (mikemas) 26-Oct-1996
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#define UNICODE 1
|
|
|
|
#include "service.h"
|
|
#include "nmp.h"
|
|
#include <ntlsa.h>
|
|
#include <ntmsv1_0.h>
|
|
#include <Wincrypt.h>
|
|
|
|
PVOID NmpEncryptedClusterKey = NULL;
|
|
DWORD NmpEncryptedClusterKeyLength = 0;
|
|
|
|
|
|
|
|
DWORD
|
|
NmpQueryString(
|
|
IN HDMKEY Key,
|
|
IN LPCWSTR ValueName,
|
|
IN DWORD ValueType,
|
|
IN LPWSTR *StringBuffer,
|
|
IN OUT LPDWORD StringBufferSize,
|
|
OUT LPDWORD StringSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads a REG_SZ or REG_MULTI_SZ registry value. If the StringBuffer is
|
|
not large enough to hold the data, it is reallocated.
|
|
|
|
Arguments:
|
|
|
|
Key - Open key for the value to be read.
|
|
|
|
ValueName - Unicode name of the value to be read.
|
|
|
|
ValueType - REG_SZ or REG_MULTI_SZ.
|
|
|
|
StringBuffer - Buffer into which to place the value data.
|
|
|
|
StringBufferSize - Pointer to the size of the StringBuffer. This parameter
|
|
is updated if StringBuffer is reallocated.
|
|
|
|
StringSize - The size of the data returned in StringBuffer, including
|
|
the terminating null character.
|
|
|
|
Return Value:
|
|
|
|
The status of the registry query.
|
|
|
|
Notes:
|
|
|
|
To avoid deadlock with DM, must not be called with NM lock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
DWORD valueType;
|
|
WCHAR *temp;
|
|
DWORD oldBufferSize = *StringBufferSize;
|
|
BOOL noBuffer = FALSE;
|
|
|
|
|
|
if (*StringBufferSize == 0) {
|
|
noBuffer = TRUE;
|
|
}
|
|
|
|
*StringSize = *StringBufferSize;
|
|
|
|
status = DmQueryValue( Key,
|
|
ValueName,
|
|
&valueType,
|
|
(LPBYTE) *StringBuffer,
|
|
StringSize
|
|
);
|
|
|
|
if (status == NO_ERROR) {
|
|
if (!noBuffer ) {
|
|
if (valueType == ValueType) {
|
|
return(NO_ERROR);
|
|
}
|
|
else {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
}
|
|
|
|
status = ERROR_MORE_DATA;
|
|
}
|
|
|
|
if (status == ERROR_MORE_DATA) {
|
|
temp = MIDL_user_allocate(*StringSize);
|
|
|
|
if (temp == NULL) {
|
|
*StringSize = 0;
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
if (!noBuffer) {
|
|
MIDL_user_free(*StringBuffer);
|
|
}
|
|
|
|
*StringBuffer = temp;
|
|
*StringBufferSize = *StringSize;
|
|
|
|
status = DmQueryValue( Key,
|
|
ValueName,
|
|
&valueType,
|
|
(LPBYTE) *StringBuffer,
|
|
StringSize
|
|
);
|
|
|
|
if (status == NO_ERROR) {
|
|
if (valueType == ValueType) {
|
|
return(NO_ERROR);
|
|
}
|
|
else {
|
|
*StringSize = 0;
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpQueryString
|
|
|
|
|
|
//
|
|
// Routines to support the common network configuration code.
|
|
//
|
|
VOID
|
|
ClNetPrint(
|
|
IN ULONG LogLevel,
|
|
IN PCHAR FormatString,
|
|
...
|
|
)
|
|
{
|
|
CHAR buffer[256];
|
|
DWORD bytes;
|
|
va_list argList;
|
|
|
|
va_start(argList, FormatString);
|
|
|
|
bytes = FormatMessageA(
|
|
FORMAT_MESSAGE_FROM_STRING,
|
|
FormatString,
|
|
0,
|
|
0,
|
|
buffer,
|
|
sizeof(buffer),
|
|
&argList
|
|
);
|
|
|
|
va_end(argList);
|
|
|
|
if (bytes != 0) {
|
|
ClRtlLogPrint(LogLevel, "%1!hs!", buffer);
|
|
}
|
|
|
|
return;
|
|
|
|
} // ClNetPrint
|
|
|
|
VOID
|
|
ClNetLogEvent(
|
|
IN DWORD LogLevel,
|
|
IN DWORD MessageId
|
|
)
|
|
{
|
|
CsLogEvent(LogLevel, MessageId);
|
|
|
|
return;
|
|
|
|
} // ClNetLogEvent
|
|
|
|
VOID
|
|
ClNetLogEvent1(
|
|
IN DWORD LogLevel,
|
|
IN DWORD MessageId,
|
|
IN LPCWSTR Arg1
|
|
)
|
|
{
|
|
CsLogEvent1(LogLevel, MessageId, Arg1);
|
|
|
|
return;
|
|
|
|
} // ClNetLogEvent1
|
|
|
|
|
|
VOID
|
|
ClNetLogEvent2(
|
|
IN DWORD LogLevel,
|
|
IN DWORD MessageId,
|
|
IN LPCWSTR Arg1,
|
|
IN LPCWSTR Arg2
|
|
)
|
|
{
|
|
CsLogEvent2(LogLevel, MessageId, Arg1, Arg2);
|
|
|
|
return;
|
|
|
|
} // ClNetLogEvent2
|
|
|
|
|
|
VOID
|
|
ClNetLogEvent3(
|
|
IN DWORD LogLevel,
|
|
IN DWORD MessageId,
|
|
IN LPCWSTR Arg1,
|
|
IN LPCWSTR Arg2,
|
|
IN LPCWSTR Arg3
|
|
)
|
|
{
|
|
CsLogEvent3(LogLevel, MessageId, Arg1, Arg2, Arg3);
|
|
|
|
return;
|
|
|
|
} // ClNetLogEvent3
|
|
|
|
|
|
BOOLEAN
|
|
NmpLockedEnterApi(
|
|
NM_STATE RequiredState
|
|
)
|
|
{
|
|
if (NmpState >= RequiredState) {
|
|
NmpActiveThreadCount++;
|
|
CL_ASSERT(NmpActiveThreadCount != 0);
|
|
return(TRUE);
|
|
}
|
|
|
|
return(FALSE);
|
|
|
|
} // NmpLockedEnterApi
|
|
|
|
|
|
BOOLEAN
|
|
NmpEnterApi(
|
|
NM_STATE RequiredState
|
|
)
|
|
{
|
|
BOOLEAN mayEnter;
|
|
|
|
|
|
NmpAcquireLock();
|
|
|
|
mayEnter = NmpLockedEnterApi(RequiredState);
|
|
|
|
NmpReleaseLock();
|
|
|
|
return(mayEnter);
|
|
|
|
} // NmpEnterApi
|
|
|
|
|
|
VOID
|
|
NmpLockedLeaveApi(
|
|
VOID
|
|
)
|
|
{
|
|
CL_ASSERT(NmpActiveThreadCount > 0);
|
|
|
|
NmpActiveThreadCount--;
|
|
|
|
if ((NmpActiveThreadCount == 0) && (NmpState == NmStateOfflinePending)) {
|
|
SetEvent(NmpShutdownEvent);
|
|
}
|
|
|
|
return;
|
|
|
|
} // NmpLockedLeaveApi
|
|
|
|
|
|
VOID
|
|
NmpLeaveApi(
|
|
VOID
|
|
)
|
|
{
|
|
NmpAcquireLock();
|
|
|
|
NmpLockedLeaveApi();
|
|
|
|
NmpReleaseLock();
|
|
|
|
return;
|
|
|
|
} // NmpLeaveApi
|
|
|
|
|
|
//
|
|
// Routines to provide a cluster shared key for signing and encrypting
|
|
// data.
|
|
//
|
|
|
|
DWORD
|
|
NmpGetLogonId(
|
|
OUT LUID * LogonId
|
|
)
|
|
{
|
|
HANDLE tokenHandle = NULL;
|
|
TOKEN_STATISTICS tokenInfo;
|
|
DWORD bytesReturned;
|
|
BOOL success = FALSE;
|
|
DWORD status;
|
|
|
|
if (LogonId == NULL) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto error_exit;
|
|
}
|
|
|
|
if (!OpenProcessToken(
|
|
GetCurrentProcess(),
|
|
TOKEN_QUERY,
|
|
&tokenHandle
|
|
)) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to open process token, status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
if (!GetTokenInformation(
|
|
tokenHandle,
|
|
TokenStatistics,
|
|
&tokenInfo,
|
|
sizeof(tokenInfo),
|
|
&bytesReturned
|
|
)) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to get token information, status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
RtlCopyMemory(LogonId, &(tokenInfo.AuthenticationId), sizeof(LUID));
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
error_exit:
|
|
|
|
if (tokenHandle != NULL) {
|
|
CloseHandle(tokenHandle);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpGetLogonId
|
|
|
|
|
|
DWORD
|
|
NmpConnectToLsaPrivileged(
|
|
OUT HANDLE * LsaHandle,
|
|
OUT BOOLEAN * Trusted
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Connect to LSA.
|
|
|
|
If running as a service, there is no need to enable the TCB privilege.
|
|
The fix for bug 337751 allows the cluster service account to issue
|
|
a MSV1_0_XXX requests even if it does not have a trusted connection
|
|
to LSA.
|
|
|
|
If not running as a service, try to elevate the privilege to TCB to
|
|
connect trusted. Fail the call if TCB privilege cannot be enabled.
|
|
If TCB is enabled and was previously not enabled, it is restored prior
|
|
to returning.
|
|
|
|
Callers that need to connect to LSA without requiring service logon
|
|
or TCB can simply use LsaConnectUntrusted.
|
|
|
|
Arguments:
|
|
|
|
LsaHandle - returns LSA handle. Must be cleaned up by caller
|
|
|
|
Trusted - returns whether connection is trusted
|
|
|
|
Return value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
BOOLEAN wasEnabled = FALSE;
|
|
BOOLEAN trusted = FALSE;
|
|
DWORD ignore;
|
|
STRING name;
|
|
HANDLE lsaHandle = NULL;
|
|
|
|
//
|
|
// Try to turn on TCB privilege if running in console mode.
|
|
//
|
|
if (!CsRunningAsService) {
|
|
status = RtlAdjustPrivilege(SE_TCB_PRIVILEGE, TRUE, FALSE, &wasEnabled);
|
|
if (!NT_SUCCESS(status)) {
|
|
status = LsaNtStatusToWinError(status);
|
|
#if CLUSTER_BETA
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Failed to turn on TCB privilege, status %1!u!.\n",
|
|
status
|
|
);
|
|
#endif // CLUSTER_BETA
|
|
return(status);
|
|
} else {
|
|
#if CLUSTER_BETA
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Turned on TCB privilege, wasEnabled = %1!ws!.\n",
|
|
(wasEnabled) ? L"TRUE" : L"FALSE"
|
|
);
|
|
#endif // CLUSTER_BETA
|
|
trusted = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Establish contact with LSA.
|
|
//
|
|
if (trusted) {
|
|
RtlInitString(&name, "ClusSvcNM");
|
|
status = LsaRegisterLogonProcess(&name, &lsaHandle, &ignore);
|
|
|
|
//
|
|
// Turn off TCB privilege
|
|
//
|
|
if (!wasEnabled) {
|
|
|
|
DWORD subStatus;
|
|
|
|
subStatus = RtlAdjustPrivilege(
|
|
SE_TCB_PRIVILEGE,
|
|
FALSE,
|
|
FALSE,
|
|
&wasEnabled
|
|
);
|
|
if (!NT_SUCCESS(subStatus)) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to disable TCB privilege, "
|
|
"status %1!u!.\n",
|
|
subStatus
|
|
);
|
|
} else {
|
|
#if CLUSTER_BETA
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Turned off TCB privilege.\n"
|
|
);
|
|
#endif // CLUSTER_BETA
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
status = LsaConnectUntrusted(&lsaHandle);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
status = LsaNtStatusToWinError(status);
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to obtain LSA logon handle in %1!ws! mode, "
|
|
"status %2!u!.\n",
|
|
(trusted) ? L"trusted" : L"untrusted", status
|
|
);
|
|
} else {
|
|
*LsaHandle = lsaHandle;
|
|
*Trusted = trusted;
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpConnectToLsaPrivileged
|
|
|
|
|
|
DWORD
|
|
NmpDeriveClusterKey(
|
|
IN PVOID MixingBytes,
|
|
IN DWORD MixingBytesSize,
|
|
OUT PVOID * Key,
|
|
OUT DWORD * KeyLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Derive the cluster key using mixing bytes. Allocate a buffer
|
|
for the key and return it.
|
|
|
|
Arguments:
|
|
|
|
Key - set to buffer containing key
|
|
|
|
KeyLength - length of resulting key
|
|
|
|
--*/
|
|
{
|
|
LUID logonId;
|
|
BOOLEAN trusted = FALSE;
|
|
HANDLE lsaHandle = NULL;
|
|
|
|
STRING name;
|
|
DWORD packageId = 0;
|
|
|
|
DWORD requestSize;
|
|
PMSV1_0_DERIVECRED_REQUEST request = NULL;
|
|
DWORD responseSize;
|
|
PMSV1_0_DERIVECRED_RESPONSE response = NULL;
|
|
|
|
PUCHAR key;
|
|
DWORD keyLength;
|
|
|
|
DWORD status = STATUS_SUCCESS;
|
|
DWORD subStatus = STATUS_SUCCESS;
|
|
|
|
status = NmpGetLogonId(&logonId);
|
|
if (!NT_SUCCESS(status)) {
|
|
ClRtlLogPrint(
|
|
LOG_UNUSUAL,
|
|
"[NM] Failed to determine logon ID, status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
status = NmpConnectToLsaPrivileged(&lsaHandle, &trusted);
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to connect to LSA, status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Lookup the authentication package.
|
|
//
|
|
RtlInitString( &name, MSV1_0_PACKAGE_NAME );
|
|
|
|
status = LsaLookupAuthenticationPackage(lsaHandle, &name, &packageId);
|
|
if (!NT_SUCCESS(status)) {
|
|
status = LsaNtStatusToWinError(status);
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to local authentication package with "
|
|
"name %1!ws!, status %2!u!.\n",
|
|
name.Buffer, status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Build the derive credentials request with the provided
|
|
// mixing bytes.
|
|
//
|
|
requestSize = sizeof(MSV1_0_DERIVECRED_REQUEST) + MixingBytesSize;
|
|
|
|
request = LocalAlloc(LMEM_FIXED, requestSize);
|
|
if (request == NULL) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to allocate LSA request of size %1!u! bytes.\n",
|
|
requestSize
|
|
);
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
|
|
request->MessageType = MsV1_0DeriveCredential;
|
|
RtlCopyMemory(&(request->LogonId), &logonId, sizeof(logonId));
|
|
request->DeriveCredType = MSV1_0_DERIVECRED_TYPE_SHA1;
|
|
request->DeriveCredInfoLength = MixingBytesSize;
|
|
RtlCopyMemory(
|
|
&(request->DeriveCredSubmitBuffer[0]),
|
|
MixingBytes,
|
|
MixingBytesSize
|
|
);
|
|
|
|
//
|
|
// Make the call through LSA to the authentication package.
|
|
//
|
|
status = LsaCallAuthenticationPackage(
|
|
lsaHandle,
|
|
packageId,
|
|
request,
|
|
requestSize,
|
|
&response,
|
|
&responseSize,
|
|
&subStatus
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
status = LsaNtStatusToWinError(status);
|
|
subStatus = LsaNtStatusToWinError(subStatus);
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] DeriveCredential call to authentication "
|
|
"package failed, status %1!u!, auth package "
|
|
"status %2!u!.\n", status, subStatus
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Allocate a non-LSA buffer to store the key.
|
|
//
|
|
keyLength = response->DeriveCredInfoLength;
|
|
key = MIDL_user_allocate(keyLength);
|
|
if (key == NULL) {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to allocate buffer for cluster "
|
|
"key of size %1!u!.\n",
|
|
keyLength
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Store the derived credentials in the key buffer.
|
|
//
|
|
RtlCopyMemory(key, &(response->DeriveCredReturnBuffer[0]), keyLength);
|
|
|
|
//
|
|
// Zero the derived credential buffer to be extra paranoid.
|
|
//
|
|
RtlZeroMemory(
|
|
&(response->DeriveCredReturnBuffer[0]),
|
|
response->DeriveCredInfoLength
|
|
);
|
|
|
|
status = STATUS_SUCCESS;
|
|
*Key = key;
|
|
*KeyLength = keyLength;
|
|
|
|
error_exit:
|
|
|
|
if (lsaHandle != NULL) {
|
|
LsaDeregisterLogonProcess(lsaHandle);
|
|
lsaHandle = NULL;
|
|
}
|
|
|
|
if (request != NULL) {
|
|
LocalFree(request);
|
|
request = NULL;
|
|
}
|
|
|
|
if (response != NULL) {
|
|
LsaFreeReturnBuffer(response);
|
|
response = NULL;
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpDeriveClusterKey
|
|
|
|
|
|
DWORD
|
|
NmpGetClusterKey(
|
|
OUT PVOID KeyBuffer,
|
|
IN OUT DWORD * KeyBufferLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decrypt and copy the shared cluster key into the buffer provided.
|
|
|
|
Arguments:
|
|
|
|
KeyBuffer - buffer to which key should be copied
|
|
|
|
KeyBufferLength - IN: length of KeyBuffer
|
|
OUT: required buffer size, if input
|
|
buffer length is insufficient
|
|
|
|
Return value:
|
|
|
|
ERROR_INSUFFICIENT_BUFFER if KeyBuffer is too small.
|
|
ERROR_FILE_NOT_FOUND if NmpEncryptedClusterKey has not
|
|
yet been generated.
|
|
ERROR_SUCCESS on success.
|
|
|
|
Notes:
|
|
|
|
Acquires and releases NM lock. Since NM lock is
|
|
implemented as a critical section, calling thread
|
|
is permitted to already hold NM lock.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
BOOL DecryptingDataSucceeded = FALSE;
|
|
BOOL Success;
|
|
DATA_BLOB DataIn;
|
|
DATA_BLOB DataOut;
|
|
|
|
RtlZeroMemory(&DataOut, sizeof(DataOut));
|
|
|
|
NmpAcquireLock();
|
|
|
|
if (NmpEncryptedClusterKey == NULL) {
|
|
status = ERROR_FILE_NOT_FOUND;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] The cluster key has not yet been derived.\n"
|
|
);
|
|
} else{
|
|
//
|
|
// Decrypt cluster key
|
|
//
|
|
DataIn.pbData = NmpEncryptedClusterKey;
|
|
DataIn.cbData = NmpEncryptedClusterKeyLength;
|
|
|
|
Success = CryptUnprotectData(&DataIn, // data to be decrypted
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0, // flags
|
|
&DataOut // decrypted data
|
|
);
|
|
|
|
|
|
if (!Success)
|
|
{
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to decrypt data using CryptUnprotectData, "
|
|
"status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
DecryptingDataSucceeded = TRUE;
|
|
|
|
if (KeyBuffer == NULL || DataOut.cbData > *KeyBufferLength) {
|
|
status = ERROR_INSUFFICIENT_BUFFER;
|
|
} else {
|
|
RtlCopyMemory(KeyBuffer, DataOut.pbData, DataOut.cbData);
|
|
status = ERROR_SUCCESS;
|
|
}
|
|
|
|
*KeyBufferLength = DataOut.cbData;
|
|
}
|
|
|
|
error_exit:
|
|
|
|
NmpReleaseLock();
|
|
|
|
if (DecryptingDataSucceeded)
|
|
{
|
|
//
|
|
// Zero the encrypted data before releasing the memory.
|
|
//
|
|
RtlSecureZeroMemory(DataOut.pbData, DataOut.cbData);
|
|
}
|
|
|
|
if (DataOut.pbData != NULL)
|
|
{
|
|
LocalFree(DataOut.pbData);
|
|
}
|
|
|
|
// To be extra secure.
|
|
RtlSecureZeroMemory(&DataOut, sizeof(DataOut));
|
|
|
|
return(status);
|
|
|
|
} // NmpGetClusterKey
|
|
|
|
|
|
|
|
DWORD
|
|
NmpRederiveClusterKey(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Forces rederivation of cluster key.
|
|
|
|
Must be called during cluster initialization to generate
|
|
cluster key the first time.
|
|
|
|
Otherwise called when cluster password changes, since the
|
|
cluster key is based on the cluster service account password.
|
|
|
|
Notes:
|
|
|
|
Acquires and releases NM lock.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
BOOLEAN lockAcquired;
|
|
PVOID key = NULL;
|
|
DWORD keyLength = 0;
|
|
PVOID oldEncryptedKey = NULL;
|
|
DWORD oldEncryptedKeyLength = 0;
|
|
PVOID mixingBytes = NULL;
|
|
DWORD mixingBytesSize;
|
|
|
|
NmpAcquireLock();
|
|
lockAcquired = TRUE;
|
|
|
|
NmpLockedEnterApi(NmStateOnlinePending);
|
|
|
|
//
|
|
// Form the mixing bytes.
|
|
//
|
|
if (NmpClusterInstanceId == NULL) {
|
|
status = ERROR_INVALID_PARAMETER;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Need cluster instance id in order to derive "
|
|
"cluster key, status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
mixingBytesSize = NM_WCSLEN(NmpClusterInstanceId);
|
|
mixingBytes = MIDL_user_allocate(mixingBytesSize);
|
|
if (mixingBytes == NULL) {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to allocate buffer of size %1!u! "
|
|
"for mixing bytes to derive cluster key.\n",
|
|
mixingBytesSize
|
|
);
|
|
goto error_exit;
|
|
}
|
|
RtlCopyMemory(mixingBytes, NmpClusterInstanceId, mixingBytesSize);
|
|
|
|
|
|
//
|
|
// Make a copy of the old encrypted key to detect changes.
|
|
//
|
|
if (NmpEncryptedClusterKey != NULL) {
|
|
|
|
CL_ASSERT(NmpEncryptedClusterKeyLength > 0);
|
|
|
|
oldEncryptedKey = MIDL_user_allocate(NmpEncryptedClusterKeyLength);
|
|
if (oldEncryptedKey == NULL) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to allocate buffer for cluster "
|
|
"key copy.\n"
|
|
);
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
oldEncryptedKeyLength = NmpEncryptedClusterKeyLength;
|
|
RtlCopyMemory(oldEncryptedKey,
|
|
NmpEncryptedClusterKey,
|
|
NmpEncryptedClusterKeyLength
|
|
);
|
|
}
|
|
|
|
|
|
NmpReleaseLock();
|
|
lockAcquired = FALSE;
|
|
|
|
status = NmpDeriveClusterKey(
|
|
mixingBytes,
|
|
mixingBytesSize,
|
|
&key,
|
|
&keyLength
|
|
);
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to derive cluster key, "
|
|
"status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
NmpAcquireLock();
|
|
lockAcquired = TRUE;
|
|
|
|
//
|
|
// Make sure another thread didn't beat us in obtaining a key.
|
|
// We replace the cluster key with the generated key if it is
|
|
// not different from the old key (or somebody set it to NULL).
|
|
//
|
|
if (NmpEncryptedClusterKey != NULL &&
|
|
(oldEncryptedKey == NULL ||
|
|
NmpEncryptedClusterKeyLength != oldEncryptedKeyLength ||
|
|
RtlCompareMemory(
|
|
NmpEncryptedClusterKey,
|
|
oldEncryptedKey,
|
|
oldEncryptedKeyLength
|
|
) != oldEncryptedKeyLength
|
|
)
|
|
) {
|
|
|
|
//
|
|
// Keep the current NmpEncryptedClusterKey.
|
|
//
|
|
} else {
|
|
|
|
|
|
//
|
|
// Encrypt derived credentials and store them
|
|
//
|
|
|
|
if (NmpEncryptedClusterKey != NULL)
|
|
{
|
|
RtlSecureZeroMemory(NmpEncryptedClusterKey, NmpEncryptedClusterKeyLength);
|
|
LocalFree(NmpEncryptedClusterKey);
|
|
}
|
|
|
|
status = NmpProtectData(key,
|
|
keyLength,
|
|
&NmpEncryptedClusterKey,
|
|
&NmpEncryptedClusterKeyLength
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to encrypt data using CryptProtectData, "
|
|
"status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
error_exit:
|
|
|
|
if (lockAcquired) {
|
|
NmpLockedLeaveApi();
|
|
NmpReleaseLock();
|
|
} else {
|
|
NmpLeaveApi();
|
|
}
|
|
|
|
if (oldEncryptedKey != NULL)
|
|
{
|
|
MIDL_user_free(oldEncryptedKey);
|
|
}
|
|
|
|
if (key != NULL) {
|
|
RtlSecureZeroMemory(key, keyLength);
|
|
MIDL_user_free(key);
|
|
key = NULL;
|
|
keyLength = 0;
|
|
}
|
|
|
|
if (mixingBytes != NULL) {
|
|
MIDL_user_free(mixingBytes);
|
|
mixingBytes = NULL;
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpRederiveClusterKey
|
|
|
|
VOID
|
|
NmpFreeClusterKey(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called during NmShutdown.
|
|
|
|
--*/
|
|
{
|
|
if (NmpEncryptedClusterKey != NULL) {
|
|
RtlSecureZeroMemory(NmpEncryptedClusterKey, NmpEncryptedClusterKeyLength);
|
|
LocalFree(NmpEncryptedClusterKey);
|
|
NmpEncryptedClusterKey = NULL;
|
|
NmpEncryptedClusterKeyLength = 0;
|
|
}
|
|
|
|
return;
|
|
|
|
} // NmpFreeClusterKey
|
|
|
|
|
|
DWORD
|
|
NmpSetLsaProcessOptions(
|
|
IN ULONG ProcessOptions
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set LSA options for this process.
|
|
|
|
Arguments:
|
|
|
|
ProcessOptions - MSV1_0_OPTION_XXX process option bit flags
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
Win32 error code otherwise.
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD ReturnStatus;
|
|
NTSTATUS Status;
|
|
BOOLEAN trusted = FALSE;
|
|
HANDLE hLsa = NULL;
|
|
LSA_STRING LsaStringBuf;
|
|
char *AuthPackage = MSV1_0_PACKAGE_NAME;
|
|
ULONG PackageId;
|
|
ULONG cbOptionsRequest, cbResponse;
|
|
MSV1_0_SETPROCESSOPTION_REQUEST OptionsRequest;
|
|
PVOID Response = NULL;
|
|
ULONG ResponseSize;
|
|
NTSTATUS SubStatus;
|
|
|
|
|
|
ReturnStatus = NmpConnectToLsaPrivileged(&hLsa, &trusted);
|
|
if (ReturnStatus != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to connect to the LSA server while setting "
|
|
"process options, status %1!u!.\n",
|
|
ReturnStatus
|
|
);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
RtlInitString(&LsaStringBuf, AuthPackage);
|
|
|
|
Status = LsaLookupAuthenticationPackage(
|
|
hLsa,
|
|
&LsaStringBuf, // MSV1_0 authentication package
|
|
&PackageId // output: authentication package identifier.
|
|
);
|
|
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
ReturnStatus = LsaNtStatusToWinError(Status);
|
|
ClRtlLogPrint(
|
|
LOG_CRITICAL,
|
|
"[NM] Authentication package lookup failed while "
|
|
"setting LSA process options, status %1!u!.\n",
|
|
ReturnStatus
|
|
);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
cbOptionsRequest = sizeof(OptionsRequest);
|
|
ZeroMemory(&OptionsRequest, sizeof(OptionsRequest));
|
|
OptionsRequest.MessageType = MsV1_0SetProcessOption;
|
|
OptionsRequest.ProcessOptions = ProcessOptions;
|
|
|
|
Status = LsaCallAuthenticationPackage(
|
|
hLsa,
|
|
PackageId,
|
|
&OptionsRequest,
|
|
cbOptionsRequest,
|
|
&Response,
|
|
&ResponseSize,
|
|
&SubStatus
|
|
);
|
|
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
ReturnStatus = LsaNtStatusToWinError(Status);
|
|
|
|
ClRtlLogPrint(
|
|
LOG_CRITICAL,
|
|
"[NM] Failed to set LSA process options (1) to %1!x! , "
|
|
"status %2!u!.\n",
|
|
ProcessOptions, ReturnStatus
|
|
);
|
|
goto ErrorExit;
|
|
}
|
|
else if (LsaNtStatusToWinError(SubStatus) != ERROR_SUCCESS)
|
|
{
|
|
ReturnStatus = LsaNtStatusToWinError(SubStatus);
|
|
|
|
ClRtlLogPrint(
|
|
LOG_CRITICAL,
|
|
"[NM] Failed to set LSA process options (2) to %1!x! , "
|
|
"status %2!u!.\n",
|
|
ProcessOptions, ReturnStatus
|
|
);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
ReturnStatus = ERROR_SUCCESS;
|
|
|
|
|
|
ErrorExit:
|
|
|
|
if (hLsa != NULL) {
|
|
LsaDeregisterLogonProcess(hLsa);
|
|
hLsa = NULL;
|
|
}
|
|
|
|
return ReturnStatus;
|
|
|
|
} // NmpSetLsaProcessOptions
|
|
|
|
|
|
// Helper routines used for encryption on the wire data
|
|
// currently used only by dmsync.c to secure transfer of checkpoints
|
|
|
|
// Here the pattern how this routines should be used
|
|
// (Crafted to produce the least amout of code change in dm to encrypt/decrypt data going over an RPC pipe)
|
|
//
|
|
// Encryption:
|
|
// NmCryptor_Init(Cryptor)
|
|
//
|
|
// NmCryptor_PrepareEncryptionBuffer(Cryptor, Buffer, Size);
|
|
// <Put upto Cryptor->PayloadSize bytes of data into Cryptor->PayloadBuffer (say X bytes)>
|
|
// NmCryptor_Encrypt(&Cryptor, X);
|
|
// <transmit/store encrypted bytes from Cryptor->EncryptedBuffer, Cryptor->EncryptedSize>
|
|
//
|
|
// ... repeat PrepareEncryptionBuffer/Encrypt for as many time as you need
|
|
//
|
|
// NmCryptor_Destroy(Cryptor)
|
|
//
|
|
// Decryption:
|
|
//
|
|
// NmCryptor_Init(Cryptor)
|
|
//
|
|
// <Copy the data to be decrypted into some buffer>
|
|
// NmCryptor_Decrypt(Cryptor, Buffer, Size);
|
|
// <Get the data from Cryptor->PayloadBuffer, Cryptor->PayloadSize>
|
|
//
|
|
// ... repeat NmCryptor_Decrypt for as many packets you have to decrypt ...
|
|
//
|
|
// NmCryptor_Destroy(Cryptor)
|
|
|
|
|
|
typedef struct _NMP_CRYPTOR_HEADER {
|
|
BYTE Salt[16]; // random goo which is mixed with the shared secret to produce a key
|
|
BYTE SaltQuickSig[16]; // simple xor of salt bytes with 0xFF used as a quick test whether stuff is encrypted or not
|
|
BYTE SaltSlowSig[16]; // encrypted salt bytes to check for the valid encryption key
|
|
} NM_CRYPTOR_HEADER, *PNM_CRYPTOR_HEADER;
|
|
|
|
VOID
|
|
NmCryptor_Init(
|
|
IN OUT PNM_CRYPTOR Cryptor,
|
|
IN BOOL EnableEncryption)
|
|
{
|
|
ZeroMemory(Cryptor, sizeof(*Cryptor));
|
|
Cryptor->EncryptionDisabled = !EnableEncryption;
|
|
}
|
|
|
|
BOOL NmpVerifyQuickSig(IN PNM_CRYPTOR_HEADER hdr)
|
|
{
|
|
int i;
|
|
if (hdr->SaltQuickSig[0] != 'Q' || hdr->SaltQuickSig[1] != 'S') {
|
|
return FALSE;
|
|
}
|
|
for (i = 2; i < sizeof(hdr->Salt); ++i) {
|
|
if (hdr->SaltQuickSig[i] != (hdr->Salt[i] ^ 0xFF) ) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
VOID NmpMakeQuickSig(IN PNM_CRYPTOR_HEADER hdr)
|
|
{
|
|
int i;
|
|
for (i = 2; i < sizeof(hdr->Salt); ++i) {
|
|
hdr->SaltQuickSig[i] = hdr->Salt[i] ^ 0xFF;
|
|
}
|
|
hdr->SaltQuickSig[0] = 'Q'; // to make it easier to spot in the sniff
|
|
hdr->SaltQuickSig[1] = 'S'; //
|
|
}
|
|
|
|
DWORD NmpCryptor_PrepareKey(
|
|
IN OUT PNM_CRYPTOR Cryptor,
|
|
IN BOOL GenerateSalt)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calls prepares the key for encrption/decryption.
|
|
Creates a symmetric key by mixing random 128bit salt with cluster password derived secret
|
|
|
|
Arguments:
|
|
|
|
GenerateSalt - if true, salt is generated, if false, salt is read from a buffer
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
Win32 error code otherwise.
|
|
|
|
--*/
|
|
{
|
|
DWORD Status = ERROR_SUCCESS;
|
|
DWORD ClusterKeyLen = 0;
|
|
PBYTE ClusterKey = NULL;
|
|
|
|
PNM_CRYPTOR_HEADER hdr = (PNM_CRYPTOR_HEADER)Cryptor->EncryptedBuffer;
|
|
|
|
if (GenerateSalt) {
|
|
// Check that a buffer is big enough
|
|
// to hold encryption header
|
|
|
|
if (Cryptor->EncryptedSize < sizeof(*hdr)) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Cryptor: No room for the header, buffer size is only %1!u!.\n",
|
|
Cryptor->EncryptedSize
|
|
);
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
} else {
|
|
// Otherwise, do a quick check whether the incoming data
|
|
// is encrypted
|
|
|
|
if (Cryptor->EncryptedSize < sizeof(*hdr)) {
|
|
Cryptor->EncryptionDisabled = TRUE;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
if ( !NmpVerifyQuickSig(hdr) ) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Cryptor: Data is not encrypted.\n"
|
|
);
|
|
Cryptor->EncryptionDisabled = TRUE;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
Status = NmpCreateCSPHandle(&Cryptor->CryptProv);
|
|
if (Status != ERROR_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
if (GenerateSalt) {
|
|
if (!CryptGenRandom(Cryptor->CryptProv, sizeof(hdr->Salt), hdr->Salt)){
|
|
Status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Cryptor: CryptGenRandom failed %1!u!.\n",
|
|
Status
|
|
);
|
|
goto exit_gracefully;
|
|
}
|
|
}
|
|
|
|
Status = NmpDeriveSessionKey(
|
|
Cryptor->CryptProv,
|
|
CALG_RC4, 128 << 16, // algorithm, key length
|
|
hdr->Salt, sizeof(hdr->Salt),
|
|
&Cryptor->CryptKey);
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
goto exit_gracefully; // error logged by NmpDeriveSessionKey
|
|
}
|
|
|
|
if (GenerateSalt) {
|
|
DWORD Len = sizeof(hdr->SaltSlowSig);
|
|
|
|
// Encrypt SlowSig so that the receiver can verify the validity
|
|
// of an encryption key of a sender
|
|
memcpy(hdr->SaltSlowSig, hdr->Salt, sizeof(hdr->SaltSlowSig));
|
|
if (!CryptEncrypt(Cryptor->CryptKey, 0, FALSE, 0, // hash,final,flags
|
|
hdr->SaltSlowSig, &Len, Len))
|
|
{
|
|
Status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to Encrypt signature, status %1!u!.\n",
|
|
Status
|
|
);
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
// now make quick signature (to test whether the incoming data is encrypted)
|
|
NmpMakeQuickSig(hdr);
|
|
|
|
} else {
|
|
DWORD Len = sizeof(hdr->SaltSlowSig);
|
|
|
|
// Verify encrypted portion of the signature
|
|
if (!CryptDecrypt(Cryptor->CryptKey, 0, FALSE, 0, // hash,final,flags
|
|
hdr->SaltSlowSig, &Len))
|
|
{
|
|
Status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to Decrypt signature, status %1!u!.\n",
|
|
Status
|
|
);
|
|
goto exit_gracefully;
|
|
}
|
|
if (memcmp(hdr->Salt, hdr->SaltSlowSig, sizeof(hdr->SaltSlowSig)) != 0) {
|
|
Status = ERROR_DECRYPTION_FAILED;
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Signatures don't match.\n"
|
|
);
|
|
goto exit_gracefully;
|
|
}
|
|
}
|
|
Cryptor->KeyGenerated = TRUE;
|
|
|
|
exit_gracefully:
|
|
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
NmCryptor_Decrypt(
|
|
IN OUT PNM_CRYPTOR Cryptor,
|
|
IN OUT PVOID Buffer,
|
|
IN DWORD BufferSize)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decrypts the supplied buffer
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
Win32 error code otherwise.
|
|
|
|
Notes:
|
|
|
|
If the routine succeeds, use
|
|
|
|
Cryptor->PayloadBuffer
|
|
Cryptor->PayloadSize
|
|
|
|
fields to get to the decrypted data
|
|
|
|
--*/
|
|
{
|
|
DWORD Status = ERROR_SUCCESS;
|
|
|
|
Cryptor->PayloadBuffer = (PBYTE)Buffer;
|
|
Cryptor->PayloadSize = BufferSize;
|
|
|
|
Cryptor->EncryptedBuffer = Cryptor->PayloadBuffer;
|
|
Cryptor->EncryptedSize = BufferSize;
|
|
|
|
if (Cryptor->EncryptionDisabled) {
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
if (!Cryptor->KeyGenerated) {
|
|
Status = NmpCryptor_PrepareKey(Cryptor, FALSE);
|
|
if (Status != ERROR_SUCCESS) {
|
|
return Status;
|
|
}
|
|
if (Cryptor->EncryptionDisabled) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Cryptor received unencrypted data.\n"
|
|
);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
Cryptor->PayloadBuffer = Cryptor->PayloadBuffer + sizeof(NM_CRYPTOR_HEADER);
|
|
Cryptor->PayloadSize -= sizeof(NM_CRYPTOR_HEADER);
|
|
}
|
|
|
|
if (!CryptDecrypt(Cryptor->CryptKey, 0, FALSE, 0,
|
|
Cryptor->PayloadBuffer, &Cryptor->PayloadSize))
|
|
{
|
|
Status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to Decrypt buffer, status %1!u!.\n",
|
|
Status
|
|
);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
NmCryptor_PrepareEncryptionBuffer(
|
|
IN OUT PNM_CRYPTOR Cryptor,
|
|
IN OUT PVOID Buffer,
|
|
IN DWORD BufferSize)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Supplies the information about the buffer ti be used for encryption
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
Win32 error code otherwise.
|
|
|
|
Notes:
|
|
|
|
When the function returns, use
|
|
|
|
Cryptor->PayloadBuffer
|
|
Cryptor->PayloadSize
|
|
|
|
to figure out where to put the data to be encrypted
|
|
|
|
--*/
|
|
{
|
|
Cryptor->PayloadBuffer = (PBYTE)Buffer;
|
|
Cryptor->PayloadSize = BufferSize;
|
|
|
|
Cryptor->EncryptedBuffer = Cryptor->PayloadBuffer;
|
|
Cryptor->EncryptedSize = BufferSize;
|
|
|
|
if (Cryptor->EncryptionDisabled || Cryptor->KeyGenerated) {
|
|
return;
|
|
}
|
|
|
|
if (NmpIsNT5NodeInCluster == FALSE) {
|
|
|
|
// Create room for the header
|
|
Cryptor->PayloadBuffer += sizeof(NM_CRYPTOR_HEADER);
|
|
Cryptor->PayloadSize -= sizeof(NM_CRYPTOR_HEADER);
|
|
} else {
|
|
Cryptor->EncryptionDisabled = TRUE;
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
NmCryptor_Encrypt(
|
|
IN OUT PNM_CRYPTOR Cryptor,
|
|
DWORD DataSize)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Encrypts DataSize bytes
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
Win32 error code otherwise.
|
|
|
|
Notes:
|
|
|
|
Input data are in the buffer pointed by
|
|
Cryptor->PayloadSize (prepared by NmCryptor_PrepareEncryptionBuffer routine)
|
|
|
|
Output data are in:
|
|
|
|
Cryptor->EncryptedBuffer == Whatever buffer was supplied to NmCryptor_PrepareEncryptionBuffer
|
|
Cryptor->EncryptedSize
|
|
|
|
--*/
|
|
{
|
|
DWORD Status = ERROR_SUCCESS;
|
|
|
|
if (Cryptor->EncryptionDisabled) {
|
|
Cryptor->EncryptedSize = DataSize;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
if (!Cryptor->KeyGenerated) {
|
|
Status = NmpCryptor_PrepareKey(Cryptor, TRUE);
|
|
if (Status != ERROR_SUCCESS) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
if (!CryptEncrypt(Cryptor->CryptKey, 0, FALSE, 0,
|
|
Cryptor->PayloadBuffer, &DataSize, Cryptor->EncryptedSize))
|
|
{
|
|
Status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to Encrypt buffer, status %1!u!.\n",
|
|
Status
|
|
);
|
|
}
|
|
Cryptor->EncryptedSize =
|
|
DataSize + (int)(Cryptor->PayloadBuffer - Cryptor->EncryptedBuffer);
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
NmCryptor_Destroy(
|
|
PNM_CRYPTOR Cryptor)
|
|
{
|
|
if (Cryptor == NULL) {
|
|
return;
|
|
}
|
|
if (Cryptor->CryptKey) {
|
|
CryptDestroyKey(Cryptor->CryptKey);
|
|
}
|
|
if (Cryptor->CryptProv) {
|
|
CryptReleaseContext(Cryptor->CryptProv, 0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
NmpFreeNetworkMulticastKey(
|
|
PNM_NETWORK_MULTICASTKEY networkMulticastKey
|
|
)
|
|
{
|
|
if (networkMulticastKey != NULL)
|
|
{
|
|
|
|
if (networkMulticastKey->EncryptedMulticastKey != NULL)
|
|
{
|
|
MIDL_user_free(networkMulticastKey->EncryptedMulticastKey);
|
|
}
|
|
|
|
if (networkMulticastKey->MAC != NULL)
|
|
{
|
|
MIDL_user_free(networkMulticastKey->MAC);
|
|
}
|
|
|
|
if (networkMulticastKey->Salt != NULL)
|
|
{
|
|
MIDL_user_free(networkMulticastKey->Salt);
|
|
}
|
|
|
|
MIDL_user_free(networkMulticastKey);
|
|
}
|
|
|
|
|
|
} // NmpFreeNetworkMulticastKey()
|
|
|
|
|
|
|
|
DWORD
|
|
NmpGetMulticastKeyFromNMLeader(
|
|
IN DWORD LeaderNodeId,
|
|
IN LPWSTR NodeIdString,
|
|
IN LPWSTR NetworkId,
|
|
IN PNM_NETWORK_MULTICASTKEY * MulticastKey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Issue a retrieve request for network multicast key to NM leader.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the request is successful.
|
|
|
|
Win32 error code on failure.
|
|
|
|
Notes:
|
|
|
|
This routine mimics GumpUpdateRemoteNode. In Longhorn, there
|
|
should be one generic async RPC call wrapper.
|
|
|
|
--*/
|
|
{
|
|
DWORD Status;
|
|
HANDLE hEventHandle;
|
|
BOOL result;
|
|
PNM_NODE Node = NULL;
|
|
HANDLE handleArr[2];
|
|
RPC_ASYNC_STATE AsyncState;
|
|
RPC_BINDING_HANDLE rpcBinding;
|
|
|
|
//
|
|
// Prepare for async RPC. We do this here to avoid hitting a failure
|
|
// after the update is already in progress.
|
|
//
|
|
ZeroMemory((PVOID) &AsyncState, sizeof(RPC_ASYNC_STATE));
|
|
|
|
AsyncState.u.hEvent = CreateEvent(
|
|
NULL, // no attributes
|
|
TRUE, // manual reset
|
|
FALSE, // initial state unsignalled
|
|
NULL // no object name
|
|
);
|
|
|
|
if (AsyncState.u.hEvent == NULL) {
|
|
Status = GetLastError();
|
|
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[GUM] NmpGetMulticastKeyFromNMLeader: Failed to allocate event object for async "
|
|
"RPC call, status %1!u!\n",
|
|
Status
|
|
);
|
|
|
|
return (Status);
|
|
}
|
|
|
|
//
|
|
// Initialize the async RPC tracking information
|
|
//
|
|
hEventHandle = AsyncState.u.hEvent;
|
|
AsyncState.u.hEvent = NULL;
|
|
|
|
|
|
Status = RpcAsyncInitializeHandle(&AsyncState, sizeof(RPC_ASYNC_STATE));
|
|
AsyncState.u.hEvent = hEventHandle;
|
|
|
|
if (Status != RPC_S_OK) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[GUM] NmpGetMulticastKeyFromNMLeader: Failed to initialize async RPC status "
|
|
"block, status %1!u!\n",
|
|
Status
|
|
);
|
|
|
|
goto error_exit;
|
|
}
|
|
|
|
AsyncState.UserInfo = NULL;
|
|
AsyncState.NotificationType = RpcNotificationTypeEvent;
|
|
|
|
|
|
result = ResetEvent(AsyncState.u.hEvent);
|
|
CL_ASSERT(result != 0);
|
|
|
|
//
|
|
// Now hook onto NM node state down event mechanism to detect node downs.
|
|
//
|
|
Node = NmReferenceNodeById(LeaderNodeId);
|
|
CL_ASSERT(Node != NULL);
|
|
if (Node == NULL) {
|
|
Status = GetLastError();
|
|
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[GUM] NmpGetMulticastKeyFromNMLeader: Failed to reference leader "
|
|
"node id %1!u!, status %2!u!\n",
|
|
LeaderNodeId, Status
|
|
);
|
|
|
|
goto error_exit;
|
|
}
|
|
|
|
handleArr[0] = AsyncState.u.hEvent;
|
|
handleArr[1] = NmGetNodeStateDownEvent(Node);
|
|
|
|
//
|
|
// Get the RPC binding handle for the leader node.
|
|
//
|
|
// Note that there is a race condition here with
|
|
// ClMsgCleanup. The Session array can be freed
|
|
// before we dereference Session. This would lead
|
|
// to an unfortunate AV exception, but it would not
|
|
// be tragic since the service is already terminating
|
|
// abnormally if ClMsgCleanup is executing.
|
|
//
|
|
if (Session != NULL) {
|
|
rpcBinding = Session[LeaderNodeId];
|
|
} else {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[GUM] NmpGetMulticastKeyFromNMLeader: No RPC "
|
|
"binding handle for leader node id %1!u!.\n",
|
|
LeaderNodeId
|
|
);
|
|
Status = ERROR_CLUSTER_NODE_UNREACHABLE;
|
|
goto error_exit;
|
|
}
|
|
|
|
try {
|
|
|
|
|
|
//
|
|
// Get multicast key from the leader
|
|
//
|
|
Status = NmRpcGetNetworkMulticastKey(
|
|
&AsyncState,
|
|
rpcBinding,
|
|
NodeIdString,
|
|
NetworkId,
|
|
MulticastKey
|
|
);
|
|
|
|
if (Status == RPC_S_OK) {
|
|
DWORD RpcStatus;
|
|
|
|
//
|
|
// The call is pending. Wait for completion.
|
|
//
|
|
Status = WaitForMultipleObjects(
|
|
2,
|
|
handleArr,
|
|
FALSE,
|
|
INFINITE
|
|
);
|
|
|
|
if (Status != WAIT_OBJECT_0) {
|
|
//
|
|
// Something went wrong.
|
|
// Either this is a rpc failure or, the target node went down.
|
|
//
|
|
CL_ASSERT(Status != WAIT_OBJECT_0);
|
|
Status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[GUM] NmpGetMulticastKeyFromNMLeader: Wait for NmRpcGetNetworkMulticastKey"
|
|
" failed, status %1!u!\n",
|
|
Status
|
|
);
|
|
|
|
//
|
|
// Cancel the call, just to be safe.
|
|
//
|
|
RpcStatus = RpcAsyncCancelCall(
|
|
&AsyncState,
|
|
TRUE // Abortive cancel
|
|
);
|
|
if (RpcStatus != RPC_S_OK) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[GUM] NmpGetMulticastKeyFromNMLeader: RpcAsyncCancelCall()= "
|
|
" %1!u!\n",
|
|
RpcStatus
|
|
);
|
|
}
|
|
CL_ASSERT(RpcStatus == RPC_S_OK);
|
|
|
|
//
|
|
// Wait for the call to complete.
|
|
//
|
|
Status = WaitForSingleObject(
|
|
AsyncState.u.hEvent,
|
|
INFINITE
|
|
);
|
|
if (Status != WAIT_OBJECT_0) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[GUM] NmpGetMulticastKeyFromNMLeader: WaitForSingleObject()= "
|
|
" %1!u!\n",
|
|
Status
|
|
);
|
|
}
|
|
CL_ASSERT(Status == WAIT_OBJECT_0);
|
|
}
|
|
|
|
//
|
|
// The call should now be complete. Get the
|
|
// completion status. Any RPC error will be
|
|
// returned in 'RpcStatus'. If there was no
|
|
// RPC error, then any application error will
|
|
// be returned in 'Status'.
|
|
//
|
|
RpcStatus = RpcAsyncCompleteCall(
|
|
&AsyncState,
|
|
&Status
|
|
);
|
|
|
|
if (RpcStatus != RPC_S_OK) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[GUM] NmpGetMulticastKeyFromNMLeader: Failed to get "
|
|
"completion status for async RPC call,"
|
|
"status %1!u!\n",
|
|
RpcStatus
|
|
);
|
|
Status = RpcStatus;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// An error was returned synchronously.
|
|
//
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[GUM] NmpGetMulticastKeyFromNMLeader: NmRpcGetNetworkMulticastKey() "
|
|
"failed synchronously, status %1!u!\n",
|
|
Status
|
|
);
|
|
}
|
|
|
|
} except (I_RpcExceptionFilter(RpcExceptionCode())) {
|
|
Status = GetExceptionCode();
|
|
}
|
|
|
|
error_exit:
|
|
|
|
if (AsyncState.u.hEvent != NULL) {
|
|
CloseHandle(AsyncState.u.hEvent);
|
|
}
|
|
|
|
if (Node != NULL) {
|
|
OmDereferenceObject(Node);
|
|
}
|
|
|
|
return(Status);
|
|
|
|
} // NmpGetMulticastKeyFromNMLeader
|
|
|
|
|
|
#ifdef MULTICAST_DEBUG
|
|
DWORD
|
|
NmpDbgPrintData(LPCWSTR InfoStr,
|
|
PVOID Data,
|
|
DWORD DataLen
|
|
)
|
|
{
|
|
DWORD i;
|
|
|
|
|
|
ClRtlLogPrint(
|
|
LOG_NOISE,
|
|
"\n\n%1!ws!\n",
|
|
InfoStr);
|
|
|
|
|
|
for (i=0; i<DataLen/sizeof(DWORD); i++)
|
|
{
|
|
ClRtlLogPrint(
|
|
LOG_NOISE,
|
|
"%1!u! = %2!u!\n",
|
|
i,
|
|
*((DWORD *)Data+i)
|
|
);
|
|
}
|
|
|
|
|
|
ClRtlLogPrint(
|
|
LOG_NOISE,
|
|
"\n\n"
|
|
);
|
|
return ERROR_SUCCESS;
|
|
|
|
} // NmpDbgPrintData()
|
|
#endif
|
|
|
|
|