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.
1801 lines
52 KiB
1801 lines
52 KiB
/**********************************************************************/
|
|
/** Microsoft Passport **/
|
|
/** Copyright(c) Microsoft Corporation, 1999 - 2001 **/
|
|
/**********************************************************************/
|
|
|
|
/*
|
|
HelperFuncs.cpp
|
|
COM object for manager interface
|
|
|
|
|
|
FILE HISTORY:
|
|
|
|
*/
|
|
// HelperFuncs.cpp : Useful functions
|
|
#include "stdafx.h"
|
|
#include <time.h>
|
|
#include "HelperFuncs.h"
|
|
#include "Monitoring.h"
|
|
#include "nsconst.h"
|
|
#include <wininet.h>
|
|
#include <commd5.h>
|
|
|
|
using namespace ATL;
|
|
|
|
LPWSTR GetVersionString(void);
|
|
BOOL PPEscapeUrl(LPCTSTR lpszStringIn,
|
|
LPTSTR lpszStringOut,
|
|
DWORD* pdwStrLen,
|
|
DWORD dwMaxLength,
|
|
DWORD dwFlags);
|
|
|
|
//===========================================================================
|
|
//
|
|
// @func Copy char string helper, and ADDs the length of the source string
|
|
// to cb.
|
|
//
|
|
// @rdesc returns the pointer into the buffer of last char copied.
|
|
//
|
|
|
|
LPSTR
|
|
CopyHelperA(
|
|
LPSTR pszDest, //@parm start of buffer to copy INTO
|
|
LPCSTR pszSrc, //@parm string to copy
|
|
LPCSTR pszBufEnd, //@parm end of buffer to prevent overwrite
|
|
DWORD &cb //@parm length of the source string will be ADDed to this value
|
|
)
|
|
{
|
|
LPCSTR pszDestTemp = pszDest;
|
|
|
|
if(!pszDest || !pszSrc)
|
|
return pszDest;
|
|
|
|
while( (pszDest < pszBufEnd) && (*pszDest = *pszSrc))
|
|
{
|
|
pszDest++;
|
|
pszSrc++;
|
|
}
|
|
|
|
cb += (DWORD) (pszDest - pszDestTemp);
|
|
|
|
while(*pszSrc++)
|
|
cb++;
|
|
|
|
return( pszDest );
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Copy wchar string helper
|
|
//
|
|
|
|
LPWSTR
|
|
CopyHelperW(
|
|
LPWSTR pszDest,
|
|
LPCWSTR pszSrc,
|
|
LPCWSTR pszBufEnd
|
|
)
|
|
{
|
|
if(!pszDest || !pszSrc)
|
|
return pszDest;
|
|
|
|
while( (pszDest < pszBufEnd) && (*pszDest = *pszSrc))
|
|
{
|
|
pszDest++;
|
|
pszSrc++;
|
|
}
|
|
return( pszDest );
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Copy wchar string helper
|
|
//
|
|
|
|
LPWSTR
|
|
CopyNHelperW(
|
|
LPWSTR pszDest,
|
|
LPCWSTR pszSrc,
|
|
ULONG ulCount,
|
|
LPCWSTR pszBufEnd
|
|
)
|
|
{
|
|
ULONG ulCur = 0;
|
|
|
|
if(!pszDest || !pszSrc)
|
|
return pszDest;
|
|
|
|
while( (pszDest < pszBufEnd) && (*pszDest = *pszSrc))
|
|
{
|
|
pszDest++;
|
|
pszSrc++;
|
|
if(++ulCur == ulCount) break;
|
|
}
|
|
|
|
return pszDest;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Format a logo tag HTML element
|
|
//
|
|
|
|
BSTR
|
|
FormatNormalLogoTag(
|
|
LPCWSTR pszLoginServerURL,
|
|
ULONG ulSiteId,
|
|
LPCWSTR pszReturnURL,
|
|
ULONG ulTimeWindow,
|
|
BOOL bForceLogin,
|
|
ULONG ulCurrentCryptVersion,
|
|
time_t tCurrentTime,
|
|
LPCWSTR pszCoBrand,
|
|
LPCWSTR pszImageURL,
|
|
LPCWSTR pszNameSpace,
|
|
int nKPP,
|
|
PM_LOGOTYPE nLogoType,
|
|
USHORT lang,
|
|
ULONG ulSecureLevel,
|
|
CRegistryConfig* pCRC,
|
|
BOOL fRedirToSelf,
|
|
BOOL bCreateTPF
|
|
|
|
)
|
|
/*
|
|
The old sprintf for reference:
|
|
_snwprintf(text, 2048, L"<A HREF=\"%s?id=%d&ru=%s&tw=%d&fs=%s&kv=%d&ct=%u%s%s\">%s</A>",
|
|
url, crc->getSiteId(), returnUrl, TimeWindow, ForceLogin ? L"1" : L"0",
|
|
crc->getCurrentCryptVersion(), ct, CBT?L"&cb=":L"", CBT?CBT:L"", iurl);
|
|
*/
|
|
{
|
|
WCHAR text[MAX_URL_LENGTH * 2];
|
|
LPWSTR pszCurrent = text;
|
|
LPCWSTR pszBufEnd = &(text[MAX_URL_LENGTH * 2 - 1]);
|
|
|
|
// logotag specific format
|
|
pszCurrent = CopyHelperW(pszCurrent, L"<A HREF=\"", pszBufEnd);
|
|
|
|
// call the common formatting function
|
|
// it is the same for AuthURL and LogoTag
|
|
pszCurrent = FormatAuthURLParameters(pszLoginServerURL,
|
|
ulSiteId,
|
|
pszReturnURL,
|
|
ulTimeWindow,
|
|
bForceLogin,
|
|
ulCurrentCryptVersion,
|
|
tCurrentTime,
|
|
pszCoBrand,
|
|
pszNameSpace,
|
|
nKPP,
|
|
pszCurrent,
|
|
MAX_URL_LENGTH,
|
|
lang,
|
|
ulSecureLevel,
|
|
pCRC,
|
|
fRedirToSelf &&
|
|
nLogoType == PM_LOGOTYPE_SIGNIN,
|
|
bCreateTPF
|
|
);
|
|
if (NULL == pszCurrent)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
pszCurrent = CopyHelperW(pszCurrent, L"\">", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, pszImageURL, pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, L"</A>", pszBufEnd);
|
|
|
|
return ALLOC_AND_GIVEAWAY_BSTR(text);
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Format a update logo tag HTML element
|
|
//
|
|
|
|
BSTR
|
|
FormatUpdateLogoTag(
|
|
LPCWSTR pszLoginServerURL,
|
|
ULONG ulSiteId,
|
|
LPCWSTR pszReturnURL,
|
|
ULONG ulTimeWindow,
|
|
BOOL bForceLogin,
|
|
ULONG ulCurrentKeyVersion,
|
|
time_t tCurrentTime,
|
|
LPCWSTR pszCoBrand,
|
|
int nKPP,
|
|
LPCWSTR pszUpdateServerURL,
|
|
BOOL bSecure,
|
|
LPCWSTR pszProfileUpdate,
|
|
PM_LOGOTYPE nLogoType,
|
|
ULONG ulSecureLevel,
|
|
CRegistryConfig* pCRC,
|
|
BOOL bCreateTPF
|
|
)
|
|
/*
|
|
The old sprintf for reference:
|
|
_snwprintf(text, 2048,
|
|
L"<A HREF=\"%s?id=%d&ru=%s&tw=%d&fs=%s&kv=%d&ct=%u%s%s\">%.*s?id=%d&ct=%u&sec=%s&ru=%s&up=%s%s</A>",
|
|
url, crc->getSiteId(), returnUrl, TimeWindow, ForceLogin ? L"1" : L"0",
|
|
crc->getCurrentCryptVersion(), ct, CBT?L"&cb=":L"", CBT?CBT:L"",
|
|
(ins-iurl), iurl, crc->getSiteId(), ct, (bSecure ? L"true" : L"false"),returnUrl,
|
|
newCH, ins+2);
|
|
*/
|
|
{
|
|
WCHAR text[MAX_URL_LENGTH * 2];
|
|
WCHAR temp[40];
|
|
WCHAR siteid[40];
|
|
WCHAR curtime[40];
|
|
LPWSTR pszCurrent = text;
|
|
LPCWSTR pszBufEnd = &(text[MAX_URL_LENGTH * 2 - 1]);
|
|
LPWSTR pszFirstHalfEnd;
|
|
HRESULT hr = S_OK;
|
|
|
|
pszCurrent = CopyHelperW(pszCurrent, L"<A HREF=\"", pszBufEnd);
|
|
LPWSTR signStart1 = pszCurrent;
|
|
pszCurrent = CopyHelperW(pszCurrent, pszLoginServerURL, pszBufEnd);
|
|
|
|
if(wcschr(text, L'?') == NULL)
|
|
pszCurrent = CopyHelperW(pszCurrent, L"?id=", pszBufEnd);
|
|
else
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&id=", pszBufEnd);
|
|
|
|
_ultow(ulSiteId, siteid, 10);
|
|
pszCurrent = CopyHelperW(pszCurrent, siteid, pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&ru=", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, pszReturnURL, pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&tw=", pszBufEnd);
|
|
|
|
_ultow(ulTimeWindow, temp, 10);
|
|
pszCurrent = CopyHelperW(pszCurrent, temp, pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&fs=", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, bForceLogin ? L"1" : L"0", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&kv=", pszBufEnd);
|
|
|
|
_ultow(ulCurrentKeyVersion, temp, 10);
|
|
pszCurrent = CopyHelperW(pszCurrent, temp, pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&ct=", pszBufEnd);
|
|
|
|
_ultow(tCurrentTime, curtime, 10);
|
|
pszCurrent = CopyHelperW(pszCurrent, curtime, pszBufEnd);
|
|
if(pszCoBrand)
|
|
{
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&cb=", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, pszCoBrand, pszBufEnd);
|
|
}
|
|
|
|
if(nKPP != -1)
|
|
{
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&kpp=", pszBufEnd);
|
|
|
|
_ultow(nKPP, temp, 10);
|
|
pszCurrent = CopyHelperW(pszCurrent, temp, pszBufEnd);
|
|
}
|
|
|
|
if(ulSecureLevel != 0)
|
|
{
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&seclog=", pszBufEnd);
|
|
|
|
_ultow(ulSecureLevel, temp, 10);
|
|
pszCurrent = CopyHelperW(pszCurrent, temp, pszBufEnd);
|
|
}
|
|
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&ver=", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, GetVersionString(), pszBufEnd);
|
|
|
|
hr = SignQueryString(pCRC, ulCurrentKeyVersion, signStart1, pszCurrent, pszBufEnd, bCreateTPF);
|
|
if (S_OK != hr)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
pszCurrent = CopyHelperW(pszCurrent, L"\">", pszBufEnd);
|
|
|
|
pszFirstHalfEnd = pszUpdateServerURL ? (wcsstr(pszUpdateServerURL, L"$1")) : NULL;
|
|
|
|
pszCurrent = CopyNHelperW(pszCurrent, pszUpdateServerURL, (ULONG)(pszFirstHalfEnd - pszUpdateServerURL), pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, L"?id=", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, siteid, pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&ct=", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, curtime, pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&sec=", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, bSecure ? L"true" : L"false", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&ru=", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, pszReturnURL, pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&up=", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, pszProfileUpdate, pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, pszFirstHalfEnd + 2, pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, L"</A>", pszBufEnd);
|
|
|
|
return ALLOC_AND_GIVEAWAY_BSTR(text);
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Sign a query string with partner's key
|
|
//
|
|
|
|
HRESULT SignQueryString(
|
|
CRegistryConfig* pCRC,
|
|
ULONG ulCurrentKeyVersion,
|
|
LPWSTR pszBufStart,
|
|
LPWSTR& pszCurrent,
|
|
LPCWSTR pszBufEnd,
|
|
BOOL bCreateTPF
|
|
|
|
)
|
|
{
|
|
BSTR signature = NULL;
|
|
HRESULT hr = S_OK;
|
|
if (!bCreateTPF)
|
|
return hr;
|
|
|
|
if(pCRC)
|
|
{
|
|
LPWSTR signStart = wcschr(pszBufStart, L'?');
|
|
|
|
// nothing to sign
|
|
if (NULL == signStart)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// if found before pszCurrent
|
|
if(signStart < pszCurrent)
|
|
{
|
|
++signStart;;
|
|
}
|
|
hr = PartnerHash(pCRC, ulCurrentKeyVersion, signStart, pszCurrent - signStart, &signature);
|
|
|
|
if (hr == S_OK && signature != NULL)
|
|
{
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&tpf=", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, signature, pszBufEnd);
|
|
}
|
|
if (!signature && g_pAlert)
|
|
{
|
|
g_pAlert->report(PassportAlertInterface::WARNING_TYPE, PM_URLSIGNATURE_NOTCREATED,
|
|
0, NULL);
|
|
}
|
|
}
|
|
else if(g_pAlert)
|
|
g_pAlert->report(PassportAlertInterface::WARNING_TYPE, PM_URLSIGNATURE_NOTCREATED,
|
|
0, NULL);
|
|
Cleanup:
|
|
if (signature)
|
|
{
|
|
SysFreeString(signature);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// MD5 hash with partner's key
|
|
//
|
|
|
|
HRESULT PartnerHash(
|
|
CRegistryConfig* pCRC,
|
|
ULONG ulCurrentKeyVersion,
|
|
LPCWSTR tobeSigned,
|
|
ULONG nChars,
|
|
BSTR* pbstrHash
|
|
)
|
|
{
|
|
// MD5 hash the url and query strings + the key of the
|
|
//
|
|
if(!pCRC || !pbstrHash) return E_INVALIDARG;
|
|
|
|
CCoCrypt* crypt = pCRC->getCrypt(ulCurrentKeyVersion, NULL);
|
|
DWORD keyLen = 0;
|
|
unsigned char* key = NULL;
|
|
BSTR bstrHash = NULL;
|
|
HRESULT hr = S_OK;
|
|
BOOL bSigned = FALSE;
|
|
BSTR binHexedKey = NULL;
|
|
LPSTR lpToBeHashed = NULL;
|
|
|
|
if (crypt && (key = crypt->getKeyMaterial(&keyLen)))
|
|
{
|
|
CBinHex BinHex;
|
|
|
|
//encode the key
|
|
hr = BinHex.ToBase64ASCII((BYTE*)key, keyLen, 0, NULL, &binHexedKey);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// W2A conversion here -- we sign ascii version
|
|
|
|
ULONG ulFullLen;
|
|
ULONG ulKeyLen;
|
|
ULONG ulWideLen = wcslen(tobeSigned);
|
|
ULONG ulAnsiLen = WideCharToMultiByte(CP_ACP, 0, tobeSigned, ulWideLen, NULL, 0, NULL, NULL);
|
|
|
|
if (ulAnsiLen == 0)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
ulKeyLen = strlen((LPCSTR)binHexedKey);
|
|
ulFullLen = ulAnsiLen + ulKeyLen;
|
|
|
|
// NOTE - the SysAllocStringByteLen allocs an additional WCHAR so we won't overflow
|
|
// The MD5Hash function actually uses SysStringByteLen when doing the hashing so unless
|
|
// we want to make a copy just allocate what we need
|
|
lpToBeHashed = (LPSTR) ::SysAllocStringByteLen(NULL, ulFullLen * sizeof(CHAR));
|
|
|
|
if (lpToBeHashed == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto Cleanup;
|
|
}
|
|
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
tobeSigned,
|
|
ulWideLen,
|
|
lpToBeHashed,
|
|
ulAnsiLen,
|
|
NULL,
|
|
NULL);
|
|
|
|
strcpy(lpToBeHashed + ulAnsiLen, (LPCSTR)binHexedKey);
|
|
RtlSecureZeroMemory((PVOID)binHexedKey, ulKeyLen);
|
|
{
|
|
CComPtr<IMD5> md5;
|
|
|
|
hr = GetGlobalCOMmd5(&md5);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
hr = md5->MD5Hash((BSTR)lpToBeHashed, &bstrHash);
|
|
RtlSecureZeroMemory(lpToBeHashed + ulAnsiLen, ulKeyLen);
|
|
|
|
if( hr == S_OK && bstrHash != NULL)
|
|
{
|
|
*pbstrHash = bstrHash;
|
|
bstrHash = NULL;
|
|
bSigned = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pbstrHash = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (g_pAlert )
|
|
{
|
|
g_pAlert->report(PassportAlertInterface::ERROR_TYPE, PM_CURRENTKEY_NOTDEFINED, 0, NULL);
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
if (bstrHash)
|
|
{
|
|
SysFreeString(bstrHash);
|
|
}
|
|
|
|
if (lpToBeHashed)
|
|
{
|
|
SysFreeString((BSTR)lpToBeHashed);
|
|
}
|
|
|
|
if (binHexedKey)
|
|
{
|
|
SysFreeString(binHexedKey);
|
|
}
|
|
|
|
if (!bSigned && g_pAlert)
|
|
{
|
|
g_pAlert->report(PassportAlertInterface::WARNING_TYPE, PM_URLSIGNATURE_NOTCREATED, 0, NULL);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// Construct AuthURL, returning BSTR
|
|
//
|
|
|
|
BSTR
|
|
FormatAuthURL(
|
|
LPCWSTR pszLoginServerURL,
|
|
ULONG ulSiteId,
|
|
LPCWSTR pszReturnURL,
|
|
ULONG ulTimeWindow,
|
|
BOOL bForceLogin,
|
|
ULONG ulCurrentKeyVersion,
|
|
time_t tCurrentTime,
|
|
LPCWSTR pszCoBrand,
|
|
LPCWSTR pszNameSpace,
|
|
int nKPP,
|
|
USHORT lang,
|
|
ULONG ulSecureLevel,
|
|
CRegistryConfig* pCRC,
|
|
BOOL fRedirToSelf,
|
|
BOOL bCreateTPF
|
|
)
|
|
/*
|
|
The old sprintf for reference:
|
|
_snwprintf(text, 2048, L"%s?id=%d&ru=%s&tw=%d&fs=%d&kv=%d&ct=%u%s%s",
|
|
url, crc->getSiteId(), returnUrl, TimeWindow, ForceLogin ? 1 : 0,
|
|
crc->getCurrentCryptVersion(), ct ,CBT?L"&cb=":L"", CBT?CBT:L"");
|
|
*/
|
|
{
|
|
WCHAR text[2048] = L"";
|
|
|
|
if (NULL == FormatAuthURLParameters(pszLoginServerURL,
|
|
ulSiteId,
|
|
pszReturnURL,
|
|
ulTimeWindow,
|
|
bForceLogin,
|
|
ulCurrentKeyVersion,
|
|
tCurrentTime,
|
|
pszCoBrand,
|
|
pszNameSpace,
|
|
nKPP,
|
|
text,
|
|
sizeof(text)/sizeof(WCHAR),
|
|
lang,
|
|
ulSecureLevel,
|
|
pCRC,
|
|
fRedirToSelf,
|
|
bCreateTPF
|
|
))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return ALLOC_AND_GIVEAWAY_BSTR(text);
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
//
|
|
// consolidate the code in FormatAuthUrl and NormalLogoTag -- with passed in buffer
|
|
//
|
|
PWSTR
|
|
FormatAuthURLParameters(
|
|
LPCWSTR pszLoginServerURL,
|
|
ULONG ulSiteId,
|
|
LPCWSTR pszReturnURL,
|
|
ULONG ulTimeWindow,
|
|
BOOL bForceLogin,
|
|
ULONG ulCurrentKeyVersion,
|
|
time_t tCurrentTime,
|
|
LPCWSTR pszCoBrand,
|
|
LPCWSTR pszNameSpace,
|
|
int nKPP,
|
|
PWSTR pszBufStart,
|
|
ULONG cBufLen, // length of buffer in WCHAR
|
|
USHORT lang,
|
|
ULONG ulSecureLevel,
|
|
CRegistryConfig* pCRC,
|
|
BOOL fRedirectToSelf, // if true, this is URL for self redirect
|
|
// otherwise the redirect is to the login server
|
|
BOOL bCreateTPF
|
|
)
|
|
{
|
|
WCHAR temp[40];
|
|
LPWSTR pszCurrent = pszBufStart, pszLoginStart, pszSignURLStart = NULL;
|
|
LPCWSTR pszBufEnd = pszBufStart + cBufLen - 1;
|
|
HRESULT hr = S_OK;
|
|
PWSTR pwszReturn = NULL;
|
|
|
|
// helper BSTR ...
|
|
BSTR bstrHelper = SysAllocStringLen(NULL, cBufLen);
|
|
if (NULL == bstrHelper)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (fRedirectToSelf)
|
|
{
|
|
//
|
|
// new authUrl is the return URL + indication a challenge - msppchlg=1 - has to be
|
|
// done + the rest of the qs parameters as they are in the original
|
|
// protocol
|
|
//
|
|
DWORD cchLen = cBufLen;
|
|
|
|
if(!InternetCanonicalizeUrl(pszReturnURL,
|
|
pszCurrent,
|
|
&cchLen,
|
|
ICU_DECODE | ICU_NO_ENCODE))
|
|
{
|
|
// this should not fail ...
|
|
_ASSERT(FALSE);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// require at least 50 chars
|
|
if (cchLen > cBufLen - 50 )
|
|
{
|
|
_ASSERT(FALSE);
|
|
goto Cleanup;
|
|
}
|
|
PWSTR psz = pszCurrent;
|
|
while(*psz && *psz != L'?') psz++;
|
|
// see if URL already contains '?'
|
|
// if so, the sequence will start with '&'
|
|
if (*psz)
|
|
pszCurrent[cchLen] = L'&';
|
|
else
|
|
pszCurrent[cchLen] = L'?';
|
|
pszCurrent += cchLen + 1;
|
|
|
|
// indicate challange
|
|
pszCurrent = CopyHelperW(pszCurrent, PPSITE_CHALLENGE, pszBufEnd);
|
|
|
|
// login server ....
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, PPLOGIN_PARAM, pszBufEnd);
|
|
|
|
//
|
|
// remember the start of the login URL
|
|
pszLoginStart = pszCurrent;
|
|
// use the temp buffer for the rest
|
|
pszCurrent = bstrHelper;
|
|
pszSignURLStart = pszCurrent;
|
|
pszBufEnd = pszCurrent + SysStringLen(bstrHelper) - 1;
|
|
//
|
|
// format loginserverUrl and qs params in a separate buffer, so
|
|
// they can be escaped ...
|
|
pszCurrent = CopyHelperW(pszCurrent, pszLoginServerURL, pszBufEnd);
|
|
|
|
// start sequence
|
|
if (wcschr(pszLoginServerURL, L'?'))
|
|
{
|
|
// login server already contains qs
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&", pszBufEnd);
|
|
}
|
|
else
|
|
{
|
|
// start qs sequence
|
|
pszCurrent = CopyHelperW(pszCurrent, L"?", pszBufEnd);
|
|
}
|
|
|
|
pszCurrent = CopyHelperW(pszCurrent, L"id=", pszBufEnd);
|
|
// common code will fill in id and the rest ....
|
|
}
|
|
else
|
|
{
|
|
// redirect directly to a login server
|
|
pszSignURLStart = pszCurrent;
|
|
pszCurrent = CopyHelperW(pszCurrent, pszLoginServerURL, pszBufEnd);
|
|
// start sequence
|
|
while(*pszLoginServerURL && *pszLoginServerURL != L'?') pszLoginServerURL++;
|
|
if (*pszLoginServerURL)
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&id=", pszBufEnd);
|
|
else
|
|
pszCurrent = CopyHelperW(pszCurrent, L"?id=", pszBufEnd);
|
|
}
|
|
|
|
|
|
_ultow(ulSiteId, temp, 10);
|
|
pszCurrent = CopyHelperW(pszCurrent, temp, pszBufEnd);
|
|
|
|
// keep the ru, so I don't have to reconstruct
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&ru=", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, pszReturnURL, pszBufEnd);
|
|
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&tw=", pszBufEnd);
|
|
|
|
_ultow(ulTimeWindow, temp, 10);
|
|
pszCurrent = CopyHelperW(pszCurrent, temp, pszBufEnd);
|
|
|
|
if(bForceLogin)
|
|
{
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&fs=1", pszBufEnd);
|
|
}
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&kv=", pszBufEnd);
|
|
|
|
_ultow(ulCurrentKeyVersion, temp, 10);
|
|
pszCurrent = CopyHelperW(pszCurrent, temp, pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&ct=", pszBufEnd);
|
|
|
|
_ultow(tCurrentTime, temp, 10);
|
|
pszCurrent = CopyHelperW(pszCurrent, temp, pszBufEnd);
|
|
if(pszCoBrand)
|
|
{
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&cb=", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, pszCoBrand, pszBufEnd);
|
|
}
|
|
|
|
if(pszNameSpace)
|
|
{
|
|
if (!_wcsicmp(pszNameSpace, L"email"))
|
|
{
|
|
// namespace == email -> ems=1
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&ems=1", pszBufEnd);
|
|
}
|
|
else if(*pszNameSpace)
|
|
{
|
|
// regular namespace logic
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&ns=", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, pszNameSpace, pszBufEnd);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// namespace == null : default to email
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&ems=1", pszBufEnd);
|
|
}
|
|
|
|
if(nKPP != -1 && nKPP != 0)
|
|
{
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&kpp=", pszBufEnd);
|
|
|
|
_ultow(nKPP, temp, 10);
|
|
pszCurrent = CopyHelperW(pszCurrent, temp, pszBufEnd);
|
|
}
|
|
|
|
if(ulSecureLevel != 0)
|
|
{
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&seclog=", pszBufEnd);
|
|
|
|
_ultow(ulSecureLevel, temp, 10);
|
|
pszCurrent = CopyHelperW(pszCurrent, temp, pszBufEnd);
|
|
}
|
|
|
|
pszCurrent = CopyHelperW(pszCurrent, L"&ver=", pszBufEnd);
|
|
pszCurrent = CopyHelperW(pszCurrent, GetVersionString(), pszBufEnd);
|
|
|
|
// MD5 hash the url and query strings + the key of the
|
|
//
|
|
hr = SignQueryString(pCRC, ulCurrentKeyVersion, pszSignURLStart, pszCurrent, pszBufEnd, bCreateTPF);
|
|
|
|
*pszCurrent = L'\0';
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (fRedirectToSelf)
|
|
{
|
|
// escape and put back in the original buffer.
|
|
// adjust the length first
|
|
cBufLen -= (ULONG) (pszLoginStart - pszBufStart);
|
|
if (!PPEscapeUrl(bstrHelper,
|
|
pszLoginStart,
|
|
&cBufLen,
|
|
cBufLen,
|
|
0))
|
|
{
|
|
_ASSERT(FALSE);
|
|
// cut the return
|
|
pszCurrent = pszLoginStart;
|
|
}
|
|
else
|
|
{
|
|
pszCurrent = pszLoginStart + cBufLen;
|
|
}
|
|
|
|
}
|
|
pwszReturn = pszCurrent;
|
|
Cleanup:
|
|
if (bstrHelper)
|
|
{
|
|
SysFreeString(bstrHelper);
|
|
}
|
|
return pwszReturn;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// retrieve a query parameter from a query string
|
|
//
|
|
|
|
BOOL
|
|
GetQueryParam(LPCSTR queryString, LPSTR param, BSTR* p)
|
|
{
|
|
LPSTR aLoc, aEnd;
|
|
int aLen, i;
|
|
|
|
// Find the first occurrence of the param in the queryString.
|
|
aLoc = strstr(queryString, param);
|
|
while(aLoc != NULL)
|
|
{
|
|
// If the string was found at the beginning of the string, or was
|
|
// preceded by a '&' then we've found the correct param. Otherwise
|
|
// we tail-matched some other query string param and should look again.
|
|
|
|
if(aLoc == queryString ||
|
|
*(aLoc - 1) == '&')
|
|
{
|
|
aLoc += strlen(param);
|
|
aEnd = strchr(aLoc, '&');
|
|
|
|
if(aEnd)
|
|
aLen = aEnd - aLoc;
|
|
else
|
|
aLen = strlen(aLoc);
|
|
|
|
BSTR aVal = ALLOC_BSTR_LEN(NULL, aLen);
|
|
if (NULL == aVal)
|
|
return FALSE;
|
|
|
|
for (i = 0; i < aLen; i++)
|
|
aVal[i] = aLoc[i];
|
|
*p = aVal;
|
|
GIVEAWAY_BSTR(aVal);
|
|
return TRUE;
|
|
}
|
|
|
|
aLoc = strstr(aLoc + 1, param);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// get t, p, and f from query string
|
|
//
|
|
|
|
BOOL
|
|
GetQueryData(
|
|
LPCSTR queryString,
|
|
BSTR* a,
|
|
BSTR* p,
|
|
BSTR* f)
|
|
{
|
|
// This one is optional, don't error out if it isn't there.
|
|
GetQueryParam(queryString, "f=", f);
|
|
|
|
if(!GetQueryParam(queryString, "t=", a))
|
|
return FALSE;
|
|
|
|
// OK if we have ticket w/o profile.
|
|
GetQueryParam(queryString, "p=", p);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define ToHexDigit(x) (('0' <= x && x <= '9') ? (x - '0') : (tolower(x) - 'a' + 10))
|
|
|
|
//===========================================================================
|
|
//
|
|
// Get a cookie from value of cookie header
|
|
//
|
|
|
|
BOOL
|
|
GetCookie(
|
|
LPCSTR pszCookieHeader,
|
|
LPCSTR pszCookieName,
|
|
BSTR* pbstrCookieVal
|
|
)
|
|
{
|
|
LPSTR nLoc;
|
|
LPCSTR pH = pszCookieHeader;
|
|
LPSTR nEnd;
|
|
int nLen, src, dst;
|
|
|
|
if(pbstrCookieVal == NULL || pszCookieHeader == NULL)
|
|
return FALSE;
|
|
|
|
*pbstrCookieVal = NULL;
|
|
|
|
_ASSERT(pszCookieName);
|
|
|
|
// find begining
|
|
while (nLoc = strstr(pH, pszCookieName))
|
|
{
|
|
nLen = strlen(pszCookieName);
|
|
|
|
_ASSERT(nLen > 0);
|
|
if ((nLoc == pszCookieHeader || *(nLoc - 1) == ' ' || *(nLoc - 1) == ';' || *(nLoc - 1) == ':') && *(nLoc + nLen) == '=')
|
|
break;
|
|
else
|
|
pH = nLoc + nLen;
|
|
}
|
|
|
|
if (nLoc == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
nLoc += nLen + 1;
|
|
|
|
// find end
|
|
nEnd = strchr(nLoc,';');
|
|
|
|
if (nEnd)
|
|
nLen = nEnd - nLoc;
|
|
else
|
|
nLen = strlen(nLoc);
|
|
|
|
if (nLen == 0) // empty cookie
|
|
return FALSE;
|
|
|
|
BSTR nVal = ALLOC_BSTR_LEN(NULL, nLen);
|
|
if(!nVal)
|
|
return FALSE;
|
|
|
|
for (src = 0, dst = 0; src < nLen;)
|
|
{
|
|
//handle any url encoded gunk
|
|
if(nLoc[src] == '%')
|
|
{
|
|
nVal[dst++] = (ToHexDigit(nLoc[src+1]) << 4) + ToHexDigit(nLoc[src+2]);
|
|
src+=3;
|
|
}
|
|
else
|
|
{
|
|
nVal[dst++] = nLoc[src++];
|
|
}
|
|
}
|
|
nVal[dst] = 0;
|
|
|
|
GIVEAWAY_BSTR(nVal);
|
|
*pbstrCookieVal = nVal;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// @func Build passport cookies (MSPAuth, MSPProf, MSPConsent) -- into a buffer
|
|
// If the buffer is not big enough, return EMPTY string in the buffer and
|
|
// the required size (including the NULL terminator) is returned in pdwBufLen.
|
|
//
|
|
// Note that CopyHelperA must be used in the construction for the right buffer length.
|
|
//
|
|
// @rdesc Returns one of the following values
|
|
// @flag TRUE | Always
|
|
|
|
BOOL
|
|
BuildCookieHeaders(
|
|
LPCSTR pszTicket,
|
|
LPCSTR pszProfile,
|
|
LPCSTR pszConsent,
|
|
LPCSTR pszSecure,
|
|
LPCSTR pszTicketDomain,
|
|
LPCSTR pszTicketPath,
|
|
LPCSTR pszConsentDomain,
|
|
LPCSTR pszConsentPath,
|
|
LPCSTR pszSecureDomain,
|
|
LPCSTR pszSecurePath,
|
|
BOOL bSave,
|
|
LPSTR pszBuf, //@parm buffer that will hold the output. Could be NULL.
|
|
IN OUT LPDWORD pdwBufLen, //@parm size of buffer. Could be 0
|
|
bool bHTTPOnly
|
|
)
|
|
/*
|
|
Here is the old code for reference:
|
|
|
|
if (domain)
|
|
{
|
|
*bufSize = _snprintf(pCookieHeader, *bufSize,
|
|
"Set-Cookie: MSPAuth=%s; path=/; domain=%s; %s\r\n"
|
|
"Set-Cookie: MSPProf=%s; path=/; domain=%s; %s\r\n",
|
|
W2A(a), domain,
|
|
persist ? "expires=Mon 1-Jan-2035 12:00:00 GMT;" : "",
|
|
W2A(p), domain,
|
|
persist ? "expires=Mon 1-Jan-2035 12:00:00 GMT;" : "");
|
|
}
|
|
else
|
|
{
|
|
*bufSize = _snprintf(pCookieHeader, *bufSize,
|
|
"Set-Cookie: MSPAuth=%s; path=/; %s\r\n"
|
|
"Set-Cookie: MSPProf=%s; path=/; %s\r\n",
|
|
W2A(a),
|
|
persist ? "expires=Mon 1-Jan-2035 12:00:00 GMT;" : "",
|
|
W2A(p),
|
|
persist ? "expires=Mon 1-Jan-2035 12:00:00 GMT;" : "");
|
|
}
|
|
|
|
*/
|
|
{
|
|
LPSTR pszCurrent = pszBuf;
|
|
LPCSTR pszBufEnd;
|
|
DWORD cbBuf = 0;
|
|
|
|
//
|
|
// 12002: if pszBuf was NULL, then we dont care about the passed in length; the caller wants to know
|
|
// the required length. In this case, set *pdwBufLen so that pszBufEnd is also NULL
|
|
//
|
|
if (NULL == pszBuf)
|
|
*pdwBufLen = 0;
|
|
|
|
pszBufEnd = pszBuf + ((*pdwBufLen > 0) ? *pdwBufLen - 1 : 0);
|
|
|
|
//
|
|
// 12002: cbBuf MUST be initialized before calling CopyHelperA since it accumulates the lengths
|
|
//
|
|
pszCurrent = CopyHelperA(pszCurrent, "Set-Cookie: MSPAuth=", pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, pszTicket, pszBufEnd, cbBuf);
|
|
if(bHTTPOnly)
|
|
pszCurrent = CopyHelperA(pszCurrent, "; HTTPOnly", pszBufEnd, cbBuf);
|
|
if(pszTicketPath)
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, "; path=", pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, pszTicketPath, pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, "; ", pszBufEnd, cbBuf);
|
|
}
|
|
else
|
|
pszCurrent = CopyHelperA(pszCurrent, "; path=/; ", pszBufEnd, cbBuf);
|
|
|
|
if(pszTicketDomain)
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, "domain=", pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, pszTicketDomain, pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, "; ", pszBufEnd, cbBuf);
|
|
}
|
|
|
|
if(bSave)
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, COOKIE_EXPIRES(EXPIRE_FUTURE), pszBufEnd, cbBuf);
|
|
}
|
|
|
|
pszCurrent = CopyHelperA(pszCurrent, "\r\n", pszBufEnd, cbBuf);
|
|
|
|
if(pszProfile)
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, "Set-Cookie: MSPProf=", pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, pszProfile, pszBufEnd, cbBuf);
|
|
if(bHTTPOnly)
|
|
pszCurrent = CopyHelperA(pszCurrent, "; HTTPOnly", pszBufEnd, cbBuf);
|
|
|
|
if(pszTicketPath)
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, "; path=", pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, pszTicketPath, pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, "; ", pszBufEnd, cbBuf);
|
|
}
|
|
else
|
|
pszCurrent = CopyHelperA(pszCurrent, "; path=/; ", pszBufEnd, cbBuf);
|
|
|
|
if(pszTicketDomain)
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, "domain=", pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, pszTicketDomain, pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, "; ", pszBufEnd, cbBuf);
|
|
}
|
|
|
|
if(bSave)
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, COOKIE_EXPIRES(EXPIRE_FUTURE), pszBufEnd, cbBuf);
|
|
}
|
|
|
|
pszCurrent = CopyHelperA(pszCurrent, "\r\n", pszBufEnd, cbBuf);
|
|
|
|
}
|
|
|
|
if(pszSecure)
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, "Set-Cookie: MSPSecAuth=", pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, pszSecure, pszBufEnd, cbBuf);
|
|
if(bHTTPOnly)
|
|
pszCurrent = CopyHelperA(pszCurrent, "; HTTPOnly", pszBufEnd, cbBuf);
|
|
if(pszSecurePath)
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, "; path=", pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, pszSecurePath, pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, "; ", pszBufEnd, cbBuf);
|
|
}
|
|
else
|
|
pszCurrent = CopyHelperA(pszCurrent, "; path=/; ", pszBufEnd, cbBuf);
|
|
|
|
if(pszSecureDomain)
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, "domain=", pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, pszSecureDomain, pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, "; ", pszBufEnd, cbBuf);
|
|
}
|
|
|
|
pszCurrent = CopyHelperA(pszCurrent, "secure\r\n", pszBufEnd, cbBuf);
|
|
}
|
|
|
|
// Set MSPConsent cookie
|
|
pszCurrent = CopyHelperA(pszCurrent, "Set-Cookie: MSPConsent=", pszBufEnd, cbBuf);
|
|
if(pszConsent)
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, pszConsent, pszBufEnd, cbBuf);
|
|
if(bHTTPOnly)
|
|
pszCurrent = CopyHelperA(pszCurrent, "; HTTPOnly", pszBufEnd, cbBuf);
|
|
}
|
|
|
|
if(pszConsentPath)
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, "; path=", pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, pszConsentPath, pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, "; ", pszBufEnd, cbBuf);
|
|
}
|
|
else
|
|
pszCurrent = CopyHelperA(pszCurrent, "; path=/; ", pszBufEnd, cbBuf);
|
|
|
|
if(pszConsentDomain)
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, "domain=", pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, pszConsentDomain, pszBufEnd, cbBuf);
|
|
pszCurrent = CopyHelperA(pszCurrent, "; ", pszBufEnd, cbBuf);
|
|
}
|
|
|
|
if(pszConsent)
|
|
{
|
|
if(bSave)
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, COOKIE_EXPIRES(EXPIRE_FUTURE), pszBufEnd, cbBuf);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pszCurrent = CopyHelperA(pszCurrent, COOKIE_EXPIRES(EXPIRE_PAST), pszBufEnd, cbBuf);
|
|
}
|
|
|
|
pszCurrent = CopyHelperA(pszCurrent, "\r\n", pszBufEnd, cbBuf);
|
|
|
|
|
|
// finally put the Auth-Info header
|
|
pszCurrent = CopyHelperA(pszCurrent,
|
|
C_AUTH_INFO_HEADER_PASSPORT,
|
|
pszBufEnd, cbBuf);
|
|
|
|
if (*pdwBufLen > 0 && (NULL != pszCurrent))
|
|
{
|
|
*(pszCurrent++) = '\0';
|
|
*pdwBufLen = pszCurrent - pszBuf;
|
|
}
|
|
|
|
//
|
|
// The length that we report to the caller when the buffer is not big enough
|
|
// includes room for the '\0'.
|
|
//
|
|
cbBuf++;
|
|
|
|
|
|
//
|
|
// 12002: return the required size of the buffer if it is not big enough.
|
|
// Also clear the buffer
|
|
//
|
|
if (cbBuf > *pdwBufLen)
|
|
{
|
|
if (*pdwBufLen > 0)
|
|
{
|
|
*pszBuf = '\0';
|
|
}
|
|
*pdwBufLen = cbBuf;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// Decrpt and set ticket andprofile
|
|
//
|
|
|
|
HRESULT
|
|
DecryptTicketAndProfile(
|
|
BSTR bstrTicket,
|
|
BSTR bstrProfile,
|
|
BOOL bCheckConsent,
|
|
BSTR bstrConsent,
|
|
CRegistryConfig* pRegistryConfig,
|
|
IPassportTicket* piTicket,
|
|
IPassportProfile* piProfile)
|
|
{
|
|
HRESULT hr;
|
|
BSTR ret = NULL;
|
|
CCoCrypt* crypt = NULL;
|
|
time_t tValidUntil;
|
|
time_t tNow = time(NULL);
|
|
int kv;
|
|
int nMemberIdHighT, nMemberIdLowT;
|
|
VARIANT vMemberIdHighP, vMemberIdLowP;
|
|
CComPtr<IPassportTicket2> spTicket2;
|
|
|
|
if (!g_config->isValid()) // Guarantees config is non-null
|
|
{
|
|
AtlReportError(CLSID_FastAuth, PP_E_NOT_CONFIGUREDSTR,
|
|
IID_IPassportFastAuth, PP_E_NOT_CONFIGURED);
|
|
hr = PP_E_NOT_CONFIGURED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Make sure we have both ticket and profile first.
|
|
if (bstrTicket == NULL || SysStringLen(bstrTicket) == 0)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Get key version and crypt object.
|
|
kv = CCoCrypt::getKeyVersion(bstrTicket);
|
|
crypt = pRegistryConfig->getCrypt(kv, &tValidUntil);
|
|
|
|
if (crypt == NULL)
|
|
{
|
|
if (g_pAlert )
|
|
g_pAlert->report(PassportAlertInterface::ERROR_TYPE, PM_INVALID_KEY,
|
|
0, NULL, SysStringByteLen(bstrTicket), (LPVOID)bstrTicket);
|
|
AtlReportError(CLSID_FastAuth, PP_E_INVALID_TICKETSTR,
|
|
IID_IPassportFastAuth, PP_E_INVALID_TICKET);
|
|
hr = PP_E_INVALID_TICKET;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Is the key still valid?
|
|
if(tValidUntil && tValidUntil < tNow)
|
|
{
|
|
DWORD dwTimes[2] = { tValidUntil, tNow };
|
|
TCHAR *pszStrings[1];
|
|
TCHAR value[34]; // the _itot only takes upto 33 chars
|
|
pszStrings[0] = _itot(pRegistryConfig->getSiteId(), value, 10);
|
|
|
|
if(g_pAlert)
|
|
g_pAlert->report(PassportAlertInterface::WARNING_TYPE, PM_KEY_EXPIRED,
|
|
1, (LPCTSTR*)pszStrings, sizeof(DWORD) << 1, (LPVOID)dwTimes);
|
|
AtlReportError(CLSID_FastAuth, PP_E_INVALID_TICKETSTR,
|
|
IID_IPassportFastAuth, PP_E_INVALID_TICKET);
|
|
hr = PP_E_INVALID_TICKET;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Decrypt the ticket and set it into the ticket object.
|
|
if(crypt->Decrypt(bstrTicket, SysStringByteLen(bstrTicket), &ret)==FALSE)
|
|
{
|
|
if(g_pAlert)
|
|
g_pAlert->report(PassportAlertInterface::WARNING_TYPE, PM_INVALID_TICKET_C,
|
|
0, NULL, SysStringByteLen(bstrTicket), (LPVOID)bstrTicket);
|
|
AtlReportError(CLSID_FastAuth, PP_E_INVALID_TICKETSTR,
|
|
IID_IPassportFastAuth, PP_E_INVALID_TICKET);
|
|
hr = PP_E_INVALID_TICKET;
|
|
goto Cleanup;
|
|
}
|
|
|
|
TAKEOVER_BSTR(ret);
|
|
hr = piTicket->put_unencryptedTicket(ret);
|
|
if (S_OK != hr)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
piTicket->QueryInterface(_uuidof(IPassportTicket2), (void**)&spTicket2);
|
|
_ASSERT(spTicket2);
|
|
FREE_BSTR(ret);
|
|
ret = NULL;
|
|
|
|
// Decrypt the profile and set it into the profile object.
|
|
if(bstrProfile && SysStringLen(bstrProfile) != 0)
|
|
{
|
|
if(crypt->Decrypt(bstrProfile, SysStringByteLen(bstrProfile), &ret) == FALSE)
|
|
{
|
|
if(g_pAlert)
|
|
g_pAlert->report(PassportAlertInterface::WARNING_TYPE, PM_INVALID_PROFILE_C,
|
|
0, NULL, SysStringByteLen(bstrProfile), (LPVOID)bstrProfile);
|
|
piProfile->put_unencryptedProfile(NULL);
|
|
}
|
|
else
|
|
{
|
|
|
|
TAKEOVER_BSTR(ret);
|
|
hr = piProfile->put_unencryptedProfile(ret);
|
|
if (S_OK != hr)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Member id in profile MUST match member id in ticket.
|
|
//
|
|
|
|
piTicket->get_MemberIdHigh(&nMemberIdHighT);
|
|
piTicket->get_MemberIdLow(&nMemberIdLowT);
|
|
|
|
VariantInit(&vMemberIdHighP);
|
|
VariantInit(&vMemberIdLowP);
|
|
|
|
// these could be missing for mobile case
|
|
HRESULT hr1 = piProfile->get_Attribute(L"memberidhigh", &vMemberIdHighP);
|
|
HRESULT hr2 = piProfile->get_Attribute(L"memberidlow", &vMemberIdLowP);
|
|
|
|
// these could be missing for mobile case
|
|
if(hr1 == S_OK && hr2 == S_OK &&
|
|
(nMemberIdHighT != vMemberIdHighP.lVal ||
|
|
nMemberIdLowT != vMemberIdLowP.lVal))
|
|
{
|
|
piProfile->put_unencryptedProfile(NULL);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
piProfile->put_unencryptedProfile(NULL);
|
|
|
|
//
|
|
// consent stuff
|
|
if(bstrConsent)
|
|
{
|
|
FREE_BSTR(ret);
|
|
ret = NULL;
|
|
|
|
if(crypt->Decrypt(bstrConsent, SysStringByteLen(bstrConsent), &ret) == FALSE)
|
|
{
|
|
if(g_pAlert)
|
|
g_pAlert->report(PassportAlertInterface::WARNING_TYPE, PM_INVALID_CONSENT,
|
|
0, NULL, SysStringByteLen(bstrProfile), (LPVOID)bstrProfile);
|
|
// we can continue
|
|
}
|
|
else
|
|
{
|
|
TAKEOVER_BSTR(ret);
|
|
spTicket2->SetTertiaryConsent(ret); // we ignore return value here
|
|
}
|
|
}
|
|
|
|
// If the caller wants us to check consent, then do it. If we don't have
|
|
// consent, then set the profile back to NULL.
|
|
if(bCheckConsent)
|
|
{
|
|
ConsentStatusEnum ConsentCode = ConsentStatus_Unknown;
|
|
|
|
VARIANT_BOOL bRequireConsentCookie = ((
|
|
lstrcmpA(pRegistryConfig->getTicketDomain(), pRegistryConfig->getProfileDomain())
|
|
|| lstrcmpA(pRegistryConfig->getTicketPath(), pRegistryConfig->getProfilePath())
|
|
) && !(pRegistryConfig->bInDA())) ? VARIANT_TRUE: VARIANT_FALSE;
|
|
spTicket2->ConsentStatus(bRequireConsentCookie, NULL, &ConsentCode);
|
|
switch(ConsentCode)
|
|
{
|
|
case ConsentStatus_Known :
|
|
case ConsentStatus_DoNotNeed :
|
|
break;
|
|
|
|
case ConsentStatus_NotDefinedInTicket : // mean 1.X ticket
|
|
{
|
|
CComVariant vFlags;
|
|
// mobile case, flags may not exist
|
|
if(S_OK == piProfile->get_Attribute(L"flags", &vFlags) &&
|
|
(V_I4(&vFlags)& k_ulFlagsConsentCookieNeeded))
|
|
{
|
|
piProfile->put_unencryptedProfile(NULL);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ConsentStatus_Unknown :
|
|
piProfile->put_unencryptedProfile(NULL);
|
|
break;
|
|
|
|
default:
|
|
_ASSERT(0); // should not be here
|
|
break;
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
Cleanup:
|
|
|
|
if (ret) FREE_BSTR(ret);
|
|
|
|
if(g_pPerf)
|
|
{
|
|
switch(hr)
|
|
{
|
|
case PP_E_INVALID_TICKET:
|
|
case E_INVALIDARG:
|
|
g_pPerf->incrementCounter(PM_INVALIDREQUESTS_TOTAL);
|
|
g_pPerf->incrementCounter(PM_INVALIDREQUESTS_SEC);
|
|
break;
|
|
|
|
default:
|
|
g_pPerf->incrementCounter(PM_VALIDREQUESTS_TOTAL);
|
|
g_pPerf->incrementCounter(PM_VALIDREQUESTS_SEC);
|
|
break;
|
|
}
|
|
|
|
g_pPerf->incrementCounter(PM_REQUESTS_TOTAL);
|
|
g_pPerf->incrementCounter(PM_REQUESTS_SEC);
|
|
}
|
|
else
|
|
{
|
|
_ASSERT(g_pPerf);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// check if the ticket is secure -- private function
|
|
//
|
|
|
|
HRESULT
|
|
DoSecureCheck(
|
|
BSTR bstrSecure,
|
|
CRegistryConfig* pRegistryConfig,
|
|
IPassportTicket* piTicket
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
BSTR ret = NULL;
|
|
CCoCrypt* crypt = NULL;
|
|
time_t tValidUntil;
|
|
time_t tNow = time(NULL);
|
|
int kv;
|
|
|
|
if (!g_config->isValid()) // Guarantees config is non-null
|
|
{
|
|
AtlReportError(CLSID_FastAuth, PP_E_NOT_CONFIGUREDSTR,
|
|
IID_IPassportFastAuth, PP_E_NOT_CONFIGURED);
|
|
hr = PP_E_NOT_CONFIGURED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Make sure we have both ticket and profile first.
|
|
if (bstrSecure == NULL || SysStringLen(bstrSecure) == 0)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Get key version and crypt object.
|
|
kv = CCoCrypt::getKeyVersion(bstrSecure);
|
|
crypt = pRegistryConfig->getCrypt(kv, &tValidUntil);
|
|
|
|
if (crypt == NULL)
|
|
{
|
|
if (g_pAlert)
|
|
g_pAlert->report(PassportAlertInterface::ERROR_TYPE, PM_INVALID_KEY,
|
|
0, NULL, sizeof(DWORD), (LPVOID)&kv);
|
|
AtlReportError(CLSID_FastAuth, PP_E_INVALID_TICKETSTR,
|
|
IID_IPassportFastAuth, PP_E_INVALID_TICKET);
|
|
hr = PP_E_INVALID_TICKET;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Is the key still valid?
|
|
if(tValidUntil && tValidUntil < tNow)
|
|
{
|
|
DWORD dwTimes[2] = { tValidUntil, tNow };
|
|
TCHAR *pszStrings[1];
|
|
TCHAR value[34]; // the _itot only takes upto 33 chars
|
|
pszStrings[0] = _itot(pRegistryConfig->getSiteId(), value, 10);
|
|
|
|
if(g_pAlert)
|
|
g_pAlert->report(PassportAlertInterface::WARNING_TYPE, PM_KEY_EXPIRED,
|
|
1, (LPCTSTR*)pszStrings, sizeof(DWORD) << 1, (LPVOID)dwTimes);
|
|
AtlReportError(CLSID_FastAuth, PP_E_INVALID_TICKETSTR,
|
|
IID_IPassportFastAuth, PP_E_INVALID_TICKET);
|
|
hr = PP_E_INVALID_TICKET;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Decrypt the ticket and set it into the ticket object.
|
|
if(crypt->Decrypt(bstrSecure, SysStringByteLen(bstrSecure), &ret)==FALSE)
|
|
{
|
|
AtlReportError(CLSID_FastAuth, PP_E_INVALID_TICKETSTR,
|
|
IID_IPassportFastAuth, PP_E_INVALID_TICKET);
|
|
hr = PP_E_INVALID_TICKET;
|
|
goto Cleanup;
|
|
}
|
|
|
|
TAKEOVER_BSTR(ret);
|
|
piTicket->DoSecureCheck(ret);
|
|
FREE_BSTR(ret);
|
|
ret = NULL;
|
|
|
|
hr = S_OK;
|
|
|
|
Cleanup:
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// Get HTTP request info from ECB
|
|
//
|
|
|
|
|
|
LPSTR
|
|
GetServerVariableECB(
|
|
EXTENSION_CONTROL_BLOCK* pECB,
|
|
LPSTR pszHeader
|
|
)
|
|
{
|
|
DWORD dwSize = 0;
|
|
LPSTR lpBuf;
|
|
|
|
pECB->GetServerVariable(pECB->ConnID, pszHeader, NULL, &dwSize);
|
|
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER || dwSize == 0)
|
|
{
|
|
lpBuf = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
lpBuf = new CHAR[dwSize];
|
|
if(!lpBuf)
|
|
goto Cleanup;
|
|
|
|
if(!pECB->GetServerVariable(pECB->ConnID, pszHeader, lpBuf, &dwSize))
|
|
{
|
|
delete [] lpBuf;
|
|
lpBuf = NULL;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
return lpBuf;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Get HTTP request info from Filter context
|
|
//
|
|
|
|
LPSTR
|
|
GetServerVariablePFC(
|
|
PHTTP_FILTER_CONTEXT pPFC,
|
|
LPSTR pszHeader
|
|
)
|
|
{
|
|
DWORD dwSize;
|
|
LPSTR lpBuf;
|
|
CHAR cDummy;
|
|
|
|
dwSize = 1;
|
|
pPFC->GetServerVariable(pPFC, pszHeader, &cDummy, &dwSize);
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || dwSize == 0)
|
|
{
|
|
lpBuf = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
lpBuf = new CHAR[dwSize];
|
|
if(!lpBuf)
|
|
goto Cleanup;
|
|
|
|
if(!pPFC->GetServerVariable(pPFC, pszHeader, lpBuf, &dwSize))
|
|
{
|
|
delete [] lpBuf;
|
|
lpBuf = NULL;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
return lpBuf;
|
|
}
|
|
|
|
LONG
|
|
HexToNum(
|
|
WCHAR c
|
|
)
|
|
{
|
|
return ((c >= L'0' && c <= L'9') ? (c - L'0') : ((c >= 'A' && c <= 'F') ? (c - L'A' + 10) : -1));
|
|
}
|
|
|
|
LONG
|
|
FromHex(
|
|
LPCWSTR pszHexString
|
|
)
|
|
{
|
|
LONG lResult = 0;
|
|
LONG lCurrent;
|
|
LPWSTR pszCurrent;
|
|
|
|
for(pszCurrent = const_cast<LPWSTR>(pszHexString); *pszCurrent; pszCurrent++)
|
|
{
|
|
if((lCurrent = HexToNum(towupper(*pszCurrent))) == -1)
|
|
break; // illegal character, we're done
|
|
|
|
lResult = (lResult << 4) + lCurrent;
|
|
}
|
|
|
|
return lResult;
|
|
}
|
|
|
|
|
|
inline BOOL PPIsUnsafeUrlChar(TCHAR chIn) throw();
|
|
|
|
//===========================================================================
|
|
//
|
|
// PPEscapeUrl
|
|
//
|
|
|
|
BOOL PPEscapeUrl(LPCTSTR lpszStringIn,
|
|
LPTSTR lpszStringOut,
|
|
DWORD* pdwStrLen,
|
|
DWORD dwMaxLength,
|
|
DWORD dwFlags)
|
|
{
|
|
TCHAR ch;
|
|
DWORD dwLen = 0;
|
|
BOOL bRet = TRUE;
|
|
BOOL bSchemeFile = FALSE;
|
|
DWORD dwColonPos = 0;
|
|
DWORD dwFlagsInternal = dwFlags;
|
|
while((ch = *lpszStringIn++) != '\0')
|
|
{
|
|
//if we are at the maximum length, set bRet to FALSE
|
|
//this ensures no more data is written to lpszStringOut, but
|
|
//the length of the string is still updated, so the user
|
|
//knows how much space to allocate
|
|
if (dwLen == dwMaxLength)
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
|
|
//if we are encoding and it is an unsafe character
|
|
if (PPIsUnsafeUrlChar(ch))
|
|
{
|
|
{
|
|
//if there is not enough space for the escape sequence
|
|
if (dwLen >= (dwMaxLength-3))
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
if (bRet)
|
|
{
|
|
//output the percent, followed by the hex value of the character
|
|
*lpszStringOut++ = '%';
|
|
_stprintf(lpszStringOut, _T("%.2X"), (unsigned char)(ch));
|
|
lpszStringOut+= 2;
|
|
}
|
|
dwLen += 2;
|
|
}
|
|
}
|
|
else //safe character
|
|
{
|
|
if (bRet)
|
|
*lpszStringOut++ = ch;
|
|
}
|
|
dwLen++;
|
|
}
|
|
|
|
if (bRet)
|
|
*lpszStringOut = L'\0';
|
|
*pdwStrLen = dwLen;
|
|
return bRet;
|
|
}
|
|
|
|
//Determine if the character is unsafe under the URI RFC document
|
|
inline BOOL PPIsUnsafeUrlChar(TCHAR chIn) throw()
|
|
{
|
|
unsigned char ch = (unsigned char)chIn;
|
|
switch(ch)
|
|
{
|
|
case ';': case '\\': case '?': case '@': case '&':
|
|
case '=': case '+': case '$': case ',': case ' ':
|
|
case '<': case '>': case '#': case '%': case '\"':
|
|
case '{': case '}': case '|':
|
|
case '^': case '[': case ']': case '`':
|
|
return TRUE;
|
|
default:
|
|
{
|
|
if (ch < 32 || ch > 126)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Get RAW headers in a parse -- a more efficient way of getting multiple headers back
|
|
// return value:
|
|
// -1: indicate failuer
|
|
// 0: or positive -- # of headers found
|
|
// input: headers, names, namescount
|
|
// output: values -- value of the corresponding header, dwSizes -- size of the value;
|
|
//
|
|
int GetRawHeaders(LPCSTR headers, LPCSTR* names, LPCSTR* values, DWORD* dwSizes, DWORD namescount)
|
|
{
|
|
if (!headers || !names || !values || !dwSizes) return -1;
|
|
if (IsBadReadPtr(names, namescount * sizeof(LPCSTR))
|
|
|| IsBadWritePtr(values, namescount * sizeof(LPCSTR*))
|
|
|| IsBadWritePtr(dwSizes, namescount * sizeof(DWORD*))
|
|
) return -1;
|
|
|
|
int c = 0;
|
|
int i = 0;
|
|
// init output params
|
|
|
|
// loop through headers
|
|
LPCSTR header = headers;
|
|
LPCSTR T;
|
|
DWORD l;
|
|
ZeroMemory(values, sizeof(LPCSTR*) * namescount);
|
|
ZeroMemory(dwSizes, sizeof(DWORD*) * namescount);
|
|
|
|
do
|
|
{
|
|
// white spaces
|
|
while(*header == ' ') ++header;
|
|
|
|
// find if the header is intersted
|
|
T = strchr(header, ':');
|
|
|
|
i = namescount;
|
|
if(T && T != header)
|
|
{
|
|
l = T - header; // size of the header name string
|
|
TempSubStr ss(header, l);
|
|
++T;
|
|
|
|
while( --i >= 0)
|
|
{
|
|
if(strcmp(*(names + i), header) == 0)
|
|
{
|
|
// white spaces
|
|
while(*T == ' ') ++T;
|
|
|
|
*(values + i) = T;
|
|
++c;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// move forward
|
|
header = T;
|
|
}
|
|
|
|
// not found
|
|
while(*header != 0 && !(*header == 0xd && *(header + 1)==0xa)) ++header;
|
|
|
|
// fillin the size of the header value
|
|
if (i >= 0 && i < (int)namescount)
|
|
*(dwSizes + i) = header - T;
|
|
|
|
// move to next header
|
|
if(*header == 0) header = 0;
|
|
else
|
|
header += 2; // skip 0x0D0A
|
|
|
|
} while(header);
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// get QueryString from HTTP request_line
|
|
//
|
|
|
|
LPCSTR GetRawQueryString(LPCSTR request_line, DWORD* dwSize)
|
|
{
|
|
if (!request_line) return NULL;
|
|
LPCSTR URI = strchr(request_line, ' ');
|
|
|
|
if (!URI) return NULL;
|
|
|
|
LPCSTR QS = strchr(URI + 1, '?');
|
|
|
|
if (!QS) return NULL;
|
|
++QS;
|
|
|
|
// make sure if not part of someother header
|
|
LPCSTR end = strchr(QS,' ');
|
|
|
|
DWORD size = 0;
|
|
if (!end)
|
|
size = strlen(QS);
|
|
else
|
|
size = end - QS;
|
|
|
|
if (size == 0)
|
|
return NULL;
|
|
|
|
if (dwSize)
|
|
*dwSize = size;
|
|
|
|
return QS;
|
|
}
|