3100 lines
96 KiB
C++
3100 lines
96 KiB
C++
// 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 "EapHostClientMethod.h"
|
|
#include "memory.h"
|
|
#include "EapHostCommon.h"
|
|
#include "Resource.h"
|
|
#include "strsafe.h"
|
|
#include "EapHostError.h"
|
|
|
|
#define CONNECTION_PROPERTIES 1
|
|
#define USER_PROPERTIES 2
|
|
using namespace SDK_METHOD_SAMPLE_COMMON;
|
|
|
|
extern HINSTANCE g_hInstance;
|
|
|
|
VOID WINAPI EapPeerFreeErrorMemory(_In_ EAP_ERROR* pEapError)
|
|
{
|
|
//Sanity Check
|
|
if(!pEapError)
|
|
{
|
|
// Nothing to do; exit cleanly.
|
|
EapTrace("EapPeerFreeErrorMemory() --- Input Parameter is NULL, exiting.");
|
|
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 WINAPI EapPeerGetInfo(
|
|
_In_ EAP_TYPE* pEapType,
|
|
_Out_ EAP_PEER_METHOD_ROUTINES* pEapInfo,
|
|
_Out_opt_ EAP_ERROR** ppEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
|
|
if (!pEapInfo)
|
|
{
|
|
EapTrace("EapPeerGetInfo() --- pEapInfo paramters is NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
ZeroMemory(pEapInfo, sizeof(EAP_PEER_METHOD_ROUTINES));
|
|
|
|
if (!ppEapError)
|
|
{
|
|
EapTrace("EapPeerGetInfo() --- ppEapError paramters is NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
*ppEapError = NULL;
|
|
|
|
//Sanity Check
|
|
if(!pEapType)
|
|
{
|
|
EapTrace("EapPeerGetInfo() --- pEapType paramters is NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Verify if pEapType passed by EapHost correctly matches the EapType of this DLL.
|
|
//
|
|
if (pEapType->type != EAPTYPE)
|
|
{
|
|
EapTrace("EapPeerGetInfo() --- Input Eap Type Info does not match the supported Eap Type");
|
|
retCode = ERROR_NOT_SUPPORTED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Fill the function pointers inside pEapInfo structure.
|
|
//
|
|
pEapInfo->dwVersion = VERSION;
|
|
//pEapInfo->pEapType = ;
|
|
pEapInfo->EapPeerInitialize = SdkEapPeerInitialize;
|
|
pEapInfo->EapPeerBeginSession = SdkEapPeerBeginSession;
|
|
|
|
// A method exports either EapPeerGetIdentity or EapPeerSetCredentials (either one of them).
|
|
// If EapPeerGetIdentity is exported, then EapPeerInvokeIdentityUI should also be implemented.
|
|
pEapInfo->EapPeerGetIdentity = SdkEapPeerGetIdentity;
|
|
// If EapPeerSetCredentials is exported, InvokeUserNameDlg regkey should be set.
|
|
pEapInfo->EapPeerSetCredentials = NULL;
|
|
|
|
pEapInfo->EapPeerProcessRequestPacket = SdkEapPeerProcessRequestPacket;
|
|
pEapInfo->EapPeerGetResponsePacket = SdkEapPeerGetResponsePacket;
|
|
pEapInfo->EapPeerGetResult = SdkEapPeerGetResult;
|
|
pEapInfo->EapPeerGetUIContext = SdkEapPeerGetUIContext;
|
|
pEapInfo->EapPeerSetUIContext = SdkEapPeerSetUIContext;
|
|
pEapInfo->EapPeerGetResponseAttributes = SdkEapPeerGetResponseAttributes;
|
|
pEapInfo->EapPeerSetResponseAttributes = SdkEapPeerSetResponseAttributes;
|
|
pEapInfo->EapPeerEndSession = SdkEapPeerEndSession;
|
|
pEapInfo->EapPeerShutdown = SdkEapPeerShutdown;
|
|
|
|
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 WINAPI SdkEapPeerInitialize(OUT EAP_ERROR** ppEapError)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
|
|
//Sanity Check
|
|
if( !ppEapError)
|
|
{
|
|
EapTrace("SdkEapPeerInitialize() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialize any resources which is required as long as this method DLL is loaded.
|
|
//
|
|
Cleanup:
|
|
return retCode;
|
|
}
|
|
|
|
DWORD WINAPI SdkEapPeerBeginSession(
|
|
IN DWORD dwFlags,
|
|
IN const EapAttributes* const pAttributeArray,
|
|
IN HANDLE hTokenImpersonateUser,
|
|
IN DWORD dwSizeofConnectionData,
|
|
IN BYTE* pConnectionData,
|
|
IN DWORD dwSizeofUserData,
|
|
IN BYTE* pUserData,
|
|
IN DWORD dwMaxSendPacketSize,
|
|
OUT EAP_SESSION_HANDLE* pSessionHandle,
|
|
OUT EAP_ERROR** ppEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
EAPCB* pwb = NULL;
|
|
DWORD attribCount = 0;
|
|
EapAttribute *pEapAttrib = NULL;
|
|
USER_DATA_BLOB *pUserBlob = NULL;
|
|
|
|
// The user data should contain User Name and Password.
|
|
WCHAR* pPassword = NULL;
|
|
WCHAR* pUserIdentity = NULL;
|
|
|
|
//Sanity Check
|
|
if( !pSessionHandle || !ppEapError)
|
|
{
|
|
EapTrace("EapPeerBeginSession() --- 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)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Save information passed in, will be used later
|
|
//
|
|
pwb->pWorkBuffer = (PVOID)pwb;
|
|
pwb->fFlags = dwFlags;
|
|
pwb->EapState = MYSTATE_Initial;
|
|
pwb->hTokenImpersonateUser = hTokenImpersonateUser;
|
|
pwb->dwMaxSendPacketSize = dwMaxSendPacketSize;
|
|
pwb->ClientOK = FALSE;
|
|
|
|
//
|
|
// Save the Connection Data.
|
|
//
|
|
if(pConnectionData != NULL)
|
|
{
|
|
pwb->dwSizeofConnectionData = dwSizeofConnectionData;
|
|
retCode = AllocateMemory(dwSizeofConnectionData, (PVOID *)&(pwb->pConnectionData));
|
|
if(retCode != NO_ERROR)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
CopyMemory(pwb->pConnectionData, pConnectionData, dwSizeofConnectionData);
|
|
}
|
|
|
|
// Save the User Data in
|
|
if ((NULL != pUserData) && (sizeof(USER_DATA_BLOB) == dwSizeofUserData))
|
|
{
|
|
pUserBlob = (USER_DATA_BLOB*)pUserData;
|
|
pUserIdentity = pUserBlob->eapUserNamePassword.awszIdentity;
|
|
pPassword = pUserBlob->eapUserNamePassword.awszPassword;
|
|
|
|
pwb->dwSizeofUserData = dwSizeofUserData;
|
|
retCode = AllocateMemory(dwSizeofUserData, (PVOID *)&(pwb->pUserData));
|
|
if(retCode != NO_ERROR)
|
|
{
|
|
// allocate memory failed. Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
CopyMemory(pwb->pUserData, pUserData, dwSizeofUserData);
|
|
}
|
|
|
|
// If username field is present, store it.
|
|
if (pUserIdentity)
|
|
{
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
NO_FLAGS,
|
|
pUserIdentity,
|
|
AUTOMATIC_STRING_LENGTH,
|
|
pwb->aszIdentity,
|
|
UNLEN + 1,
|
|
NULL,
|
|
NULL );
|
|
}
|
|
|
|
// If password field is present, store it.
|
|
if (pPassword)
|
|
{
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
NO_FLAGS,
|
|
pPassword,
|
|
AUTOMATIC_STRING_LENGTH,
|
|
pwb->aszPassword,
|
|
PWLEN + 1,
|
|
NULL,
|
|
NULL );
|
|
}
|
|
|
|
if(pAttributeArray != NULL)
|
|
{
|
|
retCode = AllocateAttributes(pAttributeArray->dwNumberOfAttributes, &(pwb->pEapAttributes));
|
|
if(retCode != NO_ERROR)
|
|
{
|
|
// AllocateAttributes() reported the error. Need to fill the EapError.
|
|
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)
|
|
{
|
|
// AddAttribute() reported the error. Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return the Session Handle.
|
|
*pSessionHandle = (VOID *)pwb;
|
|
|
|
Cleanup:
|
|
if(retCode != ERROR_SUCCESS)
|
|
{
|
|
if(pwb)
|
|
{
|
|
FreeMemory((PVOID *)&(pwb->pConnectionData));
|
|
FreeMemory((PVOID *)&(pwb->pUserData));
|
|
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 WINAPI SdkEapPeerGetIdentity(
|
|
IN DWORD flags,
|
|
IN DWORD dwSizeofConnectionData,
|
|
IN const BYTE* pConnectionData,
|
|
IN DWORD dwSizeofUserData,
|
|
IN const BYTE* pUserData,
|
|
IN HANDLE hTokenImpersonateUser,
|
|
OUT BOOL* pfInvokeUI,
|
|
IN OUT DWORD* pdwSizeOfUserDataOut,
|
|
OUT BYTE** ppUserDataOut,
|
|
OUT _Out_opt_ LPWSTR* ppwszIdentity,
|
|
OUT EAP_ERROR** ppEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
USER_DATA_BLOB* pUserBlob = NULL;
|
|
size_t lenIdentity = 0;
|
|
WCHAR* pwszIdentity = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!ppwszIdentity)
|
|
{
|
|
EapTrace("SdkEapPeerGetIdentity() --- ppwszIdentity paramters is NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
*ppwszIdentity = NULL;
|
|
|
|
if(!pfInvokeUI || !ppEapError)
|
|
{
|
|
EapTrace("SdkEapPeerGetIdentity() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
UNREFERENCED_PARAMETER(flags);
|
|
UNREFERENCED_PARAMETER(dwSizeofConnectionData);
|
|
UNREFERENCED_PARAMETER(pConnectionData);
|
|
//
|
|
// hTokenImpersonateUser can be used for retreiving the user related information.
|
|
// Sample Eap Method does not use this parameter as it does not require any impersonation.
|
|
//
|
|
UNREFERENCED_PARAMETER(hTokenImpersonateUser);
|
|
|
|
|
|
//
|
|
// If UserData is not present, need to ask EapHost to raise the IdentityUI.
|
|
// Therefore, set pfInvokeUI = true.
|
|
//
|
|
if(dwSizeofUserData == 0)
|
|
{
|
|
*pfInvokeUI = TRUE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Size of UserData should be equal to sizeof(USER_DATA_BLOB), else incorrect
|
|
// user data.
|
|
//
|
|
if((pUserData == NULL) || (dwSizeofUserData != sizeof(USER_DATA_BLOB)))
|
|
{
|
|
EapTrace("SdkEapPeerGetIdentity() --- Incorrect Input User Blob");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate Memory for OUT parameters.
|
|
//
|
|
retCode = AllocateMemory(sizeof(USER_DATA_BLOB), (PVOID*)&pUserBlob);
|
|
if (retCode != NO_ERROR)
|
|
{
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
lenIdentity = (UNLEN+1) * sizeof(WCHAR);
|
|
retCode = AllocateMemory((DWORD)lenIdentity, (PVOID*)&pwszIdentity);
|
|
if (retCode != NO_ERROR)
|
|
{
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Assign the output parameters their respective values.
|
|
//
|
|
CopyMemory(pUserBlob, pUserData, sizeof(USER_DATA_BLOB));
|
|
hr = StringCbCopyW(pwszIdentity,
|
|
lenIdentity,
|
|
pUserBlob->eapUserNamePassword.awszIdentity);
|
|
if (FAILED(hr))
|
|
{
|
|
retCode = HRESULT_CODE(hr);
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set the OUT paramters
|
|
//
|
|
*ppUserDataOut = (BYTE *)pUserBlob;
|
|
*pdwSizeOfUserDataOut = sizeof(USER_DATA_BLOB);
|
|
*ppwszIdentity = pwszIdentity;
|
|
|
|
//
|
|
// We mustn't free OUT parameters.
|
|
//
|
|
pUserBlob = NULL;
|
|
pwszIdentity = NULL;
|
|
|
|
Cleanup:
|
|
if(retCode != NO_ERROR)
|
|
{
|
|
if (pUserBlob != NULL)
|
|
FreeMemory((PVOID*)&pUserBlob);
|
|
if (pwszIdentity != NULL)
|
|
FreeMemory((PVOID*)&pwszIdentity);
|
|
}
|
|
return retCode;
|
|
}
|
|
|
|
|
|
DWORD WINAPI SdkEapPeerSetCredentials(
|
|
IN EAP_SESSION_HANDLE sessionHandle,
|
|
IN _In_ LPWSTR pwszIdentity,
|
|
IN _In_ LPWSTR pwszPassword,
|
|
OUT EAP_ERROR** pEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
|
|
UNREFERENCED_PARAMETER(sessionHandle);
|
|
UNREFERENCED_PARAMETER(pwszIdentity);
|
|
UNREFERENCED_PARAMETER(pwszPassword);
|
|
UNREFERENCED_PARAMETER(pEapError);
|
|
|
|
return retCode;
|
|
}
|
|
|
|
DWORD WINAPI SdkEapPeerProcessRequestPacket(
|
|
IN EAP_SESSION_HANDLE sessionHandle,
|
|
IN DWORD cbReceivePacket,
|
|
IN EapPacket* pReceivePacket,
|
|
OUT EapPeerMethodOutput* pEapOutput,
|
|
OUT EAP_ERROR** pEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
EAPCB* pwb = NULL;
|
|
DWORD cbPacket = 0;
|
|
|
|
if(!sessionHandle || !pReceivePacket || !pEapOutput || !pEapError)
|
|
{
|
|
EapTrace("EapPeerProcessRequestPacket() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
pwb = (EAPCB*)sessionHandle;
|
|
cbPacket = cbReceivePacket;
|
|
|
|
pwb->bRecvPacketId = pReceivePacket->Id;
|
|
|
|
//
|
|
// Main state machine.
|
|
//
|
|
switch(pwb->EapState)
|
|
{
|
|
case MYSTATE_Initial:
|
|
|
|
EapTrace("Authenticatee state: MYSTATE_Initial");
|
|
|
|
//
|
|
// The received packet can contain certain Attributes that the method cannot process.
|
|
// Either those attributes need to be consumed by EapHost (SoH Attribute) or Supplicant.
|
|
//
|
|
|
|
//
|
|
// Following is just an example. It creates an EapAttributes (assuming it receives from the
|
|
// EapPacket) and set the action = EapPeerMethodResponseActionRespond so that supplicant
|
|
// on receiving the action can call GetResponseAttributes.
|
|
//
|
|
|
|
//
|
|
// Allocate Memory for pwb->pFakeAttribute attribute.
|
|
//
|
|
retCode = AllocateMemory(sizeof(EapAttribute), (PVOID *)&(pwb->pFakeAttribute));
|
|
if(retCode != ERROR_SUCCESS)
|
|
{
|
|
EapTrace("SdkEapPeerGetResponseAttributes() --- AllocateMemory (pwb->pFakeAttribute) failed");
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Populate the pwb->pFakeAttribute attribute.
|
|
//
|
|
pwb->pFakeAttribute->eaType = (EapAttributeType)1;
|
|
pwb->pFakeAttribute->dwLength = 4;
|
|
retCode = AllocateMemory(pwb->pFakeAttribute->dwLength, (PVOID *)&pwb->pFakeAttribute->pValue);
|
|
if(retCode != ERROR_SUCCESS)
|
|
{
|
|
EapTrace("SdkEapPeerGetResponseAttributes() --- AllocateMemory (pwb->pFakeAttribute->pValue) failed");
|
|
// Free the earlier allocated memory for pwb->pFakeAttribute.
|
|
FreeMemory((PVOID *)&(pwb->pFakeAttribute));
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
{
|
|
DWORD eapType = EAPTYPE;
|
|
CopyMemory(pwb->pFakeAttribute->pValue, (BYTE *)&eapType, pwb->pFakeAttribute->dwLength);
|
|
}
|
|
|
|
//
|
|
// Set the action that supplicant needs to take.
|
|
//
|
|
pEapOutput->action = EapPeerMethodResponseActionRespond;
|
|
pEapOutput->fAllowNotifications = TRUE; //??
|
|
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Peer has to be in MYSTATE_Initial when SdkEapPeerProcessRequestPacket is called.
|
|
// Any other state is not acceptable according to the state machine of this Eap Method.
|
|
//
|
|
EapTrace("SdkEapPeerProcessRequestPacket --- Peer state: [default] Present State = %d",
|
|
pwb->EapState);
|
|
// Need to fill the EapError
|
|
retCode = ERROR_INVALID_STATE;
|
|
break;
|
|
}
|
|
|
|
Cleanup:
|
|
return retCode;
|
|
}
|
|
|
|
DWORD WINAPI SdkEapPeerGetResponsePacket(
|
|
IN EAP_SESSION_HANDLE sessionHandle,
|
|
IN OUT DWORD* pcbSendPacket,
|
|
OUT EapPacket* pSendPacket,
|
|
OUT EAP_ERROR** ppEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
EAPCB* pwb = NULL;
|
|
|
|
//Sanity Check
|
|
if(!sessionHandle || !pcbSendPacket || !pSendPacket || !ppEapError)
|
|
{
|
|
EapTrace("EapPeerGetResponsePacket() --- 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_AfterUserOK:
|
|
//
|
|
// Make the EAP-Challenge Response Message.
|
|
//
|
|
retCode = MakeResponseMessage(pwb, pcbSendPacket, pSendPacket);
|
|
if(retCode != NO_ERROR)
|
|
{
|
|
// Report Error
|
|
goto Cleanup;
|
|
}
|
|
//
|
|
// We are done (i.e., no more processing is required on Peer side).
|
|
// Peer Side just waits for the authentication result after sending this message to the authenticator.
|
|
// So EapState is changed to MYSTATE_Done.
|
|
//
|
|
pwb->EapState = MYSTATE_Done;
|
|
pwb->ClientOK = TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Peer Method has to be in MYSTATE_AfterUserOK state when SdkEapPeerGetResponsePacket
|
|
// is called. Any other state is not acceptable according to the state machine of this Eap Method.
|
|
//
|
|
EapTrace("SdkEapPeerGetResponsePacket --- Peer state: [default] Present State = %d",
|
|
pwb->EapState);
|
|
// Need to fill the EapError
|
|
retCode = ERROR_INVALID_STATE;
|
|
break;
|
|
}
|
|
|
|
Cleanup:
|
|
return retCode;
|
|
}
|
|
|
|
// This will get called either when a method says that it has completed auth.
|
|
// or when the lower layer receives an alternative result.
|
|
DWORD WINAPI SdkEapPeerGetResult(
|
|
IN EAP_SESSION_HANDLE sessionHandle,
|
|
IN EapPeerMethodResultReason reason,
|
|
OUT EapPeerMethodResult* pResult,
|
|
OUT EAP_ERROR** pEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
EAPCB* pwb = NULL;
|
|
|
|
//Sanity Check
|
|
if(!sessionHandle || !pResult || !pEapError)
|
|
{
|
|
EapTrace("EapPeerGetInfo() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
pwb = (EAPCB*)sessionHandle;
|
|
|
|
//
|
|
// Copy the data store in Working Buffer to the output parameter --- pResult
|
|
//
|
|
|
|
//
|
|
// The final result decision should be based on both the result it obtains from authenticator
|
|
// as well as its internal state machine.
|
|
//
|
|
if(reason == EapPeerMethodResultSuccess)
|
|
{
|
|
if(pwb->ClientOK == TRUE)
|
|
pResult->fIsSuccess = TRUE;
|
|
else
|
|
pResult->fIsSuccess = FALSE;
|
|
}
|
|
else
|
|
pResult->fIsSuccess = FALSE;
|
|
|
|
if(pResult->fIsSuccess == TRUE)
|
|
{
|
|
//
|
|
// Save the Connection Data.
|
|
//
|
|
if(pwb->pConnectionData != NULL)
|
|
{
|
|
pResult->fSaveConnectionData = TRUE;
|
|
pResult->dwSizeofConnectionData = pwb->dwSizeofConnectionData;
|
|
pResult->pConnectionData = pwb->pConnectionData;
|
|
}
|
|
|
|
//
|
|
// Save the User Data.
|
|
//
|
|
if(pwb->pUserData != NULL)
|
|
{
|
|
pResult->fSaveUserData = TRUE;
|
|
pResult->dwSizeofUserData = pwb->dwSizeofUserData;
|
|
pResult->pUserData = pwb->pUserData;
|
|
}
|
|
|
|
//
|
|
// Fill MPPE Attributes.
|
|
//
|
|
retCode = MakeMPPEKeyAttributes(pwb);
|
|
if(retCode != NO_ERROR)
|
|
{
|
|
// Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
pResult->pAttribArray = pwb->pMPPEKeyAttributes;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Fill the EAP_ERROR
|
|
//
|
|
retCode = AllocateandFillEapError(&(pResult->pEapError),
|
|
1, ///> Exact Cause of Authentication Failure
|
|
0,
|
|
NULL, NULL, NULL,
|
|
NULL, NULL
|
|
);
|
|
if(retCode != ERROR_SUCCESS)
|
|
{
|
|
//Report Error
|
|
}
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
if((retCode != NO_ERROR) &&
|
|
(pResult != NULL))
|
|
{
|
|
if (pResult->pConnectionData)
|
|
FreeMemory((PVOID*)&(pResult->pConnectionData));
|
|
if (pResult->pUserData)
|
|
FreeMemory((PVOID*)&(pResult->pUserData));
|
|
}
|
|
return retCode;
|
|
}
|
|
|
|
DWORD WINAPI SdkEapPeerGetUIContext(
|
|
IN EAP_SESSION_HANDLE sessionHandle,
|
|
OUT DWORD* dwSizeOfUIContextData,
|
|
OUT BYTE** pUIContextData,
|
|
OUT EAP_ERROR** ppEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
EAPCB* pwb = NULL;
|
|
|
|
//Sanity Check
|
|
if(!sessionHandle || !dwSizeOfUIContextData || !pUIContextData || !ppEapError)
|
|
{
|
|
EapTrace("EapPeerGetUIContext() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
pwb = (EAPCB*)sessionHandle;
|
|
|
|
if(pwb->pUIContext != NULL)
|
|
{
|
|
*dwSizeOfUIContextData = pwb->dwSizeOfUIContext;
|
|
*pUIContextData = pwb->pUIContext;
|
|
}
|
|
else
|
|
{
|
|
*dwSizeOfUIContextData = 0;
|
|
}
|
|
|
|
Cleanup:
|
|
return retCode;
|
|
}
|
|
|
|
DWORD WINAPI SdkEapPeerSetUIContext(
|
|
IN EAP_SESSION_HANDLE sessionHandle,
|
|
IN DWORD dwSizeOfUIContextData,
|
|
IN const BYTE* pUIContextData,
|
|
OUT EapPeerMethodOutput* pEapOutput,
|
|
OUT EAP_ERROR** ppEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
EAPCB* pwb = NULL;
|
|
|
|
//Sanity Check
|
|
if(!sessionHandle || !pEapOutput || !pUIContextData || !ppEapError)
|
|
{
|
|
EapTrace("EapPeerSetUIContext() --- 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)
|
|
{
|
|
//
|
|
// if the function is called after InvokeInteractiveUI.
|
|
//
|
|
case MYSTATE_InteractiveUI:
|
|
// Free the Interactive UI if already stored.
|
|
retCode = FreeMemory((PVOID*)&(pwb->pDataFromInteractiveUI));
|
|
if (retCode != NO_ERROR)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
//
|
|
// Look if the answer is "OK".
|
|
//
|
|
if(dwSizeOfUIContextData != STRING_LENGTH_UI_SUCCESS)
|
|
{
|
|
//Error
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
if(memcmp((void *)STRING_UI_SUCCESS, pUIContextData,
|
|
STRING_LENGTH_UI_SUCCESS) != 0)
|
|
{
|
|
//Error
|
|
goto Cleanup;
|
|
}
|
|
//
|
|
// If the user has agreed to use the Sample Eap, then the supplicant should send
|
|
// response to the EAP-Challenge (i.e., the password). The peer already has the
|
|
// password stored (from InvokeIdentityUI). Everything is ready for sending the
|
|
// EAP-Challenge response. Therefore, the action = ResponseActionSend.
|
|
//
|
|
pEapOutput->action = EapPeerMethodResponseActionSend;
|
|
pEapOutput->fAllowNotifications = FALSE; //??
|
|
//
|
|
// EapState changes to WaitForRequest signifying user agreed to use Sample Eap.
|
|
//
|
|
pwb->EapState = MYSTATE_AfterUserOK;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Peer Method has to be in MYSTATE_InteractiveUI state when SdkEapPeerSetUIContext
|
|
// is called. Any other state is not acceptable according to the state machine of this Eap Method.
|
|
//
|
|
EapTrace("SdkEapPeerSetUIContext --- Peer state: [default] Present State = %d",
|
|
pwb->EapState);
|
|
// Need to fill the EapError
|
|
retCode = ERROR_INVALID_STATE;
|
|
break;
|
|
}
|
|
|
|
Cleanup:
|
|
return retCode;
|
|
}
|
|
|
|
DWORD WINAPI SdkEapPeerGetResponseAttributes(
|
|
IN EAP_SESSION_HANDLE sessionHandle,
|
|
OUT EapAttributes* pAttribs,
|
|
OUT EAP_ERROR** ppEapError
|
|
)
|
|
{
|
|
DWORD retCode = ERROR_SUCCESS;
|
|
EAPCB* pwb = NULL;
|
|
|
|
if(!sessionHandle || !pAttribs || !ppEapError)
|
|
{
|
|
EapTrace("SdkEapPeerGetResponseAttributes() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
pwb = (EAPCB*)sessionHandle;
|
|
|
|
//
|
|
// Fill the pAttribs Structure
|
|
//
|
|
pAttribs->dwNumberOfAttributes = 1;
|
|
pAttribs->pAttribs = pwb->pFakeAttribute;
|
|
|
|
Cleanup:
|
|
return retCode;
|
|
}
|
|
|
|
DWORD WINAPI SdkEapPeerSetResponseAttributes(
|
|
IN EAP_SESSION_HANDLE sessionHandle,
|
|
IN EapAttributes* pAttribs,
|
|
OUT EapPeerMethodOutput* pEapOutput,
|
|
OUT EAP_ERROR** ppEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
HRESULT hr = S_OK;
|
|
EAPCB* pwb = NULL;
|
|
DWORD numAttributes = 0;
|
|
DWORD numAttrCtr = 0;
|
|
EapAttribute *pEapAttribute = NULL;
|
|
|
|
if(!sessionHandle || !pAttribs || !pEapOutput || !ppEapError)
|
|
{
|
|
EapTrace("SdkEapPeerSetResponseAttributes() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
pwb = (EAPCB*)sessionHandle;
|
|
|
|
//
|
|
// We need to process the attributes that is received.
|
|
//
|
|
|
|
numAttributes = pAttribs->dwNumberOfAttributes;
|
|
|
|
for(numAttrCtr = 0; numAttrCtr < numAttributes; numAttrCtr++)
|
|
{
|
|
pEapAttribute = (pAttribs->pAttribs) + numAttrCtr;
|
|
|
|
//
|
|
// Except only AttributeType = 2 in response.
|
|
//
|
|
switch(pEapAttribute->eaType)
|
|
{
|
|
case 2:
|
|
//
|
|
// Expected Attribute ---- eaType = 2; dwLength = 4; pValue = 0 or 1
|
|
//
|
|
{
|
|
DWORD attrLength = 0;
|
|
DWORD attrValue = 0;
|
|
attrLength = pEapAttribute->dwLength;
|
|
|
|
if(attrLength != sizeof(DWORD))
|
|
{
|
|
EapTrace("Sdk Peer Method expects length = 4.");
|
|
}
|
|
|
|
attrValue = *((DWORD *)pEapAttribute->pValue);
|
|
|
|
if(attrValue != 1 && attrValue != 0)
|
|
{
|
|
EapTrace("Sdk Peer Method expects attrValue to be either 1 or 0");
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
EapTrace("Sdk Peer Method does not process attributes except EapAttrType = 2");
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fill the output
|
|
//
|
|
|
|
//
|
|
// Check for EAP_FLAG_SUPRESS_UI flag.
|
|
// If this flag is set, we should not raise an interactive UI and go striaght to
|
|
// sending the packet that contains user password.
|
|
// This is used in SSO(Single Sign On) scenario where MS supplicant does not
|
|
// expect any UI to raised.
|
|
//
|
|
if(pwb->fFlags & EAP_FLAG_SUPRESS_UI)
|
|
{
|
|
pEapOutput->action = EapPeerMethodResponseActionSend;
|
|
pEapOutput->fAllowNotifications = FALSE;
|
|
//
|
|
// EapState changes to MYSTATE_AfterUserOK signifying user agreed to use Sample Eap.
|
|
//
|
|
pwb->EapState = MYSTATE_AfterUserOK;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Bring up interactive UI to notify user that he/she is being
|
|
// authenticated via the sample EAP
|
|
//
|
|
pEapOutput->action = EapPeerMethodResponseActionInvokeUI;
|
|
pEapOutput->fAllowNotifications = TRUE;
|
|
|
|
if (NULL == pwb->pDataFromInteractiveUI)
|
|
{
|
|
pwb->dwSizeOfUIContext = (DWORD)STRING_LENGTH_UI_ALLOW_AUTH;
|
|
|
|
retCode = AllocateMemory(pwb->dwSizeOfUIContext, (PVOID*)&(pwb->pUIContext));
|
|
if (retCode != NO_ERROR)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = StringCbCopyW((WCHAR*)pwb->pUIContext, pwb->dwSizeOfUIContext, STRING_UI_ALLOW_AUTH);
|
|
if (FAILED(hr))
|
|
{
|
|
retCode = HRESULT_CODE(hr);
|
|
EapTrace("Error while copying UI data to be displayed! (error %d)",retCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
pwb->EapState = MYSTATE_InteractiveUI;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
if((retCode != NO_ERROR) &&
|
|
(pwb != NULL) &&
|
|
(pwb->pUIContext != NULL))
|
|
FreeMemory((PVOID*)&(pwb->pUIContext));
|
|
|
|
return retCode;
|
|
}
|
|
|
|
DWORD WINAPI SdkEapPeerEndSession(
|
|
IN EAP_SESSION_HANDLE sessionHandle,
|
|
OUT EAP_ERROR** ppEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
EAPCB* pwb = NULL;
|
|
|
|
// Sanity check.
|
|
if (!sessionHandle || !ppEapError)
|
|
{
|
|
EapTrace("SdkEapPeerEndSession() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
pwb = (EAPCB *)sessionHandle;
|
|
|
|
//
|
|
// Free up the MPPE Key Attributes
|
|
//
|
|
FreeAttributes(&(pwb->pMPPEKeyAttributes));
|
|
|
|
//
|
|
// Free the Connection Data.
|
|
//
|
|
FreeMemory((PVOID*)&(pwb->pConnectionData));
|
|
|
|
//
|
|
// Free the User Data.
|
|
//
|
|
SecureZeroMemory(pwb->pUserData, pwb->dwSizeofUserData);
|
|
FreeMemory((PVOID*)&(pwb->pUserData));
|
|
|
|
//
|
|
// Free the UI Context Data.
|
|
//
|
|
FreeMemory((PVOID*)&(pwb->pUIContext));
|
|
|
|
//
|
|
// Free the Input Attributes.
|
|
//
|
|
FreeAttributes(&(pwb->pEapAttributes));
|
|
|
|
// Free the Fake Attribute.
|
|
FreeMemory((PVOID *)&(pwb->pFakeAttribute->pValue));
|
|
FreeMemory((PVOID *)&(pwb->pFakeAttribute));
|
|
|
|
// Use SecureZeroMemory() here, to ensure that any authentication data is
|
|
// initialized safely, before the memory block is freed back to the system.
|
|
SecureZeroMemory(pwb, sizeof(EAPCB));
|
|
FreeMemory((PVOID*)&pwb);
|
|
|
|
Cleanup:
|
|
return retCode;
|
|
}
|
|
|
|
DWORD WINAPI SdkEapPeerShutdown(OUT EAP_ERROR** ppEapError)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
|
|
//Sanity Check
|
|
if( !ppEapError)
|
|
{
|
|
EapTrace("SdkEapPeerShutdown() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// 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 Peer Method.
|
|
//
|
|
|
|
DWORD WINAPI EapPeerInvokeConfigUI(
|
|
IN EAP_METHOD_TYPE* pEapType,
|
|
IN HWND hwndParent,
|
|
IN DWORD dwFlags,
|
|
IN DWORD dwSizeOfConnectionDataIn,
|
|
IN BYTE* pConnectionDataIn,
|
|
OUT DWORD* dwSizeOfConnectionDataOut,
|
|
OUT BYTE** ppConnectionDataOut,
|
|
OUT EAP_ERROR** pEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
|
|
UNREFERENCED_PARAMETER(pEapType);
|
|
if(!pEapType || !dwSizeOfConnectionDataOut || !ppConnectionDataOut || !pEapError)
|
|
{
|
|
EapTrace("EapPeerInvokeConfigUI() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((pEapType->eapType.type != EAPTYPE) ||
|
|
(pEapType->dwAuthorId != AUTHOR_ID))
|
|
{
|
|
EapTrace("EapPeerInvokeConfigUI() --- Input Eap Type Info does not match the supported Eap Type");
|
|
retCode = ERROR_NOT_SUPPORTED;
|
|
// Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
|
|
retCode = GetConfigData(hwndParent,
|
|
pConnectionDataIn,
|
|
dwSizeOfConnectionDataIn,
|
|
ppConnectionDataOut,
|
|
dwSizeOfConnectionDataOut);
|
|
if(retCode != ERROR_SUCCESS)
|
|
{
|
|
EapTrace("EapPeerInvokeConfigUI() --- GetConfigData returned with retCode = %d", retCode);
|
|
// Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
return retCode;
|
|
}
|
|
|
|
|
|
DWORD WINAPI EapPeerConfigXml2Blob(
|
|
IN DWORD dwFlags,
|
|
IN EAP_METHOD_TYPE eapMethodType,
|
|
IN IXMLDOMDocument2* pConfigDoc,
|
|
OUT _Out_writes_(*pdwSizeOfConfigOut) BYTE** ppConfigOut,
|
|
OUT DWORD* pdwSizeOfConfigOut,
|
|
OUT EAP_ERROR** ppEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
BSTR elementValue = NULL;
|
|
CONN_DATA_BLOB* pEapConfigData = NULL;
|
|
|
|
if(!ppConfigOut || !pdwSizeOfConfigOut || !pConfigDoc || !ppEapError)
|
|
{
|
|
EapTrace("EapPeerConfigXml2Blob() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError, if ppEapError != NULL else just return
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((eapMethodType.eapType.type != EAPTYPE) ||
|
|
(eapMethodType.dwAuthorId != AUTHOR_ID))
|
|
{
|
|
EapTrace("EapPeerConfigXml2Blob() --- Input Eap Type Info does not match the supported Eap Type");
|
|
retCode = ERROR_NOT_SUPPORTED;
|
|
// Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
|
|
//
|
|
// Allocate memory for OUT parameters
|
|
//
|
|
retCode = AllocateMemory(sizeof(CONN_DATA_BLOB), (PVOID*)&pEapConfigData);
|
|
if (retCode != NO_ERROR)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Process the pConfigDoc to get the Connection BLOB.
|
|
//
|
|
//
|
|
// Sample Eap Method expects ---
|
|
// <EapType>40</EapType><ConfigData>MethodSpecificConfigData</ConfigData>
|
|
// Therefore, reads the two elements -- EapType and ConfigData
|
|
//
|
|
|
|
// Read the EapType element from XML
|
|
retCode = GetXmlElementValue(pConfigDoc, L"EapType", CONNECTION_PROPERTIES, elementValue);
|
|
if((retCode != ERROR_SUCCESS) || (elementValue == NULL))
|
|
{
|
|
//Need to fill EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Set the EapTypeId of CONN_DATA_BLOB
|
|
pEapConfigData->eapTypeId = _wtol((WCHAR*)elementValue);
|
|
SysFreeString(elementValue);
|
|
|
|
// Read the ConfigData element from XML
|
|
retCode = GetXmlElementValue(pConfigDoc, L"ConfigData", CONNECTION_PROPERTIES, elementValue);
|
|
if(retCode != ERROR_SUCCESS)
|
|
{
|
|
//Need to fill EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Set the awszData of CONN_DATA_BLOB
|
|
CopyMemory(pEapConfigData->awszData, (BYTE *)elementValue,
|
|
((SysStringLen(elementValue) + 1) * sizeof(wchar_t)));
|
|
SysFreeString(elementValue);
|
|
|
|
//
|
|
// Set the output parameters.
|
|
//
|
|
*ppConfigOut = (BYTE *)pEapConfigData;
|
|
*pdwSizeOfConfigOut = sizeof(CONN_DATA_BLOB);
|
|
|
|
pEapConfigData = NULL;
|
|
|
|
Cleanup:
|
|
if((retCode != ERROR_SUCCESS) && (pEapConfigData != NULL))
|
|
FreeMemory((PVOID*)&pEapConfigData);
|
|
|
|
return retCode;
|
|
}
|
|
|
|
|
|
|
|
DWORD WINAPI EapPeerCredentialsXml2Blob(
|
|
IN DWORD dwFlags,
|
|
IN EAP_METHOD_TYPE eapMethodType,
|
|
IN IXMLDOMDocument2* pCredentialsDoc,
|
|
IN _In_reads_(dwSizeOfConfigIn) const BYTE* pConfigIn,
|
|
IN DWORD dwSizeOfConfigIn,
|
|
OUT _Out_writes_(*pdwSizeOfCredentialsOut) BYTE** ppCredentialsOut,
|
|
OUT DWORD* pdwSizeOfCredentialsOut,
|
|
OUT EAP_ERROR** ppEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
BSTR elementValue = NULL;
|
|
USER_DATA_BLOB* pEapUserData = NULL;
|
|
|
|
if(!ppCredentialsOut || !pdwSizeOfCredentialsOut || !pCredentialsDoc || !ppEapError)
|
|
{
|
|
EapTrace("EapPeerCredentialsXml2Blob() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError, if ppEapError != NULL else just return
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((eapMethodType.eapType.type != EAPTYPE) ||
|
|
(eapMethodType.dwAuthorId != AUTHOR_ID))
|
|
{
|
|
EapTrace("EapPeerCredentialsXml2Blob() --- Input Eap Type Info does not match the supported Eap Type");
|
|
retCode = ERROR_NOT_SUPPORTED;
|
|
// Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
UNREFERENCED_PARAMETER(pConfigIn);
|
|
UNREFERENCED_PARAMETER(dwSizeOfConfigIn);
|
|
|
|
//
|
|
// Allocate memory for OUT parameters
|
|
//
|
|
retCode = AllocateMemory(sizeof(USER_DATA_BLOB), (PVOID*)&pEapUserData);
|
|
if (retCode != NO_ERROR)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Process the pCredentialsDoc to get the User BLOB.
|
|
//
|
|
// Sample Eap Method expects ---
|
|
// <EapType>40</EapType><Identity>IdentityInformation</Identity><Password>PasswordInformation</Password>
|
|
// Therefore, reads the three elements -- EapType, Identity and Password
|
|
//
|
|
|
|
// Read the EapType element from XML
|
|
retCode = GetXmlElementValue(pCredentialsDoc, L"EapType", USER_PROPERTIES, elementValue);
|
|
if((retCode != ERROR_SUCCESS) || (elementValue == NULL))
|
|
{
|
|
//Need to fill EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Set the EapTypeId of USER_DATA_BLOB
|
|
pEapUserData->eapTypeId = _wtol((WCHAR*)elementValue);
|
|
SysFreeString(elementValue);
|
|
|
|
// Read the Identity element from XML
|
|
retCode = GetXmlElementValue(pCredentialsDoc, L"Identity", USER_PROPERTIES, elementValue);
|
|
if(retCode != ERROR_SUCCESS)
|
|
{
|
|
//Need to fill EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Set the awszIdentity of USER_DATA_BLOB
|
|
CopyMemory(pEapUserData->eapUserNamePassword.awszIdentity,
|
|
(BYTE *)elementValue,
|
|
((SysStringLen(elementValue) + 1) * sizeof(wchar_t)));
|
|
SysFreeString(elementValue);
|
|
|
|
// Read the Password element from XML
|
|
retCode = GetXmlElementValue(pCredentialsDoc, L"Password", USER_PROPERTIES, elementValue);
|
|
if(retCode != ERROR_SUCCESS)
|
|
{
|
|
//Need to fill EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Set the awszPassword of USER_DATA_BLOB
|
|
CopyMemory(pEapUserData->eapUserNamePassword.awszPassword,
|
|
(BYTE *)elementValue,
|
|
((SysStringLen(elementValue) + 1) * sizeof(wchar_t)));
|
|
SysFreeString(elementValue);
|
|
|
|
|
|
//
|
|
// Set the output parameters.
|
|
//
|
|
*ppCredentialsOut = (BYTE *)pEapUserData;
|
|
*pdwSizeOfCredentialsOut = sizeof(USER_DATA_BLOB);
|
|
|
|
pEapUserData = NULL;
|
|
|
|
Cleanup:
|
|
if(retCode != ERROR_SUCCESS)
|
|
FreeMemory((PVOID*)&pEapUserData);
|
|
|
|
return retCode;
|
|
}
|
|
|
|
|
|
|
|
DWORD WINAPI EapPeerConfigBlob2Xml(
|
|
IN DWORD dwFlags,
|
|
IN EAP_METHOD_TYPE eapMethodType,
|
|
IN _In_reads_(dwSizeOfConfigIn) const BYTE* pConfigIn,
|
|
IN DWORD dwSizeOfConfigIn,
|
|
OUT IXMLDOMDocument2** ppConfigDoc,
|
|
OUT EAP_ERROR** ppEapError
|
|
)
|
|
{
|
|
DWORD retCode = ERROR_SUCCESS;
|
|
IXMLDOMDocument2 *pXmlDoc = NULL;
|
|
|
|
// pConfigIn can be NULL. If it is, use the default configuration of the method.
|
|
if(!ppConfigDoc || !ppEapError)
|
|
{
|
|
EapTrace("EapPeerConfigBlob2Xml() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError, if ppEapError != NULL else just return
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((eapMethodType.eapType.type != EAPTYPE) ||
|
|
(eapMethodType.dwAuthorId != AUTHOR_ID))
|
|
{
|
|
EapTrace("EapPeerConfigBlob2Xml() --- Input Eap Type Info does not match the supported Eap Type");
|
|
retCode = ERROR_NOT_SUPPORTED;
|
|
// Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
UNREFERENCED_PARAMETER(pConfigIn);
|
|
UNREFERENCED_PARAMETER(dwSizeOfConfigIn);
|
|
|
|
HRESULT hr = CoCreateInstance(CLSID_DOMDocument60,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IXMLDOMDocument2,
|
|
reinterpret_cast<void**>(&pXmlDoc)
|
|
);
|
|
if(FAILED(hr))
|
|
{
|
|
retCode = HRESULT_CODE(hr);
|
|
EapTrace("EapPeerConfigBlob2Xml() --- Unable to CoCreate XMLDOMDocument2: retCode = %d", retCode);
|
|
// Need to fill EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = pXmlDoc->put_async(VARIANT_FALSE);
|
|
if (FAILED(hr))
|
|
{
|
|
retCode = HRESULT_CODE(hr);
|
|
EapTrace("EapPeerConfigBlob2Xml() --- put_async failed: retCode = %d", retCode);
|
|
// Need to fill EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// One is expected to read the pConfigIn and then decide what should be the content of XML Doc.
|
|
// For simplicity, we have hardcoded the XML Doc that is sent in response.
|
|
//
|
|
BSTR xml = L"<ConfigBlob xmlns='http://www.microsoft.com/provisioning/EapHostConfig'>2800000061006E0075006A006B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</ConfigBlob>";
|
|
VARIANT_BOOL isSuccess = VARIANT_FALSE;
|
|
hr = pXmlDoc->loadXML(xml, &isSuccess);
|
|
if (FAILED(hr) || (isSuccess != VARIANT_TRUE))
|
|
{
|
|
retCode = HRESULT_CODE(hr);
|
|
EapTrace("EapPeerConfigBlob2Xml() --- loadXML failed: retCode = %d", retCode);
|
|
// Need to fill EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set the output parameters.
|
|
//
|
|
*ppConfigDoc = pXmlDoc;
|
|
|
|
pXmlDoc = NULL;
|
|
|
|
Cleanup:
|
|
|
|
if(pXmlDoc)
|
|
pXmlDoc->Release();
|
|
|
|
return retCode;
|
|
}
|
|
|
|
|
|
DWORD WINAPI EapPeerInvokeInteractiveUI(
|
|
IN EAP_METHOD_TYPE* pEapType,
|
|
IN HWND hwndParent,
|
|
IN DWORD dwSizeofUIContextData,
|
|
IN BYTE* pUIContextData,
|
|
OUT DWORD* pdwSizeOfDataFromInteractiveUI,
|
|
OUT BYTE** ppDataFromInteractiveUI,
|
|
OUT EAP_ERROR** ppEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
HRESULT hr = S_OK;
|
|
|
|
if (! pUIContextData || !ppDataFromInteractiveUI || !pdwSizeOfDataFromInteractiveUI || !ppEapError)
|
|
{
|
|
EapTrace("EapPeerInvokeInteractiveUI() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((pEapType->eapType.type != EAPTYPE) ||
|
|
(pEapType->dwAuthorId != AUTHOR_ID))
|
|
{
|
|
EapTrace("EapPeerInvokeInteractiveUI() --- Input Eap Type Info does not match the supported Eap Type");
|
|
retCode = ERROR_NOT_SUPPORTED;
|
|
// Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
|
|
UNREFERENCED_PARAMETER(dwSizeofUIContextData);
|
|
UNREFERENCED_PARAMETER(hwndParent);
|
|
|
|
if (MessageBox(hwndParent, (WCHAR*)pUIContextData,
|
|
STRING_INTERACTIVE_UI_TITLE,
|
|
MB_OKCANCEL) == IDOK)
|
|
{
|
|
//
|
|
// If the user presses OK on the Message Box, then he/she agrees to use the EAP Sample.
|
|
//
|
|
//
|
|
// Populate the output parameters -- pdwSizeOfDataFromInteractiveUI & ppDataFromInteractiveUI
|
|
//
|
|
*pdwSizeOfDataFromInteractiveUI = (DWORD)STRING_LENGTH_UI_SUCCESS;
|
|
|
|
retCode = AllocateMemory(*pdwSizeOfDataFromInteractiveUI,
|
|
(PVOID*)ppDataFromInteractiveUI);
|
|
if (retCode != NO_ERROR)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Safely copy the UI data into our output buffer.
|
|
//
|
|
hr = StringCbCopyW((WCHAR*)*ppDataFromInteractiveUI,
|
|
*pdwSizeOfDataFromInteractiveUI,
|
|
STRING_UI_SUCCESS);
|
|
if (FAILED(hr))
|
|
{
|
|
retCode = HRESULT_CODE(hr);
|
|
EapTrace("Error while copying UI data! (error %d)",retCode);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*ppDataFromInteractiveUI = NULL;
|
|
*pdwSizeOfDataFromInteractiveUI = 0;
|
|
}
|
|
|
|
Cleanup:
|
|
if (retCode != NO_ERROR && ppDataFromInteractiveUI != NULL)
|
|
{
|
|
FreeMemory((PVOID*)ppDataFromInteractiveUI);
|
|
|
|
if (pdwSizeOfDataFromInteractiveUI)
|
|
*pdwSizeOfDataFromInteractiveUI = 0;
|
|
}
|
|
|
|
return retCode;
|
|
}
|
|
|
|
DWORD WINAPI EapPeerInvokeIdentityUI(
|
|
IN EAP_METHOD_TYPE* pEapType,
|
|
IN DWORD dwFlags,
|
|
IN HWND hwndParent,
|
|
IN DWORD dwSizeOfConnectionData,
|
|
IN const BYTE* pConnectionData,
|
|
IN DWORD dwSizeOfUserData,
|
|
IN const BYTE* pUserData,
|
|
OUT DWORD* pdwSizeOfUserDataOut,
|
|
OUT BYTE** ppUserDataOut,
|
|
OUT _Outptr_ LPWSTR* ppwszIdentity,
|
|
OUT EAP_ERROR** pEapError
|
|
)
|
|
{
|
|
DWORD retCode = NO_ERROR;
|
|
|
|
if (!ppwszIdentity)
|
|
{
|
|
EapTrace("SdkEapPeerGetIdentity() --- ppwszIdentity paramters is NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
*ppwszIdentity = L"";
|
|
|
|
if(!pEapType || !pdwSizeOfUserDataOut || !ppUserDataOut || !pEapError)
|
|
{
|
|
EapTrace("EapPeerInvokeIdentityUI() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((pEapType->eapType.type != EAPTYPE) ||
|
|
(pEapType->dwAuthorId != AUTHOR_ID))
|
|
{
|
|
EapTrace("EapPeerInvokeIdentityUI() --- Input Eap Type Info does not match the supported Eap Type");
|
|
retCode = ERROR_NOT_SUPPORTED;
|
|
// Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
UNREFERENCED_PARAMETER(dwSizeOfConnectionData);
|
|
UNREFERENCED_PARAMETER(pConnectionData);
|
|
|
|
retCode = GetIdentity(
|
|
hwndParent,
|
|
(BYTE *)pUserData,
|
|
dwSizeOfUserData,
|
|
ppUserDataOut,
|
|
pdwSizeOfUserDataOut,
|
|
ppwszIdentity);
|
|
|
|
Cleanup:
|
|
|
|
return retCode;
|
|
}
|
|
|
|
|
|
DWORD WINAPI EapPeerQueryCredentialInputFields(
|
|
IN HANDLE hUserToken,
|
|
IN EAP_METHOD_TYPE eapType,
|
|
IN DWORD dwFlags,
|
|
IN DWORD dwEapConnDataSize,
|
|
IN _In_reads_(dwEapConnDataSize)PBYTE pbEapConnData,
|
|
OUT EAP_CONFIG_INPUT_FIELD_ARRAY *pEapConfigFieldsArray,
|
|
OUT EAP_ERROR **ppEapError
|
|
) throw()
|
|
{
|
|
DWORD retval = NO_ERROR;
|
|
|
|
UNREFERENCED_PARAMETER(hUserToken);
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
UNREFERENCED_PARAMETER(dwEapConnDataSize);
|
|
UNREFERENCED_PARAMETER(pbEapConnData);
|
|
|
|
if (! pEapConfigFieldsArray ||
|
|
! ppEapError)
|
|
{
|
|
retval = ERROR_INVALID_PARAMETER;
|
|
EapTrace("QueryCredentalInputFields(): Error: Output pointer was NULL!");
|
|
// Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Verify if eapType passed by EapHost correctly matches the EapType of this DLL.
|
|
//
|
|
if ((eapType.eapType.type != EAPTYPE) ||
|
|
(eapType.dwAuthorId != AUTHOR_ID))
|
|
{
|
|
EapTrace("EapPeerQueryCredentialInputFields() --- Input Eap Type Info does not match the supported Eap Type");
|
|
retval = ERROR_NOT_SUPPORTED;
|
|
// Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Copy this method's default credential input field array, and pass it
|
|
// back to the caller.
|
|
retval = CopyCredentialInputArray(pEapConfigFieldsArray,
|
|
&defaultCredentialInputArray);
|
|
if (retval != NO_ERROR)
|
|
{
|
|
EapTrace("QueryCredentalInputFields(): Hit error %d while copying credential field array!",
|
|
retval);
|
|
// Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
return retval;
|
|
}
|
|
|
|
|
|
DWORD WINAPI EapPeerQueryUserBlobFromCredentialInputFields(
|
|
IN HANDLE hUserToken,
|
|
IN EAP_METHOD_TYPE eapType,
|
|
IN DWORD dwFlags,
|
|
IN DWORD dwEapConnDataSize,
|
|
IN PBYTE pbEapConnData,
|
|
IN EAP_CONFIG_INPUT_FIELD_ARRAY *pEapConfigFieldsArray,
|
|
OUT DWORD *pdwUserBlobSize,
|
|
OUT PBYTE *ppbUserBlob,
|
|
OUT EAP_ERROR **ppEapError
|
|
) throw()
|
|
{
|
|
DWORD retval = NO_ERROR;
|
|
|
|
UNREFERENCED_PARAMETER(hUserToken);
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
UNREFERENCED_PARAMETER(dwEapConnDataSize);
|
|
UNREFERENCED_PARAMETER(pbEapConnData);
|
|
|
|
|
|
if (! pEapConfigFieldsArray ||
|
|
! pdwUserBlobSize ||
|
|
! ppbUserBlob ||
|
|
! ppEapError)
|
|
{
|
|
retval = ERROR_INVALID_PARAMETER;
|
|
EapTrace("QueryUserBlobFromCredentialInputFields(): Error: One or more input/output pointers were NULL!");
|
|
// Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Verify if eapType passed by EapHost correctly matches the EapType of this DLL.
|
|
//
|
|
if ((eapType.eapType.type != EAPTYPE) ||
|
|
(eapType.dwAuthorId != AUTHOR_ID))
|
|
{
|
|
retval = ERROR_NOT_SUPPORTED;
|
|
EapTrace("EapPeerQueryUserBlobFromCredentialInputFields() --- Input Eap Type Info does not match the supported Eap Type");
|
|
// Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Generate a User Data blob that corresponds to the credential input field
|
|
// data received.
|
|
retval = ConstructUserBlobFromCredentialInputArray(
|
|
&eapType,
|
|
pEapConfigFieldsArray,
|
|
pdwUserBlobSize,
|
|
ppbUserBlob);
|
|
if (retval != NO_ERROR)
|
|
{
|
|
EapTrace("QueryUserBlobFromCredentialInputFields(): Hit error %d while copying credential field array!",
|
|
retval);
|
|
// Need to fill the EapError.
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
return retval;
|
|
}
|
|
|
|
|
|
//
|
|
// Function exposed for getting the default connection blob
|
|
// and user blob to be used during the method discovery
|
|
//
|
|
DWORD WINAPI EapPeerGetConfigBlobAndUserBlob(
|
|
IN DWORD dwFlags,
|
|
IN EAP_METHOD_TYPE eapMethodType,
|
|
IN EapCredential eapCred,
|
|
OUT DWORD* pdwConfigBlobSize,
|
|
OUT BYTE** ppConfigBlob,
|
|
OUT DWORD* pdwUserBlobSize,
|
|
OUT BYTE** ppUserBlob,
|
|
OUT EAP_ERROR** ppEapError
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
UNREFERENCED_PARAMETER(eapMethodType);
|
|
|
|
DWORD retCode = NO_ERROR;
|
|
CONN_DATA_BLOB* pEapConfigData = NULL;
|
|
USER_DATA_BLOB* pEapUserData = NULL;
|
|
|
|
*pdwConfigBlobSize = 0;
|
|
*ppConfigBlob = NULL;
|
|
*pdwUserBlobSize = 0;
|
|
*ppUserBlob = NULL;
|
|
*ppEapError = NULL;
|
|
|
|
// check if the credential type is anything other than username/password
|
|
if (eapCred.credType != EAP_USERNAME_PASSWORD_CREDENTIAL)
|
|
{
|
|
EapTrace("EapPeerGetConfigBlobAndUserBlob() --- Unknown credential type");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Get the default connection data
|
|
retCode = AllocateMemory(sizeof(CONN_DATA_BLOB), (PVOID*)&pEapConfigData);
|
|
if (retCode != NO_ERROR)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
pEapConfigData->eapTypeId = EAPTYPE;
|
|
|
|
// Construct the user data
|
|
retCode = AllocateMemory(sizeof(USER_DATA_BLOB), (PVOID*)&pEapUserData);
|
|
if (retCode != NO_ERROR)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
pEapUserData->eapTypeId = EAPTYPE;
|
|
|
|
wcscpy_s(pEapUserData->eapUserNamePassword.awszIdentity, UNLEN, eapCred.credData.username_password.username);
|
|
wcscpy_s(pEapUserData->eapUserNamePassword.awszPassword, PWLEN, eapCred.credData.username_password.password);
|
|
|
|
*pdwConfigBlobSize = sizeof(CONN_DATA_BLOB);
|
|
*ppConfigBlob = (BYTE *)pEapConfigData;
|
|
*pdwUserBlobSize = sizeof(USER_DATA_BLOB);
|
|
*ppUserBlob = (BYTE *)pEapUserData;
|
|
|
|
Cleanup:
|
|
return retCode;
|
|
}
|
|
|
|
|
|
VOID WINAPI EapPeerFreeMemory(
|
|
_In_ void* pUIContextData
|
|
)
|
|
{
|
|
FreeMemory((PVOID*)&pUIContextData);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Helper Functions
|
|
//
|
|
|
|
/**
|
|
* MakeResponseMessage() helper function: Construct a response message
|
|
*
|
|
* This function constructs the EAP provider's response to the received
|
|
* EAP-Challenge packet.
|
|
*
|
|
*
|
|
* @param pwb [in] Pointer to the work buffer.
|
|
*
|
|
* @param pSendBuf [out] Pointer to a EapPacket structure.
|
|
*
|
|
* @param cbSendBuf [out] Specifies the size, in bytes, of the buffer
|
|
* pointed to by pSendBuf.
|
|
*
|
|
*
|
|
* @return If the function succeeds, the return value is NO_ERROR.
|
|
*/
|
|
DWORD
|
|
MakeResponseMessage(
|
|
IN EAPCB* pwb,
|
|
IN DWORD * pcbSendBuf,
|
|
OUT EapPacket * pSendBuf
|
|
)
|
|
{
|
|
DWORD retCode = ERROR_SUCCESS;
|
|
BYTE* pcbPassword = NULL;
|
|
CHAR* pszPassword = NULL;
|
|
size_t sizePassword = 0;
|
|
HRESULT hr = S_OK;
|
|
DWORD packetSize = 0;
|
|
|
|
if(!pwb || !pcbSendBuf || !pSendBuf)
|
|
{
|
|
EapTrace("MakeResponseMessage() --- One/Some of the paramters is/are NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Packet Structure
|
|
// Code - 1 Byte
|
|
// Id - 1 Byte
|
|
// Length - 2 Bytes
|
|
// Data[0] - EAPTYPE
|
|
// Data[1] - Length of Password (X).
|
|
// Data[2] to Data[X+1] - Password
|
|
//
|
|
|
|
//
|
|
// Fill in the password.
|
|
//
|
|
|
|
pcbPassword = pSendBuf->Data + 1;
|
|
|
|
// Note: StringCbLength() does not include room for the trailing NULL. This
|
|
// is valid for the packet (which doesn't need the NULL), but calls to any
|
|
// length-based string copy functions will need to add room for the NULL.
|
|
hr = StringCbLengthA(pwb->aszPassword, (size_t)(PWLEN + 1), &sizePassword);
|
|
if (FAILED(hr))
|
|
{
|
|
retCode = HRESULT_CODE(hr);
|
|
EapTrace("Error while calculating password length! (error %d)", retCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
*pcbPassword = (BYTE)sizePassword;
|
|
|
|
pszPassword = (PCHAR)(pcbPassword + 1);
|
|
|
|
// This function needs the length parameter to include room for the NULL.
|
|
hr = StringCbCopyA(pszPassword, (*pcbPassword)+1, pwb->aszPassword);
|
|
if (FAILED(hr))
|
|
{
|
|
retCode = HRESULT_CODE(hr);
|
|
EapTrace("Error while copying password into response message! (error %d)",
|
|
retCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
packetSize = EAP_PACKET_HDR_LEN+1+*pcbPassword+1;
|
|
|
|
if(packetSize > *pcbSendBuf)
|
|
{
|
|
// Error
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
*pcbSendBuf = packetSize;
|
|
|
|
//
|
|
// Set the response code
|
|
//
|
|
pSendBuf->Code = (BYTE)EapCodeResponse;
|
|
|
|
//
|
|
// The Reponse packet Id MUST match the Request packet Id.
|
|
//
|
|
pSendBuf->Id = pwb->bRecvPacketId;
|
|
|
|
//
|
|
// Set the EAP type ID
|
|
//
|
|
pSendBuf->Data[0] = (BYTE)EAPTYPE;
|
|
|
|
//
|
|
// Set the length of the packet
|
|
//
|
|
HostToWireFormat16((WORD)packetSize, pSendBuf->Length);
|
|
|
|
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 pUserDataIn [in] Pointer to the user-specific data.
|
|
*
|
|
*
|
|
* @param dwSizeOfUserDataIn [in] Specifies the size of the
|
|
* user-specific data currently stored.
|
|
*
|
|
* @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
|
|
GetIdentity(
|
|
IN HWND hwndParent,
|
|
IN BYTE* pUserDataIn,
|
|
IN DWORD dwSizeOfUserDataIn,
|
|
OUT BYTE** ppUserDataOut,
|
|
OUT DWORD* pdwSizeOfUserDataOut,
|
|
OUT _Out_ LPWSTR* ppwszIdentityOut
|
|
)
|
|
{
|
|
USER_DATA_BLOB* pEapUserData = NULL;
|
|
DWORD retCode = NO_ERROR;
|
|
size_t lenIdentity = 0;
|
|
WCHAR* pwszIdentity = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
if (!ppwszIdentityOut)
|
|
{
|
|
EapTrace("SdkEapPeerGetIdentity() --- ppwszIdentityOut paramters is NULL");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
// Need to fill the EapError
|
|
goto Cleanup;
|
|
}
|
|
*ppwszIdentityOut = NULL;
|
|
|
|
// Sanity checks.
|
|
if (! ppUserDataOut || ! pdwSizeOfUserDataOut)
|
|
{
|
|
EapTrace("Error -- one or more output pointers is NULL!");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
UNREFERENCED_PARAMETER(dwSizeOfUserDataIn);
|
|
UNREFERENCED_PARAMETER(hwndParent);
|
|
|
|
//
|
|
// Allocate memory for OUT parameters
|
|
//
|
|
|
|
retCode = AllocateMemory(sizeof(USER_DATA_BLOB), (PVOID*)&pEapUserData);
|
|
if (retCode != NO_ERROR)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
lenIdentity = (UNLEN+1) * sizeof(WCHAR);
|
|
retCode = AllocateMemory((DWORD)lenIdentity, (PVOID*)&pwszIdentity);
|
|
if (retCode != NO_ERROR)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (NULL != pUserDataIn)
|
|
{
|
|
//
|
|
// Use the saved credentials if they exist
|
|
//
|
|
|
|
CopyMemory(pEapUserData, pUserDataIn, sizeof(USER_DATA_BLOB));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Else prompt for username and password
|
|
//
|
|
|
|
GetUsernameAndPassword(hwndParent, pEapUserData);
|
|
}
|
|
|
|
pEapUserData->eapTypeId = EAPTYPE;
|
|
|
|
// Safely copy the UI data into our output buffer.
|
|
hr = StringCbCopy(pwszIdentity,
|
|
lenIdentity,
|
|
pEapUserData->eapUserNamePassword.awszIdentity);
|
|
if (FAILED(hr))
|
|
{
|
|
retCode = HRESULT_CODE(hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set the OUT paramters
|
|
//
|
|
|
|
*ppUserDataOut = (BYTE*)pEapUserData;
|
|
*pdwSizeOfUserDataOut = sizeof(USER_DATA_BLOB);
|
|
*ppwszIdentityOut = pwszIdentity;
|
|
|
|
//
|
|
// We mustn't free OUT parameters.
|
|
//
|
|
|
|
pEapUserData = NULL;
|
|
pwszIdentity = NULL;
|
|
|
|
Cleanup:
|
|
if(retCode != NO_ERROR)
|
|
{
|
|
if (pEapUserData != NULL)
|
|
FreeMemory((PVOID*)&pEapUserData);
|
|
if (pwszIdentity != NULL)
|
|
FreeMemory((PVOID*)&pwszIdentity);
|
|
}
|
|
|
|
return retCode;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Dialog routines.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* GetUsernameAndPassword() helper function: Display user credentials UI.
|
|
*
|
|
* This function displays an interactive UI (the IDD_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_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;
|
|
|
|
// 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
GetWindowText(hWnd, pEapUserData->eapUserNamePassword.awszPassword, PWLEN + 1);
|
|
|
|
// Fall through
|
|
|
|
case IDCANCEL:
|
|
|
|
EndDialog(hWndDlg, wId);
|
|
fOk = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
LDone:
|
|
return fOk;
|
|
}
|
|
|
|
|
|
DWORD GetConfigData(
|
|
IN HWND hwndParent,
|
|
IN BYTE* pConnectionDataIn,
|
|
IN DWORD dwSizeOfConnectionDataIn,
|
|
OUT BYTE** ppConnectionDataOut,
|
|
OUT DWORD* dwSizeOfConnectionDataOut
|
|
)
|
|
{
|
|
CONN_DATA_BLOB* pEapConfigData = NULL;
|
|
DWORD retCode = NO_ERROR;
|
|
|
|
|
|
// Sanity checks.
|
|
if (! ppConnectionDataOut || ! dwSizeOfConnectionDataOut)
|
|
{
|
|
EapTrace("Error -- one or more output pointers is NULL!");
|
|
retCode = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
UNREFERENCED_PARAMETER(pConnectionDataIn);
|
|
UNREFERENCED_PARAMETER(dwSizeOfConnectionDataIn);
|
|
|
|
//
|
|
// Allocate memory for OUT parameters
|
|
//
|
|
|
|
retCode = AllocateMemory(sizeof(CONN_DATA_BLOB), (PVOID*)&pEapConfigData);
|
|
if (retCode != NO_ERROR)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Prompt for config data from UI. Sets the awszData within CONN_DATA_BLOB
|
|
//
|
|
GetConnectionData(hwndParent, pEapConfigData);
|
|
|
|
//
|
|
// Set the EapType within CONN_DATA_BLOB.
|
|
//
|
|
pEapConfigData->eapTypeId = EAPTYPE;
|
|
|
|
//
|
|
// Set the OUT paramters
|
|
//
|
|
|
|
*ppConnectionDataOut = (BYTE*)pEapConfigData;
|
|
*dwSizeOfConnectionDataOut = sizeof(CONN_DATA_BLOB);
|
|
|
|
//
|
|
// We mustn't free OUT parameters.
|
|
//
|
|
|
|
pEapConfigData = NULL;
|
|
|
|
Cleanup:
|
|
return retCode;
|
|
}
|
|
|
|
DWORD
|
|
GetConnectionData(
|
|
IN HWND hwndParent,
|
|
IN CONN_DATA_BLOB* pEapConfigData
|
|
)
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
int result = 0;
|
|
|
|
result = (int)DialogBoxParam(
|
|
g_hInstance,
|
|
MAKEINTRESOURCE(IDD_CONFIG_DIALOG),
|
|
hwndParent,
|
|
ConnectionDialogProc,
|
|
(LPARAM)pEapConfigData);
|
|
if (result < 0)
|
|
{
|
|
dwErr = GetLastError();
|
|
EapTrace("Hit error %d while displaying Connection dialog!",
|
|
dwErr);
|
|
}
|
|
|
|
return(dwErr);
|
|
}
|
|
|
|
INT_PTR CALLBACK
|
|
ConnectionDialogProc(
|
|
IN HWND hWnd,
|
|
IN UINT unMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
{
|
|
DWORD dwErr = FALSE; // By default, return FALSE.
|
|
CONN_DATA_BLOB* pEapConfigData = NULL;
|
|
|
|
switch (unMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
|
|
dwErr = InitConnectionDialog(hWnd, lParam);
|
|
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
|
|
pEapConfigData = (CONN_DATA_BLOB*)((LONG_PTR)GetWindowLongPtr(hWnd, DWLP_USER));
|
|
dwErr = ConnectionCommand(pEapConfigData, LOWORD(wParam), hWnd);
|
|
|
|
break;
|
|
}
|
|
|
|
return(dwErr);
|
|
}
|
|
|
|
BOOL
|
|
InitConnectionDialog(
|
|
IN HWND hWnd,
|
|
IN LPARAM lParam
|
|
)
|
|
{
|
|
SetWindowLongPtr(hWnd, DWLP_USER, (LONG)lParam);
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
ConnectionCommand(
|
|
IN CONN_DATA_BLOB* pEapConfigData,
|
|
IN WORD wId,
|
|
IN HWND hWndDlg
|
|
)
|
|
{
|
|
BOOL fOk = FALSE;
|
|
HWND hWnd = NULL;
|
|
|
|
|
|
// Sanity checks.
|
|
if (! pEapConfigData)
|
|
{
|
|
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_CONFIG_NAME);
|
|
if (hWnd == NULL) // GetDlgItem() returns NULL for errors.
|
|
{
|
|
EapTrace("Error -- couldn't get config value! (error %d)",
|
|
GetLastError());
|
|
goto LDone;
|
|
}
|
|
|
|
GetWindowText(hWnd, pEapConfigData->awszData, PATHLEN + 1);
|
|
|
|
// Fall through
|
|
|
|
case IDCANCEL:
|
|
|
|
EndDialog(hWndDlg, wId);
|
|
fOk = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
LDone:
|
|
return fOk;
|
|
}
|
|
|
|
/**
|
|
* 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 = 0xCD;
|
|
dwRecvPattern = 0xAB;
|
|
|
|
//
|
|
// 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 EAP 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 EAP attribute data.
|
|
//
|
|
pAttrib->eaType = eatVendorSpecific;
|
|
pAttrib->dwLength = cbAttribValue;
|
|
|
|
Cleanup:
|
|
if (retCode != NO_ERROR && pAttrib != NULL)
|
|
FreeMemory((PVOID*)&(pAttrib->pValue));
|
|
|
|
return retCode;
|
|
}
|
|
|
|
|
|
DWORD GetXmlElementValue(
|
|
IXMLDOMDocument2 *pXmlDoc,
|
|
_In_ LPWSTR pElementName,
|
|
DWORD dwTypeOfDoc,
|
|
BSTR &pElementValue
|
|
)
|
|
{
|
|
DWORD retCode = ERROR_SUCCESS;
|
|
IXMLDOMNode *pDOMNode = NULL;
|
|
HRESULT hr = S_OK;
|
|
WCHAR *fullNodeName = NULL;
|
|
VARIANT var = {0};
|
|
|
|
var.vt = VT_EMPTY;
|
|
if(dwTypeOfDoc == CONNECTION_PROPERTIES)
|
|
var.bstrVal = SysAllocString(L"xmlns:SDKEapClientMethod='http://www.microsoft.com/provisioning/EapHostConfig'");
|
|
else
|
|
var.bstrVal = SysAllocString(L"xmlns:SDKEapClientMethod='http://www.microsoft.com/provisioning/EapHostUserCredentials'");
|
|
if (var.bstrVal == NULL)
|
|
{
|
|
retCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
var.vt = VT_BSTR;
|
|
|
|
hr = pXmlDoc->setProperty((BSTR)L"SelectionNamespaces", var);
|
|
if(FAILED(hr))
|
|
{
|
|
retCode = HRESULT_CODE(hr);
|
|
EapTrace("GetXmlElementValue --- setProperty returned error (for Element = %s) = %d \n",
|
|
pElementName,
|
|
retCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the size of ElementName.
|
|
//
|
|
WCHAR initialNodeName[] = L"//SDKEapClientMethod:";
|
|
DWORD initailNodeNameSize = sizeof(initialNodeName); // length includes null terminated character
|
|
|
|
size_t elementNameSize = 0;
|
|
hr = StringCbLengthW(pElementName, (size_t)(STRSAFE_MAX_CCH * sizeof(wchar_t)), &elementNameSize);
|
|
if (FAILED(hr))
|
|
{
|
|
retCode = HRESULT_CODE(hr);
|
|
EapTrace("CopyPCWSTR -- StringCbLengthW : retCode = %d", retCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
size_t fullNodeNameSize = elementNameSize + initailNodeNameSize;
|
|
|
|
retCode = AllocateMemory((DWORD)fullNodeNameSize, (PVOID*)&fullNodeName);
|
|
if (retCode != NO_ERROR)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
CopyMemory(fullNodeName, initialNodeName, initailNodeNameSize);
|
|
CopyMemory(((BYTE *)fullNodeName) + (initailNodeNameSize - sizeof(wchar_t)), pElementName, elementNameSize);
|
|
|
|
//
|
|
// Selecting the node we are interested in.
|
|
//
|
|
hr = pXmlDoc->selectSingleNode((BSTR)fullNodeName, &pDOMNode);
|
|
if(FAILED(hr))
|
|
{
|
|
retCode = HRESULT_CODE(hr);
|
|
EapTrace("GetXmlElementValue --- Invalid Node returned error (for Element = %s) = %d \n",
|
|
pElementName,
|
|
retCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( pDOMNode != NULL )
|
|
{
|
|
//
|
|
// Get the content of the node as BSTR.
|
|
//
|
|
hr = pDOMNode->get_text(&pElementValue);
|
|
if(FAILED(hr))
|
|
{
|
|
retCode = HRESULT_CODE(hr);
|
|
EapTrace("GetXmlElementValue --- get_text returned error (for Element = %s) = %d \n",
|
|
pElementName,
|
|
retCode);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
if(pDOMNode != NULL)
|
|
{
|
|
pDOMNode->Release();
|
|
pDOMNode = NULL;
|
|
}
|
|
if(fullNodeName != NULL)
|
|
FreeMemory((PVOID *)&fullNodeName);
|
|
if(var.bstrVal)
|
|
SysFreeString(var.bstrVal);
|
|
|
|
return retCode;
|
|
}
|
|
|
|
|
|
/**
|
|
* Allocate a new, independent copy of an input string.
|
|
*
|
|
*
|
|
* @param pInput [in] A string to copy.
|
|
*
|
|
*
|
|
* @return A pointer to the new string. If the input string is NULL, or the
|
|
* memory allocation fails, this returns NULL.
|
|
*/
|
|
|
|
LPWSTR NewString(IN const _In_ LPWSTR pInput)
|
|
{
|
|
DWORD result = NO_ERROR;
|
|
HRESULT hr = S_OK;
|
|
|
|
LPWSTR pOutput = NULL;
|
|
size_t cbOutput = 0;
|
|
|
|
// If NULL, just return.
|
|
if (! pInput)
|
|
goto Cleanup;
|
|
|
|
// Get the input string's length.
|
|
hr = StringCbLengthW(pInput, MAX_PATH, &cbOutput);
|
|
if ( FAILED(hr) )
|
|
{
|
|
result = HRESULT_CODE(hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Include room for the trailing NULL.
|
|
cbOutput += sizeof(WCHAR);
|
|
|
|
// Allocate a new string buffer for it.
|
|
result = AllocateMemory((DWORD)cbOutput, (PVOID*)&pOutput);
|
|
if (result != NO_ERROR)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Copy the string into it.
|
|
hr = StringCbCopyW(pOutput, cbOutput, pInput);
|
|
if ( FAILED(hr) )
|
|
{
|
|
result = HRESULT_CODE(hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
if (result != NO_ERROR)
|
|
{
|
|
EapTrace("NewString(): Hit error %d (hr = %#x)!",
|
|
result, hr);
|
|
|
|
FreeMemory((PVOID*)&pOutput);
|
|
pOutput = NULL;
|
|
}
|
|
|
|
return pOutput;
|
|
}
|
|
|
|
|
|
DWORD CopyCredentialInputArray(
|
|
OUT EAP_CONFIG_INPUT_FIELD_ARRAY *pDest,
|
|
IN const EAP_CONFIG_INPUT_FIELD_ARRAY *pSrc
|
|
) throw()
|
|
{
|
|
DWORD retval = NO_ERROR;
|
|
DWORD i = 0;
|
|
|
|
EAP_CONFIG_INPUT_FIELD_DATA *pWalkerSrc = NULL;
|
|
EAP_CONFIG_INPUT_FIELD_DATA *pWalkerDest = NULL;
|
|
|
|
|
|
// The input array pointer is optional; if NULL, ignore it and exit cleanly.
|
|
if (pSrc == NULL)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// The output array pointer is required; if NULL, exit with an error.
|
|
if (pDest == NULL)
|
|
{
|
|
retval = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
retval = AllocateMemory(pSrc->dwNumberOfFields
|
|
* sizeof(EAP_CONFIG_INPUT_FIELD_DATA),
|
|
(PVOID*)&(pDest->pFields));
|
|
if (retval != NO_ERROR)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
pDest->dwNumberOfFields = pSrc->dwNumberOfFields;
|
|
|
|
// Sanity check.
|
|
if (pSrc->dwNumberOfFields > 0 &&
|
|
pSrc->pFields == NULL)
|
|
{
|
|
retval = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
// Copy over the individual array elements.
|
|
pWalkerSrc = pSrc->pFields;
|
|
pWalkerDest = pDest->pFields;
|
|
|
|
for (i = 0; i < pDest->dwNumberOfFields; i++)
|
|
{
|
|
// First, copy members that require allocating new memory.
|
|
pWalkerDest->pwszLabel = NewString(pWalkerSrc->pwszLabel);
|
|
pWalkerDest->pwszData = NewString(pWalkerSrc->pwszData);
|
|
|
|
if (! pWalkerDest->pwszLabel ||
|
|
! pWalkerDest->pwszData )
|
|
{
|
|
retval = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Next, copy simple members.
|
|
pWalkerDest->dwSize = sizeof(EAP_CONFIG_INPUT_FIELD_DATA);
|
|
pWalkerDest->Type = pWalkerSrc->Type;
|
|
pWalkerDest->dwFlagProps = pWalkerSrc->dwFlagProps;
|
|
pWalkerDest->dwMinDataLength = pWalkerSrc->dwMinDataLength;
|
|
pWalkerDest->dwMaxDataLength = pWalkerSrc->dwMaxDataLength;
|
|
|
|
// Finally, move along to the next pair of elements.
|
|
pWalkerDest++;
|
|
pWalkerSrc++;
|
|
}
|
|
|
|
Cleanup:
|
|
if (retval != NO_ERROR &&
|
|
pDest != NULL)
|
|
{
|
|
FreeCredentialInputArray(pDest);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
void FreeCredentialInputArray(IN OUT EAP_CONFIG_INPUT_FIELD_ARRAY *pArray) throw()
|
|
{
|
|
DWORD i = 0;
|
|
DWORD numElements = 0;
|
|
|
|
EAP_CONFIG_INPUT_FIELD_DATA *pWalker = NULL;
|
|
|
|
if (! pArray)
|
|
goto Cleanup;
|
|
|
|
// Walk the array, freeing each element's memory allocations.
|
|
numElements = pArray->dwNumberOfFields;
|
|
|
|
for (i = 0, pWalker = pArray->pFields;
|
|
i < numElements;
|
|
i++, pWalker++)
|
|
{
|
|
FreeMemory((PVOID*)&(pWalker->pwszLabel));
|
|
FreeMemory((PVOID*)&(pWalker->pwszData));
|
|
}
|
|
|
|
// Last, free the array buffer itself.
|
|
FreeMemory((PVOID*)&(pArray->pFields));
|
|
|
|
Cleanup:
|
|
return;
|
|
}
|
|
|
|
|
|
DWORD ConstructUserBlobFromCredentialInputArray(
|
|
IN const EAP_METHOD_TYPE *pEapTypeIn,
|
|
IN const EAP_CONFIG_INPUT_FIELD_ARRAY *pEapConfigFieldsArray,
|
|
IN OUT DWORD *pdwUserBlobSize,
|
|
OUT PBYTE *ppbUserBlob
|
|
) throw()
|
|
{
|
|
DWORD retval = NO_ERROR;
|
|
HRESULT hr = S_OK;
|
|
LPWSTR pIdentity = NULL;
|
|
LPWSTR pPassword = NULL;
|
|
|
|
DWORD eapTypeId = 0;
|
|
|
|
USER_DATA_BLOB *pBlob = NULL;
|
|
|
|
|
|
if (! pEapConfigFieldsArray ||
|
|
! pdwUserBlobSize ||
|
|
! ppbUserBlob)
|
|
{
|
|
retval = ERROR_INVALID_PARAMETER;
|
|
EapTrace("ConstructUserBlobFromCredentialInputArray(): Error: One or more input/output pointers were NULL!");
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
// First, find the credential input data fields we care about.
|
|
|
|
pIdentity = GetCredentialInputValue(pEapConfigFieldsArray,
|
|
EapConfigInputUsername);
|
|
|
|
pPassword = GetCredentialInputValue(pEapConfigFieldsArray,
|
|
EapConfigInputPassword);
|
|
|
|
if (! pIdentity ||
|
|
! pPassword )
|
|
{
|
|
retval = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
// Next, create the output user blob buffer, and fill in the values.
|
|
|
|
retval = AllocateMemory(sizeof(USER_DATA_BLOB), (PVOID*)&pBlob);
|
|
|
|
if (retval != NO_ERROR)
|
|
goto Cleanup;
|
|
|
|
|
|
eapTypeId = EAPTYPE;
|
|
|
|
if (pEapTypeIn)
|
|
eapTypeId = pEapTypeIn->eapType.type;
|
|
|
|
pBlob->eapTypeId = eapTypeId;
|
|
|
|
|
|
hr = StringCbCopyW(pBlob->eapUserNamePassword.awszIdentity,
|
|
(UNLEN + 1),
|
|
pIdentity);
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
hr = StringCbCopyW(pBlob->eapUserNamePassword.awszPassword,
|
|
(PWLEN + 1),
|
|
pPassword);
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
|
|
// Finally, pass the blob back to the caller.
|
|
*ppbUserBlob = (PBYTE)pBlob;
|
|
*pdwUserBlobSize = sizeof(USER_DATA_BLOB);
|
|
|
|
Cleanup:
|
|
if (FAILED(hr))
|
|
retval = HRESULT_CODE(hr);
|
|
|
|
if (retval != NO_ERROR)
|
|
{
|
|
if (pBlob != NULL)
|
|
FreeMemory((PVOID*)&pBlob);
|
|
if (ppbUserBlob != NULL)
|
|
*ppbUserBlob = NULL;
|
|
if (pdwUserBlobSize != NULL)
|
|
*pdwUserBlobSize = 0;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
LPWSTR GetCredentialInputValue(
|
|
IN const EAP_CONFIG_INPUT_FIELD_ARRAY *pSrc,
|
|
IN const DWORD valueType,
|
|
IN const _In_opt_ LPWSTR pValueLabel
|
|
) throw()
|
|
{
|
|
DWORD result = ERROR_NOT_FOUND;
|
|
HRESULT hr = S_OK;
|
|
DWORD i = 0;
|
|
DWORD numElements = 0;
|
|
size_t sizeLabel = 0;
|
|
LPWSTR pData = NULL;
|
|
|
|
EAP_CONFIG_INPUT_FIELD_DATA *pWalker = 0;
|
|
|
|
|
|
if (pSrc == NULL)
|
|
{
|
|
result = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// The Label parameter is optional; if specified, get its length (used below).
|
|
if (pValueLabel != NULL)
|
|
{
|
|
hr = StringCbLengthW(pValueLabel, MAX_PATH, &sizeLabel);
|
|
if (FAILED(hr))
|
|
{
|
|
result = HRESULT_CODE(hr);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
// Walk the array, checking for mismatched data.
|
|
numElements = pSrc->dwNumberOfFields;
|
|
pWalker = pSrc->pFields;
|
|
|
|
for (i = 0, pWalker = pSrc->pFields;
|
|
i < numElements;
|
|
i++, pWalker++)
|
|
{
|
|
if (pWalker->Type != (int)valueType)
|
|
continue;
|
|
|
|
if (pValueLabel != NULL &&
|
|
memcmp(pWalker->pwszLabel, pValueLabel, sizeLabel) != 0)
|
|
continue;
|
|
|
|
// All filters passed; we've found it!
|
|
pData = pWalker->pwszData;
|
|
result = NO_ERROR;
|
|
break;
|
|
}
|
|
|
|
Cleanup:
|
|
return pData;
|
|
}
|