// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. #include "EapHostServerMethod.h" #include "EapHostCommon.h" #include "memory.h" #include "strsafe.h" #include "EapHostError.h" #include "Resource.h" using namespace SDK_METHOD_SAMPLE_COMMON; extern HINSTANCE g_hInstance; // // EAP Functions // void WINAPI EapMethodAuthenticatorFreeErrorMemory(IN EAP_ERROR* pEapError) { //Sanity Check if(!pEapError) { EapTrace("EapMethodAuthenticatorFreeErrorMemory() --- Input Paramter is NULL"); goto Cleanup; } // //If RootCauseString in EapError, free it. // if(pEapError->pRootCauseString) FreeMemory((PVOID *)&(pEapError->pRootCauseString)); // //If error string in EapError, free it. // if(pEapError->pRepairString) FreeMemory((PVOID *)&(pEapError->pRepairString)); // //Finally, free the EapError structure. // FreeMemory((PVOID *)&pEapError); Cleanup: return; } DWORD EapMethodAuthenticatorGetInfo( IN EAP_METHOD_TYPE* pEapType, OUT EAP_AUTHENTICATOR_METHOD_ROUTINES* pEapInfo, OUT EAP_ERROR** ppEapError ) { DWORD retCode = NO_ERROR; //Sanity Check if((!pEapType) || (!pEapInfo) || (!ppEapError)) { EapTrace("EapMethodAuthenticatorGetInfo() --- One/Some of the paramters is/are NULL"); retCode = ERROR_INVALID_PARAMETER; goto Cleanup; } // // Verify if pEapType passed by EapHost correctly matches the EapType of this DLL. // if ((pEapType->eapType.type != EAPTYPE) || (pEapType->dwAuthorId != AUTHOR_ID)) { EapTrace("EapMethodAuthenticatorGetInfo() --- Input Eap Type Info does not match the supported Eap Type"); retCode = ERROR_NOT_SUPPORTED; goto Cleanup; } ZeroMemory(pEapInfo, sizeof(EAP_AUTHENTICATOR_METHOD_ROUTINES)); // Need to fill < dwSizeInBytes, pEapType> (???) // // Fill the function pointers inside pEapInfo structure. // pEapInfo->EapMethodAuthenticatorInitialize = SdkEapMethodAuthenticatorInitialize; pEapInfo->EapMethodAuthenticatorBeginSession = SdkEapMethodAuthenticatorBeginSession; pEapInfo->EapMethodAuthenticatorUpdateInnerMethodParams = SdkEapMethodAuthenticatorUpdateInnerMethodParams; pEapInfo->EapMethodAuthenticatorReceivePacket = SdkEapMethodAuthenticatorReceivePacket; pEapInfo->EapMethodAuthenticatorSendPacket = SdkEapMethodAuthenticatorSendPacket; pEapInfo->EapMethodAuthenticatorGetAttributes = SdkEapMethodAuthenticatorGetAttributes; pEapInfo->EapMethodAuthenticatorSetAttributes = SdkEapMethodAuthenticatorSetAttributes; pEapInfo->EapMethodAuthenticatorGetResult = SdkEapMethodAuthenticatorGetResult; pEapInfo->EapMethodAuthenticatorEndSession = SdkEapMethodAuthenticatorEndSession; pEapInfo->EapMethodAuthenticatorShutdown = SdkEapAuthenticatorShutdown; Cleanup: if(retCode != ERROR_SUCCESS) { // Populate the EapError if(ppEapError) { DWORD errCode = ERROR_SUCCESS; errCode = AllocateandFillEapError(ppEapError, retCode, 0, NULL, NULL, NULL, NULL, NULL ); if(errCode != ERROR_SUCCESS) { //Report Error } } } return retCode; } DWORD APIENTRY SdkEapMethodAuthenticatorInitialize( IN EAP_METHOD_TYPE* pEapType, OUT EAP_ERROR** ppEapError ) { DWORD retCode = NO_ERROR; //Sanity Check if( (!pEapType) || (!ppEapError)) { EapTrace("EapMethodAuthenticatorInitialize() --- One/Some of the paramters is/are NULL"); retCode = ERROR_INVALID_PARAMETER; goto Cleanup; } // // Verify if pEapType passed by EapHost correctly matches the EapType of this DLL. // if ((pEapType->eapType.type != EAPTYPE) || (pEapType->dwAuthorId != AUTHOR_ID)) { EapTrace("EapMethodAuthenticatorInitialize() --- Input Eap Method Type Info does not match the supported Eap Method Type"); retCode = ERROR_NOT_SUPPORTED; goto Cleanup; } // // Initialize any resources which is required as long as this method DLL is loaded. // Cleanup: if(retCode != ERROR_SUCCESS) { // Populate the EapError if(ppEapError) { DWORD errCode = ERROR_SUCCESS; errCode = AllocateandFillEapError(ppEapError, retCode, 0, NULL, NULL, NULL, NULL, NULL ); if(errCode != ERROR_SUCCESS) { //Report Error } } } return retCode; } DWORD APIENTRY SdkEapMethodAuthenticatorBeginSession( // Flags to qualify the authentication process. IN DWORD dwFlags, // Identity of the user being authenticated IN LPCWSTR pwszIdentity, // Pointer to an array of attributes. This array contains attributes that // describe the entity being authenticated. IN const EapAttributes* const pAttributeArray, // Specifies the size in bytes of the data pointed to by pConnectionData. // If pConnectionData is NULL, this member is zero. IN DWORD dwSizeofConnectionData, // Pointer to connection data received from the authentication protocol's // configuration user interface. IN const BYTE* const pConnectionData, // This is the maximum size of an eap packet that the authenticator can send. IN DWORD dwMaxSendPacketSize, // The session handle that identifies the current authentication session. OUT EAP_SESSION_HANDLE* pSessionHandle, // On an unsuccessful call, this will contain any error information about // the failure. This will be null on a successful call. OUT EAP_ERROR** ppEapError ) { DWORD retCode = NO_ERROR; EAPCB* pwb = NULL; DWORD attribCount = 0; EapAttribute *pEapAttrib = NULL; // Sanity Check if( !pSessionHandle || !ppEapError) { EapTrace("EapMethodAuthenticatorBeginSession() --- One/Some of the paramters is/are NULL"); retCode = ERROR_INVALID_PARAMETER; goto Cleanup; } // // Allocate work buffer. This will remain for the entire session. In each API after BeginSession, // the buffer will be passed as EAP_SESSION_HANDLE. // retCode = AllocateMemory(sizeof(EAPCB), (PVOID*)&pwb); if (retCode != NO_ERROR) { // AllocateMemory() already reported the error. Need to fill the EapError. goto Cleanup; } // // Save information passed in, will be used later // pwb->pWorkBuffer = (PVOID)pwb; pwb->fFlags = dwFlags; pwb->EapState = MYSTATE_Initial; pwb->dwMaxSendPacketSize = dwMaxSendPacketSize; // // Save the identity information. // if(pwszIdentity != NULL) { WideCharToMultiByte( CP_ACP, NO_FLAGS, pwszIdentity, AUTOMATIC_STRING_LENGTH, pwb->aszIdentity, UNLEN + 1, NULL, NULL ); EapTrace("Identity: \"%s\"", pwb->aszIdentity); } // // Save the Connection Data. // For demostration purpose only, the Connection Data passed here is the same data // passed by configuring the Sample Eap Method. The configuration of Sample Eap Method // required passing a UserName and Password. Therefore, this check that // dwSizeofConnectionData == sizeof(USER_DATA_BLOB). // if(pConnectionData != NULL && (sizeof(USER_DATA_BLOB) == dwSizeofConnectionData)) { pwb->dwSizeofConnectionData = dwSizeofConnectionData; retCode = AllocateMemory(dwSizeofConnectionData, (PVOID *)&(pwb->pConnectionData)); if(retCode != NO_ERROR) { goto Cleanup; } CopyMemory(pwb->pConnectionData, pConnectionData, dwSizeofConnectionData); } // // Save EapAttributes if any. // if(pAttributeArray != NULL) { retCode = AllocateAttributes(pAttributeArray->dwNumberOfAttributes, &(pwb->pEapAttributes)); if(retCode != NO_ERROR) { goto Cleanup; } for(attribCount = 0; attribCount < pwb->pEapAttributes->dwNumberOfAttributes; attribCount++) { pEapAttrib = &((pAttributeArray->pAttribs)[attribCount]); retCode = AddAttribute(pwb->pEapAttributes, pEapAttrib->eaType, pEapAttrib->dwLength, pEapAttrib->pValue); if(retCode != NO_ERROR) { goto Cleanup; } } } // Return the Session Handle. *pSessionHandle = (VOID *)pwb; Cleanup: if(retCode != NO_ERROR) { if(pwb) { FreeMemory((PVOID *)&(pwb->pConnectionData)); FreeAttributes(&(pwb->pEapAttributes)); FreeMemory((PVOID*)&pwb); } // Populate the EapError if(ppEapError) { DWORD errCode = ERROR_SUCCESS; errCode = AllocateandFillEapError(ppEapError, retCode, 0, NULL, NULL, NULL, NULL, NULL ); if(errCode != ERROR_SUCCESS) { //Report Error } } } return retCode; } DWORD APIENTRY SdkEapMethodAuthenticatorUpdateInnerMethodParams( // context handle as returned from a successful call to // EapAuthenticatorBeginSession IN EAP_SESSION_HANDLE sessionHandle, IN DWORD dwFlags, IN CONST WCHAR* pwszIdentity, // Pointer to an array of attributes. This array contains attributes that // describe the entity being authenticated. IN const EapAttributes* const pAttributeArray, // On an unsuccessful call, this will contain any error information about // the failure. This will be null on a successful call. OUT EAP_ERROR** pEapError ) { DWORD retCode = ERROR_SUCCESS; UNREFERENCED_PARAMETER(sessionHandle); UNREFERENCED_PARAMETER(dwFlags); UNREFERENCED_PARAMETER(pwszIdentity); UNREFERENCED_PARAMETER(pAttributeArray); UNREFERENCED_PARAMETER(pEapError); return retCode; } // The authenticator calls this any time it receives a packet that the eaphost // needs to process. This should be called only after a successful call to // EapAuthenticatorBeginSession. DWORD APIENTRY SdkEapMethodAuthenticatorReceivePacket( // context handle as returned from a successful call to // EapAuthenticatorBeginSession IN EAP_SESSION_HANDLE sessionHandle, // Specifies the size, in bytes, of the buffer pointed to by // pReceivePacket IN DWORD cbReceivePacket, // Pointer to a buffer that contains the incoming EAP data received by // the supplicant. IN const EapPacket* const pReceivePacket, // This enumeration tells the supplicant to take an appropriate action. // The supplicant will typically look at this action and either call // another method on eaphost or do something else on its own. OUT EAP_METHOD_AUTHENTICATOR_RESPONSE_ACTION* pEapOutput, // On an unsuccessful call, this will contain any error information about // the failure. This will be null on a successful call. OUT EAP_ERROR** pEapError ) { DWORD retCode = NO_ERROR; EAPCB* pwb = NULL; DWORD cbPacket = 0; EapPacket *pEapReceivePacket = NULL; size_t passwordLength = 0; // Sanity checks. if (!sessionHandle || !pEapOutput || !pEapError) { EapTrace("EapMethodAuthenticatorReceivePacket() --- One/Some of the paramters is/are NULL"); retCode = ERROR_INVALID_PARAMETER; // Need to fill the EapError goto Cleanup; } pwb = (EAPCB*)sessionHandle; pEapReceivePacket = (EapPacket *)pReceivePacket; cbPacket = cbReceivePacket; // // Main state machine. // switch(pwb->EapState) { case MYSTATE_ReqSent: EapTrace("Authenticator state: RecvPacket --- MYSTATE_ReqSent"); if (pEapReceivePacket != NULL) { // // If we received a packet // if (pEapReceivePacket->Code == EapCodeResponse) { // // If we receive a response to our identity request, // then process it. // retCode = GetPasswordFromResponse((BYTE *)pEapReceivePacket, pwb->aszPassword, passwordLength); if (retCode != NO_ERROR) { // Need to fill the EapError goto Cleanup; } else { // // Place UserName and Password in an Attribute. // retCode = MakeAuthenticationAttributes( pwb->aszIdentity, pwb->aszPassword, pwb); if (retCode != NO_ERROR) { EapTrace("MakeAuthenticationAttributes failed %d", retCode); goto Cleanup; } else { // // Now we have username and password obtained from the client. // The actual method will employ some mechanism to authenticate the client. // Here we compare the username and password obtained from the client with // the username and password passed by the administrator while configuring // the Sample Eap Method. The username and password is stored as // ConnectionData in the EAPCB (i.e., Eap Session Handle). // retCode = VerifyAuthenticationAttributes(pwb); if(retCode != NO_ERROR) { EapTrace("VerifyAuthenticationAttributes failed %d", retCode); goto Cleanup; } *pEapOutput = EAP_METHOD_AUTHENTICATOR_RESPONSE_RESULT; } } break; } else { // // Otherwise silently drop the packet. // We should only get response // *pEapOutput = EAP_METHOD_AUTHENTICATOR_RESPONSE_DISCARD; break; } } break; default: // // Authenticator has to be in MYSTATE_ReqSent state when SdkEapMethodAuthenticatorReceivePacket // is called. Any other state is not acceptable according to the state machine of this Eap Method. // EapTrace("SdkEapMethodAuthenticatorReceivePacket -- Authenticator state: [default] Present State = %d", pwb->EapState); // Need to fill the EapError retCode = ERROR_INVALID_STATE; break; } Cleanup: return retCode; } DWORD APIENTRY SdkEapMethodAuthenticatorSendPacket( // context handle as returned from a successful call to // EapHostAuthenticatorBeginSession IN EAP_SESSION_HANDLE sessionHandle, // Id to use when constructing the SendPacket IN BYTE bPacketId, // Specifies the limit on the size, in bytes, on the packet generated // by eaphost. On a successful return, this will contain the size of the // data added by the eap module. OUT DWORD* pcbSendPacket, // Pointer to a buffer that is allocated by the client and populated // by the eap module. The value of the incoming buffer is ignored and // the method populates it from the beginning of the buffer. OUT EapPacket* pSendPacket, // Timeout option for sending the packet OUT EAP_AUTHENTICATOR_SEND_TIMEOUT* pTimeout, // On an unsuccessful call, this will contain any error information about // the failure. This will be null on a successful call. OUT EAP_ERROR** ppEapError ) { DWORD retCode = NO_ERROR; EAPCB* pwb = NULL; // Sanity Check if(!sessionHandle || !pcbSendPacket || !pSendPacket || !pTimeout || !ppEapError) { EapTrace("EapMethodAuthenticatorSendPacket() --- One/Some of the paramters is/are NULL"); retCode = ERROR_INVALID_PARAMETER; // Need to fill the EapError goto Cleanup; } pwb = (EAPCB*)sessionHandle; // // Main state machine. // switch(pwb->EapState) { case MYSTATE_Initial: // // Create EAP-Challenge packet // retCode = MakeRequestMessage(pwb, bPacketId, pSendPacket, pcbSendPacket); if(retCode != ERROR_SUCCESS) { // Need to fill the EapError goto Cleanup; } *pTimeout = EAP_AUTHENTICATOR_SEND_TIMEOUT_INTERACTIVE; // // Since we have sent a request we change to the ReqSent state // where we will wait for a response. // pwb->EapState = MYSTATE_ReqSent; break; default: // // Authenticator has to be in MYSTATE_Initial state when SdkEapMethodAuthenticatorSendPacket // is called. Any other state is not acceptable according to the state machine of this Eap Method. // EapTrace("SdkEapMethodAuthenticatorSendPacket -- Authenticator state: [default] Present State = %d", pwb->EapState); // Need to fill the EapError retCode = ERROR_INVALID_STATE; break; } Cleanup: return retCode; } // Returns an array of attributes that the caller needs to act on. // The supplicant will call this when a call to // EapHostAuthenticatorProcessRequestPacket returns EapHostAuthenticatorResponseRespond. DWORD APIENTRY SdkEapMethodAuthenticatorGetAttributes( // context handle as returned from a successful call to // EapHostAuthenticatorBeginSession IN EAP_SESSION_HANDLE sessionHandle, // Array of attributes that the caller needs to act on. OUT EapAttributes* pAttribs, OUT EAP_ERROR** pEapError ) { DWORD retCode = NO_ERROR; UNREFERENCED_PARAMETER(sessionHandle); UNREFERENCED_PARAMETER(pAttribs); UNREFERENCED_PARAMETER(pEapError); return retCode; } // Sets an array of attributes that the caller wants the eap method to act on. DWORD APIENTRY SdkEapMethodAuthenticatorSetAttributes( // context handle as returned from a successful call to // EapHostAuthenticatorBeginSession IN EAP_SESSION_HANDLE sessionHandle, IN const EapAttributes* const pAttribs, // This enumeration tells the supplicant to take an appropriate action. // The supplicant will typically look at this action and either call // another method on eaphost or do something else on its own. OUT EAP_METHOD_AUTHENTICATOR_RESPONSE_ACTION* pEapOutput, // On an unsuccessful call, this will contain any error information about // the failure. This will be null on a successful call. OUT EAP_ERROR** ppEapError ) { DWORD retCode = NO_ERROR; UNREFERENCED_PARAMETER(sessionHandle); UNREFERENCED_PARAMETER(pAttribs); UNREFERENCED_PARAMETER(pEapOutput); UNREFERENCED_PARAMETER(ppEapError); return retCode; } // The authenticator will call this on completion of an authentication. This // can happen in any of the following scenarios: // 1. A call to EapHostAuthenticatorReceivePacket returned // EAP_HOST_AUTHENTICATOR_RESPONSE_SUCCESS or EAP_HOST_AUTHENTICATOR_RESPONSE_FAILURE // Even if the action returned above was a success, the authenticator can choose to call // this method with a failure. // 2. The server can choose to terminate an authentication with a failure in the middle of // an authentication. DWORD APIENTRY SdkEapMethodAuthenticatorGetResult( // context handle as returned from a successful call to // EapHostPeerBeginSession IN EAP_SESSION_HANDLE sessionHandle, // A structure that indicates the result and any state that the // supplicant needs to save for future authentications. OUT EAP_METHOD_AUTHENTICATOR_RESULT* pResult, // On an unsuccessful call, this will contain any error information about // the failure. This will be null on a successful call. OUT EAP_ERROR** pEapError ) { DWORD retCode = NO_ERROR; EAPCB* pwb = NULL; //Sanity Check if(!sessionHandle || !pResult || !pEapError) { EapTrace("EapMethodAuthenticatorGetResult() --- One/Some of the paramters is/are NULL"); retCode = ERROR_INVALID_PARAMETER; // Need to fill the EapError goto Cleanup; } pwb = (EAPCB*)sessionHandle; if(pwb->dwResult == AUTH_SUCCESS) pResult->fIsSuccess = TRUE; else pResult->fIsSuccess = FALSE; if(pResult->fIsSuccess == TRUE) pResult->dwFailureReason = 0; else { // Check if authentication failed or the server choose to terminate the authentication. if(pwb->dwResult == NO_ERROR) pResult->dwFailureReason = 1; // ERROR_EAPHOST_SERVER (??) else pResult->dwFailureReason = pwb->dwResult; } // // If we made a Success packet, create the MPPE Key Attribute // and give it to the EAP-Host (finally to the Authenticator). // if(pResult->fIsSuccess == TRUE) { retCode = MakeMPPEKeyAttributes(pwb); if(retCode != NO_ERROR) { // MakeMPPEKeyAttributes() reported the error. Need to fill the EapError. goto Cleanup; } pResult->pAuthAttribs = pwb->pMPPEKeyAttributes; } Cleanup: return retCode; } // Ends the authentication session. This cleans up any state that the eap // method or eaphost might be keeping. DWORD APIENTRY SdkEapMethodAuthenticatorEndSession( // context handle as returned from a successful call to // EapHostPeerBeginSession. This will be set to NULL on a successful call. IN EAP_SESSION_HANDLE sessionHandle, // On an unsuccessful call, this will contain any error information about // the failure. This will be null on a successful call. OUT EAP_ERROR** ppEapError ) { DWORD retCode = NO_ERROR; EAPCB* pwb = NULL; DWORD index = 0; EapAttribute *pAttr = NULL; // Sanity check. if (!sessionHandle || !ppEapError) { EapTrace("EapMethodAuthenticatorEndSession() --- One/Some of the paramters is/are NULL"); retCode = ERROR_INVALID_PARAMETER; // Need to fill the EapError goto Cleanup; } pwb = (EAPCB *)sessionHandle; EapTrace("Identity: \"%s\"", pwb->aszIdentity); // // Free up User Attributes // if(pwb->pUserAttributes) { // Use SecureZeroMemory() here, to ensure that any authentication data is // initialized safely, before the memory block is freed back to the system. for(index = 0; index < pwb->pUserAttributes->dwNumberOfAttributes; index++) { pAttr = &((pwb->pUserAttributes->pAttribs)[index]); SecureZeroMemory(pAttr->pValue, pAttr->dwLength); } } FreeAttributes(&(pwb->pUserAttributes)); // // Free up the MPPE Key Attributes // FreeAttributes(&(pwb->pMPPEKeyAttributes)); // // Free the Connection Data. // SecureZeroMemory(pwb->pConnectionData, pwb->dwSizeofConnectionData); FreeMemory((PVOID*)&(pwb->pConnectionData)); // // Free the Input Eap Attributes. // FreeAttributes(&(pwb->pEapAttributes)); SecureZeroMemory(pwb, sizeof(EAPCB)); FreeMemory((PVOID*)&pwb); Cleanup: return retCode; } DWORD APIENTRY SdkEapAuthenticatorShutdown( IN EAP_METHOD_TYPE* pEapType, OUT EAP_ERROR** ppEapError ) { DWORD retCode = NO_ERROR; //Sanity Check if( (!pEapType) || (!ppEapError)) { EapTrace("EapAuthenticatorShutdown() --- One/Some of the paramters is/are NULL"); retCode = ERROR_INVALID_PARAMETER; // Need to fill the EapError goto Cleanup; } // // Verify if pEapType passed by EapHost correctly matches the EapType of this DLL. // if ((pEapType->eapType.type != EAPTYPE) || (pEapType->dwAuthorId != AUTHOR_ID)) { EapTrace("EapAuthenticatorShutdown() --- Input Eap Method Type Info does not match the supported Eap Method Type"); retCode = ERROR_NOT_SUPPORTED; // Need to fill the EapError. goto Cleanup; } // // Clear up any resources as this dll will get unloaded. // Cleanup: return retCode; } // // Functions exposed for Configuring EAP Authentication Method. // DWORD WINAPI EapMethodAuthenticatorInvokeConfigUI( IN EAP_METHOD_TYPE* pEapMethodType, IN HWND hwndParent, IN DWORD dwFlags, IN LPCWSTR pwszMachineName, IN DWORD dwSizeOfConfigIn, IN BYTE* pConfigIn, OUT DWORD* pdwSizeOfConfigOut, OUT BYTE** ppConfigOut, OUT EAP_ERROR** ppEapError ) { DWORD retCode = ERROR_SUCCESS; //Sanity Check if( (!pEapMethodType) || (!ppEapError) || (!pwszMachineName)) { EapTrace("EapMethodAuthenticatorInvokeConfigUI() --- One/Some of the paramters is/are NULL"); retCode = ERROR_INVALID_PARAMETER; // Need to fill the EapError goto Cleanup; } // // Verify if pEapType passed by EapHost correctly matches the EapType of this DLL. // if ((pEapMethodType->eapType.type != EAPTYPE) || (pEapMethodType->dwAuthorId != AUTHOR_ID)) { EapTrace("EapMethodAuthenticatorInvokeConfigUI() --- Input Eap Method Type Info does not match the supported Eap Method Type"); retCode = ERROR_NOT_SUPPORTED; // Need to fill the EapError. goto Cleanup; } UNREFERENCED_PARAMETER(dwFlags); UNREFERENCED_PARAMETER(dwSizeOfConfigIn); UNREFERENCED_PARAMETER(pConfigIn); // // This is for Demonstration use Only!!! // We raise an Dialog Box in which the Administrator can configure an username and // password. This combination of username and password will only be authenticated // by the Sample Eap Method. For any other combination, authentication will fail. // If the Administrator does not configure, this Sample Eap Method will fail all the // authentication attempts. // retCode = GetIdentityAsConfigData( hwndParent, ppConfigOut, pdwSizeOfConfigOut); Cleanup: return retCode; } VOID WINAPI EapMethodAuthenticatorFreeMemory(IN BYTE* pData) { FreeMemory((PVOID *)&pData); } // // Helper Functions // /** * MakeRequestMessage() helper function: Construct a request message * * This function builds the EAP-Challenge message(s) sent to the client. * * @param pwb [in] Pointer to the work buffer. * * @param bPacketId [in] PacketId to be used in the EapChallenge Method. * * @param pSendPacket [out] Pointer to a EapPacket structure. The * authentication protocol can use this structure * to specify a packet to send. * * @param pcbSendPacket [out] Specifies the size, in bytes, of the buffer * pointed to by pSendPacket. * * * @return If the function succeeds, the return value is NO_ERROR. */ DWORD MakeRequestMessage( IN EAPCB* pwb, IN BYTE bPacketId, OUT EapPacket *pSendPacket, OUT DWORD * pcbSendPacket ) { BYTE *pcbPeerMessage = NULL; CHAR *pchPeerMessage = NULL; HRESULT hr = S_OK; DWORD retCode = NO_ERROR; EapPacket *pEapRequestPacket = NULL; DWORD sendPktSize = 0; // // Packet Structure // Code - 1 Byte // Id - 1 Byte // Length - 2 Bytes // Data[0] - EAPTYPE // Data[1] - Length of the Challenge Message (X). // Data[2] to Data[X+1] - Challenge Message // // Sanity checks. if (! pwb || ! pSendPacket || !pcbSendPacket) { retCode = ERROR_INVALID_PARAMETER; EapTrace("Error -- one or more input parameters is NULL!"); goto Cleanup; } // Calculate the length of the Request Packet. sendPktSize = EAP_PACKET_HDR_LEN + // Header - Code, Id, Length 1 + // Data[0] = Type STRING_LENGTH_CHALLENGE_MESSAGE; // Challenge String Length includes NULL which counts for Data[1] if(sendPktSize > *pcbSendPacket) { // Packet is less than the required size. retCode = ERROR_INSUFFICIENT_BUFFER; //SdkEapMethodAuthenticatorSendPacket should reply with better error in EAP_ERROR goto Cleanup; } // Assign actual size of the packet. *pcbSendPacket = sendPktSize; pEapRequestPacket = (EapPacket *)(pSendPacket); pcbPeerMessage = pEapRequestPacket->Data + 1; // The packet data doesn't need room for a trailing NULL. *pcbPeerMessage = (BYTE)(STRING_LENGTH_CHALLENGE_MESSAGE - 1); pchPeerMessage = (PCHAR)(pcbPeerMessage + 1); // This function needs the length parameter to include room for the NULL. hr = StringCbCopyA(pchPeerMessage, STRING_LENGTH_CHALLENGE_MESSAGE, STRING_CHALLENGE_MESSAGE); if (FAILED(hr)) { retCode = HRESULT_CODE(hr); EapTrace("Error while copying password into response message! (error %d)", retCode); goto Cleanup; } // Set the Request Code pEapRequestPacket->Code = EapCodeRequest; // Set the request packet identifier. Start with the Id that was give to us pEapRequestPacket->Id = bPacketId; // Set the length HostToWireFormat16((WORD)(*pcbSendPacket), pEapRequestPacket->Length); // Set the EAP Type Id pEapRequestPacket->Data[0] = EAPTYPE; Cleanup: return retCode; } /** * GetPasswordFromResponse() helper function: Parse a response packet to extract * the password. * * This function handles reading the EAP client's response packet. It * obtains the user's password, to use when authenticating the user. * * * @param pReceiveBuf [in] Pointer to a EapPacket structure that * contains a received packet. * * @param pszPassword [out] Pointer to a buffer, into which the password * will be copied. The caller is responsible for * allocating & freeing this buffer. * * @param size [in,out] size of the password * * @return If the function succeeds, the return value is NO_ERROR. */ DWORD GetPasswordFromResponse( IN const BYTE* const pReceivePacket, OUT _Out_writes_bytes_(size) CHAR* pszPassword, size_t &size ) { BYTE* pcbPassword = NULL; CHAR* pchPassword = NULL; WORD cbPacket = 0; WORD *pBufLength = NULL; EapPacket* pReceiveBuf = NULL; // Start off with the error code returned for most errors. DWORD retCode = EAP_METHOD_INVALID_PACKET; // Sanity checks. if (!pszPassword || !pReceivePacket) { EapTrace("Error -- input password pointer is NULL!"); retCode = ERROR_INVALID_PARAMETER; goto Cleanup; } pReceiveBuf = (EapPacket *)pReceivePacket; pBufLength = (PWORD)(pReceiveBuf->Length); WireToHostFormat16(*pBufLength, (PBYTE)&cbPacket); // // Extract the password // if (cbPacket < (EAP_PACKET_HDR_LEN + 1 + 1)) { EapTrace("Packet is too short! (packet length is only %d bytes)", cbPacket); goto Cleanup; } pcbPassword = pReceiveBuf->Data + 1; pchPassword = (PCHAR)(pcbPassword + 1); // // Check the validity about the length if password. // if ( (*pcbPassword >= PWLEN) || (*pcbPassword == 0) ) { EapTrace ("Password length received in the packet is > PWLEN (%d)", PWLEN); goto Cleanup; } if (cbPacket < EAP_PACKET_HDR_LEN + 1 + 1 + *pcbPassword) { EapTrace("Number of characters in password is %d", *pcbPassword); EapTrace("Number of bytes in the EAP packet is %d", cbPacket); goto Cleanup; } if (*pcbPassword >= size) { EapTrace("Password buffer is insufficient"); retCode = ERROR_INSUFFICIENT_BUFFER; goto Cleanup; } CopyMemory(pszPassword, pchPassword, *pcbPassword); // // NULL terminate the password // pszPassword[ *pcbPassword ] = '\0'; size = *pcbPassword; // // No errors were hit, so return success. // retCode = NO_ERROR; Cleanup: return retCode; } /** * MakeAuthenticationAttributes() helper function: Build authentication * attributes to be sent to the authentication provider. * * This function wraps the received username & password into EAP * authentication attributes. The authentication provider will process these * attributes, determine whether the authentication succeeded or failed. * * * @param szUserName [in] The username received from the EAP client. * * @param szPassword [in] The password received from the EAP client. * * @param pwb [in] Pointer to the work buffer. * * * @return If the function succeeds, the return value is NO_ERROR. */ DWORD MakeAuthenticationAttributes( IN _In_ CHAR * szUserName, IN _In_ CHAR * szPassword, IN EAPCB * pwb ) { HRESULT hr = S_OK; DWORD retCode = NO_ERROR; DWORD attribCount = 0; size_t buflen = 0; // Sanity checks. if (!pwb ||!szUserName || !szPassword) { EapTrace("Error -- one or more input parameters is NULL!"); retCode = ERROR_INVALID_PARAMETER; goto Cleanup; } if (pwb->pUserAttributes != NULL) { // Cleanup any existing user attributes. retCode = FreeAttributes(&(pwb->pUserAttributes)); if (retCode != NO_ERROR) { EapTrace("Error while freeing existing attributes!"); goto Cleanup; } } // For authentication, we need 2 attributes. attribCount = ATTRIBUTE_COUNT_AUTHENTICATION; retCode = AllocateAttributes(attribCount, &(pwb->pUserAttributes)); if (retCode != NO_ERROR) { // Comments/Tracing goto Cleanup; } // // for user name // hr = StringCbLengthA(szUserName, UNLEN+1, &buflen); if (FAILED(hr)) { retCode = HRESULT_CODE(hr); EapTrace("Error while calculating username length! (error %d)", retCode); goto Cleanup; } retCode = AddAttribute(pwb->pUserAttributes, eatUserName, (DWORD)buflen , szUserName); if (retCode != NO_ERROR) { goto Cleanup; } // // for password // hr = StringCbLengthA(szPassword, PWLEN+1, &buflen); if (FAILED(hr)) { retCode = HRESULT_CODE(hr); EapTrace("Error while calculating password length! (error %d)", retCode); goto Cleanup; } retCode = AddAttribute(pwb->pUserAttributes, eatUserPassword, (DWORD)buflen , szPassword); if (retCode != NO_ERROR) { goto Cleanup; } Cleanup: if (retCode != NO_ERROR && pwb != NULL) { FreeAttributes(&(pwb->pUserAttributes)); } return retCode; } /** * MakeMPPEKeyAttributes() helper function: Build MPPE Key attributes. * * This function constructs MPPE encryption key attributes and saves them * into the EAPCB work buffer. * * * @param pwb [in] Pointer to the work buffer. * * * @return If the function succeeds, the return value is NO_ERROR. */ DWORD MakeMPPEKeyAttributes( IN EAPCB * pwb ) { DWORD retCode = NO_ERROR; DWORD dwSendPattern = 0; DWORD dwRecvPattern = 0; BYTE* pByte = NULL; EapAttribute keyAttrib = {eatMinimum, 0, 0}; // Sanity checks. if (! pwb) { retCode = ERROR_INVALID_PARAMETER; goto Cleanup; } // Free any prior MPPE key attributes. retCode = FreeAttributes(&(pwb->pMPPEKeyAttributes)); if (retCode != NO_ERROR) { goto Cleanup; } // Set up a scratch buffer to use when constructing keys. retCode = AllocateMemory(MPPE_KEY_LENGTH, (PVOID*)&pByte); if (retCode != NO_ERROR) goto Cleanup; // // The "keys" used by this Sample EAP provider are simple values XOR'ed // with these values. When implementing a real EAP protocol, a much // stronger key generation mechanism should be used. // dwSendPattern = 0xAB; dwRecvPattern = 0xCD; // // Construct the MPPE Send key. // retCode = FillMppeKeyAttribute(pwb, pByte, dwSendPattern, MS_MPPE_SEND_KEY, keyAttrib); if (retCode != NO_ERROR) goto Cleanup; // // Construct the MPPE Recv key. // retCode = FillMppeKeyAttribute(pwb, pByte, dwRecvPattern, MS_MPPE_RECV_KEY, keyAttrib); if (retCode != NO_ERROR) goto Cleanup; Cleanup: FreeMemory((PVOID*)&pByte); if (NO_ERROR != retCode) { if (pwb) FreeAttributes(&(pwb->pMPPEKeyAttributes)); } return(retCode); } /** * FillMppeKeyAttribute() helper function: Construct MPPE Key attributes. * * This function constructs MPPE encryption key attributes and saves them * into the EAPCB work buffer. */ DWORD FillMppeKeyAttribute(IN EAPCB *pwb, IN BYTE *&bBuffer, IN DWORD pattern, IN BYTE bKeyDirection, IN OUT EapAttribute &pAttrib) { DWORD retCode = ERROR_SUCCESS; DWORD dwIndex = 0; CopyMemory(bBuffer, pwb->aszPassword, MPPE_KEY_LENGTH); for (dwIndex = 0; dwIndex < MPPE_KEY_LENGTH; dwIndex++) { bBuffer[dwIndex] ^= pattern; } retCode = ConstructMppeKeyAttribute(bKeyDirection, bBuffer, MPPE_KEY_LENGTH, &pAttrib); if (retCode != NO_ERROR) goto Cleanup; // Add the newly-constructed attribute to the list of attribs. retCode = AppendAttributeToList(&(pwb->pMPPEKeyAttributes), pAttrib.eaType, pAttrib.dwLength, pAttrib.pValue); if (retCode != NO_ERROR) goto Cleanup; // Cleanup. retCode = FreeMemory((PVOID*)&(pAttrib.pValue)); if (retCode != NO_ERROR) goto Cleanup; Cleanup: return retCode; } /** * Construct a MPPE key vendor attribute. * * @param bKeyDirection [in] Either MS_MPPE_SEND_KEY or MS_MPPE_RECV_KEY. * * @param pMppeKeyData [in] The MPPE Key data. * * @param cbMppeKeyData [in] The byte length of the MPPE Key data. * * @param pAttrib [out] The EAP attribute that will contain the * final MPPE Key vendor attribute. * * * @return A Win32 error code, indicating success or failure. */ DWORD ConstructMppeKeyAttribute( IN BYTE bKeyDirection, IN PBYTE pMppeKeyData, IN DWORD cbMppeKeyData, IN OUT EapAttribute *pAttrib ) { DWORD retCode = NO_ERROR; PBYTE pByte = NULL; BYTE cbAttribValue = 0; // Sanity check. if (! pAttrib) { retCode = ERROR_INVALID_PARAMETER; goto Cleanup; } // // Bytes needed: // 4: Vendor-Id // 1: Vendor-Type // 1: Vendor-Length // 2: Salt // 1: Key-Length // NN: Key (default = 32 bytes) // pp: Padding (so Key-Length + Key + Padding = multiple of 16 bytes) // ----------------- // tt: Total (i.e. key = 32 bytes, padding = 15, overall total = 56) // // // Choose an appropriate key buffer size. // cbAttribValue = (BYTE)(cbMppeKeyData + 1); // Add leading "key-length" byte. // If key length isn't a multiple of 16, pad it out accordingly. if (cbAttribValue % MPPE_KEY_BLOCK_LENGTH) cbAttribValue += MPPE_KEY_BLOCK_LENGTH - ( cbAttribValue % MPPE_KEY_BLOCK_LENGTH ); // Include room for the vendor-attribute header & the Salt field (above). cbAttribValue = cbAttribValue + VENDOR_ATTRIBUTE_HEADER_LENGTH + MPPE_KEY_SALT_LENGTH; retCode = AllocateMemory(cbAttribValue, (PVOID*)&(pAttrib->pValue)); if (retCode != NO_ERROR) goto Cleanup; pByte = (PBYTE)(pAttrib->pValue); // // Populate the RADIUS vendor attribute's values. // HostToWireFormat32(VENDOR_ID_MICROSOFT, pByte); // Vendor-Id pByte[4] = bKeyDirection; // Vendor-Type (ie, MS-MPPE-Recv-Key) pByte[5] = cbAttribValue - sizeof(DWORD); // Vendor-Length (all except Vendor-Id) // pByte[6-7] is the zero-filled salt field pByte[8] = (BYTE)cbMppeKeyData; // Key-Length // // Fill in the MPPE key, starting after the vendor attrib header, the // Salt field, and the MPPE key length subfield (1 byte). // CopyMemory(pByte + VENDOR_ATTRIBUTE_HEADER_LENGTH + MPPE_KEY_SALT_LENGTH + 1, pMppeKeyData, cbMppeKeyData); // // Fill in the rest of the RADIUS attribute data. // pAttrib->eaType = eatVendorSpecific; pAttrib->dwLength = cbAttribValue; Cleanup: if ((retCode != NO_ERROR) && (pAttrib != NULL) && (pAttrib->pValue != NULL) ) FreeMemory((PVOID*)&(pAttrib->pValue)); return retCode; } DWORD VerifyAuthenticationAttributes( IN EAPCB *pwb) { DWORD retCode = NO_ERROR; USER_DATA_BLOB *pUserBlob = NULL; EapAttribute *pEapAttr = NULL; // // pwb->pUserAttributes contains the UserName and Password obtained from Client. // pwb->pConnectionData contains the UserName and Password obtained from Administrator. // After checking if the two matches result is to be set in pwb->dwResult. // if(pwb->pConnectionData) { // 1. Get the UserName and Password in Char * format. pUserBlob = (USER_DATA_BLOB*)pwb->pConnectionData; { CHAR aszIdentity[ UNLEN + 1 ]; CHAR aszPassword[ PWLEN + 1 ]; // If username field is present, store it. WideCharToMultiByte( CP_ACP, NO_FLAGS, pUserBlob->eapUserNamePassword.awszIdentity, AUTOMATIC_STRING_LENGTH, aszIdentity, UNLEN + 1, NULL, NULL ); // If password field is present, store it. WideCharToMultiByte( CP_ACP, NO_FLAGS, pUserBlob->eapUserNamePassword.awszPassword, AUTOMATIC_STRING_LENGTH, aszPassword, PWLEN + 1, NULL, NULL ); // User Name Attribute pEapAttr = &(((pwb->pUserAttributes)->pAttribs)[0]); if(0 != memcmp(aszIdentity, pEapAttr->pValue, pEapAttr->dwLength)) { pwb->dwResult = AUTH_FAILURE; goto Cleanup; } // Password Attribute pEapAttr = &(((pwb->pUserAttributes)->pAttribs)[1]); if(0 != memcmp(aszPassword, pEapAttr->pValue, pEapAttr->dwLength)) { pwb->dwResult = AUTH_FAILURE; goto Cleanup; } pwb->dwResult = AUTH_SUCCESS; } } else pwb->dwResult = AUTH_FAILURE; Cleanup: return retCode; } /** * GetIdentity() helper function: Get the username & password for the * user to be authenticated. * * This function obtains credentials for the user being authenticated. * It display UI requesting the username & password. * * * @param hwndParent [in] Handle to the parent window for the * user interface dialog. * * * @param ppUserDataOut [out] Pointer to a pointer that, on * successful return, points to the * identity data for the user. * * @param pdwSizeOfUserDataOut [out] Pointer to a DWORD variable that * receives the size of the data pointed * to by the ppUserDataOut parameter. * * * @return If the function succeeds, the return value is NO_ERROR. */ DWORD GetIdentityAsConfigData( IN HWND hwndParent, OUT BYTE** ppUserDataOut, OUT DWORD* pdwSizeOfUserDataOut ) { USER_DATA_BLOB* pEapUserData = NULL; DWORD retCode = NO_ERROR; // Sanity checks. if (! ppUserDataOut || ! pdwSizeOfUserDataOut) { EapTrace("Error -- one or more output pointers is NULL!"); retCode = ERROR_INVALID_PARAMETER; goto Cleanup; } // // Allocate memory for OUT parameters // retCode = AllocateMemory(sizeof(USER_DATA_BLOB), (PVOID*)&pEapUserData); if (retCode != NO_ERROR) { goto Cleanup; } // // Else prompt for username and password // retCode = GetUsernameAndPassword(hwndParent, pEapUserData); if(retCode != ERROR_SUCCESS) goto Cleanup; pEapUserData->eapTypeId = EAPTYPE; // // Set the OUT paramters // *ppUserDataOut = (BYTE*)pEapUserData; *pdwSizeOfUserDataOut = sizeof(USER_DATA_BLOB); // // We mustn't free OUT parameters. // pEapUserData = NULL; Cleanup: if(retCode != NO_ERROR) FreeMemory((PVOID*)&pEapUserData); return retCode; } //--------------------------------------------------------------------------- // // Dialog routines. // //--------------------------------------------------------------------------- /** * GetUsernameAndPassword() helper function: Display user credentials UI. * * This function displays an interactive UI (the IDD_CONFIG_IDENTITY_DIALOG dialog box) * requesting the user to enter their username and password. * * * @param hwndParent [in] Handle to the parent window for the * user interface dialog. * * @param pEapNameDialog [in] Pointer to an EAP_NAME_DIALOG structure * that, on successful return, will be filled * in with the user's username & password. * The caller is responsible for allocating * and freeing this buffer. * * * @return If the function succeeds, the return value is NO_ERROR. */ DWORD GetUsernameAndPassword( IN HWND hwndParent, IN USER_DATA_BLOB* pEapUserData ) { DWORD dwErr = NO_ERROR; int result = 0; result = (int)DialogBoxParam( g_hInstance, MAKEINTRESOURCE(IDD_CONFIG_IDENTITY_DIALOG), hwndParent, UsernameDialogProc, (LPARAM)pEapUserData); if (result < 0) { dwErr = GetLastError(); EapTrace("Hit error %d while displaying Username/Password dialog!", dwErr); } return(dwErr); } /** * This function handles Windows messages sent to the username/password UI * dialog box. It is called by the Windows UI subsystem. * * @param hWnd [in] Handle to the dialog box. * * @param unMsg [in] Specifies the message. Messages supported: * \li WM_INITDIALOG -- Initialize the dialog box. * \li WM_COMMAND -- The user has clicked on a * menu, control, or has used an accelerator * key. * * @param wParam [in] Specifies additional message-specific * information. * * @param lParam [in] Specifies additional message-specific * information. * * * @return The dialog box procedure should return TRUE if it processed the * message, and FALSE if it did not. If the dialog box procedure * returns FALSE, the dialog manager performs the default dialog * operation in response to the message. */ INT_PTR CALLBACK UsernameDialogProc( IN HWND hWnd, IN UINT unMsg, IN WPARAM wParam, IN LPARAM lParam ) { DWORD dwErr = FALSE; // By default, return FALSE. USER_DATA_BLOB* pEapUserData = NULL; switch (unMsg) { case WM_INITDIALOG: dwErr = InitUsernameDialog(hWnd, lParam); break; case WM_COMMAND: pEapUserData = (USER_DATA_BLOB*)((LONG_PTR)GetWindowLongPtr(hWnd, DWLP_USER)); dwErr = UsernameCommand(pEapUserData, LOWORD(wParam), hWnd); break; } return(dwErr); } /** * UsernameDialogProc() helper function: initialize the username dialog. * * This function handles the WM_INITDIALOG message, by initializing the * username dialog. * * * @param hWnd [in] Handle to the dialog box. * * @param lParam [in] Specifies additional message-specific * information. * * * @return FALSE, to prevent Windows from setting the default keyboard focus. */ BOOL InitUsernameDialog( IN HWND hWnd, IN LPARAM lParam ) { SetWindowLongPtr(hWnd, DWLP_USER, (LONG)lParam); return(FALSE); } /** * * This function handles the WM_COMMAND message, by saving any data the user * has entered into the text fields of the username dialog. * * @param pEapNameDialog [in] Pointer to an EAP_NAME_DIALOG structure * that, on successful return, will be filled * in with the user's username & password. This * buffer must be allocated before this * function is called. * * @param wId [in] The identifier of the menu item, control, * or accelerator. Supported identifiers: * \li IDOK - the dialog's OK button. The * user's credentials will be saved. * \li IDCANCEL - the dialog's Cancel button. * * @param hWndDlg [in] Handle to the dialog box. * * * @return TRUE if we processed this message; FALSE if we did not process * this message. */ BOOL UsernameCommand( IN USER_DATA_BLOB* pEapUserData, IN WORD wId, IN HWND hWndDlg ) { BOOL fOk = FALSE; HWND hWnd = NULL; DWORD editFieldLen = 0; // Sanity checks. if (! pEapUserData) { EapTrace("Error -- input dialog pointer is NULL!"); goto LDone; } switch(wId) { case IDOK: // // Save whatever the user typed in as the user name // hWnd = GetDlgItem(hWndDlg, IDC_EDIT_NAME); if (hWnd == NULL) // GetDlgItem() returns NULL for errors. { EapTrace("Error -- couldn't get username value! (error %d)", GetLastError()); goto LDone; } editFieldLen = GetWindowTextLength(hWnd); if(editFieldLen == 0) { MessageBox(NULL, L"UserName Field Cannot be Empty", L"MisConfiguration...", MB_OK); goto LDone; } GetWindowText(hWnd, pEapUserData->eapUserNamePassword.awszIdentity, UNLEN + 1); // // Save whatever the user typed in as the password // hWnd = GetDlgItem(hWndDlg, IDC_EDIT_PASSWD); if (hWnd == NULL) // GetDlgItem() returns NULL for errors. { EapTrace("Error -- couldn't get password value! (error %d)", GetLastError()); goto LDone; } editFieldLen = GetWindowTextLength(hWnd); if(editFieldLen == 0) { MessageBox(NULL, L"Password Field Cannot be Empty", L"MisConfiguration...", MB_OK); goto LDone; } GetWindowText(hWnd, pEapUserData->eapUserNamePassword.awszPassword, PWLEN + 1); // Fall through case IDCANCEL: EndDialog(hWndDlg, wId); fOk = TRUE; break; } LDone: if(fOk == FALSE) { // Case where UserName id populated but password is left blank. // When password is blank, that is an error condition. if(pEapUserData) ZeroMemory(pEapUserData, sizeof(USER_DATA_BLOB)); } return fOk; }