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.
916 lines
25 KiB
916 lines
25 KiB
#include <wininetp.h>
|
|
#include <urlmon.h>
|
|
#include <splugin.hxx>
|
|
#include "auth.h"
|
|
#include "sspspm.h"
|
|
#include "winctxt.h"
|
|
|
|
extern "C"
|
|
{
|
|
extern SspData *g_pSspData;
|
|
}
|
|
/*-----------------------------------------------------------------------------
|
|
PLUG_CTX
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Load
|
|
---------------------------------------------------------------------------*/
|
|
DWORD PLUG_CTX::Load()
|
|
{
|
|
DEBUG_ENTER ((
|
|
DBG_HTTPAUTH,
|
|
Dword,
|
|
"PLUG_CTX::Load",
|
|
"this=%#x",
|
|
this
|
|
));
|
|
|
|
INET_ASSERT(_pSPMData == _pPWC->pSPM);
|
|
|
|
DWORD_PTR dwAuthCode = 0;
|
|
|
|
|
|
DEBUG_ENTER ((
|
|
DBG_HTTPAUTH,
|
|
Pointer,
|
|
"SSPI_InitScheme",
|
|
"%s",
|
|
GetScheme()
|
|
));
|
|
|
|
dwAuthCode = SSPI_InitScheme (GetScheme());
|
|
|
|
DEBUG_LEAVE(dwAuthCode);
|
|
|
|
|
|
if (!dwAuthCode)
|
|
{
|
|
_pSPMData->eState = STATE_ERROR;
|
|
DEBUG_LEAVE(ERROR_INTERNET_INTERNAL_ERROR);
|
|
return ERROR_INTERNET_INTERNAL_ERROR;
|
|
}
|
|
|
|
_pSPMData->eState = STATE_LOADED;
|
|
DEBUG_LEAVE(ERROR_SUCCESS);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
ClearAuthUser
|
|
---------------------------------------------------------------------------*/
|
|
DWORD PLUG_CTX::ClearAuthUser(LPVOID *ppvContext, LPSTR szServer)
|
|
{
|
|
DEBUG_ENTER ((
|
|
DBG_HTTPAUTH,
|
|
Dword,
|
|
"PLUG_CTX::ClearAuthUser",
|
|
"this=%#x ctx=%#x server=%.16s",
|
|
this,
|
|
*ppvContext,
|
|
szServer
|
|
));
|
|
|
|
if (GetState() == AUTHCTX::STATE_LOADED)
|
|
{
|
|
AuthLock();
|
|
|
|
__try
|
|
{
|
|
DEBUG_ENTER ((
|
|
DBG_HTTPAUTH,
|
|
None,
|
|
"UnloadAuthenticateUser",
|
|
"ctx=%#x server=%s scheme=%s",
|
|
*ppvContext,
|
|
szServer,
|
|
GetScheme()
|
|
));
|
|
|
|
UnloadAuthenticateUser(ppvContext, szServer, GetScheme());
|
|
|
|
DEBUG_LEAVE(0);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
DEBUG_PRINT(
|
|
HTTPAUTH,
|
|
ERROR,
|
|
("UnloadAuthenticateUser call down faulted\n")
|
|
);
|
|
}
|
|
ENDEXCEPT
|
|
|
|
AuthUnlock();
|
|
}
|
|
*ppvContext = 0;
|
|
DEBUG_LEAVE(ERROR_SUCCESS);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
wQueryHeadersAlloc
|
|
|
|
Routine Description:
|
|
|
|
Allocates a HTTP Header String, and queries the HTTP handle for it.
|
|
|
|
Arguments:
|
|
|
|
hRequestMapped - An open HTTP request handle
|
|
where headers can be quiered
|
|
dwQuery - The Query Type to pass to HttpQueryHeaders
|
|
lpdwQueryIndex - The Index of the header to pass to HttpQueryHeaders,
|
|
make sure to inialize to 0.
|
|
lppszOutStr - On success, a pointer to Allocated string with header string,
|
|
lpdwSize - size of the string returned in lppszOutStr
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - One of Several Error codes defined in winerror.h or wininet.w
|
|
|
|
Comments:
|
|
|
|
On Error, lppszOutStr may still contain an allocated string that will need to be
|
|
freed.
|
|
-----------------------------------------------------------------------------*/
|
|
DWORD PLUG_CTX::wQueryHeadersAlloc
|
|
(
|
|
IN HINTERNET hRequestMapped,
|
|
IN DWORD dwQuery,
|
|
OUT LPDWORD lpdwQueryIndex,
|
|
OUT LPSTR *lppszOutStr,
|
|
OUT LPDWORD lpdwSize
|
|
)
|
|
{
|
|
DEBUG_ENTER ((
|
|
DBG_HTTPAUTH,
|
|
Dword,
|
|
"PLUG_CTX::wQueryHeadersAlloc",
|
|
"this=%#x request=%#x query=%d queryidx=%#x {%d} ppoutstr=%#x lpdwSize=%#x",
|
|
this,
|
|
hRequestMapped,
|
|
dwQuery,
|
|
lpdwQueryIndex,
|
|
*lpdwQueryIndex,
|
|
lppszOutStr,
|
|
lpdwSize
|
|
));
|
|
|
|
LPSTR lpszRawHeaderBuf = NULL;
|
|
DWORD dwcbRawHeaderBuf = 0;
|
|
DWORD error;
|
|
DWORD length;
|
|
HTTP_REQUEST_HANDLE_OBJECT * pHttpRequest;
|
|
|
|
INET_ASSERT(lppszOutStr);
|
|
INET_ASSERT(hRequestMapped);
|
|
INET_ASSERT(lpdwSize);
|
|
|
|
|
|
*lppszOutStr = NULL;
|
|
error = ERROR_SUCCESS;
|
|
pHttpRequest = (HTTP_REQUEST_HANDLE_OBJECT *) hRequestMapped;
|
|
|
|
// Attempt to determine whether our header is there.
|
|
length = 0;
|
|
if (pHttpRequest->QueryInfo(dwQuery, NULL, &length, lpdwQueryIndex)
|
|
!= ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
// no authentication happening, we're done
|
|
error = ERROR_HTTP_HEADER_NOT_FOUND;
|
|
goto quit;
|
|
}
|
|
|
|
// Allocate a Fixed Size Buffer
|
|
lpszRawHeaderBuf = (LPSTR) ALLOCATE_MEMORY(LPTR, length);
|
|
dwcbRawHeaderBuf = length;
|
|
|
|
if ( lpszRawHeaderBuf == NULL )
|
|
{
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
error = pHttpRequest->QueryInfo
|
|
(dwQuery, lpszRawHeaderBuf, &dwcbRawHeaderBuf, lpdwQueryIndex);
|
|
|
|
INET_ASSERT(error != ERROR_INSUFFICIENT_BUFFER );
|
|
INET_ASSERT(error != ERROR_HTTP_HEADER_NOT_FOUND );
|
|
|
|
quit:
|
|
|
|
if ( error != ERROR_SUCCESS )
|
|
{
|
|
dwcbRawHeaderBuf = 0;
|
|
|
|
if ( lpszRawHeaderBuf )
|
|
*lpszRawHeaderBuf = '\0';
|
|
}
|
|
|
|
*lppszOutStr = lpszRawHeaderBuf;
|
|
*lpdwSize = dwcbRawHeaderBuf;
|
|
|
|
DEBUG_LEAVE(error);
|
|
return error;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
CrackAuthenticationHeader
|
|
|
|
Routine Description:
|
|
|
|
Attempts to decode a HTTP 1.1 Authentication header into its
|
|
components.
|
|
|
|
Arguments:
|
|
|
|
hRequestMapped - Mapped Request handle
|
|
fIsProxy - Whether proxy or server auth
|
|
lpdwAuthenticationIndex - Index of current HTTP header. ( initally called with 0 )
|
|
lppszAuthHeader - allocated pointer which should be freed by client
|
|
lppszAuthScheme - Pointer to Authentication scheme string.
|
|
lppszRealm - Pointer to Realm string,
|
|
lpExtra - Pointer to any Extra String data in the header that is not
|
|
part of the Realm
|
|
lpdwExtra - Pointer to Size of Extra data.
|
|
lppszAuthScheme
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_NOT_ENOUGH_MEMORY,
|
|
ERROR_HTTP_HEADER_NOT_FOUND
|
|
|
|
Comments:
|
|
-----------------------------------------------------------------------------*/
|
|
DWORD PLUG_CTX::CrackAuthenticationHeader
|
|
(
|
|
IN HINTERNET hRequestMapped,
|
|
IN BOOL fIsProxy,
|
|
IN DWORD dwAuthenticationIndex,
|
|
IN OUT LPSTR *lppszAuthHeader,
|
|
IN OUT LPSTR *lppszExtra,
|
|
IN OUT DWORD *lpdwExtra,
|
|
OUT LPSTR *lppszAuthScheme
|
|
)
|
|
{
|
|
DEBUG_ENTER ((
|
|
DBG_HTTPAUTH,
|
|
Dword,
|
|
"PLUG_CTX::CrackAuthenticationHeader",
|
|
"this=%#x request=%#x isproxy=%B authidx=%d ppszAuthHeader=%#x ppszExtra=%#x pdwExtra=%#x ppszAuthScheme=%#x",
|
|
this,
|
|
hRequestMapped,
|
|
fIsProxy,
|
|
dwAuthenticationIndex,
|
|
lppszAuthHeader,
|
|
lppszExtra,
|
|
lpdwExtra,
|
|
lppszAuthScheme
|
|
));
|
|
|
|
DWORD error = ERROR_SUCCESS;
|
|
|
|
LPSTR lpszAuthHeader = NULL;
|
|
DWORD cbAuthHeader = 0;
|
|
LPSTR lpszExtra = NULL;
|
|
LPSTR lpszAuthScheme = NULL;
|
|
|
|
LPDWORD lpdwAuthenticationIndex = &dwAuthenticationIndex;
|
|
INET_ASSERT(lpdwExtra);
|
|
INET_ASSERT(lppszExtra);
|
|
INET_ASSERT(lpdwAuthenticationIndex);
|
|
|
|
DWORD dwQuery = fIsProxy?
|
|
HTTP_QUERY_PROXY_AUTHENTICATE : HTTP_QUERY_WWW_AUTHENTICATE;
|
|
|
|
error = wQueryHeadersAlloc (hRequestMapped, dwQuery,
|
|
lpdwAuthenticationIndex, &lpszAuthHeader, &cbAuthHeader);
|
|
|
|
if ( error != ERROR_SUCCESS )
|
|
{
|
|
INET_ASSERT(*lpdwAuthenticationIndex
|
|
|| error == ERROR_HTTP_HEADER_NOT_FOUND );
|
|
goto quit;
|
|
}
|
|
|
|
|
|
//
|
|
// Parse Header for Scheme type
|
|
//
|
|
lpszAuthScheme = lpszAuthHeader;
|
|
|
|
while ( *lpszAuthScheme == ' ' ) // strip spaces
|
|
lpszAuthScheme++;
|
|
|
|
lpszExtra = strchr(lpszAuthScheme, ' ');
|
|
|
|
if (lpszExtra)
|
|
*lpszExtra++ = '\0';
|
|
|
|
if (lstrcmpi(GetScheme(), lpszAuthScheme))
|
|
{
|
|
DEBUG_PRINT(
|
|
HTTPAUTH,
|
|
ERROR,
|
|
("Authentication: HTTP Scheme has changed!: Scheme=%q\n", lpszAuthScheme)
|
|
);
|
|
|
|
goto quit;
|
|
|
|
}
|
|
|
|
|
|
DEBUG_PRINT(
|
|
HTTPAUTH,
|
|
INFO,
|
|
("Authentication: found in headers: Scheme=%q, Extra=%q\n",
|
|
lpszAuthScheme, lpszExtra)
|
|
);
|
|
|
|
quit:
|
|
*lppszExtra = lpszExtra;
|
|
*lpdwExtra = lpszExtra ? lstrlen(lpszExtra) : 0;
|
|
*lppszAuthHeader = lpszAuthHeader;
|
|
*lppszAuthScheme = lpszAuthScheme;
|
|
|
|
DEBUG_LEAVE(error);
|
|
return error;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
ResolveProtocol
|
|
---------------------------------------------------------------------------*/
|
|
VOID PLUG_CTX::ResolveProtocol()
|
|
{
|
|
DEBUG_ENTER ((
|
|
DBG_HTTPAUTH,
|
|
None,
|
|
"PLUG_CTX::ResolveProtocol",
|
|
"this=%#x",
|
|
this
|
|
));
|
|
|
|
SECURITY_STATUS ssResult;
|
|
PWINCONTEXT pWinContext;
|
|
SecPkgContext_NegotiationInfo SecPkgCtxtInfo;
|
|
|
|
INET_ASSERT(GetSchemeType() == SCHEME_NEGOTIATE);
|
|
|
|
SecPkgCtxtInfo.PackageInfo = NULL;
|
|
|
|
// Call QueryContextAttributes on the context handle.
|
|
pWinContext = (PWINCONTEXT) (_pvContext);
|
|
ssResult = (*(g_pSspData->pFuncTbl->QueryContextAttributes))
|
|
(pWinContext->pSspContextHandle, SECPKG_ATTR_NEGOTIATION_INFO, &SecPkgCtxtInfo);
|
|
|
|
if (ssResult == SEC_E_OK
|
|
&& (SecPkgCtxtInfo.NegotiationState == SECPKG_NEGOTIATION_COMPLETE
|
|
|| (SecPkgCtxtInfo.NegotiationState == SECPKG_NEGOTIATION_OPTIMISTIC)))
|
|
{
|
|
// Resolve actual auth protocol from package name.
|
|
// update both the auth context and pwc entry.
|
|
if (!lstrcmpi(SecPkgCtxtInfo.PackageInfo->Name, "NTLM"))
|
|
{
|
|
_eSubScheme = SCHEME_NTLM;
|
|
_dwSubFlags = PLUGIN_AUTH_FLAGS_NO_REALM;
|
|
}
|
|
else if (!lstrcmpi(SecPkgCtxtInfo.PackageInfo->Name, "Kerberos"))
|
|
{
|
|
_eSubScheme = SCHEME_KERBEROS;
|
|
_dwSubFlags = PLUGIN_AUTH_FLAGS_KEEP_ALIVE_NOT_REQUIRED | PLUGIN_AUTH_FLAGS_NO_REALM;
|
|
}
|
|
|
|
DEBUG_PRINT(
|
|
HTTPAUTH,
|
|
INFO,
|
|
("Negotiate package is using %s\n", SecPkgCtxtInfo.PackageInfo->Name)
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
if( SecPkgCtxtInfo.PackageInfo )
|
|
{
|
|
(*(g_pSspData->pFuncTbl->FreeContextBuffer))(SecPkgCtxtInfo.PackageInfo);
|
|
}
|
|
|
|
DEBUG_LEAVE(0);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Constructor
|
|
---------------------------------------------------------------------------*/
|
|
PLUG_CTX::PLUG_CTX(HTTP_REQUEST_HANDLE_OBJECT *pRequest, BOOL fIsProxy,
|
|
SPMData *pSPM, PWC* pPWC)
|
|
: AUTHCTX(pSPM, pPWC)
|
|
{
|
|
DEBUG_ENTER ((
|
|
DBG_HTTPAUTH,
|
|
Pointer,
|
|
"PLUG_CTX::PLUG_CTX",
|
|
"this=%#x request=%#x isproxy=%B pSPM=%#x pPWC=%#x",
|
|
this,
|
|
pRequest,
|
|
fIsProxy,
|
|
pSPM,
|
|
pPWC
|
|
));
|
|
|
|
_fIsProxy = fIsProxy;
|
|
_pRequest = pRequest;
|
|
_szAlloc = NULL;
|
|
_szData = NULL;
|
|
_cbData = 0;
|
|
_pRequest->SetAuthState(AUTHSTATE_NONE);
|
|
_fNTLMProxyAuth = _fIsProxy && (GetSchemeType() == SCHEME_NTLM );
|
|
|
|
_SecStatus = 0;
|
|
_dwResolutionId = 0;
|
|
|
|
DEBUG_LEAVE(this);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Destructor
|
|
---------------------------------------------------------------------------*/
|
|
PLUG_CTX::~PLUG_CTX()
|
|
{
|
|
DEBUG_ENTER ((
|
|
DBG_HTTPAUTH,
|
|
None,
|
|
"PLUG_CTX::~PLUG_CTX",
|
|
"this=%#x",
|
|
this
|
|
));
|
|
|
|
if (GetState() == AUTHCTX::STATE_LOADED)
|
|
{
|
|
if (_pPWC)
|
|
{
|
|
ClearAuthUser(&_pvContext, _pPWC->lpszHost);
|
|
}
|
|
}
|
|
if (_pRequest)
|
|
{
|
|
_pRequest->SetAuthState(AUTHSTATE_NONE);
|
|
}
|
|
|
|
DEBUG_LEAVE(0);
|
|
}
|
|
|
|
PCSTR PLUG_CTX::GetUrl(void) const
|
|
{
|
|
return _pRequest->GetURL();
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
PreAuthUser
|
|
---------------------------------------------------------------------------*/
|
|
DWORD PLUG_CTX::PreAuthUser(OUT LPSTR pBuf, IN OUT LPDWORD pcbBuf)
|
|
{
|
|
DEBUG_ENTER ((
|
|
DBG_HTTPAUTH,
|
|
Dword,
|
|
"PLUG_CTX::PreAuthUser",
|
|
"this=%#x",
|
|
this
|
|
));
|
|
|
|
INET_ASSERT(_pSPMData == _pPWC->pSPM);
|
|
|
|
AuthLock();
|
|
|
|
DWORD dwError;
|
|
SECURITY_STATUS ssResult;
|
|
PSTR lpszPass = _pPWC->GetPass();
|
|
|
|
// Make sure the auth provider is loaded.
|
|
if (GetState() != AUTHCTX::STATE_LOADED)
|
|
{
|
|
if (GetState() != AUTHCTX::STATE_ERROR )
|
|
Load();
|
|
if (GetState() != AUTHCTX::STATE_LOADED)
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
__try
|
|
{
|
|
ssResult = SEC_E_INTERNAL_ERROR;
|
|
|
|
DEBUG_ENTER ((
|
|
DBG_HTTPAUTH,
|
|
Dword,
|
|
"PreAuthenticateUser",
|
|
"ctx=%#x host=%s scheme=%s {buf=%x (%.16s...) cbbuf=%d} user=%s pass=%s",
|
|
_pvContext,
|
|
_pPWC->lpszHost,
|
|
InternetMapAuthScheme(GetSchemeType()),
|
|
pBuf,
|
|
pBuf,
|
|
*pcbBuf,
|
|
_pPWC->lpszUser,
|
|
lpszPass
|
|
));
|
|
|
|
LPSTR lpszFQDN = (LPSTR)GetFQDN((LPCSTR)_pPWC->lpszHost);
|
|
LPSTR lpszHostName = lpszFQDN ? lpszFQDN : _pPWC->lpszHost;
|
|
|
|
dwError = PreAuthenticateUser(&_pvContext,
|
|
lpszHostName,
|
|
GetScheme(),
|
|
0, // dwFlags
|
|
pBuf,
|
|
pcbBuf,
|
|
_pPWC->lpszUser,
|
|
lpszPass,
|
|
GetUrl(),
|
|
&ssResult);
|
|
|
|
DEBUG_PRINT(HTTPAUTH, INFO, ("ssres: %#x [%s]\n", ssResult, InternetMapError(ssResult)));
|
|
DEBUG_LEAVE(dwError);
|
|
|
|
// Transit to the correct auth state.
|
|
if (ssResult == SEC_E_OK || ssResult == SEC_I_CONTINUE_NEEDED)
|
|
{
|
|
if (GetSchemeType() == SCHEME_NEGOTIATE)
|
|
ResolveProtocol();
|
|
|
|
// Kerberos + SEC_E_OK or SEC_I_CONTINUE_NEEDED transits to challenge.
|
|
// Negotiate does not transit to challenge.
|
|
// Any other protocol + SEC_E_OK only transits to challenge.
|
|
|
|
if ((GetSchemeType() == SCHEME_KERBEROS
|
|
&& (ssResult == SEC_E_OK || ssResult == SEC_I_CONTINUE_NEEDED))
|
|
|| (GetSchemeType() != SCHEME_NEGOTIATE && ssResult == SEC_E_OK))
|
|
{
|
|
_pRequest->SetAuthState(AUTHSTATE_CHALLENGE);
|
|
}
|
|
}
|
|
}
|
|
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
DEBUG_PRINT (HTTPAUTH, ERROR, ("PreAuthenticateUser call down faulted\n"));
|
|
_pSPMData->eState = STATE_ERROR;
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
}
|
|
|
|
ENDEXCEPT
|
|
|
|
DEBUG_PRINT(
|
|
HTTPAUTH,
|
|
INFO,
|
|
(
|
|
"request %#x [%s] now in %s using %s\n",
|
|
_pRequest,
|
|
_pPWC->lpszHost,
|
|
InternetMapAuthState(_pRequest->GetAuthState()),
|
|
InternetMapAuthScheme(GetSchemeType())
|
|
)
|
|
);
|
|
|
|
exit:
|
|
if (lpszPass)
|
|
{
|
|
memset(lpszPass, 0, strlen(lpszPass));
|
|
FREE_MEMORY(lpszPass);
|
|
}
|
|
|
|
AuthUnlock();
|
|
DEBUG_LEAVE(dwError);
|
|
return dwError;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
UpdateFromHeaders
|
|
---------------------------------------------------------------------------*/
|
|
DWORD PLUG_CTX::UpdateFromHeaders(HTTP_REQUEST_HANDLE_OBJECT *pRequest, BOOL fIsProxy)
|
|
{
|
|
DEBUG_ENTER ((
|
|
DBG_HTTPAUTH,
|
|
Dword,
|
|
"PLUG_CTX::UpdateFromHeaders",
|
|
"this=%#x request=%#x isproxy=%B",
|
|
this,
|
|
pRequest,
|
|
fIsProxy
|
|
));
|
|
|
|
DWORD dwError, cbExtra, dwAuthIdx;
|
|
LPSTR szAuthHeader, szExtra, szScheme;
|
|
|
|
AuthLock();
|
|
|
|
// Get the auth header index corresponding to the scheme of this ctx.
|
|
if ((dwError = FindHdrIdxFromScheme(&dwAuthIdx)) != ERROR_SUCCESS)
|
|
goto quit;
|
|
|
|
// Get the scheme and any extra data.
|
|
if ((dwError = CrackAuthenticationHeader(pRequest, fIsProxy, dwAuthIdx,
|
|
&szAuthHeader, &szExtra, &cbExtra, &szScheme)) != ERROR_SUCCESS)
|
|
goto quit;
|
|
|
|
if (!cbExtra)
|
|
_pRequest->SetAuthState(AUTHSTATE_NEGOTIATE);
|
|
|
|
// Check if auth scheme requires keep-alive.
|
|
if (!(GetFlags() & PLUGIN_AUTH_FLAGS_KEEP_ALIVE_NOT_REQUIRED))
|
|
{
|
|
// if in negotiate phase check if we are going via proxy.
|
|
if (pRequest->GetAuthState() == AUTHSTATE_NEGOTIATE)
|
|
{
|
|
// BUGBUG: if via proxy, we are not going to get keep-alive
|
|
// connection to the server. It would be nice if we knew
|
|
// a priori the whether proxy would allow us to tunnel to
|
|
// http port on the server. Otherwise if we try and fail,
|
|
// we look bad vs. other browsers who are ignorant of ntlm
|
|
// and fall back to basic.
|
|
CHAR szBuffer[64];
|
|
DWORD dwBufferLength = sizeof(szBuffer);
|
|
DWORD dwIndex = 0;
|
|
BOOL fSessionBasedAuth = FALSE;
|
|
if (pRequest->QueryResponseHeader(HTTP_QUERY_PROXY_SUPPORT,
|
|
szBuffer, &dwBufferLength,
|
|
0, &dwIndex) == ERROR_SUCCESS)
|
|
{
|
|
if (!_stricmp(szBuffer, "Session-Based-Authentication"))
|
|
{
|
|
fSessionBasedAuth = TRUE;
|
|
}
|
|
}
|
|
if (!fIsProxy && pRequest->IsRequestUsingProxy()
|
|
&& !pRequest->IsTalkingToSecureServerViaProxy() && !fSessionBasedAuth)
|
|
{
|
|
// Ignore NTLM via proxy since we won't get k-a to server.
|
|
dwError = ERROR_HTTP_HEADER_NOT_FOUND;
|
|
goto quit;
|
|
}
|
|
}
|
|
|
|
// Else if in challenge phase, we require a persistent connection.
|
|
else
|
|
{
|
|
// If we don't have a keep-alive connection ...
|
|
if (!(pRequest->IsPersistentConnection (fIsProxy)) && !(pRequest->IsRequestUsingProxy()))
|
|
{
|
|
dwError = ERROR_HTTP_HEADER_NOT_FOUND;
|
|
goto quit;
|
|
}
|
|
}
|
|
|
|
} // end if keep-alive required
|
|
|
|
quit:
|
|
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
// If no password cache is set in the auth context,
|
|
// find or create one and set it in the handle.
|
|
if (!_pPWC)
|
|
{
|
|
_pPWC = FindOrCreatePWC(pRequest, fIsProxy, _pSPMData, NULL);
|
|
|
|
if (!_pPWC)
|
|
{
|
|
INET_ASSERT(FALSE);
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
INET_ASSERT(_pPWC->pSPM == _pSPMData);
|
|
_pPWC->nLockCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
// Point to allocated data.
|
|
_szAlloc = szAuthHeader;
|
|
_szData = szExtra;
|
|
_cbData = cbExtra;
|
|
}
|
|
else
|
|
{
|
|
// Free allocated data.
|
|
if (_szAlloc)
|
|
delete _szAlloc;
|
|
_szAlloc = NULL;
|
|
_szData = NULL;
|
|
_cbData = 0;
|
|
}
|
|
|
|
DEBUG_PRINT(
|
|
HTTPAUTH,
|
|
INFO,
|
|
(
|
|
"request %#x [%s] now in %s using %s\n",
|
|
_pRequest,
|
|
_pPWC->lpszHost,
|
|
InternetMapAuthState(_pRequest->GetAuthState()),
|
|
InternetMapAuthScheme(GetSchemeType())
|
|
)
|
|
);
|
|
|
|
// Return of non-success will cancel auth session.
|
|
AuthUnlock();
|
|
DEBUG_LEAVE(dwError);
|
|
return dwError;
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
PostAuthUser
|
|
---------------------------------------------------------------------------*/
|
|
DWORD PLUG_CTX::PostAuthUser()
|
|
{
|
|
DEBUG_ENTER ((
|
|
DBG_HTTPAUTH,
|
|
Dword,
|
|
"PLUG_CTX::PostAuthUser",
|
|
"this=%#x",
|
|
this
|
|
));
|
|
|
|
INET_ASSERT(_pSPMData == _pPWC->pSPM);
|
|
|
|
AuthLock();
|
|
|
|
DWORD dwError;
|
|
PSTR lpszPass = _pPWC->GetPass();
|
|
|
|
// Make sure the auth provider is loaded.
|
|
if (GetState() != AUTHCTX::STATE_LOADED)
|
|
{
|
|
if (GetState() != AUTHCTX::STATE_ERROR )
|
|
Load();
|
|
if (GetState() != AUTHCTX::STATE_LOADED)
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
BOOL fCanUseLogon = _fIsProxy || _pRequest->GetCredPolicy()
|
|
== URLPOLICY_CREDENTIALS_SILENT_LOGON_OK;
|
|
|
|
SECURITY_STATUS ssResult;
|
|
__try
|
|
{
|
|
ssResult = SEC_E_INTERNAL_ERROR;
|
|
|
|
|
|
DEBUG_ENTER ((
|
|
DBG_HTTPAUTH,
|
|
Dword,
|
|
"AuthenticateUser",
|
|
"ctx=%#x host=%s scheme=%s {data=%#x (%.16s...) cbdata=%d} user=%s pass=%s",
|
|
_pvContext,
|
|
_pPWC->lpszHost,
|
|
InternetMapAuthScheme(GetSchemeType()),
|
|
_szData,
|
|
_szData,
|
|
_cbData,
|
|
_pPWC->lpszUser,
|
|
lpszPass
|
|
));
|
|
|
|
LPSTR lpszFQDN = (LPSTR)GetFQDN((LPCSTR)_pPWC->lpszHost);
|
|
LPSTR lpszHostName = lpszFQDN ? lpszFQDN : _pPWC->lpszHost;
|
|
|
|
dwError = AuthenticateUser(&_pvContext,
|
|
lpszHostName,
|
|
GetScheme(),
|
|
fCanUseLogon,
|
|
_szData,
|
|
_cbData,
|
|
_pPWC->lpszUser,
|
|
lpszPass,
|
|
GetUrl(),
|
|
&ssResult);
|
|
|
|
_SecStatus = ssResult;
|
|
|
|
DEBUG_PRINT(HTTPAUTH, INFO, ("ssres: %#x [%s]\n", ssResult, InternetMapError(ssResult)));
|
|
DEBUG_LEAVE(dwError);
|
|
|
|
// Kerberos package can get into a bad state.
|
|
if (GetSchemeType() == SCHEME_KERBEROS && ssResult == SEC_E_WRONG_PRINCIPAL)
|
|
dwError = ERROR_INTERNET_INCORRECT_PASSWORD;
|
|
|
|
// Transit to the correct auth state.
|
|
if (ssResult == SEC_E_OK || ssResult == SEC_I_CONTINUE_NEEDED)
|
|
{
|
|
if (GetSchemeType() == SCHEME_NEGOTIATE)
|
|
ResolveProtocol();
|
|
|
|
// Kerberos + SEC_E_OK or SEC_I_CONTINUE_NEEDED transits to challenge.
|
|
// Negotiate does not transit to challenge.
|
|
// Any other protocol + SEC_E_OK only transits to challenge.
|
|
|
|
if ((GetSchemeType() == SCHEME_KERBEROS
|
|
&& (ssResult == SEC_E_OK || ssResult == SEC_I_CONTINUE_NEEDED))
|
|
|| (GetSchemeType() != SCHEME_NEGOTIATE && ssResult == SEC_E_OK))
|
|
{
|
|
_pRequest->SetAuthState(AUTHSTATE_CHALLENGE);
|
|
}
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
DEBUG_PRINT (HTTPAUTH, ERROR, ("AuthenticateUser faulted!\n"));
|
|
dwError = ERROR_BAD_FORMAT;
|
|
_pSPMData->eState = STATE_ERROR;
|
|
}
|
|
ENDEXCEPT
|
|
|
|
if (_szAlloc)
|
|
{
|
|
delete _szAlloc;
|
|
_szAlloc = NULL;
|
|
_szData = NULL;
|
|
}
|
|
|
|
_cbData = 0;
|
|
|
|
DEBUG_PRINT(
|
|
HTTPAUTH,
|
|
INFO,
|
|
(
|
|
"request %#x [%s] now in %s using %s\n",
|
|
_pRequest,
|
|
_pPWC->lpszHost,
|
|
InternetMapAuthState(_pRequest->GetAuthState()),
|
|
InternetMapAuthScheme(GetSchemeType())
|
|
)
|
|
);
|
|
|
|
exit:
|
|
if (lpszPass)
|
|
{
|
|
memset(lpszPass, 0, strlen(lpszPass));
|
|
FREE_MEMORY(lpszPass);
|
|
}
|
|
|
|
AuthUnlock();
|
|
DEBUG_LEAVE(dwError);
|
|
return dwError;
|
|
}
|
|
|
|
LPCSTR PLUG_CTX::GetFQDN(LPCSTR lpszHostName)
|
|
{
|
|
if (lstrcmpi(GetScheme(), "Negotiate")) // only need to get FQDN for Kerberos
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (_pszFQDN)
|
|
{
|
|
return _pszFQDN;
|
|
}
|
|
|
|
LPRESOLVER_CACHE_ENTRY lpResolverCacheEntry;
|
|
|
|
|
|
DWORD TTL;
|
|
LPADDRINFO lpAddrInfo;
|
|
if (lpResolverCacheEntry = QueryResolverCache((LPSTR)lpszHostName, NULL, &lpAddrInfo, &TTL))
|
|
{
|
|
_pszFQDN = (lpAddrInfo->ai_canonname ? NewString(lpAddrInfo->ai_canonname) : NULL);
|
|
ReleaseResolverCacheEntry(lpResolverCacheEntry);
|
|
return _pszFQDN;
|
|
}
|
|
|
|
/*
|
|
CAddressList TempAddressList;
|
|
DWORD dwResolutionId;
|
|
TempAddressList.ResolveHost((LPSTR)lpszHostName, &_dwResolutionId, SF_FORCE);
|
|
|
|
if (lpResolverCacheEntry = QueryResolverCache((LPSTR)lpszHostName, NULL, &lpAddrInfo, &TTL))
|
|
{
|
|
_pszFQDN = (lpAddrInfo->ai_canonname ? NewString(lpAddrInfo->ai_canonname) : NULL);
|
|
ReleaseResolverCacheEntry(lpResolverCacheEntry);
|
|
return _pszFQDN;
|
|
}
|
|
*/
|
|
|
|
return NULL;
|
|
}
|