📄 auth.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*--
Module Name: AUTH.CPP
Abstract: Authentication
--*/
#include "httpd.h"
const DWORD g_fAuthModule = TRUE;
//**********************************************************************
// Authentication
//**********************************************************************
#define SECURITY_DOMAIN TEXT("Domain") // dummy data.
#define SEC_SUCCESS(Status) ((Status) >= 0)
BOOL SecurityServerContext(
PSEC_WINNT_AUTH_IDENTITY pAuthIdentity,
PAUTH_STATE pAS,BYTE *pIn, DWORD cbIn, BYTE *pOut,
DWORD *pcbOut, BOOL *pfDone, const TCHAR *szPackageName
);
BOOL SecurityClientContext(
PSEC_WINNT_AUTH_IDENTITY pAuthIdentity,
PAUTH_STATE pAS, BYTE *pIn, DWORD cbIn,
BYTE *pOut, DWORD *pcbOut
);
void CGlobalVariables::InitAuthentication(CReg *pReg) {
m_fBasicAuth = pReg->ValueDW(RV_BASIC);
// On root site, always load the security library whether we are using NTLM/negotiate
// or not. Do this in the case that one of our sub-websites is using NTLM/negotiate,
// so we can point it back to the global function pointer table.
if (! InitSecurityLib())
return;
m_fNTLMAuth = pReg->ValueDW(RV_NTLM);
m_fNegotiateAuth = pReg->ValueDW(RV_NEGOTIATE);
}
// Called after we've mapped the virtual root.
BOOL CHttpRequest::HandleAuthentication(void) {
if (m_pszAuthType && m_pszRawRemoteUser) {
if (AllowBasic() && 0==strcmpi(m_pszAuthType, cszBasic)) {
FreePersistedAuthInfo();
HandleBasicAuth(m_pszRawRemoteUser);
}
else if (AllowNTLM() && 0==strcmpi(m_pszAuthType, cszNTLM)) {
FreePersistedAuthInfo();
if ((m_AuthState.m_AuthType != AUTHTYPE_NONE) && (m_AuthState.m_AuthType != AUTHTYPE_NTLM))
return FALSE;
m_AuthState.m_AuthType = AUTHTYPE_NTLM;
HandleNTLMNegotiateAuth(m_pszRawRemoteUser,TRUE);
}
else if (AllowNegotiate() && 0==strcmpi(m_pszAuthType,cszNegotiate)) {
FreePersistedAuthInfo();
if ((m_AuthState.m_AuthType != AUTHTYPE_NONE) && (m_AuthState.m_AuthType != AUTHTYPE_NEGOTIATE))
return FALSE;
m_AuthState.m_AuthType = AUTHTYPE_NEGOTIATE;
HandleNTLMNegotiateAuth(m_pszRawRemoteUser,FALSE);
}
else {
DEBUGMSG(ZONE_PARSER,(L"HTTPD: Unknown authorization type requested or requested type not enabled\r\n"));
m_AuthLevelGranted = DeterminePermissionGranted(GetUserList(),m_AuthLevelGranted);
}
}
else if (IsSecure() && m_SSLInfo.m_pClientCertContext) {
HandleSSLClientCertCheck();
}
else {
m_AuthLevelGranted = DeterminePermissionGranted(GetUserList(),m_AuthLevelGranted);
}
return TRUE;
}
// For calls to Basic Authentication, only called during the parsing stage.
BOOL CHttpRequest::HandleBasicAuth(PSTR pszData) {
char szUserName[MAXUSERPASS];
PSTR pszFinalUserName;
DWORD dwLen = sizeof(szUserName);
DEBUGCHK((m_pszRemoteUser == NULL) && (m_pszPassword == NULL));
m_AuthLevelGranted = AUTH_PUBLIC;
m_pszRemoteUser = NULL;
if (strlen(pszData) >= MAXUSERPASS) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: Base64 data > 256 bytes on Basic Auth, failing request\r\n"));
return FALSE;
}
// decode the base64
if (! svsutil_Base64Decode(pszData,(void*)szUserName,sizeof(szUserName),NULL,TRUE)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: Base64 data is invalid, failing request\r\n"));
return FALSE;
}
// find the password
PSTR pszPassword = strchr(szUserName, ':');
if(!pszPassword) {
DEBUGMSG(ZONE_ERROR, (L"HTTPD: Bad Format for Basic userpass(%a)-->(%a)\r\n", pszData, szUserName));
return FALSE;
}
*pszPassword++ = 0; // seperate user & pass
// Some clients prepend a domain name or machine name in front of user name, preceeding by a '\'.
// Strip this out.
if (NULL != (pszFinalUserName = strchr(szUserName,'\\'))) {
DEBUGMSG(ZONE_AUTH,(L"HTTPD: UserName <<%s>> has \\ in it, skip preceeding data\r\n",szUserName));
pszFinalUserName++;
}
else
pszFinalUserName = szUserName;
// We save the data no matter what, for logging purposes and for possible
// GetServerVariable call.
m_pszRemoteUser = MySzDupA(pszFinalUserName);
m_pszPassword = MySzDupA(pszPassword);
if ((NULL==m_pszRemoteUser) || (NULL==m_pszPassword))
return FALSE;
if (! CallAuthFilterIfNeeded())
return FALSE;
WCHAR wszPassword[MAXUSERPASS];
WCHAR wszRemoteUser[MAXUSERPASS];
MyA2W(m_pszPassword, wszPassword, CCHSIZEOF(wszPassword));
MyA2W(m_pszRemoteUser,wszRemoteUser, CCHSIZEOF(wszRemoteUser));
// BasicToNTLM and helpers requires m_wszRemoteUser set.
if (NULL == (m_wszRemoteUser = MySzDupW(wszRemoteUser)))
return FALSE;
if (BasicToNTLM(wszPassword))
return TRUE;
// On failure, remove m_wszRemoteUser
ZeroAndFree(m_wszRemoteUser);
m_AuthLevelGranted = AUTH_PUBLIC;
DEBUGMSG(ZONE_ERROR, (L"HTTPD: Failed logon with Basic userpass(%a)-->(%a)(%a)\r\n", pszData, pszFinalUserName, pszPassword));
return FALSE;
}
BOOL CGlobalVariables::InitSecurityLib(void) {
DEBUG_CODE_INIT;
FARPROC pInit;
SECURITY_STATUS ss = 0;
BOOL ret = FALSE;
PSecurityFunctionTable pSecFun;
if (!m_fRootSite) {
// No need to reload DLL and get new interface ptrs for each website.
if (g_pVars->m_hSecurityLib) {
memcpy(&m_SecurityInterface,&g_pVars->m_SecurityInterface,sizeof(m_SecurityInterface));
m_cbMaxToken = g_pVars->m_cbMaxToken;
return TRUE;
}
return FALSE;
}
DEBUGCHK(!m_hSecurityLib);
m_hSecurityLib = LoadLibrary (SECURITY_DLL_NAME);
if (NULL == m_hSecurityLib) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: Unable to load %s, GLE=0x%08x\r\n",SECURITY_DLL_NAME,GetLastError()));
myleave(700);
}
pInit = (FARPROC) GetProcAddress (m_hSecurityLib, SECURITY_ENTRYPOINT_CE);
if (NULL == pInit) {
DEBUGCHK(0);
myleave(701);
}
pSecFun = (PSecurityFunctionTable) pInit ();
if (NULL == pSecFun) {
DEBUGCHK(0);
myleave(702);
}
memcpy(&m_SecurityInterface,pSecFun,sizeof(SecurityFunctionTable));
PSecPkgInfo pkgInfo;
ss = m_SecurityInterface.QuerySecurityPackageInfo (NTLM_PACKAGE_NAME, &pkgInfo);
if (!SEC_SUCCESS(ss)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: QuerySecurityPackageInfo failed, GLE=0x%08x\r\n",GetLastError()));
myleave(703);
}
m_cbMaxToken = pkgInfo->cbMaxToken;
m_SecurityInterface.FreeContextBuffer (pkgInfo);
m_SecurityInterface.EncryptMessage = (ENCRYPT_MESSAGE_FN) m_SecurityInterface.Reserved3;
m_SecurityInterface.DecryptMessage = (DECRYPT_MESSAGE_FN) m_SecurityInterface.Reserved4;
DEBUGMSG(ZONE_AUTH,(L"HTTPD: Security Library successfully initialized\r\n"));
ret = TRUE;
done:
if (FALSE == ret) {
CReg reg(HKEY_LOCAL_MACHINE,RK_HTTPD);
MyFreeLib (m_hSecurityLib);
m_hSecurityLib = 0;
// only complain if the user requested NTLM.
if (reg.ValueDW(RV_NTLM) || reg.ValueDW(RV_NEGOTIATE)) {
if (ss == 0) // if unset above, then API that failed uses SetLastError().
ss = GetLastError();
m_pLog->WriteEvent(IDS_HTTPD_AUTH_INIT_ERROR,ss);
}
}
return ret;
}
// 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).
BOOL CHttpRequest::HandleNTLMNegotiateAuth(PSTR pszSecurityToken, BOOL fUseNTLM) {
DEBUG_CODE_INIT;
BOOL ret = FALSE;
DWORD dwIn;
DWORD dwOut;
DWORD dwSecurityOutBufLen;
BOOL fDone = FALSE;
PBYTE pOutBuf = NULL;
PBYTE pInBuf = NULL; // Base64 decoded data from pszSecurityToken
const TCHAR *szPackageName = fUseNTLM ? NTLM_PACKAGE_NAME : NEGOTIATE_PACKAGE_NAME ;
DEBUGCHK((fUseNTLM && AllowNTLM()) || (!fUseNTLM && AllowNegotiate()));
if (!g_pVars->m_hSecurityLib)
myretleave(FALSE,94);
dwOut = g_pVars->m_cbMaxToken;
dwSecurityOutBufLen = 1 + (dwOut + 2) / 3 * 4;
if (NULL == (pOutBuf = MyRgAllocNZ(BYTE,dwOut)))
myleave(360);
if (NULL == m_pszSecurityOutBuf) {
// We will later Base64Encode pOutBuf, encoding writes 4 outbut bytes
// for every 3 input bytes
if (NULL == (m_pszSecurityOutBuf = MyRgAllocNZ(CHAR,dwSecurityOutBufLen)))
myleave(361);
}
dwIn = strlen(pszSecurityToken) + 1;
if (NULL == (pInBuf = MyRgAllocNZ(BYTE,dwIn)))
myleave(363);
if (! svsutil_Base64Decode(pszSecurityToken,pInBuf,dwIn,&dwIn,FALSE))
myleave(365);
// On the 1st pass this gets a data blob to be sent back to the client
// broweser in pOutBuf, which is encoded to m_pszSecurityOutBuf. On the 2nd
// pass it either authenticates or fails.
if (! SecurityServerContext(NULL, &m_AuthState, pInBuf,
dwIn,pOutBuf,&dwOut,&fDone,szPackageName))
{
// Note: We MUST free the m_pszNTMLOutBuf on 2nd pass failure. If the
// client receives 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_pszSecurityOutBuf);
// 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_AuthState.m_Stage = SEC_STATE_DONE;
myleave(362);
}
if (fDone) {
DEBUGMSG(ZONE_AUTH,(L"HTTPD: Successfully authenticated user\r\n"));
GetUserAndGroupInfo(&m_AuthState);
m_AuthLevelGranted = DeterminePermissionGranted(GetUserList(),AUTH_USER);
m_dwAuthFlags |= m_AuthLevelGranted;
m_AuthState.m_Stage = SEC_STATE_DONE;
MyFree(m_pszSecurityOutBuf);
myretleave(TRUE,0);
}
ret = svsutil_Base64Encode(pOutBuf, dwOut, m_pszSecurityOutBuf, dwSecurityOutBufLen, NULL);
done:
DEBUGMSG_ERR(ZONE_ERROR,(L"HTTPD: HandleNTLMNegotiateAuth died, err = %d, gle = %d\r\n",err,GetLastError()));
MyFree(pOutBuf);
MyFree(pInBuf);
return ret;
}
// Unload the contexts. The library is NOT freed in this call, only freed
// in CHttpRequest destructor.
void FreeSecContextHandles(PAUTH_STATE pAuthState) {
if (NULL == pAuthState || NULL == g_pVars->m_hSecurityLib)
return;
if (pAuthState->m_fHaveCtxtHandle)
g_pVars->m_SecurityInterface.DeleteSecurityContext (&pAuthState->m_hctxt);
if (pAuthState->m_fHaveCredHandle)
g_pVars->m_SecurityInterface.FreeCredentialHandle (&pAuthState->m_hcred);
pAuthState->m_fHaveCredHandle = FALSE;
pAuthState->m_fHaveCtxtHandle = FALSE;
pAuthState->m_AuthType = AUTHTYPE_NONE;
}
// 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
// pAuthState is CHttpRequest::m_AuthState
BOOL CHttpRequest::BasicToNTLM(WCHAR * wszPassword) {
DEBUG_CODE_INIT;
AUTH_STATE ClientState; // forges the client role
AUTH_STATE ServerState; // forges the server role
BOOL fDone = FALSE;
PBYTE pClientOutBuf = NULL;
PBYTE pServerOutBuf = NULL;
DWORD cbServerBuf;
DWORD cbClientBuf;
DEBUGCHK(wszPassword != NULL && m_wszRemoteUser != NULL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -