Windows2003-3790/inetcore/outlookexpress/msoeacct/propcryp.cpp
2020-09-30 16:53:55 +02:00

488 lines
14 KiB
C++

/*
** p r o p c r y p . c p p
**
** Purpose:
** Functions to provide blob-level access to the pstore
**
** History
** 3/04/97: (t-erikne) support for non-pstore systems
** 2/15/97: (t-erikne) rewritten for pstore
** 12/04/96: (sbailey) created
**
** Copyright (C) Microsoft Corp. 1996, 1997.
*/
#include "pch.hxx"
#include "propcryp.h"
#include <imnact.h>
#include <demand.h>
///////////////////////////////////////////////////////////////////////////
//
// Structures, definitions
//
#define OBFUSCATOR 0x14151875;
#define PROT_SIZEOF_HEADER 0x02 // 2 bytes in the header
#define PROT_SIZEOF_XORHEADER (PROT_SIZEOF_HEADER+sizeof(DWORD))
#define PROT_VERSION_1 0x01
#define PROT_PASS_XOR 0x01
#define PROT_PASS_PST 0x02
// Layout of registry data (v0)
//
// /--------------------------------
// | protected store name, a LPWSTR
// \--------------------------------
//
//
// Layout of registry data (v1)
//
// /----------------------------------------------------------------------
// | version (1 b) =0x01 | type (1 b) =PROT_PASS_* | data (see below)
// \----------------------------------------------------------------------
//
// data for PROT_PASS_PST
// struct _data
// { LPWSTR szPSTItemName; }
// data for PROT_PASS_XOR
// struct _data
// { DWORD cb; BYTE pb[cb]; }
//
///////////////////////////////////////////////////////////////////////////
//
// Prototypes
//
static inline BOOL FDataIsValidV0(BLOB *pblob);
static BOOL FDataIsValidV1(BYTE *pb);
static inline BOOL FDataIsPST(BYTE *pb);
static HRESULT XOREncodeProp(const BLOB *const pClear, BLOB *const pEncoded);
static HRESULT XORDecodeProp(const BLOB *const pEncoded, BLOB *const pClear);
///////////////////////////////////////////////////////////////////////////
//
// Admin functions (init, addref, release, ctor, dtor)
//
HRESULT HrCreatePropCrypt(CPropCrypt **ppPropCrypt)
{
*ppPropCrypt = new CPropCrypt();
if (NULL == *ppPropCrypt)
return TRAPHR(E_OUTOFMEMORY);
return (*ppPropCrypt)->HrInit();
}
CPropCrypt::CPropCrypt(void) : m_cRef(1), m_fInit(FALSE),
m_pISecProv(NULL)
{ }
CPropCrypt::~CPropCrypt(void)
{
ReleaseObj(m_pISecProv);
}
ULONG CPropCrypt::AddRef(void)
{ return ++m_cRef; }
ULONG CPropCrypt::Release(void)
{
if (0 != --m_cRef)
return m_cRef;
delete this;
return 0;
}
HRESULT CPropCrypt::HrInit(void)
{
HRESULT hr;
PST_PROVIDERID provId = MS_BASE_PSTPROVIDER_ID;
Assert(!m_pISecProv);
if (FAILED(hr = PStoreCreateInstance(&m_pISecProv, &provId, NULL, 0)))
{
// this is true because we will now handle
// all transactions without the protected store
m_fInit = TRUE;
hr = S_OK;
}
else if (SUCCEEDED(hr = PSTCreateTypeSubType_NoUI(
m_pISecProv,
&PST_IDENT_TYPE_GUID,
PST_IDENT_TYPE_STRING,
&PST_IMNACCT_SUBTYPE_GUID,
PST_IMNACCT_SUBTYPE_STRING)))
{
m_fInit = TRUE;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////
//
// Public encode/decode/delete functions
//
///////////////////////////////////////////////////////////////////////////
HRESULT CPropCrypt::HrEncodeNewProp(LPSTR szAccountName, BLOB *pClear, BLOB *pEncoded)
{
HRESULT hr = S_OK;
const int cchFastbuf = 50;
WCHAR szWfast[cchFastbuf];
LPWSTR szWalloc = NULL;
LPWSTR wszCookie = NULL;
BLOB blob;
DWORD dwErr;
int cchW;
AssertSz (pClear && pEncoded, "Null Parameter");
pEncoded->pBlobData = NULL;
if (m_fInit == FALSE)
return TRAPHR(E_FAIL);
if (!m_pISecProv)
{
// protected store does not exist
hr = XOREncodeProp(pClear, pEncoded);
goto exit;
}
if (szAccountName)
{
if (!MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szAccountName, -1,
szWfast, cchFastbuf))
{
dwErr = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == dwErr)
{
// get proper size and alloc buffer
cchW = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
szAccountName, -1, NULL, 0);
if (FAILED(hr = HrAlloc((LPVOID *)&szWalloc, cchW*sizeof(WCHAR))))
goto exit;
if (!(MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
szAccountName, -1, szWalloc, cchW)))
{
hr = GetLastError();
goto exit;
}
}
else
{
hr = dwErr;
goto exit;
}
}
}
else
{
szWfast[0] = '\000';
}
if (SUCCEEDED(hr = PSTSetNewData(m_pISecProv, &PST_IDENT_TYPE_GUID,
&PST_IMNACCT_SUBTYPE_GUID, szWalloc?szWalloc:szWfast, pClear, pEncoded)))
{
BYTE *pb = pEncoded->pBlobData;
DWORD sz = pEncoded->cbSize;
Assert(pb);
pEncoded->cbSize += PROT_SIZEOF_HEADER;
//N This realloc is annoying. If we assume the memory allocator used
//N by the PST function, we could be smarter....
if (FAILED(hr = HrAlloc((LPVOID *)&pEncoded->pBlobData, pEncoded->cbSize)))
goto exit;
pEncoded->pBlobData[0] = PROT_VERSION_1;
pEncoded->pBlobData[1] = PROT_PASS_PST;
Assert(2 == PROT_SIZEOF_HEADER);
CopyMemory(&pEncoded->pBlobData[PROT_SIZEOF_HEADER], pb, sz);
PSTFreeHandle(pb);
}
exit:
if (szWalloc)
MemFree(szWalloc);
if (FAILED(hr) && pEncoded->pBlobData)
MemFree(pEncoded->pBlobData);
return hr;
}
HRESULT CPropCrypt::HrEncode(BLOB *pClear, BLOB *pEncoded)
{
HRESULT hr;
PST_PROMPTINFO PromptInfo = { sizeof(PST_PROMPTINFO), 0, NULL, L""};
AssertSz (pClear && pEncoded &&
pClear->pBlobData && pClear->cbSize, "Null Parameter");
if (m_fInit == FALSE)
return TRAPHR(E_FAIL);
if (m_pISecProv)
{
if (FDataIsValidV1(pEncoded->pBlobData) && FDataIsPST(pEncoded->pBlobData))
{
Assert(pEncoded->cbSize-PROT_SIZEOF_HEADER ==
(lstrlenW((LPWSTR)(pEncoded->pBlobData+PROT_SIZEOF_HEADER))+1)*sizeof(WCHAR));
tryagain:
hr = m_pISecProv->WriteItem(
PST_KEY_CURRENT_USER,
&PST_IDENT_TYPE_GUID,
&PST_IMNACCT_SUBTYPE_GUID,
(LPCWSTR)&pEncoded->pBlobData[PROT_SIZEOF_HEADER],
(DWORD)pClear->cbSize,
pClear->pBlobData,
&PromptInfo,
PST_CF_NONE,
0);
if (PST_E_TYPE_NO_EXISTS == hr)
{
DOUTL(DOUTL_CPROP, "PropCryp: somebody ruined my type or subtype");
hr = PSTCreateTypeSubType_NoUI(
m_pISecProv,
&PST_IDENT_TYPE_GUID,
PST_IDENT_TYPE_STRING,
&PST_IMNACCT_SUBTYPE_GUID,
PST_IMNACCT_SUBTYPE_STRING);
if (SUCCEEDED(hr))
goto tryagain;
}
}
else
{
#ifdef DEBUG
if (FDataIsValidV0(pEncoded))
DOUTL(DOUTL_CPROP, "PropCryp: V0 to V1 upgrade");
else if (!FDataIsValidV1(pEncoded->pBlobData))
DOUTL(DOUTL_CPROP, "PropCryp: invalid data on save");
#endif
// now we have XOR data in a PST environment
hr = HrEncodeNewProp(NULL, pClear, pEncoded);
}
}
else
{
// protected store does not exist
hr = XOREncodeProp(pClear, pEncoded);
}
return TrapError(hr);
}
/* HrDecode:
**
** Purpose:
** Uses the protstor functions to retrieve a piece of secure data
** unless the data is not pstore, then it maps to the XOR function
** Takes:
** IN pEncoded - blob containing name to pass to PSTGetData
** OUT pClear - blob containing property data
** Notes:
** pBlobData in pClear must be freed with a call to CoTaskMemFree()
** Returns:
** hresult
*/
HRESULT CPropCrypt::HrDecode(BLOB *pEncoded, BLOB *pClear)
{
HRESULT hr;
AssertSz(pEncoded && pEncoded->pBlobData && pClear, TEXT("Null Parameter"));
pClear->pBlobData = NULL;
if (m_fInit == FALSE)
return TRAPHR(E_FAIL);
if (!FDataIsValidV1(pEncoded->pBlobData))
{
if (FDataIsValidV0(pEncoded))
{
DOUTL(DOUTL_CPROP, "PropCryp: obtaining v0 value");
// looks like we might have a v0 blob: the name string
hr = PSTGetData(m_pISecProv, &PST_IDENT_TYPE_GUID, &PST_IMNACCT_SUBTYPE_GUID,
(LPCWSTR)pEncoded->pBlobData, pClear);
}
else
hr = E_InvalidValue;
}
else if (FDataIsPST(pEncoded->pBlobData))
{
Assert(pEncoded->cbSize-PROT_SIZEOF_HEADER ==
(lstrlenW((LPWSTR)(pEncoded->pBlobData+PROT_SIZEOF_HEADER))+1)*sizeof(WCHAR));
hr = PSTGetData(m_pISecProv, &PST_IDENT_TYPE_GUID, &PST_IMNACCT_SUBTYPE_GUID,
(LPCWSTR)&pEncoded->pBlobData[PROT_SIZEOF_HEADER], pClear);
}
else
{
hr = XORDecodeProp(pEncoded, pClear);
}
return hr;
}
HRESULT CPropCrypt::HrDelete(BLOB *pProp)
{
HRESULT hr;
PST_PROMPTINFO PromptInfo = { sizeof(PST_PROMPTINFO), 0, NULL, L""};
if (m_fInit == FALSE)
return TRAPHR(E_FAIL);
if (m_pISecProv && FDataIsValidV1(pProp->pBlobData) && FDataIsPST(pProp->pBlobData))
{
Assert(pProp->cbSize-PROT_SIZEOF_HEADER ==
(lstrlenW((LPWSTR)(pProp->pBlobData+PROT_SIZEOF_HEADER))+1)*sizeof(WCHAR));
hr = m_pISecProv->DeleteItem(
PST_KEY_CURRENT_USER,
&PST_IDENT_TYPE_GUID,
&PST_IMNACCT_SUBTYPE_GUID,
(LPCWSTR)&pProp->pBlobData[PROT_SIZEOF_HEADER],
&PromptInfo,
0);
}
else
// nothing to do
hr = S_OK;
return hr;
}
///////////////////////////////////////////////////////////////////////////
//
// XOR functions
//
///////////////////////////////////////////////////////////////////////////
HRESULT XOREncodeProp(const BLOB *const pClear, BLOB *const pEncoded)
{
DWORD dwSize;
DWORD last, last2;
DWORD *pdwCypher;
DWORD dex;
pEncoded->cbSize = pClear->cbSize+PROT_SIZEOF_XORHEADER;
if (!MemAlloc((LPVOID *)&pEncoded->pBlobData, pEncoded->cbSize))
return E_OUTOFMEMORY;
// set up header data
Assert(2 == PROT_SIZEOF_HEADER);
pEncoded->pBlobData[0] = PROT_VERSION_1;
pEncoded->pBlobData[1] = PROT_PASS_XOR;
*((DWORD *)&(pEncoded->pBlobData[2])) = pClear->cbSize;
// nevermind that the pointer is offset by the header size, this is
// where we start to write out the modified password
pdwCypher = (DWORD *)&(pEncoded->pBlobData[PROT_SIZEOF_XORHEADER]);
dex = 0;
last = OBFUSCATOR; // 0' = 0 ^ ob
if (dwSize = pClear->cbSize / sizeof(DWORD))
{
// case where data is >= 4 bytes
for (; dex < dwSize; dex++)
{
last2 = ((DWORD *)pClear->pBlobData)[dex]; // 1
pdwCypher[dex] = last2 ^ last; // 1' = 1 ^ 0
last = last2; // save 1 for the 2 round
}
}
// if we have bits left over
// note that dwSize is computed now in bits
if (dwSize = (pClear->cbSize % sizeof(DWORD))*8)
{
// need to not munge memory that isn't ours
last >>= sizeof(DWORD)*8-dwSize;
pdwCypher[dex] &= ((DWORD)-1) << dwSize;
pdwCypher[dex] |=
((((DWORD *)pClear->pBlobData)[dex] & (((DWORD)-1) >> (sizeof(DWORD)*8-dwSize))) ^ last);
}
return S_OK;
}
HRESULT XORDecodeProp(const BLOB *const pEncoded, BLOB *const pClear)
{
DWORD dwSize;
DWORD last;
DWORD *pdwCypher;
DWORD dex;
// we use CoTaskMemAlloc to be in line with the PST implementation
pClear->cbSize = pEncoded->pBlobData[2];
if (!(pClear->pBlobData = (BYTE *)CoTaskMemAlloc(pClear->cbSize)))
return E_OUTOFMEMORY;
// should have been tested by now
Assert(FDataIsValidV1(pEncoded->pBlobData));
Assert(!FDataIsPST(pEncoded->pBlobData));
// nevermind that the pointer is offset by the header size, this is
// where the password starts
pdwCypher = (DWORD *)&(pEncoded->pBlobData[PROT_SIZEOF_XORHEADER]);
dex = 0;
last = OBFUSCATOR;
if (dwSize = pClear->cbSize / sizeof(DWORD))
{
// case where data is >= 4 bytes
for (; dex < dwSize; dex++)
last = ((DWORD *)pClear->pBlobData)[dex] = pdwCypher[dex] ^ last;
}
// if we have bits left over
if (dwSize = (pClear->cbSize % sizeof(DWORD))*8)
{
// need to not munge memory that isn't ours
last >>= sizeof(DWORD)*8-dwSize;
((DWORD *)pClear->pBlobData)[dex] &= ((DWORD)-1) << dwSize;
((DWORD *)pClear->pBlobData)[dex] |=
((pdwCypher[dex] & (((DWORD)-1) >> (sizeof(DWORD)*8-dwSize))) ^ last);
}
return S_OK;
}
///////////////////////////////////////////////////////////////////////////
//
// Other static functions
//
///////////////////////////////////////////////////////////////////////////
BOOL FDataIsValidV1(BYTE *pb)
{ return pb && pb[0] == PROT_VERSION_1 && (pb[1] == PROT_PASS_XOR || pb[1] == PROT_PASS_PST); }
BOOL FDataIsValidV0(BLOB *pblob)
{ return ((lstrlenW((LPWSTR)pblob->pBlobData)+1)*sizeof(WCHAR) == pblob->cbSize); }
BOOL FDataIsPST(BYTE *pb)
#ifdef DEBUG
{
if (pb)
if (pb[1] == PROT_PASS_PST)
{
DOUTL(DOUTL_CPROP, "PropCryp: Data is PST");
return TRUE;
}
else
{
DOUTL(DOUTL_CPROP, "PropCryp: Data is XOR");
return FALSE;
}
else
return FALSE;
}
#else
{ return pb && pb[1] == PROT_PASS_PST; }
#endif