windows-nt/Source/XPSP1/NT/net/layer2svc/eapol/service/elport.c
2020-09-26 16:20:57 +08:00

2436 lines
75 KiB
C

/*++
Copyright (c) 1999, Microsoft Corporation
Module Name:
elport.c
Abstract:
This module deals with the port management for EAPOL, r/w to ports
Revision History:
sachins, Apr 28 2000, Created
--*/
#include "pcheapol.h"
#pragma hdrstop
#include "intfhdl.h"
BYTE g_bDefaultGroupMacAddr[]={0x01, 0x80, 0xc2, 0x00, 0x00, 0x03};
BYTE g_bEtherType8021X[SIZE_ETHERNET_TYPE]={0x88, 0x8E};
BYTE DEFAULT_8021X_VERSION=0x01;
//
// ElReadPerPortRegistryParams
//
// Description:
//
// Function called to read per port interface parameters from the registry
//
// Arguments:
// pwszDeviceGUID - GUID-string for the port
// pPCB - Pointer to PCB for the port
//
// Return values:
// NO_ERROR - success
// NON-zero - error
//
DWORD
ElReadPerPortRegistryParams (
IN WCHAR *pwszDeviceGUID,
IN EAPOL_PCB *pPCB
)
{
EAPOL_INTF_PARAMS EapolIntfParams;
EAPOL_POLICY_PARAMS EAPOLPolicyParams = {0};
DWORD dwSizeOfAuthData = 0;
BYTE *pbAuthData = NULL;
DWORD dwRetCode = NO_ERROR;
do
{
// Set the Auth Mode and the Supplicant mode for the context
pPCB->dwEAPOLAuthMode = g_dwEAPOLAuthMode;
pPCB->dwSupplicantMode = g_dwSupplicantMode;
// Read EAP type and default EAPOL state
ZeroMemory ((BYTE *)&EapolIntfParams, sizeof(EAPOL_INTF_PARAMS));
EapolIntfParams.dwVersion = EAPOL_CURRENT_VERSION;
EapolIntfParams.dwEapFlags = DEFAULT_EAP_STATE;
EapolIntfParams.dwEapType = DEFAULT_EAP_TYPE;
if (pPCB->pSSID != NULL)
{
memcpy (EapolIntfParams.bSSID, pPCB->pSSID->Ssid, pPCB->pSSID->SsidLength);
EapolIntfParams.dwSizeOfSSID = pPCB->pSSID->SsidLength;
}
if ((dwRetCode = ElGetInterfaceParams (
pwszDeviceGUID,
&EapolIntfParams
)) != NO_ERROR)
{
TRACE1 (PORT, "ElReadPerPortRegistryParams: ElGetInterfaceParams failed with error %ld",
dwRetCode);
if (dwRetCode == ERROR_FILE_NOT_FOUND)
{
dwRetCode = NO_ERROR;
}
else
{
break;
}
}
// Do version check here
// If registry blob has a version not equal to latest version,
// modify parameters to reflect default settings for current version
if ((EapolIntfParams.dwVersion != EAPOL_CURRENT_VERSION) &&
(EapolIntfParams.dwEapType == EAP_TYPE_TLS))
{
EapolIntfParams.dwVersion = EAPOL_CURRENT_VERSION;
EapolIntfParams.dwEapFlags |= DEFAULT_MACHINE_AUTH_STATE;
EapolIntfParams.dwEapFlags &= ~EAPOL_GUEST_AUTH_ENABLED;
if ((dwRetCode = ElSetInterfaceParams (
pwszDeviceGUID,
&EapolIntfParams
)) != NO_ERROR)
{
TRACE1 (PORT, "ElReadPerPortRegistryParams: ElSetInterfaceParams failed with error %ld, continuing",
dwRetCode);
dwRetCode = NO_ERROR;
}
}
if ((pPCB->PhysicalMediumType == NdisPhysicalMediumWirelessLan) &&
(EapolIntfParams.dwEapType == EAP_TYPE_MD5))
{
EapolIntfParams.dwEapType = EAP_TYPE_TLS;
if ((dwRetCode = ElSetInterfaceParams (
pwszDeviceGUID,
&EapolIntfParams
)) != NO_ERROR)
{
TRACE1 (PORT, "ElReadPerPortRegistryParams: ElSetInterfaceParams for TLS failed with error %ld, continuing",
dwRetCode);
dwRetCode = NO_ERROR;
}
}
pPCB->dwEapFlags = EapolIntfParams.dwEapFlags;
pPCB->dwEapTypeToBeUsed = EapolIntfParams.dwEapType;
//
// Query with zero-config and see if it is enabled on the interface
// or not. If zero-config is disabled on the interface, 802.1x should
// also be disabled
//
{
DWORD dwErr = 0;
INTF_ENTRY ZCIntfEntry = {0};
ZCIntfEntry.wszGuid = pwszDeviceGUID;
if ((dwErr = LstQueryInterface (
INTF_ENABLED,
&ZCIntfEntry,
NULL
)) == NO_ERROR)
{
if (!(ZCIntfEntry.dwCtlFlags & INTFCTL_ENABLED))
{
// TRACE0 (ANY, "LstQueryInterface returned Zero-configuration is disabled on network");
pPCB->dwEapFlags &= ~EAPOL_ENABLED;
}
else
{
// TRACE0 (ANY, "LstQueryInterface returned Zero-configuration is enabled on network");
}
}
else
{
if (dwErr != ERROR_FILE_NOT_FOUND)
{
TRACE1 (ANY, "LstQueryInterface failed with error (%ld)",
dwErr);
}
}
}
// Get the size of the EAP blob
if ((dwRetCode = ElGetCustomAuthData (
pwszDeviceGUID,
pPCB->dwEapTypeToBeUsed,
(pPCB->pSSID)?pPCB->pSSID->SsidLength:0,
(pPCB->pSSID)?pPCB->pSSID->Ssid:NULL,
NULL,
&dwSizeOfAuthData
)) != NO_ERROR)
{
if (dwRetCode == ERROR_BUFFER_TOO_SMALL)
{
if (dwSizeOfAuthData <= 0)
{
// No EAP blob stored in the registry
// Port can have NULL EAP blob
pbAuthData = NULL;
dwRetCode = NO_ERROR;
}
else
{
// Allocate memory to hold the blob
pbAuthData = MALLOC (dwSizeOfAuthData);
if (pbAuthData == NULL)
{
dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
TRACE0 (USER, "ElReadPerPortRegistryParams: Error in memory allocation for EAP blob");
break;
}
if ((dwRetCode = ElGetCustomAuthData (
pwszDeviceGUID,
pPCB->dwEapTypeToBeUsed,
(pPCB->pSSID)?pPCB->pSSID->SsidLength:0,
(pPCB->pSSID)?pPCB->pSSID->Ssid:NULL,
pbAuthData,
&dwSizeOfAuthData
)) != NO_ERROR)
{
TRACE1 (USER, "ElReadPerPortRegistryParams: ElGetCustomAuthData failed with %ld",
dwRetCode);
break;
}
}
if (pPCB->pCustomAuthConnData != NULL)
{
FREE (pPCB->pCustomAuthConnData);
pPCB->pCustomAuthConnData = NULL;
}
pPCB->pCustomAuthConnData = MALLOC (dwSizeOfAuthData + sizeof (DWORD));
if (pPCB->pCustomAuthConnData == NULL)
{
dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
TRACE0 (EAPOL, "ElReadPerPortRegistryParams: MALLOC failed for pCustomAuthConnData");
break;
}
pPCB->pCustomAuthConnData->dwSizeOfCustomAuthData = dwSizeOfAuthData;
if ((dwSizeOfAuthData != 0) && (pbAuthData != NULL))
{
memcpy ((BYTE *)pPCB->pCustomAuthConnData->pbCustomAuthData,
(BYTE *)pbAuthData, dwSizeOfAuthData);
}
}
else
{
TRACE1 (USER, "ElReadPerPortRegistryParams: ElGetCustomAuthData size estimation failed with error %ld",
dwRetCode);
break;
}
}
// Initialize Policy parameters not in EAPOL_INTF_PARAMS
if ((dwRetCode = ElGetPolicyInterfaceParams (
EapolIntfParams.dwSizeOfSSID,
EapolIntfParams.bSSID,
&EAPOLPolicyParams
)) == NO_ERROR)
{
pPCB->dwEAPOLAuthMode = EAPOLPolicyParams.dwEAPOLAuthMode;
pPCB->dwSupplicantMode = EAPOLPolicyParams.dwSupplicantMode;
pPCB->EapolConfig.dwheldPeriod = EAPOLPolicyParams.dwheldPeriod;
pPCB->EapolConfig.dwauthPeriod = EAPOLPolicyParams.dwauthPeriod;
pPCB->EapolConfig.dwstartPeriod = EAPOLPolicyParams.dwstartPeriod;
pPCB->EapolConfig.dwmaxStart = EAPOLPolicyParams.dwmaxStart;
}
else
{
if (dwRetCode != ERROR_FILE_NOT_FOUND)
{
TRACE1 (USER, "ElReadPerPortRegistryParams: ElGetPolicyInterfaceParams failed with error (%ld)",
dwRetCode);
}
dwRetCode = NO_ERROR;
}
// Determine maximum fail count possible before being parked into failure
// state (DISCONNECTED)
switch (pPCB->dwEAPOLAuthMode)
{
case EAPOL_AUTH_MODE_0:
case EAPOL_AUTH_MODE_1:
if (g_fUserLoggedOn)
{
// When user is logged in, only user and guest will be tried
pPCB->dwTotalMaxAuthFailCount = EAPOL_MAX_AUTH_FAIL_COUNT;
pPCB->dwTotalMaxAuthFailCount += ((IS_GUEST_AUTH_ENABLED(pPCB->dwEapFlags))?1:0)*EAPOL_MAX_AUTH_FAIL_COUNT;
}
else
{
// When user is logged out, only machine and guest will be tried
pPCB->dwTotalMaxAuthFailCount = ((IS_GUEST_AUTH_ENABLED(pPCB->dwEapFlags))?1:0)*EAPOL_MAX_AUTH_FAIL_COUNT;
pPCB->dwTotalMaxAuthFailCount += ((IS_MACHINE_AUTH_ENABLED(pPCB->dwEapFlags))?1:0)*EAPOL_MAX_AUTH_FAIL_COUNT;
}
break;
case EAPOL_AUTH_MODE_2:
// In Mode 2, only machine and guest will be tried
pPCB->dwTotalMaxAuthFailCount = ((IS_GUEST_AUTH_ENABLED(pPCB->dwEapFlags))?1:0)*EAPOL_MAX_AUTH_FAIL_COUNT;
pPCB->dwTotalMaxAuthFailCount += ((IS_MACHINE_AUTH_ENABLED(pPCB->dwEapFlags))?1:0)*EAPOL_MAX_AUTH_FAIL_COUNT;
break;
}
TRACE1 (PORT, "ElReadPerPortRegistryParams: dwTotalMaxAuthFailCount = (%ld)",
pPCB->dwTotalMaxAuthFailCount);
memcpy(pPCB->bEtherType, &g_bEtherType8021X[0], SIZE_ETHERNET_TYPE);
pPCB->bProtocolVersion = DEFAULT_8021X_VERSION;
}
while (FALSE);
if (pbAuthData != NULL)
{
FREE (pbAuthData);
}
return dwRetCode;
}
//
// ElHashPortToBucket
//
// Description:
//
// Function called to convert Device GUID into PCB hash table index.
//
// Arguments:
// pwszDeviceGUID - GUID-string for the port
//
// Return values:
// PCB hash table index from 0 to PORT_TABLE_BUCKETS-1
//
DWORD
ElHashPortToBucket (
IN WCHAR *pwszDeviceGUID
)
{
return ((DWORD)((_wtol(pwszDeviceGUID)) % PORT_TABLE_BUCKETS));
}
//
// ElRemovePCBFromTable
//
// Description:
//
// Function called to remove a PCB from the Hash Bucket table
// Delink it from the hash table, but do not free up the memory
//
// Arguments:
// pPCB - Pointer to PCB entry to be removed
//
// Return values:
//
VOID
ElRemovePCBFromTable (
IN EAPOL_PCB *pPCB
)
{
DWORD dwIndex;
EAPOL_PCB *pPCBWalker = NULL;
EAPOL_PCB *pPCBTemp = NULL;
if (pPCB == NULL)
{
TRACE0 (PORT, "ElRemovePCBFromTable: Deleting NULL PCB, returning");
return;
}
dwIndex = ElHashPortToBucket (pPCB->pwszDeviceGUID);
pPCBWalker = g_PCBTable.pPCBBuckets[dwIndex].pPorts;
pPCBTemp = pPCBWalker;
while (pPCBTemp != NULL)
{
if (wcsncmp (pPCBTemp->pwszDeviceGUID,
pPCB->pwszDeviceGUID, wcslen (pPCB->pwszDeviceGUID)) == 0)
{
// Entry is at head of list in table
if (pPCBTemp == g_PCBTable.pPCBBuckets[dwIndex].pPorts)
{
g_PCBTable.pPCBBuckets[dwIndex].pPorts = pPCBTemp->pNext;
}
else
{
// Entry in inside list in table
pPCBWalker->pNext = pPCBTemp->pNext;
}
break;
}
pPCBWalker = pPCBTemp;
pPCBTemp = pPCBWalker->pNext;
}
return;
}
//
// ElGetPCBPointerFromPortGUID
//
// Description:
//
// Function called to convert interface GUID to PCB pointer for the entry in
// the PCB hash table
//
// Arguments:
// pwszDeviceGUID - Identifier of the form GUID-String
//
// Return values:
//
PEAPOL_PCB
ElGetPCBPointerFromPortGUID (
IN WCHAR *pwszDeviceGUID
)
{
EAPOL_PCB *pPCBWalker = NULL;
DWORD dwIndex;
// TRACE1 (PORT, "ElGetPCBPointerFromPortGUID: GUID %ws", pwszDeviceGUID);
dwIndex = ElHashPortToBucket (pwszDeviceGUID);
// TRACE1 (PORT, "ElGetPCBPointerFromPortGUID: Index %d", dwIndex);
for (pPCBWalker = g_PCBTable.pPCBBuckets[dwIndex].pPorts;
pPCBWalker != NULL;
pPCBWalker = pPCBWalker->pNext
)
{
if (wcslen(pPCBWalker->pwszDeviceGUID) == wcslen(pwszDeviceGUID))
{
if (wcsncmp (pPCBWalker->pwszDeviceGUID, pwszDeviceGUID, wcslen (pwszDeviceGUID)) == 0)
{
return pPCBWalker;
}
}
}
return (NULL);
}
//
// ElInitializeEAPOL
//
// Description:
//
// Function to initialize EAPOL protocol module.
// Global EAPOL parameters are read from the registry.
// PCB hash table is initialized.
// EAP protocol is intialized.
//
// Arguments:
//
// Return values:
// NO_ERROR - success
// non-zero - error
//
DWORD
ElInitializeEAPOL (
)
{
DWORD dwIndex;
HANDLE hLocalTimerQueue = NULL;
DWORD dwRetCode = NO_ERROR;
do
{
// Initialize global config locks
if (dwRetCode = CREATE_READ_WRITE_LOCK(&(g_EAPOLConfig), "CFG") != NO_ERROR)
{
TRACE1(PORT, "ElInitializeEAPOL: Error %d creating g_EAPOLConfig read-write-lock", dwRetCode);
// LOG
break;
}
// Read parameters stored in registry
if ((dwRetCode = ElReadGlobalRegistryParams ()) != NO_ERROR)
{
TRACE1 (PORT, "ElInitializeEAPOL: ElReadGlobalRegistryParams failed with error = %ld",
dwRetCode);
dwRetCode = NO_ERROR;
// Don't exit, since default values will be used
}
// Initialize Hash Bucket Table
g_PCBTable.pPCBBuckets = (PCB_BUCKET *) MALLOC ( PORT_TABLE_BUCKETS * sizeof (PCB_BUCKET));
if (g_PCBTable.pPCBBuckets == NULL)
{
TRACE0 (PORT, "ElInitializeEAPOL: Error in allocating memory for PCB buckets");
dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
break;
}
g_PCBTable.dwNumPCBBuckets = PORT_TABLE_BUCKETS;
for (dwIndex=0; dwIndex < PORT_TABLE_BUCKETS; dwIndex++)
{
g_PCBTable.pPCBBuckets[dwIndex].pPorts=NULL;
}
// Initialize global locks
if (dwRetCode = CREATE_READ_WRITE_LOCK(&(g_PCBLock), "PCB") != NO_ERROR)
{
TRACE1(PORT, "ElInitializeEAPOL: Error %d creating g_PCBLock read-write-lock", dwRetCode);
// LOG
break;
}
// Create global timer queue for the various EAPOL state machines
if ((g_hTimerQueue = CreateTimerQueue()) == NULL)
{
dwRetCode = GetLastError();
TRACE1(PORT, "ElInitializeEAPOL: Error %d creating timer queue", dwRetCode);
break;
}
// Initialize EAP
if ((dwRetCode = ElEapInit(TRUE)) != NO_ERROR)
{
TRACE1 (PORT, "ElInitializeEAPOL: Error in ElEapInit= %ld",
dwRetCode);
break;
}
} while (FALSE);
if (dwRetCode != NO_ERROR)
{
if (g_PCBTable.pPCBBuckets != NULL)
{
FREE (g_PCBTable.pPCBBuckets);
g_PCBTable.pPCBBuckets = NULL;
}
if (READ_WRITE_LOCK_CREATED(&(g_PCBLock)))
{
DELETE_READ_WRITE_LOCK(&(g_PCBLock));
}
if (READ_WRITE_LOCK_CREATED(&(g_EAPOLConfig)))
{
DELETE_READ_WRITE_LOCK(&(g_EAPOLConfig));
}
if (g_hTimerQueue != NULL)
{
hLocalTimerQueue = g_hTimerQueue;
g_hTimerQueue = NULL;
if (!DeleteTimerQueueEx(
hLocalTimerQueue,
INVALID_HANDLE_VALUE
))
{
dwRetCode = GetLastError();
TRACE1 (PORT, "ElInitializeEAPOL: Error in DeleteTimerQueueEx = %d",
dwRetCode);
}
}
// DeInit EAP
ElEapInit(FALSE);
}
TRACE1 (PORT, "ElInitializeEAPOL: Completed, RetCode = %ld", dwRetCode);
return dwRetCode;
}
//
// ElCreatePort
//
// Description:
//
// Function to initialize Port Control Block for a port and start EAPOL
// on it. If the PCB already exists for the GUID, EAPOL state machine
// is restarted for that port.
//
// Arguments:
// hDevice - Handle to open NDISUIO driver on the interface
// pwszGUID - Pointer to GUID-String for the interface
// pwszFriendlyName - Friendly name of the interface
//
// Return values:
// NO_ERROR - success
// non-zero - error
//
DWORD
ElCreatePort (
IN HANDLE hDevice,
IN WCHAR *pwszGUID,
IN WCHAR *pwszFriendlyName,
IN DWORD dwZeroConfigId,
IN PRAW_DATA prdUserData
)
{
EAPOL_PCB *pNewPCB;
BOOL fPortToBeReStarted = FALSE;
BOOL fPCBCreated = FALSE;
DWORD dwIndex = 0;
DWORD dwSizeofMacAddr = 0;
DWORD dwSizeofSSID = 0;
DWORD ulOidDataLength = 0;
NIC_STATISTICS NicStatistics;
EAPOL_ZC_INTF *pZCData = NULL;
NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode = Ndis802_11InfrastructureMax;
DWORD dwSizeOfInfrastructureMode = 0;
DWORD dwRetCode = NO_ERROR;
do
{
TRACE5 (PORT, "ElCreatePort: Entered for Handle=(%p), GUID=(%ws), Name=(%ws), ZCId=(%ld), UserData=(%p)",
hDevice, pwszGUID, pwszFriendlyName, dwZeroConfigId, prdUserData);
// See if the port already exists
// If yes, initialize the state machine
// Else, create a new port
ACQUIRE_WRITE_LOCK (&g_PCBLock);
pNewPCB = ElGetPCBPointerFromPortGUID (pwszGUID);
if (pNewPCB != NULL)
{
// PCB found, restart EAPOL STATE machine
fPortToBeReStarted = TRUE;
}
else
{
// PCB not found, create new PCB and initialize it
TRACE1 (PORT, "ElCreatePort: No PCB found for %ws", pwszGUID);
// Allocate and initialize a new PCB
pNewPCB = (PEAPOL_PCB) MALLOC (sizeof(EAPOL_PCB));
if (pNewPCB == NULL)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE0(PORT, "ElCreatePort: Error in memory allocation using MALLOC");
dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
return dwRetCode;
}
}
// Get Media Statistics for the interface
ZeroMemory ((PVOID)&NicStatistics, sizeof(NIC_STATISTICS));
if ((dwRetCode = ElGetInterfaceNdisStatistics (
pwszGUID,
&NicStatistics
)) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE1(PORT, "ElCreatePort: ElGetInterfaceNdisStatistics failed with error %ld",
dwRetCode);
break;
}
if (fPortToBeReStarted)
{
if (NicStatistics.MediaState != MEDIA_STATE_CONNECTED)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
dwRetCode = ERROR_INVALID_STATE;
TRACE1(PORT, "ElCreatePort: Invalid media status for port to be restarted = (%ld)",
NicStatistics.MediaState);
break;
}
}
else
{
if ((NicStatistics.MediaState != MEDIA_STATE_CONNECTED) &&
(NicStatistics.MediaState != MEDIA_STATE_DISCONNECTED))
{
RELEASE_WRITE_LOCK (&g_PCBLock);
dwRetCode = ERROR_INVALID_STATE;
TRACE1(PORT, "ElCreatePort: Invalid media status for port = (%ld)",
NicStatistics.MediaState);
break;
}
}
pNewPCB->MediaState = NicStatistics.MediaState;
pNewPCB->PhysicalMediumType = NicStatistics.PhysicalMediaType;
if (fPortToBeReStarted)
{
// Only port state will be changed to CONNECTING
// No read requests will be cancelled
// Hence no new read request will be posted
TRACE1 (PORT, "ElCreatePort: PCB found for %ws", pwszGUID);
if ((dwRetCode = ElReStartPort (
pNewPCB,
dwZeroConfigId,
prdUserData))
!= NO_ERROR)
{
TRACE1 (PORT, "ElCreatePort: Error in ElReStartPort = %d",
dwRetCode);
}
RELEASE_WRITE_LOCK (&g_PCBLock);
break;
}
else
{
// New Port Control Block created
// PCB creation reference count
pNewPCB->dwRefCount = 1;
pNewPCB->hPort = hDevice;
// Mark the port as active
pNewPCB->dwFlags = EAPOL_PORT_FLAG_ACTIVE;
if (wcslen(pwszGUID) > (GUID_STRING_LEN_WITH_TERM-1))
{
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE0(PORT, "ElCreatePort: Invalid GUID for port");
break;
}
pNewPCB->pwszDeviceGUID =
(PWCHAR) MALLOC ((wcslen(pwszGUID) + 1)*sizeof(WCHAR));
if (pNewPCB->pwszDeviceGUID == NULL)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE0(PORT, "ElCreatePort: Error in memory allocation for GUID");
dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
break;
}
wcscpy (pNewPCB->pwszDeviceGUID, pwszGUID);
pNewPCB->pwszFriendlyName =
(PWCHAR) MALLOC ((wcslen(pwszFriendlyName) + 1)*sizeof(WCHAR));
if (pNewPCB->pwszFriendlyName == NULL)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE0(PORT, "ElCreatePort: Error in memory allocation for Friendly Name");
dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
break;
}
wcscpy (pNewPCB->pwszFriendlyName, pwszFriendlyName);
// Get the Local Current Mac address
dwSizeofMacAddr = SIZE_MAC_ADDR;
if (dwRetCode = ElNdisuioQueryOIDValue (
pNewPCB->hPort,
OID_802_3_CURRENT_ADDRESS,
pNewPCB->bSrcMacAddr,
&dwSizeofMacAddr
) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE1 (PORT, "ElCreatePort: ElNdisuioQueryOIDValue for OID_802_3_CURRENT_ADDRESS failed with error %ld",
dwRetCode);
break;
}
else
{
TRACE0 (PORT, "ElCreatePort: ElNdisuioQueryOIDValue for OID_802_3_CURRENT_ADDRESS successful");
EAPOL_DUMPBA (pNewPCB->bSrcMacAddr, dwSizeofMacAddr);
}
if (pNewPCB->PhysicalMediumType == NdisPhysicalMediumWirelessLan)
{
// Query the BSSID and SSID if media_connect
if (pNewPCB->MediaState == MEDIA_STATE_CONNECTED)
{
dwSizeOfInfrastructureMode = sizeof (InfrastructureMode);
// Get the infrastructure mode
// 802.1x cannot work on Adhoc networks
if (dwRetCode = ElNdisuioQueryOIDValue (
pNewPCB->hPort,
OID_802_11_INFRASTRUCTURE_MODE,
(BYTE *)&InfrastructureMode,
&dwSizeOfInfrastructureMode
) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE1 (PORT, "ElCreatePort: ElNdisuioQueryOIDValue for OID_802_11_INFRASTRUCTURE_MODE failed with error %ld",
dwRetCode);
break;
}
else
{
TRACE1 (PORT, "ElCreatePort: ElNdisuioQueryOIDValue for OID_802_11_INFRASTRUCTURE_MODE successful, Mode = (%ld)",
InfrastructureMode);
}
if (InfrastructureMode != Ndis802_11Infrastructure)
{
dwRetCode = ERROR_NOT_SUPPORTED;
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE0 (PORT, "ElCreatePort: 802.1x cannot work on non-infrastructure networks");
break;
}
// Get the Remote MAC address if possible
dwSizeofMacAddr = SIZE_MAC_ADDR;
if (dwRetCode = ElNdisuioQueryOIDValue (
pNewPCB->hPort,
OID_802_11_BSSID,
pNewPCB->bDestMacAddr,
&dwSizeofMacAddr
) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE1 (PORT, "ElCreatePort: ElNdisuioQueryOIDValue for OID_802_11_BSSID failed with error %ld",
dwRetCode);
break;
}
else
{
TRACE0 (PORT, "ElCreatePort: ElNdisuioQueryOIDValue for OID_802_11_BSSID successful");
EAPOL_DUMPBA (pNewPCB->bDestMacAddr, dwSizeofMacAddr);
}
if ((pNewPCB->pSSID = MALLOC (NDIS_802_11_SSID_LEN)) == NULL)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
TRACE0 (PORT, "ElCreatePort: MALLOC failed for pSSID");
break;
}
dwSizeofSSID = NDIS_802_11_SSID_LEN;
if (dwRetCode = ElNdisuioQueryOIDValue (
pNewPCB->hPort,
OID_802_11_SSID,
(BYTE *)pNewPCB->pSSID,
&dwSizeofSSID
) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE1 (PORT, "ElCreatePort: ElNdisuioQueryOIDValue for OID_802_11_SSID failed with error %ld",
dwRetCode);
break;
}
else
{
if (pNewPCB->pSSID->SsidLength > MAX_SSID_LEN)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
dwRetCode = ERROR_INVALID_PARAMETER;
TRACE0 (PORT, "ElCreatePort: ElNdisuioQueryOIDValue OID_802_11_SSID returned invalid SSID");
break;
}
TRACE0 (PORT, "ElCreatePort: ElNdisuioQueryOIDValue for OID_802_11_SSID successful");
EAPOL_DUMPBA (pNewPCB->pSSID->Ssid, pNewPCB->pSSID->SsidLength);
}
}
}
else
{
// Wired Lan
// Copy default destination Mac address value
memcpy(pNewPCB->bDestMacAddr, &g_bDefaultGroupMacAddr[0], SIZE_MAC_ADDR);
// If destination MacAddress is going to be multicast
// inform the driver to accept the packets to this address
if ((dwRetCode = ElNdisuioSetOIDValue (
pNewPCB->hPort,
OID_802_3_MULTICAST_LIST,
(BYTE *)&g_bDefaultGroupMacAddr[0],
SIZE_MAC_ADDR))
!= NO_ERROR)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE1 (PORT, "ElCreatePort: ElNdisuioSetOIDValue for OID_802_3_MULTICAST_LIST failed with error %ld",
dwRetCode);
break;
}
else
{
TRACE0 (PORT, "ElCreatePort: ElNdisuioSetOIDValue for OID_802_3_MULTICAST_LIST successful");
}
}
// Identity related initialization
pNewPCB->PreviousAuthenticationType = EAPOL_UNAUTHENTICATED_ACCESS;
pNewPCB->fGotUserIdentity = FALSE;
if (prdUserData != NULL)
{
if ((prdUserData->dwDataLen >= sizeof (EAPOL_ZC_INTF))
&& (prdUserData->pData != NULL))
{
// Extract information stored with Zero-Config
pZCData = (PEAPOL_ZC_INTF) prdUserData->pData;
pNewPCB->dwAuthFailCount = pZCData->dwAuthFailCount;
pNewPCB->PreviousAuthenticationType =
pZCData->PreviousAuthenticationType;
TRACE2 (PORT, "ElCreatePort: prdUserData: Authfailcount = %ld, PreviousAuthType = %ld",
pZCData->dwAuthFailCount, pZCData->PreviousAuthenticationType);
}
else
{
// Reset for zeroed out prdUserData
pNewPCB->dwAuthFailCount = 0;
TRACE0 (PORT, "ElCreatePort: prdUserData not valid");
}
}
pNewPCB->dwTotalMaxAuthFailCount = EAPOL_TOTAL_MAX_AUTH_FAIL_COUNT;
pNewPCB->dwZeroConfigId = dwZeroConfigId;
// Not yet received 802.1X packet from remote end
pNewPCB->fIsRemoteEndEAPOLAware = FALSE;
// EAPOL state machine variables
pNewPCB->State = EAPOLSTATE_LOGOFF;
// Create timer with very high due time and infinite period
// Timer will be deleted when the port is deleted
CREATE_TIMER (&(pNewPCB->hTimer),
ElTimeoutCallbackRoutine,
(PVOID)pNewPCB,
INFINITE_SECONDS,
"PCB",
&dwRetCode);
if (dwRetCode != NO_ERROR)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE1 (PORT, "ElCreatePort: Error in CREATE_TIMER %ld", dwRetCode);
break;
}
// EAPOL_Start s that have been sent out
pNewPCB->ulStartCount = 0;
// Last received Id from the remote end
pNewPCB->dwPreviousId = 256;
ACQUIRE_WRITE_LOCK (&g_EAPOLConfig);
pNewPCB->EapolConfig.dwheldPeriod = g_dwheldPeriod;
pNewPCB->EapolConfig.dwauthPeriod = g_dwauthPeriod;
pNewPCB->EapolConfig.dwstartPeriod = g_dwstartPeriod;
pNewPCB->EapolConfig.dwmaxStart = g_dwmaxStart;
RELEASE_WRITE_LOCK (&g_EAPOLConfig);
// Initialize read-write lock
if (dwRetCode = CREATE_READ_WRITE_LOCK(&(pNewPCB->rwLock), "EPL")
!= NO_ERROR)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE1(PORT, "ElCreatePort: Error %d creating read-write-lock",
dwRetCode);
// LOG
break;
}
// Initialize registry connection auth data for this port
// If connection data is not present for EAP-TLS and SSID="Default"
// create the blob
if ((dwRetCode = ElInitRegPortData (
pNewPCB->pwszDeviceGUID
)) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE1 (PORT, "ElCreatePort: Error in ElInitRegPortData = %d",
dwRetCode);
break;
}
// Initialize per port information from registry
if ((dwRetCode = ElReadPerPortRegistryParams(pwszGUID, pNewPCB)) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&g_PCBLock);
TRACE1(PORT, "ElCreatePort: ElReadPerPortRegistryParams failed with error %ld",
dwRetCode);
break;
}
switch (pNewPCB->dwSupplicantMode)
{
case SUPPLICANT_MODE_0:
case SUPPLICANT_MODE_1:
case SUPPLICANT_MODE_2:
pNewPCB->fEAPOLTransmissionFlag = FALSE;
break;
case SUPPLICANT_MODE_3:
pNewPCB->fEAPOLTransmissionFlag = TRUE;
break;
}
// Unicast mode, can talk with peer without broadcast messages
if (pNewPCB->PhysicalMediumType == NdisPhysicalMediumWirelessLan)
{
pNewPCB->fEAPOLTransmissionFlag = TRUE;
}
if ((!IS_EAPOL_ENABLED(pNewPCB->dwEapFlags)) ||
(pNewPCB->dwSupplicantMode == SUPPLICANT_MODE_0))
{
TRACE0 (PORT, "ElCreatePort: Marking port as disabled");
pNewPCB->dwFlags &= ~EAPOL_PORT_FLAG_ACTIVE;
pNewPCB->dwFlags |= EAPOL_PORT_FLAG_DISABLED;
}
// Add one more for local access
pNewPCB->dwRefCount += 1;
// Insert NewPCB into PCB hash table
dwIndex = ElHashPortToBucket (pwszGUID);
pNewPCB->pNext = g_PCBTable.pPCBBuckets[dwIndex].pPorts;
g_PCBTable.pPCBBuckets[dwIndex].pPorts = pNewPCB;
pNewPCB->dwPortIndex = dwIndex;
fPCBCreated = TRUE;
RELEASE_WRITE_LOCK (&g_PCBLock);
ACQUIRE_WRITE_LOCK (&(pNewPCB->rwLock));
//
// Post a read request on the port
//
// Initiate read operation on the port, since it is now active
if (dwRetCode = ElReadFromPort (
pNewPCB,
NULL,
0
)
!= NO_ERROR)
{
RELEASE_WRITE_LOCK (&(pNewPCB->rwLock));
TRACE1 (PORT, "ElCreatePort: Error in ElReadFromPort = %d",
dwRetCode);
break;
}
//
// Kick off EAPOL state machine
//
if ((pNewPCB->MediaState == MEDIA_STATE_CONNECTED) &&
EAPOL_PORT_ACTIVE(pNewPCB))
{
// Set port to EAPOLSTATE_CONNECTING State
// Send out EAPOL_Start Packets to detect if it is a secure
// or non-secure LAN based on response received from remote end
if ((dwRetCode = FSMConnecting (pNewPCB, NULL)) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&(pNewPCB->rwLock));
TRACE1 (PORT, "ElCreatePort: FSMConnecting failed with error %ld",
dwRetCode);
break;
}
}
else
{
// Set port to EAPOLSTATE_DISCONNECTED State
if ((dwRetCode = FSMDisconnected (pNewPCB, NULL)) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&(pNewPCB->rwLock));
TRACE1 (PORT, "ElCreatePort: FSMDisconnected failed with error %ld",
dwRetCode);
break;
}
}
RELEASE_WRITE_LOCK (&(pNewPCB->rwLock));
TRACE2 (PORT, "ElCreatePort: Completed for GUID= %ws, Name = %ws",
pNewPCB->pwszDeviceGUID, pNewPCB->pwszFriendlyName);
}
}
while (FALSE);
// Remove the local access reference
if (fPCBCreated)
{
EAPOL_DEREFERENCE_PORT(pNewPCB);
}
if (dwRetCode != NO_ERROR)
{
// If PCB was not being restarted
if (!fPortToBeReStarted)
{
// If PCB was created
if (fPCBCreated)
{
HANDLE hTempDevice;
// Mark the Port as deleted. Cleanup if possible
// Don't worry about return code
ElDeletePort (
pNewPCB->pwszDeviceGUID,
&hDevice
);
}
else
{
// Remove all partial traces of port creation
if (pNewPCB->hTimer != NULL)
{
if (InterlockedCompareExchangePointer (
&g_hTimerQueue,
NULL,
NULL
))
{
DWORD dwTmpRetCode = NO_ERROR;
TRACE2 (PORT, "ElCreatePort: DeleteTimer (%p), queue (%p)",
pNewPCB->hTimer, g_hTimerQueue);
DELETE_TIMER (pNewPCB->hTimer, INVALID_HANDLE_VALUE,
&dwTmpRetCode);
if (dwTmpRetCode != NO_ERROR)
{
TRACE1 (PORT, "ElCreatePort: DeleteTimer failed with error %ld",
dwTmpRetCode);
}
}
}
if (READ_WRITE_LOCK_CREATED(&(pNewPCB->rwLock)))
{
DELETE_READ_WRITE_LOCK(&(pNewPCB->rwLock));
}
if (pNewPCB->pwszDeviceGUID != NULL)
{
FREE(pNewPCB->pwszDeviceGUID);
pNewPCB->pwszDeviceGUID = NULL;
}
if (pNewPCB->pwszFriendlyName != NULL)
{
FREE(pNewPCB->pwszFriendlyName);
pNewPCB->pwszFriendlyName = NULL;
}
if (pNewPCB != NULL)
{
ZeroMemory ((PVOID)pNewPCB, sizeof (EAPOL_PCB));
FREE (pNewPCB);
pNewPCB = NULL;
}
}
}
}
return dwRetCode;
}
//
// ElDeletePort
//
// Description:
//
// Function to stop EAPOL and delete PCB for a port.
// Returns back pointer to handle opened on the interface so that
// the handle can be closed by the interface management module.
//
// Input arguments:
// pwszDeviceGUID - GUID-String of the interface whose PCB needs to be
// deleted
// pHandle - Output: Handle to NDISUIO driver for this port
//
// Return values:
// NO_ERROR - success
// non-zero - error
//
DWORD
ElDeletePort (
IN WCHAR *pwszDeviceGUID,
OUT HANDLE *pHandle
)
{
EAPOL_PCB *pPCB = NULL;
HANDLE hTimer = NULL;
DWORD dwRetCode = NO_ERROR;
ACQUIRE_WRITE_LOCK (&(g_PCBLock));
// Verify if PCB exists for this GUID
TRACE1 (PORT, "ElDeletePort entered for GUID %ws", pwszDeviceGUID);
pPCB = ElGetPCBPointerFromPortGUID (pwszDeviceGUID);
if (pPCB == NULL)
{
RELEASE_WRITE_LOCK (&(g_PCBLock));
TRACE1 (PORT, "ElDeletePort: PCB not found entered for Port %s",
pwszDeviceGUID);
return ERROR_NO_SUCH_INTERFACE;
}
ACQUIRE_WRITE_LOCK (&(pPCB->rwLock));
// Make sure it isn't already deleted
if (EAPOL_PORT_DELETED(pPCB))
{
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
RELEASE_WRITE_LOCK (&(g_PCBLock));
TRACE1 (PORT, "ElDeletePort: PCB already marked deleted for Port %ws",
pwszDeviceGUID);
return ERROR_NO_SUCH_INTERFACE;
}
InterlockedIncrement (&g_lPCBContextsAlive);
// Retain handle to NDISUIO device
*pHandle = pPCB->hPort;
// Mark the PCB as deleted and remove it from the hash bucket
pPCB->dwFlags = EAPOL_PORT_FLAG_DELETED;
ElRemovePCBFromTable(pPCB);
// Shutdown EAP
// Will always return NO_ERROR, so no check on return value
ElEapEnd (pPCB);
// Delete timer since PCB is not longer to be used
hTimer = pPCB->hTimer;
TRACE1 (PORT, "ElDeletePort: RefCount for port = %ld", pPCB->dwRefCount);
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
if (InterlockedCompareExchangePointer (
&g_hTimerQueue,
NULL,
NULL
))
{
TRACE2 (PORT, "ElDeletePort: DeleteTimer (%p), queue (%p)",
hTimer, g_hTimerQueue);
DELETE_TIMER (hTimer, INVALID_HANDLE_VALUE, &dwRetCode);
if (dwRetCode != NO_ERROR)
{
TRACE1 (PORT, "ElDeletePort: DeleteTimer failed with error %ld",
dwRetCode);
}
}
// If reference count is zero, perform final cleanup
EAPOL_DEREFERENCE_PORT (pPCB);
RELEASE_WRITE_LOCK (&(g_PCBLock));
return NO_ERROR;
}
//
// ElCleanupPort
//
// Description:
//
// Function called when the very last reference to a PCB
// is released. The PCB memory is released and zeroed out
//
// Arguments:
// pPCB - Pointer to port control block to be destroyed
//
//
VOID
ElCleanupPort (
IN PEAPOL_PCB pPCB
)
{
DWORD dwRetCode = NO_ERROR;
TRACE1 (PORT, "ElCleanupPort entered for %ws", pPCB->pwszDeviceGUID);
if (pPCB->pwszDeviceGUID != NULL)
{
FREE (pPCB->pwszDeviceGUID);
}
if (pPCB->pwszFriendlyName)
{
FREE (pPCB->pwszFriendlyName);
}
if (pPCB->pwszEapReplyMessage != NULL)
{
FREE (pPCB->pwszEapReplyMessage);
}
if (pPCB->pwszSSID != NULL)
{
FREE (pPCB->pwszSSID);
}
if (pPCB->pSSID != NULL)
{
FREE (pPCB->pSSID);
}
if (pPCB->EapUIData.pEapUIData != NULL)
{
FREE (pPCB->EapUIData.pEapUIData);
}
if (pPCB->MasterSecretSend.cbData != 0)
{
FREE (pPCB->MasterSecretSend.pbData);
pPCB->MasterSecretSend.cbData = 0;
pPCB->MasterSecretSend.pbData = NULL;
}
if (pPCB->MasterSecretRecv.cbData != 0)
{
FREE (pPCB->MasterSecretRecv.pbData);
pPCB->MasterSecretRecv.cbData = 0;
pPCB->MasterSecretRecv.pbData = NULL;
}
if (pPCB->MPPESendKey.cbData != 0)
{
FREE (pPCB->MPPESendKey.pbData);
pPCB->MPPESendKey.cbData = 0;
pPCB->MPPESendKey.pbData = NULL;
}
if (pPCB->MPPERecvKey.cbData != 0)
{
FREE (pPCB->MPPERecvKey.pbData);
pPCB->MPPERecvKey.cbData = 0;
pPCB->MPPERecvKey.pbData = NULL;
}
if (pPCB->hUserToken != NULL)
{
if (!CloseHandle (pPCB->hUserToken))
{
dwRetCode = GetLastError ();
TRACE1 (PORT, "ElCleanupPort: CloseHandle failed with error %ld",
dwRetCode);
dwRetCode = NO_ERROR;
}
}
pPCB->hUserToken = NULL;
if (pPCB->pszIdentity != NULL)
{
FREE (pPCB->pszIdentity);
}
if (pPCB->PasswordBlob.pbData != NULL)
{
FREE (pPCB->PasswordBlob.pbData);
}
if (pPCB->pCustomAuthUserData != NULL)
{
FREE (pPCB->pCustomAuthUserData);
}
if (pPCB->pCustomAuthConnData != NULL)
{
FREE (pPCB->pCustomAuthConnData);
}
if (pPCB->pbPreviousEAPOLPkt != NULL)
{
FREE (pPCB->pbPreviousEAPOLPkt);
}
if (READ_WRITE_LOCK_CREATED(&(pPCB->rwLock)))
{
DELETE_READ_WRITE_LOCK(&(pPCB->rwLock));
}
ZeroMemory ((PVOID)pPCB, sizeof(EAPOL_PCB));
FREE (pPCB);
pPCB = NULL;
InterlockedDecrement (&g_lPCBContextsAlive);
TRACE0 (PORT, "ElCleanupPort completed");
return;
}
//
// ElReStartPort
//
// Description:
//
// Function called to reset the EAPOL state machine to Connecting state
// This may be called due to:
// 1. From ElCreatePort, for an existing PCB
// 2. Configuration parameters may have changed. Initialization
// is required to allow new values to take effect.
// Initialization will take the EAPOL state to CONNECTING
//
// Arguments:
// pPCB - Pointer to port control block to be initialized
//
// Return values:
// NO_ERROR - success
// non-zero - error
//
DWORD
ElReStartPort (
IN EAPOL_PCB *pPCB,
IN DWORD dwZeroConfigId,
IN PRAW_DATA prdUserData
)
{
DWORD dwSizeofSSID = 0;
DWORD dwSizeofMacAddr = 0;
DWORD dwCurrenTickCount = 0;
EAPOL_ZC_INTF *pZCData = NULL;
NIC_STATISTICS NicStatistics;
NDIS_802_11_SSID PreviousSSID;
BOOLEAN fResetCredentials = TRUE;
BYTE bTmpDestMacAddr[SIZE_MAC_ADDR];
NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode = Ndis802_11InfrastructureMax;
DWORD dwSizeOfInfrastructureMode = 0;
DWORD dwRetCode = NO_ERROR;
TRACE1 (PORT, "ElReStartPort: Entered: Refcnt = %ld",
pPCB->dwRefCount);
do
{
ACQUIRE_WRITE_LOCK (&pPCB->rwLock);
if (EAPOL_PORT_DELETED(pPCB))
{
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
TRACE1 (PORT, "ElReStartPort: PCB already marked deleted for Port %ws",
pPCB->pwszDeviceGUID);
break;
}
pPCB->dwFlags = EAPOL_PORT_FLAG_ACTIVE;
// Set current authentication mode, based on administrative setting
pPCB->PreviousAuthenticationType = EAPOL_UNAUTHENTICATED_ACCESS;
if (prdUserData != NULL)
{
if ((prdUserData->dwDataLen >= sizeof (EAPOL_ZC_INTF))
&& (prdUserData->pData != NULL))
{
// Extract information stored with Zero-Config
pZCData = (PEAPOL_ZC_INTF) prdUserData->pData;
pPCB->dwAuthFailCount = pZCData->dwAuthFailCount;
pPCB->PreviousAuthenticationType =
pZCData->PreviousAuthenticationType;
TRACE2 (PORT, "ElReStartPort: prdUserData: Authfailcount = %ld, PreviousAuthType = %ld",
pZCData->dwAuthFailCount, pZCData->PreviousAuthenticationType);
}
else
{
// Reset for zeroed out prdUserData
pPCB->dwAuthFailCount = 0;
TRACE0 (PORT, "ElReStartPort: prdUserData not valid");
}
}
pPCB->EapUIState = 0;
pPCB->dwTotalMaxAuthFailCount = EAPOL_TOTAL_MAX_AUTH_FAIL_COUNT;
pPCB->dwZeroConfigId = dwZeroConfigId;
pPCB->ulStartCount = 0;
pPCB->dwPreviousId = 256;
pPCB->dwLogoffSent = 0;
pPCB->ullLastReplayCounter = 0;
pPCB->fAuthenticationOnNewNetwork = FALSE;
// Clean out CustomAuthData since EAP type may have changed
// During authentication, CustomAuthData for the connection will be
// picked up again
if (pPCB->pCustomAuthConnData != NULL)
{
FREE (pPCB->pCustomAuthConnData);
pPCB->pCustomAuthConnData = NULL;
}
// Parameters initialization
memcpy(pPCB->bEtherType, &g_bEtherType8021X[0], SIZE_ETHERNET_TYPE);
pPCB->bProtocolVersion = DEFAULT_8021X_VERSION;
// Not yet received 802.1X packet from remote end
pPCB->fIsRemoteEndEAPOLAware = FALSE;
// Set EAPOL timeout values
ACQUIRE_WRITE_LOCK (&g_EAPOLConfig);
pPCB->EapolConfig.dwheldPeriod = g_dwheldPeriod;
pPCB->EapolConfig.dwauthPeriod = g_dwauthPeriod;
pPCB->EapolConfig.dwstartPeriod = g_dwstartPeriod;
pPCB->EapolConfig.dwmaxStart = g_dwmaxStart;
RELEASE_WRITE_LOCK (&g_EAPOLConfig);
ZeroMemory ((PVOID)&NicStatistics, sizeof(NIC_STATISTICS));
if ((dwRetCode = ElGetInterfaceNdisStatistics (
pPCB->pwszDeviceGUID,
&NicStatistics
)) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&pPCB->rwLock);
TRACE1(PORT, "ElReStartPort: ElGetInterfaceNdisStatistics failed with error %ld",
dwRetCode);
break;
}
pPCB->MediaState = NicStatistics.MediaState;
ZeroMemory ((BYTE *)&PreviousSSID, sizeof(NDIS_802_11_SSID));
if (pPCB->pSSID != NULL)
{
memcpy ((BYTE *)&PreviousSSID, (BYTE *)pPCB->pSSID,
sizeof(NDIS_802_11_SSID));
}
// Get the Remote Mac address if possible, since we may have roamed
if (pPCB->PhysicalMediumType == NdisPhysicalMediumWirelessLan)
{
// Since authentication is to be restarted, flag that transmit
// key was not received
pPCB->fTransmitKeyReceived = FALSE;
dwSizeOfInfrastructureMode = sizeof (InfrastructureMode);
// Get the infrastructure mode
// 802.1x cannot work on Adhoc networks
if (dwRetCode = ElNdisuioQueryOIDValue (
pPCB->hPort,
OID_802_11_INFRASTRUCTURE_MODE,
(BYTE *)&InfrastructureMode,
&dwSizeOfInfrastructureMode
) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&pPCB->rwLock);
TRACE1 (PORT, "ElReStartPort: ElNdisuioQueryOIDValue for OID_802_11_INFRASTRUCTURE_MODE failed with error %ld",
dwRetCode);
break;
}
else
{
TRACE1 (PORT, "ElReStartPort: ElNdisuioQueryOIDValue for OID_802_11_INFRASTRUCTURE_MODE successful, Mode = (%ld)",
InfrastructureMode);
}
if (InfrastructureMode != Ndis802_11Infrastructure)
{
dwRetCode = ERROR_NOT_SUPPORTED;
RELEASE_WRITE_LOCK (&pPCB->rwLock);
TRACE0 (PORT, "ElReStartPort: 802.1x cannot work on non-infrastructure networks");
break;
}
ZeroMemory (bTmpDestMacAddr, SIZE_MAC_ADDR);
dwSizeofMacAddr = SIZE_MAC_ADDR;
if (dwRetCode = ElNdisuioQueryOIDValue (
pPCB->hPort,
OID_802_11_BSSID,
bTmpDestMacAddr,
&dwSizeofMacAddr
) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&pPCB->rwLock);
TRACE1 (PORT, "ElReStartPort: ElNdisuioQueryOIDValue for OID_802_11_BSSID failed with error %ld",
dwRetCode);
break;
}
else
{
TRACE0 (PORT, "ElReStartPort: ElNdisuioQueryOIDValue for OID_802_11_BSSID successful");
EAPOL_DUMPBA (bTmpDestMacAddr, dwSizeofMacAddr);
}
memcpy (pPCB->bDestMacAddr, bTmpDestMacAddr, SIZE_MAC_ADDR);
// Query the SSID if media_connect
if (pPCB->pSSID != NULL)
{
FREE (pPCB->pSSID);
pPCB->pSSID = NULL;
}
if ((pPCB->pSSID = MALLOC (NDIS_802_11_SSID_LEN)) == NULL)
{
RELEASE_WRITE_LOCK (&pPCB->rwLock);
dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
TRACE0 (PORT, "ElReStartPort: MALLOC failed for pSSID");
break;
}
dwSizeofSSID = NDIS_802_11_SSID_LEN;
if (dwRetCode = ElNdisuioQueryOIDValue (
pPCB->hPort,
OID_802_11_SSID,
(BYTE *)pPCB->pSSID,
&dwSizeofSSID
) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&pPCB->rwLock);
TRACE1 (PORT, "ElReStartPort: ElNdisuioQueryOIDValue for OID_802_11_SSID failed with error %ld",
dwRetCode);
break;
}
else
{
if (pPCB->pSSID->SsidLength > MAX_SSID_LEN)
{
RELEASE_WRITE_LOCK (&pPCB->rwLock);
dwRetCode = ERROR_INVALID_PARAMETER;
TRACE0 (PORT, "ElReStartPort: ElNdisuioQueryOIDValue OID_802_11_SSID returned invalid SSID");
break;
}
TRACE0 (PORT, "ElReStartPort: ElNdisuioQueryOIDValue for OID_802_11_SSID successful");
EAPOL_DUMPBA (pPCB->pSSID->Ssid, pPCB->pSSID->SsidLength);
}
}
// Retain credentials if on same network
if (pPCB->pSSID != NULL)
{
if (!memcmp ((BYTE *)pPCB->pSSID, (BYTE *)&PreviousSSID,
sizeof(NDIS_802_11_SSID)))
{
fResetCredentials = FALSE;
}
}
if (fResetCredentials)
{
pPCB->fGotUserIdentity = FALSE;
if (pPCB->PasswordBlob.pbData != NULL)
{
FREE (pPCB->PasswordBlob.pbData);
pPCB->PasswordBlob.pbData = NULL;
pPCB->PasswordBlob.cbData = 0;
}
if (pPCB->hUserToken != NULL)
{
if (!CloseHandle (pPCB->hUserToken))
{
dwRetCode = GetLastError ();
TRACE1 (PORT, "ElReStartPort: CloseHandle failed with error %ld",
dwRetCode);
dwRetCode = NO_ERROR;
}
}
pPCB->hUserToken = NULL;
}
else
{
// If this is the same SSID refresh the Master Secret with the
// last copy of MPPE Keys. If re-keying has stomped on keys, this
// will ensure that with the new AP with IAPP, the keys will
// be same on the supplicant too
if ((dwRetCode = ElReloadMasterSecrets (pPCB)) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&pPCB->rwLock);
TRACE1 (PORT, "ElReStartPort: ElReloadMasterSecret failed with error %ld",
dwRetCode);
break;
}
}
// Initialize per port information from registry
if ((dwRetCode = ElReadPerPortRegistryParams(pPCB->pwszDeviceGUID,
pPCB)) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&pPCB->rwLock);
TRACE1(PORT, "ElReStartPort: ElReadPerPortRegistryParams failed with error %ld",
dwRetCode);
break;
}
// Set correct supplicant mode
switch (pPCB->dwSupplicantMode)
{
case SUPPLICANT_MODE_0:
case SUPPLICANT_MODE_1:
case SUPPLICANT_MODE_2:
pPCB->fEAPOLTransmissionFlag = FALSE;
break;
case SUPPLICANT_MODE_3:
pPCB->fEAPOLTransmissionFlag = TRUE;
break;
}
// Unicast mode, can talk with peer without broadcast messages
if (pPCB->PhysicalMediumType == NdisPhysicalMediumWirelessLan)
{
pPCB->fEAPOLTransmissionFlag = TRUE;
}
if ((!IS_EAPOL_ENABLED(pPCB->dwEapFlags)) ||
(pPCB->dwSupplicantMode == SUPPLICANT_MODE_0))
{
TRACE0 (PORT, "ElReStartPort: Marking port as disabled");
pPCB->dwFlags &= ~EAPOL_PORT_FLAG_ACTIVE;
pPCB->dwFlags |= EAPOL_PORT_FLAG_DISABLED;
}
if ((pPCB->MediaState == MEDIA_STATE_CONNECTED) &&
EAPOL_PORT_ACTIVE(pPCB))
{
// Set port to EAPOLSTATE_CONNECTING State
// Send out EAPOL_Start Packets to detect if it is a secure
// or non-secure LAN based on response received from remote end
if ((dwRetCode = FSMConnecting (pPCB, NULL)) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
TRACE1 (PORT, "ElReStartPort: FSMConnecting failed with error %ld",
dwRetCode);
break;
}
}
else
{
// Set port to EAPOLSTATE_DISCONNECTED State
if ((dwRetCode = FSMDisconnected (pPCB, NULL)) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
TRACE1 (PORT, "ElReStartPort: FSMDisconnected failed with error %ld",
dwRetCode);
break;
}
}
RELEASE_WRITE_LOCK (&pPCB->rwLock);
} while (FALSE);
return dwRetCode;
}
//
// ElEAPOLDeInit
//
// Description:
//
// Function called to shutdown EAPOL module
// Shutdown EAP.
// Cleanup all used memory
//
// Arguments:
//
// Return values:
// NO_ERROR - success
// non-zero - error
//
//
DWORD
ElEAPOLDeInit (
)
{
EAPOL_PCB *pPCBWalker = NULL;
EAPOL_PCB *pPCB = NULL;
DWORD dwIndex = 0;
HANDLE hLocalTimerQueue = NULL;
HANDLE hNULL = NULL;
HANDLE hTimer = NULL;
DWORD dwRetCode = NO_ERROR;
TRACE0 (PORT, "ElEAPOLDeInit entered");
do
{
// Walk the hash table
// Mark PCBs as deleted. Free PCBs which we can
ACQUIRE_WRITE_LOCK (&(g_PCBLock));
for (dwIndex = 0; dwIndex < PORT_TABLE_BUCKETS; dwIndex++)
{
pPCBWalker = g_PCBTable.pPCBBuckets[dwIndex].pPorts;
while (pPCBWalker != NULL)
{
pPCB = pPCBWalker;
pPCBWalker = pPCB->pNext;
ACQUIRE_WRITE_LOCK (&(pPCB->rwLock));
// Send out Logoff Packet so that no one else can
// ride on the connection
// If the mode does not allow EAPOL_Logoff packet to sent
// out, there is not much that can be done to break the
// connection
FSMLogoff (pPCB, NULL);
// Mark the PCB as deleted and remove it from the hash bucket
pPCB->dwFlags = EAPOL_PORT_FLAG_DELETED;
ElRemovePCBFromTable(pPCB);
// Shutdown EAP
ElEapEnd (pPCB);
hTimer = pPCB->hTimer;
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
if (InterlockedCompareExchangePointer (
&g_hTimerQueue,
NULL,
NULL
))
{
TRACE2 (PORT, "ElEAPOLDeInit: DeleteTimer (%p), queue (%p)",
hTimer, g_hTimerQueue);
DELETE_TIMER (hTimer, INVALID_HANDLE_VALUE, &dwRetCode);
if (dwRetCode != NO_ERROR)
{
TRACE1 (PORT, "ElEAPOLDeInit: DeleteTimer 1 failed with error %ld",
dwRetCode);
}
}
ACQUIRE_WRITE_LOCK (&(pPCB->rwLock));
// Close the handle to the NDISUIO driver
if ((dwRetCode = ElCloseInterfaceHandle (
pPCB->hPort,
pPCB->pwszDeviceGUID))
!= NO_ERROR)
{
TRACE1 (DEVICE,
"ElEAPOLDeInit: Error in ElCloseInterfaceHandle %d",
dwRetCode);
}
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
InterlockedIncrement (&g_lPCBContextsAlive);
EAPOL_DEREFERENCE_PORT (pPCB);
}
}
RELEASE_WRITE_LOCK (&(g_PCBLock));
do
{
TRACE1 (PORT, "ElEAPOLDeInit: Waiting for %ld PCB contexts to terminate ...",
g_lPCBContextsAlive);
Sleep (1000);
}
while (g_lPCBContextsAlive != 0);
// Delete EAPOL config lock
if (READ_WRITE_LOCK_CREATED(&(g_EAPOLConfig)))
{
DELETE_READ_WRITE_LOCK(&(g_EAPOLConfig));
}
// Delete global PCB table lock
if (READ_WRITE_LOCK_CREATED(&(g_PCBLock)))
{
DELETE_READ_WRITE_LOCK(&(g_PCBLock));
}
if (g_PCBTable.pPCBBuckets != NULL)
{
FREE (g_PCBTable.pPCBBuckets);
g_PCBTable.pPCBBuckets = NULL;
}
// Delete global timer queue
if (g_hTimerQueue != NULL)
{
hLocalTimerQueue = InterlockedExchangePointer (
&g_hTimerQueue,
hNULL
);
if (!DeleteTimerQueueEx(
hLocalTimerQueue,
INVALID_HANDLE_VALUE // Wait for ALL timer callbacks to complete
))
{
dwRetCode = GetLastError();
TRACE1 (PORT, "ElEAPOLDeInit: Error in DeleteTimerQueueEx = %d",
dwRetCode);
}
}
// Un-initialize EAP
if ((dwRetCode = ElEapInit(FALSE)) != NO_ERROR)
{
TRACE1 (PORT, "ElEAPOLDeInit: Error in ElEapInit(FALSE) = %ld",
dwRetCode);
break;
}
} while (FALSE);
TRACE1 (PORT, "ElEAPOLDeInit completed, RetCode = %d", dwRetCode);
return dwRetCode;
}
//
// Currently Unsupported
// Read EAPOL statistics for the port
//
VOID
ElReadPortStatistics (
IN WCHAR *pwszDeviceGUID,
OUT PEAPOL_STATS pEapolStats
)
{
}
//
// Currently Unsupported
// Read EAPOL Port Configuration for the mentioned port
//
VOID
ElReadPortConfiguration (
IN WCHAR *pwszDeviceGUID,
OUT PEAPOL_CONFIG pEapolConfig
)
{
}
//
// Currently Unsupported
// Set EAPOL Port Configuration for the mentioned port
//
DWORD
ElSetPortConfiguration (
IN WCHAR *pwszDeviceGUID,
IN PEAPOL_CONFIG pEapolConfig
)
{
DWORD dwRetCode = NO_ERROR;
return dwRetCode;
}
//
// ElReadCompletionRoutine
//
// Description:
//
// This routine is invoked upon completion of an OVERLAPPED read operation
// on an interface on which EAPOL is running
//
// The message read is validated and processed, and if necessary,
// a reply is generated and sent out
//
// Arguments:
// dwError - Win32 status code for the I/O operation
//
// dwBytesTransferred - number of bytes in 'pEapolBuffer'
//
// pEapolBuffer - holds data read from the datagram socket
//
// Notes:
// A reference to the component will have been made on our behalf
// by ElReadPort(). Hence g_PCBLock, will not be required
// to be taken since current PCB existence is guaranteed
//
VOID
CALLBACK
ElReadCompletionRoutine (
DWORD dwError,
DWORD dwBytesReceived,
EAPOL_BUFFER *pEapolBuffer
)
{
EAPOL_PCB *pPCB;
DWORD dwRetCode;
pPCB = (EAPOL_PCB *)pEapolBuffer->pvContext;
TRACE1 (PORT, "ElReadCompletionRoutine entered, %ld bytes recvd",
dwBytesReceived);
do
{
if (dwError)
{
// Error in read request
TRACE2 (PORT, "ElReadCompletionRoutine: Error %d on port %ws",
dwError, pPCB->pwszDeviceGUID);
// Having a ref count from the read posted, guarantees existence
// of PCB. Hence no need to acquire g_PCBLock
ACQUIRE_WRITE_LOCK (&(pPCB->rwLock));
if (EAPOL_PORT_DELETED(pPCB))
{
TRACE1 (PORT, "ElReadCompletionRoutine: Port %ws not active",
pPCB->pwszDeviceGUID);
// Port is not active, release Context buffer
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
FREE (pEapolBuffer);
}
else
{
TRACE1 (PORT, "ElReadCompletionRoutine: Reposting buffer on port %ws",
pPCB->pwszDeviceGUID);
// Repost buffer for another read operation
// Free the current buffer, ElReadFromPort creates a new
// buffer
FREE(pEapolBuffer);
if ((dwRetCode = ElReadFromPort (
pPCB,
NULL,
0
)) != NO_ERROR)
{
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
TRACE1 (PORT, "ElReadCompletionRoutine: ElReadFromPort 1 error %d",
dwRetCode);
break;
}
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
}
break;
}
// Successful read completion
ACQUIRE_WRITE_LOCK (&(pPCB->rwLock));
if (EAPOL_PORT_DELETED(pPCB))
{
// Port is not active
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
FREE (pEapolBuffer);
TRACE1 (PORT, "ElReadCompletionRoutine: Port %ws is inactive",
pPCB->pwszDeviceGUID);
break;
}
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
// Queue a work item to the Thread Pool to execute in the
// I/O component. Callbacks from BindIoCompletionCallback do not
// guarantee to be running in I/O component. So, on a non I/O
// component thread may die while requests are pending.
// (Refer to Jeffrey Richter, pg 416, Programming Applications for
// Microsoft Windows, Fourth Edition
// pEapolBuffer will be the context for the function
// since it stores all relevant information for processing
// i.e. pBuffer, dwBytesTransferred, pContext => pPCB
InterlockedIncrement (&g_lWorkerThreads);
if (!QueueUserWorkItem (
(LPTHREAD_START_ROUTINE)ElProcessReceivedPacket,
(PVOID)pEapolBuffer,
WT_EXECUTELONGFUNCTION
))
{
InterlockedDecrement (&g_lWorkerThreads);
FREE (pEapolBuffer);
dwRetCode = GetLastError();
TRACE1 (PORT, "ElReadCompletionRoutine: Critical error: QueueUserWorkItem failed with error %ld",
dwRetCode);
break;
}
else
{
//TRACE1 (PORT, "ElReadCompletionRoutine: QueueUserWorkItem work item queued for port %p",
//pPCB);
// The received packet has still not been processed.
// The ref count cannot be decrement, yet
return;
}
} while (FALSE);
TRACE2 (PORT, "ElReadCompletionRoutine: pPCB= %p, RefCnt = %ld",
pPCB, pPCB->dwRefCount);
// Decrement refcount for error cases
EAPOL_DEREFERENCE_PORT(pPCB);
}
//
// ElWriteCompletionRoutine
//
// Description:
//
// This routine is invoked upon completion of an OVERLAPPED write operation
// on an interface on which EAPOL is running.
//
//
// Arguments:
// dwError - Win32 status code for the I/O operation
//
// dwBytesTransferred - number of bytes sent out
//
// pEapolBuffer - buffer sent to the WriteFile command
//
// Notes:
// The reference count for the write operation is removed.
//
VOID
CALLBACK
ElWriteCompletionRoutine (
DWORD dwError,
DWORD dwBytesSent,
EAPOL_BUFFER *pEapolBuffer
)
{
PEAPOL_PCB pPCB = (PEAPOL_PCB)pEapolBuffer->pvContext;
TRACE2 (DEVICE, "ElWriteCompletionRoutine sent out %d bytes with error %d",
dwBytesSent, dwError);
// No need to acquire locks, since PCB existence is guaranteed
// by reference made when write was posted
EAPOL_DEREFERENCE_PORT(pPCB);
TRACE2 (PORT, "ElWriteCompletionRoutine: pPCB= %p, RefCnt = %ld",
pPCB, pPCB->dwRefCount);
FREE(pEapolBuffer);
return;
// Free Read/Write buffer area, if it is dynamically allocated
// We have static Read-write buffer for now
}
//
// ElIoCompletionRoutine
//
// Description:
//
// Callback function defined to BindIoCompletionCallback
// This routine is invoked by the I/O system upon completion of a read/write
// operation
// This routine in turn calls ElReadCompletionRoutine or
// ElWriteCompletionRoutine depending on what command invoked the
// I/O operation i.e. ReadFile or WriteFile
//
// Input arguments:
// dwError - system-supplied error code
// dwBytesTransferred - system-supplied byte-count
// lpOverlapped - called-supplied context area
//
// Return values:
//
VOID
CALLBACK
ElIoCompletionRoutine (
DWORD dwError,
DWORD dwBytesTransferred,
LPOVERLAPPED lpOverlapped
)
{
PEAPOL_BUFFER pBuffer = CONTAINING_RECORD (lpOverlapped, EAPOL_BUFFER, Overlapped);
TRACE1 (DEVICE, "ElIoCompletionRoutine called, %ld bytes xferred",
dwBytesTransferred);
pBuffer->dwErrorCode = dwError;
pBuffer->dwBytesTransferred = dwBytesTransferred;
pBuffer->CompletionRoutine (
pBuffer->dwErrorCode,
pBuffer->dwBytesTransferred,
pBuffer
);
return;
}
//
// ElReadFromPort
//
// Description:
//
// Function to read EAPOL packets from a port
//
// Arguments:
// pPCB - Pointer to PCB for port on which read is to be performed
// pBuffer - unused
// dwBufferLength - unused
//
// Return values:
//
// Locks:
// pPCB->rw_Lock should be acquired before calling this function
//
DWORD
ElReadFromPort (
IN PEAPOL_PCB pPCB,
IN PCHAR pBuffer,
IN DWORD dwBufferLength
)
{
PEAPOL_BUFFER pEapolBuffer;
DWORD dwRetCode = NO_ERROR;
TRACE0 (PORT, "ElReadFromPort entered");
// Allocate Context buffer
if ((pEapolBuffer = (PEAPOL_BUFFER) MALLOC (sizeof(EAPOL_BUFFER))) == NULL)
{
TRACE0 (PORT, "ElReadFromPort: Error in memory allocation");
return ERROR_NOT_ENOUGH_MEMORY;
}
// Initialize Context data used in Overlapped operations
pEapolBuffer->pvContext = (PVOID)pPCB;
pEapolBuffer->CompletionRoutine = ElReadCompletionRoutine;
// Make a reference to the port
// this reference is released in the completion routine
if (!EAPOL_REFERENCE_PORT(pPCB))
{
//RELEASE_WRITE_LOCK (&(g_PCBLock));
TRACE0 (PORT, "ElReadFromPort: Unable to obtain reference to port");
FREE (pEapolBuffer);
return ERROR_CAN_NOT_COMPLETE;
}
TRACE2 (DEVICE, "ElReadFromPort: pPCB = %p, RefCnt = %ld",
pPCB, pPCB->dwRefCount);
// Read from the NDISUIO interface corresponding to this port
if ((dwRetCode = ElReadFromInterface(
pPCB->hPort,
pEapolBuffer,
MAX_PACKET_SIZE - SIZE_ETHERNET_CRC
// read the maximum data possible
)) != NO_ERROR)
{
TRACE1 (DEVICE, "ElReadFromPort: Error in ElReadFromInterface = %d",
dwRetCode);
FREE(pEapolBuffer);
// Decrement refcount just incremented, since it will not be
// decremented in ReadCompletionRoutine which is not called
EAPOL_DEREFERENCE_PORT(pPCB);
TRACE2 (PORT, "ElReadFromPort: pPCB= %p, RefCnt = %ld",
pPCB, pPCB->dwRefCount);
}
return dwRetCode;
}
//
// ElWriteToPort
//
// Description:
//
// Function to write EAPOL packets to a port
//
// Input arguments:
// pPCB - Pointer to PCB for port on which write is to be performed
// pBuffer - Pointer to data to be sent out
// dwBufferLength - Number of bytes to be sent out
//
// Return values:
//
// Locks:
// pPCB->rw_Lock should be acquired before calling this function
//
DWORD
ElWriteToPort (
IN PEAPOL_PCB pPCB,
IN PCHAR pBuffer,
IN DWORD dwBufferLength
)
{
PEAPOL_BUFFER pEapolBuffer;
PETH_HEADER pEthHeader;
DWORD dwTotalBytes = 0;
DWORD dwRetCode = NO_ERROR;
TRACE1 (PORT, "ElWriteToPort entered: Pkt Length = %ld", dwBufferLength);
if ((pEapolBuffer = (PEAPOL_BUFFER) MALLOC (sizeof(EAPOL_BUFFER))) == NULL)
{
TRACE0 (PORT, "ElWriteToPort: Error in memory allocation");
return ERROR_NOT_ENOUGH_MEMORY;
}
// Initialize Context data used in Overlapped operations
pEapolBuffer->pvContext = (PVOID)pPCB;
pEapolBuffer->CompletionRoutine = ElWriteCompletionRoutine;
pEthHeader = (PETH_HEADER)pEapolBuffer->pBuffer;
// Copy the source MAC address and the destination MAC address
memcpy ((PBYTE)pEthHeader->bDstAddr,
(PBYTE)pPCB->bDestMacAddr,
SIZE_MAC_ADDR);
memcpy ((PBYTE)pEthHeader->bSrcAddr,
(PBYTE)pPCB->bSrcMacAddr,
SIZE_MAC_ADDR);
// Validate packet length
if ((dwBufferLength + sizeof(ETH_HEADER)) >
(MAX_PACKET_SIZE - SIZE_ETHERNET_CRC))
{
TRACE2 (PORT, "ElWriteToPort: Packetsize %d greater than maximum allowed",
dwBufferLength,
(MAX_PACKET_SIZE - SIZE_ETHERNET_CRC - sizeof(ETH_HEADER)));
FREE (pEapolBuffer);
return ERROR_BAD_LENGTH;
}
// Copy the EAPOL packet and body
if (pBuffer != NULL)
{
memcpy ((PBYTE)((PBYTE)pEapolBuffer->pBuffer+sizeof(ETH_HEADER)),
(PBYTE)pBuffer,
dwBufferLength);
}
dwTotalBytes = dwBufferLength + sizeof(ETH_HEADER);
ElParsePacket (pEapolBuffer->pBuffer, dwTotalBytes,
FALSE);
// Buffer will be released by calling function
// Write to the NDISUIO interface corresponding to this port
// Make a reference to the port
// this reference is released in the completion routine
if (!EAPOL_REFERENCE_PORT(pPCB))
{
TRACE0 (PORT, "ElWriteToPort: Unable to obtain reference to port");
FREE (pEapolBuffer);
return ERROR_CAN_NOT_COMPLETE;
}
else
{
TRACE2 (DEVICE, "ElWriteToPort: pPCB = %p, RefCnt = %ld",
pPCB, pPCB->dwRefCount);
if ((dwRetCode = ElWriteToInterface (
pPCB->hPort,
pEapolBuffer,
dwTotalBytes
)) != NO_ERROR)
{
FREE (pEapolBuffer);
TRACE1 (PORT, "ElWriteToPort: Error %d", dwRetCode);
// Decrement refcount incremented in this function,
// since it will not be decremented in WriteCompletionRoutine
// as it will never be called.
EAPOL_DEREFERENCE_PORT(pPCB);
TRACE2 (PORT, "ElWriteToPort: pPCB= %p, RefCnt = %ld",
pPCB, pPCB->dwRefCount);
return dwRetCode;
}
}
//TRACE1 (PORT, "ElWriteToPort completed, dwRetCode =%d", dwRetCode);
return dwRetCode;
}