Leaked source code of windows server 2003
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.
 
 
 
 
 
 

426 lines
14 KiB

//Copyright (c) Microsoft Corporation. All rights reserved.
/*
security.cpp
*/
#ifdef WHISTLER_BUILD
#include "ntverp.h"
#else
#include <solarver.h>
#endif //WHISTER_BUILD
#include <stddef.h>
#include <common.ver>
#include <debug.h>
#include <TlntUtils.h>
#include <iohandlr.h>
#include <issperr.h>
#include <TelnetD.h>
#include <Session.h>
using namespace _Utils;
using CDebugLevel::TRACE_DEBUGGING;
using CDebugLevel::TRACE_HANDLE;
using CDebugLevel::TRACE_SOCKET;
#pragma warning( disable: 4127 )
#pragma warning( disable: 4706 )
extern HANDLE g_hSyncCloseHandle;
bool CIoHandler::StartNTLMAuth()
{
TimeStamp tsExpiry;
SECURITY_STATUS secStatus;
m_hContext.dwLower = m_hContext.dwUpper = 0 ;
m_hCredential.dwLower = m_hCredential.dwUpper = 0 ;
if ( SEC_E_OK != (secStatus = AcquireCredentialsHandle(
NULL, // name of principal
L"NTLM", // name of package
SECPKG_CRED_BOTH, // flags indicating use
NULL, //PLUID pvLogonID, // pointer to logon identifier
NULL, //PVOID pAuthData, // package-specific data
NULL, //PVOID pGetKeyFn, // pointer to GetKey function
NULL, //PVOID pvGetKeyArgument, // value to pass to GetKey
&m_hCredential, // credential handle
&tsExpiry) ) ) // life time of the returned credentials);
{
return false;
}
secStatus = QuerySecurityPackageInfo(L"NTLM", &m_pspi);
if ( secStatus != SEC_E_OK )
{
return false;
}
return true;
}
bool
CIoHandler::DoNTLMAuth( PUCHAR pBuffer, DWORD dwSize, PUCHAR* pBuf )
{
m_SocketControlState = CIoHandler::STATE_NTLMAUTH;
SECURITY_STATUS secStatus;
SecBufferDesc InBuffDesc;
SecBuffer InSecBuff;
SecBufferDesc OutBuffDesc;
SecBuffer OutSecBuff;
ULONG fContextAttr;
TimeStamp tsExpiry;
__try
{
OutSecBuff.pvBuffer = NULL;
// if we are getting nothing then we fail.
if( dwSize == 0 )
{
goto error;
}
// make sure we get only NTLM now - thats the only thing we support
if( *pBuffer != AUTH_TYPE_NTLM )
{
goto error;
}
if( dwSize < ( 3 + sizeof( SecBuffer ) ))
{
goto error;
}
// get past the auth type and the modifier byte and the Auth scheme.
pBuffer += 3;
dwSize -= 3;
//
// Prepare our Input buffer - Note the server is expecting the client's
// negotiation packet on the first call
//
InBuffDesc.ulVersion = SECBUFFER_VERSION;
InBuffDesc.cBuffers = 1;
InBuffDesc.pBuffers = &InSecBuff;
// Copy the 1st two fields of SecBuffer from pBuffer to pInSecBuffer. Use memcpy because
// pBuffer is not guaranteed to be an aligned pointer.
memcpy((PVOID)&InSecBuff, (PVOID)pBuffer, offsetof(SecBuffer, pvBuffer)); // NO Attack here, Baskar.
// if we don't have enough buffer then let this call return
if( dwSize < InSecBuff.cbBuffer )
{
//m_pReadFromSocketBufferCursor += dwSize;
return false;
}
InSecBuff.pvBuffer = (PVOID)(pBuffer + offsetof(SecBuffer, pvBuffer));
//
// Prepare our output buffer. We use a temporary buffer because
// the real output buffer will most likely need to be uuencoded
//
OutBuffDesc.ulVersion = SECBUFFER_VERSION;
OutBuffDesc.cBuffers = 1;
OutBuffDesc.pBuffers = &OutSecBuff;
OutSecBuff.cbBuffer = m_pspi->cbMaxToken;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = new WCHAR[m_pspi->cbMaxToken];
if( !OutSecBuff.pvBuffer )
{
return false;
}
SfuZeroMemory( OutSecBuff.pvBuffer, m_pspi->cbMaxToken );
secStatus = AcceptSecurityContext(
&m_hCredential, // handle to the credentials
((fDoNTLMAuthFirstTime) ? NULL: &m_hContext), // handle of partially formed context
&InBuffDesc, // pointer to the input buffers
ASC_REQ_REPLAY_DETECT |
ASC_REQ_MUTUAL_AUTH |
ASC_REQ_DELEGATE, // required context attributes
SECURITY_NATIVE_DREP, // data representation on the target
&m_hContext, // receives the new context handle
&OutBuffDesc, // pointer to the output buffers
&fContextAttr, // receives the context attributes
&tsExpiry // receives the life span of the security context
);
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
secStatus = SEC_E_LOGON_DENIED;
}
switch ( secStatus ) {
case SEC_E_OK:
m_bNTLMAuthenticated = true;
// done with the authentication, we need to send an accept to the client.
(*pBuf)[0] = TC_IAC;
(*pBuf)[1] = TC_SB;
(*pBuf)[2] = TO_AUTH;
(*pBuf)[3] = AU_REPLY;
(*pBuf)[4] = AUTH_TYPE_NTLM;
(*pBuf)[5] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_ONE_WAY;
(*pBuf)[6] = NTLM_ACCEPT;
(*pBuf)[7] = TC_IAC;
(*pBuf)[8] = TC_SE;
*pBuf += 9;
m_SocketControlState = CIoHandler::STATE_CHECK_LICENSE;
if( m_bNTLMAuthenticated )
{
GetUserName();
//Needed for logging at logging off
m_pSession->m_fLogonUserResult = SUCCESS;
}
break;
case SEC_I_COMPLETE_NEEDED:
case SEC_I_COMPLETE_AND_CONTINUE:
// these two return values should never be returned for NTLM.
// so we treat them as if we need a continue. we send the data to the client
// and wait for something to come back.
case SEC_I_CONTINUE_NEEDED:
fDoNTLMAuthFirstTime = false;
(*pBuf)[0] = TC_IAC;
(*pBuf)[1] = TC_SB;
(*pBuf)[2] = TO_AUTH;
(*pBuf)[3] = AU_REPLY;
(*pBuf)[4] = AUTH_TYPE_NTLM;
(*pBuf)[5] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_ONE_WAY;
(*pBuf)[6] = NTLM_CHALLENGE;
*pBuf += 7;
{
DWORD dwResultSize = sizeof( OutSecBuff ) - sizeof( LPSTR );
StuffEscapeIACs( *pBuf, ( PUCHAR ) &OutSecBuff, &dwResultSize );
*pBuf += dwResultSize;
dwResultSize = OutSecBuff.cbBuffer;
StuffEscapeIACs( *pBuf, ( PUCHAR ) OutSecBuff.pvBuffer, &dwResultSize );
*pBuf += dwResultSize;
}
*(*pBuf) = TC_IAC;
*pBuf +=1;
*(*pBuf) = TC_SE;
*pBuf +=1;
break;
default:
#ifdef LOGGING_ENABLED
m_pSession->LogIfOpted( FAIL, LOGON, true );
#endif
// AcceptSecurityContext returned a value which we don't like.
// We Reject the NTLM authentication and then continue with the clear text user name
// and password.
(*pBuf)[0] = TC_IAC;
(*pBuf)[1] = TC_SB;
(*pBuf)[2] = TO_AUTH;
(*pBuf)[3] = AU_REPLY;
(*pBuf)[4] = AUTH_TYPE_NTLM;
(*pBuf)[5] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_ONE_WAY;
(*pBuf)[6] = NTLM_REJECT;
(*pBuf)[7] = TC_IAC;
(*pBuf)[8] = TC_SE;
*pBuf += 9;
strncpy( (char *)*pBuf, NTLM_LOGON_FAIL,MAX_READ_SOCKET_BUFFER); // No Attack, internal Baskar ?
*pBuf += strlen(NTLM_LOGON_FAIL);
switch( secStatus )
{
case SEC_E_INVALID_TOKEN:
case SEC_E_INVALID_HANDLE:
case SEC_E_INTERNAL_ERROR:
strncpy( (char *)*pBuf, INVALID_TOKEN_OR_HANDLE,MAX_READ_SOCKET_BUFFER); // No Attack, internal Baskar ?
*pBuf += strlen(INVALID_TOKEN_OR_HANDLE);
break;
case SEC_E_LOGON_DENIED:
strncpy( (char *)*pBuf, LOGON_DENIED,MAX_READ_SOCKET_BUFFER); // No Attack, internal Baskar ?
*pBuf += strlen(LOGON_DENIED);
break;
case SEC_E_NO_AUTHENTICATING_AUTHORITY:
strncpy( (char *)*pBuf, NO_AUTHENTICATING_AUTHORITY,MAX_READ_SOCKET_BUFFER); // No Attack, internal Baskar ?
*pBuf += strlen(NO_AUTHENTICATING_AUTHORITY);
break;
default:
strncpy( (char *)*pBuf, NTLM_REJECT_STR,MAX_READ_SOCKET_BUFFER); // No Attack, internal Baskar ?
*pBuf += strlen(NTLM_REJECT_STR);
break;
}
strncpy( (char *)*pBuf, USE_PASSWD,MAX_READ_SOCKET_BUFFER); // No Attack, internal Baskar ?
*pBuf += strlen(USE_PASSWD);
error:
char* p = (char*)*pBuf;
if( m_pSession->m_dwNTLMSetting == NTLM_ONLY )
{
sprintf(p, "%s%s", NTLM_ONLY_STR, TERMINATE); // No Attack, internal Baskar ?
*pBuf += strlen(p);
m_SocketControlState = CIoHandler::STATE_TERMINATE;
m_pSession->CIoHandler::m_fShutDownAfterIO = true;
}
else
{
m_SocketControlState = CIoHandler::STATE_BANNER_FOR_AUTH;
}
}
if ( OutSecBuff.pvBuffer != NULL )
delete [] OutSecBuff.pvBuffer;
return true;
}
bool
CIoHandler::GetUserName()
{
bool success = false;
int iStatus = 0;
if ( m_pSession->CIoHandler::m_bNTLMAuthenticated )
{
HANDLE hToken = NULL;
HANDLE hTempToken = NULL;
if (SEC_E_OK == ImpersonateSecurityContext(&m_pSession->CIoHandler::m_hContext))
{
if (OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY,
FALSE,
&hTempToken
))
{
if (DuplicateTokenEx(
hTempToken,
MAXIMUM_ALLOWED, //TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY,
NULL,
SecurityImpersonation,
TokenPrimary,
&hToken
))
{
DWORD dwSizeReqd;
TOKEN_USER *tokenData;
GetTokenInformation(
hToken,
TokenUser,
NULL,
0,
&dwSizeReqd
); // This call must fail with insufficient buffer
tokenData = (TOKEN_USER*) new BYTE[dwSizeReqd];
//allocate that memory
if (NULL != tokenData)
{
//actually get the user info
if (0 != GetTokenInformation(
hToken,
TokenUser,
(LPVOID)tokenData,
dwSizeReqd,
&dwSizeReqd
))
{
//convert user SID into a name and domain
SID_NAME_USE sidType;
DWORD dwStrSize1 = MAX_PATH + 1;
DWORD dwStrSize2 = MAX_PATH + 1;
WCHAR szUser [ MAX_PATH + 1 ];
WCHAR szDomain [ MAX_PATH + 1 ];
if(LookupAccountSid( NULL, tokenData->User.Sid,
szUser, &dwStrSize1,
szDomain, &dwStrSize2, &sidType ) )
{
dwStrSize2++; //To account for null char at the end
dwStrSize1++;
//LookupAccountSid seems to return data as per 1252. Convert accordingly
_chVERIFY2( iStatus = WideCharToMultiByte( GetConsoleCP(), 0, szUser,
-1, m_pSession->m_pszUserName, dwStrSize1, NULL, NULL ) );
_chVERIFY2( iStatus = WideCharToMultiByte( GetConsoleCP(), 0, szDomain,
-1, m_pSession->m_pszDomain, dwStrSize2, NULL, NULL ) );
wcscpy(m_pSession->m_szDomain,szDomain);
success = true;
}
else
{
_TRACE( TRACE_DEBUGGING, "Error: LookupAccountSid()" );
}
}
else
{
_TRACE( TRACE_DEBUGGING, "Error: GetTokenInformation()" );
}
delete [] tokenData;
}
m_pSession->m_hToken = hToken;
}
else
{
_TRACE( TRACE_DEBUGGING, "Error: DuplicateTokenEx() - 0x%lx",
GetLastError());
_chASSERT( 0 );
}
TELNET_CLOSE_HANDLE(hTempToken);
}
else
{
_TRACE( TRACE_DEBUGGING, "Error: OpenThreadToken() - 0x%lx",
GetLastError());
_chASSERT( 0 );
}
if(SEC_E_OK != RevertSecurityContext( &m_pSession->CIoHandler::m_hContext ))
{
_TRACE( TRACE_DEBUGGING, "Error: RevertSecurityContext() - "
"0x%lx", GetLastError());
_chASSERT( 0 );
}
}
else
{
_TRACE( TRACE_DEBUGGING, "Error: ImpersonateSecurityContext() - "
"0x%lx", GetLastError());
_chASSERT( 0 );
}
}
return success;
}