2436 lines
75 KiB
C
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;
|
|
|
|
}
|