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
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;
|
|
}
|