Leaked source code of windows server 2003
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.
 
 
 
 
 
 

812 lines
21 KiB

/* Copyright (c) 1994, Microsoft Corporation, all rights reserved
**
** pwutil.c
** Remote Access
** Password handling routines
**
** 03/01/94 Steve Cobb
*/
#include <windows.h>
#include <wincrypt.h>
#include <stdlib.h>
#include <string.h>
#include "pwutil.h"
#include "common.h"
#include "debug.h"
#define PASSWORDMAGIC 0xA5
#define Malloc( x ) LocalAlloc( LMEM_ZEROINIT, x )
#define Free(x) LocalFree(x)
long g_EncodeMemoryAlloc = 0;
VOID
ReverseSzA(
CHAR* psz )
/* Reverses order of characters in 'psz'.
*/
{
CHAR* pszBegin;
CHAR* pszEnd;
for (pszBegin = psz, pszEnd = psz + strlen( psz ) - 1;
pszBegin < pszEnd;
++pszBegin, --pszEnd)
{
CHAR ch = *pszBegin;
*pszBegin = *pszEnd;
*pszEnd = ch;
}
}
VOID
ReverseSzW(
WCHAR* psz )
/* Reverses order of characters in 'psz'.
*/
{
WCHAR* pszBegin;
WCHAR* pszEnd;
for (pszBegin = psz, pszEnd = psz + wcslen( psz ) - 1;
pszBegin < pszEnd;
++pszBegin, --pszEnd)
{
WCHAR ch = *pszBegin;
*pszBegin = *pszEnd;
*pszEnd = ch;
}
}
VOID
DecodePasswordA(
IN OUT CHAR* pszPassword )
/* Un-obfuscate 'pszPassword' in place.
**
** Returns the address of 'pszPassword'.
*/
{
EncodePasswordA( pszPassword );
}
VOID
DecodePasswordW(
IN OUT WCHAR* pszPassword )
/* Un-obfuscate 'pszPassword' in place.
**
** Returns the address of 'pszPassword'.
*/
{
EncodePasswordW( pszPassword );
}
VOID
EncodePasswordA(
IN OUT CHAR* pszPassword )
/* Obfuscate 'pszPassword' in place to foil memory scans for passwords.
**
** Returns the address of 'pszPassword'.
*/
{
if (pszPassword)
{
CHAR* psz;
ReverseSzA( pszPassword );
for (psz = pszPassword; *psz != '\0'; ++psz)
{
if (*psz != PASSWORDMAGIC)
*psz ^= PASSWORDMAGIC;
}
}
}
VOID
EncodePasswordW(
IN OUT WCHAR* pszPassword )
/* Obfuscate 'pszPassword' in place to foil memory scans for passwords.
**
** Returns the address of 'pszPassword'.
*/
{
if (pszPassword)
{
WCHAR* psz;
ReverseSzW( pszPassword );
for (psz = pszPassword; *psz != L'\0'; ++psz)
{
if (*psz != PASSWORDMAGIC)
*psz ^= PASSWORDMAGIC;
}
}
}
VOID
WipePasswordA(
IN OUT CHAR* pszPassword )
/* Zero out the memory occupied by a password.
**
** Returns the address of 'pszPassword'.
*/
{
if (pszPassword)
{
CHAR* psz = pszPassword;
while (*psz != '\0')
*psz++ = '\0';
}
}
VOID
WipePasswordW(
IN OUT WCHAR* pszPassword )
/* Zero out the memory occupied by a password.
**
** Returns the address of 'pszPassword'.
*/
{
if (pszPassword)
{
WCHAR* psz = pszPassword;
while (*psz != L'\0')
*psz++ = L'\0';
}
}
long IncPwdEncoded()
{
long lCounter = 0;
lCounter = InterlockedIncrement( &g_EncodeMemoryAlloc);
TRACE1("g_EncodeMemoryAlloc=%ld\n",lCounter);
return lCounter;
}
long DecPwdEncoded()
{
long lCounter = 0;
lCounter= InterlockedDecrement( &g_EncodeMemoryAlloc);
TRACE1("g_EncodeMemoryAlloc=%ld\n",lCounter);
return lCounter;
}
long TotalPwdEncoded()
{
return g_EncodeMemoryAlloc;
}
//Added for Secuely storing password in memory.
//For .Net 534499 and LH 754400
void FreeEncryptBlob(
DATA_BLOB * pIn )
{
ASSERT( NULL != pIn);
if( NULL == pIn )
{
return;
}
else
{
if( NULL != pIn->pbData )
{
RtlSecureZeroMemory(pIn->pbData, pIn->cbData);
LocalFree( pIn->pbData );
}
RtlSecureZeroMemory(pIn, sizeof(DATA_BLOB) );
Free( pIn );
}
}
//On XP and below, only CryptProtectData() is available which will allocate memory and the caller has
//to free the memory.
//On .Net and above, CryptProtectMeory() is available too which does encryption in place
//To simplifiy coding use macros and USE_PROTECT_MEMORY flag to switch between these two sets of
// APIs.
#ifdef USE_PROTECT_MEMORY
//Meant for copy the password
DWORD CopyMemoryInPlace(
IN OUT PBYTE pbDest,
IN DWORD dwDestSize,
IN PBYTE pbSrc,
IN DWORD dwSrcSize)
{
DWORD dwErr = NO_ERROR;
ASSERT( NULL!=pbDest);
ASSERT( NULL != pbSrc );
ASSERT( 0 != dwDestSize );
ASSERT( 0 != dwSrcSize);
ASSERT( dwDestSize == dwSrcSize );
if( NULL == pbDest ||
NULL == pbSrc ||
0 == dwDestSize ||
0 == dwSrcSize ||
dwDestSize != dwSrcSize )
{
return ERROR_INVALID_PARAMETER;
}
if( dwDestSize < dwSrcSize)
{
ASSERT(dwDestSize >= dwSrcSize);
TRACE("dwDestSize is wronly less than dwSrcSize");
return ERROR_INVALID_PARAMETER;
}
CopyMemory(pbDest,pbSrc, dwDestSize);
return dwErr;
}
//A wrapper for RtlSecureZeroMemory()
//
DWORD WipeMemoryInPlace(
IN OUT PBYTE pbIn,
IN DWORD dwInSize)
{
DWORD dwErr = NO_ERROR;
ASSERT( NULL != pbIn );
ASSERT( 0 != dwInSize );
if( NULL == pbIn ||
0 == dwInSize)
{
return ERROR_INVALID_PARAMETER;
}
RtlSecureZeroMemory( pbIn, dwInSize);
return dwErr;
}
//(1)This function will encryp an input buffer and store the encrypted password in position
//(2) the size of the input buffer is required to be mulitple times of 16 bytes that is set by the
// underlying function CryptProtectMemory();
//
DWORD EncryptMemoryInPlace(
IN OUT PBYTE pbIn,
IN DWORD dwInSize)
{
DWORD dwErr = NO_ERROR;
BOOL fSuccess = FALSE;
ASSERT( NULL != pbIn );
ASSERT( 0!= dwInSize );
ASSERT( 0 == dwInSize %16 );
if( NULL == pbIn ||
0 == dwInSize ||
0 != dwInSize %16 )
{
return ERROR_INVALID_PARAMETER;
}
fSuccess = CryptProtectMemory(
pbIn,
dwInSize,
CRYPTPROTECTMEMORY_SAME_PROCESS);
if( FALSE == fSuccess)
{
ASSERTMSG("EncryptMemoryInPlace()--CryptProtectMemory() failed");
dwErr = GetLastError();
TRACE2("EncryptMemoryInPlace()--CryptProtectMemory() failed:0x%x=0n%d",dwErr,dwErr);
}
return dwErr;
}
//(1)This function will decrypt an input buffer and store the clear text password in position
//(2) the size of the input buffer is required to be mulitple times of 16 bytes that is set by the
// underlying function CryptUpprotectMemory();
//
DWORD DecryptMemoryInPlace(
IN OUT PBYTE pbIn,
IN DWORD dwInSize)
{
DWORD dwErr = NO_ERROR;
BOOL fSuccess = FALSE;
ASSERT( NULL != pbIn );
ASSERT( 0!= dwInSize );
ASSERT( 0 == dwInSize %16 );
if( NULL == pbIn ||
0 == dwInSize ||
0 != dwInSize %16 )
{
return ERROR_INVALID_PARAMETER;
}
fSuccess = CryptUnprotectMemory(
pbIn,
dwInSize,
CRYPTPROTECTMEMORY_SAME_PROCESS);
if( FALSE == fSuccess)
{
ASSERTMSG("DecryptMemoryInPlace()--CryptUnprotectMemory() failed");
dwErr = GetLastError();
TRACE2("DecryptMemoryInPlace()--CryptUnprotectMemory() failed:0x%x=0n%d",dwErr,dwErr);
}
return dwErr;
}
//(1)CryptProtectData requires the size of the input memory is multiple times of 16 bytes
//(2)So the actual maximum length of the password is always recommend to be PWLEN
//(3)usually the password buffer is declared as szPassword[PWLEN+1] which is not exactly the mulitple
// times of 16 bytes, but the valid part is.
//(4) For a buffer in (3). no matter how short the actual password is, we just encrypt the maximum
// usable buffer which is a trimmed value of the oringinal buffer length.
DWORD TrimToMul16(
IN DWORD dwSize)
{
return dwSize/16*16;
}
#else
//Structure to store the pointer to encrypted data blob and a signature
typedef struct
{
DATA_BLOB ** pSignature;
DATA_BLOB * pBlob;
}STRSIGNATURE, * PSTRSIGNATURE;
BOOL
IsBufferEncoded(
IN PSTRSIGNATURE pSig )
{
BOOL fEncoded = FALSE;
ASSERT( pSig );
if( NULL == pSig )
{
TRACE("IsBufferEncoded(): input NULL pointer!");
return FALSE;
}
fEncoded = (pSig->pSignature == &(pSig->pBlob) );
return fEncoded;
}
DWORD DecryptInBlob(
IN PBYTE pbIn,
IN DWORD dwInSize,
OUT DATA_BLOB ** ppOut)
{
DWORD dwErr = NO_ERROR;
DATA_BLOB blobIn;
DATA_BLOB * pblobOut = NULL;
BOOL fSuccess = FALSE;
ASSERT( NULL != pbIn );
ASSERT( NULL != ppOut );
ASSERT( 0 != dwInSize);
if( NULL == pbIn ||
NULL == ppOut ||
0 == dwInSize)
{
return ERROR_INVALID_PARAMETER;
}
pblobOut = (DATA_BLOB *)Malloc(sizeof(DATA_BLOB));
if( NULL == pblobOut )
{
return ERROR_NOT_ENOUGH_MEMORY;
}
RtlSecureZeroMemory( pblobOut, sizeof(DATA_BLOB) );
blobIn.pbData = pbIn;
blobIn.cbData = dwInSize;
// TRACE1("Un-Protected data length:%d\n",blobIn.cbData);
// TRACE1("Un-Protected data is:%s\n",blobIn.pbData);
fSuccess = CryptUnprotectData(
&blobIn,
NULL,
NULL,
NULL,
NULL,
CRYPTPROTECT_UI_FORBIDDEN,
pblobOut);
if( FALSE == fSuccess )
{
ASSERTMSG("DecryptInBlob()-->CryptUnprotectData() failed");
dwErr = GetLastError();
TRACE2("DecryptInBlob()-->CryptUnprotectData() failed:0x%x=0n%d",dwErr,dwErr);
}
// TRACE1("Protected data length:%d\n",blobOut.cbData);
// TRACE1("Protected data is:%s\n",blobOut.pbData);
if( NO_ERROR != dwErr )
{
if( NULL != pblobOut )
{
if( NULL != pblobOut->pbData )
{
LocalFree( pblobOut->pbData );
}
Free( pblobOut );
}
}
else
{
*ppOut = pblobOut;
}
return dwErr;
}
// The real function to call CryptProtectData() to encrypte the password and return the encrypted
// data in an allocated meory
DWORD EncryptInBlob(
IN PBYTE pbIn,
IN DWORD dwInSize,
OUT DATA_BLOB ** ppOut)
{
DWORD dwErr = NO_ERROR;
DATA_BLOB blobIn;
DATA_BLOB * pblobOut = NULL;
BOOL fSuccess = FALSE;
ASSERT( NULL != pbIn );
ASSERT( NULL != ppOut );
ASSERT( 0 != dwInSize);
if( NULL == pbIn ||
NULL == ppOut ||
0 == dwInSize)
{
return ERROR_INVALID_PARAMETER;
}
*ppOut = NULL;
pblobOut = (DATA_BLOB *)Malloc(sizeof(DATA_BLOB));
if( NULL == pblobOut )
{
return ERROR_NOT_ENOUGH_MEMORY;
}
RtlSecureZeroMemory( pblobOut, sizeof(DATA_BLOB) );
do
{
blobIn.pbData = pbIn;
blobIn.cbData = dwInSize;
fSuccess = CryptProtectData(
&blobIn,
NULL,
NULL,
NULL,
NULL,
CRYPTPROTECT_UI_FORBIDDEN,
pblobOut);
if( FALSE == fSuccess )
{
ASSERTMSG("EncryptInBlob()-->CryptProtectData() failed");
dwErr = GetLastError();
TRACE2("EncryptInBlob()-->CryptProtectData() failed:0x%x=0n%d",dwErr,dwErr);
break;
}
}
while(FALSE);
if( NO_ERROR != dwErr )
{
if( NULL != pblobOut )
{
if( NULL != pblobOut->pbData )
{
LocalFree( pblobOut->pbData );
}
Free( pblobOut );
}
}
else
{
*ppOut = pblobOut;
}
return dwErr;
}
// (1)This function finally calls CryptProtectData() to encrypt the original password in the input buffer
// (2) This func will allocate a DATA_BLOB memory and store the encrpted data in
// it, then zero out the original password buffer, store the pointer to the encrypted data in the
// original password buffer
// (3) if the orignial password buffer is not large enough to store the
// pointer(32 bit or 64 bit depends on the platform) it will return error
//
DWORD EncodePasswordInPlace(
IN OUT PBYTE pbIn,
IN DWORD dwInSize)
{
DWORD dwErr = NO_ERROR, dwSidLen = 0;
DATA_BLOB * pEncrypt = NULL;
PSTRSIGNATURE pSig = NULL;
ASSERT( NULL != pbIn );
ASSERT( 0 != dwInSize );
ASSERT( dwInSize >=sizeof(STRSIGNATURE) );
if( NULL == pbIn ||
0 == dwInSize ||
dwInSize < sizeof(STRSIGNATURE) )
{
return ERROR_INVALID_PARAMETER;
}
__try
{
if( IsBufferEncoded((PSTRSIGNATURE)pbIn) )
{
ASSERTMSG( "This buffer is already encoded, encode it twice?");
TRACE("EncodePasswordInPlace():This buffer is already encoded, encode it twice?");
}
dwErr = EncryptInBlob( pbIn, dwInSize, &pEncrypt );
if( NO_ERROR != dwErr )
{
__leave;
}
RtlSecureZeroMemory( pbIn, dwInSize );
//Generate signature
//Encryptedl buffer pbin will be zeroed out and store two pieces of information
// (1) the second piece is the pointer to the DATA_BLOB containing the encrypted data info
// (2) the first piece is the address where the second piece is stored in this buffer.
//
pSig = (PSTRSIGNATURE)pbIn;
pSig->pSignature = &(pSig->pBlob);
pSig->pBlob = pEncrypt;
}
__finally
{
if( NO_ERROR != dwErr )
{
FreeEncryptBlob( pEncrypt );
}
else
{
IncPwdEncoded();
}
}
return dwErr;
}
// (1) This function assume the input data is already treated by
// EncodePasswordInPlace() function, it will take the first 32 bit or 64 bits
// (depend on the platform) as the pointer to a DATA_BLOB structure.then
// decrypt that encrypt data and restore it onto the password pos.
// (2) A signauture check will be applied to the input buffer first, if there is no signautre, it will
// return failure.
// will result un-expected error like AV etc.
// (3) dwInSize is the original size of the password buffer passed into
// EncodePasswordInPlace function.
DWORD DecodePasswordInPlace(
IN OUT PBYTE pbIn,
IN DWORD dwInSize)
{
DWORD dwErr = NO_ERROR;
DATA_BLOB * pEncrypt = NULL, *pDecrypt = NULL;
PSTRSIGNATURE pSig = NULL;
ASSERT( NULL != pbIn );
ASSERT( 0 != dwInSize );
ASSERT( dwInSize >=sizeof(STRSIGNATURE) );
if( NULL == pbIn ||
0 == dwInSize ||
dwInSize < sizeof(STRSIGNATURE) )
{
return ERROR_INVALID_PARAMETER;
}
//Check signature
//The encryped buffer will be zeroed out and store two pieces of information
// (1) the second piece is the pointer to the DATA_BLOB containing the encrypted data info
// (2) the first piece is the address where the second piece is stored in this buffer.
//
pSig = (PSTRSIGNATURE)pbIn;
if( !IsBufferEncoded(pSig) )
{
ASSERTMSG("DecodePasswordInPlace(): wrong signature");
TRACE("DecodePasswordInPlace(): wrong signature");
return ERROR_INVALID_PARAMETER;
}
__try
{
pEncrypt = pSig->pBlob;
if( NULL == pEncrypt )
{
dwErr = ERROR_INVALID_PARAMETER;
__leave;
}
dwErr = DecryptInBlob( pEncrypt->pbData, pEncrypt->cbData, &pDecrypt );
if( NO_ERROR != dwErr )
{
__leave;
}
if( dwInSize != pDecrypt->cbData )
{
dwErr = ERROR_CAN_NOT_COMPLETE;
__leave;
}
RtlSecureZeroMemory( pbIn, dwInSize );
CopyMemory(pbIn, pDecrypt->pbData, dwInSize);
}
__finally
{
if( NULL != pEncrypt )
{
FreeEncryptBlob( pEncrypt );
}
if( NULL != pDecrypt )
{
FreeEncryptBlob( pDecrypt );
}
if( NO_ERROR == dwErr )
{
DecPwdEncoded();
}
}
return dwErr;
}
//(1)This function will copy the password from the source buffer to the destination buffer.
//(2) If the destination already contains an encoded password, it will decoded it to free the memory
//(3) the destination buffer will be wiped out before copying the password from the source
//(4)The resulted destination password will always be encrypted!!
//(5) Encode/Decode status of the source password will be left unchanged.
//(6) But a minimum requirement on the size of the password buffers
DWORD CopyPasswordInPlace(
IN OUT PBYTE pbDest,
IN DWORD dwDestSize,
IN PBYTE pbSrc,
IN DWORD dwSrcSize)
{
DWORD dwErr = NO_ERROR;
BOOL fSrcEncoded = FALSE;
ASSERT( NULL != pbDest );
ASSERT( NULL != pbSrc );
ASSERT( dwDestSize == dwSrcSize );
ASSERT( 0!= dwDestSize );
ASSERT( 0!= dwSrcSize );
if( 0 == dwDestSize ||
dwDestSize < sizeof(STRSIGNATURE) ||
0 == dwSrcSize ||
dwSrcSize < sizeof(STRSIGNATURE)
)
{
TRACE("CopyPasswordInPlace():the sizes of the input password buffer is invalid");
return ERROR_INVALID_PARAMETER;
}
if( IsBufferEncoded( (PSTRSIGNATURE)pbDest ) )
{
DecodePasswordInPlace(pbDest, dwDestSize);
}
RtlSecureZeroMemory(pbDest, dwDestSize);
if( IsBufferEncoded( (PSTRSIGNATURE)pbSrc ) )
{
DecodePasswordInPlace(pbSrc, dwSrcSize);
fSrcEncoded = TRUE;
}
CopyMemory(pbDest, pbSrc, dwDestSize );
EncodePasswordInPlace(pbDest, dwDestSize);
if( fSrcEncoded )
{
EncodePasswordInPlace(pbSrc, dwSrcSize);
}
return dwErr;
}
//(1)This function wipe the password buffer
//(2) if the password buffer is already encoded, it will decode it to free the memory allocated
// by CryptProtectData()
DWORD
WipePasswordInPlace(
IN OUT PBYTE pbIn,
IN DWORD dwInSize)
{
DWORD dwErr = NO_ERROR;
ASSERT( NULL!=pbIn );
ASSERT( 0 != dwInSize );
ASSERT( dwInSize >=sizeof(STRSIGNATURE) );
if( NULL == pbIn ||
0 == dwInSize ||
dwInSize < sizeof(STRSIGNATURE) )
{
return ERROR_INVALID_PARAMETER;
}
__try
{
if( IsBufferEncoded((PSTRSIGNATURE)pbIn) )
{
DecodePasswordInPlace(pbIn, dwInSize);
}
}
__finally
{
RtlSecureZeroMemory(pbIn, dwInSize);
}
return dwErr;
}
#endif