Source code of Windows XP (NT5)
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

/*++
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 */