EAP.C

/********************************************************************/ 
/** Copyright(c) 1985-1997 Microsoft Corporation. **/
/********************************************************************/

//***
//
// Filename: eap.c
//
// Description: Sample Extensible Authentication Protocol.
// Here is a graphic of the EAP sample protocol:
//
//
// Authenticator Authenticatee
// ------------- -------------
//
// "Send Password"
// ---------------------->
// EAP Request
//
// <password>
// <----------------------
// EAP Response
//
//
// ----------------------->
// Success/Failure
//
//

#include <windows.h>
#include <winuser.h>
#include <lmcons.h>
#include <string.h>
#include <stdlib.h>
#include <rasauth.h>
#include <raseapif.h>
#include <raserror.h>
#define SDEBUGGLOBALS
#define RASEAPGLOBALS
#include "eap.h"

/*---------------------------------------------------------------------------
** External entry points
**---------------------------------------------------------------------------
*/

BOOL
RasEapDllEntry(
IN HANDLE hinstDll,
IN DWORD fdwReason,
IN LPVOID lpReserved
)
//
// This routine is called by the system on various events such as the
// process attachment and detachment. See Win32 DllEntryPoint
// documentation.
//
// Returns TRUE if successful, FALSE otherwise.
//
{
switch ( fdwReason )
{
case DLL_PROCESS_ATTACH:
{
DisableThreadLibraryCalls( hinstDll );

break;
}

case DLL_PROCESS_DETACH:
{
break;
}
}

return( TRUE );
}


DWORD APIENTRY
RasEapGetInfo (
IN DWORD dwEapTypeId,
OUT PPP_EAP_INFO* pInfo
)
//
// RasEapGetInfo entry point called by the EAP-PPP engine by name.
//
{
if ( dwEapTypeId != PPP_EAP_PROTOCOL_ID )
{
//
// We only support PPP_EAP_PROTOCOL_ID eap type
//

return( ERROR_NOT_SUPPORTED );
}

ZeroMemory( pInfo, sizeof( PPP_EAP_INFO ) );

//
// Fill in the required information
//

pInfo->dwEapTypeId = PPP_EAP_PROTOCOL_ID;
pInfo->RasEapBegin = EapBegin;
pInfo->RasEapEnd = EapEnd;
pInfo->RasEapMakeMessage = EapMakeMessage;

return( NO_ERROR );
}


DWORD APIENTRY
EapBegin(
OUT VOID** ppWorkBuf,
IN VOID* pInfo
)
//
// EapBegin entry point called by the EAP PPP engine thru the passed
// address.
//
{
PPP_EAP_INPUT* pInput = (PPP_EAP_INPUT* )pInfo;
EAPCB* pwb;

//
// Allocate work buffer.
//

if ( ( pwb = (EAPCB* )LocalAlloc( LPTR, sizeof( EAPCB ) ) ) == NULL )
{
return( ERROR_NOT_ENOUGH_MEMORY );
}

//
// Save information passed in, will be used later
//

pwb->fAuthenticator = pInput->fAuthenticator;
pwb->EapState = MYSTATE_Initial;
pwb->hPort = pInput->hPort;
FpRasAuthenticateClient = pInput->RasAuthenticateClient;

//
// Save the identity. On the authenticatee side, this is obtained by user
// input, on the authenticator side this was obtained by the Identity
// request message.
//

strcpy( pwb->szIdentity, pInput->pszIdentity );

//
// If we are an authenticatee, then use the password passed in
//

if ( !pwb->fAuthenticator )
{
strcpy( pwb->szPassword, pInput->pszPassword );
}

//
// Register work buffer with engine.
//

*ppWorkBuf = pwb;

return( NO_ERROR );
}


DWORD APIENTRY
EapEnd(
IN VOID* pWorkBuf
)
//
// EapEnd entry point called by the PPP engine thru the passed address.
// See EAP interface documentation.
//
{
if ( pWorkBuf != NULL )
{
//
// Release all resources used by this authentication session.
//

EAPCB * pwb = ( EAPCB *)pWorkBuf;

if ( pwb->pUIContext != NULL )
{
LocalFree( pwb->pUIContext );
}

ZeroMemory( pWorkBuf, sizeof(EAPCB) );

LocalFree( pWorkBuf );
}

return( NO_ERROR );
}

DWORD APIENTRY
EapMakeMessage(
IN VOID* pWorkBuf,
IN PPP_EAP_PACKET* pReceiveBuf,
OUT PPP_EAP_PACKET* pSendBuf,
IN DWORD cbSendBuf,
OUT PPP_EAP_OUTPUT* pResult,
IN PPP_EAP_INPUT* pInput
)
//
// RasEapMakeMessage entry point called by the PPP engine thru the passed
// address.
//
{
EAPCB * pwb = (EAPCB* )pWorkBuf;

//
// Call the appropriate routine to process the event.
//

if ( pwb->fAuthenticator )
{
return( AuthenticatorMakeMessage( pwb,
pReceiveBuf,
pSendBuf,
cbSendBuf,
pInput,
pResult ) );
}
else
{
return( AuthenticateeMakeMessage( pwb,
pReceiveBuf,
pSendBuf,
cbSendBuf,
pInput,
pResult ) );
}
}


/*---------------------------------------------------------------------------
** Internal routines
**---------------------------------------------------------------------------
*/

VOID
HostToWireFormat16(
IN WORD wHostFormat,
IN OUT PBYTE pWireFormat
)
{
//
// Will convert a 16 bit integer from host format to wire format
//

*((PBYTE)(pWireFormat)+0) = (BYTE) ((DWORD)(wHostFormat) >> 8);
*((PBYTE)(pWireFormat)+1) = (BYTE) (wHostFormat);
}

WORD
WireToHostFormat16(
IN PBYTE pWireFormat
)
{
//
// Will convert a 16 bit integer from wire format to host format
//

WORD wHostFormat = ((*((PBYTE)(pWireFormat)+0) << 8) +
(*((PBYTE)(pWireFormat)+1)));

return( wHostFormat );
}

DWORD
AuthenticateeMakeMessage(
IN EAPCB* pwb,
IN PPP_EAP_PACKET* pReceiveBuf,
OUT PPP_EAP_PACKET* pSendBuf,
IN DWORD cbSendBuf,
IN PPP_EAP_INPUT* pInput,
OUT PPP_EAP_OUTPUT* pResult
)
//
// Authenticatee side event handler
//
{
DWORD dwRetCode = NO_ERROR;

switch( pwb->EapState )
{
case MYSTATE_Initial:

//
// Bring up interactive UI to notify user that he/she is being
// authenticated via the sample EAP
//

pResult->fInvokeInteractiveUI = TRUE;

pResult->dwSizeOfUIContextData =
strlen("You are being authenticated by a Sample EAP" )+1;

pResult->pUIContextData = LocalAlloc(
LPTR,
pResult->dwSizeOfUIContextData);

if ( pResult->pUIContextData == NULL )
{
return( ERROR_NOT_ENOUGH_MEMORY );
}

strcpy( pResult->pUIContextData,
"You are being authenticated by a Sample EAP" );

pwb->pUIContext = pResult->pUIContextData;

pwb->EapState = MYSTATE_WaitForUserOK;

break;

case MYSTATE_WaitForUserOK:

//
// Wait for response from user
//

if ( pInput->fDataReceivedFromInteractiveUI )
{
if ( pwb->pUIContext != NULL )
{
LocalFree( pwb->pUIContext );

pwb->pUIContext = NULL;
}

//
// If user doesn't like this, then we hangup the line
//

if ( pInput->dwSizeOfDataFromInteractiveUI != (strlen("OK")+1) )
{
dwRetCode = ERROR_ACCESS_DENIED;

break;
}

if ( strcmp( pInput->pDataFromInteractiveUI, "OK" ) != 0 )
{
dwRetCode = ERROR_ACCESS_DENIED;

break;
}

pwb->EapState = MYSTATE_WaitForRequest;
}
else
{
//
// Ignore all other events.
//

pResult->Action = EAPACTION_NoAction;
}

break;

case MYSTATE_WaitForRequest:

if ( pReceiveBuf != NULL )
{
//
// If we received a request packet from the server then we
// process it.
//

if ( pReceiveBuf->Code == EAPCODE_Request )
{
//
// Build the response packet
//

MakeResponseMessage(pwb, pReceiveBuf, pSendBuf, cbSendBuf);

//
// Response packets should not be sent with any timeout
//

pResult->Action = EAPACTION_Send;

//
// We are done so we change to MYSTATE_Done
//

pwb->EapState = MYSTATE_Done;

break;
}
else
{
//
// We shouldn't get any other packet in this state so
// we simply drop this invalid packet
//

pResult->Action = EAPACTION_NoAction;

dwRetCode = ERROR_PPP_INVALID_PACKET;

break;
}
}

break;

case MYSTATE_Done:
{
if ( pReceiveBuf == NULL )
{
//
// If we did not receive a packet then we check to see if
// the fSuccessPacketReceived flag is set
//

if ( ( pInput != NULL ) && ( pInput->fSuccessPacketReceived ) )
{
//
// We are done
//

pResult->Action = EAPACTION_Done;
pwb->EapState = MYSTATE_Done;
}
else
{
//
// Otherwise we ignore this event
//

pResult->Action = EAPACTION_NoAction;
}

break;
}

if ( ( pReceiveBuf->Code == EAPCODE_Success ) ||
( pReceiveBuf->Code == EAPCODE_Failure ) )
{
//
// If we received success or failure, we are done
//

if ( pReceiveBuf->Id != pwb->dwIdExpected )
{
//
// But first make sure the the Success/Failure packet ID
// matches that of the last response sent.
// If not silently discard the packet.
//

pResult->Action = EAPACTION_NoAction;
dwRetCode = ERROR_PPP_INVALID_PACKET;
}
else
{

pResult->Action = EAPACTION_Done;
pwb->EapState = MYSTATE_Done;
}

break;
}
else if ( pReceiveBuf->Code == EAPCODE_Request )
{
//
// We must always respond to requests
//

MakeResponseMessage(pwb, pReceiveBuf, pSendBuf, cbSendBuf);

//
// Response packets should not be sent with any timeout
//

pResult->Action = EAPACTION_Send;
}
else
{
//
// Otherwise we received an illegal packet, wrong code set
// So simply drop the packet.
//

pResult->Action = EAPACTION_NoAction;
dwRetCode = ERROR_PPP_INVALID_PACKET;
}
}
}

return( dwRetCode );
}


VOID
MakeResponseMessage(
IN EAPCB* pwb,
IN PPP_EAP_PACKET * pReceiveBuf,
OUT PPP_EAP_PACKET * pSendBuf,
IN DWORD cbSendBuf
)
//
// Builds a response packet. 'pwb' is the address of the work
// buffer associated with the port.
//
{
BYTE* pcbPassword;
CHAR* pchPassword;

(void )cbSendBuf;

//
// Fill in the password.
//

pcbPassword = pSendBuf->Data + 1;

*pcbPassword = (BYTE )strlen( pwb->szPassword );

pchPassword = pcbPassword + 1;

strcpy( pchPassword, pwb->szPassword );

//
// Set the response code
//

pSendBuf->Code = (BYTE )EAPCODE_Response;

//
// The Reponse packet Id MUST match the Request packet Id.
//

pSendBuf->Id = pReceiveBuf->Id;

//
// The Success/Failure packet that we get must match the ID of the last
// response sent
//

pwb->dwIdExpected = pSendBuf->Id;

//
// Set the EAP type ID
//

pSendBuf->Data[0] = (BYTE )PPP_EAP_PROTOCOL_ID;

//
// Set the length of the packet
//

HostToWireFormat16((WORD )(PPP_EAP_PACKET_HDR_LEN+1+*pcbPassword+1),
pSendBuf->Length );
}


VOID
MakeResultMessage(
IN DWORD dwError,
IN BYTE bId,
OUT PPP_EAP_PACKET* 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 an Success or Failure should be generated,
// 'bId' is the Id of the Success of Failure packet.
//
{
(void )cbSendBuf;

//
// If there was no error then we send a Success packet, otherwise we send
// a failure message
//

if ( dwError == NO_ERROR )
{
pSendBuf->Code = EAPCODE_Success;
}
else
{
pSendBuf->Code = EAPCODE_Failure;
}

//
// Id must match the last response received
//

pSendBuf->Id = bId;

//
// Set the length
//

HostToWireFormat16((WORD)PPP_EAP_PACKET_HDR_LEN, (PBYTE )pSendBuf->Length);
}

VOID
MakeRequestMessage(
IN EAPCB* pwb,
OUT PPP_EAP_PACKET * pSendBuf,
IN DWORD cbSendBuf
)

//
// Will build a request packet
//
{
BYTE *pcbPeerMessage;
CHAR *pchPeerMessage;

pcbPeerMessage = pSendBuf->Data + 1;

*pcbPeerMessage = (BYTE)strlen("send password");

pchPeerMessage = pcbPeerMessage + 1;

strcpy (pchPeerMessage,"send password");

//
// Set the Request Code
//

pSendBuf->Code = EAPCODE_Request;

//
// Set the Identifier
//

pSendBuf->Id = (BYTE) dwNextId++;

//
// Remember this Id since we need to send the Success/Failure packet with
// this Id
//

pwb->dwIdExpected = pSendBuf->Id;

//
// Set the length
//

HostToWireFormat16((WORD)(PPP_EAP_PACKET_HDR_LEN+1+*pcbPeerMessage+1),
pSendBuf->Length );

//
// Set the EAP Type Id
//

pSendBuf->Data[0] = PPP_EAP_PROTOCOL_ID;

}

DWORD
AuthenticatorMakeMessage(
IN EAPCB* pwb,
IN PPP_EAP_PACKET* pReceiveBuf,
OUT PPP_EAP_PACKET* pSendBuf,
IN DWORD cbSendBuf,
IN PPP_EAP_INPUT* pInput,
OUT PPP_EAP_OUTPUT* pResult
)
//
// Authenticator side event handler
//
{
DWORD dwRetCode = NO_ERROR;

switch( pwb->EapState )
{
case MYSTATE_ReqSent:

if ( pReceiveBuf != NULL )
{
//
// If we received a packet
//

if ( pReceiveBuf->Code == EAPCODE_Response )
{
CHAR szPassword[PWLEN + 1];

//
// If we received a response to our identity request,
// then process it. There is no need to check the Id
// here since the PPP engine will only pass on packets
// whose Id matches those set with the
// EAPACTION_SendWithTimeout action.
//

dwRetCode = GetPasswordFromResponse( pReceiveBuf,
szPassword );

if ( dwRetCode != NO_ERROR )
{
if ( dwRetCode != ERROR_PPP_INVALID_PACKET )
{
//
// Fatal error, we fail the connection.
//

return( dwRetCode );
}

//
// Otherwise the packet is most likely corrupt.
// Fall thru to the initial state to resend the
// request with the same Id.
//

dwNextId = pwb->dwIdExpected;
}
else
{

//
// Request authentication provider to authenticate
// this user.
//

dwRetCode = AuthenticateUser( pwb->szIdentity,
szPassword,
pwb );

if ( dwRetCode != NO_ERROR )
{
return( dwRetCode );
}
else
{
//
// Authentication request completed successfully.
// This is an asynchronous call so we change state
// and wait for the provider to complete the
// authentication.
//

pwb->EapState =
MYSTATE_WaitForAuthenticationToComplete;

pResult->Action = EAPACTION_NoAction;
}
}

break;
}
else
{
//
// Otherwise silently drop the packet.
// We should only get requests
//

pResult->Action = EAPACTION_NoAction;

break;
}
}
else
{
//
// If in this state we get called with a NULL pReceiveBuf, this
// means that we timed out waiting for a response from the
// the authenticatee, we need to resend with the same Id.
//

dwNextId = pwb->dwIdExpected;

//
// Fall thru to resend the request packet
//
}

case MYSTATE_Initial:

//
// Create Request packet
//

MakeRequestMessage( pwb, pSendBuf, cbSendBuf );

//
// Tell the PPP engine to drop all responses that do not mactch
// this Id
//

pResult->dwIdExpected = (BYTE)pwb->dwIdExpected;

//
// Request messages must be sent with a timeout
//

pResult->Action = EAPACTION_SendWithTimeoutInteractive;

//
// Send we have sent a Request we change to the ReqSent state
// where we will wait for a response
//

pwb->EapState = MYSTATE_ReqSent;

break;

case MYSTATE_WaitForAuthenticationToComplete:
{
if ( pInput != NULL )
{
//
// Did the authentication provider complete the authentication?
//

if ( pInput->fAuthenticationComplete )
{
//
// If there was a processing error, simply return this
// error.
//

if ( pInput->dwAuthError != NO_ERROR )
{
return( pInput->dwAuthError );
}

//
// If the authentication process completed successfully but
// the user failed to authenticate, save the failure code.
//

if ( pInput->dwAuthResultCode != NO_ERROR )
{
pwb->dwResult = pInput->dwAuthResultCode;
}

pResult->Action = EAPACTION_SendAndDone;
pwb->EapState = MYSTATE_Done;

//
// fall thru to the MYSTATE_Done state where we will
// send a Success or Failure packet
//
}
}

if ( ( pInput == NULL ) || ( !pInput->fAuthenticationComplete ) )
{
//
// Ignore everything if authentication is not complete
//

pResult->Action = EAPACTION_NoAction;

break;
}

//
// ...fall thru to the MYSTATE_Done state where we will
// send a Success or Failure packet
//
}

case MYSTATE_Done:
{
//
// Make Success or Failure packet.
//

MakeResultMessage( pwb->dwResult,
(BYTE)pwb->dwIdExpected,
pSendBuf,
cbSendBuf);

strcpy( pResult->szIdentity, pwb->szIdentity );

pResult->Action = EAPACTION_SendAndDone;

pResult->dwAuthResultCode = pwb->dwResult;

break;
}

default:

break;
}

return( dwRetCode );

}

DWORD
GetPasswordFromResponse(
IN PPP_EAP_PACKET* pReceiveBuf,
OUT CHAR* pszPassword
)
//
// Fill caller's pszPassword' buffer with the password, in the request
// packet.
//
// Returns NO_ERROR if successful., or ERROR_PPP_INVALID_PACKET if the
// packet is misformatted in any way.
//
{
BYTE* pcbPassword;
CHAR* pchPassword;
WORD cbPacket;

cbPacket = WireToHostFormat16( pReceiveBuf->Length );

//
// Extract the password
//

if ( cbPacket < ( PPP_EAP_PACKET_HDR_LEN + 1 + 1 ) )
{
return( ERROR_PPP_INVALID_PACKET );
}

pcbPassword = pReceiveBuf->Data + 1;
pchPassword = pcbPassword + 1;

if (cbPacket < PPP_EAP_PACKET_HDR_LEN + 1 + 1 + *pcbPassword)
{
return ERROR_PPP_INVALID_PACKET;
}

CopyMemory( pszPassword, pchPassword, *pcbPassword );

//
// NULL terminate the password
//

pszPassword[ *pcbPassword ] = '\0';

return( NO_ERROR );
}

DWORD
AuthenticateUser(
IN CHAR *szUserName,
IN CHAR *szPassword,
IN EAPCB *pwb
)
//
// Will build user attributes and send them to the authentication provider
// for authentication.
//
{

RAS_AUTH_ATTRIBUTE *pAttributes = NULL;
DWORD dwRetCode;

pAttributes = (RAS_AUTH_ATTRIBUTE *)
LocalAlloc(LPTR, sizeof (RAS_AUTH_ATTRIBUTE) * 3);

if (pAttributes == NULL)
{
return (GetLastError());
}

//
// for user name
//

pAttributes[0].raaType = raatUserName;
pAttributes[0].dwLength =strlen(szUserName);
pAttributes[0].Value = LocalAlloc(LPTR, (strlen(szUserName)+1));

if (pAttributes == NULL)
{
LocalFree (pAttributes);
return (GetLastError());
}

CopyMemory (pAttributes[0].Value,szUserName, strlen(szUserName));

//
// for password
//

pAttributes[1].raaType = raatUserPassword;
pAttributes[1].dwLength =strlen(szPassword);
pAttributes[1].Value = LocalAlloc(LPTR, (strlen(szPassword)+1));

if (pAttributes == NULL)
{
LocalFree (pAttributes[0].Value);
LocalFree (pAttributes);
return (GetLastError());
}

CopyMemory (pAttributes[1].Value,szPassword, strlen(szPassword));

//
// For Termination
//

pAttributes[2].raaType = raatMinimum;
pAttributes[2].dwLength = 0;
pAttributes[2].Value = NULL;


//
// Call the authentication provider
//

dwRetCode = (*FpRasAuthenticateClient)( pwb->hPort, pAttributes );

//
// Free up Attributes
//

LocalFree (pAttributes[0].Value);
LocalFree (pAttributes[1].Value);

return( dwRetCode );
}

DWORD APIENTRY
RasEapInvokeInteractiveUI(
IN HWND hWndParent,
IN PBYTE pUIContextData,
IN DWORD dwSizeofUIContextData,
OUT PBYTE * ppUserData OPTIONAL,
OUT DWORD * lpdwSizeOfUserData
)
{
if ( MessageBoxA( hWndParent,
pUIContextData,
"EAP sample",
MB_OKCANCEL ) == IDOK )
{
*lpdwSizeOfUserData = strlen( "OK" )+ 1;

if ( ( *ppUserData = LocalAlloc( LPTR, strlen( "OK" )+ 1 ) ) == NULL )
{
return( ERROR_NOT_ENOUGH_MEMORY );
}

strcpy( *ppUserData, "OK" );
}
else
{
*ppUserData = NULL;
*lpdwSizeOfUserData = 0;
}

return( NO_ERROR );
}

DWORD APIENTRY
RasEapFreeUserData(
IN PBYTE pUserData
)
{
if ( pUserData != NULL )
{
LocalFree( pUserData );
}

return( NO_ERROR );
}