2788 lines
84 KiB
C
2788 lines
84 KiB
C
|
/* Copyright (c) 1993, Microsoft Corporation, all rights reserved
|
||
|
**
|
||
|
** raschap.c
|
||
|
** Remote Access PPP Challenge Handshake Authentication Protocol
|
||
|
** Core routines
|
||
|
**
|
||
|
** 11/05/93 Steve Cobb
|
||
|
**
|
||
|
**
|
||
|
** ---------------------------------------------------------------------------
|
||
|
** Regular
|
||
|
** Client Server
|
||
|
** ---------------------------------------------------------------------------
|
||
|
**
|
||
|
** <- Challenge (SendWithTimeout,ID)
|
||
|
** Response (SendWithTimeout,ID) ->
|
||
|
** <- Result (OK:SendAndDone, ID)
|
||
|
**
|
||
|
** ---------------------------------------------------------------------------
|
||
|
** Retry logon
|
||
|
** Client Server
|
||
|
** ---------------------------------------------------------------------------
|
||
|
**
|
||
|
** <- Challenge (SendWithTimeout,ID)
|
||
|
** Response (SendWithTimeout,ID) ->
|
||
|
** <- Result (Fail:SendWithTimeout2,ID,R=1)
|
||
|
** R=1 implies challenge of last+23
|
||
|
** Response (SendWithTimeout,++ID) ->
|
||
|
** to last challenge+23
|
||
|
** or C=xxxxxxxx if present
|
||
|
** e.g. Chicago server
|
||
|
** <- Result (Fail:SendAndDone,ID,R=0)
|
||
|
**
|
||
|
** ---------------------------------------------------------------------------
|
||
|
** Change password
|
||
|
** Client Server
|
||
|
** ---------------------------------------------------------------------------
|
||
|
**
|
||
|
** <- Challenge (SendWithTimeout,ID)
|
||
|
** Response (SendWithTimeout,ID) ->
|
||
|
** <- Result (Fail:SendWithTimeout2,ID,R=1,V=2)
|
||
|
** E=ERROR_PASSWD_EXPIRED
|
||
|
** ChangePw (SendWithTimeout,++ID) ->
|
||
|
** to last challenge
|
||
|
** <- Result (Fail:SendAndDone,ID,R=0)
|
||
|
**
|
||
|
** Note: Retry is never allowed after Change Password. Change Password may
|
||
|
** occur on a retry. ChangePw2 is sent if Result included V=2 (or
|
||
|
** higher), while ChangePw1 is sent if V<2 or is not provided.
|
||
|
**
|
||
|
** ---------------------------------------------------------------------------
|
||
|
** ChangePw1 packet
|
||
|
** ---------------------------------------------------------------------------
|
||
|
**
|
||
|
** 1-octet : Code (=CHAP_ChangePw1)
|
||
|
** 1-octet : Identifier
|
||
|
** 2-octet : Length (=72)
|
||
|
** 16-octets : New LM OWF password encrypted with challenge
|
||
|
** 16-octets : Old LM OWF password encrypted with challenge
|
||
|
** 16-octets : New NT OWF password encrypted with challenge
|
||
|
** 16-octets : Old NT OWF password encrypted with challenge
|
||
|
** 2-octets : New password length in bytes
|
||
|
** 2-octets : Flags (1=NT forms present)
|
||
|
**
|
||
|
** Note: Encrypting with the challenge is not good because it is not secret
|
||
|
** from line snoopers. This bug got ported to NT 3.5 from AMB. It is
|
||
|
** fixed in the V2 packet where everything depends on knowledge of the
|
||
|
** old NT OWF password, which is a proper secret.
|
||
|
**
|
||
|
** ---------------------------------------------------------------------------
|
||
|
** ChangePw2 packet
|
||
|
** ---------------------------------------------------------------------------
|
||
|
**
|
||
|
** 1-octet : Code (=CHAP_ChangePw2)
|
||
|
** 1-octet : Identifier
|
||
|
** 2-octet : Length (=1070)
|
||
|
** 516-octets : New password encrypted with old NT OWF password
|
||
|
** 16-octets : Old NT OWF password encrypted with new NT OWF password
|
||
|
** 516-octets : New password encrypted with old LM OWF password
|
||
|
** 16-octets : Old LM OWF password encrypted with new NT OWF password
|
||
|
** 24-octets : LM challenge response
|
||
|
** 24-octets : NT challenge response
|
||
|
** 2-octet : Flags
|
||
|
*/
|
||
|
|
||
|
#include <nt.h>
|
||
|
#include <ntrtl.h>
|
||
|
#include <nturtl.h>
|
||
|
#include <ntlsa.h>
|
||
|
#include <ntmsv1_0.h>
|
||
|
#include <ntsamp.h>
|
||
|
#include <crypt.h>
|
||
|
#include <windows.h>
|
||
|
#include <lmcons.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <rasman.h>
|
||
|
#include <pppcp.h>
|
||
|
#include <raserror.h>
|
||
|
#include <rtutils.h>
|
||
|
#include <rasauth.h>
|
||
|
#define INCL_PWUTIL
|
||
|
#define INCL_HOSTWIRE
|
||
|
#define INCL_CLSA
|
||
|
#define INCL_RASAUTHATTRIBUTES
|
||
|
#define INCL_MISC
|
||
|
#include <ppputil.h>
|
||
|
#define RASCHAPGLOBALS
|
||
|
#include "sha.h"
|
||
|
#include "raschap.h"
|
||
|
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------------
|
||
|
** External entry points
|
||
|
**---------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
DWORD
|
||
|
ChapInit(
|
||
|
IN BOOL fInitialize )
|
||
|
|
||
|
/* Called to initialize/uninitialize this CP. In the former case,
|
||
|
** fInitialize will be TRUE; in the latter case, it will be FALSE.
|
||
|
*/
|
||
|
{
|
||
|
DWORD dwRetCode;
|
||
|
|
||
|
if ( fInitialize )
|
||
|
{
|
||
|
if (0 == g_dwRefCount)
|
||
|
{
|
||
|
g_dwTraceIdChap = TraceRegisterA( "RASCHAP" );
|
||
|
|
||
|
if ( g_hLsa == INVALID_HANDLE_VALUE )
|
||
|
{
|
||
|
if ( ( dwRetCode = InitLSA() ) != NO_ERROR )
|
||
|
{
|
||
|
return( dwRetCode );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the computer name for local identification to send in
|
||
|
// chap challenges
|
||
|
//
|
||
|
|
||
|
{
|
||
|
DWORD dwLength = sizeof( szComputerName );
|
||
|
|
||
|
if ( !GetComputerNameA( szComputerName, &dwLength ) )
|
||
|
{
|
||
|
return( GetLastError() );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
ChapChangeNotification();
|
||
|
}
|
||
|
|
||
|
g_dwRefCount++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_dwRefCount--;
|
||
|
|
||
|
if (0 == g_dwRefCount)
|
||
|
{
|
||
|
if ( g_dwTraceIdChap != INVALID_TRACEID )
|
||
|
{
|
||
|
TraceDeregisterA( g_dwTraceIdChap );
|
||
|
|
||
|
g_dwTraceIdChap = INVALID_TRACEID;
|
||
|
}
|
||
|
|
||
|
if ( g_hLsa != INVALID_HANDLE_VALUE )
|
||
|
{
|
||
|
EndLSA();
|
||
|
|
||
|
g_hLsa = INVALID_HANDLE_VALUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(NO_ERROR);
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
ChapChangeNotification(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
return( NO_ERROR );
|
||
|
}
|
||
|
|
||
|
DWORD APIENTRY
|
||
|
RasCpEnumProtocolIds(
|
||
|
OUT DWORD* pdwProtocolIds,
|
||
|
OUT DWORD* pcProtocolIds )
|
||
|
|
||
|
/* RasCpEnumProtocolIds entry point called by the PPP engine by name. See
|
||
|
** RasCp interface documentation.
|
||
|
*/
|
||
|
{
|
||
|
TRACE("RasCpEnumProtocolIds");
|
||
|
|
||
|
pdwProtocolIds[ 0 ] = (DWORD )PPP_CHAP_PROTOCOL;
|
||
|
*pcProtocolIds = 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
RasCpGetInfo(
|
||
|
IN DWORD dwProtocolId,
|
||
|
OUT PPPCP_INFO* pInfo )
|
||
|
|
||
|
/* ChapGetInfo entry point called by the PPP engine. See RasCp
|
||
|
** interface documentation.
|
||
|
*/
|
||
|
{
|
||
|
memset( pInfo, '\0', sizeof(*pInfo) );
|
||
|
lstrcpy( pInfo->SzProtocolName, "CHAP" );
|
||
|
|
||
|
pInfo->Protocol = (DWORD )PPP_CHAP_PROTOCOL;
|
||
|
pInfo->Recognize = MAXCHAPCODE + 1;
|
||
|
pInfo->RasCpInit = ChapInit;
|
||
|
pInfo->RasCpBegin = ChapBegin;
|
||
|
pInfo->RasCpEnd = ChapEnd;
|
||
|
pInfo->RasApMakeMessage = ChapMakeMessage;
|
||
|
pInfo->RasCpChangeNotification = ChapChangeNotification;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
ChapBegin(
|
||
|
OUT VOID** ppWorkBuf,
|
||
|
IN VOID* pInfo )
|
||
|
|
||
|
/* RasCpBegin entry point called by the PPP engine thru the passed
|
||
|
** address. See RasCp interface documentation.
|
||
|
*/
|
||
|
{
|
||
|
DWORD dwErr;
|
||
|
PPPAP_INPUT* pInput = (PPPAP_INPUT* )pInfo;
|
||
|
CHAPWB* pwb;
|
||
|
|
||
|
TRACE2("ChapBegin(fS=%d,bA=0x%x)",pInput->fServer,*(pInput->pAPData));
|
||
|
|
||
|
if ( ( *(pInput->pAPData) != PPP_CHAP_DIGEST_MSEXT ) &&
|
||
|
( *(pInput->pAPData) != PPP_CHAP_DIGEST_MD5 ) &&
|
||
|
( *(pInput->pAPData) != PPP_CHAP_DIGEST_MSEXT_NEW ) )
|
||
|
{
|
||
|
TRACE("Bogus digest");
|
||
|
return ERROR_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
/* Allocate work buffer.
|
||
|
*/
|
||
|
if (!(pwb = (CHAPWB* )LocalAlloc( LPTR, sizeof(CHAPWB) )))
|
||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
||
|
|
||
|
pwb->fServer = pInput->fServer;
|
||
|
pwb->hport = pInput->hPort;
|
||
|
pwb->bAlgorithm = *(pInput->pAPData);
|
||
|
pwb->fConfigInfo = pInput->fConfigInfo;
|
||
|
pwb->chSeed = GEN_RAND_ENCODE_SEED;
|
||
|
|
||
|
if (pwb->fServer)
|
||
|
{
|
||
|
pwb->dwTriesLeft = pInput->dwRetries;
|
||
|
|
||
|
pwb->hPort = pInput->hPort;
|
||
|
|
||
|
pwb->dwInitialPacketId = pInput->dwInitialPacketId;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ((dwErr = StoreCredentials( pwb, pInput )) != 0)
|
||
|
{
|
||
|
LocalFree( (HLOCAL )pwb);
|
||
|
return dwErr;
|
||
|
}
|
||
|
|
||
|
pwb->Luid = pInput->Luid;
|
||
|
}
|
||
|
|
||
|
pwb->state = CS_Initial;
|
||
|
|
||
|
/* Register work buffer with engine.
|
||
|
*/
|
||
|
*ppWorkBuf = pwb;
|
||
|
TRACE("ChapBegin done.");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
ChapEnd(
|
||
|
IN VOID* pWorkBuf )
|
||
|
|
||
|
/* RasCpEnd entry point called by the PPP engine thru the passed address.
|
||
|
** See RasCp interface documentation.
|
||
|
*/
|
||
|
{
|
||
|
TRACE("ChapEnd");
|
||
|
|
||
|
if ( pWorkBuf != NULL )
|
||
|
{
|
||
|
CHAPWB* pwb = (CHAPWB* )pWorkBuf;
|
||
|
|
||
|
if ( pwb->pUserAttributes != NULL )
|
||
|
{
|
||
|
RasAuthAttributeDestroy( pwb->pUserAttributes );
|
||
|
}
|
||
|
|
||
|
if ( pwb->pMPPEKeys != NULL )
|
||
|
{
|
||
|
RasAuthAttributeDestroy( pwb->pMPPEKeys );
|
||
|
}
|
||
|
|
||
|
/* Nuke any credentials in memory.
|
||
|
*/
|
||
|
ZeroMemory( pWorkBuf, sizeof(CHAPWB) );
|
||
|
|
||
|
LocalFree( (HLOCAL )pWorkBuf );
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
ChapMakeMessage(
|
||
|
IN VOID* pWorkBuf,
|
||
|
IN PPP_CONFIG* pReceiveBuf,
|
||
|
OUT PPP_CONFIG* pSendBuf,
|
||
|
IN DWORD cbSendBuf,
|
||
|
OUT PPPAP_RESULT* pResult,
|
||
|
IN PPPAP_INPUT* pInput )
|
||
|
|
||
|
/* RasApMakeMessage entry point called by the PPP engine thru the passed
|
||
|
** address. See RasCp interface documentation.
|
||
|
*/
|
||
|
{
|
||
|
CHAPWB* pwb = (CHAPWB* )pWorkBuf;
|
||
|
|
||
|
TRACE1("ChapMakeMessage,RBuf=%p",pReceiveBuf);
|
||
|
|
||
|
return
|
||
|
(pwb->fServer)
|
||
|
? ChapSMakeMessage(
|
||
|
pwb, pReceiveBuf, pSendBuf, cbSendBuf, pResult, pInput )
|
||
|
: ChapCMakeMessage(
|
||
|
pwb, pReceiveBuf, pSendBuf, cbSendBuf, pResult, pInput );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------------
|
||
|
** Internal routines
|
||
|
**---------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
VOID
|
||
|
ChapExtractMessage(
|
||
|
IN PPP_CONFIG* pReceiveBuf,
|
||
|
IN BYTE bAlgorithm,
|
||
|
OUT PPPAP_RESULT* pResult )
|
||
|
{
|
||
|
WORD cbPacket;
|
||
|
DWORD dwNumBytes;
|
||
|
CHAR* pszReplyMessage = NULL;
|
||
|
DWORD cbMessage;
|
||
|
CHAR szBuf[ MAXINFOLEN + 1 ];
|
||
|
CHAR* pszValue;
|
||
|
|
||
|
cbPacket = WireToHostFormat16(pReceiveBuf->Length);
|
||
|
|
||
|
if (PPP_CONFIG_HDR_LEN >= cbPacket)
|
||
|
{
|
||
|
goto LDone;
|
||
|
}
|
||
|
|
||
|
cbMessage = min( cbPacket - PPP_CONFIG_HDR_LEN, MAXINFOLEN );
|
||
|
CopyMemory( szBuf, pReceiveBuf->Data, cbMessage );
|
||
|
szBuf[ cbMessage ] = '\0';
|
||
|
|
||
|
if (PPP_CHAP_DIGEST_MD5 == bAlgorithm)
|
||
|
{
|
||
|
pszValue = szBuf;
|
||
|
dwNumBytes = cbMessage;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pszValue = strstr(szBuf, "M=");
|
||
|
|
||
|
if (pszValue == NULL)
|
||
|
{
|
||
|
dwNumBytes = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Eat the "M="
|
||
|
//
|
||
|
|
||
|
pszValue += 2;
|
||
|
|
||
|
dwNumBytes = strlen(pszValue);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (0 == dwNumBytes)
|
||
|
{
|
||
|
goto LDone;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// One more for the terminating NULL.
|
||
|
//
|
||
|
|
||
|
pszReplyMessage = LocalAlloc(LPTR, dwNumBytes + 1);
|
||
|
|
||
|
if (NULL == pszReplyMessage)
|
||
|
{
|
||
|
TRACE("LocalAlloc failed. Cannot extract server's message.");
|
||
|
goto LDone;
|
||
|
}
|
||
|
|
||
|
CopyMemory(pszReplyMessage, pszValue, dwNumBytes);
|
||
|
|
||
|
LocalFree(pResult->szReplyMessage);
|
||
|
|
||
|
pResult->szReplyMessage = pszReplyMessage;
|
||
|
|
||
|
pszReplyMessage = NULL;
|
||
|
|
||
|
LDone:
|
||
|
|
||
|
LocalFree(pszReplyMessage);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
IsSuccessPacketValid(
|
||
|
IN CHAPWB* pwb,
|
||
|
IN PPP_CONFIG* pReceiveBuf
|
||
|
)
|
||
|
{
|
||
|
A_SHA_CTX SHAContext1;
|
||
|
A_SHA_CTX SHAContext2;
|
||
|
BYTE SHADigest1[A_SHA_DIGEST_LEN];
|
||
|
BYTE SHADigest2[A_SHA_DIGEST_LEN];
|
||
|
DWORD cbSignature;
|
||
|
CHAR szBuf[ MAXINFOLEN + 2];
|
||
|
CHAR* pszValue;
|
||
|
DWORD dwLength = WireToHostFormat16( pReceiveBuf->Length );
|
||
|
BYTE bSignature[sizeof(SHADigest2)];
|
||
|
|
||
|
if ( dwLength < PPP_CONFIG_HDR_LEN )
|
||
|
{
|
||
|
return( FALSE );
|
||
|
}
|
||
|
|
||
|
cbSignature = min( dwLength - PPP_CONFIG_HDR_LEN, MAXINFOLEN );
|
||
|
CopyMemory( szBuf, pReceiveBuf->Data, cbSignature );
|
||
|
szBuf[ cbSignature ] = szBuf[ cbSignature + 1 ] = '\0';
|
||
|
|
||
|
pszValue = strstr( szBuf, "S=" );
|
||
|
|
||
|
if ( pszValue == NULL )
|
||
|
{
|
||
|
return( FALSE );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CHAR* pchIn = pszValue + 2;
|
||
|
CHAR* pchOut = (CHAR* )bSignature;
|
||
|
INT i;
|
||
|
|
||
|
ZeroMemory( bSignature, sizeof( bSignature ) );
|
||
|
|
||
|
for (i = 0; i < sizeof( bSignature ) + sizeof( bSignature ); ++i)
|
||
|
{
|
||
|
BYTE bHexCharValue = HexCharValue( *pchIn++ );
|
||
|
|
||
|
if (bHexCharValue == 0xFF)
|
||
|
break;
|
||
|
|
||
|
if (i & 1)
|
||
|
*pchOut++ += bHexCharValue;
|
||
|
else
|
||
|
*pchOut = bHexCharValue << 4;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
A_SHAInit( &SHAContext1 );
|
||
|
|
||
|
A_SHAUpdate( &SHAContext1, (PBYTE)&(pwb->keyUser), sizeof( pwb->keyUser) );
|
||
|
|
||
|
A_SHAUpdate( &SHAContext1,
|
||
|
pwb->abResponse + LM_RESPONSE_LENGTH,
|
||
|
NT_RESPONSE_LENGTH );
|
||
|
|
||
|
A_SHAUpdate( &SHAContext1,
|
||
|
"Magic server to client signing constant",
|
||
|
strlen( "Magic server to client signing constant" ) );
|
||
|
|
||
|
A_SHAFinal( &SHAContext1, SHADigest1 );
|
||
|
|
||
|
A_SHAInit( &SHAContext2 );
|
||
|
|
||
|
A_SHAUpdate( &SHAContext2, SHADigest1, sizeof( SHADigest1 ) );
|
||
|
|
||
|
A_SHAUpdate( &SHAContext2, pwb->abComputedChallenge, 8 );
|
||
|
|
||
|
A_SHAUpdate( &SHAContext2,
|
||
|
"Pad to make it do more than one iteration",
|
||
|
strlen( "Pad to make it do more than one iteration" ) );
|
||
|
|
||
|
A_SHAFinal( &SHAContext2, SHADigest2 );
|
||
|
|
||
|
if ( memcmp( SHADigest2, bSignature, sizeof( SHADigest2 ) ) != 0 )
|
||
|
{
|
||
|
TRACE(("CHAP: Signature received...\n"));
|
||
|
DUMPB(bSignature,(WORD)sizeof( SHADigest2 ) );
|
||
|
|
||
|
TRACE(("CHAP: Signature should be...\n"));
|
||
|
DUMPB( SHADigest2,(WORD)sizeof( SHADigest2 ) );
|
||
|
|
||
|
return( FALSE );
|
||
|
}
|
||
|
|
||
|
return( TRUE );
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
ChapCMakeMessage(
|
||
|
IN CHAPWB* pwb,
|
||
|
IN PPP_CONFIG* pReceiveBuf,
|
||
|
OUT PPP_CONFIG* pSendBuf,
|
||
|
IN DWORD cbSendBuf,
|
||
|
OUT PPPAP_RESULT* pResult,
|
||
|
IN PPPAP_INPUT* pInput )
|
||
|
|
||
|
/* Client side "make message" entry point. See RasCp interface
|
||
|
** documentation.
|
||
|
*/
|
||
|
{
|
||
|
DWORD dwErr;
|
||
|
|
||
|
TRACE("ChapCMakeMessage...");
|
||
|
|
||
|
switch (pwb->state)
|
||
|
{
|
||
|
case CS_Initial:
|
||
|
{
|
||
|
TRACE("CS_Initial");
|
||
|
|
||
|
/* Tell engine we're waiting for the server to initiate the
|
||
|
** conversation.
|
||
|
*/
|
||
|
pResult->Action = APA_NoAction;
|
||
|
pwb->state = CS_WaitForChallenge;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CS_WaitForChallenge:
|
||
|
case CS_Done:
|
||
|
{
|
||
|
TRACE1("CS_%s",(pwb->state==CS_Done)?"Done":"WaitForChallenge");
|
||
|
|
||
|
/*
|
||
|
** Should not receive Timeouts in this state. If we do simply
|
||
|
** ignore it.
|
||
|
*/
|
||
|
|
||
|
if (!pReceiveBuf)
|
||
|
{
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Note: Done state is same as WaitForChallenge per CHAP spec.
|
||
|
** Must be ready to respond to new Challenge at any time during
|
||
|
** Network Protocol phase.
|
||
|
*/
|
||
|
|
||
|
if (pReceiveBuf->Code != CHAPCODE_Challenge)
|
||
|
{
|
||
|
/* Everything but a Challenge is garbage at this point, and is
|
||
|
** silently discarded.
|
||
|
*/
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ((dwErr = GetChallengeFromChallenge( pwb, pReceiveBuf )))
|
||
|
{
|
||
|
TRACE1("GetChallengeFromChallenge=%d",dwErr);
|
||
|
return dwErr;
|
||
|
}
|
||
|
|
||
|
/* Build a Response to the Challenge and send it.
|
||
|
*/
|
||
|
pwb->fNewChallengeProvided = FALSE;
|
||
|
pwb->bIdToSend = pwb->bIdExpected = pReceiveBuf->Id;
|
||
|
|
||
|
if ((dwErr = MakeResponseMessage(
|
||
|
pwb, pSendBuf, cbSendBuf, FALSE )) != 0)
|
||
|
{
|
||
|
TRACE1("MakeResponseMessage(WC)=%d",dwErr);
|
||
|
return dwErr;
|
||
|
}
|
||
|
|
||
|
pResult->Action = APA_SendWithTimeout;
|
||
|
pResult->bIdExpected = pwb->bIdExpected;
|
||
|
pwb->state = CS_ResponseSent;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CS_ResponseSent:
|
||
|
case CS_ChangePw1Sent:
|
||
|
case CS_ChangePw2Sent:
|
||
|
{
|
||
|
TRACE1("CS_%sSent",
|
||
|
(pwb->state==CS_ResponseSent)
|
||
|
?"Response"
|
||
|
:(pwb->state==CS_ChangePw1Sent)
|
||
|
?"ChangePw1"
|
||
|
:"ChangePw2");
|
||
|
|
||
|
if (!pReceiveBuf)
|
||
|
{
|
||
|
/* Timed out, resend our message.
|
||
|
*/
|
||
|
if (pwb->state == CS_ResponseSent)
|
||
|
{
|
||
|
if ((dwErr = MakeResponseMessage(
|
||
|
pwb, pSendBuf, cbSendBuf, TRUE )) != 0)
|
||
|
{
|
||
|
TRACE1("MakeResponseMessage(RS)=%d",dwErr);
|
||
|
return dwErr;
|
||
|
}
|
||
|
}
|
||
|
else if (pwb->state == CS_ChangePw1Sent)
|
||
|
{
|
||
|
if ((dwErr = MakeChangePw1Message(
|
||
|
pwb, pSendBuf, cbSendBuf )) != 0)
|
||
|
{
|
||
|
TRACE1("MakeChangePw1Message(CPS)=%d",dwErr);
|
||
|
return dwErr;
|
||
|
}
|
||
|
}
|
||
|
else // if (pwb->state == CS_ChangePw2Sent)
|
||
|
{
|
||
|
if ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT )
|
||
|
{
|
||
|
if ((dwErr = MakeChangePw2Message(
|
||
|
pwb, pSendBuf, cbSendBuf )) != 0)
|
||
|
{
|
||
|
TRACE1("MakeChangePw2Message(CPS)=%d",dwErr);
|
||
|
return dwErr;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ((dwErr = MakeChangePw3Message(
|
||
|
pwb, pSendBuf, cbSendBuf, TRUE )) != 0)
|
||
|
{
|
||
|
TRACE1("MakeChangePw3Message(CPS)=%d",dwErr);
|
||
|
return dwErr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pResult->Action = APA_SendWithTimeout;
|
||
|
pResult->bIdExpected = pwb->bIdExpected;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
TRACE("Message received...");
|
||
|
DUMPB(pReceiveBuf,(WORD)(((BYTE*)pReceiveBuf)[3]));
|
||
|
|
||
|
if (pReceiveBuf->Code == CHAPCODE_Challenge)
|
||
|
{
|
||
|
/* Restart when new challenge is received, per CHAP spec.
|
||
|
*/
|
||
|
pwb->state = CS_WaitForChallenge;
|
||
|
return ChapCMakeMessage(
|
||
|
pwb, pReceiveBuf, pSendBuf, cbSendBuf, pResult, NULL );
|
||
|
}
|
||
|
|
||
|
if (pReceiveBuf->Id != pwb->bIdExpected)
|
||
|
{
|
||
|
/* Received a packet out of sequence. Silently discard it.
|
||
|
*/
|
||
|
TRACE2("Got ID %d when expecting %d",
|
||
|
pReceiveBuf->Id,pwb->bIdExpected);
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ChapExtractMessage( pReceiveBuf, pwb->bAlgorithm, pResult );
|
||
|
|
||
|
if ( pReceiveBuf->Code == CHAPCODE_Success )
|
||
|
{
|
||
|
/* Passed authentication.
|
||
|
**
|
||
|
** Get the session key for encryption.
|
||
|
*/
|
||
|
if ( ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT ) ||
|
||
|
( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW ) )
|
||
|
{
|
||
|
if ( !pwb->fSessionKeysObtained )
|
||
|
{
|
||
|
DecodePw( pwb->chSeed, pwb->szPassword );
|
||
|
CGetSessionKeys(
|
||
|
pwb->szPassword, &pwb->keyLm, &pwb->keyUser );
|
||
|
EncodePw( pwb->chSeed, pwb->szPassword );
|
||
|
|
||
|
pwb->fSessionKeysObtained = TRUE;
|
||
|
}
|
||
|
|
||
|
if ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW )
|
||
|
{
|
||
|
if ( !IsSuccessPacketValid( pwb, pReceiveBuf ) )
|
||
|
{
|
||
|
pwb->state = CS_Done;
|
||
|
pResult->dwError =
|
||
|
ERROR_UNABLE_TO_AUTHENTICATE_SERVER;
|
||
|
pResult->fRetry = FALSE;
|
||
|
pResult->Action = APA_Done;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( pwb->pMPPEKeys == NULL )
|
||
|
{
|
||
|
//
|
||
|
// We set up the MPPE key attribute to be passed to
|
||
|
// the PPP engine
|
||
|
//
|
||
|
|
||
|
BYTE MPPEKeys[6+8+16];
|
||
|
|
||
|
pwb->pMPPEKeys = RasAuthAttributeCreate( 1 );
|
||
|
|
||
|
if ( pwb->pMPPEKeys == NULL )
|
||
|
{
|
||
|
return( GetLastError() );
|
||
|
}
|
||
|
|
||
|
HostToWireFormat32( 311, MPPEKeys ); // Vendor Id
|
||
|
MPPEKeys[4] = 12; // Vendor Type
|
||
|
MPPEKeys[5] = 24; // Vendor Length
|
||
|
|
||
|
CopyMemory( MPPEKeys+6, &(pwb->keyLm), 8 );
|
||
|
|
||
|
CopyMemory( MPPEKeys+6+8, &(pwb->keyUser), 16 );
|
||
|
|
||
|
dwErr = RasAuthAttributeInsert(
|
||
|
0,
|
||
|
pwb->pMPPEKeys,
|
||
|
raatVendorSpecific,
|
||
|
FALSE,
|
||
|
6+8+16,
|
||
|
MPPEKeys );
|
||
|
|
||
|
if ( dwErr != NO_ERROR )
|
||
|
{
|
||
|
return( dwErr );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pResult->pUserAttributes = pwb->pMPPEKeys;
|
||
|
|
||
|
CopyMemory( pResult->abResponse,
|
||
|
pwb->abResponse+LM_RESPONSE_LENGTH,
|
||
|
NT_RESPONSE_LENGTH );
|
||
|
|
||
|
CopyMemory( pResult->abChallenge,
|
||
|
( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW )
|
||
|
? pwb->abComputedChallenge
|
||
|
: pwb->abChallenge,
|
||
|
sizeof( pResult->abChallenge ) );
|
||
|
}
|
||
|
|
||
|
pResult->Action = APA_Done;
|
||
|
pResult->dwError = 0;
|
||
|
pResult->fRetry = FALSE;
|
||
|
pwb->state = CS_Done;
|
||
|
strcpy( pResult->szUserName, pwb->szUserName );
|
||
|
|
||
|
TRACE("Done :)");
|
||
|
}
|
||
|
else if (pReceiveBuf->Code == CHAPCODE_Failure)
|
||
|
{
|
||
|
DWORD dwVersion = 1;
|
||
|
|
||
|
/* Failed authentication.
|
||
|
*/
|
||
|
if ( ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT ) ||
|
||
|
( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW ) )
|
||
|
{
|
||
|
GetInfoFromFailure(
|
||
|
pwb, pReceiveBuf,
|
||
|
&pResult->dwError, &pResult->fRetry, &dwVersion );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pResult->dwError = ERROR_AUTHENTICATION_FAILURE;
|
||
|
pResult->fRetry = 0;
|
||
|
}
|
||
|
|
||
|
pResult->Action = APA_Done;
|
||
|
|
||
|
if (pResult->dwError == ERROR_PASSWD_EXPIRED)
|
||
|
{
|
||
|
pwb->state = (dwVersion < 2) ? CS_ChangePw1 : CS_ChangePw2;
|
||
|
pwb->bIdToSend = pReceiveBuf->Id + 1;
|
||
|
pwb->bIdExpected = pwb->bIdToSend;
|
||
|
TRACE3("ChangePw(%d) :| ex=%d ts=%d",
|
||
|
dwVersion,pwb->bIdExpected,pwb->bIdToSend);
|
||
|
}
|
||
|
else if (pResult->fRetry)
|
||
|
{
|
||
|
pwb->state = CS_Retry;
|
||
|
pwb->bIdToSend = pReceiveBuf->Id + 1;
|
||
|
pwb->bIdExpected = pwb->bIdToSend;
|
||
|
pwb->fSessionKeysObtained = FALSE;
|
||
|
TRACE2("Retry :| ex=%d ts=%d",
|
||
|
pwb->bIdExpected,pwb->bIdToSend);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pwb->state = CS_Done;
|
||
|
TRACE("Done :(");
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Received a CHAPCODE_* besides CHAPCODE_Challenge,
|
||
|
** CHAPCODE_Success, and CHAPCODE_Failure. The engine filters
|
||
|
** all non-CHAPCODEs. Shouldn't happen, but silently discard
|
||
|
** it.
|
||
|
*/
|
||
|
ASSERT(!"Bogus pReceiveBuf->Code");
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CS_Retry:
|
||
|
case CS_ChangePw1:
|
||
|
case CS_ChangePw2:
|
||
|
{
|
||
|
TRACE1("CS_%s",
|
||
|
(pwb->state==CS_Retry)
|
||
|
?"Retry"
|
||
|
:(pwb->state==CS_ChangePw1)
|
||
|
?"ChangePw1"
|
||
|
:"ChangePw2");
|
||
|
|
||
|
if (pReceiveBuf)
|
||
|
{
|
||
|
if (pReceiveBuf->Code == CHAPCODE_Challenge)
|
||
|
{
|
||
|
/* Restart when new challenge is received, per CHAP spec.
|
||
|
*/
|
||
|
pwb->state = CS_WaitForChallenge;
|
||
|
return ChapCMakeMessage(
|
||
|
pwb, pReceiveBuf, pSendBuf, cbSendBuf, pResult, NULL );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Silently discard.
|
||
|
*/
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!pInput)
|
||
|
{
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ((dwErr = StoreCredentials( pwb, pInput )) != 0)
|
||
|
return dwErr;
|
||
|
|
||
|
if (pwb->state == CS_Retry)
|
||
|
{
|
||
|
/* Build a response to the challenge and send it.
|
||
|
*/
|
||
|
if (!pwb->fNewChallengeProvided)
|
||
|
{
|
||
|
/* Implied challenge of old challenge + 23.
|
||
|
*/
|
||
|
pwb->abChallenge[ 0 ] += 23;
|
||
|
}
|
||
|
|
||
|
if ((dwErr = MakeResponseMessage(
|
||
|
pwb, pSendBuf, cbSendBuf, FALSE )) != 0)
|
||
|
{
|
||
|
return dwErr;
|
||
|
}
|
||
|
|
||
|
pwb->state = CS_ResponseSent;
|
||
|
}
|
||
|
else if (pwb->state == CS_ChangePw1)
|
||
|
{
|
||
|
/* Build a response to the NT35-style password expired
|
||
|
** notification and send it.
|
||
|
*/
|
||
|
if ((dwErr = MakeChangePw1Message(
|
||
|
pwb, pSendBuf, cbSendBuf )) != 0)
|
||
|
{
|
||
|
return dwErr;
|
||
|
}
|
||
|
|
||
|
pwb->state = CS_ChangePw1Sent;
|
||
|
}
|
||
|
else // if (pwb->state == CS_ChangePw2)
|
||
|
{
|
||
|
if ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT )
|
||
|
{
|
||
|
/* Build a response to the NT351-style password expired
|
||
|
** notification and send it.
|
||
|
*/
|
||
|
if ((dwErr = MakeChangePw2Message(
|
||
|
pwb, pSendBuf, cbSendBuf )) != 0)
|
||
|
{
|
||
|
return dwErr;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ((dwErr = MakeChangePw3Message(
|
||
|
pwb, pSendBuf, cbSendBuf, FALSE )) != 0)
|
||
|
{
|
||
|
return dwErr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pwb->state = CS_ChangePw2Sent;
|
||
|
}
|
||
|
|
||
|
pResult->Action = APA_SendWithTimeout;
|
||
|
pResult->bIdExpected = pwb->bIdExpected;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
GetChallengeFromChallenge(
|
||
|
OUT CHAPWB* pwb,
|
||
|
IN PPP_CONFIG* pReceiveBuf )
|
||
|
|
||
|
/* Fill work buffer challenge array and length from that received in the
|
||
|
** received Challenge message.
|
||
|
**
|
||
|
** Returns 0 if successful, or ERRORBADPACKET if the packet is
|
||
|
** misformatted in any way.
|
||
|
*/
|
||
|
{
|
||
|
WORD cbPacket = WireToHostFormat16( pReceiveBuf->Length );
|
||
|
|
||
|
if (cbPacket < PPP_CONFIG_HDR_LEN + 1)
|
||
|
return ERRORBADPACKET;
|
||
|
|
||
|
pwb->cbChallenge = *pReceiveBuf->Data;
|
||
|
|
||
|
if (cbPacket < PPP_CONFIG_HDR_LEN + 1 + pwb->cbChallenge)
|
||
|
return ERRORBADPACKET;
|
||
|
|
||
|
memcpy( pwb->abChallenge, pReceiveBuf->Data + 1, pwb->cbChallenge );
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
GetCredentialsFromResponse(
|
||
|
IN PPP_CONFIG* pReceiveBuf,
|
||
|
IN BYTE bAlgorithm,
|
||
|
OUT CHAR* pszIdentity,
|
||
|
OUT BYTE* pbResponse )
|
||
|
|
||
|
/* Fill caller's 'pszUserName' and 'pbResponse' buffers with
|
||
|
** the username, and response in the Response packet. Caller's
|
||
|
** buffers should be at least UNLEN+DNLEN+1, and MSRESPONSELEN bytes long,
|
||
|
** respectively. 'BAlgorithm' is the CHAP algorithm code for either
|
||
|
** MS-CHAP or MD5.
|
||
|
**
|
||
|
** Returns 0 if successful, or ERRORBADPACKET if the packet is
|
||
|
** misformatted in any way.
|
||
|
*/
|
||
|
{
|
||
|
BYTE cbIdentity;
|
||
|
CHAR* pchIdentity;
|
||
|
BYTE* pcbResponse;
|
||
|
CHAR* pchResponse;
|
||
|
WORD cbPacket;
|
||
|
|
||
|
cbPacket = WireToHostFormat16( pReceiveBuf->Length );
|
||
|
|
||
|
/* Extract the response.
|
||
|
*/
|
||
|
if (cbPacket < PPP_CONFIG_HDR_LEN + 1)
|
||
|
return ERRORBADPACKET;
|
||
|
|
||
|
pcbResponse = pReceiveBuf->Data;
|
||
|
pchResponse = pcbResponse + 1;
|
||
|
|
||
|
ASSERT(MSRESPONSELEN<=255);
|
||
|
ASSERT(MD5RESPONSELEN<=255);
|
||
|
|
||
|
if ( ( ( ( bAlgorithm == PPP_CHAP_DIGEST_MSEXT ) ||
|
||
|
( bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW ) )
|
||
|
&& *pcbResponse != MSRESPONSELEN )
|
||
|
|| ( ( bAlgorithm == PPP_CHAP_DIGEST_MD5 )
|
||
|
&& ( *pcbResponse != MD5RESPONSELEN ) )
|
||
|
|| ( cbPacket < ( PPP_CONFIG_HDR_LEN + 1 + *pcbResponse ) ) )
|
||
|
{
|
||
|
return ERRORBADPACKET;
|
||
|
}
|
||
|
|
||
|
memcpy( pbResponse, pchResponse, *pcbResponse );
|
||
|
|
||
|
/* Parse out username
|
||
|
*/
|
||
|
pchIdentity = pchResponse + *pcbResponse;
|
||
|
cbIdentity = (BYTE) (((BYTE* )pReceiveBuf) + cbPacket - pchIdentity);
|
||
|
|
||
|
/* Extract the username.
|
||
|
*/
|
||
|
ASSERT(cbIdentity<=(UNLEN+DNLEN+1));
|
||
|
memcpy( pszIdentity, pchIdentity, cbIdentity );
|
||
|
pszIdentity[ cbIdentity ] = '\0';
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
GetInfoFromChangePw1(
|
||
|
IN PPP_CONFIG* pReceiveBuf,
|
||
|
OUT CHANGEPW1* pchangepw1 )
|
||
|
|
||
|
/* Loads caller's '*pchangepw' buffer with the information from the
|
||
|
** version 1 change password packet.
|
||
|
**
|
||
|
** Returns 0 if successful, or ERRORBADPACKET if the packet is
|
||
|
** misformatted in any way.
|
||
|
*/
|
||
|
{
|
||
|
WORD cbPacket = WireToHostFormat16( pReceiveBuf->Length );
|
||
|
|
||
|
TRACE("GetInfoFromChangePw1...");
|
||
|
|
||
|
if (cbPacket < ( PPP_CONFIG_HDR_LEN + sizeof(CHANGEPW1) ) )
|
||
|
return ERRORBADPACKET;
|
||
|
|
||
|
CopyMemory( pchangepw1, pReceiveBuf->Data, sizeof(CHANGEPW1) );
|
||
|
|
||
|
TRACE("GetInfoFromChangePw done(0)");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
GetInfoFromChangePw2(
|
||
|
IN PPP_CONFIG* pReceiveBuf,
|
||
|
OUT CHANGEPW2* pchangepw2,
|
||
|
OUT BYTE* pResponse )
|
||
|
|
||
|
/* Loads caller's '*pchangepw2' buffer with the information from the
|
||
|
** version 2 change password packet, and caller's 'pResponse' buffer with
|
||
|
** the challenge response data from 'pchangepw2'.
|
||
|
**
|
||
|
** Returns 0 if successful, or ERRORBADPACKET if the packet is
|
||
|
** misformatted in any way.
|
||
|
*/
|
||
|
{
|
||
|
WORD cbPacket = WireToHostFormat16( pReceiveBuf->Length );
|
||
|
WORD wFlags;
|
||
|
|
||
|
TRACE("GetInfoFromChangePw2...");
|
||
|
|
||
|
if (cbPacket < ( PPP_CONFIG_HDR_LEN + sizeof(CHANGEPW2) ) )
|
||
|
return ERRORBADPACKET;
|
||
|
|
||
|
CopyMemory( pchangepw2, pReceiveBuf->Data, sizeof(CHANGEPW2) );
|
||
|
|
||
|
CopyMemory( pResponse, pchangepw2->abLmResponse, LM_RESPONSE_LENGTH );
|
||
|
CopyMemory( pResponse + LM_RESPONSE_LENGTH, pchangepw2->abNtResponse,
|
||
|
NT_RESPONSE_LENGTH );
|
||
|
|
||
|
wFlags = WireToHostFormat16( pchangepw2->abFlags );
|
||
|
pResponse[ LM_RESPONSE_LENGTH + NT_RESPONSE_LENGTH ] =
|
||
|
(wFlags & CPW2F_UseNtResponse);
|
||
|
|
||
|
TRACE("GetInfoFromChangePw2 done(0)");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
GetInfoFromChangePw3(
|
||
|
IN PPP_CONFIG* pReceiveBuf,
|
||
|
OUT CHANGEPW3* pchangepw3,
|
||
|
OUT BYTE* pResponse )
|
||
|
|
||
|
/* Loads caller's '*pchangepw3' buffer with the information from the
|
||
|
** version 3 change password packet, and caller's 'pResponse' buffer with
|
||
|
** the challenge response data from 'pchangepw3'.
|
||
|
**
|
||
|
** Returns 0 if successful, or ERRORBADPACKET if the packet is
|
||
|
** misformatted in any way.
|
||
|
*/
|
||
|
{
|
||
|
WORD cbPacket = WireToHostFormat16( pReceiveBuf->Length );
|
||
|
WORD wFlags;
|
||
|
|
||
|
TRACE("GetInfoFromChangePw3...");
|
||
|
|
||
|
if ( cbPacket < ( PPP_CONFIG_HDR_LEN + sizeof( CHANGEPW3 ) ) )
|
||
|
return ERRORBADPACKET;
|
||
|
|
||
|
memcpy( pchangepw3, pReceiveBuf->Data, sizeof(CHANGEPW3) );
|
||
|
|
||
|
memcpy( pResponse, pchangepw3->abPeerChallenge, 16 );
|
||
|
memcpy( pResponse + 16, pchangepw3->abNTResponse, 16 );
|
||
|
|
||
|
pResponse[ 16 + 16 ] = 0;
|
||
|
|
||
|
TRACE("GetInfoFromChangePw3 done(0)");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
GetInfoFromFailure(
|
||
|
IN CHAPWB* pwb,
|
||
|
IN PPP_CONFIG* pReceiveBuf,
|
||
|
OUT DWORD* pdwError,
|
||
|
OUT BOOL* pfRetry,
|
||
|
OUT DWORD* pdwVersion )
|
||
|
|
||
|
/* Returns the RAS error number, retry flag, version number, and new
|
||
|
** challenge (sets challenge info in pwb) out of the Message portion of
|
||
|
** the Failure message buffer 'pReceiveBuf' or 0 if none. This call
|
||
|
** applies to Microsoft extended CHAP Failure messages only.
|
||
|
**
|
||
|
** Format of the message text portion of the result is a string of any of
|
||
|
** the following separated by a space.
|
||
|
**
|
||
|
** "E=dddddddddd"
|
||
|
** "R=b"
|
||
|
** "C=xxxxxxxxxxxxxxxx"
|
||
|
** "V=v"
|
||
|
**
|
||
|
** where
|
||
|
**
|
||
|
** 'dddddddddd' is the decimal error code (need not be 10 digits).
|
||
|
**
|
||
|
** 'b' is a boolean flag <0/1> that is set if a retry is allowed.
|
||
|
**
|
||
|
** 'xxxxxxxxxxxxxxxx' is 16-hex digits representing a new challenge to
|
||
|
** be used in place of the previous challenge + 23. This is useful
|
||
|
** for pass-thru authentication where server may be unable to deal
|
||
|
** with the implicit challenge. (Win95 guys requested it).
|
||
|
**
|
||
|
** 'v' is a version code where 2 indicates NT 3.51 level support. 'v'
|
||
|
** is assumed 1, i.e. NT 3.5 level support, if missing.
|
||
|
*/
|
||
|
{
|
||
|
#define MAXINFOLEN 1500
|
||
|
|
||
|
WORD cbPacket = WireToHostFormat16( pReceiveBuf->Length );
|
||
|
WORD cbError;
|
||
|
CHAR szBuf[ MAXINFOLEN + 2 ];
|
||
|
CHAR* pszValue;
|
||
|
|
||
|
TRACE("GetInfoFromFailure...");
|
||
|
|
||
|
*pdwError = ERROR_AUTHENTICATION_FAILURE;
|
||
|
*pfRetry = 0;
|
||
|
*pdwVersion = 2;
|
||
|
|
||
|
if (cbPacket <= PPP_CONFIG_HDR_LEN)
|
||
|
return;
|
||
|
|
||
|
/* Copy message to double-NUL-terminated 'szBuf' for convenient safe
|
||
|
** strstr value scanning. For convenience, we assume that information
|
||
|
** appearing beyond 1500 bytes in the packet in not interesting.
|
||
|
*/
|
||
|
cbError = min( cbPacket - PPP_CONFIG_HDR_LEN, MAXINFOLEN );
|
||
|
memcpy( szBuf, pReceiveBuf->Data, cbError );
|
||
|
szBuf[ cbError ] = szBuf[ cbError + 1 ] = '\0';
|
||
|
|
||
|
pszValue = strstr( szBuf, "E=" );
|
||
|
if (pszValue)
|
||
|
*pdwError = (DWORD )atol( pszValue + 2 );
|
||
|
|
||
|
*pfRetry = (strstr( szBuf, "R=1" ) != NULL);
|
||
|
|
||
|
pszValue = strstr( szBuf, "V=" );
|
||
|
if (pszValue)
|
||
|
*pdwVersion = (DWORD )atol( pszValue + 2 );
|
||
|
|
||
|
pszValue = strstr( szBuf, "C=" );
|
||
|
pwb->fNewChallengeProvided = (pszValue != NULL);
|
||
|
if (pwb->fNewChallengeProvided)
|
||
|
{
|
||
|
CHAR* pchIn = pszValue + 2;
|
||
|
CHAR* pchOut = (CHAR* )pwb->abChallenge;
|
||
|
INT i;
|
||
|
|
||
|
memset( pwb->abChallenge, '\0', sizeof(pwb->abChallenge) );
|
||
|
|
||
|
for (i = 0; i < pwb->cbChallenge + pwb->cbChallenge; ++i)
|
||
|
{
|
||
|
BYTE bHexCharValue = HexCharValue( *pchIn++ );
|
||
|
|
||
|
if (bHexCharValue == 0xFF)
|
||
|
break;
|
||
|
|
||
|
if (i & 1)
|
||
|
*pchOut++ += bHexCharValue;
|
||
|
else
|
||
|
*pchOut = bHexCharValue << 4;
|
||
|
}
|
||
|
|
||
|
TRACE1("'C=' challenge provided,bytes=%d...",pwb->cbChallenge);
|
||
|
DUMPB(pwb->abChallenge,pwb->cbChallenge);
|
||
|
}
|
||
|
|
||
|
TRACE3("GetInfoFromFailure done,e=%d,r=%d,v=%d",*pdwError,*pfRetry,*pdwVersion);
|
||
|
}
|
||
|
|
||
|
|
||
|
BYTE
|
||
|
HexCharValue(
|
||
|
IN CHAR ch )
|
||
|
|
||
|
/* Returns the integer value of hexidecimal character 'ch' or 0xFF if 'ch'
|
||
|
** is not a hexidecimal character.
|
||
|
*/
|
||
|
{
|
||
|
if (ch >= '0' && ch <= '9')
|
||
|
return (BYTE )(ch - '0');
|
||
|
else if (ch >= 'A' && ch <= 'F')
|
||
|
return (BYTE )(ch - 'A'+ 10);
|
||
|
else if (ch >= 'a' && ch <= 'f')
|
||
|
return (BYTE )(ch - 'a' + 10);
|
||
|
else
|
||
|
return 0xFF;
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
MakeChallengeMessage(
|
||
|
IN CHAPWB* pwb,
|
||
|
OUT PPP_CONFIG* pSendBuf,
|
||
|
IN DWORD cbSendBuf )
|
||
|
|
||
|
/* Builds a Challenge packet in caller's 'pSendBuf' buffer. 'cbSendBuf'
|
||
|
** is the length of caller's buffer. 'pwb' is the address of the work
|
||
|
** buffer associated with the port.
|
||
|
*/
|
||
|
{
|
||
|
DWORD dwErr;
|
||
|
WORD wLength;
|
||
|
BYTE* pcbChallenge;
|
||
|
BYTE* pbChallenge;
|
||
|
|
||
|
TRACE("MakeChallengeMessage...");
|
||
|
|
||
|
if ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT )
|
||
|
{
|
||
|
ASSERT(cbSendBuf>=PPP_CONFIG_HDR_LEN+1+MSV1_0_CHALLENGE_LENGTH);
|
||
|
|
||
|
/* Fill in the challenge.
|
||
|
*/
|
||
|
pwb->cbChallenge = (BYTE )MSV1_0_CHALLENGE_LENGTH;
|
||
|
if ((dwErr = GetChallenge( pwb->abChallenge )) != 0)
|
||
|
return dwErr;
|
||
|
}
|
||
|
else if ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW )
|
||
|
{
|
||
|
ASSERT(cbSendBuf>=PPP_CONFIG_HDR_LEN+1+16);
|
||
|
|
||
|
/* Fill in the challenge.
|
||
|
*/
|
||
|
pwb->cbChallenge = (BYTE )16;
|
||
|
|
||
|
if ((dwErr = (DWORD )GetChallenge( pwb->abChallenge )) != 0)
|
||
|
return dwErr;
|
||
|
|
||
|
if ((dwErr = (DWORD )GetChallenge( pwb->abChallenge+8 )) != 0)
|
||
|
return dwErr;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ASSERT(cbSendBuf>=PPP_CONFIG_HDR_LEN+1+16);
|
||
|
|
||
|
/* Fill in the challenge.
|
||
|
*/
|
||
|
pwb->cbChallenge = (BYTE )16;
|
||
|
if ((dwErr = GetChallenge( pwb->abChallenge )) != 0)
|
||
|
return dwErr;
|
||
|
|
||
|
GetSystemTimeAsFileTime( (FILETIME*)(pwb->abChallenge+8));
|
||
|
}
|
||
|
|
||
|
pcbChallenge = pSendBuf->Data;
|
||
|
*pcbChallenge = pwb->cbChallenge;
|
||
|
|
||
|
pbChallenge = pcbChallenge + 1;
|
||
|
CopyMemory( pbChallenge, pwb->abChallenge, pwb->cbChallenge );
|
||
|
|
||
|
//
|
||
|
// Insert local identifcation at the end of the challenge
|
||
|
//
|
||
|
|
||
|
strcpy( pbChallenge + pwb->cbChallenge, szComputerName );
|
||
|
|
||
|
/* Fill in the header.
|
||
|
*/
|
||
|
pSendBuf->Code = (BYTE )CHAPCODE_Challenge;
|
||
|
pSendBuf->Id = pwb->bIdToSend;
|
||
|
|
||
|
wLength = (WORD )(PPP_CONFIG_HDR_LEN + 1
|
||
|
+ pwb->cbChallenge + strlen( szComputerName) );
|
||
|
|
||
|
HostToWireFormat16( wLength, pSendBuf->Length );
|
||
|
|
||
|
DUMPB(pSendBuf,wLength);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
MakeChangePw1Message(
|
||
|
IN CHAPWB* pwb,
|
||
|
OUT PPP_CONFIG* pSendBuf,
|
||
|
IN DWORD cbSendBuf )
|
||
|
|
||
|
/* Builds a ChangePw1 response packet in caller's 'pSendBuf' buffer.
|
||
|
** 'cbSendBuf' is the length of caller's buffer. 'pwb' is the address of
|
||
|
** the work buffer associated with the port.
|
||
|
**
|
||
|
** Returns 0 if successful, or a non-0 error code.
|
||
|
*/
|
||
|
{
|
||
|
DWORD dwErr;
|
||
|
WORD wPwLength;
|
||
|
|
||
|
TRACE("MakeChangePw1Message...");
|
||
|
ASSERT(cbSendBuf>=PPP_CONFIG_HDR_LEN+sizeof(CHANGEPW1));
|
||
|
|
||
|
(void )cbSendBuf;
|
||
|
|
||
|
if ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW )
|
||
|
{
|
||
|
return( ERROR_NOT_SUPPORTED );
|
||
|
}
|
||
|
|
||
|
if ( !( pwb->fConfigInfo & PPPCFG_UseLmPassword ) )
|
||
|
{
|
||
|
return( ERROR_NOT_SUPPORTED );
|
||
|
}
|
||
|
|
||
|
DecodePw( pwb->chSeed, pwb->szOldPassword );
|
||
|
DecodePw( pwb->chSeed, pwb->szPassword );
|
||
|
|
||
|
dwErr =
|
||
|
GetEncryptedOwfPasswordsForChangePassword(
|
||
|
pwb->szOldPassword,
|
||
|
pwb->szPassword,
|
||
|
(PLM_SESSION_KEY )pwb->abChallenge,
|
||
|
(PENCRYPTED_LM_OWF_PASSWORD )pwb->changepw.v1.abEncryptedLmOwfOldPw,
|
||
|
(PENCRYPTED_LM_OWF_PASSWORD )pwb->changepw.v1.abEncryptedLmOwfNewPw,
|
||
|
(PENCRYPTED_NT_OWF_PASSWORD )pwb->changepw.v1.abEncryptedNtOwfOldPw,
|
||
|
(PENCRYPTED_NT_OWF_PASSWORD )pwb->changepw.v1.abEncryptedNtOwfNewPw);
|
||
|
|
||
|
wPwLength = (UCHAR) strlen( pwb->szPassword );
|
||
|
|
||
|
EncodePw( pwb->chSeed, pwb->szOldPassword );
|
||
|
EncodePw( pwb->chSeed, pwb->szPassword );
|
||
|
|
||
|
if (dwErr != 0)
|
||
|
return dwErr;
|
||
|
|
||
|
HostToWireFormat16( wPwLength, pwb->changepw.v1.abPasswordLength );
|
||
|
HostToWireFormat16( CPW1F_UseNtResponse, pwb->changepw.v1.abFlags );
|
||
|
CopyMemory( pSendBuf->Data, &pwb->changepw.v1, sizeof(CHANGEPW1) );
|
||
|
|
||
|
/* Fill in the header.
|
||
|
*/
|
||
|
pSendBuf->Code = (BYTE )CHAPCODE_ChangePw1;
|
||
|
pSendBuf->Id = pwb->bIdToSend;
|
||
|
HostToWireFormat16(
|
||
|
PPP_CONFIG_HDR_LEN + sizeof(CHANGEPW1), pSendBuf->Length );
|
||
|
|
||
|
TRACE("MakeChangePw1Message done(0)");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
MakeChangePw2Message(
|
||
|
IN CHAPWB* pwb,
|
||
|
OUT PPP_CONFIG* pSendBuf,
|
||
|
IN DWORD cbSendBuf )
|
||
|
|
||
|
/* Builds a ChangePw2 response packet in caller's 'pSendBuf' buffer.
|
||
|
** 'cbSendBuf' is the length of caller's buffer. 'pwb' is the address of
|
||
|
** the work buffer associated with the port.
|
||
|
**
|
||
|
** Returns 0 if successful, or a non-0 error code.
|
||
|
*/
|
||
|
{
|
||
|
DWORD dwErr;
|
||
|
BOOLEAN fLmPresent;
|
||
|
BYTE fbUseNtResponse;
|
||
|
BYTE bRandomNumber[MSV1_0_CHALLENGE_LENGTH];
|
||
|
|
||
|
TRACE("MakeChangePw2Message...");
|
||
|
ASSERT(cbSendBuf>=PPP_CONFIG_HDR_LEN+sizeof(CHANGEPW2));
|
||
|
|
||
|
(void )cbSendBuf;
|
||
|
|
||
|
DecodePw( pwb->chSeed, pwb->szOldPassword );
|
||
|
DecodePw( pwb->chSeed, pwb->szPassword );
|
||
|
|
||
|
dwErr =
|
||
|
GetEncryptedPasswordsForChangePassword2(
|
||
|
pwb->szOldPassword,
|
||
|
pwb->szPassword,
|
||
|
(SAMPR_ENCRYPTED_USER_PASSWORD* )
|
||
|
pwb->changepw.v2.abNewEncryptedWithOldNtOwf,
|
||
|
(ENCRYPTED_NT_OWF_PASSWORD* )
|
||
|
pwb->changepw.v2.abOldNtOwfEncryptedWithNewNtOwf,
|
||
|
(SAMPR_ENCRYPTED_USER_PASSWORD* )
|
||
|
pwb->changepw.v2.abNewEncryptedWithOldLmOwf,
|
||
|
(ENCRYPTED_NT_OWF_PASSWORD* )
|
||
|
pwb->changepw.v2.abOldLmOwfEncryptedWithNewNtOwf,
|
||
|
&fLmPresent );
|
||
|
|
||
|
if (dwErr == 0)
|
||
|
{
|
||
|
BOOL fEmptyUserName = (pwb->szUserName[ 0 ] == '\0');
|
||
|
|
||
|
pwb->fSessionKeysObtained = FALSE;
|
||
|
|
||
|
dwErr =
|
||
|
GetChallengeResponse(
|
||
|
g_dwTraceIdChap,
|
||
|
pwb->szUserName,
|
||
|
pwb->szPassword,
|
||
|
&pwb->Luid,
|
||
|
pwb->abChallenge,
|
||
|
( pwb->fConfigInfo & PPPCFG_MachineAuthentication ),
|
||
|
pwb->changepw.v2.abLmResponse,
|
||
|
pwb->changepw.v2.abNtResponse,
|
||
|
&fbUseNtResponse,
|
||
|
(PBYTE )&pwb->keyLm,
|
||
|
(PBYTE )&pwb->keyUser );
|
||
|
|
||
|
if (dwErr == 0 && fEmptyUserName)
|
||
|
pwb->fSessionKeysObtained = TRUE;
|
||
|
}
|
||
|
|
||
|
EncodePw( pwb->chSeed, pwb->szOldPassword );
|
||
|
EncodePw( pwb->chSeed, pwb->szPassword );
|
||
|
|
||
|
if (dwErr != 0)
|
||
|
return dwErr;
|
||
|
|
||
|
if ( !( pwb->fConfigInfo & PPPCFG_UseLmPassword ) )
|
||
|
{
|
||
|
//
|
||
|
// Zero out all the LM password stuff since this has been cracked
|
||
|
//
|
||
|
|
||
|
ZeroMemory( pwb->changepw.v2.abNewEncryptedWithOldLmOwf,
|
||
|
sizeof( pwb->changepw.v2.abNewEncryptedWithOldLmOwf ) );
|
||
|
|
||
|
ZeroMemory( pwb->changepw.v2.abOldLmOwfEncryptedWithNewNtOwf,
|
||
|
sizeof( pwb->changepw.v2.abOldLmOwfEncryptedWithNewNtOwf ));
|
||
|
|
||
|
ZeroMemory( pwb->changepw.v2.abLmResponse,
|
||
|
sizeof( pwb->changepw.v2.abLmResponse ) );
|
||
|
|
||
|
HostToWireFormat16( CPW2F_UseNtResponse, pwb->changepw.v2.abFlags );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WORD wf = 0;
|
||
|
|
||
|
if (fLmPresent)
|
||
|
wf |= CPW2F_LmPasswordPresent;
|
||
|
|
||
|
if (fbUseNtResponse)
|
||
|
wf |= CPW2F_UseNtResponse;
|
||
|
|
||
|
HostToWireFormat16( wf, pwb->changepw.v2.abFlags );
|
||
|
}
|
||
|
|
||
|
memcpy( pSendBuf->Data, &pwb->changepw.v2, sizeof(CHANGEPW2) );
|
||
|
|
||
|
/* Fill in the header.
|
||
|
*/
|
||
|
pSendBuf->Code = (BYTE )CHAPCODE_ChangePw2;
|
||
|
pSendBuf->Id = pwb->bIdToSend;
|
||
|
HostToWireFormat16(
|
||
|
PPP_CONFIG_HDR_LEN + sizeof(CHANGEPW2), pSendBuf->Length );
|
||
|
|
||
|
TRACE("MakeChangePw2Message done(0)");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
MakeChangePw3Message(
|
||
|
IN CHAPWB* pwb,
|
||
|
OUT PPP_CONFIG* pSendBuf,
|
||
|
IN DWORD cbSendBuf,
|
||
|
IN BOOL fTimeout )
|
||
|
|
||
|
/* Builds a ChangePw3 response packet in caller's 'pSendBuf' buffer.
|
||
|
** 'cbSendBuf' is the length of caller's buffer. 'pwb' is the address of
|
||
|
** the work buffer associated with the port.
|
||
|
**
|
||
|
** Returns 0 if successful, or a non-0 error code.
|
||
|
*/
|
||
|
{
|
||
|
DWORD dwErr;
|
||
|
BOOLEAN fLmPresent;
|
||
|
BYTE fbUseNtResponse;
|
||
|
BYTE bRandomNumber[16];
|
||
|
SAMPR_ENCRYPTED_USER_PASSWORD abNewEncryptedWithOldLmOwf;
|
||
|
ENCRYPTED_NT_OWF_PASSWORD abOldLmOwfEncryptedWithNewNtOwf;
|
||
|
|
||
|
TRACE("MakeChangePw3Message...");
|
||
|
ASSERT(cbSendBuf>=PPP_CONFIG_HDR_LEN+sizeof(CHANGEPW2));
|
||
|
|
||
|
(void )cbSendBuf;
|
||
|
|
||
|
DecodePw( pwb->chSeed, pwb->szOldPassword );
|
||
|
DecodePw( pwb->chSeed, pwb->szPassword );
|
||
|
|
||
|
dwErr =
|
||
|
GetEncryptedPasswordsForChangePassword2(
|
||
|
pwb->szOldPassword,
|
||
|
pwb->szPassword,
|
||
|
(SAMPR_ENCRYPTED_USER_PASSWORD* )
|
||
|
pwb->changepw.v3.abEncryptedPassword,
|
||
|
(ENCRYPTED_NT_OWF_PASSWORD* )
|
||
|
pwb->changepw.v3.abEncryptedHash,
|
||
|
&abNewEncryptedWithOldLmOwf,
|
||
|
&abOldLmOwfEncryptedWithNewNtOwf,
|
||
|
&fLmPresent );
|
||
|
|
||
|
if (dwErr == 0)
|
||
|
{
|
||
|
BOOL fEmptyUserName = (pwb->szUserName[ 0 ] == '\0');
|
||
|
A_SHA_CTX SHAContext;
|
||
|
BYTE SHADigest[A_SHA_DIGEST_LEN];
|
||
|
|
||
|
//
|
||
|
// Get 16 byte random number and generate a new challenge if this is
|
||
|
// not a timeout
|
||
|
//
|
||
|
|
||
|
if ( !fTimeout )
|
||
|
{
|
||
|
if ((dwErr = (DWORD )GetChallenge( bRandomNumber )) != 0)
|
||
|
return dwErr;
|
||
|
|
||
|
if ((dwErr = (DWORD )GetChallenge( bRandomNumber+8 )) != 0)
|
||
|
return dwErr;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CopyMemory( bRandomNumber,
|
||
|
pwb->changepw.v3.abPeerChallenge,
|
||
|
sizeof( bRandomNumber ) );
|
||
|
}
|
||
|
|
||
|
A_SHAInit( &SHAContext );
|
||
|
|
||
|
A_SHAUpdate( &SHAContext, bRandomNumber, sizeof( bRandomNumber ) );
|
||
|
|
||
|
A_SHAUpdate( &SHAContext, pwb->abChallenge, pwb->cbChallenge );
|
||
|
|
||
|
A_SHAUpdate( &SHAContext, pwb->szUserName, strlen(pwb->szUserName));
|
||
|
|
||
|
A_SHAFinal( &SHAContext, SHADigest );
|
||
|
|
||
|
CopyMemory( pwb->abComputedChallenge, SHADigest, 8 );
|
||
|
|
||
|
pwb->fSessionKeysObtained = FALSE;
|
||
|
|
||
|
dwErr =
|
||
|
GetChallengeResponse(
|
||
|
g_dwTraceIdChap,
|
||
|
pwb->szUserName,
|
||
|
pwb->szPassword,
|
||
|
&pwb->Luid,
|
||
|
pwb->abComputedChallenge,
|
||
|
( pwb->fConfigInfo & PPPCFG_MachineAuthentication ),
|
||
|
pwb->changepw.v3.abPeerChallenge,
|
||
|
pwb->changepw.v3.abNTResponse,
|
||
|
&fbUseNtResponse,
|
||
|
(PBYTE )&pwb->keyLm,
|
||
|
(PBYTE )&pwb->keyUser );
|
||
|
|
||
|
if (dwErr == 0 && fEmptyUserName)
|
||
|
pwb->fSessionKeysObtained = TRUE;
|
||
|
}
|
||
|
|
||
|
EncodePw( pwb->chSeed, pwb->szOldPassword );
|
||
|
EncodePw( pwb->chSeed, pwb->szPassword );
|
||
|
|
||
|
if (dwErr != 0)
|
||
|
return dwErr;
|
||
|
|
||
|
ZeroMemory( pwb->changepw.v3.abPeerChallenge,
|
||
|
sizeof( pwb->changepw.v3.abPeerChallenge ) );
|
||
|
|
||
|
HostToWireFormat16( 0, pwb->changepw.v3.abFlags );
|
||
|
|
||
|
//
|
||
|
// We are doing new MS-CHAP so fill the LM response field with an
|
||
|
// 16 byte random number
|
||
|
//
|
||
|
|
||
|
CopyMemory( pwb->changepw.v3.abPeerChallenge,
|
||
|
bRandomNumber,
|
||
|
sizeof( bRandomNumber ));
|
||
|
|
||
|
//
|
||
|
// Also copy the NtResponse into pwb->abResponse since this will be
|
||
|
// used by the IsSuccessPakcetValid call.
|
||
|
//
|
||
|
|
||
|
CopyMemory( pwb->abResponse + LM_RESPONSE_LENGTH,
|
||
|
pwb->changepw.v3.abNTResponse,
|
||
|
NT_RESPONSE_LENGTH );
|
||
|
|
||
|
CopyMemory( pSendBuf->Data, &pwb->changepw.v3, sizeof( CHANGEPW3 ) );
|
||
|
|
||
|
/* Fill in the header.
|
||
|
*/
|
||
|
pSendBuf->Code = (BYTE )CHAPCODE_ChangePw3;
|
||
|
pSendBuf->Id = pwb->bIdToSend;
|
||
|
HostToWireFormat16(
|
||
|
PPP_CONFIG_HDR_LEN + sizeof(CHANGEPW3), pSendBuf->Length );
|
||
|
|
||
|
TRACE("MakeChangePw3Message done(0)");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
MakeResponseMessage(
|
||
|
IN CHAPWB* pwb,
|
||
|
OUT PPP_CONFIG* pSendBuf,
|
||
|
IN DWORD cbSendBuf,
|
||
|
IN BOOL fTimeout )
|
||
|
|
||
|
/* Builds a Response packet in caller's 'pSendBuf' buffer. 'cbSendBuf' is
|
||
|
** the length of caller's buffer. 'pwb' is the address of the work
|
||
|
** buffer associated with the port.
|
||
|
**
|
||
|
** Returns 0 if successful, or a non-0 error code.
|
||
|
*/
|
||
|
{
|
||
|
DWORD dwErr;
|
||
|
WORD wLength;
|
||
|
BYTE* pcbResponse;
|
||
|
BYTE* pbResponse;
|
||
|
CHAR* pszName;
|
||
|
CHAR szUserName[ UNLEN + 1 ] = {0};
|
||
|
|
||
|
TRACE("MakeResponseMessage...");
|
||
|
|
||
|
(void )cbSendBuf;
|
||
|
|
||
|
/* Fill in the response.
|
||
|
*/
|
||
|
if ( ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT ) ||
|
||
|
( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW ) )
|
||
|
{
|
||
|
BYTE bRandomNumber[16];
|
||
|
BOOL fEmptyUserName = (pwb->szUserName[ 0 ] == '\0');
|
||
|
|
||
|
/* Microsoft extended CHAP.
|
||
|
*/
|
||
|
ASSERT(cbSendBuf>=PPP_CONFIG_HDR_LEN+1+MSRESPONSELEN+UNLEN+1+DNLEN);
|
||
|
ASSERT(MSRESPONSELEN<=255);
|
||
|
|
||
|
DecodePw( pwb->chSeed, pwb->szPassword );
|
||
|
|
||
|
if ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW )
|
||
|
{
|
||
|
A_SHA_CTX SHAContext;
|
||
|
BYTE SHADigest[A_SHA_DIGEST_LEN];
|
||
|
|
||
|
szUserName[ 0 ] = '\0';
|
||
|
|
||
|
//
|
||
|
// If we do not have a username since we are dialing out using
|
||
|
// the windows' password we get the username now by doing an
|
||
|
// extra call to get challenge response
|
||
|
//
|
||
|
/*
|
||
|
if ( lstrlenA( pwb->szUserName ) == 0 &&
|
||
|
!(pwb->fConfigInfo & PPPCFG_MachineAuthentication)
|
||
|
)
|
||
|
*/
|
||
|
if ( lstrlenA( pwb->szUserName ) == 0 )
|
||
|
{
|
||
|
BYTE abLmResponse[ LM_RESPONSE_LENGTH ];
|
||
|
BYTE abNtResponse[ NT_RESPONSE_LENGTH ];
|
||
|
BYTE bUseNtResponse;
|
||
|
LM_SESSION_KEY keyLm;
|
||
|
USER_SESSION_KEY keyUser;
|
||
|
|
||
|
dwErr =
|
||
|
GetChallengeResponse(
|
||
|
g_dwTraceIdChap,
|
||
|
szUserName,
|
||
|
pwb->szPassword,
|
||
|
&pwb->Luid,
|
||
|
pwb->abChallenge,
|
||
|
( pwb->fConfigInfo & PPPCFG_MachineAuthentication ),
|
||
|
abLmResponse,
|
||
|
abNtResponse,
|
||
|
&bUseNtResponse,
|
||
|
(PBYTE )&keyLm,
|
||
|
(PBYTE )&keyUser );
|
||
|
|
||
|
if ( dwErr != NO_ERROR )
|
||
|
{
|
||
|
return( dwErr );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
strncpy( szUserName, pwb->szUserName, UNLEN );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get 16 byte random number and generate a new challenge if this
|
||
|
// is not a timeout
|
||
|
//
|
||
|
|
||
|
if ( !fTimeout )
|
||
|
{
|
||
|
if ((dwErr = (DWORD )GetChallenge( bRandomNumber )) != 0)
|
||
|
{
|
||
|
return dwErr;
|
||
|
}
|
||
|
|
||
|
if ((dwErr = (DWORD )GetChallenge( bRandomNumber+8 )) != 0)
|
||
|
{
|
||
|
return dwErr;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CopyMemory( bRandomNumber,
|
||
|
pwb->abResponse,
|
||
|
sizeof(bRandomNumber) );
|
||
|
}
|
||
|
{
|
||
|
|
||
|
CHAR szUserNameWoDomain[ UNLEN + DNLEN + 2 ];
|
||
|
CHAR szDomain[ DNLEN + 1 ];
|
||
|
|
||
|
|
||
|
//
|
||
|
//This sucks but the only hacky way of
|
||
|
//doing it without major change... Must look at it for BC
|
||
|
//
|
||
|
ExtractUsernameAndDomain( szUserName,
|
||
|
szUserNameWoDomain,
|
||
|
szDomain );
|
||
|
|
||
|
A_SHAInit( &SHAContext );
|
||
|
|
||
|
A_SHAUpdate( &SHAContext, bRandomNumber, sizeof( bRandomNumber ) );
|
||
|
|
||
|
A_SHAUpdate( &SHAContext, pwb->abChallenge, pwb->cbChallenge );
|
||
|
|
||
|
A_SHAUpdate( &SHAContext, szUserNameWoDomain, strlen( szUserNameWoDomain));
|
||
|
|
||
|
A_SHAFinal( &SHAContext, SHADigest );
|
||
|
|
||
|
CopyMemory( pwb->abComputedChallenge, SHADigest, 8 );
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pwb->fSessionKeysObtained = FALSE;
|
||
|
|
||
|
if ( fEmptyUserName )
|
||
|
{
|
||
|
szUserName[ 0 ] = '\0';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
strncpy( szUserName, pwb->szUserName, UNLEN );
|
||
|
}
|
||
|
|
||
|
dwErr = GetChallengeResponse(
|
||
|
g_dwTraceIdChap,
|
||
|
szUserName,
|
||
|
pwb->szPassword,
|
||
|
&pwb->Luid,
|
||
|
( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW )
|
||
|
? pwb->abComputedChallenge
|
||
|
: pwb->abChallenge,
|
||
|
( pwb->fConfigInfo & PPPCFG_MachineAuthentication ),
|
||
|
pwb->abResponse,
|
||
|
pwb->abResponse + LM_RESPONSE_LENGTH,
|
||
|
pwb->abResponse + LM_RESPONSE_LENGTH + NT_RESPONSE_LENGTH,
|
||
|
(PBYTE )&pwb->keyLm,
|
||
|
(PBYTE )&pwb->keyUser );
|
||
|
|
||
|
TRACE1("GetChallengeResponse=%d",dwErr);
|
||
|
//
|
||
|
//check to see if the domain name in the same as
|
||
|
//local computer name. If so strip it out
|
||
|
//
|
||
|
{
|
||
|
CHAR szUserNameWoDomain[ UNLEN + DNLEN + 2 ];
|
||
|
CHAR szDomain[ DNLEN + 1 ];
|
||
|
|
||
|
//
|
||
|
//This sucks but the only hacky way of
|
||
|
//doing it without major change... Must look at it for BC
|
||
|
//
|
||
|
ExtractUsernameAndDomain( szUserName,
|
||
|
szUserNameWoDomain,
|
||
|
szDomain );
|
||
|
//if the domain name is local machine name
|
||
|
//dont send it across.
|
||
|
if ( !lstrcmpi ( szDomain, szComputerName ) )
|
||
|
{
|
||
|
strncpy ( szUserName, szUserNameWoDomain, UNLEN );
|
||
|
}
|
||
|
//
|
||
|
//Also, if use winlogon is specified
|
||
|
//and we have a domain in the username,
|
||
|
//and domain in domain name then
|
||
|
//strip the domain in username
|
||
|
//
|
||
|
if ( fEmptyUserName ) //winlogon specified
|
||
|
{
|
||
|
if ( szDomain[0] != '\0' &&
|
||
|
pwb->szDomain[ 0 ] != '\0'
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
//we have a domain in username
|
||
|
//and a domain passed in by the user
|
||
|
//
|
||
|
strncpy ( szUserName, szUserNameWoDomain, UNLEN );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
EncodePw( pwb->chSeed, pwb->szPassword );
|
||
|
|
||
|
if (dwErr != 0)
|
||
|
return dwErr;
|
||
|
|
||
|
if ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW )
|
||
|
{
|
||
|
ZeroMemory( pwb->abResponse, LM_RESPONSE_LENGTH );
|
||
|
|
||
|
CopyMemory(pwb->abResponse, bRandomNumber, sizeof(bRandomNumber));
|
||
|
|
||
|
*(pwb->abResponse+LM_RESPONSE_LENGTH+NT_RESPONSE_LENGTH) = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( !( pwb->fConfigInfo & PPPCFG_UseLmPassword ) )
|
||
|
{
|
||
|
//
|
||
|
// Zero out all the LM password stuff since this has been
|
||
|
// cracked
|
||
|
//
|
||
|
|
||
|
ZeroMemory( pwb->abResponse, LM_RESPONSE_LENGTH );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (fEmptyUserName || pwb->fConfigInfo & PPPCFG_MachineAuthentication )
|
||
|
pwb->fSessionKeysObtained = TRUE;
|
||
|
|
||
|
pwb->cbResponse = MSRESPONSELEN;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* MD5 CHAP.
|
||
|
*/
|
||
|
MD5_CTX md5ctx;
|
||
|
|
||
|
ASSERT(cbSendBuf>=PPP_CONFIG_HDR_LEN+1+MD5RESPONSELEN+UNLEN+1+DNLEN);
|
||
|
ASSERT(MD5RESPONSELEN<=255);
|
||
|
|
||
|
DecodePw( pwb->chSeed, pwb->szPassword );
|
||
|
|
||
|
MD5Init( &md5ctx );
|
||
|
MD5Update( &md5ctx, &pwb->bIdToSend, 1 );
|
||
|
MD5Update( &md5ctx, pwb->szPassword, strlen( pwb->szPassword ) );
|
||
|
MD5Update( &md5ctx, pwb->abChallenge, pwb->cbChallenge );
|
||
|
MD5Final( &md5ctx );
|
||
|
|
||
|
EncodePw( pwb->chSeed, pwb->szPassword );
|
||
|
|
||
|
pwb->cbResponse = MD5RESPONSELEN;
|
||
|
memcpy( pwb->abResponse, md5ctx.digest, MD5RESPONSELEN );
|
||
|
|
||
|
strncpy( szUserName, pwb->szUserName, UNLEN );
|
||
|
}
|
||
|
|
||
|
pcbResponse = pSendBuf->Data;
|
||
|
*pcbResponse = pwb->cbResponse;
|
||
|
pbResponse = pcbResponse + 1;
|
||
|
memcpy( pbResponse, pwb->abResponse, *pcbResponse );
|
||
|
|
||
|
/* Fill in the Name in domain\username format. When domain is "", no "\"
|
||
|
** is sent (to facilitate connecting to foreign systems which use a simple
|
||
|
** string identifier). Otherwise when username is "", the "\" is sent,
|
||
|
** i.e. "domain\". This form will currently fail, but could be mapped to
|
||
|
** some sort of "guest" access in the future.
|
||
|
*/
|
||
|
pszName = pbResponse + *pcbResponse;
|
||
|
pszName[ 0 ] = '\0';
|
||
|
|
||
|
if (pwb->szDomain[ 0 ] != '\0')
|
||
|
{
|
||
|
strcpy( pszName, pwb->szDomain );
|
||
|
strcat( pszName, "\\" );
|
||
|
}
|
||
|
|
||
|
strcat( pszName, szUserName );
|
||
|
|
||
|
/* Fill in the header.
|
||
|
*/
|
||
|
pSendBuf->Code = (BYTE )CHAPCODE_Response;
|
||
|
pSendBuf->Id = pwb->bIdToSend;
|
||
|
|
||
|
wLength =
|
||
|
(WORD )(PPP_CONFIG_HDR_LEN + 1 + *pcbResponse + strlen( pszName ));
|
||
|
HostToWireFormat16( wLength, pSendBuf->Length );
|
||
|
|
||
|
DUMPB(pSendBuf,wLength);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
ChapMakeResultMessage(
|
||
|
IN CHAPWB* pwb,
|
||
|
IN DWORD dwError,
|
||
|
IN BOOL fRetry,
|
||
|
OUT PPP_CONFIG* pSendBuf,
|
||
|
IN DWORD cbSendBuf )
|
||
|
|
||
|
/* Builds a result packet (Success or Failure) in caller's 'pSendBuf'
|
||
|
** buffer. 'cbSendBuf' is the length of caller's buffer. 'dwError'
|
||
|
** indicates whether a Success or Failure should be generated, and for
|
||
|
** Failure the failure code to include. 'fRetry' indicates if the client
|
||
|
** should be told he can retry.
|
||
|
**
|
||
|
** Format of the message text portion of the result is:
|
||
|
**
|
||
|
** "E=dddddddddd R=b C=xxxxxxxxxxxxxxxx V=v"
|
||
|
**
|
||
|
** where
|
||
|
**
|
||
|
** 'dddddddddd' is the decimal error code (need not be 10 digits).
|
||
|
**
|
||
|
** 'b' is a boolean flag that is set if a retry is allowed.
|
||
|
**
|
||
|
** 'xxxxxxxxxxxxxxxx' is 16 hex digits representing a new challenge
|
||
|
** value.
|
||
|
**
|
||
|
** 'v' is our version level supported, currently 2.
|
||
|
**
|
||
|
** Note: C=xxxxxxxxxxxxxxxxx not currently provided on server-side. To
|
||
|
** provide what's needed for this routine, add the following two
|
||
|
** parameters to this routine and enable the #if 0 code.
|
||
|
**
|
||
|
** IN BYTE* pNewChallenge,
|
||
|
** IN DWORD cbNewChallenge,
|
||
|
*/
|
||
|
{
|
||
|
CHAR* pchMsg;
|
||
|
WORD wLength;
|
||
|
CHAR* pszReplyMessage = NULL;
|
||
|
DWORD dwNumBytes;
|
||
|
DWORD dwExtraBytes;
|
||
|
RAS_AUTH_ATTRIBUTE* pAttribute;
|
||
|
|
||
|
ASSERT(cbSendBuf>=PPP_CONFIG_HDR_LEN+35);
|
||
|
|
||
|
/* Fill in the header and message. The message is only used if
|
||
|
** unsuccessful in which case it is the decimal RAS error code in ASCII.
|
||
|
*/
|
||
|
pSendBuf->Id = pwb->bIdToSend;
|
||
|
pchMsg = pSendBuf->Data;
|
||
|
|
||
|
if (dwError == 0)
|
||
|
{
|
||
|
pSendBuf->Code = CHAPCODE_Success;
|
||
|
if (pwb->bAlgorithm == PPP_CHAP_DIGEST_MD5)
|
||
|
{
|
||
|
wLength = PPP_CONFIG_HDR_LEN;
|
||
|
}
|
||
|
else if ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT )
|
||
|
{
|
||
|
wLength = PPP_CONFIG_HDR_LEN;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
wLength = PPP_CONFIG_HDR_LEN;
|
||
|
|
||
|
//
|
||
|
// Search for MS-CHAP2-Success attributes
|
||
|
//
|
||
|
|
||
|
pAttribute = RasAuthAttributeGetVendorSpecific(
|
||
|
311,
|
||
|
26,
|
||
|
pwb->pAttributesFromAuthenticator );
|
||
|
|
||
|
if ( ( pAttribute != NULL )
|
||
|
&& ( ((BYTE*)(pAttribute->Value))[5] == 45 ) )
|
||
|
{
|
||
|
CopyMemory(pSendBuf->Data, (BYTE*)(pAttribute->Value) + 7, 42);
|
||
|
wLength += 42;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pSendBuf->Code = CHAPCODE_Failure;
|
||
|
|
||
|
if (pwb->bAlgorithm == PPP_CHAP_DIGEST_MD5)
|
||
|
{
|
||
|
wLength = PPP_CONFIG_HDR_LEN;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CHAR* psz = pchMsg;
|
||
|
|
||
|
strcpy( psz, "E=" );
|
||
|
psz += 2;
|
||
|
_ltoa( (long )dwError, (char* )psz, 10 );
|
||
|
psz = strchr( psz, '\0' );
|
||
|
|
||
|
strcat( psz,
|
||
|
(dwError != ERROR_PASSWD_EXPIRED && fRetry)
|
||
|
? " R=1 " : " R=0 " );
|
||
|
psz = strchr( psz, '\0' );
|
||
|
|
||
|
//
|
||
|
// Search for MS-CHAP Error attributes
|
||
|
//
|
||
|
|
||
|
pAttribute = RasAuthAttributeGetVendorSpecific(
|
||
|
311,
|
||
|
2,
|
||
|
pwb->pAttributesFromAuthenticator );
|
||
|
|
||
|
if ( pAttribute != NULL )
|
||
|
{
|
||
|
//
|
||
|
// If one was sent then use the C= portion onwards in the
|
||
|
// response
|
||
|
//
|
||
|
|
||
|
CHAR chErrorBuffer[150];
|
||
|
CHAR* pszValue;
|
||
|
DWORD cbError = (DWORD)*(((PBYTE)(pAttribute->Value))+5);
|
||
|
|
||
|
//
|
||
|
// Leave one byte for NULL terminator
|
||
|
//
|
||
|
|
||
|
if ( cbError > sizeof( chErrorBuffer ) - 1 )
|
||
|
{
|
||
|
cbError = sizeof( chErrorBuffer ) - 1;
|
||
|
}
|
||
|
|
||
|
ZeroMemory( chErrorBuffer, sizeof( chErrorBuffer ) );
|
||
|
|
||
|
CopyMemory( chErrorBuffer,
|
||
|
(CHAR *)((PBYTE)(pAttribute->Value) + 7),
|
||
|
cbError );
|
||
|
|
||
|
if ( ( pszValue = strstr( chErrorBuffer, "C=" ) ) != NULL )
|
||
|
{
|
||
|
strcat( psz, pszValue );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW ) &&
|
||
|
( ( fRetry ) || ( dwError == ERROR_PASSWD_EXPIRED ) ) )
|
||
|
{
|
||
|
CHAR* pszHex = "0123456789ABCDEF";
|
||
|
INT i;
|
||
|
BYTE * pNewChallenge;
|
||
|
|
||
|
strcat( psz, "C=" );
|
||
|
|
||
|
if ( !(pwb->fNewChallengeProvided ) )
|
||
|
{
|
||
|
(DWORD )GetChallenge( pwb->abChallenge );
|
||
|
|
||
|
(DWORD )GetChallenge( pwb->abChallenge+8 );
|
||
|
|
||
|
pwb->fNewChallengeProvided = TRUE;
|
||
|
}
|
||
|
|
||
|
psz = strchr( psz, '\0' );
|
||
|
|
||
|
pNewChallenge = pwb->abChallenge;
|
||
|
|
||
|
for (i = 0; i < pwb->cbChallenge; ++i)
|
||
|
{
|
||
|
*psz++ = pszHex[ *pNewChallenge / 16 ];
|
||
|
*psz++ = pszHex[ *pNewChallenge % 16 ];
|
||
|
++pNewChallenge;
|
||
|
}
|
||
|
|
||
|
*psz = '\0';
|
||
|
|
||
|
strcat( psz, " V=3" );
|
||
|
}
|
||
|
|
||
|
if ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT )
|
||
|
{
|
||
|
if (( pszValue=strstr( chErrorBuffer, "V=" ) ) != NULL )
|
||
|
{
|
||
|
strcat( psz, " " );
|
||
|
|
||
|
strcat( psz, pszValue );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( dwError == ERROR_PASSWD_EXPIRED )
|
||
|
{
|
||
|
if ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT )
|
||
|
{
|
||
|
strcat( psz, " V=2" );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
strcat( psz, " V=3" );
|
||
|
}
|
||
|
|
||
|
psz = strchr( psz, '\0' );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
wLength = (WORD)(PPP_CONFIG_HDR_LEN + strlen( pchMsg ));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pszReplyMessage = RasAuthAttributeGetConcatString(
|
||
|
raatReplyMessage, pwb->pAttributesFromAuthenticator,
|
||
|
&dwNumBytes );
|
||
|
|
||
|
if ( ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT )
|
||
|
|| ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW ) )
|
||
|
{
|
||
|
//
|
||
|
// For the string "M="
|
||
|
//
|
||
|
|
||
|
dwExtraBytes = 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dwExtraBytes = 0;
|
||
|
}
|
||
|
|
||
|
if (NULL != pszReplyMessage)
|
||
|
{
|
||
|
if (wLength + dwNumBytes > cbSendBuf)
|
||
|
{
|
||
|
dwNumBytes = cbSendBuf - wLength;
|
||
|
}
|
||
|
|
||
|
if (wLength + dwNumBytes + dwExtraBytes > cbSendBuf)
|
||
|
{
|
||
|
if (dwNumBytes > dwExtraBytes)
|
||
|
{
|
||
|
//
|
||
|
// For the string "M="
|
||
|
//
|
||
|
|
||
|
dwNumBytes -= dwExtraBytes;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// If we cannot insert "M=", we will not insert the reply
|
||
|
// message.
|
||
|
//
|
||
|
|
||
|
dwNumBytes = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (dwNumBytes)
|
||
|
{
|
||
|
if (dwExtraBytes)
|
||
|
{
|
||
|
CopyMemory((BYTE*)pSendBuf + wLength, "M=", dwExtraBytes);
|
||
|
}
|
||
|
|
||
|
CopyMemory((BYTE*)pSendBuf + wLength + dwExtraBytes,
|
||
|
pszReplyMessage, dwNumBytes);
|
||
|
|
||
|
wLength += (WORD)(dwNumBytes + dwExtraBytes);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LocalFree(pszReplyMessage);
|
||
|
|
||
|
HostToWireFormat16( wLength, pSendBuf->Length );
|
||
|
DUMPB(pSendBuf,wLength);
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
ChapSMakeMessage(
|
||
|
IN CHAPWB* pwb,
|
||
|
IN PPP_CONFIG* pReceiveBuf,
|
||
|
OUT PPP_CONFIG* pSendBuf,
|
||
|
IN DWORD cbSendBuf,
|
||
|
OUT PPPAP_RESULT* pResult,
|
||
|
IN PPPAP_INPUT* pInput )
|
||
|
|
||
|
/* Server side "make message" entry point. See RasCp interface
|
||
|
** documentation.
|
||
|
*/
|
||
|
{
|
||
|
DWORD dwErr = 0;
|
||
|
|
||
|
switch (pwb->state)
|
||
|
{
|
||
|
case CS_Initial:
|
||
|
{
|
||
|
TRACE("CS_Initial...");
|
||
|
pwb->bIdToSend = (BYTE)(pwb->dwInitialPacketId++);
|
||
|
pwb->bIdExpected = pwb->bIdToSend;
|
||
|
|
||
|
if ((dwErr = MakeChallengeMessage(
|
||
|
pwb, pSendBuf, cbSendBuf )) != 0)
|
||
|
{
|
||
|
return dwErr;
|
||
|
}
|
||
|
|
||
|
pResult->Action = APA_SendWithTimeout;
|
||
|
pwb->result.bIdExpected = pwb->bIdExpected;
|
||
|
pwb->state = CS_ChallengeSent;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CS_ChallengeSent:
|
||
|
case CS_Retry:
|
||
|
case CS_ChangePw:
|
||
|
{
|
||
|
TRACE1("CS_%s...",(pwb->state==CS_Retry)
|
||
|
?"Retry"
|
||
|
:(pwb->state==CS_ChallengeSent)?"ChallengeSent":"ChangePw");
|
||
|
|
||
|
if (!pReceiveBuf)
|
||
|
{
|
||
|
//
|
||
|
// Ignore this event if in these states
|
||
|
//
|
||
|
|
||
|
if ( ( pInput != NULL ) && ( pInput->fAuthenticationComplete ) )
|
||
|
{
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (pwb->state != CS_ChallengeSent)
|
||
|
{
|
||
|
ChapMakeResultMessage(
|
||
|
pwb, pwb->result.dwError, pwb->result.fRetry,
|
||
|
pSendBuf, cbSendBuf );
|
||
|
|
||
|
*pResult = pwb->result;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Timeout waiting for a Response message. Send a new
|
||
|
** Challenge.
|
||
|
*/
|
||
|
pwb->state = CS_Initial;
|
||
|
return ChapSMakeMessage(
|
||
|
pwb, pReceiveBuf, pSendBuf, cbSendBuf, pResult, NULL );
|
||
|
}
|
||
|
|
||
|
if ((pwb->state == CS_ChangePw
|
||
|
&& pReceiveBuf->Code != CHAPCODE_ChangePw1
|
||
|
&& pReceiveBuf->Code != CHAPCODE_ChangePw2
|
||
|
&& pReceiveBuf->Code != CHAPCODE_ChangePw3)
|
||
|
|| (pwb->state != CS_ChangePw
|
||
|
&& pReceiveBuf->Code != CHAPCODE_Response)
|
||
|
|| pReceiveBuf->Id != pwb->bIdExpected)
|
||
|
{
|
||
|
/* Not the packet we're looking for, wrong code or sequence
|
||
|
** number. Silently discard it.
|
||
|
*/
|
||
|
TRACE2("Got ID %d when expecting %d",
|
||
|
pReceiveBuf->Id,pwb->bIdExpected);
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (pwb->state == CS_ChangePw)
|
||
|
{
|
||
|
if (pReceiveBuf->Code == CHAPCODE_ChangePw1)
|
||
|
{
|
||
|
if ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW )
|
||
|
{
|
||
|
return( ERROR_AUTHENTICATION_FAILURE );
|
||
|
}
|
||
|
|
||
|
/* Extract encrypted passwords and options from received
|
||
|
** packet.
|
||
|
*/
|
||
|
if ((dwErr = GetInfoFromChangePw1(
|
||
|
pReceiveBuf, &pwb->changepw.v1 )) != 0)
|
||
|
{
|
||
|
/* The packet is corrupt. Silently discard it.
|
||
|
*/
|
||
|
TRACE("Corrupt packet");
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Change the user's password.
|
||
|
*/
|
||
|
{
|
||
|
WORD wPwLen =
|
||
|
WireToHostFormat16(
|
||
|
pwb->changepw.v1.abPasswordLength );
|
||
|
WORD wFlags =
|
||
|
WireToHostFormat16( pwb->changepw.v1.abFlags )
|
||
|
& CPW1F_UseNtResponse;
|
||
|
|
||
|
if ( MakeChangePasswordV1RequestAttributes(
|
||
|
pwb,
|
||
|
pReceiveBuf->Id,
|
||
|
pwb->szUserName,
|
||
|
pwb->abChallenge,
|
||
|
(PENCRYPTED_LM_OWF_PASSWORD )
|
||
|
pwb->changepw.v1.abEncryptedLmOwfOldPw,
|
||
|
(PENCRYPTED_LM_OWF_PASSWORD )
|
||
|
pwb->changepw.v1.abEncryptedLmOwfNewPw,
|
||
|
(PENCRYPTED_NT_OWF_PASSWORD )
|
||
|
pwb->changepw.v1.abEncryptedNtOwfOldPw,
|
||
|
(PENCRYPTED_NT_OWF_PASSWORD )
|
||
|
pwb->changepw.v1.abEncryptedNtOwfNewPw,
|
||
|
wPwLen, wFlags,
|
||
|
pwb->cbChallenge,
|
||
|
pwb->abChallenge ) != NO_ERROR )
|
||
|
{
|
||
|
dwErr = pwb->result.dwError =
|
||
|
ERROR_CHANGING_PASSWORD;
|
||
|
}
|
||
|
|
||
|
*(pwb->abResponse + LM_RESPONSE_LENGTH +
|
||
|
NT_RESPONSE_LENGTH) = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else if ( pReceiveBuf->Code == CHAPCODE_ChangePw2 )
|
||
|
{
|
||
|
/* Extract encrypted passwords and options from received
|
||
|
** packet.
|
||
|
*/
|
||
|
if ((dwErr = GetInfoFromChangePw2(
|
||
|
pReceiveBuf, &pwb->changepw.v2,
|
||
|
pwb->abResponse )) != 0)
|
||
|
{
|
||
|
/* The packet is corrupt. Silently discard it.
|
||
|
*/
|
||
|
TRACE("Corrupt packet");
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( dwErr == NO_ERROR )
|
||
|
{
|
||
|
/* Change the user's password.
|
||
|
*/
|
||
|
|
||
|
if ( MakeChangePasswordV2RequestAttributes(
|
||
|
pwb,
|
||
|
pReceiveBuf->Id,
|
||
|
pwb->szUserName,
|
||
|
(SAMPR_ENCRYPTED_USER_PASSWORD* )
|
||
|
pwb->changepw.v2.abNewEncryptedWithOldNtOwf,
|
||
|
(ENCRYPTED_NT_OWF_PASSWORD* )
|
||
|
pwb->changepw.v2.abOldNtOwfEncryptedWithNewNtOwf,
|
||
|
(SAMPR_ENCRYPTED_USER_PASSWORD* )
|
||
|
pwb->changepw.v2.abNewEncryptedWithOldLmOwf,
|
||
|
(ENCRYPTED_NT_OWF_PASSWORD* )
|
||
|
pwb->changepw.v2.abOldLmOwfEncryptedWithNewNtOwf,
|
||
|
pwb->cbChallenge,
|
||
|
pwb->abChallenge,
|
||
|
pwb->abResponse,
|
||
|
WireToHostFormat16( pwb->changepw.v2.abFlags )
|
||
|
) != NO_ERROR )
|
||
|
{
|
||
|
dwErr = pwb->result.dwError =
|
||
|
ERROR_CHANGING_PASSWORD;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if ( pReceiveBuf->Code == CHAPCODE_ChangePw3 )
|
||
|
{
|
||
|
/* Extract encrypted passwords and options from received
|
||
|
** packet.
|
||
|
*/
|
||
|
if ((dwErr = GetInfoFromChangePw3(
|
||
|
pReceiveBuf, &pwb->changepw.v3,
|
||
|
pwb->abResponse )) != 0)
|
||
|
{
|
||
|
/* The packet is corrupt. Silently discard it.
|
||
|
*/
|
||
|
TRACE("Corrupt packet");
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Change the user's password.
|
||
|
*/
|
||
|
|
||
|
if ( MakeChangePasswordV3RequestAttributes(
|
||
|
pwb,
|
||
|
pReceiveBuf->Id,
|
||
|
pwb->szUserName,
|
||
|
&pwb->changepw.v3,
|
||
|
pwb->cbChallenge,
|
||
|
pwb->abChallenge
|
||
|
) != NO_ERROR )
|
||
|
{
|
||
|
dwErr = pwb->result.dwError = ERROR_CHANGING_PASSWORD;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* The packet is corrupt. Silently discard it.
|
||
|
*/
|
||
|
TRACE("Corrupt packet");
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( dwErr == 0 )
|
||
|
{
|
||
|
pResult->pUserAttributes = pwb->pUserAttributes;
|
||
|
pResult->Action = APA_Authenticate;
|
||
|
pwb->state = CS_WaitForAuthenticationToComplete1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pwb->result.bIdExpected = pwb->bIdToSend = pwb->bIdExpected;
|
||
|
pwb->result.Action = APA_SendAndDone;
|
||
|
pwb->result.fRetry = FALSE;
|
||
|
pwb->state = CS_Done;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Extract user's credentials from received packet.
|
||
|
*/
|
||
|
if ((dwErr = GetCredentialsFromResponse(
|
||
|
pReceiveBuf, pwb->bAlgorithm,
|
||
|
pwb->szUserName, pwb->abResponse )) != 0)
|
||
|
{
|
||
|
if (dwErr == ERRORBADPACKET)
|
||
|
{
|
||
|
/* The packet is corrupt. Silently discard it.
|
||
|
*/
|
||
|
TRACE("Corrupt packet");
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT ) ||
|
||
|
( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW ) )
|
||
|
{
|
||
|
/* Update to the implied challenge if processing a retry.
|
||
|
*/
|
||
|
if ( ( pwb->state == CS_Retry ) &&
|
||
|
( !pwb->fNewChallengeProvided ) )
|
||
|
pwb->abChallenge[ 0 ] += 23;
|
||
|
|
||
|
/* Check user's credentials with the system, recording the
|
||
|
** outcome in the work buffer in case the result packet
|
||
|
** must be regenerated later.
|
||
|
*/
|
||
|
if ((dwErr = MakeAuthenticationRequestAttributes(
|
||
|
pwb,
|
||
|
TRUE,
|
||
|
pwb->bAlgorithm,
|
||
|
pwb->szUserName,
|
||
|
pwb->abChallenge,
|
||
|
pwb->cbChallenge,
|
||
|
pwb->abResponse,
|
||
|
MSRESPONSELEN,
|
||
|
pReceiveBuf->Id )) != 0)
|
||
|
{
|
||
|
return dwErr;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Check user's credentials with the system, recording the
|
||
|
** outcome in the work buffer in case the result packet
|
||
|
** must be regenerated later.
|
||
|
*/
|
||
|
if ((dwErr = MakeAuthenticationRequestAttributes(
|
||
|
pwb,
|
||
|
FALSE,
|
||
|
pwb->bAlgorithm,
|
||
|
pwb->szUserName,
|
||
|
pwb->abChallenge,
|
||
|
pwb->cbChallenge,
|
||
|
pwb->abResponse,
|
||
|
MD5RESPONSELEN,
|
||
|
pReceiveBuf->Id )) != 0)
|
||
|
{
|
||
|
return dwErr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
strcpy( pwb->result.szUserName, pwb->szUserName );
|
||
|
|
||
|
pResult->pUserAttributes = pwb->pUserAttributes;
|
||
|
pResult->Action = APA_Authenticate;
|
||
|
pwb->state = CS_WaitForAuthenticationToComplete2;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CS_WaitForAuthenticationToComplete1:
|
||
|
case CS_WaitForAuthenticationToComplete2:
|
||
|
{
|
||
|
if ( pInput != NULL )
|
||
|
{
|
||
|
if ( pInput->fAuthenticationComplete )
|
||
|
{
|
||
|
strcpy( pResult->szUserName, pwb->result.szUserName );
|
||
|
|
||
|
if ( pInput->dwAuthError != NO_ERROR )
|
||
|
{
|
||
|
return( pInput->dwAuthError );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pwb->pAttributesFromAuthenticator =
|
||
|
pInput->pAttributesFromAuthenticator;
|
||
|
|
||
|
if ( pInput->dwAuthResultCode != NO_ERROR )
|
||
|
{
|
||
|
if ( ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT ) ||
|
||
|
( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW ) )
|
||
|
{
|
||
|
dwErr = GetErrorCodeFromAttributes( pwb );
|
||
|
|
||
|
if ( dwErr != NO_ERROR )
|
||
|
{
|
||
|
return( dwErr );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pwb->result.dwError = pInput->dwAuthResultCode;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pwb->result.dwError = NO_ERROR;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pResult->Action = APA_NoAction;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( pwb->state == CS_WaitForAuthenticationToComplete1 )
|
||
|
{
|
||
|
pwb->result.bIdExpected = pwb->bIdToSend = pwb->bIdExpected;
|
||
|
pwb->result.Action = APA_SendAndDone;
|
||
|
pwb->result.fRetry = FALSE;
|
||
|
pwb->state = CS_Done;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pwb->bIdToSend = pwb->bIdExpected;
|
||
|
|
||
|
TRACE2("Result=%d,Tries=%d",pwb->result.dwError,
|
||
|
pwb->dwTriesLeft);
|
||
|
|
||
|
if (pwb->result.dwError == ERROR_PASSWD_EXPIRED)
|
||
|
{
|
||
|
pwb->fNewChallengeProvided = FALSE;
|
||
|
pwb->dwTriesLeft = 0;
|
||
|
++pwb->bIdExpected;
|
||
|
pwb->result.bIdExpected = pwb->bIdExpected;
|
||
|
pwb->result.Action = APA_SendWithTimeout2;
|
||
|
pwb->result.fRetry = FALSE;
|
||
|
pwb->state = CS_ChangePw;
|
||
|
}
|
||
|
else if (pwb->bAlgorithm == PPP_CHAP_DIGEST_MD5
|
||
|
|| pwb->result.dwError != ERROR_AUTHENTICATION_FAILURE
|
||
|
|| pwb->dwTriesLeft == 0)
|
||
|
{
|
||
|
/* Passed or failed in a non-retry-able manner.
|
||
|
*/
|
||
|
pwb->result.Action = APA_SendAndDone;
|
||
|
pwb->result.fRetry = FALSE;
|
||
|
pwb->state = CS_Done;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Retry-able failure.
|
||
|
*/
|
||
|
pwb->fNewChallengeProvided = FALSE;
|
||
|
--pwb->dwTriesLeft;
|
||
|
++pwb->bIdExpected;
|
||
|
pwb->result.bIdExpected = pwb->bIdExpected;
|
||
|
pwb->result.Action = APA_SendWithTimeout2;
|
||
|
pwb->result.fRetry = TRUE;
|
||
|
pwb->state = CS_Retry;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* ...fall thru...
|
||
|
*/
|
||
|
|
||
|
case CS_Done:
|
||
|
{
|
||
|
TRACE("CS_Done...");
|
||
|
|
||
|
//
|
||
|
// If we received a packet or the back-end authenticator completed
|
||
|
//
|
||
|
|
||
|
if ( ( pReceiveBuf != NULL ) ||
|
||
|
( ( pInput != NULL ) && ( pInput->fAuthenticationComplete ) ) )
|
||
|
{
|
||
|
/* Build the Success or Failure packet. The same packet sent in
|
||
|
** response to the first Response message with this ID is sent
|
||
|
** regardless of any change in credentials (per CHAP spec).
|
||
|
*/
|
||
|
ChapMakeResultMessage(
|
||
|
pwb, pwb->result.dwError,
|
||
|
pwb->result.fRetry, pSendBuf, cbSendBuf );
|
||
|
|
||
|
*pResult = pwb->result;
|
||
|
|
||
|
CopyMemory( pResult->abResponse,
|
||
|
pwb->abResponse+LM_RESPONSE_LENGTH,
|
||
|
NT_RESPONSE_LENGTH );
|
||
|
|
||
|
CopyMemory( pResult->abChallenge,
|
||
|
pwb->abChallenge,
|
||
|
sizeof( pResult->abChallenge ) );
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pResult->Action = APA_NoAction;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
StoreCredentials(
|
||
|
OUT CHAPWB* pwb,
|
||
|
IN PPPAP_INPUT* pInput )
|
||
|
|
||
|
/* Transfer credentials from 'pInput' format to 'pwb' format.
|
||
|
**
|
||
|
** Returns 0 if successful, false otherwise.
|
||
|
*/
|
||
|
{
|
||
|
/* Validate credential lengths. The credential strings will never be
|
||
|
** NULL, but may be "".
|
||
|
*/
|
||
|
if (strlen( pInput->pszUserName ) > UNLEN
|
||
|
|| strlen( pInput->pszDomain ) > DNLEN
|
||
|
|| strlen( pInput->pszPassword ) > PWLEN
|
||
|
|| strlen( pInput->pszOldPassword ) > PWLEN)
|
||
|
{
|
||
|
return ERROR_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If are doing MS-CHAP V2, then we need to parse the username field if no
|
||
|
// domain was supplied.
|
||
|
// Bug# 310113 RAS: "domain\username" syntax fails to authenticate
|
||
|
//
|
||
|
|
||
|
if ( pwb->bAlgorithm == PPP_CHAP_DIGEST_MSEXT_NEW )
|
||
|
{
|
||
|
//
|
||
|
// If there is no domain, then parse the username to see if it contains the
|
||
|
// domain field.
|
||
|
//
|
||
|
|
||
|
if ( strlen( pInput->pszDomain ) == 0 )
|
||
|
{
|
||
|
if ( ExtractUsernameAndDomain( pInput->pszUserName,
|
||
|
pwb->szUserName,
|
||
|
pwb->szDomain ) != NO_ERROR )
|
||
|
{
|
||
|
strcpy( pwb->szUserName, pInput->pszUserName );
|
||
|
strcpy( pwb->szDomain, pInput->pszDomain );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
strcpy( pwb->szUserName, pInput->pszUserName );
|
||
|
strcpy( pwb->szDomain, pInput->pszDomain );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
strcpy( pwb->szUserName, pInput->pszUserName );
|
||
|
strcpy( pwb->szDomain, pInput->pszDomain );
|
||
|
}
|
||
|
|
||
|
strcpy( pwb->szPassword, pInput->pszPassword );
|
||
|
strcpy( pwb->szOldPassword, pInput->pszOldPassword );
|
||
|
EncodePw( pwb->chSeed, pwb->szPassword );
|
||
|
EncodePw( pwb->chSeed, pwb->szOldPassword );
|
||
|
|
||
|
return 0;
|
||
|
}
|