715 lines
23 KiB
C++
715 lines
23 KiB
C++
|
/*--
|
||
|
Copyright (c) 1995-1998 Microsoft Corporation
|
||
|
Module Name: AUTH.CPP
|
||
|
Author: Arul Menezes
|
||
|
Abstract: Authentication
|
||
|
--*/
|
||
|
#include "pch.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include "httpd.h"
|
||
|
|
||
|
#define IsAccessAllowed(a,b,c,d) TRUE
|
||
|
|
||
|
#define NTLM_DOMAIN TEXT("Domain") // dummy data.
|
||
|
#define SEC_SUCCESS(Status) ((Status) >= 0)
|
||
|
|
||
|
#ifndef OLD_CE_BUILD
|
||
|
BOOL NTLMServerContext(
|
||
|
PSEC_WINNT_AUTH_IDENTITY pAuthIdentity,
|
||
|
PAUTH_NTLM pAS,BYTE *pIn, DWORD cbIn, BYTE *pOut,
|
||
|
DWORD *pcbOut, BOOL *pfDone
|
||
|
);
|
||
|
|
||
|
BOOL NTLMClientContext(
|
||
|
PSEC_WINNT_AUTH_IDENTITY pAuthIdentity,
|
||
|
PAUTH_NTLM pAS, BYTE *pIn, DWORD cbIn,
|
||
|
BYTE *pOut, DWORD *pcbOut
|
||
|
);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
AUTHLEVEL GetAuthFromACL(PAUTH_NTLM pAuth, PWSTR wszUser, PWSTR wszVRootUserList);
|
||
|
|
||
|
void AuthInitialize(CReg *pReg, BOOL * pfBasicAuth, BOOL * pfNTLMAuth)
|
||
|
{
|
||
|
*pfBasicAuth = pReg->ValueDW(RV_BASIC);
|
||
|
#ifdef OLD_CE_BUILD
|
||
|
|
||
|
// No passthrough Auth on CE 2.11 devices, set it to 0 to avoid misconfigurations
|
||
|
*pfNTLMAuth = 0;
|
||
|
#else
|
||
|
*pfNTLMAuth = pReg->ValueDW(RV_NTLM);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// For calls to Basic Authentication, only called during the parsing stage.
|
||
|
BOOL HandleBasicAuth(PSTR pszData, PSTR* ppszUser, PSTR *ppszPassword,
|
||
|
AUTHLEVEL* pAuth, PAUTH_NTLM pNTLMState, WCHAR *wszVRootUserList)
|
||
|
{
|
||
|
char szUserName[MAXUSERPASS];
|
||
|
DWORD dwLen = sizeof(szUserName);
|
||
|
|
||
|
*pAuth = AUTH_PUBLIC;
|
||
|
*ppszUser = NULL;
|
||
|
|
||
|
// decode the base64
|
||
|
Base64Decode(pszData, szUserName, &dwLen);
|
||
|
|
||
|
// find the password
|
||
|
PSTR pszPassword = strchr(szUserName, ':');
|
||
|
if (!pszPassword)
|
||
|
{
|
||
|
TraceTag(ttidWebServer, "Bad Format for Basic userpass(%s)-->(%s)", pszData, szUserName);
|
||
|
return FALSE;
|
||
|
}
|
||
|
*pszPassword++ = 0; // seperate user & pass
|
||
|
|
||
|
|
||
|
|
||
|
WCHAR wszPassword[MAXUSERPASS];
|
||
|
MyA2W(pszPassword, wszPassword, CCHSIZEOF(wszPassword));
|
||
|
|
||
|
WCHAR wszUserName[MAXUSERPASS];
|
||
|
MyA2W(szUserName,wszUserName, CCHSIZEOF(wszUserName));
|
||
|
|
||
|
// We save the data no matter what, for logging purposes and for possible
|
||
|
// GetServerVariable call.
|
||
|
*ppszUser = MySzDupA(szUserName);
|
||
|
*ppszPassword = MySzDupA(pszPassword);
|
||
|
|
||
|
|
||
|
// If AUTH_USER has been granted, check to see if they're an administrator.
|
||
|
// In BASIC we can only check against the name (no NTLM group info)
|
||
|
#ifdef UNDER_CE
|
||
|
if (CheckPassword(wszPassword))
|
||
|
{
|
||
|
*pAuth = GetAuthFromACL(NULL,wszUserName,wszVRootUserList);
|
||
|
return TRUE;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifndef OLD_CE_BUILD
|
||
|
if (g_pVars->m_fNTLMAuth && BasicToNTLM(pNTLMState, wszPassword,wszUserName,pAuth,wszVRootUserList))
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
TraceTag(ttidWebServer, "Failed logon with Basic userpass(%s)-->(%s)(%s)", pszData, szUserName, pszPassword);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
#ifdef OLD_CE_BUILD
|
||
|
// The web server beta doesn't have NTLM, but it uses this file so we
|
||
|
// don't use AuthStub.cpp here
|
||
|
|
||
|
BOOL CHttpRequest::HandleNTLMAuth(PSTR pszNTLMData)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
BOOL NTLMInitLib(PAUTH_NTLM pNTLMState)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
void FreeNTLMHandles(PAUTH_NTLM pNTLMState)
|
||
|
{
|
||
|
;
|
||
|
}
|
||
|
#else
|
||
|
|
||
|
// This function is called 2 times during an NTLM auth session. The first
|
||
|
// time it has the Client user name, domain,... which it forwards to DC or
|
||
|
// looks in registry for (this NTLM detail is transparent to httpd)
|
||
|
// On 2nd time it has the client's response, which either is or is not
|
||
|
// enough to grant access to page. On 2nd pass, free up all NTLM context data.
|
||
|
|
||
|
// FILTER NOTES: On IIS no user name or password info is given to the filter
|
||
|
// on NTLM calls, so neither do we. (WinCE does give BASIC data, though).
|
||
|
// The main reason this is is that
|
||
|
|
||
|
|
||
|
BOOL CHttpRequest::HandleNTLMAuth(PSTR pszNTLMData)
|
||
|
{
|
||
|
DEBUG_CODE_INIT;
|
||
|
BOOL ret = FALSE;
|
||
|
DWORD dwIn;
|
||
|
DWORD dwOut;
|
||
|
BOOL fDone = FALSE;
|
||
|
PBYTE pOutBuf = NULL;
|
||
|
PBYTE pInBuf = NULL; // Base64 decoded data from pszNTLMData
|
||
|
|
||
|
|
||
|
// Are the NTLM libs loaded already?
|
||
|
if (NTLM_NO_INIT_LIB == m_NTLMState.m_Conversation)
|
||
|
{
|
||
|
if ( ! NTLMInitLib(&m_NTLMState))
|
||
|
myretleave(FALSE,94);
|
||
|
}
|
||
|
|
||
|
dwOut = g_pVars->m_cbNTLMMax;
|
||
|
if (NULL == (pOutBuf = MyRgAllocNZ(BYTE,dwOut)))
|
||
|
myleave(360);
|
||
|
|
||
|
if (NULL == m_pszNTLMOutBuf)
|
||
|
{
|
||
|
// We will later Base64Encode pOutBuf later, encoding writes 4 outbut bytes
|
||
|
// for every 3 input bytes
|
||
|
if (NULL == (m_pszNTLMOutBuf = MyRgAllocNZ(CHAR,dwOut*(4/3) + 1)))
|
||
|
myleave(361);
|
||
|
}
|
||
|
|
||
|
dwIn = strlen(pszNTLMData) + 1;
|
||
|
if (NULL == (pInBuf = MyRgAllocNZ(BYTE,dwIn)))
|
||
|
myleave(363);
|
||
|
|
||
|
|
||
|
Base64Decode(pszNTLMData,(PSTR) pInBuf,&dwIn);
|
||
|
|
||
|
// On the 1st pass this gets a data blob to be sent back to the client
|
||
|
// broweser in pOutBuf, which is encoded to m_pszNTLMOutBuf. On the 2nd
|
||
|
// pass it either authenticates or fails.
|
||
|
|
||
|
|
||
|
if (! NTLMServerContext(NULL, &m_NTLMState, pInBuf,
|
||
|
dwIn,pOutBuf,&dwOut,&fDone))
|
||
|
{
|
||
|
// Note: We MUST free the m_pszNTMLOutBuf on 2nd pass failure. If the
|
||
|
// client recieves the blob on a failure
|
||
|
// it will consider the web server to be malfunctioning and will not send
|
||
|
// another set of data, and will not prompt the user for a password.
|
||
|
MyFree(m_pszNTLMOutBuf);
|
||
|
|
||
|
|
||
|
// Setting to DONE will cause the local structs to be freed; they must
|
||
|
// be fresh in case browser attempts to do NTLM again with new user name/
|
||
|
// password on same session. Don't bother unloading the lib.
|
||
|
m_NTLMState.m_Conversation = NTLM_DONE;
|
||
|
myleave(362);
|
||
|
}
|
||
|
|
||
|
if (fDone)
|
||
|
{
|
||
|
TraceTag(ttidWebServer, "NTLM Successfully authenticated user");
|
||
|
m_AuthLevelGranted = GetAuthFromACL(&m_NTLMState,NULL,m_wszVRootUserList);
|
||
|
|
||
|
m_dwAuthFlags |= m_AuthLevelGranted;
|
||
|
m_NTLMState.m_Conversation = NTLM_DONE;
|
||
|
MyFree(m_pszNTLMOutBuf);
|
||
|
|
||
|
myretleave(TRUE,0);
|
||
|
}
|
||
|
Base64Encode(pOutBuf,dwOut,m_pszNTLMOutBuf);
|
||
|
|
||
|
ret = TRUE;
|
||
|
done:
|
||
|
TraceTag(ttidWebServer, "HandleNTLMAuthent died, err = %d, gle = %d",err,GetLastError());
|
||
|
|
||
|
MyFree(pOutBuf);
|
||
|
MyFree(pInBuf);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Sets up NTLM info in the passed data structure. This fcn loads the library,
|
||
|
// gets the function table, and determines the maximum buffer size.
|
||
|
|
||
|
// stolen from osinternal\comm\test\security\sockauth\security.c
|
||
|
BOOL NTLMInitLib(PAUTH_NTLM pNTLMState)
|
||
|
{
|
||
|
DEBUG_CODE_INIT;
|
||
|
FARPROC pInit;
|
||
|
SECURITY_STATUS ss;
|
||
|
PSecPkgInfo pkgInfo;
|
||
|
BOOL ret = FALSE;
|
||
|
PSecurityFunctionTable pLocal = NULL;
|
||
|
|
||
|
// load and initialize the ntlm ssp
|
||
|
//
|
||
|
|
||
|
if (g_pVars->m_pNTLMFuncs)
|
||
|
{
|
||
|
pNTLMState->m_Conversation = NTLM_NO_INIT_CONTEXT;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
g_pVars->m_hNTLMLib = LoadLibrary (NTLM_DLL_NAME);
|
||
|
if (NULL == g_pVars->m_hNTLMLib)
|
||
|
myleave(700);
|
||
|
|
||
|
pInit = (FARPROC) GetProcAddress (g_pVars->m_hNTLMLib, SECURITY_ENTRYPOINT_CE);
|
||
|
if (NULL == pInit)
|
||
|
myleave(701);
|
||
|
|
||
|
pLocal = (PSecurityFunctionTable) pInit ();
|
||
|
if (NULL == pLocal)
|
||
|
myleave(702);
|
||
|
|
||
|
// Query for the package we're interested in
|
||
|
//
|
||
|
ss = pLocal->QuerySecurityPackageInfo (NTLM_PACKAGE_NAME, &pkgInfo);
|
||
|
if (!SEC_SUCCESS(ss))
|
||
|
myleave(703);
|
||
|
|
||
|
g_pVars->m_cbNTLMMax = pkgInfo->cbMaxToken;
|
||
|
pLocal->FreeContextBuffer (pkgInfo);
|
||
|
|
||
|
|
||
|
// The libraries have been set, but pNTLMState's structures are still empty
|
||
|
pNTLMState->m_Conversation = NTLM_NO_INIT_CONTEXT;
|
||
|
|
||
|
TraceTag(ttidWebServer, "NTLM Libs successfully initialized");
|
||
|
|
||
|
ret = TRUE;
|
||
|
done:
|
||
|
if (FALSE == ret)
|
||
|
{
|
||
|
// Don't worry about freeing pkgInfo, it was freed right after creation
|
||
|
// anyway - no chance to go wrong
|
||
|
MyFreeLib (g_pVars->m_hNTLMLib);
|
||
|
|
||
|
// Set everything to false so httpd doesn't think we have legit data later.
|
||
|
memset(pNTLMState, 0 , sizeof(AUTH_NTLM));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_pVars->m_pNTLMFuncs = pLocal; // Flag that shows we've initialized
|
||
|
}
|
||
|
|
||
|
TraceTag(ttidWebServer, "NTLMInitLib failed, err = %d, GLE = 0x%08x",err,
|
||
|
GetLastError());
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Unload the contexts. The library is NOT freed in this call, only freed
|
||
|
// in CHttpRequest destructor.
|
||
|
void FreeNTLMHandles(PAUTH_NTLM pNTLMState)
|
||
|
{
|
||
|
if (NULL == pNTLMState || NULL == g_pVars->m_pNTLMFuncs)
|
||
|
return;
|
||
|
|
||
|
if (pNTLMState->m_fHaveCtxtHandle)
|
||
|
g_pVars->m_pNTLMFuncs->DeleteSecurityContext (&pNTLMState->m_hctxt);
|
||
|
|
||
|
if (pNTLMState->m_fHaveCredHandle)
|
||
|
g_pVars->m_pNTLMFuncs->FreeCredentialHandle (&pNTLMState->m_hcred);
|
||
|
|
||
|
pNTLMState->m_fHaveCredHandle = FALSE;
|
||
|
pNTLMState->m_fHaveCtxtHandle = FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// Given Basic authentication data, we try to "forge" and NTLM request
|
||
|
// This fcn simulates a client+server talking to each other, though it's in the
|
||
|
// same proc. The client is "virtual," doesn't refer to the http client
|
||
|
|
||
|
// pNTLMState is CHttpRequest::m_NTLMState
|
||
|
|
||
|
|
||
|
BOOL BasicToNTLM(PAUTH_NTLM pNTLMState, WCHAR * wszPassword, WCHAR * wszRemoteUser,
|
||
|
AUTHLEVEL *pAuth, WCHAR *wszVRootUserList)
|
||
|
{
|
||
|
DEBUG_CODE_INIT;
|
||
|
|
||
|
AUTH_NTLM ClientState; // forges the client role
|
||
|
AUTH_NTLM ServerState; // forges the server role
|
||
|
BOOL fDone = FALSE;
|
||
|
PBYTE pClientOutBuf = NULL;
|
||
|
PBYTE pServerOutBuf = NULL;
|
||
|
DWORD cbServerBuf;
|
||
|
DWORD cbClientBuf;
|
||
|
|
||
|
DEBUGCHK(wszPassword != NULL && wszRemoteUser != NULL && pNTLMState != NULL);
|
||
|
|
||
|
|
||
|
SEC_WINNT_AUTH_IDENTITY AuthIdentityClient = {
|
||
|
(unsigned short *)wszRemoteUser, wcslen(wszRemoteUser),
|
||
|
(unsigned short *)NTLM_DOMAIN,sizeof(NTLM_DOMAIN)/sizeof(TCHAR) - 1,
|
||
|
(unsigned short *)wszPassword, wcslen(wszPassword),
|
||
|
0}; // dummy domain needed
|
||
|
|
||
|
memset(&ServerState,0,sizeof(AUTH_NTLM));
|
||
|
memset(&ClientState,0,sizeof(AUTH_NTLM));
|
||
|
|
||
|
|
||
|
// 1st pass through, load up library.
|
||
|
if (NTLM_NO_INIT_LIB == pNTLMState->m_Conversation)
|
||
|
{
|
||
|
if ( ! NTLMInitLib(pNTLMState))
|
||
|
myleave(369);
|
||
|
|
||
|
pNTLMState->m_Conversation = NTLM_NO_INIT_CONTEXT;
|
||
|
}
|
||
|
|
||
|
// NTLM auth functions seem to expect that these buffer will be zeroed.
|
||
|
pClientOutBuf = MyRgAllocZ(BYTE,g_pVars->m_cbNTLMMax);
|
||
|
if (NULL == pClientOutBuf)
|
||
|
myleave(370);
|
||
|
|
||
|
pServerOutBuf = MyRgAllocZ(BYTE,g_pVars->m_cbNTLMMax);
|
||
|
if (NULL == pServerOutBuf)
|
||
|
myleave(371);
|
||
|
|
||
|
|
||
|
ServerState.m_Conversation = NTLM_NO_INIT_CONTEXT;
|
||
|
ClientState.m_Conversation = NTLM_NO_INIT_CONTEXT;
|
||
|
|
||
|
cbClientBuf = cbServerBuf = g_pVars->m_cbNTLMMax;
|
||
|
|
||
|
// Main loop that forges client and server talking.
|
||
|
while (!fDone)
|
||
|
{
|
||
|
cbClientBuf = g_pVars->m_cbNTLMMax;
|
||
|
if (! NTLMClientContext(&AuthIdentityClient,&ClientState,pServerOutBuf,
|
||
|
cbServerBuf, pClientOutBuf, &cbClientBuf))
|
||
|
{
|
||
|
myleave(372);
|
||
|
}
|
||
|
|
||
|
cbServerBuf = g_pVars->m_cbNTLMMax;
|
||
|
if (! NTLMServerContext(&AuthIdentityClient,&ServerState, pClientOutBuf,
|
||
|
cbClientBuf, pServerOutBuf, &cbServerBuf, &fDone))
|
||
|
{
|
||
|
myleave(373);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
TraceTag(ttidWebServer, "Unable to convert Basic Auth to NTLM Auth, err = %d",err);
|
||
|
|
||
|
if (fDone)
|
||
|
{
|
||
|
*pAuth = GetAuthFromACL(&ServerState,wszRemoteUser,wszVRootUserList);
|
||
|
}
|
||
|
|
||
|
MyFree(pClientOutBuf);
|
||
|
MyFree(pServerOutBuf);
|
||
|
|
||
|
FreeNTLMHandles(&ServerState);
|
||
|
FreeNTLMHandles(&ClientState);
|
||
|
|
||
|
return fDone;
|
||
|
}
|
||
|
|
||
|
// This calls the DC (or goes to registry in local case), either getting a
|
||
|
// data blob to return to client or granting auth or denying.
|
||
|
|
||
|
// stolen from osinternal\comm\test\security\sockauth\security.c
|
||
|
BOOL NTLMServerContext(
|
||
|
PSEC_WINNT_AUTH_IDENTITY pAuthIdentity,
|
||
|
PAUTH_NTLM pAS, // NTLM state info
|
||
|
BYTE *pIn,
|
||
|
DWORD cbIn,
|
||
|
BYTE *pOut,
|
||
|
DWORD *pcbOut,
|
||
|
BOOL *pfDone)
|
||
|
{
|
||
|
SECURITY_STATUS ss;
|
||
|
TimeStamp Lifetime;
|
||
|
SecBufferDesc OutBuffDesc;
|
||
|
SecBuffer OutSecBuff;
|
||
|
SecBufferDesc InBuffDesc;
|
||
|
SecBuffer InSecBuff;
|
||
|
ULONG ContextAttributes;
|
||
|
|
||
|
|
||
|
if (NTLM_NO_INIT_CONTEXT == pAS->m_Conversation)
|
||
|
{
|
||
|
ss = g_pVars->m_pNTLMFuncs->AcquireCredentialsHandle (
|
||
|
NULL, // principal
|
||
|
NTLM_PACKAGE_NAME,
|
||
|
SECPKG_CRED_INBOUND,
|
||
|
NULL, // LOGON id
|
||
|
pAuthIdentity,
|
||
|
NULL, // get key fn
|
||
|
NULL, // get key arg
|
||
|
&pAS->m_hcred,
|
||
|
&Lifetime
|
||
|
);
|
||
|
if (SEC_SUCCESS (ss))
|
||
|
pAS->m_fHaveCredHandle = TRUE;
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidWebServer, "NTLM AcquireCreds failed: %X", ss);
|
||
|
return(FALSE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// prepare output buffer
|
||
|
//
|
||
|
OutBuffDesc.ulVersion = 0;
|
||
|
OutBuffDesc.cBuffers = 1;
|
||
|
OutBuffDesc.pBuffers = &OutSecBuff;
|
||
|
|
||
|
OutSecBuff.cbBuffer = *pcbOut;
|
||
|
OutSecBuff.BufferType = SECBUFFER_TOKEN;
|
||
|
OutSecBuff.pvBuffer = pOut;
|
||
|
|
||
|
// prepare input buffer
|
||
|
//
|
||
|
InBuffDesc.ulVersion = 0;
|
||
|
InBuffDesc.cBuffers = 1;
|
||
|
InBuffDesc.pBuffers = &InSecBuff;
|
||
|
|
||
|
InSecBuff.cbBuffer = cbIn;
|
||
|
InSecBuff.BufferType = SECBUFFER_TOKEN;
|
||
|
InSecBuff.pvBuffer = pIn;
|
||
|
|
||
|
ss = g_pVars->m_pNTLMFuncs->AcceptSecurityContext (
|
||
|
&pAS->m_hcred,
|
||
|
(pAS->m_Conversation == NTLM_PROCESSING) ? &pAS->m_hctxt : NULL,
|
||
|
&InBuffDesc,
|
||
|
0, // context requirements
|
||
|
SECURITY_NATIVE_DREP,
|
||
|
&pAS->m_hctxt,
|
||
|
&OutBuffDesc,
|
||
|
&ContextAttributes,
|
||
|
&Lifetime
|
||
|
);
|
||
|
if (!SEC_SUCCESS (ss))
|
||
|
{
|
||
|
TraceTag(ttidWebServer, "NTLM init context failed: %X", ss);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
pAS->m_fHaveCtxtHandle = TRUE;
|
||
|
|
||
|
// Complete token -- if applicable
|
||
|
//
|
||
|
if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))
|
||
|
{
|
||
|
if (g_pVars->m_pNTLMFuncs->CompleteAuthToken)
|
||
|
{
|
||
|
ss = g_pVars->m_pNTLMFuncs->CompleteAuthToken (&pAS->m_hctxt, &OutBuffDesc);
|
||
|
if (!SEC_SUCCESS(ss))
|
||
|
{
|
||
|
TraceTag(ttidWebServer, " NTLM complete failed: %X", ss);
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidWebServer, "Complete not supported.");
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*pcbOut = OutSecBuff.cbBuffer;
|
||
|
pAS->m_Conversation = NTLM_PROCESSING;
|
||
|
|
||
|
*pfDone = !((SEC_I_CONTINUE_NEEDED == ss) ||
|
||
|
(SEC_I_COMPLETE_AND_CONTINUE == ss));
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Forges the client browser's part in NTLM communication if the browser
|
||
|
// sent a Basic request. This is used primarily for Netscape clients, which only
|
||
|
// supports Basic.
|
||
|
|
||
|
// stolen from osinternal\comm\test\security\sockauth\security.c
|
||
|
|
||
|
BOOL NTLMClientContext(
|
||
|
PSEC_WINNT_AUTH_IDENTITY pAuthIdentity,
|
||
|
PAUTH_NTLM pAS, // NTLM state info
|
||
|
BYTE *pIn,
|
||
|
DWORD cbIn,
|
||
|
BYTE *pOut,
|
||
|
DWORD *pcbOut)
|
||
|
{
|
||
|
SECURITY_STATUS ss;
|
||
|
TimeStamp Lifetime;
|
||
|
SecBufferDesc OutBuffDesc;
|
||
|
SecBuffer OutSecBuff;
|
||
|
SecBufferDesc InBuffDesc;
|
||
|
SecBuffer InSecBuff;
|
||
|
ULONG ContextAttributes;
|
||
|
|
||
|
if (NTLM_NO_INIT_CONTEXT == pAS->m_Conversation)
|
||
|
{
|
||
|
ss = g_pVars->m_pNTLMFuncs->AcquireCredentialsHandle (
|
||
|
NULL, // principal
|
||
|
NTLM_PACKAGE_NAME,
|
||
|
SECPKG_CRED_OUTBOUND,
|
||
|
NULL, // LOGON id
|
||
|
pAuthIdentity, // auth data
|
||
|
NULL, // get key fn
|
||
|
NULL, // get key arg
|
||
|
&pAS->m_hcred,
|
||
|
&Lifetime
|
||
|
);
|
||
|
if (SEC_SUCCESS (ss))
|
||
|
pAS->m_fHaveCredHandle = TRUE;
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidWebServer, "AcquireCreds failed: %X", ss);
|
||
|
return(FALSE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// prepare output buffer
|
||
|
//
|
||
|
OutBuffDesc.ulVersion = 0;
|
||
|
OutBuffDesc.cBuffers = 1;
|
||
|
OutBuffDesc.pBuffers = &OutSecBuff;
|
||
|
|
||
|
OutSecBuff.cbBuffer = *pcbOut;
|
||
|
OutSecBuff.BufferType = SECBUFFER_TOKEN;
|
||
|
OutSecBuff.pvBuffer = pOut;
|
||
|
|
||
|
// prepare input buffer
|
||
|
|
||
|
if (NTLM_NO_INIT_CONTEXT != pAS->m_Conversation)
|
||
|
{
|
||
|
InBuffDesc.ulVersion = 0;
|
||
|
InBuffDesc.cBuffers = 1;
|
||
|
InBuffDesc.pBuffers = &InSecBuff;
|
||
|
|
||
|
InSecBuff.cbBuffer = cbIn;
|
||
|
InSecBuff.BufferType = SECBUFFER_TOKEN;
|
||
|
InSecBuff.pvBuffer = pIn;
|
||
|
}
|
||
|
|
||
|
ss = g_pVars->m_pNTLMFuncs->InitializeSecurityContext (
|
||
|
&pAS->m_hcred,
|
||
|
(pAS->m_Conversation == NTLM_PROCESSING) ? &pAS->m_hctxt : NULL,
|
||
|
NULL,
|
||
|
0, // context requirements
|
||
|
0, // reserved1
|
||
|
SECURITY_NATIVE_DREP,
|
||
|
(pAS->m_Conversation == NTLM_PROCESSING) ? &InBuffDesc : NULL,
|
||
|
0, // reserved2
|
||
|
&pAS->m_hctxt,
|
||
|
&OutBuffDesc,
|
||
|
&ContextAttributes,
|
||
|
&Lifetime
|
||
|
);
|
||
|
if (!SEC_SUCCESS (ss))
|
||
|
{
|
||
|
TraceTag(ttidWebServer, "init context failed: %X", ss);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
pAS->m_fHaveCtxtHandle = TRUE;
|
||
|
|
||
|
// Complete token -- if applicable
|
||
|
//
|
||
|
if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))
|
||
|
{
|
||
|
if (g_pVars->m_pNTLMFuncs->CompleteAuthToken)
|
||
|
{
|
||
|
ss = g_pVars->m_pNTLMFuncs->CompleteAuthToken (&pAS->m_hctxt, &OutBuffDesc);
|
||
|
if (!SEC_SUCCESS(ss))
|
||
|
{
|
||
|
TraceTag(ttidWebServer, "complete failed: %X", ss);
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidWebServer, "Complete not supported.");
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*pcbOut = OutSecBuff.cbBuffer;
|
||
|
pAS->m_Conversation = NTLM_PROCESSING;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
#endif // OLD_CE_BUILD
|
||
|
|
||
|
|
||
|
// Called after a user has been successfully authenticated with either BASIC or NTLM.
|
||
|
// See \winceos\comm\security\authhlp to see ACL algorithm function/description.
|
||
|
|
||
|
// Our algorithm: AdminUsers reg value is set and user is a member, always grant
|
||
|
// Admin auth. If no AdminUser key is set always grant Admin auth level,
|
||
|
// UNLESS this vroot has a UserList set. If user is a member, then grant
|
||
|
// ADMIN_USER, otherwise set to AUTH_PUBLIC (no auth).
|
||
|
// If user is not in admin list but in UserList for vroot, grant AUTH_USER.
|
||
|
|
||
|
AUTHLEVEL GetAuthFromACL(PAUTH_NTLM pAuth, PWSTR wszUser, PWSTR wszVRootUserList)
|
||
|
{
|
||
|
AUTHLEVEL AuthGranted = AUTH_USER;
|
||
|
PWSTR wsz = NULL; // User (in function)
|
||
|
PWSTR wszGroup = NULL;
|
||
|
SecPkgContext_Names pkgName;
|
||
|
|
||
|
#if defined(UNDER_CE) && !defined (OLD_CE_BUILD)
|
||
|
SecPkgContext_GroupNames ContextGroups;
|
||
|
ContextGroups.msGroupNames = NULL;
|
||
|
#endif
|
||
|
|
||
|
pkgName.sUserName = NULL;
|
||
|
|
||
|
|
||
|
// If we're called from NTLM request (pAuth != NULL) then we need
|
||
|
// to get the user name if we don't have it. On BASIC request, we
|
||
|
// have wszUser name already.
|
||
|
if (pAuth && !wszUser)
|
||
|
{
|
||
|
if ( SEC_SUCCESS(g_pVars->m_pNTLMFuncs->QueryContextAttributes(&(pAuth->m_hctxt),
|
||
|
SECPKG_ATTR_NAMES, &pkgName)))
|
||
|
{
|
||
|
wsz = pkgName.sUserName;
|
||
|
}
|
||
|
else
|
||
|
goto done; // If we can't get user name, don't bother continuing
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
wsz = wszUser;
|
||
|
}
|
||
|
|
||
|
|
||
|
#if defined(UNDER_CE) && !defined (OLD_CE_BUILD)
|
||
|
if (pAuth)
|
||
|
g_pVars->m_pNTLMFuncs->QueryContextAttributes(&(pAuth->m_hctxt),SECPKG_ATTR_GROUP_NAMES,&ContextGroups);
|
||
|
|
||
|
wszGroup = ContextGroups.msGroupNames;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
TraceTag(ttidWebServer, "ADmin Users = %s, wsz = %s, pkgName.sUserName = %s, group name = %s\r\n",
|
||
|
g_pVars->m_wszAdminUsers,wsz,pkgName.sUserName,wszGroup);
|
||
|
|
||
|
// Administrators always get admin access and access to the page, even if
|
||
|
// they're barred in the VRoot list. If there is a vroot list and we fail
|
||
|
// the IsAccessAllowed test, we set the auth granted to 0 - this
|
||
|
// will deny access. If no VRoot user list is set, keep us at AUTH_USER.
|
||
|
|
||
|
if ( !g_pVars->m_wszAdminUsers && !wszVRootUserList)
|
||
|
AuthGranted = AUTH_ADMIN;
|
||
|
else if (g_pVars->m_wszAdminUsers && IsAccessAllowed(wsz,wszGroup,g_pVars->m_wszAdminUsers,FALSE))
|
||
|
AuthGranted = AUTH_ADMIN;
|
||
|
else if (wszVRootUserList && IsAccessAllowed(wsz,wszGroup,wszVRootUserList,FALSE))
|
||
|
{
|
||
|
// If there is no Admin list set, grant admin
|
||
|
if (!g_pVars->m_wszAdminUsers)
|
||
|
AuthGranted = AUTH_ADMIN;
|
||
|
else
|
||
|
AuthGranted = AUTH_USER;
|
||
|
}
|
||
|
else if (wszVRootUserList)
|
||
|
AuthGranted = AUTH_PUBLIC;
|
||
|
|
||
|
done:
|
||
|
if (pkgName.sUserName)
|
||
|
g_pVars->m_pNTLMFuncs->FreeContextBuffer(pkgName.sUserName);
|
||
|
|
||
|
#if defined(UNDER_CE) && !defined (OLD_CE_BUILD)
|
||
|
if (ContextGroups.msGroupNames)
|
||
|
g_pVars->m_pNTLMFuncs->FreeContextBuffer(ContextGroups.msGroupNames);
|
||
|
#endif
|
||
|
|
||
|
TraceTag(ttidWebServer, "HTTPD: GetAuthFromACL Admin Priveleges granted = %d",AuthGranted);
|
||
|
return AuthGranted;
|
||
|
}
|