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