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