1139 lines
32 KiB
C++
1139 lines
32 KiB
C++
#include <wininetp.h>
|
|
#include <urlmon.h>
|
|
#include <splugin.hxx>
|
|
#include "htuu.h"
|
|
|
|
/*---------------------------------------------------------------------------
|
|
PASSPORT_CTX
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Constructor
|
|
---------------------------------------------------------------------------*/
|
|
PASSPORT_CTX::PASSPORT_CTX(HTTP_REQUEST_HANDLE_OBJECT *pRequest, BOOL fIsProxy,
|
|
SPMData* pSPM, AUTH_CREDS* pCreds)
|
|
: AUTHCTX(pSPM, pCreds)
|
|
{
|
|
_fIsProxy = fIsProxy;
|
|
|
|
_pRequest = pRequest;
|
|
|
|
m_hLogon = NULL;
|
|
|
|
m_pNewThreadInfo = NULL;
|
|
m_pwszPartnerInfo = NULL;
|
|
m_lpszRetUrl = NULL;
|
|
|
|
m_wRealm[0] = '\0';
|
|
m_pszFromPP = NULL;
|
|
|
|
m_fPreauthFailed = FALSE;
|
|
m_fAnonymous = TRUE;
|
|
|
|
m_AuthComplete = FALSE;
|
|
|
|
m_pszCbUrl = NULL;
|
|
m_pszCbTxt = NULL;
|
|
|
|
::MultiByteToWideChar(CP_ACP, 0, _pRequest->GetServerName(), -1, m_wTarget, MAX_AUTH_TARGET_LEN);
|
|
}
|
|
|
|
BOOL PASSPORT_CTX::Init(void)
|
|
{
|
|
m_pNewThreadInfo = ::InternetCreateThreadInfo(FALSE);
|
|
if (m_pNewThreadInfo == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
LPINTERNET_THREAD_INFO pCurrentThreadInfo = ::InternetGetThreadInfo();
|
|
m_pNewThreadInfo->ThreadId = ::GetCurrentThreadId();
|
|
::InternetSetThreadInfo(m_pNewThreadInfo);
|
|
|
|
m_pInternet = GetRootHandle (_pRequest);
|
|
|
|
if (!m_pInternet->GetPPContext())
|
|
{
|
|
PWSTR pProxyUser = NULL;
|
|
PWSTR pProxyPass = NULL;
|
|
|
|
if (_pRequest->_pTweenerProxyCreds)
|
|
{
|
|
if (_pRequest->_pTweenerProxyCreds->lpszUser)
|
|
{
|
|
DWORD dwProxyUserLength = strlen(_pRequest->_pTweenerProxyCreds->lpszUser);
|
|
pProxyUser = new WCHAR[dwProxyUserLength+1];
|
|
if (pProxyUser)
|
|
{
|
|
::MultiByteToWideChar(CP_ACP, 0, _pRequest->_pTweenerProxyCreds->lpszUser, -1, pProxyUser, dwProxyUserLength+1);
|
|
}
|
|
}
|
|
|
|
if (_pRequest->_pTweenerProxyCreds->lpszPass)
|
|
{
|
|
DWORD dwProxyPassLength = strlen(_pRequest->_pTweenerProxyCreds->lpszPass);
|
|
pProxyPass = new WCHAR[dwProxyPassLength+1];
|
|
if (pProxyPass)
|
|
{
|
|
::MultiByteToWideChar(CP_ACP, 0, _pRequest->_pTweenerProxyCreds->lpszPass, -1, pProxyPass, dwProxyPassLength+1);
|
|
}
|
|
}
|
|
}
|
|
|
|
PP_CONTEXT hPP = ::PP_InitContext(L"WinHttp.Dll", m_pInternet->GetPseudoHandle(), pProxyUser, pProxyPass);
|
|
m_pInternet->SetPPContext(hPP);
|
|
hPP = NULL;
|
|
|
|
pProxyUser = NULL;
|
|
pProxyPass = NULL;
|
|
|
|
}
|
|
|
|
::InternetSetThreadInfo(pCurrentThreadInfo);
|
|
|
|
if (!m_pInternet->GetPPContext())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
/*---------------------------------------------------------------------------
|
|
Destructor
|
|
---------------------------------------------------------------------------*/
|
|
PASSPORT_CTX::~PASSPORT_CTX()
|
|
{
|
|
LPINTERNET_THREAD_INFO pCurrentThreadInfo = ::InternetGetThreadInfo();
|
|
|
|
m_pNewThreadInfo->ThreadId = ::GetCurrentThreadId();
|
|
::InternetSetThreadInfo(m_pNewThreadInfo);
|
|
|
|
if (m_hLogon)
|
|
{
|
|
::PP_FreeLogonContext(m_hLogon);
|
|
m_hLogon = NULL;
|
|
}
|
|
|
|
::InternetSetThreadInfo(pCurrentThreadInfo);
|
|
|
|
if (m_pNewThreadInfo)
|
|
{
|
|
::InternetFreeThreadInfo(m_pNewThreadInfo);
|
|
}
|
|
|
|
if (m_pwszPartnerInfo)
|
|
{
|
|
delete [] m_pwszPartnerInfo;
|
|
}
|
|
|
|
if (m_lpszRetUrl)
|
|
{
|
|
delete [] m_lpszRetUrl;
|
|
}
|
|
|
|
if (m_pszFromPP)
|
|
{
|
|
delete [] m_pszFromPP;
|
|
}
|
|
|
|
if (m_pszCbUrl != NULL)
|
|
{
|
|
delete [] m_pszCbUrl;
|
|
}
|
|
|
|
if (m_pszCbTxt != NULL)
|
|
{
|
|
delete [] m_pszCbTxt;
|
|
}
|
|
}
|
|
|
|
BOOL PASSPORT_CTX::CallbackRegistered(void)
|
|
{
|
|
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
|
|
if (lpThreadInfo)
|
|
{
|
|
WINHTTP_STATUS_CALLBACK appCallback =
|
|
((INTERNET_HANDLE_BASE *)lpThreadInfo->hObjectMapped)->GetStatusCallback();
|
|
|
|
if (appCallback != NULL)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
DWORD PASSPORT_CTX::HandleSuccessfulLogon(
|
|
LPWSTR* ppwszFromPP,
|
|
PDWORD pdwFromPP,
|
|
BOOL fPreAuth
|
|
)
|
|
{
|
|
// biaow-todo: I am betting the RU DWORD UrlLength = 1024;
|
|
LPWSTR pwszUrl = (LPWSTR) ALLOCATE_FIXED_MEMORY(1024 * sizeof(WCHAR));
|
|
DWORD dwwUrlLength = 1024;// won't be too long, but I could be wrong
|
|
LPSTR pszUrl = (LPSTR) ALLOCATE_FIXED_MEMORY(dwwUrlLength * sizeof(CHAR));
|
|
BOOL fRetrySameUrl;
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
|
|
if (pwszUrl == NULL || pszUrl == NULL)
|
|
{
|
|
dwRet = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
*pdwFromPP = 0;
|
|
|
|
if (::PP_GetAuthorizationInfo(m_hLogon,
|
|
NULL,
|
|
pdwFromPP,
|
|
&fRetrySameUrl,
|
|
pwszUrl,
|
|
&dwwUrlLength
|
|
) == FALSE)
|
|
{
|
|
*ppwszFromPP = new WCHAR[*pdwFromPP];
|
|
if (*ppwszFromPP == NULL)
|
|
{
|
|
dwRet = ERROR_WINHTTP_LOGIN_FAILURE;
|
|
goto exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
INET_ASSERT(TRUE); // this shouldn't happen
|
|
}
|
|
|
|
if (::PP_GetAuthorizationInfo(m_hLogon,
|
|
*ppwszFromPP,
|
|
pdwFromPP,
|
|
&fRetrySameUrl,
|
|
pwszUrl,
|
|
&dwwUrlLength
|
|
) == FALSE)
|
|
{
|
|
INET_ASSERT(TRUE); // this shouldn't happen
|
|
dwRet = ERROR_WINHTTP_LOGIN_FAILURE;
|
|
goto exit;
|
|
}
|
|
|
|
// save the DA Host name for Logout security check
|
|
/*
|
|
WCHAR wszDAHost[256];
|
|
DWORD dwHostLen = ARRAY_ELEMENTS(wszDAHost);
|
|
if (::PP_GetLogonHost(m_hLogon, wszDAHost, &dwHostLen) == TRUE)
|
|
{
|
|
::WideCharToMultiByte(CP_ACP, 0, wszDAHost, -1, g_szPassportDAHost, 256, NULL, NULL);
|
|
}
|
|
*/
|
|
|
|
if (!fRetrySameUrl)
|
|
{
|
|
if (_pRequest->GetMethodType() == HTTP_METHOD_TYPE_GET)
|
|
{
|
|
// DA wanted us to GET to a new Url
|
|
::WideCharToMultiByte(CP_ACP, 0, pwszUrl, -1, pszUrl, 1024, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
fRetrySameUrl = TRUE; // *** WinHttp currently supports retry custom verb to same URL only ***
|
|
}
|
|
}
|
|
|
|
if (fPreAuth)
|
|
{
|
|
// We are sending, in the context of AuthOnRequest.
|
|
|
|
if (fRetrySameUrl)
|
|
{
|
|
// DA told us to keep Verb & Url, so there is nothing more needs to be done
|
|
goto exit;
|
|
}
|
|
|
|
// Regardless whether we are asked to handle redirect, we'll need to fake
|
|
// that a 302 just came in.
|
|
|
|
// biaow-todo: this is causing problem for QueryHeaders(StatusCode). I don't know why yet...
|
|
/*
|
|
_pRequest->AddInternalResponseHeader(HTTP_QUERY_STATUS_TEXT, // use non-standard index, since we never query this normally
|
|
"HTTP/1.0 302 Object Moved",
|
|
strlen("HTTP/1.0 302 Object Moved")
|
|
);
|
|
_pRequest->AddInternalResponseHeader(HTTP_QUERY_LOCATION,
|
|
pszUrl,
|
|
strlen(pszUrl));
|
|
*/
|
|
|
|
|
|
//
|
|
// todo: if REDIRECT_POLICY is POLICY_DISALLOW_HTTPS_TO_HTTP, do not allow
|
|
//the passport server to redirect to an HTTP site if the original request
|
|
//was to an HTTPS site
|
|
//
|
|
if (_pRequest->GetDwordOption(WINHTTP_OPTION_REDIRECT_POLICY) == WINHTTP_OPTION_REDIRECT_POLICY_NEVER)
|
|
{
|
|
if (!CallbackRegistered())
|
|
{
|
|
_pRequest->SetPPAbort(TRUE);
|
|
dwRet = ERROR_WINHTTP_LOGIN_FAILURE;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
::InternetIndicateStatusString(WINHTTP_CALLBACK_STATUS_REDIRECT, pszUrl);
|
|
}
|
|
else
|
|
{
|
|
// We are receiving a 302, in the context of AuthOnResponse.
|
|
|
|
// Here we need to re-play the request to lpszRetUrl. One way to
|
|
// achieve this is returning ERROR_INTERNET_FORCE_RETRY. But before
|
|
// that, we'll need to remember the lpszRetUrl.
|
|
|
|
// *NOTE* This is in effective an 401. To prevent the send path from
|
|
// following the 302 Location: header, caller must set the status code
|
|
// to 401.
|
|
|
|
if (!fRetrySameUrl)
|
|
{
|
|
//
|
|
// todo: if REDIRECT_POLICY is POLICY_DISALLOW_HTTPS_TO_HTTP, do not allow
|
|
//the passport server to redirect to an HTTP site if the original request
|
|
//was to an HTTPS site
|
|
//
|
|
if (_pRequest->GetDwordOption(WINHTTP_OPTION_REDIRECT_POLICY) == WINHTTP_OPTION_REDIRECT_POLICY_NEVER)
|
|
{
|
|
if (!CallbackRegistered())
|
|
{
|
|
dwRet = ERROR_WINHTTP_LOGIN_FAILURE;
|
|
goto exit;
|
|
}
|
|
|
|
::InternetIndicateStatusString(WINHTTP_CALLBACK_STATUS_REDIRECT, pszUrl);
|
|
}
|
|
}
|
|
|
|
dwRet = ERROR_WINHTTP_RESEND_REQUEST;
|
|
}
|
|
|
|
PCSTR lpszRetUrl = NULL;
|
|
|
|
if (fRetrySameUrl)
|
|
{
|
|
lpszRetUrl = _pRequest->GetURL();
|
|
}
|
|
else
|
|
{
|
|
lpszRetUrl = pszUrl;
|
|
}
|
|
|
|
if (m_lpszRetUrl)
|
|
{
|
|
delete [] m_lpszRetUrl;
|
|
}
|
|
|
|
m_lpszRetUrl = new CHAR[strlen(lpszRetUrl) + 1];
|
|
if (m_lpszRetUrl)
|
|
{
|
|
strcpy(m_lpszRetUrl, lpszRetUrl);
|
|
}
|
|
|
|
exit:
|
|
|
|
if (pwszUrl)
|
|
{
|
|
FREE_MEMORY(pwszUrl);
|
|
}
|
|
if (pszUrl)
|
|
{
|
|
FREE_MEMORY(pszUrl);
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
DWORD PASSPORT_CTX::SetCreds(BOOL* pfCredSet)
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
BOOL fUseDefaultCreds;
|
|
LPWSTR pwszUser = NULL;
|
|
LPWSTR pwszPass = NULL;
|
|
|
|
if (_pCreds->lpszUser && _pCreds->lpszPass) // both User and Pass are specified
|
|
{
|
|
fUseDefaultCreds = FALSE;
|
|
}
|
|
else if (!_pCreds->lpszUser && !_pCreds->lpszPass) // both User and Pass are NULL
|
|
{
|
|
fUseDefaultCreds = TRUE;
|
|
}
|
|
else
|
|
{
|
|
INET_ASSERT(TRUE); // this case should not happen
|
|
fUseDefaultCreds = TRUE;
|
|
}
|
|
|
|
PSYSTEMTIME pCredTimestamp = NULL;
|
|
SYSTEMTIME TimeCredsEntered;
|
|
|
|
if (!fUseDefaultCreds)
|
|
{
|
|
pwszUser = (LPWSTR) ALLOCATE_FIXED_MEMORY((strlen(_pCreds->lpszUser) + 1) * sizeof(WCHAR));
|
|
pwszPass = (LPWSTR) ALLOCATE_FIXED_MEMORY((strlen(_pCreds->lpszPass) + 1) * sizeof(WCHAR));
|
|
|
|
if (pwszUser && pwszPass)
|
|
{
|
|
if( (0 == ::MultiByteToWideChar(CP_ACP, 0, _pCreds->lpszUser, -1, pwszUser, strlen(_pCreds->lpszUser) + 1))
|
|
|| (0 ==::MultiByteToWideChar(CP_ACP, 0, _pCreds->lpszPass, -1, pwszPass, strlen(_pCreds->lpszPass) + 1)))
|
|
{
|
|
pwszUser[0] = L'\0';
|
|
pwszPass[0] = L'\0';
|
|
dwError=GetLastError();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
pCredTimestamp = &TimeCredsEntered;
|
|
::GetSystemTime(pCredTimestamp); // time-stamp the creds
|
|
}
|
|
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
if (pwszUser == NULL && pwszPass == NULL && m_pInternet->KeyringDisabled())
|
|
{
|
|
*pfCredSet = FALSE;
|
|
}
|
|
else
|
|
{
|
|
*pfCredSet = ::PP_SetCredentials(m_hLogon, m_wRealm, m_wTarget, pwszUser, pwszPass, pCredTimestamp);
|
|
}
|
|
}
|
|
|
|
if (pwszUser)
|
|
{
|
|
memset( pwszUser, 0, sizeof(pwszUser[0])*wcslen(pwszUser));
|
|
FREE_MEMORY(pwszUser);
|
|
}
|
|
if (pwszPass)
|
|
{
|
|
memset( pwszPass, 0, sizeof(pwszPass[0])*wcslen(pwszPass));
|
|
FREE_MEMORY(pwszPass);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
DWORD PASSPORT_CTX::ModifyRequestBasedOnRU(void)
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
INTERNET_SCHEME schemeType;
|
|
INTERNET_SCHEME currentSchemeType;
|
|
INTERNET_PORT currentHostPort;
|
|
LPSTR currentHostName;
|
|
DWORD currentHostNameLength;
|
|
|
|
INTERNET_PORT port = 0;
|
|
LPSTR pszHostName;
|
|
DWORD dwHostNameLength = 0;
|
|
LPSTR pszUrlPath;
|
|
DWORD dwUrlPathLength = 0;
|
|
LPSTR extra;
|
|
DWORD extraLength;
|
|
|
|
dwError = CrackUrl(m_lpszRetUrl,
|
|
0,
|
|
FALSE, // don't escape URL-path
|
|
&schemeType,
|
|
NULL, // scheme name, don't care
|
|
NULL,
|
|
&pszHostName,
|
|
&dwHostNameLength,
|
|
TRUE,
|
|
&port,
|
|
NULL, // UserName, don't care
|
|
NULL,
|
|
NULL, // Password, don't care
|
|
NULL,
|
|
&pszUrlPath,
|
|
&dwUrlPathLength,
|
|
&extra,
|
|
&extraLength,
|
|
NULL);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// if there is an intra-page link on the redirected URL then get rid of it:
|
|
// we don't send it to the server, and we have already indicated it to the
|
|
// app
|
|
//
|
|
|
|
if (extraLength != 0) {
|
|
|
|
INET_ASSERT(extra != NULL);
|
|
INET_ASSERT(!IsBadWritePtr(extra, 1));
|
|
|
|
if (*extra == '#') {
|
|
*extra = '\0';
|
|
// newUrlLength -= extraLength;
|
|
} else {
|
|
dwUrlPathLength += extraLength;
|
|
}
|
|
}
|
|
|
|
if (port == INTERNET_INVALID_PORT_NUMBER) {
|
|
port = (INTERNET_PORT)((schemeType == INTERNET_SCHEME_HTTPS)
|
|
? INTERNET_DEFAULT_HTTPS_PORT
|
|
: INTERNET_DEFAULT_HTTP_PORT);
|
|
}
|
|
|
|
currentHostPort = _pRequest->GetHostPort();
|
|
currentHostName = _pRequest->GetHostName(¤tHostNameLength);
|
|
|
|
if (port != currentHostPort) {
|
|
_pRequest->SetHostPort(port);
|
|
}
|
|
if ((dwHostNameLength != currentHostNameLength)
|
|
|| (strnicmp(pszHostName, currentHostName, dwHostNameLength) != 0)) {
|
|
|
|
char hostValue[INTERNET_MAX_HOST_NAME_LENGTH + sizeof(":4294967295")];
|
|
LPSTR hostValueStr;
|
|
DWORD hostValueSize;
|
|
|
|
CHAR chBkChar = pszHostName[dwHostNameLength]; // save off char
|
|
|
|
pszHostName[dwHostNameLength] = '\0';
|
|
_pRequest->SetHostName(pszHostName);
|
|
|
|
hostValueSize = dwHostNameLength;
|
|
hostValueStr = pszHostName;
|
|
|
|
if ((port != INTERNET_DEFAULT_HTTP_PORT)
|
|
&& (port != INTERNET_DEFAULT_HTTPS_PORT)) {
|
|
if (hostValueSize > INTERNET_MAX_HOST_NAME_LENGTH)
|
|
{
|
|
pszHostName[dwHostNameLength] = chBkChar; // put back char
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
hostValueSize = wsprintf(hostValue, "%s:%d", pszHostName, (port & 0xffff));
|
|
hostValueStr = hostValue;
|
|
}
|
|
|
|
pszHostName[dwHostNameLength] = chBkChar; // put back char
|
|
|
|
//
|
|
// replace the "Host:" header
|
|
//
|
|
|
|
_pRequest->ReplaceRequestHeader(HTTP_QUERY_HOST,
|
|
hostValueStr,
|
|
hostValueSize,
|
|
0, // dwIndex
|
|
ADD_HEADER
|
|
);
|
|
|
|
//
|
|
// and get the corresponding server info, resolving the name if
|
|
// required
|
|
//
|
|
|
|
// _pRequest->SetServerInfo(FALSE);
|
|
|
|
//
|
|
// Since we are redirecting to a different host, force an update of the origin
|
|
// server. Otherwise, we will still pick up the proxy info of the first server.
|
|
//
|
|
|
|
// todo:
|
|
// _pRequest->SetOriginServer(TRUE);
|
|
}
|
|
|
|
currentSchemeType = ((WINHTTP_FLAG_SECURE & _pRequest->GetOpenFlags()) ?
|
|
INTERNET_SCHEME_HTTPS :
|
|
INTERNET_SCHEME_HTTP);
|
|
|
|
if ( currentSchemeType != schemeType )
|
|
{
|
|
DWORD OpenFlags = _pRequest->GetOpenFlags();
|
|
|
|
// Switched From HTTPS to HTTP
|
|
if ( currentSchemeType == INTERNET_SCHEME_HTTPS )
|
|
{
|
|
INET_ASSERT(schemeType != INTERNET_SCHEME_HTTPS );
|
|
|
|
OpenFlags &= ~(WINHTTP_FLAG_SECURE);
|
|
}
|
|
|
|
// Switched From HTTP to HTTPS
|
|
else if ( schemeType == INTERNET_SCHEME_HTTPS )
|
|
{
|
|
INET_ASSERT(currentSchemeType == INTERNET_SCHEME_HTTP );
|
|
|
|
OpenFlags |= (WINHTTP_FLAG_SECURE);
|
|
}
|
|
|
|
_pRequest->SetOpenFlags(OpenFlags);
|
|
_pRequest->SetSchemeType(schemeType);
|
|
|
|
}
|
|
|
|
_pRequest->SetURL(m_lpszRetUrl);
|
|
|
|
if (_pRequest->IsRequestUsingProxy())
|
|
{
|
|
_pRequest->ModifyRequest(_pRequest->GetMethodType(),
|
|
m_lpszRetUrl,
|
|
strlen(m_lpszRetUrl),
|
|
NULL,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
_pRequest->ModifyRequest(_pRequest->GetMethodType(),
|
|
pszUrlPath, // m_lpszRetUrl,
|
|
strlen(pszUrlPath),//strlen(m_lpszRetUrl),
|
|
NULL,
|
|
0);
|
|
}
|
|
|
|
cleanup:
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
PreAuthUser
|
|
---------------------------------------------------------------------------*/
|
|
DWORD PASSPORT_CTX::PreAuthUser(IN LPSTR pBuf, IN OUT LPDWORD pcbBuf)
|
|
{
|
|
DEBUG_ENTER ((
|
|
DBG_HTTP,
|
|
Dword,
|
|
"PASSPORT_CTX::PreAuthUser",
|
|
"this=%#x pBuf=%#x pcbBuf=%#x {%d}",
|
|
this,
|
|
pBuf,
|
|
pcbBuf,
|
|
*pcbBuf
|
|
));
|
|
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
LPWSTR pwszFromPP = NULL;
|
|
|
|
// Prefix the header value with the auth type.
|
|
const static BYTE szPassport[] = "Passport1.4 ";
|
|
#define PASSPORT_LEN sizeof(szPassport)-1
|
|
|
|
if (m_pszFromPP == NULL)
|
|
{
|
|
if (m_hLogon == NULL)
|
|
{
|
|
dwError = ERROR_WINHTTP_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
DWORD dwFromPPLen = 0;
|
|
BOOL fCredSet;
|
|
dwError = SetCreds(&fCredSet);
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
LPINTERNET_THREAD_INFO pCurrentThreadInfo = ::InternetGetThreadInfo();
|
|
|
|
m_pNewThreadInfo->ThreadId = ::GetCurrentThreadId();
|
|
::InternetSetThreadInfo(m_pNewThreadInfo);
|
|
|
|
|
|
DWORD dwLogonStatus = ::PP_Logon(m_hLogon,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
0);
|
|
|
|
::InternetSetThreadInfo(pCurrentThreadInfo);
|
|
|
|
if (dwLogonStatus != PP_LOGON_SUCCESS)
|
|
{
|
|
if (dwLogonStatus == PP_LOGON_FAILED)
|
|
{
|
|
m_fPreauthFailed = TRUE;
|
|
}
|
|
|
|
dwError = ERROR_WINHTTP_INTERNAL_ERROR; // need to double check this return error
|
|
goto cleanup;
|
|
}
|
|
|
|
dwError = HandleSuccessfulLogon(&pwszFromPP, &dwFromPPLen, TRUE);
|
|
|
|
if (dwError == ERROR_WINHTTP_LOGIN_FAILURE)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
m_pszFromPP = new CHAR [dwFromPPLen];
|
|
if (m_pszFromPP == NULL)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
::WideCharToMultiByte(CP_ACP, 0, pwszFromPP, -1, m_pszFromPP, dwFromPPLen, NULL, NULL);
|
|
}
|
|
|
|
// check to see if we need to update url
|
|
|
|
if (m_lpszRetUrl)
|
|
{
|
|
dwError = ModifyRequestBasedOnRU();
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
// delete [] m_lpszRetUrl;
|
|
// m_lpszRetUrl = NULL;
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// delete [] m_lpszRetUrl;
|
|
// m_lpszRetUrl = NULL;
|
|
}
|
|
|
|
// Ticket and profile is already present
|
|
|
|
// put in the header
|
|
memcpy (pBuf, szPassport, PASSPORT_LEN);
|
|
pBuf += PASSPORT_LEN;
|
|
|
|
// append the ticket
|
|
strcpy(pBuf, m_pszFromPP);
|
|
*pcbBuf = (DWORD)(PASSPORT_LEN + strlen(m_pszFromPP));
|
|
|
|
m_AuthComplete = TRUE;
|
|
|
|
cleanup:
|
|
if (pwszFromPP)
|
|
delete [] pwszFromPP;
|
|
|
|
DEBUG_LEAVE(dwError);
|
|
return dwError;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
UpdateFromHeaders
|
|
---------------------------------------------------------------------------*/
|
|
DWORD PASSPORT_CTX::UpdateFromHeaders(HTTP_REQUEST_HANDLE_OBJECT *pRequest, BOOL fIsProxy)
|
|
{
|
|
DEBUG_ENTER ((
|
|
DBG_HTTP,
|
|
Dword,
|
|
"PASSPORT_CTX::UpdateFromHeaders",
|
|
"this=%#x request=%#x isproxy=%B",
|
|
this,
|
|
pRequest,
|
|
fIsProxy
|
|
));
|
|
|
|
DWORD dwAuthIdx, cbChallenge, dwError;
|
|
LPSTR szChallenge = NULL;
|
|
|
|
// Get the associated header.
|
|
if ((dwError = FindHdrIdxFromScheme(&dwAuthIdx)) != ERROR_SUCCESS)
|
|
goto exit;
|
|
|
|
if (m_AuthComplete)
|
|
{
|
|
_pRequest->SetStatusCode(401); // this is needed to prevent send code from tracing Location: header
|
|
dwError = ERROR_WINHTTP_LOGIN_FAILURE;
|
|
goto exit;
|
|
}
|
|
|
|
// Get the complete auth header.
|
|
dwError = GetAuthHeaderData(pRequest, fIsProxy, NULL,
|
|
&szChallenge, &cbChallenge, ALLOCATE_BUFFER, dwAuthIdx);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
szChallenge = NULL;
|
|
goto exit;
|
|
}
|
|
|
|
if (!_pCreds)
|
|
{
|
|
_pCreds = CreateCreds(pRequest, fIsProxy, _pSPMData, NULL);
|
|
if (!_pCreds)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (m_pwszPartnerInfo)
|
|
{
|
|
delete [] m_pwszPartnerInfo;
|
|
}
|
|
|
|
{
|
|
LPSTR lpszVerb;
|
|
DWORD dwVerbLength;
|
|
lpszVerb = _pRequest->_RequestHeaders.GetVerb(&dwVerbLength);
|
|
#define MAX_VERB_LENGTH 16
|
|
CHAR szOrgVerb[MAX_VERB_LENGTH] = {0};
|
|
if (dwVerbLength > MAX_VERB_LENGTH - 1)
|
|
{
|
|
goto exit;
|
|
}
|
|
strncpy(szOrgVerb, lpszVerb, dwVerbLength+1);
|
|
PCSTR pszOrgUrl = _pRequest->GetURL();
|
|
|
|
const LPWSTR pwszOrgVerbAttr = L",OrgVerb=";
|
|
const LPWSTR pwszOrgUrlAttr = L",OrgUrl=";
|
|
|
|
DWORD dwPartnerInfoLength = cbChallenge
|
|
+::wcslen(pwszOrgVerbAttr)
|
|
+::strlen(szOrgVerb)
|
|
+::wcslen(pwszOrgUrlAttr)
|
|
+::strlen(pszOrgUrl)
|
|
+ 1; // NULL terminator
|
|
|
|
DWORD dwSize = 0;
|
|
PWSTR pwszPartnerInfo = NULL;
|
|
|
|
m_pwszPartnerInfo = new WCHAR[dwPartnerInfoLength];
|
|
if (m_pwszPartnerInfo == NULL)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
pwszPartnerInfo = m_pwszPartnerInfo;
|
|
|
|
dwSize = ::MultiByteToWideChar(CP_ACP, 0, szChallenge, -1, pwszPartnerInfo, dwPartnerInfoLength) - 1;
|
|
::wcscat(pwszPartnerInfo, pwszOrgVerbAttr);
|
|
pwszPartnerInfo += (dwSize + wcslen(pwszOrgVerbAttr));
|
|
dwPartnerInfoLength -= (dwSize + wcslen(pwszOrgVerbAttr));
|
|
dwSize = ::MultiByteToWideChar(CP_ACP, 0, szOrgVerb, -1, pwszPartnerInfo, dwPartnerInfoLength) - 1;
|
|
::wcscat(pwszPartnerInfo, pwszOrgUrlAttr);
|
|
pwszPartnerInfo += (dwSize + wcslen(pwszOrgUrlAttr));
|
|
dwPartnerInfoLength -= (dwSize + wcslen(pwszOrgUrlAttr));
|
|
dwSize = ::MultiByteToWideChar(CP_ACP, 0, pszOrgUrl, -1, pwszPartnerInfo, dwPartnerInfoLength) - 1;
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
}
|
|
|
|
exit:
|
|
|
|
if (szChallenge)
|
|
delete []szChallenge;
|
|
|
|
DEBUG_LEAVE(dwError);
|
|
return dwError;
|
|
}
|
|
|
|
BOOL PASSPORT_CTX::InitLogonContext(void)
|
|
{
|
|
// set up the thread context before calling the Passport auth library
|
|
|
|
LPINTERNET_THREAD_INFO pCurrentThreadInfo = ::InternetGetThreadInfo();
|
|
m_pNewThreadInfo->ThreadId = ::GetCurrentThreadId();
|
|
::InternetSetThreadInfo(m_pNewThreadInfo);
|
|
|
|
if (!m_hLogon)
|
|
{
|
|
INET_ASSERT(m_pInternet->GetPPContext()); // must have been initialized in the Init() call
|
|
|
|
m_hLogon = ::PP_InitLogonContext(
|
|
m_pInternet->GetPPContext(),
|
|
m_pwszPartnerInfo,
|
|
(_pRequest->GetOpenFlags() & INTERNET_FLAG_NO_COOKIES),
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
// restore the WinHttp thread context
|
|
::InternetSetThreadInfo(pCurrentThreadInfo);
|
|
|
|
PCWSTR pwszRealm = ::wcsstr(m_pwszPartnerInfo, L"srealm");
|
|
if (pwszRealm)
|
|
{
|
|
pwszRealm += ::wcslen(L"srealm");
|
|
if (*pwszRealm == L'=')
|
|
{
|
|
pwszRealm++;
|
|
DWORD i = 0;
|
|
while (*pwszRealm != 0 && *pwszRealm != L',' && i < MAX_AUTH_REALM_LEN-1)
|
|
{
|
|
m_wRealm[i++] = *pwszRealm++;
|
|
}
|
|
|
|
m_wRealm[i] = 0; // null-terminate it
|
|
}
|
|
}
|
|
|
|
if (!m_wRealm[0])
|
|
{
|
|
DWORD dwRealmLen = MAX_AUTH_REALM_LEN;
|
|
PP_GetRealm(m_pInternet->GetPPContext(), m_wRealm, &dwRealmLen);
|
|
}
|
|
|
|
return m_hLogon != NULL;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
PostAuthUser
|
|
---------------------------------------------------------------------------*/
|
|
DWORD PASSPORT_CTX::PostAuthUser()
|
|
{
|
|
DEBUG_ENTER ((
|
|
DBG_HTTP,
|
|
Dword,
|
|
"PASSPORT_CTX::PostAuthUser",
|
|
"this=%#x",
|
|
this
|
|
));
|
|
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
|
|
if (InitLogonContext() == FALSE)
|
|
{
|
|
dwRet = ERROR_WINHTTP_LOGIN_FAILURE;
|
|
goto exit;
|
|
}
|
|
|
|
BOOL fCredSet;
|
|
dwRet = SetCreds(&fCredSet);
|
|
if (dwRet != ERROR_SUCCESS)
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
// Ok, Let's give it a try
|
|
|
|
LPINTERNET_THREAD_INFO pCurrentThreadInfo = ::InternetGetThreadInfo();
|
|
m_pNewThreadInfo->ThreadId = ::GetCurrentThreadId();
|
|
::InternetSetThreadInfo(m_pNewThreadInfo);
|
|
|
|
DWORD dwLogonStatus = ::PP_Logon(m_hLogon,
|
|
m_fAnonymous,
|
|
0,
|
|
NULL,
|
|
0);
|
|
|
|
// restore the WinHttp thread context
|
|
::InternetSetThreadInfo(pCurrentThreadInfo);
|
|
|
|
if (dwLogonStatus == PP_LOGON_REQUIRED)
|
|
{
|
|
// no creds specified, we are required to sign on.
|
|
|
|
// change from 302 to 401
|
|
_pRequest->ReplaceResponseHeader(HTTP_QUERY_STATUS_CODE,
|
|
"401", strlen("401"),
|
|
0, HTTP_ADDREQ_FLAG_REPLACE);
|
|
|
|
// biaow-todo: 1) nice to replace the status text as well; weird to have "HTTP/1.1 401 object moved"
|
|
// for example 2) remove the Location: header
|
|
|
|
BOOL fPrompt;
|
|
DWORD dwCbUrlSize = 0;
|
|
DWORD dwCbTxtSize = 0;
|
|
::PP_GetChallengeInfo(m_hLogon,
|
|
&fPrompt, NULL, &dwCbUrlSize, NULL, &dwCbTxtSize, m_wRealm, MAX_AUTH_REALM_LEN);
|
|
|
|
PWSTR pwszCbUrl = NULL;
|
|
PWSTR pwszCbTxt = NULL;
|
|
|
|
if (dwCbUrlSize)
|
|
{
|
|
pwszCbUrl = new WCHAR[dwCbUrlSize];
|
|
}
|
|
|
|
if (dwCbTxtSize)
|
|
{
|
|
pwszCbTxt = new WCHAR[dwCbTxtSize];
|
|
}
|
|
|
|
::PP_GetChallengeInfo(m_hLogon,
|
|
NULL, pwszCbUrl, &dwCbUrlSize, pwszCbTxt, &dwCbTxtSize, NULL, 0);
|
|
|
|
if (pwszCbUrl)
|
|
{
|
|
m_pszCbUrl = new CHAR[wcslen(pwszCbUrl)+1];
|
|
|
|
if (m_pszCbUrl)
|
|
{
|
|
::WideCharToMultiByte(CP_ACP, 0, pwszCbUrl, -1, m_pszCbUrl, wcslen(pwszCbUrl)+1, NULL, NULL);
|
|
UrlUnescapeA(m_pszCbUrl, NULL, NULL, URL_UNESCAPE_INPLACE);
|
|
}
|
|
}
|
|
|
|
if (pwszCbTxt)
|
|
{
|
|
m_pszCbTxt = new CHAR[wcslen(pwszCbTxt)+1];
|
|
if (m_pszCbTxt)
|
|
{
|
|
::WideCharToMultiByte(CP_ACP, 0, pwszCbTxt, -1, m_pszCbTxt, wcslen(pwszCbTxt)+1, NULL, NULL);
|
|
UrlUnescapeA(m_pszCbTxt, NULL, NULL, URL_UNESCAPE_INPLACE);
|
|
}
|
|
}
|
|
|
|
delete [] pwszCbUrl;
|
|
delete [] pwszCbTxt;
|
|
|
|
if (fPrompt)
|
|
{
|
|
dwRet = ERROR_WINHTTP_INCORRECT_PASSWORD;
|
|
}
|
|
else
|
|
{
|
|
|
|
if (m_fAnonymous)
|
|
{
|
|
if (fCredSet)
|
|
{
|
|
dwRet = ERROR_WINHTTP_RESEND_REQUEST;
|
|
}
|
|
else
|
|
{
|
|
dwRet = ERROR_WINHTTP_INCORRECT_PASSWORD;
|
|
}
|
|
|
|
m_fAnonymous = FALSE;
|
|
}
|
|
else
|
|
{
|
|
dwRet = ERROR_WINHTTP_INCORRECT_PASSWORD;
|
|
}
|
|
}
|
|
|
|
if (dwRet == ERROR_WINHTTP_INCORRECT_PASSWORD)
|
|
{
|
|
Transfer401ContentFromPP();
|
|
}
|
|
|
|
/*
|
|
if (RetryLogon() == TRUE)
|
|
{
|
|
dwRet = ERROR_WINHTTP_RESEND_REQUEST;
|
|
}
|
|
else
|
|
{
|
|
dwRet = ERROR_WINHTTP_INCORRECT_PASSWORD;
|
|
}
|
|
*/
|
|
}
|
|
else if (dwLogonStatus == PP_LOGON_SUCCESS)
|
|
{
|
|
// wow! we got in!!!
|
|
|
|
DWORD dwFromPPLen = 0;
|
|
LPWSTR pwszFromPP = NULL;
|
|
|
|
dwRet = HandleSuccessfulLogon(&pwszFromPP, &dwFromPPLen, FALSE);
|
|
if (dwRet != ERROR_WINHTTP_LOGIN_FAILURE)
|
|
{
|
|
if (m_pszFromPP)
|
|
{
|
|
delete [] m_pszFromPP;
|
|
}
|
|
|
|
m_pszFromPP = new CHAR [dwFromPPLen];
|
|
if (m_pszFromPP)
|
|
{
|
|
::WideCharToMultiByte(CP_ACP, 0, pwszFromPP, -1, m_pszFromPP, dwFromPPLen, NULL, NULL);
|
|
}
|
|
}
|
|
if (pwszFromPP)
|
|
{
|
|
delete [] pwszFromPP;
|
|
}
|
|
|
|
m_fAnonymous = FALSE;
|
|
}
|
|
else
|
|
{
|
|
Transfer401ContentFromPP();
|
|
|
|
dwRet = ERROR_WINHTTP_LOGIN_FAILURE;
|
|
}
|
|
|
|
|
|
exit:
|
|
_pRequest->SetStatusCode(401); // this is needed to prevent send code from tracing Location: header
|
|
|
|
DEBUG_LEAVE(dwRet);
|
|
return dwRet;
|
|
}
|
|
|
|
BOOL PASSPORT_CTX::Transfer401ContentFromPP(void)
|
|
{
|
|
DWORD ContentLength = 0;
|
|
::PP_GetChallengeContent(m_hLogon,
|
|
NULL,
|
|
&ContentLength);
|
|
if (ContentLength > 0)
|
|
{
|
|
LPBYTE pContent = (LPBYTE)ALLOCATE_FIXED_MEMORY(ContentLength);
|
|
if (pContent == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (::PP_GetChallengeContent(m_hLogon,
|
|
pContent,
|
|
&ContentLength) == TRUE)
|
|
{
|
|
BOOL fDrained;
|
|
|
|
// play with socket mode to force DrainResponse to return synchronously
|
|
|
|
ICSocket* pSocket = _pRequest->_Socket;
|
|
if (pSocket)
|
|
{
|
|
BOOL fSocketModeSet = FALSE;
|
|
if (pSocket->IsNonBlocking())
|
|
{
|
|
pSocket->SetNonBlockingMode(FALSE);
|
|
fSocketModeSet = TRUE;
|
|
}
|
|
|
|
INET_ASSERT(pSocket->IsNonBlocking() == FALSE);
|
|
|
|
_pRequest->DrainResponse(&fDrained);
|
|
|
|
if (fSocketModeSet)
|
|
{
|
|
pSocket->SetNonBlockingMode(TRUE);
|
|
}
|
|
}
|
|
|
|
_pRequest->_ResponseHeaders.FreeHeaders();
|
|
_pRequest->FreeResponseBuffer();
|
|
_pRequest->ResetResponseVariables();
|
|
_pRequest->_ResponseHeaders.Initialize();
|
|
|
|
// _pRequest->_dwCurrentStreamPosition = 0;
|
|
|
|
_pRequest->CloneResponseBuffer(pContent, ContentLength);
|
|
}
|
|
FREE_MEMORY(pContent);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|