mirror of https://github.com/tongzx/nt5src
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.
516 lines
15 KiB
516 lines
15 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
crypto.c
|
|
|
|
Abstract:
|
|
|
|
routines for encrypting/decrypting random data blob. heavily modelled
|
|
after service\cp\crypto.c
|
|
|
|
Author:
|
|
|
|
Charlie Wickham (charlwi) 14-Feb-2001
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "clusres.h"
|
|
#include "clusrtl.h"
|
|
#include "netname.h"
|
|
|
|
#include <wincrypt.h>
|
|
#include <lm.h>
|
|
|
|
//
|
|
// header for encrypted data.
|
|
//
|
|
#define SALT_SIZE 16
|
|
#define IV_SIZE 8
|
|
|
|
typedef struct _NETNAME_ENCRYPTED_DATA {
|
|
DWORD Version;
|
|
struct _NETNAME_ENCRYPTION_INITIALIZATION_DATA {
|
|
BYTE IV[IV_SIZE];
|
|
BYTE Salt[SALT_SIZE];
|
|
} InitData;
|
|
BYTE Data[0];
|
|
} NETNAME_ENCRYPTED_DATA, *PNETNAME_ENCRYPTED_DATA;
|
|
|
|
// current version for the CRYPTO_KEY_INFO struct
|
|
#define NETNAME_ENCRYPTED_DATA_VERSION 1
|
|
|
|
DWORD
|
|
GenSymKey(
|
|
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);
|
|
} // GenSymKey
|
|
|
|
DWORD
|
|
EncryptNetNameData(
|
|
RESOURCE_HANDLE ResourceHandle,
|
|
LPWSTR MachinePwd,
|
|
PBYTE * EncryptedInfo,
|
|
PDWORD EncryptedInfoLength,
|
|
HKEY Key
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
encrypt the password, set a pointer to the encrypted data and store it in
|
|
the registry.
|
|
|
|
There is a chicken/egg problem of sorts in that we have to generate the
|
|
key before we can use it to encrypt data. This requires having a spot to
|
|
store the Salt and IV. Since we have to hold all the info in one buffer
|
|
for the registry write, we tempoarily use stack-based buffer
|
|
(keyGenBuffer) for the header info. Once we know the size of the encrypted
|
|
data, we can allocate the proper sized buffer (encryptedInfo), copy
|
|
everything into it, and write the registry. It is this buffer that is
|
|
handed back to the caller via the EncryptedInfo argument.
|
|
|
|
Arguments:
|
|
|
|
ResourceHandle - for logging to the cluster log
|
|
|
|
MachinePwd - pointer to unicode string password
|
|
|
|
EncryptedInfo - address of a pointer that receives a pointer to the encrypted blob
|
|
|
|
EncryptedInfoLength - pointer to a DWORD that receives the length of the blob
|
|
|
|
Key - handle to netname parameters key where the data is stored
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS, otherwise Win32 error
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
DWORD encInfoLength;
|
|
DWORD encDataLength = 0;
|
|
BOOL success;
|
|
DWORD pwdLength = ( wcslen( MachinePwd ) + 1 ) * sizeof( WCHAR );
|
|
|
|
HCRYPTPROV cryptoProvider = 0;
|
|
HCRYPTKEY sessionKey = 0;
|
|
|
|
NETNAME_ENCRYPTED_DATA keyGenBuffer; // temp header buffer to generate key
|
|
PNETNAME_ENCRYPTED_DATA encryptedInfo = NULL; // final data area
|
|
|
|
|
|
//
|
|
// get a handle to the full RSA provider
|
|
//
|
|
if ( !CryptAcquireContext(&cryptoProvider,
|
|
NULL,
|
|
NULL,
|
|
PROV_RSA_FULL,
|
|
CRYPT_MACHINE_KEYSET | CRYPT_SILENT))
|
|
{
|
|
status = GetLastError();
|
|
if ( status == NTE_BAD_KEYSET ) {
|
|
success = CryptAcquireContext(&cryptoProvider,
|
|
NULL,
|
|
NULL,
|
|
PROV_RSA_FULL,
|
|
CRYPT_MACHINE_KEYSET |
|
|
CRYPT_SILENT |
|
|
CRYPT_NEWKEYSET);
|
|
|
|
status = success ? ERROR_SUCCESS : GetLastError();
|
|
}
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Can't acquire crypto context for encrypt. status %1!u!.\n",
|
|
status);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// generate the session key.
|
|
//
|
|
if ( !CryptGenRandom(cryptoProvider,
|
|
sizeof(struct _NETNAME_ENCRYPTION_INITIALIZATION_DATA),
|
|
(PBYTE)&keyGenBuffer.InitData))
|
|
{
|
|
status = GetLastError();
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Can't generate seed data. status %1!u!.\n",
|
|
status);
|
|
goto cleanup;
|
|
}
|
|
keyGenBuffer.Version = NETNAME_ENCRYPTED_DATA_VERSION;
|
|
|
|
status = GenSymKey(cryptoProvider,
|
|
keyGenBuffer.InitData.Salt,
|
|
keyGenBuffer.InitData.IV,
|
|
&sessionKey);
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Can't generate session key for encrypt. status %1!u!.\n",
|
|
status);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// find the size we need for the buffer to receive the encrypted data
|
|
//
|
|
encDataLength = pwdLength;
|
|
if ( CryptEncrypt(sessionKey,
|
|
0,
|
|
TRUE,
|
|
0,
|
|
NULL,
|
|
&encDataLength,
|
|
0))
|
|
{
|
|
//
|
|
// alloc a buffer large enough to hold the data and copy the password into it.
|
|
//
|
|
ASSERT( encDataLength >= pwdLength );
|
|
|
|
encInfoLength = sizeof( NETNAME_ENCRYPTED_DATA ) + encDataLength;
|
|
|
|
encryptedInfo = HeapAlloc( GetProcessHeap(), 0, encInfoLength );
|
|
|
|
if ( encryptedInfo != NULL ) {
|
|
wcscpy( (PWCHAR)encryptedInfo->Data, MachinePwd );
|
|
|
|
if ( CryptEncrypt(sessionKey,
|
|
0,
|
|
TRUE,
|
|
0,
|
|
encryptedInfo->Data,
|
|
&pwdLength,
|
|
encDataLength))
|
|
{
|
|
*encryptedInfo = keyGenBuffer;
|
|
|
|
status = ResUtilSetBinaryValue(Key,
|
|
PARAM_NAME__RANDOM,
|
|
(const LPBYTE)encryptedInfo,
|
|
encInfoLength,
|
|
NULL,
|
|
NULL);
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Can't write %1!u! bytes of data to registry. status %2!u!.\n",
|
|
encInfoLength,
|
|
status);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else {
|
|
status = GetLastError();
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Can't encrypt %1!u! bytes. status %2!u!.\n",
|
|
pwdLength,
|
|
status);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Can't allocate %1!u! bytes for encrypted data. status %2!u!.\n",
|
|
encInfoLength,
|
|
status);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else {
|
|
status = GetLastError();
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Can't determine size of encrypted data buffer for %1!u! bytes of data. status %2!u!.\n",
|
|
pwdLength,
|
|
status);
|
|
goto cleanup;
|
|
}
|
|
|
|
*EncryptedInfoLength = encInfoLength;
|
|
*EncryptedInfo = (PBYTE)encryptedInfo;
|
|
|
|
cleanup:
|
|
|
|
if ( status != ERROR_SUCCESS && encryptedInfo != NULL ) {
|
|
HeapFree( GetProcessHeap(), 0, encryptedInfo );
|
|
}
|
|
|
|
if ( sessionKey != 0 ) {
|
|
if ( !CryptDestroyKey( sessionKey )) {
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_WARNING,
|
|
L"Couldn't destory session key. status %1!u!.\n",
|
|
GetLastError());
|
|
}
|
|
}
|
|
|
|
if ( !CryptReleaseContext( cryptoProvider, 0 )) {
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_WARNING,
|
|
L"Can't release crypto context. status %1!u!.\n",
|
|
GetLastError());
|
|
}
|
|
|
|
return status;
|
|
} // EncryptNetNameData
|
|
|
|
DWORD
|
|
DecryptNetNameData(
|
|
RESOURCE_HANDLE ResourceHandle,
|
|
PBYTE EncryptedInfo,
|
|
DWORD EncryptedInfoLength,
|
|
LPWSTR MachinePwd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reverse of encrypt routine - decrypt random blob and hand back the
|
|
password
|
|
|
|
Arguments:
|
|
|
|
ResourceHandle - used to log into the cluster log
|
|
|
|
EncryptedInfo - pointer to encrypted info header and data
|
|
|
|
EncryptedInfoLength - # of bytes in EncryptedInfo
|
|
|
|
MachinePwd - pointer to buffer that receives the unicode password
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS, otherwise Win32 error
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
DWORD encDataLength = EncryptedInfoLength - sizeof( NETNAME_ENCRYPTED_DATA );
|
|
BOOL success;
|
|
DWORD pwdByteLength;
|
|
DWORD pwdBufferSize;
|
|
PWCHAR machinePwd = NULL;
|
|
|
|
HCRYPTPROV cryptoProvider = 0;
|
|
HCRYPTKEY sessionKey = 0;
|
|
|
|
PNETNAME_ENCRYPTED_DATA encryptedInfo = (PNETNAME_ENCRYPTED_DATA)EncryptedInfo;
|
|
|
|
//
|
|
// get a handle to the full RSA provider
|
|
//
|
|
if ( !CryptAcquireContext(&cryptoProvider,
|
|
NULL,
|
|
NULL,
|
|
PROV_RSA_FULL,
|
|
CRYPT_MACHINE_KEYSET | CRYPT_SILENT))
|
|
{
|
|
status = GetLastError();
|
|
if ( status == NTE_BAD_KEYSET ) {
|
|
success = CryptAcquireContext(&cryptoProvider,
|
|
NULL,
|
|
NULL,
|
|
PROV_RSA_FULL,
|
|
CRYPT_MACHINE_KEYSET |
|
|
CRYPT_SILENT |
|
|
CRYPT_NEWKEYSET);
|
|
|
|
status = success ? ERROR_SUCCESS : GetLastError();
|
|
}
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Can't acquire crypto context for decrypt. status %1!u!.\n",
|
|
status);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
status = GenSymKey(cryptoProvider,
|
|
encryptedInfo->InitData.Salt,
|
|
encryptedInfo->InitData.IV,
|
|
&sessionKey);
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Can't generate session key for decrypt. status %1!u!.\n",
|
|
status);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// CryptDecrypt writes the decrypted data back into the buffer that was
|
|
// holding the encrypted data. For this reason, allocate a new buffer that
|
|
// will eventually contain the password.
|
|
//
|
|
pwdByteLength = ( LM20_PWLEN + 1 ) * sizeof( WCHAR );
|
|
pwdBufferSize = ( pwdByteLength > encDataLength ? pwdByteLength : encDataLength );
|
|
|
|
machinePwd = HeapAlloc( GetProcessHeap(), 0, pwdBufferSize );
|
|
if ( machinePwd != NULL ) {
|
|
RtlCopyMemory( machinePwd, encryptedInfo->Data, encDataLength );
|
|
|
|
if ( CryptDecrypt(sessionKey,
|
|
0,
|
|
TRUE,
|
|
0,
|
|
(PBYTE)machinePwd,
|
|
&encDataLength))
|
|
{
|
|
ASSERT( pwdByteLength == encDataLength );
|
|
wcscpy( MachinePwd, machinePwd );
|
|
}
|
|
else {
|
|
status = GetLastError();
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Can't decrypt %1!u! bytes of data. status %2!u!.\n",
|
|
encDataLength,
|
|
status);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Can't allocate %1!u! bytes for decrypt. status %2!u!.\n",
|
|
pwdBufferSize,
|
|
status);
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if ( machinePwd != NULL) {
|
|
HeapFree( GetProcessHeap(), 0, machinePwd );
|
|
}
|
|
|
|
if ( sessionKey != 0 ) {
|
|
if ( !CryptDestroyKey( sessionKey )) {
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_WARNING,
|
|
L"Couldn't destory session key. status %1!u!.\n",
|
|
GetLastError());
|
|
}
|
|
}
|
|
|
|
if ( !CryptReleaseContext( cryptoProvider, 0 )) {
|
|
(NetNameLogEvent)(ResourceHandle,
|
|
LOG_WARNING,
|
|
L"Can't release crypto context. status %1!u!.\n",
|
|
GetLastError());
|
|
}
|
|
|
|
return status;
|
|
} // DecryptNetNameData
|
|
|
|
|
|
/* end crypto.c */
|