|
|
/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
splugin.cxx
Abstract:
This file contains the implementation for Plug In Authentication
The following functions are exported by this module:
AuthOnRequest AuthOnResponse AuthCtxClose AuthInDialog AuthNotify AuthUnload
Author:
Arthur Bierer (arthurbi) 25-Dec-1995
Revision History: Rajeev Dujari (rajeevd) 01-Oct-1996 overhauled
Adriaan Canter (adriaanc) 01-03-1998 : AUTHCTX now a virtual base class, from which derived classes inherit to implement the different authentication protocols:
BASIC_CTX (Basic auth), PLUG_CTX (NTLM, Negotiate) DIGEST_CTX (Digest auth, new)
--*/
#include <wininetp.h>
#include <splugin.hxx>
#include "auth.h"
#include "sspspm.h"
//
// constants
//
#define WILDCARD 0x05 // don't use '*' since it can appear in an URL
#define AssertHaveLock() INET_ASSERT(g_dwOwnerId == GetCurrentThreadId())
#define MAX_AUTH_HDR_SIZE (MAX_BLOB_SIZE + HTTP_PROXY_AUTHORIZATION_LEN + 2)
//
// globals
//
// Global authentication providers list and state.
AUTHCTX::SPMState AUTHCTX::g_eState; AUTHCTX::SPMData *AUTHCTX::g_pSPMList = NULL;
// Global auth crit sect.
CCritSec g_crstAuth;
#ifdef DBG
DWORD g_dwOwnerId = 0; LONG g_nLockCount = 0; #endif
//
// private prototypes
//
//-----------------------------------------------------------------------------
//
// AUTH_CREDS class definition.
//
//
PRIVATE AUTH_CREDS *Creds_Create ( LPSTR lpszHost, LPSTR lpszRealm, AUTHCTX::SPMData* pSPM );
void Creds_Free (AUTH_CREDS *Creds);
//-----------------------------------------------------------------------------
//
// Utilities
//
//
PRIVATE VOID SspiFlush (LPSTR pszDll); PRIVATE BOOL TemplateMatch(LPSTR lpszTemplate, LPSTR lpszFilename); PRIVATE LPSTR MakeTemplate (LPSTR docname); PRIVATE LPSTR GetProxyName (HTTP_REQUEST_HANDLE_OBJECT *pRequest);
PRIVATE BOOL ReadRegKey( BYTE * pbRegValue, DWORD * pdwNumBytes, LPSTR pszRegKey, LPSTR pszRegValueName, DWORD dwRegTypeExpected);
//-----------------------------------------------------------------------------
//
//
// AUTH_CREDS functions
//
// Creds_CREATE
// Creds_FREE
// SetUser
// SetPass
// FlushCredsList
//
PRIVATE AUTH_CREDS *Creds_Create // AUTH_CREDS constructor
( LPSTR lpszHost, // Host Name to place in structure.
LPSTR lpszRealm, // Realm string to add.
AUTHCTX::SPMData * pSPM ) { AUTH_CREDS* Creds = (AUTH_CREDS *) ALLOCATE_ZERO_MEMORY(sizeof(*Creds)); if (!Creds) return NULL;
INET_ASSERT (!Creds->lpszHost); Creds->lpszHost = lpszHost ? NewString(lpszHost) : NULL; INET_ASSERT (!Creds->lpszRealm); Creds->lpszRealm = lpszRealm ? NewString(lpszRealm) : NULL; Creds->pSPM = pSPM;
Creds->xszPass.SetSecuritySensitive();
if ( (!Creds->lpszHost && lpszHost) || (!Creds->lpszRealm && lpszRealm) ) { Creds_Free(Creds); return NULL; }
return Creds; }
PRIVATE VOID Creds_Free(AUTH_CREDS *Creds) // AUTH_CREDS destructor
{ if ( Creds ) { if (Creds->lpszHost) FREE_MEMORY(Creds->lpszHost); if ( Creds->lpszUser ) { SecureZeroMemory(Creds->lpszUser, strlen(Creds->lpszUser)); FREE_MEMORY(Creds->lpszUser); } Creds->xszPass.Free(); if ( Creds->lpszRealm ) FREE_MEMORY(Creds->lpszRealm); FREE_MEMORY(Creds); } }
PUBLIC DWORD AUTH_CREDS::SetUser (LPSTR lpszInput) { //AssertHaveLock();
if (!lpszInput) { if (lpszUser) { SecureZeroMemory(lpszUser, strlen(lpszUser)); FREE_MEMORY (lpszUser); } lpszUser = NULL; return ERROR_SUCCESS; } if (lpszUser && !lstrcmp (lpszUser, lpszInput)) return ERROR_SUCCESS; // didn't change
LPSTR lpszTemp = NewString(lpszInput); if (!lpszTemp) return ERROR_NOT_ENOUGH_MEMORY; if (lpszUser) { SecureZeroMemory(lpszUser, strlen(lpszUser)); FREE_MEMORY (lpszUser); } lpszUser = lpszTemp; return ERROR_SUCCESS; }
PUBLIC DWORD AUTH_CREDS::SetPass (LPSTR lpszInput) { //AssertHaveLock();
if (!lpszInput) { xszPass.Free(); return ERROR_SUCCESS; }
return xszPass.SetData(lpszInput) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY; }
/*++
Delete some entries from a singly linked list. --*/ /*
PRIVATE void FlushCredsList (AUTH_CREDS **ppList) { AssertHaveLock();
AUTH_CREDS *Creds = *ppList; while (Creds) { AUTH_CREDS *CredsNext = Creds->pNext;
if (!Creds->nLockCount) Creds_Free (Creds); else { *ppList = Creds; ppList = &(Creds->pNext); }
Creds = CredsNext; } *ppList = NULL; } */
//-----------------------------------------------------------------------------
//
//
// Auth* functions
//
// AuthOpen
// AuthClose
// AuthLock
// AuthUnlock
// AuthInDialog
// AuthNotify
//
//
BOOL AuthOpen (void) { INET_ASSERT (!g_dwOwnerId && !g_nLockCount); return g_crstAuth.Init(); }
void AuthClose (void) { g_crstAuth.FreeLock(); INET_ASSERT (!g_dwOwnerId && !g_nLockCount); }
BOOL AuthLock (void) { INET_ASSERT(g_crstAuth.IsInitialized() == TRUE); if (!g_crstAuth.Lock()) { INET_ASSERT(FALSE); return FALSE; } DEBUG_ONLY (if (!g_nLockCount++) g_dwOwnerId = GetCurrentThreadId();) return TRUE; }
void AuthUnlock (void) { INET_ASSERT(g_crstAuth.IsInitialized() == TRUE); INET_ASSERT (g_nLockCount > 0); DEBUG_ONLY (if (!--g_nLockCount) g_dwOwnerId = 0;) g_crstAuth.Unlock(); }
PUBLIC void AuthUnload (void) /*++
Routine Description: Frees all Cached URLs, and unloads any loaded DLL authentication modeles.
--*/ { if (g_crstAuth.IsInitialized()) { if (AuthLock()) { AUTHCTX::UnloadAll(); AuthUnlock(); } }
DIGEST_CTX::GlobalRelease(); }
//-----------------------------------------------------------------------------
//
//
// Utility Functions:
//
// SspiFlush
// TemplateMatch
// MakeTemplate
// GetProxyName
// ReadRegKey
// TrimQuotes
// TrimWhiteSpace
// GetDelimitedToken
// GetKeyValuePair
//
//
typedef BOOL (WINAPI * SSPI_FLUSH) (VOID) ;
void SspiFlush (LPSTR pszDll) { __try { HINSTANCE hinst = GetModuleHandle (pszDll);
if (hinst) { SSPI_FLUSH pfnFlush = (SSPI_FLUSH) GetProcAddress (hinst, "CleanupCredentialCache");
if (pfnFlush) { (*pfnFlush) (); } } } __except (EXCEPTION_EXECUTE_HANDLER) { INET_ASSERT (FALSE); } ENDEXCEPT }
PRIVATE BOOL TemplateMatch(LPSTR lpszTemplate, LPSTR lpszFilename)
/*++
Routine Description:
Attempts to match a template URL string with a URL ( FileName )
Arguments:
lpszTemplate - Template to match against. lpszFilename - URL to match with the template
Return Value:
BOOL Success - TRUE - match Failure - FALSE - no match
Comments:
Note: This Legacy code from the SpyGlass IE 1.0 browser
--*/
{ /* code for this routine cloned from HTAA_templateMatch() */
CHAR *p = lpszTemplate; CHAR *q = lpszFilename; int m;
if (!lpszTemplate || !lpszFilename) return 0;
for (; *p && *q && *p == *q; p++, q++) /* Find first mismatch */ ; /* do nothing else */
if (!*p && !*q) return 1; /* Equally long equal strings */ else if (WILDCARD == *p) { /* Wildcard */ p++; /* Skip wildcard character */ m = strlen(q) - strlen(p); /* Amount to match to wildcard */ if (m < 0) return 0; /* No match, filename too short */ else { /* Skip the matched characters and compare */ if (lstrcmp(p, q + m)) return 0; /* Tail mismatch */ else return 1; /* Tail match */ } } /* if wildcard */ else return 0; /* Length or character mismatch */ }
PRIVATE LPSTR MakeTemplate (LPSTR docname)
/*++
Routine Description: Makes a Template String (from a URL) that can later be used to match a range of URLs.
Arguments: ppszTemplate - pointer to pointer of where Template can be stored docname - URL to create a template with.
Return Value: BOOL Success - TRUE - created Failure - FALSE - error
Comments: Note: This Legacy code from the SpyGlass IE 1.0 browser --*/
{ CHAR *pszTemplate = NULL; unsigned long k; k = 0;
if (docname) { CHAR *slash; CHAR *first_slash;
//
// Ignore everything after first reserved character.
//
BYTE chSave = 0; LPSTR lpszScan = docname; while (*lpszScan) { if (*lpszScan == '?' || *lpszScan == ';') { chSave = *lpszScan; *lpszScan = 0; break; } lpszScan++; }
slash = strrchr(docname, '/');
//
// If there is a "//" and no other slashes,
// then make sure not to chop the hostname off
// the URL. ex: http://www.netscape.com
// should be //www.netscape.com* not //*
//
if (slash) { first_slash = strchr(docname, '/' ); if ((first_slash+1) == slash) k = lstrlen(docname); else k = (unsigned long)(slash-docname)+1; }
// Restore any reserved character (or rewrite terminating null)
*lpszScan = chSave; }
pszTemplate = (CHAR *) ALLOCATE_FIXED_MEMORY(k+2); if (!pszTemplate) return 0;
memcpy(pszTemplate, docname, k); pszTemplate[k]= WILDCARD; pszTemplate[k+1]=0;
DEBUG_PRINT(HTTP, INFO, ("MakeTemplate: made template [%s] from [%s]\n", pszTemplate, docname ));
return pszTemplate; }
LPSTR GetProxyName (HTTP_REQUEST_HANDLE_OBJECT *pRequest) { // Get the proxy name.
LPSTR lpszProxy; DWORD cbProxy; INTERNET_PORT port;
pRequest->GetProxyName( &lpszProxy, &cbProxy, &port);
return lpszProxy; }
//++------------------------------------------------------------------------
//
// Function: ReadRegKey
//
// Synopsis: This function reads a registry key.
//
// Arguments:
//
// Returns: TRUE no error
// FALSE a fatal error happened
//
// History: AshishS Created 5/22/96
//------------------------------------------------------------------------
BOOL ReadRegKey( BYTE * pbRegValue, // The value of the reg key will be
// stored here
DWORD * pdwNumBytes, // Pointer to DWORD conataining
// the number of bytes in the above buffer - will be
// set to actual bytes stored.
LPSTR pszRegKey, // Reg Key to be opened
LPSTR pszRegValueName, // Reg Value to query
DWORD dwRegTypeExpected) // Expected type of Value
{ HKEY hRegKey; DWORD dwRegType; LONG lResult;
//read registry to find out name of the file
if ( (lResult = REGOPENKEYEX(HKEY_LOCAL_MACHINE, pszRegKey, // address of subkey name
0, // reserved
KEY_READ, // samDesired
&hRegKey // address of handle of open key
)) != ERROR_SUCCESS ) { goto cleanup; }
if ( (lResult =RegQueryValueEx( hRegKey, pszRegValueName, 0, // reserved
&dwRegType,// address of buffer
// for value type
pbRegValue, pdwNumBytes)) != ERROR_SUCCESS ) { REGCLOSEKEY(hRegKey); goto cleanup; }
REGCLOSEKEY(hRegKey);
if ( dwRegType != dwRegTypeExpected ) { goto cleanup; }
return TRUE;
cleanup:
return FALSE;
}
/*-----------------------------------------------------------------------------
Inplace trim of one leading and one trailing quote. -----------------------------------------------------------------------------*/ VOID TrimQuotes(LPSTR *psz, LPDWORD pcb) { if (*pcb && (**psz == '"')) { (*psz)++; (*pcb)--; } if (*pcb && (*(*psz + *pcb - 1) == '"')) (*pcb)--; }
/*-----------------------------------------------------------------------------
Inplace trim of leading and trailing whitespace. -----------------------------------------------------------------------------*/ VOID TrimWhiteSpace(LPSTR *psz, LPDWORD pcb) { DWORD cb = *pcb; CHAR* beg = *psz; CHAR* end = beg + cb - 1;
while ((cb != 0) && ((*beg == ' ') || (*beg == '\t'))) { beg++; cb--; }
while ((cb != 0) && ((*end == ' ') || (*end == '\t'))) { end--; cb--; }
*psz = beg; *pcb = cb; }
/*-----------------------------------------------------------------------------
Inplace strtok based on one delimiter. Ignores delimiter scoped by quotes. -----------------------------------------------------------------------------*/ BOOL GetDelimitedToken(LPSTR* pszBuf, LPDWORD pcbBuf, LPSTR* pszTok, LPDWORD pcbTok, CHAR cDelim) { CHAR *pEnd; BOOL fQuote = FALSE, fRet = FALSE;
*pcbTok = 0; *pszTok = *pszBuf; pEnd = *pszBuf + *pcbBuf - 1;
while (*pcbBuf) { if ( ((**pszBuf == cDelim) && !fQuote) || (*pszBuf == pEnd) || (**pszBuf =='\r') || (**pszBuf =='\n')) { fRet = TRUE; break; }
if (**pszBuf == '"') fQuote = !fQuote;
(*pszBuf)++; (*pcbBuf)--; }
if (fRet) { *pcbBuf = (DWORD) (pEnd - *pszBuf); if (**pszBuf == cDelim) { *pcbTok = (DWORD)(*pszBuf - *pszTok); (*pszBuf)++; } else *pcbTok = (DWORD) (*pszBuf - *pszTok) + 1; }
return fRet; }
/*-----------------------------------------------------------------------------
Inplace retrieval of key and value from a buffer of form key = <">value<"> -----------------------------------------------------------------------------*/ BOOL GetKeyValuePair(LPSTR szB, DWORD cbB, LPSTR* pszK, LPDWORD pcbK, LPSTR* pszV, LPDWORD pcbV) { if (GetDelimitedToken(&szB, &cbB, pszK, pcbK, '=')) { TrimWhiteSpace(pszK, pcbK);
if (cbB) { *pszV = szB; *pcbV = cbB; TrimWhiteSpace(pszV, pcbV); } else { *pszV = NULL; *pcbV = 0; } return TRUE; }
else { *pszK = *pszV = NULL; *pcbK = *pcbV = 0; } return FALSE; }
//-----------------------------------------------------------------------------
//
//
// Main authentication functions:
//
// AddAuthorizationHeader
// AuthOnRequest
// ProcessResponseHeader
// AuthOnRequest
//
//
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/ PRIVATE void AddAuthorizationHeader ( HTTP_REQUEST_HANDLE_OBJECT *pRequest, AUTHCTX* pAuthCtx ) { if (!pAuthCtx) return;
INET_ASSERT(pAuthCtx->_pSPMData);
// AssertHaveLock();
// Call the auth package.
// CHAR *szHeader;
// DWORD dwFastHeaderSize = MAX_BLOB_SIZE + HTTP_PROXY_AUTHORIZATION_LEN + 2;
// CHAR* pszFastHeader = NULL;
CHAR *szSlowHeader = NULL; // ULONG cbHeader;
LPSTR pBuf; DWORD cbBuf; DWORD dwPlugInError; // CHAR *pszHeader;
/*
pszFastHeader = (CHAR*)ALLOCATE_FIXED_MEMORY(dwFastHeaderSize); if (!pszFastHeader) { // Don't worry about reporting an error.
// Since this is a low mem condition, the only failure resulting
// from this is that the header won't be added, and this won't
// directly cause any more harm than unexpectedly failing to
// authenticate, which isn't bad, given the low mem state.
return; } */
INT PackageId; ULONG cbMaxToken;
//
// GetPkgMaxToken() will return 10000 if invalid pkg.
//
if( (pAuthCtx->_pSPMData) && (pAuthCtx->_pSPMData->szScheme) && ((PackageId = GetPkgId( pAuthCtx->_pSPMData->szScheme )) != -1) ) { cbMaxToken = GetPkgMaxToken( PackageId ); } else { cbMaxToken = MAX_AUTH_MSG_SIZE; }
//
// add space for base64 overhead (33%, but round up)
//
cbMaxToken += (cbMaxToken/2); // Prefix with the appropriate header.
BOOL fOkToRetry = TRUE;
cbBuf = HTTP_PROXY_AUTHORIZATION_LEN + 1 + cbMaxToken + 2 + 1;
retry_alloc:
szSlowHeader = (CHAR*)ALLOCATE_FIXED_MEMORY(cbBuf);
if( szSlowHeader == NULL ) { return; }
if (pAuthCtx->_fIsProxy) { memcpy (szSlowHeader, HTTP_PROXY_AUTHORIZATION_SZ, HTTP_PROXY_AUTHORIZATION_LEN); pBuf = szSlowHeader + HTTP_PROXY_AUTHORIZATION_LEN;
// Don't reuse this keep-alive socket after a password cache flush.
pRequest->SetAuthorized(); } else { memcpy (szSlowHeader, HTTP_AUTHORIZATION_SZ, HTTP_AUTHORIZATION_LEN); pBuf = szSlowHeader + HTTP_AUTHORIZATION_LEN;
// Don't reuse a keep-alive socket after a password cache flush.
pRequest->SetAuthorized(); }
*pBuf++ = ' '; INET_ASSERT (HTTP_PROXY_AUTHORIZATION_LEN >= HTTP_AUTHORIZATION_LEN);
dwPlugInError = pAuthCtx->PreAuthUser(pBuf, &cbBuf);
if ((dwPlugInError == ERROR_INSUFFICIENT_BUFFER) && (pAuthCtx->GetSchemeType() == WINHTTP_AUTH_SCHEME_PASSPORT)) { if (fOkToRetry) { fOkToRetry = FALSE; FREE_MEMORY(szSlowHeader); szSlowHeader = NULL; goto retry_alloc; } }
// If the plug in did not fail, add its header to the outgoing header list
if (dwPlugInError == ERROR_SUCCESS && pAuthCtx->GetState() != AUTHCTX::STATE_ERROR) { // Append CR-LF.
pBuf += cbBuf; *pBuf++ = '\r'; *pBuf++ = '\n'; *pBuf = 0; cbBuf = (DWORD) (pBuf - szSlowHeader);
// Add or replace the (proxy-)authorization header.
wHttpAddRequestHeaders (pRequest, szSlowHeader, cbBuf, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
}
// delete [] pszHeader;
if( szSlowHeader ) { FREE_MEMORY( szSlowHeader ); }
/*
if (pszFastHeader) { FREE_MEMORY( pszFastHeader ); } */
//DEBUG_LEAVE(0);
}
// biaow: move this to a better place
PSTR DwordSchemeToString(DWORD dwScheme) { switch (dwScheme) { case WINHTTP_AUTH_SCHEME_BASIC: return "Basic"; case WINHTTP_AUTH_SCHEME_NTLM: return "NTLM"; case WINHTTP_AUTH_SCHEME_PASSPORT: return "Passport1.4"; case WINHTTP_AUTH_SCHEME_DIGEST: return "Digest"; case WINHTTP_AUTH_SCHEME_NEGOTIATE: return "Negotiate"; default: return ""; } }
DWORD DwordSchemeToFlags(DWORD dwScheme) { switch (dwScheme) { case WINHTTP_AUTH_SCHEME_BASIC: return PLUGIN_AUTH_FLAGS_KEEP_ALIVE_NOT_REQUIRED; case WINHTTP_AUTH_SCHEME_NTLM: return PLUGIN_AUTH_FLAGS_NO_REALM; case WINHTTP_AUTH_SCHEME_PASSPORT: return PLUGIN_AUTH_FLAGS_KEEP_ALIVE_NOT_REQUIRED; case WINHTTP_AUTH_SCHEME_DIGEST: return PLUGIN_AUTH_FLAGS_KEEP_ALIVE_NOT_REQUIRED | PLUGIN_AUTH_FLAGS_CAN_HANDLE_UI; case WINHTTP_AUTH_SCHEME_NEGOTIATE: return PLUGIN_AUTH_FLAGS_KEEP_ALIVE_NOT_REQUIRED | PLUGIN_AUTH_FLAGS_NO_REALM; default: return 0; } }
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/ PUBLIC DWORD AuthOnRequest (IN HINTERNET hRequestMapped) { DEBUG_ENTER ((DBG_HTTP, Dword, "AuthOnRequest", "%#x", hRequestMapped));
DWORD dwError = ERROR_SUCCESS; LPSTR lpszUser, lpszPass = NULL; // Get username, password, url, and auth context from request handle.
HTTP_REQUEST_HANDLE_OBJECT *pRequest = (HTTP_REQUEST_HANDLE_OBJECT *) hRequestMapped;
AUTHCTX *pAuthCtx = pRequest->GetAuthCtx(); BOOL fCredsChanged = FALSE; // PROXY AUTHENTICATION
//
// CERN proxies should remove proxy-authorization headers before forwarding
// requests to servers. Otherwise, don't add proxy authorization headers
// that would be seen by servers on a direct connect or via SSL tunneling.
if (pRequest->IsRequestUsingProxy() && !pRequest->IsTalkingToSecureServerViaProxy()) { // if an app sets proxy creds, we want to make sure that the specified
// auth scheme is the same as the auth scheme in progress. Otherwise, we
// will delete the current auth context and build a new one based on the
// new scheme.
if (pRequest->_pProxyCreds && (pAuthCtx && pAuthCtx->_fIsProxy) && (pAuthCtx->GetRawSchemeType() != pRequest->_pProxyCreds->_AuthScheme)) { delete pAuthCtx; pAuthCtx = NULL; pRequest->SetAuthCtx(NULL); }
if (pAuthCtx && pAuthCtx->_fIsProxy) { // We have a proxy authentication in progress.
// If a user/pass set on handle, transfer to AUTH_CREDS.
// First check for proxy credentials and fallback to server
// for legacy wininet apps. This will invalidate the credentials
// on the handle they were found for any subsequent calls to
// GetUserAndPass.
// Serialize access to globals.
if (AuthLock()) { if (pRequest->_pProxyCreds) { lpszPass = pRequest->_pProxyCreds->GetPassword(); pAuthCtx->_pCreds->SetUser(pRequest->_pProxyCreds->_pszUserName); pAuthCtx->_pCreds->SetPass(lpszPass);
} else if (pRequest->GetUserAndPass(IS_PROXY, &lpszUser, &lpszPass)) { pAuthCtx->_pCreds->SetUser(lpszUser); pAuthCtx->_pCreds->SetPass(lpszPass); }
AuthUnlock();
// Add the authorization header.
AddAuthorizationHeader (pRequest, pAuthCtx); } }
// NO PROXY AUTHENTICATION CONTEXT -> SEE IF A AUTH_CREDS EXISTS.
else // See if we have a cached proxy user/pass.
{ if (pRequest->_pProxyCreds && pRequest->_pProxyCreds->_AuthScheme == WINHTTP_AUTH_SCHEME_BASIC) { AUTHCTX::SPMData* pSPMData = AUTHCTX::SPMLookup(DwordSchemeToString(pRequest->_pProxyCreds->_AuthScheme));
/*
reinterpret_cast<AUTHCTX::SPMData*>(New AUTHCTX::SPMData( DwordSchemeToString(pRequest->_pProxyCreds->_AuthScheme), DwordSchemeToFlags(pRequest->_pProxyCreds->_AuthScheme))); */
if (pSPMData) { AUTH_CREDS *pCreds = AUTHCTX::CreateCreds(pRequest, TRUE, pSPMData, pRequest->_pProxyCreds->_pszRealm); if (pCreds) { lpszPass = pRequest->_pProxyCreds->GetPassword(); pCreds->SetUser(pRequest->_pProxyCreds->_pszUserName); pCreds->SetPass(lpszPass);
AUTHCTX* pNewAuthCtx = AUTHCTX::CreateAuthCtx(pRequest, IS_PROXY, pCreds); if (pNewAuthCtx && pNewAuthCtx->GetSchemeType() == WINHTTP_AUTH_SCHEME_BASIC) { AddAuthorizationHeader (pRequest, pNewAuthCtx); } delete pNewAuthCtx; pNewAuthCtx = NULL; } } } } }
if (lpszPass) { SecureZeroMemory(lpszPass, strlen(lpszPass)); FREE_MEMORY(lpszPass); lpszPass = NULL; }
// SERVER AUTHENTICATION
//
// Don't send server authorization when initiating SSL tunneling with proxy.
if (!pRequest->IsTunnel()) { // if an app sets server creds, we want to make sure that the specified
// auth scheme is the same as the auth scheme in progress. Otherwise, we
// will delete the current auth context and build a new one based on the
// new scheme.
BOOL fNonePreferredSchemeUsed = FALSE;
if (pRequest->_pServerCreds && (pAuthCtx && !pAuthCtx->_fIsProxy) && (pAuthCtx->GetRawSchemeType() != pRequest->_pServerCreds->_AuthScheme)) { delete pAuthCtx; pAuthCtx = NULL; pRequest->SetAuthCtx(NULL);
fNonePreferredSchemeUsed = TRUE; }
// See if we have a server authentication in progress
if (pAuthCtx && !pAuthCtx->_fIsProxy) { // Server authentication in progress.
// If a user/pass set on handle, transfer to AUTH_CREDS.
// This will invalidate the credentials on the handle they
// were found for any subsequent calls to GetUserAndPass.
if (AuthLock()) { if (pRequest->_pServerCreds) { lpszPass = pRequest->_pServerCreds->GetPassword(); pAuthCtx->_pCreds->SetUser(pRequest->_pServerCreds->_pszUserName); pAuthCtx->_pCreds->SetPass(lpszPass); } else if (pRequest->GetUserAndPass(IS_SERVER, &lpszUser, &lpszPass)) { pAuthCtx->_pCreds->SetUser(lpszUser); pAuthCtx->_pCreds->SetPass(lpszPass); }
AuthUnlock();
// Add the authorization header.
AddAuthorizationHeader (pRequest, pAuthCtx); } } else // See if we have a cached server user/pass.
{ // NO PROXY AUTHENTICATION CONTEXT -> SEE IF A AUTH_CREDS EXISTS
if (pRequest->_pServerCreds) { AUTHCTX *pNewAuthCtx = NULL;
AUTHCTX::SPMData* pSPMData = AUTHCTX::SPMLookup(DwordSchemeToString(pRequest->_pServerCreds->_AuthScheme));
/*
reinterpret_cast<AUTHCTX::SPMData*>(New AUTHCTX::SPMData( DwordSchemeToString(pRequest->_pServerCreds->_AuthScheme), DwordSchemeToFlags(pRequest->_pServerCreds->_AuthScheme))); */ INET_ASSERT(pSPMData);
AUTH_CREDS *pCreds = AUTHCTX::CreateCreds(pRequest, FALSE, pSPMData, pRequest->_pServerCreds->_pszRealm, pRequest->_pServerCreds); if (pCreds) { if (pRequest->_pServerCreds->_AuthScheme != WINHTTP_AUTH_SCHEME_PASSPORT) { pNewAuthCtx = AUTHCTX::CreateAuthCtx(pRequest, IS_SERVER, pCreds); } if(pNewAuthCtx) { if (pNewAuthCtx->GetSchemeType() == WINHTTP_AUTH_SCHEME_NTLM || pNewAuthCtx->GetSchemeType() == WINHTTP_AUTH_SCHEME_NEGOTIATE) { // NTLM or Negotiate (in which case we don't really know the
// protocol yet) - create the auth context, set it in the handle
// and set state to AUTHSTATE_NEGOTIATE. Handle now has
// a valid auth context and is in the correct auth state
// for the remainder of the authorization sequence.
// It's possible that the Creds entry was created when no proxy
// was in use and the user set a proxy. Check that this is
// not the case.
if (!fNonePreferredSchemeUsed &&
(!pRequest->IsMethodBody() || (pNewAuthCtx->GetSchemeType() == WINHTTP_AUTH_SCHEME_NTLM && pRequest->IsDisableNTLMPreauth()) || (pRequest->IsRequestUsingProxy() && !pRequest->IsTalkingToSecureServerViaProxy()))) { // NTLM preauth disabled or over proxy; no preauth.
delete pNewAuthCtx; pNewAuthCtx = NULL; } else { // Set the auth context in the handle and
// add the auth header.
pRequest->SetAuthCtx(pNewAuthCtx); pRequest->SetAuthState(AUTHSTATE_NEGOTIATE); AddAuthorizationHeader (pRequest, pNewAuthCtx); pNewAuthCtx = NULL; } } else { if ((pNewAuthCtx->GetSchemeType() == WINHTTP_AUTH_SCHEME_DIGEST && !fCredsChanged) || (pNewAuthCtx->GetSchemeType() == WINHTTP_AUTH_SCHEME_BASIC)) { pRequest->SetAuthCtx(pNewAuthCtx); AddAuthorizationHeader (pRequest, pNewAuthCtx); pRequest->SetCreds(pNewAuthCtx->_pCreds); pNewAuthCtx = NULL; } //else if (pNewAuthCtx->GetSchemeType() == WINHTTP_AUTH_SCHEME_PASSPORT)
//{
// pRequest->SetAuthCtx(pNewAuthCtx);
// AddAuthorizationHeader (pRequest, pNewAuthCtx);
// pNewAuthCtx = NULL;
//}
}
if (pNewAuthCtx != NULL) { delete pNewAuthCtx; pNewAuthCtx = NULL; } } } } } }
if (lpszPass) { SecureZeroMemory(lpszPass, strlen(lpszPass)); FREE_MEMORY(lpszPass); lpszPass = NULL; }
DEBUG_LEAVE(dwError); return dwError; }
/*-----------------------------------------------------------------------------
-----------------------------------------------------------------------------*/ DWORD ProcessResponseHeaders ( HINTERNET hRequestMapped, BOOL fIsProxy ) { DEBUG_ENTER ((DBG_HTTP, Dword, "ProcessResponseHeaders", "%#x", hRequestMapped));
DWORD dwError = ERROR_SUCCESS;
// Get context from request handle.
HTTP_REQUEST_HANDLE_OBJECT *pRequest = (HTTP_REQUEST_HANDLE_OBJECT *) hRequestMapped; AUTHCTX* pAuthCtx = pRequest->GetAuthCtx(); BOOL fDoNotSetCreds = FALSE;
if (pAuthCtx) { if ((dwError = pAuthCtx->UpdateFromHeaders(pRequest, fIsProxy)) != ERROR_SUCCESS) { // Delete the auth context and fail auth
// immediately if any other error than
// scheme has been changed.
delete pAuthCtx; pRequest->SetAuthCtx(NULL); if (dwError != ERROR_HTTP_HEADER_NOT_FOUND) { goto cleanup; }
fDoNotSetCreds = TRUE;
if (fIsProxy) { if (pRequest->_pProxyCreds) { delete pRequest->_pProxyCreds; pRequest->_pProxyCreds = NULL; } } else { if (pRequest->_pServerCreds) { delete pRequest->_pServerCreds; pRequest->_pServerCreds = NULL; } }
pRequest->ClearUserAndPass(fIsProxy);
// Attempt to create a new auth context using
// the challenge received from the server.
// If this fails, we follow logic as commented
// below.
pAuthCtx = AUTHCTX::CreateAuthCtx(pRequest, fIsProxy); if (!pAuthCtx) { dwError = ERROR_SUCCESS; goto cleanup; }
pAuthCtx->DisableAutoLogon(); } } else { // CreateAuthCtx returns NULL if auth scheme not
// supported (fall through from HttpFiltOnResponse
// in sendreq.cxx) or if scheme is NTLM and the
// socket is not keep-alive or via proxy.
// In these cases it is necessary to check for a NULL
// return value. The correct return code for these cases is
// ERROR_SUCCESS, which will be returned by AuthOnResponse.
pAuthCtx = AUTHCTX::CreateAuthCtx(pRequest, fIsProxy); if (!pAuthCtx) { dwError = ERROR_SUCCESS; goto cleanup; } }
LPSTR lpszUser, lpszPass = NULL;
// First check for proxy credentials and fallback to server
// for legacy wininet apps. This will invalidate the credentials
// on the handle they were found for any subsequent calls to
// GetUserAndPass.
// I believe we should be putting the credentials in the
// password cache at this time. The scenario is that a client
// sets credentials on a handle, after a successful authentication
// the Creds will have null credentials. Pre-auth will then pull up
// credentials for the default user!!!!!!!!!!!
// Serialize access to globals.
if (AuthLock()) { if (pRequest->_pServerCreds && !fIsProxy) { lpszUser = pRequest->_pServerCreds->_pszUserName; lpszPass = pRequest->_pServerCreds->GetPassword(); } else if (pRequest->_pProxyCreds && fIsProxy) { lpszUser = pRequest->_pProxyCreds->_pszUserName; lpszPass = pRequest->_pProxyCreds->GetPassword(); } else if ((!pRequest->GetUserAndPass(fIsProxy, &lpszUser, &lpszPass)) && fIsProxy) { pRequest->GetUserAndPass(IS_SERVER, &lpszUser, &lpszPass); }
AuthUnlock(); } else { dwError = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; }
// If we retrieved credentials from the handle set
// them in the Creds.
if (lpszUser && lpszPass && !fDoNotSetCreds) { pAuthCtx->_pCreds->SetUser(lpszUser); pAuthCtx->_pCreds->SetPass(lpszPass); }
if (lpszPass) { SecureZeroMemory(lpszPass, strlen(lpszPass)); FREE_MEMORY(lpszPass); lpszPass = NULL; }
// Post authenticate user.
dwError = pAuthCtx->PostAuthUser();
// Map all unexpected error codes to login failure.
if (dwError != ERROR_WINHTTP_FORCE_RETRY && dwError != ERROR_WINHTTP_INCORRECT_PASSWORD) { dwError = ERROR_WINHTTP_LOGIN_FAILURE; }
pRequest->SetAuthCtx(pAuthCtx);
cleanup:
DEBUG_LEAVE (dwError); return dwError; }
BOOL IsSameDomain(LPCSTR pszTarget, LPCSTR pszResponse) { LPCSTR pszTargetR = pszTarget + strlen(pszTarget); DWORD dwDotsSeen = 0; while (--pszTargetR > pszTarget) { if (*pszTargetR == '.') { if (++dwDotsSeen == 2) { break; } } }
if (dwDotsSeen == 2) { ++pszTargetR; DWORD dwOffsetR = strlen(pszTargetR); if (strlen(pszResponse) < dwOffsetR) { return FALSE; }
LPCSTR pszResponseR = pszResponse + strlen(pszResponse) - dwOffsetR;
return !strcmp(pszTargetR, pszResponseR); } else { return FALSE; } }
VOID CheckForTweenerLogout(HTTP_REQUEST_HANDLE_OBJECT *pRequest) { DWORD dwIndex = 0; DWORD dwError;
do { LPSTR szData; DWORD cbData; dwError = pRequest->FastQueryResponseHeader(HTTP_QUERY_AUTHENTICATION_INFO, (LPVOID*) &szData, &cbData, dwIndex); if (dwError == ERROR_SUCCESS) { BOOL fLogout = FALSE; CHAR TempChar = szData[cbData]; szData[cbData] = '\0';
if (strstr(szData, "Passport1.4") && strstr(szData, "logout")) { fLogout = TRUE; } szData[cbData] = TempChar;
if (fLogout) { PP_CONTEXT hPP = GetRootHandle(pRequest)->GetPPContext(); if (hPP != NULL) { WCHAR wszDAHost[256]; DWORD dwDAHostLen = 256; CHAR szDAHost[256]; ::PP_GetEffectiveDAHost(hPP, wszDAHost, &dwDAHostLen); ::WideCharToMultiByte(CP_ACP, 0, wszDAHost, -1, szDAHost, 256, NULL, NULL); if (IsSameDomain(pRequest->GetServerName(), szDAHost)) { ::PP_Logout(hPP, 0); } } break; } }
++dwIndex;
} while (dwError == ERROR_SUCCESS); }
/*-----------------------------------------------------------------------------
Routine Description:
Validates, and Caches Authentication Request headers if needed. If a URL matches a cached set of templates it is assumed to require specific authentication information.
Arguments:
hRequest - An open HTTP request handle where headers will be added if needed.
Return Value:
DWORD Success - ERROR_SUCCESS
Failure - One of Several Error codes defined in winerror.h or wininet.w
Comments:
Need to handle mutiple authentication pages.
-----------------------------------------------------------------------------*/ PUBLIC DWORD AuthOnResponse (HINTERNET hRequestMapped) { DEBUG_ENTER((DBG_HTTP, Dword, "AuthOnResponse", "%#x [%#x]", hRequestMapped, ((INTERNET_HANDLE_BASE *)hRequestMapped)->GetPseudoHandle() ));
// Get URL and password cache entry from request handle.
HTTP_REQUEST_HANDLE_OBJECT *pRequest = (HTTP_REQUEST_HANDLE_OBJECT *) hRequestMapped;
AUTHCTX *pAuthCtx = pRequest->GetAuthCtx(); DWORD dwStatus = pRequest->GetStatusCode();
if (pAuthCtx) { if (pAuthCtx->_fIsProxy && dwStatus != HTTP_STATUS_PROXY_AUTH_REQ) { if (dwStatus == HTTP_STATUS_REDIRECT) // we might need to do Tweener auth
{ if (pRequest->_pTweenerProxyCreds) { Creds_Free(pRequest->_pTweenerProxyCreds); pRequest->_pTweenerProxyCreds = NULL; } pRequest->_pTweenerProxyCreds = pAuthCtx->_pCreds; pAuthCtx->_pCreds = NULL; }
// We are done with proxy authentication.
delete pAuthCtx; pRequest->SetAuthCtx (NULL); if (pRequest->_pProxyCreds) { delete pRequest->_pProxyCreds; pRequest->_pProxyCreds = NULL; }
pRequest->ClearUserAndPass(IS_PROXY); } else if (!pAuthCtx->_fIsProxy && dwStatus != HTTP_STATUS_DENIED) { if ((pAuthCtx->GetSchemeType() != WINHTTP_AUTH_SCHEME_PASSPORT) || dwStatus != HTTP_STATUS_REDIRECT) { // We are done with server authentication.
if (pAuthCtx->GetSchemeType() == WINHTTP_AUTH_SCHEME_PASSPORT) { delete [] pRequest->m_lpszRetUrl; pRequest->m_lpszRetUrl = ((PASSPORT_CTX*)pAuthCtx)->m_lpszRetUrl; ((PASSPORT_CTX*)pAuthCtx)->m_lpszRetUrl = NULL; }
delete pAuthCtx; pRequest->SetAuthCtx (NULL);
if (pRequest->_pServerCreds) { delete pRequest->_pServerCreds; pRequest->_pServerCreds = NULL; }
pRequest->ClearUserAndPass(IS_SERVER); } else { // in the case of Passport Auth, 302 is still not done yet,
// but this is quite strange since 302 came a second time
// biaow-todo: we could be in a loop here
} } }
// Remove any stale authorization headers in case wHttpSendRequest
// loops, for example, to handle a redirect. To ignore trailing colon,
// subtract 1 from header lengths passed to ReplaceRequestHeader.
pRequest->ReplaceRequestHeader (HTTP_QUERY_AUTHORIZATION, "", 0, 0, HTTP_ADDREQ_FLAG_REPLACE); pRequest->ReplaceRequestHeader (HTTP_QUERY_PROXY_AUTHORIZATION, "", 0, 0, HTTP_ADDREQ_FLAG_REPLACE);
DWORD error;
//
// note: Negotiate MUTUAL_AUTH can return dwStatus = 200 with a final
// WWW-Authenticate blob to process. logic below could be adjusted
// to ProcessResponseHeaders() in this situation, which allows MUTUAL_AUTH
// to be enforced.
//
switch (dwStatus) { case HTTP_STATUS_PROXY_AUTH_REQ: // 407
if (pRequest->IsRequestUsingProxy()) error = ProcessResponseHeaders(pRequest, IS_PROXY); else error = ERROR_WINHTTP_INVALID_SERVER_RESPONSE; break;
case HTTP_STATUS_REDIRECT: // 302
{ INTERNET_HANDLE_OBJECT * pInternet = GetRootHandle((INTERNET_HANDLE_BASE*)pRequest); if (pInternet->TweenerDisabled()) { // biaow-todo: no passport support for down-levels yet
pRequest->SetAuthState(AUTHSTATE_NONE); error = ERROR_SUCCESS;
break; } } // process the header to see whether this is a 302 passport1.4 challenge
case HTTP_STATUS_DENIED: // 401
error = ProcessResponseHeaders(pRequest, IS_SERVER); break;
default: pRequest->SetAuthState(AUTHSTATE_NONE); error = ERROR_SUCCESS; }
// creds set by WinHttpSetCredentials() is good for one "authentication attempt" only.
// Since we are done authentication here, we are deleting it.
// biaow: detect the "final" failure so that we can delete the creds.
if (pRequest->_pTweenerProxyCreds) { Creds_Free(pRequest->_pTweenerProxyCreds); pRequest->_pTweenerProxyCreds = NULL; } CheckForTweenerLogout(pRequest); DEBUG_LEAVE(error); return error; }
//-----------------------------------------------------------------------------
//
//
// AUTHCTX Base class definitions
//
//
//
// static funcs:
// InitializeSPMList
// UnloadAll
// CreateAuthCtx
// CreateAuthCtx (using Creds*)
// GetSPMListState
// SearchCredsList
// FindOrCreateCreds
// GetAuthHeaderData
//
// base funcs:
// FindHdrIdxFromScheme
// GetScheme
// GetSchemeType - returns enum
// GetFlags - returns SPM flags
// GetState - returns state of SPM provider
//
//
// virtual overrides: defined in basic.cxx, plug.cxx and digest.cxx
// UpdateFromHeaders
// PreAuthUser
// PostAuthUser
//
//
//
/*---------------------------------------------------------------------------
AUTHCTX::SPMData constructor ---------------------------------------------------------------------------*/ AUTHCTX::SPMData::SPMData(LPSTR _szScheme, DWORD _dwFlags) { if (_szScheme) { szScheme = NewString(_szScheme); cbScheme = strlen(_szScheme); } else { szScheme = NULL; cbScheme = 0; }
if (szScheme) { if (!lstrcmpi(szScheme, "Basic")) eScheme = WINHTTP_AUTH_SCHEME_BASIC; else if (!lstrcmpi(szScheme, "NTLM")) eScheme = WINHTTP_AUTH_SCHEME_NTLM; else if (!lstrcmpi(szScheme, "Digest")) eScheme = WINHTTP_AUTH_SCHEME_DIGEST; else if (!lstrcmpi(szScheme, "Negotiate")) eScheme = WINHTTP_AUTH_SCHEME_NEGOTIATE; else if (!lstrcmpi(szScheme, "Passport1.4")) eScheme = WINHTTP_AUTH_SCHEME_PASSPORT; else eScheme = 0;
dwFlags = _dwFlags; eState = STATE_NOTLOADED; } else { dwFlags = 0; eState = STATE_ERROR; } }
/*---------------------------------------------------------------------------
AUTHCTX::SPMData destructor ---------------------------------------------------------------------------*/ AUTHCTX::SPMData::~SPMData() { delete szScheme; }
/*---------------------------------------------------------------------------
AUTHCTX constructor ---------------------------------------------------------------------------*/ AUTHCTX::AUTHCTX(SPMData *pData, AUTH_CREDS *pCreds) { _pSPMData = pData; _pCreds = pCreds; _pRequest = NULL; _fIsProxy = FALSE; _pvContext = NULL; _eSubScheme = 0; _dwSubFlags = 0;
_fDisableAutoLogon = FALSE;
_CtxCriSec.Init(); }
/*---------------------------------------------------------------------------
Destructor ---------------------------------------------------------------------------*/ AUTHCTX::~AUTHCTX() { if (AuthLock()) { if (_pCreds) Creds_Free(_pCreds); AuthUnlock(); }
_CtxCriSec.FreeLock(); }
// ------------------------ Static Functions ---------------------------------
/*---------------------------------------------------------------------------
InitializeSPMList ---------------------------------------------------------------------------*/ BOOL AUTHCTX::InitializeSPMList() { BOOL retVal = FALSE; struct SchemeFlagsPair { LPSTR pszScheme; DWORD Flags; };
SchemeFlagsPair SchemeFlags[] = { {"NTLM", PLUGIN_AUTH_FLAGS_NO_REALM}, {"Basic", PLUGIN_AUTH_FLAGS_KEEP_ALIVE_NOT_REQUIRED}, {"Digest", PLUGIN_AUTH_FLAGS_KEEP_ALIVE_NOT_REQUIRED | PLUGIN_AUTH_FLAGS_CAN_HANDLE_UI}, {"Negotiate", PLUGIN_AUTH_FLAGS_KEEP_ALIVE_NOT_REQUIRED | PLUGIN_AUTH_FLAGS_NO_REALM}, {"Passport1.4", PLUGIN_AUTH_FLAGS_KEEP_ALIVE_NOT_REQUIRED} }; if( GetSPMListState() == STATE_LOADED) { retVal = TRUE; goto done; }
SPMData *pNew; g_pSPMList = NULL; g_eState = STATE_ERROR;
AssertHaveLock();
// Hard-wired Basic, NTLM and Digest
for (DWORD dwIndex = 0; dwIndex < sizeof(SchemeFlags) / sizeof(SchemeFlagsPair); dwIndex++) { if (!GlobalPlatformVersion5 // we don't support Negotiate on NT4 or Win9x
&& !stricmp(SchemeFlags[dwIndex].pszScheme, "Negotiate")) { continue; }
pNew = (AUTHCTX::SPMData*) New SPMData(SchemeFlags[dwIndex].pszScheme, SchemeFlags[dwIndex].Flags); if (!pNew) goto done;
// Add to head of list.
if (pNew->eState != STATE_ERROR) { pNew->pNext = g_pSPMList; g_pSPMList = pNew; } }
// The list is now in the correct state.
g_eState = STATE_LOADED; retVal = TRUE;
done: return retVal; }
VOID AUTHCTX::UnloadAll() {
SPMData *pData = g_pSPMList; while (pData) { SPMData *pNext = pData->pNext; delete pData; pData = pNext; }
SSPI_Unload();
g_eState = STATE_NOTLOADED; g_pSPMList = NULL; }
DWORD StringSchemeToDword(LPSTR szScheme) { if (!stricmp(szScheme, "Basic")) { return WINHTTP_AUTH_SCHEME_BASIC; } else if (!stricmp(szScheme, "Digest")) { return WINHTTP_AUTH_SCHEME_DIGEST; } else if (!stricmp(szScheme, "Passport1.4")) { return WINHTTP_AUTH_SCHEME_PASSPORT; } else if (!stricmp(szScheme, "NTLM")) { return WINHTTP_AUTH_SCHEME_NTLM; } else if (!stricmp(szScheme, "Negotiate")) { return WINHTTP_AUTH_SCHEME_NEGOTIATE; } else { return 0; } }
/*---------------------------------------------------------------------------
CreateAuthCtx Create an AUTHCTX* from headers - initially the authentication context is created without a AUTH_CREDS entry. The AUTH_CREDS entry will be found or created and possibly updated in UpdateFromHeaders. ---------------------------------------------------------------------------*/ AUTHCTX* AUTHCTX::CreateAuthCtx(HTTP_REQUEST_HANDLE_OBJECT * pRequest, BOOL fIsProxy) { LPSTR szScheme = NULL; DWORD cbScheme = 0, dwError = 0, dwAuthIdx = 0; AUTHCTX *pAuthCtx = NULL; WINHTTP_REQUEST_CREDENTIALS* pRequestCreds = fIsProxy ? pRequest->_pProxyCreds : pRequest->_pServerCreds; pRequest->_PreferredScheme = 0; pRequest->_SupportedSchemes = 0; // Get scheme. This is assumed to be the first
// non-ws token in the auth header info.
do { DWORD dwScheme = 0; AUTHCTX::SPMData *pSPM = NULL;
delete [] szScheme; szScheme = NULL;
// It is necessary to hold on to the auth index
// in this loop because we could have gone through
// more than one scheme.
dwError = GetAuthHeaderData(pRequest, fIsProxy, NULL, &szScheme, &cbScheme, ALLOCATE_BUFFER | GET_SCHEME, dwAuthIdx++);
if (dwError != ERROR_SUCCESS) goto quit;
pSPM = SPMLookup(szScheme);
if (pSPM == NULL) continue; dwScheme = pSPM ? pSPM->eScheme : 0;
pRequest->_KnowSupportedSchemes = TRUE; pRequest->_SupportedSchemes |= dwScheme; pRequest->_AuthTarget = fIsProxy ? WINHTTP_AUTH_TARGET_PROXY : WINHTTP_AUTH_TARGET_SERVER; DEBUG_PRINT(API, INFO, ("CreateAuthCtx(%x) : Auth Target = %x\n", fIsProxy, pRequest->_AuthTarget ));
if (pRequest->_PreferredScheme == 0) pRequest->_PreferredScheme = dwScheme;
// If we haven't created an auth context yet, maybe create one.
if (pAuthCtx == NULL) { // If there is a scheme we have credentials for,
//only create an auth context for that scheme.
if (pRequestCreds && pRequestCreds->_AuthScheme == dwScheme) { AUTH_CREDS* pAuthCreds = AUTHCTX::CreateCreds(pRequest, fIsProxy, pSPM, pRequestCreds->_pszRealm, pRequestCreds); if (pAuthCreds) { pAuthCtx = AUTHCTX::CreateAuthCtx(pRequest, fIsProxy, pAuthCreds); } } // If there isn't a scheme we have credentials for,
//create an auth context for the first scheme.
else if (pRequestCreds == NULL) { pAuthCtx = CreateAuthCtx(pRequest, fIsProxy, szScheme); }
// If creation of an auth context is successful, update
// the context from any header info.
if (pAuthCtx) { if (pAuthCtx->UpdateFromHeaders(pRequest, fIsProxy) != ERROR_SUCCESS) { delete pAuthCtx; pAuthCtx = NULL; } } } } while (1);
quit:
if (szScheme) delete szScheme;
return pAuthCtx; }
AUTHCTX::SPMData* AUTHCTX::SPMLookup(LPSTR szScheme) { if (!AuthLock()) { return NULL; }
AUTHCTX::SPMData *pSPM = NULL;
if (!AUTHCTX::InitializeSPMList()) { // not critical, just no authentication
goto quit; }
pSPM = g_pSPMList;
// Find SPMData to create from scheme.
while (pSPM) { if (!lstrcmpi(pSPM->szScheme, szScheme)) break;
pSPM = pSPM->pNext; }
quit: AuthUnlock(); return pSPM; }
/*---------------------------------------------------------------------------
CreateAuthCtx Create an AUTHCTX without a AUTH_CREDS from scheme ---------------------------------------------------------------------------*/ AUTHCTX* AUTHCTX::CreateAuthCtx(HTTP_REQUEST_HANDLE_OBJECT *pRequest, BOOL fIsProxy, LPSTR szScheme) { if (!AuthLock()) { return NULL; }
AUTHCTX *pNewAuthCtx = NULL;
// we don't want to create a Passport1.4 context on 401 response (from DA)
if (!lstrcmpi("Passport1.4", szScheme)) { if (pRequest->GetStatusCode() == HTTP_STATUS_DENIED) { goto quit; } }
SPMData *pSPM = SPMLookup( szScheme);
if (!pSPM) { // No matching auth scheme found.
// Not critical, just no auth.
goto quit; }
// Create an auth context without Creds
switch(pSPM->eScheme) { // Create BASIC_CTX with NULL AUTH_CREDS.
case WINHTTP_AUTH_SCHEME_BASIC: pNewAuthCtx = New BASIC_CTX(pRequest, fIsProxy, pSPM, NULL); break;
// Create DIGEST_CTX with NULL AUTH_CREDS.
case WINHTTP_AUTH_SCHEME_DIGEST: pNewAuthCtx = New DIGEST_CTX(pRequest, fIsProxy, pSPM, NULL); break;
case WINHTTP_AUTH_SCHEME_PASSPORT: pNewAuthCtx = new PASSPORT_CTX(pRequest, fIsProxy, pSPM, NULL); if (pNewAuthCtx) { if (((PASSPORT_CTX*)pNewAuthCtx)->Init() == FALSE) { delete pNewAuthCtx; pNewAuthCtx = NULL; } } break;
// Create PLUG_CTX with NULL AUTH_CREDS.
case WINHTTP_AUTH_SCHEME_NTLM: case WINHTTP_AUTH_SCHEME_NEGOTIATE:
default: pNewAuthCtx = New PLUG_CTX(pRequest, fIsProxy, pSPM, NULL);
}
quit: AuthUnlock(); return pNewAuthCtx; }
/*---------------------------------------------------------------------------
CreateAuthCtx Create an AUTHCTX from a AUTH_CREDS. ---------------------------------------------------------------------------*/ AUTHCTX* AUTHCTX::CreateAuthCtx(HTTP_REQUEST_HANDLE_OBJECT *pRequest, BOOL fIsProxy, AUTH_CREDS* pCreds) { if (!AuthLock()) { return NULL; } AUTHCTX *pNewAuthCtx = NULL;
if (!AUTHCTX::InitializeSPMList()) { // not critical, just no authentication
goto quit; }
// Handle tests (via proxy, is keep-alive)
// will be done in UpdateFromHeaders. Here
// we just construct the AUTHCTX.
switch(pCreds->pSPM->eScheme) { // Create BASIC_CTX.
case WINHTTP_AUTH_SCHEME_BASIC:
pNewAuthCtx = New BASIC_CTX(pRequest, fIsProxy, pCreds->pSPM, pCreds); break;
// Create DIGEST_CTX.
case WINHTTP_AUTH_SCHEME_DIGEST: pNewAuthCtx = New DIGEST_CTX(pRequest, fIsProxy, pCreds->pSPM, pCreds); break;
case WINHTTP_AUTH_SCHEME_PASSPORT: pNewAuthCtx = new PASSPORT_CTX(pRequest, fIsProxy, pCreds->pSPM, pCreds); if (pNewAuthCtx) { if (((PASSPORT_CTX*)pNewAuthCtx)->Init() == FALSE) { delete pNewAuthCtx; pNewAuthCtx = NULL; } } break;
// Create PLUG_CTX.
case WINHTTP_AUTH_SCHEME_NTLM: case WINHTTP_AUTH_SCHEME_NEGOTIATE:
default: pNewAuthCtx = New PLUG_CTX(pRequest, fIsProxy, pCreds->pSPM, pCreds);
}
quit: AuthUnlock(); return pNewAuthCtx; }
AUTHCTX::SPMState AUTHCTX::GetSPMListState() { return g_eState; }
/*-----------------------------------------------------------------------------
SearchCredsList -----------------------------------------------------------------------------*/ //AUTH_CREDS* AUTHCTX::SearchCredsList
// (AUTH_CREDS* Creds, LPSTR lpszHost, LPSTR lpszUri, LPSTR lpszRealm, SPMData *pSPM)
/*++
Routine Description: Scans the Linked List Cache for URLs, Realms, and Servers. Also allows filter fields, to narrow searches.
Arguments: Creds - Pointer to first item to search from. lpszHost - Host, or Server name to search on. lpszUri - URL to search on. lpszRealm - Security Realm to search on lpszScheme - Authentication scheme to search on. Return Value:
AUTH_CREDS * Success - Pointer to found item.
Failure - NULL pointer.
Comments: Note: This Legacy code from the SpyGlass IE 1.0 browser
The AUTH_CREDS lists are searched on every request. Could optimize by keeping a hash value of the server/proxy name.
If an exact match isn't found on a 401 response, the list is walked again to search for a realm match. Could add a parameter to do both at once. --*/
/*
{ AssertHaveLock();
while (Creds) { if ( (!pSPM || pSPM == Creds->pSPM) && (!lpszHost || !lstrcmpi(Creds->lpszHost,lpszHost)) && (!lpszRealm || !lstrcmpi(Creds->lpszRealm,lpszRealm)) && (!lpszUri || TemplateMatch (Creds->lpszUrl, lpszUri)) ) {
DEBUG_PRINT(HTTP, INFO, ("Lookup: Found template match [%q]\n", Creds->lpszUser)); return Creds; } else { Creds = Creds->pNext; } }
return NULL; } */
/*-----------------------------------------------------------------------------
FindOrCreateCreds -----------------------------------------------------------------------------*/ AUTH_CREDS* AUTHCTX::CreateCreds( HTTP_REQUEST_HANDLE_OBJECT *pRequest, BOOL fIsProxy, SPMData *pSPM, LPSTR lpszRealm, WINHTTP_REQUEST_CREDENTIALS* pRequestCredentials // defaults to NULL
) { //AssertHaveLock();
// Create a AUTH_CREDS.
AUTH_CREDS *Creds; Creds = NULL;
if (!pSPM) { INET_ASSERT(FALSE); goto quit; }
// Get host from request handle.
LPSTR lpszHost; lpszHost = fIsProxy? GetProxyName(pRequest) : pRequest->GetServerName();
// For NTLM, use the hostname analagously to basic realm.
if (pSPM->eScheme == WINHTTP_AUTH_SCHEME_NTLM || pSPM->eScheme == WINHTTP_AUTH_SCHEME_NEGOTIATE) { INET_ASSERT (!lpszRealm); lpszRealm = lpszHost; }
Creds = Creds_Create (lpszHost, lpszRealm, pSPM);
if (pRequestCredentials) { LPSTR lpszPass = pRequestCredentials->GetPassword();
if( ERROR_SUCCESS != Creds->SetUser(pRequestCredentials->_pszUserName) || ERROR_SUCCESS != Creds->SetPass(lpszPass)) { Creds_Free(Creds); Creds = NULL; }
if (lpszPass) { SecureZeroMemory(lpszPass, strlen(lpszPass)); FREE_MEMORY(lpszPass); } }
quit: return Creds; }
/*-----------------------------------------------------------------------------
GetAuthHeaderData -----------------------------------------------------------------------------*/ DWORD AUTHCTX::GetAuthHeaderData( HTTP_REQUEST_HANDLE_OBJECT *pRequest, BOOL fIsProxy, LPSTR szItem, LPSTR *pszData, LPDWORD pcbData, DWORD dwFlags, DWORD dwIndex) { LPSTR szData; DWORD cbData, cbItem, dwError = ERROR_SUCCESS;; CHAR *szTok, *szKey, *szValue; DWORD cbTok, cbKey, cbValue;
szTok = szKey = szValue = NULL; cbTok = cbKey = cbValue = NULL;
cbItem = szItem ? strlen(szItem) : 0;
DWORD dwQuery = fIsProxy? HTTP_QUERY_PROXY_AUTHENTICATE : HTTP_QUERY_WWW_AUTHENTICATE;
// NULL item passed in means get up to the first \r\n, or
// possibly only the scheme is desired depending on dwFlags.
if (!cbItem) { if ((dwError = pRequest->FastQueryResponseHeader(dwQuery, (LPVOID*) &szData, &cbData, dwIndex)) != ERROR_SUCCESS) { goto quit; }
// Only the scheme is desired.
if (dwFlags & GET_SCHEME) { CHAR* ptr; ptr = szValue = szData; cbValue = 0; while (!(*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n')) { ptr++; cbValue++; } } else { // The entire header is desired.
szValue = szData; cbValue = cbData; }
} else { // An item was passed in - attempt to parse this
// from the headers and return the corresponding
// value.
if ((dwError = pRequest->FastQueryResponseHeader(dwQuery, (LPVOID*) &szData, &cbData, dwIndex)) != ERROR_SUCCESS) { goto quit; }
// Successfully retrieved header. Parse for the desired item.
// Point past scheme
while (!(*szData == ' ' || *szData == '\t' || *szData == '\r' || *szData == '\n')) { szData++; cbData--; }
// Attempt to parse an item of the format 'key = <">value<">'
// from a comma delmited list of items.
dwError = ERROR_HTTP_HEADER_NOT_FOUND; while (GetDelimitedToken(&szData, &cbData, &szTok, &cbTok, ',')) { if (GetKeyValuePair(szTok, cbTok, &szKey, &cbKey, &szValue, &cbValue)) { if ((cbItem == cbKey) && !strnicmp(szKey, szItem, cbItem)) { TrimQuotes(&szValue, &cbValue); dwError = ERROR_SUCCESS; break; } } }
}
if (dwError == ERROR_SUCCESS) { // Allocate buffer containing data
// or return reference.
if (dwFlags & ALLOCATE_BUFFER) { *pszData = New CHAR[cbValue+1]; if (!*pszData) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto quit; } memcpy(*pszData, szValue, cbValue); (*pszData)[cbValue] = '\0'; *pcbData = cbValue; } else { *pszData = szValue; *pcbData = cbValue; } }
quit:
if (dwError != ERROR_SUCCESS) { INET_ASSERT(dwIndex || dwError == ERROR_HTTP_HEADER_NOT_FOUND); }
return dwError; }
// ------------------------ Base class funcs---------------------------------
/*-----------------------------------------------------------------------------
FindHdrIdxFromScheme -----------------------------------------------------------------------------*/ DWORD AUTHCTX::FindHdrIdxFromScheme(LPDWORD pdwIndex) { LPSTR szHeader; DWORD cbHeader, dwQuery, dwError;
dwQuery = _fIsProxy? HTTP_QUERY_PROXY_AUTHENTICATE : HTTP_QUERY_WWW_AUTHENTICATE;
*pdwIndex = 0;
while ((dwError = _pRequest->FastQueryResponseHeader(dwQuery, (LPVOID*) &szHeader, &cbHeader, *pdwIndex)) == ERROR_SUCCESS) { DWORD cb = 0; CHAR *ptr = szHeader; while (!(*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n')) { ptr++; cb++; }
if ((_pSPMData->cbScheme == cb) && (!strnicmp(_pSPMData->szScheme, szHeader, cb))) { break; } (*pdwIndex)++; } return dwError; }
/*-----------------------------------------------------------------------------
Get funcs. -----------------------------------------------------------------------------*/ LPSTR AUTHCTX::GetScheme() { return _pSPMData->szScheme; }
DWORD AUTHCTX::GetSchemeType() { if (_pSPMData->eScheme == WINHTTP_AUTH_SCHEME_NEGOTIATE) { if (_eSubScheme == WINHTTP_AUTH_SCHEME_NTLM || _eSubScheme == WINHTTP_AUTH_SCHEME_KERBEROS) { return _eSubScheme; } } return _pSPMData->eScheme; }
DWORD AUTHCTX::GetRawSchemeType() { return _pSPMData->eScheme; }
DWORD AUTHCTX::GetFlags() { if (_pSPMData->eScheme == WINHTTP_AUTH_SCHEME_NEGOTIATE) { if (_eSubScheme == WINHTTP_AUTH_SCHEME_NTLM || _eSubScheme == WINHTTP_AUTH_SCHEME_KERBEROS) { return _dwSubFlags; } } return _pSPMData->dwFlags; }
AUTHCTX::SPMState AUTHCTX::GetState() { return _pSPMData->eState; }
|