|
|
/* Copyright (c) 1993, Microsoft Corporation, all rights reserved
** ** raspap.c ** Remote Access PPP Password Authentication Protocol ** Core routines ** ** 11/05/93 Steve Cobb */
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntlsa.h>
#include <ntmsv1_0.h>
#include <crypt.h>
#include <windows.h>
#include <lmcons.h>
#include <string.h>
#include <stdlib.h>
#include <rasman.h>
#include <pppcp.h>
#include <rtutils.h>
#define INCL_PWUTIL
#define INCL_HOSTWIRE
#define INCL_RASAUTHATTRIBUTES
#define INCL_MISC
#include <ppputil.h>
#include <rasauth.h>
#define SDEBUGGLOBALS
#define RASPAPGLOBALS
#include "raspap.h"
#include <raserror.h>
#define TRACE_RASPAP (0x00010000|TRACE_USE_MASK|TRACE_USE_MSEC|TRACE_USE_DATE)
#define TRACE(a) TracePrintfExA(g_dwTraceIdPap,TRACE_RASPAP,a )
#define TRACE1(a,b) TracePrintfExA(g_dwTraceIdPap,TRACE_RASPAP,a,b )
#define TRACE2(a,b,c) TracePrintfExA(g_dwTraceIdPap,TRACE_RASPAP,a,b,c )
#define TRACE3(a,b,c,d) TracePrintfExA(g_dwTraceIdPap,TRACE_RASPAP,a,b,c,d )
#define DUMPW(X,Y) TraceDumpExA(g_dwTraceIdPap,1,(LPBYTE)X,Y,4,1,NULL)
#define DUMPB(X,Y) TraceDumpExA(g_dwTraceIdPap,1,(LPBYTE)X,Y,1,1,NULL)
#define REGKEY_Pap \
"SYSTEM\\CurrentControlSet\\Services\\RasMan\\PPP\\ControlProtocols\\BuiltIn" #define REGVAL_FollowStrictSequencing "FollowStrictSequencing"
#define STRSAFE_NO_DEPRECATE
#include "strsafe.h"
/*---------------------------------------------------------------------------
** External entry points **--------------------------------------------------------------------------- */
DWORD PapInit( BOOL fInitialize)
{ if (fInitialize) { HKEY hkey; DWORD dwType; DWORD dwValue; DWORD cb = sizeof(DWORD);
if (RegOpenKey( HKEY_LOCAL_MACHINE, REGKEY_Pap, &hkey ) == 0) { if (RegQueryValueEx( hkey, REGVAL_FollowStrictSequencing, NULL, &dwType, (LPBYTE )&dwValue, &cb ) == 0 && dwType == REG_DWORD && cb == sizeof(DWORD) && dwValue) { fFollowStrictSequencing = TRUE; }
RegCloseKey( hkey ); }
g_dwTraceIdPap = TraceRegisterA( "RASPAP" ); } else { if ( g_dwTraceIdPap != INVALID_TRACEID ) { TraceDeregisterA( g_dwTraceIdPap ); g_dwTraceIdPap = INVALID_TRACEID; } }
return(NO_ERROR); }
DWORD PapGetInfo( IN DWORD dwProtocolId, OUT PPPCP_INFO* pInfo )
/* PapGetInfo entry point called by the PPP engine. See RasCp
** interface documentation. */ { TRACE(("PAP: PapGetInfo\n"));
ZeroMemory( pInfo, sizeof(*pInfo) );
pInfo->Protocol = (DWORD )PPP_PAP_PROTOCOL; pInfo->Recognize = MAXPAPCODE + 1; pInfo->RasCpInit = PapInit; pInfo->RasCpBegin = PapBegin; pInfo->RasCpEnd = PapEnd; pInfo->RasApMakeMessage = PapMakeMessage;
return 0; }
DWORD PapBegin( OUT VOID** ppWorkBuf, IN VOID* pInfo )
/* RasCpBegin entry point called by the PPP engine thru the passed
** address. See RasCp interface documentation. */ { PPPAP_INPUT* pInput = (PPPAP_INPUT* )pInfo; PAPWB* pwb;
/* Allocate work buffer.
*/ if (!(pwb = (PAPWB* )LocalAlloc( LPTR, sizeof(PAPWB) ))) return ERROR_NOT_ENOUGH_MEMORY;
pwb->fServer = pInput->fServer; // pwb->chSeed = GEN_RAND_ENCODE_SEED;
if (!pwb->fServer) { DWORD cbPassword; PBYTE pbPassword = NULL; DWORD dwErr = NO_ERROR; TRACE2("PAP: PapBegin(u=%s,d=%s\n",pInput->pszUserName ,pInput->pszDomain);
/* Validate credential lengths. The credential strings will never be
** NULL, but may be "". ** ** !!! PAP requires the domain\username length to fit in a byte. ** Currently, UNLEN is defined as 256 and DNLEN is defined as 15. ** This means that some valid domain\username combinations cannot ** be validated over PAP, but it's only on *really* long ** usernames. Likewise, a password of exactly 256 characters ** cannot be validated. */ { DWORD cbUserName = strlen( pInput->pszUserName ); DWORD cbDomain = strlen( pInput->pszDomain ); cbPassword = strlen( pInput->pszPassword );
if (cbUserName > UNLEN || cbDomain > DNLEN || cbDomain + 1 + cbUserName > 255 || cbPassword > max( PWLEN, 255 )) { LocalFree( pwb ); return ERROR_INVALID_PARAMETER; } }
/* "Account" refers to the 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. */ if (*(pInput->pszDomain) != '\0') { strcpy( pwb->szAccount, pInput->pszDomain ); strcat( pwb->szAccount, "\\" ); } strcat( pwb->szAccount, pInput->pszUserName ); // strcpy( pwb->szPassword, pInput->pszPassword );
// EncodePw( pwb->chSeed, pwb->szPassword );
dwErr = EncodePassword(strlen(pInput->pszPassword) + 1, pInput->pszPassword, &pwb->DBPassword); if(dwErr != NO_ERROR) { return dwErr; } } else { pwb->hPort = pInput->hPort; }
pwb->state = PS_Initial;
/* Register work buffer with engine.
*/ *ppWorkBuf = pwb; return 0; }
DWORD PapEnd( IN VOID* pWorkBuf )
/* RasCpEnd entry point called by the PPP engine thru the passed address.
** See RasCp interface documentation. */ { TRACE("PAP: PapEnd\n");
if ( pWorkBuf != NULL ) { PAPWB* pwb = (PAPWB* )pWorkBuf;
if ( pwb->pUserAttributes != NULL ) { RasAuthAttributeDestroy( pwb->pUserAttributes );
pwb->pUserAttributes = NULL; }
FreePassword(&pwb->DBPassword); RtlSecureZeroMemory( pWorkBuf, sizeof(PAPWB) );
LocalFree( (HLOCAL )pWorkBuf ); }
return 0; }
DWORD PapMakeMessage( 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. */ { PAPWB* pwb = (PAPWB* )pWorkBuf;
TRACE1("PAP: PapMakeMessage,RBuf=%p\n",pReceiveBuf);
(void )pInput;
return (pwb->fServer) ? PapSMakeMessage(pwb, pReceiveBuf, pSendBuf, cbSendBuf, pInput, pResult) : PapCMakeMessage( pwb, pReceiveBuf, pSendBuf, cbSendBuf, pResult ); }
/*---------------------------------------------------------------------------
** Internal routines (alphabetically) **--------------------------------------------------------------------------- */
DWORD PapCMakeMessage( IN PAPWB* pwb, IN PPP_CONFIG* pReceiveBuf, OUT PPP_CONFIG* pSendBuf, IN DWORD cbSendBuf, OUT PPPAP_RESULT* pResult )
/* Client side "make message" entry point. See RasCp interface
** documentation. */ { /* Start over if timeout waiting for a reply.
*/ if (!pReceiveBuf && pwb->state != PS_Initial) pwb->state = PS_Initial;
switch (pwb->state) { case PS_Initial: { /* Send an Authenticate-Req packet, then wait for the reply.
*/ pResult->bIdExpected = BNextIdPap; PapMakeRequestMessage( pwb, pSendBuf, cbSendBuf ); pResult->Action = APA_SendWithTimeout; pwb->state = PS_RequestSent;
break; }
case PS_RequestSent: { //
// pReceiveBuf && added to keep prefast happy
//
if (pReceiveBuf && pReceiveBuf->Id != pwb->bIdSent) { //
// See bug # 22508
//
if ( fFollowStrictSequencing ) { /* Received a packet out of sequence. Silently discard it.
*/ pResult->Action = APA_NoAction; break; } }
pResult->fRetry = FALSE;
PapExtractMessage( pReceiveBuf, pResult );
if (pReceiveBuf && pReceiveBuf->Code == PAPCODE_Ack) { /* Passed authentication.
*/ pResult->Action = APA_Done; pResult->dwError = 0; pwb->state = PS_Done; } else if (pReceiveBuf && pReceiveBuf->Code == PAPCODE_Nak) { /* Failed authentication.
*/ pResult->Action = APA_Done; pResult->dwError = GetErrorFromNak( pReceiveBuf ); pwb->state = PS_Done; } else { /* Received an Authenticate-Req packet. The engine filters
** all others. Shouldn't happen, but silently discard it. */ RTASSERT(!"Bogus pReceiveBuf->Code"); pResult->Action = APA_NoAction; break; }
break; } }
return 0; }
DWORD GetCredentialsFromRequest( IN PPP_CONFIG* pReceiveBuf, OUT CHAR* pszIdentity, OUT CHAR* pszPassword )
/* Fill caller's 'pszIdentity' and 'pszPassword' buffers
** with the username and password in the request packet. ** Caller's buffers should be at least UNLEN+DNLEN+1 and PWLEN bytes long, ** respectively. ** ** Returns 0 if successful, or ERRORBADPACKET if the packet is ** misformatted in any way. */ { BYTE* pcbPeerId; CHAR* pchPeerId; BYTE* pcbPassword; CHAR* pchPassword; WORD cbPacket;
cbPacket = WireToHostFormat16( pReceiveBuf->Length );
/* Parse out username and domain from the peer ID (domain\username or
** username format). */ if (cbPacket < PPP_CONFIG_HDR_LEN + 1) return ERRORBADPACKET;
pcbPeerId = pReceiveBuf->Data; pchPeerId = pcbPeerId + 1;
if (cbPacket < PPP_CONFIG_HDR_LEN + 1 + *pcbPeerId) { return ERRORBADPACKET; }
/* Extract the username.
*/ RTASSERT(*pcbPeerId <= (UNLEN+DNLEN+1)); CopyMemory( pszIdentity, pchPeerId, *pcbPeerId ); pszIdentity[ *pcbPeerId ] = '\0';
/* Extract the password.
*/ if (cbPacket < PPP_CONFIG_HDR_LEN + 1 + *pcbPeerId + 1) return ERRORBADPACKET;
pcbPassword = pchPeerId + *pcbPeerId; pchPassword = pcbPassword + 1; RTASSERT(*pcbPassword<=PWLEN);
if (cbPacket < PPP_CONFIG_HDR_LEN + 1 + *pcbPeerId + 1 + *pcbPassword) return ERRORBADPACKET;
CopyMemory( pszPassword, pchPassword, *pcbPassword ); pszPassword[ *pcbPassword ] = '\0';
return 0; }
DWORD GetErrorFromNak( IN PPP_CONFIG* pReceiveBuf )
/* Returns the RAS error number out of the Message portion of the
** Authenticate-Nak message buffer 'pReceiveBuf' or 0 if none. */ { DWORD dwError = 0; CHAR szBuf[ 255 + 1 ]; BYTE* pcbMsg = pReceiveBuf->Data; WORD cbPacket = WireToHostFormat16( pReceiveBuf->Length );
TRACE("PAP: GetErrorFromNak...\n");
if (cbPacket > PPP_CONFIG_HDR_LEN && *pcbMsg) { CHAR* pchBuf = szBuf; CHAR* pchMsg = pcbMsg + 1; BYTE i;
if (*pcbMsg > 2 && pchMsg[ 0 ] == 'E' || pchMsg[ 1 ] == '=') { for (i = 2; i < *pcbMsg; ++i) { if (pchMsg[ i ] < '0' || pchMsg[ i ] > '9') break;
*pchBuf++ = pchMsg[ i ]; }
*pchBuf = '\0'; dwError = (DWORD )atol( szBuf ); } }
if (dwError == 0) { TRACE("PAP: Error code not found.\n"); dwError = ERROR_AUTHENTICATION_FAILURE; }
TRACE1("PAP: GetErrorFromNak done(%d)\n",dwError); return dwError; }
VOID PapMakeRequestMessage( IN PAPWB* pwb, OUT PPP_CONFIG* pSendBuf, IN DWORD cbSendBuf )
/* Builds a request 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. */ { BYTE* pcbPeerId; CHAR* pchPeerId; BYTE* pcbPassword; CHAR* pchPassword; DWORD cbPassword; PBYTE pbPassword = NULL; DWORD dwErr;
RTASSERT(cbSendBuf>=PPP_CONFIG_HDR_LEN+1+UNLEN+1+DNLEN+1+PWLEN); (void )cbSendBuf;
/* Fill in the peer ID, i.e. the account.
*/ pcbPeerId = pSendBuf->Data; *pcbPeerId = (BYTE )strlen( pwb->szAccount );
pchPeerId = pcbPeerId + 1; strcpy( pchPeerId, pwb->szAccount );
/* Fill in the password.
*/ pcbPassword = pchPeerId + *pcbPeerId;
pchPassword = pcbPassword + 1;
dwErr = DecodePassword(&pwb->DBPassword, &cbPassword, &pbPassword); if(dwErr == NO_ERROR) { strcpy( pchPassword, pbPassword ); *pcbPassword = (BYTE )strlen( pbPassword ); RtlSecureZeroMemory(pbPassword, cbPassword); LocalFree(pbPassword); } else { *pcbPassword = 0; } // DecodePw( pwb->chSeed, pchPassword );
/* Fill in the header.
*/ pSendBuf->Code = (BYTE )PAPCODE_Req; pSendBuf->Id = pwb->bIdSent = BNextIdPap++;
{ WORD wLength = (WORD )(PPP_CONFIG_HDR_LEN + 1 + *pcbPeerId + 1 + *pcbPassword); HostToWireFormat16( wLength, pSendBuf->Length ); TRACE("PAP: Request...\n");//DUMPB(pSendBuf,(DWORD )wLength);
} }
VOID PapMakeResultMessage( IN DWORD dwError, IN BYTE bId, OUT PPP_CONFIG* pSendBuf, IN DWORD cbSendBuf, IN RAS_AUTH_ATTRIBUTE* pAttributesFromAuthenticator)
/* Builds a result packet (Ack or Nak) in caller's 'pSendBuf' buffer.
** 'cbSendBuf' is the length of caller's buffer. 'dwError' indicates ** whether an Ack (0) or Nak (!0) should be generated, and for Nak the ** failure code to include. 'bId' is the packet sequence number of the ** corresponding request packet. pAttributesFromAuthenticator points to ** attributes returned by the authenticator. */ { BYTE* pcbMsg; BYTE cbMsg; CHAR* pchMsg; CHAR* pszReplyMessage = NULL; DWORD dwNumBytes;
RTASSERT(cbSendBuf>=PPP_CONFIG_HDR_LEN+1+10);
/* Fill in the header and message. If unsuccessful, the message is the
** decimal RAS error code in ASCII. */ pSendBuf->Id = bId; pcbMsg = pSendBuf->Data; pchMsg = pcbMsg + 1;
if (dwError == 0) { pSendBuf->Code = PAPCODE_Ack; cbMsg = 0; } else { pSendBuf->Code = PAPCODE_Nak;
strcpy( pchMsg, "E=" ); _ltoa( (long )dwError, (char* )pchMsg + 2, 10 );
cbMsg = (BYTE )strlen( pchMsg ); }
if (pAttributesFromAuthenticator != NULL) { pszReplyMessage = RasAuthAttributeGetConcatString( raatReplyMessage, pAttributesFromAuthenticator, &dwNumBytes ); }
if (NULL != pszReplyMessage) { if (dwNumBytes + cbMsg > 0xFF) { dwNumBytes = 0xFF - cbMsg; }
if (dwNumBytes > cbSendBuf - PPP_CONFIG_HDR_LEN - 1 - cbMsg) { dwNumBytes = cbSendBuf - PPP_CONFIG_HDR_LEN - 1 - cbMsg; }
CopyMemory(pchMsg + cbMsg, pszReplyMessage, dwNumBytes);
cbMsg += (BYTE)dwNumBytes; }
LocalFree(pszReplyMessage);
{ WORD wLength = (WORD )(PPP_CONFIG_HDR_LEN + 1 + cbMsg); HostToWireFormat16( wLength, (PBYTE )pSendBuf->Length ); *pcbMsg = cbMsg; TRACE("PAP: Result...\n");DUMPB(pSendBuf,(DWORD )wLength); } }
VOID PapExtractMessage( IN PPP_CONFIG* pReceiveBuf, OUT PPPAP_RESULT* pResult ) { DWORD dwNumBytes; CHAR* pszReplyMessage = NULL; WORD cbPacket;
cbPacket = WireToHostFormat16(pReceiveBuf->Length);
if (PPP_CONFIG_HDR_LEN >= cbPacket) { goto LDone; }
//
// There is one extra byte for Msg-Length
//
dwNumBytes = cbPacket - PPP_CONFIG_HDR_LEN - 1;
//
// 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, pReceiveBuf->Data + 1, dwNumBytes);
LocalFree(pResult->szReplyMessage);
pResult->szReplyMessage = pszReplyMessage;
pszReplyMessage = NULL;
LDone:
LocalFree(pszReplyMessage);
return; }
DWORD PapSMakeMessage( IN PAPWB* pwb, IN PPP_CONFIG* pReceiveBuf, OUT PPP_CONFIG* pSendBuf, IN DWORD cbSendBuf, IN PPPAP_INPUT* pInput, OUT PPPAP_RESULT* pResult )
/* Server side "make message" entry point. See RasCp interface
** documentation. */ { DWORD dwErr;
switch (pwb->state) { case PS_Initial: { /* Tell engine we're waiting for the client to initiate the
** conversation. */ pResult->Action = APA_NoAction; pwb->state = PS_WaitForRequest; break; }
case PS_WaitForRequest: { CHAR szIdentity[ UNLEN + DNLEN + 2 ]; CHAR szPassword[ PWLEN + 1 ];
//
// Only process events where we received a packet, igore all other
// events in this state.
//
if ( pReceiveBuf == NULL ) { pResult->Action = APA_NoAction; break; }
if (pReceiveBuf->Code != PAPCODE_Req) { /* Silently discard Ack or Nak. Engine catches the one's that
** aren't even valid codes. */ RTASSERT(pReceiveBuf->Code!=PAPCODE_Req); pResult->Action = APA_NoAction; break; }
/* Extract user's credentials from received packet.
*/ if ((dwErr = GetCredentialsFromRequest( pReceiveBuf, szIdentity, szPassword )) != 0) { if (dwErr == ERRORBADPACKET) { /* The packet is corrupt. Silently discard it.
*/ RTASSERT(dwErr!=ERRORBADPACKET); pResult->Action = APA_NoAction; break; }
return dwErr; }
pwb->bLastIdReceived = pReceiveBuf->Id;
//
// Make credentials attributes that will be used to authenticate
// the client.
//
if ( pwb->pUserAttributes != NULL ) { RasAuthAttributeDestroy( pwb->pUserAttributes );
pwb->pUserAttributes = NULL; }
if (( pwb->pUserAttributes = RasAuthAttributeCreate( 2 ) ) == NULL) { return( GetLastError() ); }
dwErr = RasAuthAttributeInsert( 0, pwb->pUserAttributes, raatUserName, FALSE, strlen( szIdentity ), szIdentity );
if ( dwErr != NO_ERROR ) { RasAuthAttributeDestroy( pwb->pUserAttributes );
pwb->pUserAttributes = NULL;
return( dwErr ); }
dwErr = RasAuthAttributeInsert( 1, pwb->pUserAttributes, raatUserPassword, FALSE, strlen( szPassword ), szPassword );
if ( dwErr != NO_ERROR ) { RasAuthAttributeDestroy( pwb->pUserAttributes );
pwb->pUserAttributes = NULL;
return( dwErr ); } //
// Start authentication with back-end module
//
(VOID) StringCchCopyA( pwb->result.szUserName, UNLEN + 1, szIdentity );
pResult->pUserAttributes = pwb->pUserAttributes;
pResult->Action = APA_Authenticate;
pwb->state = PS_WaitForAuthenticationToComplete;
break; }
case PS_WaitForAuthenticationToComplete: { if ( pInput != NULL ) { if ( pInput->fAuthenticationComplete ) { strcpy( pResult->szUserName, pwb->result.szUserName );
if ( pInput->dwAuthError != NO_ERROR ) { return( pInput->dwAuthError ); }
if ( pInput->dwAuthResultCode != NO_ERROR ) { pwb->result.dwError = pInput->dwAuthResultCode; }
pwb->result.Action = APA_SendAndDone; pwb->state = PS_Done;
/* ...fall thru...
*/ } }
if ( ( pInput == NULL ) || ( !pInput->fAuthenticationComplete ) ) { //
// Ignore everything if authentication is not complete
//
if ( pReceiveBuf != NULL ) { pwb->bLastIdReceived = pReceiveBuf->Id; }
pResult->Action = APA_NoAction;
break; } }
case PS_Done: { //
// If we received a packet or the back-end authenticator completed
//
if ( ( pReceiveBuf != NULL ) || ( ( pInput != NULL ) && ( pInput->fAuthenticationComplete ) ) ) { //
// Build the Ack or Nak packet. The same packet sent in
// response to the first Authenticate-Req packet is sent in
// response to all subsequent Authenticate-Req packets
// regardless of credentials (per PAP spec).
//
if ( pReceiveBuf != NULL ) { pwb->bLastIdReceived = pReceiveBuf->Id; }
PapMakeResultMessage( pwb->result.dwError, pwb->bLastIdReceived, pSendBuf, cbSendBuf, (pInput != NULL) ? pInput->pAttributesFromAuthenticator : NULL );
CopyMemory( pResult, &pwb->result, sizeof(*pResult) ); } else { pResult->Action = APA_NoAction;
break; }
break; } }
return 0; }
|