488 lines
14 KiB
C++
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
|