1769 lines
52 KiB
C
1769 lines
52 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
|
|
|
|
BYTE DEFAULT_DEST_MAC_ADDR[]={0x01, 0x80, 0xc2, 0x00, 0x00, 0x0f};
|
|
BYTE ETHERNET_TYPE_8021X[]={0x81, 0x80};
|
|
BYTE DEFAULT_8021X_VERSION=0x01;
|
|
|
|
|
|
//
|
|
// ElReadPerPortRegistryParams
|
|
//
|
|
// Description:
|
|
//
|
|
// Function called to read per port interface parameters from the registry
|
|
//
|
|
// Arguments:
|
|
// pszDeviceGUID - GUID-string for the port
|
|
// pNewPCB - Pointer to PCB for the port
|
|
//
|
|
// Return values:
|
|
// NO_ERROR - success
|
|
// NON-zero - error
|
|
//
|
|
|
|
DWORD
|
|
ElReadPerPortRegistryParams (
|
|
IN CHAR *pszDeviceGUID,
|
|
IN EAPOL_PCB *pNewPCB
|
|
)
|
|
{
|
|
CHAR czDummyBuffer[256];
|
|
DWORD dwSizeofRemoteMacAddr = 0;
|
|
DWORD dwRetCode = NO_ERROR;
|
|
|
|
// Read EAP type and default EAPOL state
|
|
|
|
if ((dwRetCode = ElGetInterfaceParams (
|
|
pszDeviceGUID,
|
|
&pNewPCB->dwEapTypeToBeUsed,
|
|
czDummyBuffer,
|
|
&pNewPCB->dwEapolEnabled
|
|
)) != NO_ERROR)
|
|
{
|
|
TRACE1 (PORT, "ElReadPerPortRegistryParams: ElGetInterfaceParams failed with error %ld",
|
|
dwRetCode);
|
|
|
|
pNewPCB->dwEapTypeToBeUsed = DEFAULT_EAP_TYPE;
|
|
pNewPCB->dwEapolEnabled = DEFAULT_EAPOL_STATE;
|
|
|
|
if (pNewPCB->dwEapolEnabled)
|
|
{
|
|
dwRetCode = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
return dwRetCode;
|
|
}
|
|
}
|
|
|
|
|
|
memcpy(pNewPCB->bEtherType, ÐERNET_TYPE_8021X[0], SIZE_ETHERNET_TYPE);
|
|
|
|
pNewPCB->bProtocolVersion = DEFAULT_8021X_VERSION;
|
|
|
|
return dwRetCode;
|
|
}
|
|
|
|
|
|
//
|
|
// ElHashPortToBucket
|
|
//
|
|
// Description:
|
|
//
|
|
// Function called to convert Device GUID into PCB hash table index.
|
|
//
|
|
// Arguments:
|
|
// pszDeviceGUID - GUID-string for the port
|
|
//
|
|
// Return values:
|
|
// PCB hash table index from 0 to PORT_TABLE_BUCKETS-1
|
|
//
|
|
|
|
DWORD
|
|
ElHashPortToBucket (
|
|
IN CHAR *pszDeviceGUID
|
|
)
|
|
{
|
|
return ((DWORD)((atol(pszDeviceGUID)) % 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->pszDeviceGUID);
|
|
pPCBWalker = g_PCBTable.pPCBBuckets[dwIndex].pPorts;
|
|
pPCBTemp = pPCBWalker;
|
|
|
|
while (pPCBTemp != NULL)
|
|
{
|
|
if (strncmp (pPCBTemp->pszDeviceGUID,
|
|
pPCB->pszDeviceGUID, strlen(pPCB->pszDeviceGUID)) == 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:
|
|
// pszDeviceGUID - Identifier of the form GUID-String
|
|
//
|
|
// Return values:
|
|
//
|
|
|
|
PEAPOL_PCB
|
|
ElGetPCBPointerFromPortGUID (
|
|
IN CHAR *pszDeviceGUID
|
|
)
|
|
{
|
|
EAPOL_PCB *pPCBWalker = NULL;
|
|
DWORD dwIndex;
|
|
|
|
TRACE1 (PORT, "ElGetPCBPointerFromPortGUID: GUID %s", pszDeviceGUID);
|
|
|
|
dwIndex = ElHashPortToBucket (pszDeviceGUID);
|
|
|
|
TRACE1 (PORT, "ElGetPCBPointerFromPortGUID: Index %d", dwIndex);
|
|
|
|
for (pPCBWalker = g_PCBTable.pPCBBuckets[dwIndex].pPorts;
|
|
pPCBWalker != NULL;
|
|
pPCBWalker = pPCBWalker->pNext
|
|
)
|
|
{
|
|
if (strncmp (pPCBWalker->pszDeviceGUID, pszDeviceGUID, strlen(pszDeviceGUID)) == 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;
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (!DeleteTimerQueueEx(
|
|
g_hTimerQueue,
|
|
INVALID_HANDLE_VALUE
|
|
))
|
|
{
|
|
dwRetCode = GetLastError();
|
|
|
|
// Pending on timer deletion is asked above
|
|
if (dwRetCode != ERROR_IO_PENDING)
|
|
{
|
|
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
|
|
// pszGUID - Pointer to GUID-String for the interface
|
|
// pszFriendlyName - Friendly name of the interface
|
|
// psSrcMacAddr - Mac Address of the interface
|
|
//
|
|
// Return values:
|
|
// NO_ERROR - success
|
|
// non-zero - error
|
|
//
|
|
|
|
DWORD
|
|
ElCreatePort (
|
|
IN HANDLE hDevice,
|
|
IN CHAR *pszGUID,
|
|
IN CHAR *pszFriendlyName,
|
|
IN BYTE *psSrcMacAddr
|
|
)
|
|
{
|
|
EAPOL_PCB *pNewPCB;
|
|
BOOL fPortToBeReStarted = FALSE;
|
|
BOOL fPCBCreated = FALSE;
|
|
DWORD dwIndex = 0;
|
|
DWORD dwSizeofRemoteMacAddr = 0;
|
|
DWORD ulOidDataLength = 0;
|
|
NIC_STATISTICS NicStatistics;
|
|
DWORD dwRetCode = NO_ERROR;
|
|
|
|
do
|
|
{
|
|
TRACE2 (PORT, "ElCreatePort: Entered for Handle=%p, GUID=%s",
|
|
hDevice, pszGUID);
|
|
|
|
// See if the port already exists
|
|
// If yes, initialize the state machine
|
|
// Else, create a new port
|
|
|
|
ACQUIRE_WRITE_LOCK (&g_PCBLock);
|
|
|
|
pNewPCB = ElGetPCBPointerFromPortGUID (pszGUID);
|
|
|
|
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 %s", pszGUID);
|
|
|
|
// 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 (
|
|
pszGUID,
|
|
&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)",
|
|
dwRetCode);
|
|
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;
|
|
|
|
|
|
// Initialize per port information from registry
|
|
if ((dwRetCode = ElReadPerPortRegistryParams(pszGUID, pNewPCB)) != NO_ERROR)
|
|
{
|
|
RELEASE_WRITE_LOCK (&g_PCBLock);
|
|
TRACE1(PORT, "ElCreatePort: ElReadPerPortRegistryParams failed with error %ld",
|
|
dwRetCode);
|
|
break;
|
|
}
|
|
|
|
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 %s", pszGUID);
|
|
|
|
if ((dwRetCode = ElReStartPort (pNewPCB)) != NO_ERROR)
|
|
{
|
|
TRACE1 (PORT, "ElCreatePort: Error in ElReStartPort = %d",
|
|
dwRetCode);
|
|
|
|
}
|
|
RELEASE_WRITE_LOCK (&g_PCBLock);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// New Port Control Block created
|
|
|
|
// Port management variables
|
|
pNewPCB->dwRefCount = 1; // Creation reference count
|
|
pNewPCB->hPort = hDevice;
|
|
|
|
// Mark the port as active
|
|
pNewPCB->dwFlags = EAPOL_PORT_FLAG_ACTIVE;
|
|
|
|
pNewPCB->pszDeviceGUID = (PCHAR) MALLOC (strlen(pszGUID) + 1);
|
|
if (pNewPCB->pszDeviceGUID == NULL)
|
|
{
|
|
RELEASE_WRITE_LOCK (&g_PCBLock);
|
|
TRACE0(PORT, "ElCreatePort: Error in memory allocation for GUID");
|
|
dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
memcpy (pNewPCB->pszDeviceGUID, pszGUID, strlen(pszGUID));
|
|
pNewPCB->pszDeviceGUID[strlen(pszGUID)] = '\0';
|
|
|
|
pNewPCB->pszFriendlyName = (PCHAR) MALLOC (strlen(pszFriendlyName) + 1);
|
|
if (pNewPCB->pszFriendlyName == NULL)
|
|
{
|
|
RELEASE_WRITE_LOCK (&g_PCBLock);
|
|
TRACE0(PORT, "ElCreatePort: Error in memory allocation for Friendly Name");
|
|
dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
memcpy (pNewPCB->pszFriendlyName,
|
|
pszFriendlyName, strlen(pszFriendlyName));
|
|
pNewPCB->pszFriendlyName[strlen(pszFriendlyName)] = '\0';
|
|
|
|
memcpy(pNewPCB->bSrcMacAddr, psSrcMacAddr, SIZE_MAC_ADDR);
|
|
|
|
// Get the Remote Mac address if possible
|
|
dwSizeofRemoteMacAddr = SIZE_MAC_ADDR;
|
|
|
|
if (dwRetCode = ElNdisuioQueryOIDValue (
|
|
pNewPCB->hPort,
|
|
OID_802_11_BSSID,
|
|
pNewPCB->bDestMacAddr,
|
|
&dwSizeofRemoteMacAddr
|
|
) != NO_ERROR)
|
|
{
|
|
TRACE1 (PORT, "ElCreatePort: ElNdisuioQueryOIDValue for OID_802_11_BSSID failed with error %ld",
|
|
dwRetCode);
|
|
|
|
// Copy default destination Mac address value
|
|
memcpy(pNewPCB->bDestMacAddr, &DEFAULT_DEST_MAC_ADDR[0], SIZE_MAC_ADDR);
|
|
dwRetCode = NO_ERROR;
|
|
|
|
// If destination MacAddress is going to be multicast
|
|
// inform the driver to accept the packets from this address
|
|
|
|
if ((dwRetCode = ElNdisuioSetOIDValue (
|
|
pNewPCB->hPort,
|
|
OID_802_3_MULTICAST_LIST,
|
|
(BYTE *)&DEFAULT_DEST_MAC_ADDR[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");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE0 (PORT, "ElCreatePort: ElNdisuioQueryOIDValue for OID_802_11_BSSID successful");
|
|
EAPOL_DUMPBA (pNewPCB->bDestMacAddr, dwSizeofRemoteMacAddr);
|
|
}
|
|
|
|
|
|
|
|
pNewPCB->fGotUserIdentity = FALSE;
|
|
|
|
// Assume port is on a network with authentication enabled
|
|
pNewPCB->fIsRemoteEndEAPOLAware = TRUE;
|
|
|
|
// 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;
|
|
|
|
// Which 802.1X version state machines will talk?
|
|
// Default = draft 7
|
|
pNewPCB->fRemoteEnd8021XD8 = FALSE;
|
|
|
|
|
|
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->pszDeviceGUID
|
|
)) != NO_ERROR)
|
|
{
|
|
TRACE1 (PORT, "ElCreatePort: Error in ElInitRegPortData = %d",
|
|
dwRetCode);
|
|
break;
|
|
}
|
|
|
|
// Insert NewPCB into PCB hash table
|
|
dwIndex = ElHashPortToBucket (pszGUID);
|
|
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)
|
|
{
|
|
// 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)) != 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)) != NO_ERROR)
|
|
{
|
|
RELEASE_WRITE_LOCK (&(pNewPCB->rwLock));
|
|
TRACE1 (PORT, "ElCreatePort: FSMDisconnected failed with error %ld",
|
|
dwRetCode);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RELEASE_WRITE_LOCK (&(pNewPCB->rwLock));
|
|
|
|
TRACE1 (PORT, "ElCreatePort: Completed for GUID=%s", pszGUID);
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
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->pszDeviceGUID,
|
|
&hDevice
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// Remove all partial traces of port creation
|
|
if (pNewPCB->pszDeviceGUID != NULL)
|
|
{
|
|
FREE(pNewPCB->pszDeviceGUID);
|
|
pNewPCB->pszDeviceGUID = NULL;
|
|
}
|
|
if (pNewPCB->pszFriendlyName != NULL);
|
|
{
|
|
FREE(pNewPCB->pszFriendlyName);
|
|
pNewPCB->pszFriendlyName = 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:
|
|
// pszDeviceGUID - 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 CHAR *pszDeviceGUID,
|
|
OUT HANDLE *pHandle
|
|
)
|
|
{
|
|
EAPOL_PCB *pPCB = NULL;
|
|
DWORD dwRetCode = NO_ERROR;
|
|
|
|
ACQUIRE_WRITE_LOCK (&(g_PCBLock));
|
|
|
|
// Verify if PCB exists for this GUID
|
|
|
|
TRACE1 (PORT, "ElDeletePort entered for GUID %s", pszDeviceGUID);
|
|
pPCB = ElGetPCBPointerFromPortGUID (pszDeviceGUID);
|
|
|
|
if (pPCB == NULL)
|
|
{
|
|
RELEASE_WRITE_LOCK (&(g_PCBLock));
|
|
TRACE1 (PORT, "ElDeletePort: PCB not found entered for Port %s",
|
|
pszDeviceGUID);
|
|
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 %s",
|
|
pszDeviceGUID);
|
|
return ERROR_NO_SUCH_INTERFACE;
|
|
}
|
|
|
|
// 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);
|
|
|
|
// Do explicit decrement reference count.
|
|
// If it is non-zero, cleanup will complete later
|
|
|
|
|
|
TRACE1 (PORT, "ElDeletePort: RefCount for port = %ld", pPCB->dwRefCount);
|
|
|
|
if (--pPCB->dwRefCount)
|
|
{
|
|
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
|
|
RELEASE_WRITE_LOCK (&(g_PCBLock));
|
|
TRACE1 (PORT, "ElDeletePort: PCB deletion pending for Port %s",
|
|
pszDeviceGUID);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
|
|
|
|
// The reference count is zero, so perform final cleanup
|
|
|
|
ElCleanupPort (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 %s", pPCB->pszDeviceGUID);
|
|
|
|
// Do a BLOCKING deletion of the timer that was created on this port
|
|
// If a timer callback function was queued, it will be executed before
|
|
// the deletion returns.
|
|
// The PCB->rwLock needs to be held in the callback function
|
|
// ElCleanupPort is called always without holding any locks
|
|
// So, no deadlock issues should occur
|
|
DELETE_TIMER (pPCB->hTimer, INVALID_HANDLE_VALUE, &dwRetCode);
|
|
|
|
if (pPCB->pszDeviceGUID != NULL)
|
|
{
|
|
FREE (pPCB->pszDeviceGUID);
|
|
}
|
|
if (pPCB->pszFriendlyName)
|
|
{
|
|
FREE (pPCB->pszFriendlyName);
|
|
}
|
|
|
|
if (pPCB->pszEapReplyMessage != NULL)
|
|
{
|
|
FREE (pPCB->pszEapReplyMessage);
|
|
}
|
|
|
|
if (pPCB->pszSSID != NULL)
|
|
{
|
|
FREE (pPCB->pszSSID);
|
|
}
|
|
|
|
if (pPCB->EapUIData.pEapUIData != NULL)
|
|
{
|
|
FREE (pPCB->EapUIData.pEapUIData);
|
|
}
|
|
|
|
if (pPCB->pbMPPESendKey != NULL)
|
|
{
|
|
FREE (pPCB->pbMPPESendKey);
|
|
}
|
|
|
|
if (pPCB->pbMPPERecvKey != NULL)
|
|
{
|
|
FREE (pPCB->pbMPPERecvKey);
|
|
}
|
|
|
|
if (pPCB->pszIdentity != NULL)
|
|
{
|
|
FREE (pPCB->pszIdentity);
|
|
}
|
|
|
|
if (pPCB->pszPassword != NULL)
|
|
{
|
|
FREE (pPCB->pszPassword);
|
|
}
|
|
|
|
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;
|
|
|
|
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
|
|
)
|
|
{
|
|
DWORD dwSizeofRemoteMacAddr = 0;
|
|
DWORD dwCurrenTickCount = 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 %s",
|
|
pPCB->pszDeviceGUID);
|
|
break;
|
|
}
|
|
|
|
// If port was not restarted in the last 2 seconds,
|
|
// only then restart the machine. Else, there are too many
|
|
// restarts during roaming
|
|
// 2000 milliseconds should be sufficient time for a TLS authentication
|
|
// to go through
|
|
|
|
dwCurrenTickCount = GetTickCount ();
|
|
|
|
if ((dwCurrenTickCount - pPCB->dwLastRestartTickCount) <= 2000)
|
|
{
|
|
RELEASE_WRITE_LOCK (&pPCB->rwLock);
|
|
TRACE1 (PORT, "ElReStartPort: NOT restarting, since only %ld msecs have passed",
|
|
(dwCurrenTickCount - pPCB->dwLastRestartTickCount));
|
|
break;
|
|
}
|
|
|
|
if (EAPOL_PORT_DELETED(pPCB))
|
|
{
|
|
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
|
|
TRACE1 (PORT, "ElReStartPort: PCB already marked deleted for Port %s",
|
|
pPCB->pszDeviceGUID);
|
|
break;
|
|
}
|
|
|
|
pPCB->dwFlags = EAPOL_PORT_FLAG_ACTIVE;
|
|
|
|
pPCB->fGotUserIdentity = FALSE;
|
|
pPCB->ulStartCount = 0;
|
|
pPCB->dwPreviousId = 256;
|
|
pPCB->dwLogoffSent = 0;
|
|
pPCB->fIsRemoteEndEAPOLAware = TRUE;
|
|
pPCB->ullLastReplayCounter = 0;
|
|
pPCB->fAuthenticationOnNewNetwork = FALSE;
|
|
pPCB->fRemoteEnd8021XD8 = FALSE;
|
|
|
|
// Initialize per port information from registry
|
|
if ((dwRetCode = ElReadPerPortRegistryParams(pPCB->pszDeviceGUID,
|
|
pPCB)) != NO_ERROR)
|
|
{
|
|
RELEASE_WRITE_LOCK (&pPCB->rwLock);
|
|
TRACE1(PORT, "ElReStartPort: ElReadPerPortRegistryParams failed with error %ld",
|
|
dwRetCode);
|
|
break;
|
|
}
|
|
|
|
// 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, ÐERNET_TYPE_8021X[0], SIZE_ETHERNET_TYPE);
|
|
pPCB->bProtocolVersion = DEFAULT_8021X_VERSION;
|
|
|
|
// Get the Remote-end Mac address if possible
|
|
|
|
dwSizeofRemoteMacAddr = SIZE_MAC_ADDR;
|
|
|
|
if (dwRetCode = ElNdisuioQueryOIDValue (
|
|
pPCB->hPort,
|
|
OID_802_11_BSSID,
|
|
pPCB->bDestMacAddr,
|
|
&dwSizeofRemoteMacAddr
|
|
) != NO_ERROR)
|
|
{
|
|
TRACE1 (PORT, "ElReStartPort: ElNdisuioQueryOIDValue for OID_802_11_BSSID failed with error %ld",
|
|
dwRetCode);
|
|
|
|
// Copy default destination Mac address value
|
|
memcpy(pPCB->bDestMacAddr, &DEFAULT_DEST_MAC_ADDR[0], SIZE_MAC_ADDR);
|
|
dwRetCode = NO_ERROR;
|
|
|
|
// If destination MacAddress is going to be multicast
|
|
// inform the driver to accept the packets from this address
|
|
|
|
if ((dwRetCode = ElNdisuioSetOIDValue (
|
|
pPCB->hPort,
|
|
OID_802_3_MULTICAST_LIST,
|
|
(BYTE *)&DEFAULT_DEST_MAC_ADDR[0],
|
|
SIZE_MAC_ADDR))
|
|
!= NO_ERROR)
|
|
{
|
|
TRACE1 (PORT, "ElReStartPort: ElNdisuioSetOIDValue for OID_802_3_MULTICAST_LIST failed with error %ld",
|
|
dwRetCode);
|
|
}
|
|
else
|
|
{
|
|
TRACE0 (PORT, "ElReStartPort: ElNdisuioSetOIDValue for OID_802_3_MULTICAST_LIST successful");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE0 (PORT, "ElReStartPort: ElNdisuioQueryOIDValue for OID_802_11_BSSID successful");
|
|
EAPOL_DUMPBA (pPCB->bDestMacAddr, dwSizeofRemoteMacAddr);
|
|
}
|
|
|
|
// 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);
|
|
|
|
// Set restart tickcount
|
|
|
|
pPCB->dwLastRestartTickCount = dwCurrenTickCount;
|
|
|
|
|
|
// Send out EAPOL_Start Packets
|
|
|
|
if ((dwRetCode = FSMConnecting (pPCB)) != NO_ERROR)
|
|
{
|
|
TRACE1 (PORT, "ElReStartPort: FSMConnecting failed with error %ld",
|
|
dwRetCode);
|
|
}
|
|
|
|
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;
|
|
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));
|
|
|
|
// Mark the PCB as deleted and remove it from the hash bucket
|
|
pPCB->dwFlags = EAPOL_PORT_FLAG_DELETED;
|
|
ElRemovePCBFromTable(pPCB);
|
|
|
|
// Shutdown EAP
|
|
ElEapEnd (pPCB);
|
|
|
|
// Close the handle to the NDISUIO driver
|
|
if ((dwRetCode = ElCloseInterfaceHandle (pPCB->hPort))
|
|
!= NO_ERROR)
|
|
{
|
|
TRACE1 (DEVICE,
|
|
"ElMediaSenseCallback: Error in ElCloseInterfaceHandle %d",
|
|
dwRetCode);
|
|
}
|
|
|
|
// Do explicit decrement reference count.
|
|
// If it is zero, cleanup right now
|
|
|
|
if ((pPCB->dwRefCount--) == 0)
|
|
{
|
|
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
|
|
ElCleanupPort (pPCB);
|
|
}
|
|
else
|
|
{
|
|
// Cleanup will happen later
|
|
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
|
|
}
|
|
}
|
|
}
|
|
|
|
RELEASE_WRITE_LOCK (&(g_PCBLock));
|
|
|
|
// 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));
|
|
}
|
|
|
|
// Delete global timer queue
|
|
if (g_hTimerQueue != NULL)
|
|
{
|
|
if (!DeleteTimerQueueEx(
|
|
g_hTimerQueue,
|
|
NULL // Not waiting for timer callbacks to complete
|
|
))
|
|
{
|
|
dwRetCode = GetLastError();
|
|
|
|
// Pending on timer deletion is asked above
|
|
if (dwRetCode != ERROR_IO_PENDING)
|
|
{
|
|
TRACE1 (PORT, "ElEAPOLDeInit: Error in DeleteTimerQueueEx = %d",
|
|
dwRetCode);
|
|
break;
|
|
}
|
|
|
|
// If ERROR_IO_PENDING ignore error
|
|
dwRetCode = NO_ERROR;
|
|
}
|
|
}
|
|
|
|
// 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 CHAR *pszDeviceGUID,
|
|
OUT PEAPOL_STATS pEapolStats
|
|
)
|
|
{
|
|
}
|
|
|
|
|
|
//
|
|
// Currently Unsupported
|
|
// Read EAPOL Port Configuration for the mentioned port
|
|
//
|
|
|
|
VOID
|
|
ElReadPortConfiguration (
|
|
IN CHAR *pszDeviceGUID,
|
|
OUT PEAPOL_CONFIG pEapolConfig
|
|
)
|
|
{
|
|
}
|
|
|
|
|
|
//
|
|
// Currently Unsupported
|
|
// Set EAPOL Port Configuration for the mentioned port
|
|
//
|
|
|
|
DWORD
|
|
ElSetPortConfiguration (
|
|
IN CHAR *pszDeviceGUID,
|
|
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 %s",
|
|
dwError, pPCB->pszDeviceGUID);
|
|
|
|
// 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_ACTIVE(pPCB))
|
|
{
|
|
TRACE1 (PORT, "ElReadCompletionRoutine: Port %s not active",
|
|
pPCB->pszDeviceGUID);
|
|
// Port is not active, release Context buffer
|
|
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
|
|
FREE (pEapolBuffer);
|
|
}
|
|
else
|
|
{
|
|
TRACE1 (PORT, "ElReadCompletionRoutine: Reposting buffer on port %s",
|
|
pPCB->pszDeviceGUID);
|
|
|
|
|
|
// 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 error %d",
|
|
dwRetCode);
|
|
break;
|
|
}
|
|
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Successful read completion
|
|
|
|
ACQUIRE_WRITE_LOCK (&(pPCB->rwLock));
|
|
if (!EAPOL_PORT_ACTIVE(pPCB))
|
|
{
|
|
// Port is not active
|
|
RELEASE_WRITE_LOCK (&(pPCB->rwLock));
|
|
FREE (pEapolBuffer);
|
|
TRACE1 (PORT, "ElReadCompletionRoutine: Port %s is inactive",
|
|
pPCB->pszDeviceGUID);
|
|
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
|
|
|
|
if (!QueueUserWorkItem (
|
|
(LPTHREAD_START_ROUTINE)ElProcessReceivedPacket,
|
|
(PVOID)pEapolBuffer,
|
|
WT_EXECUTEINIOTHREAD
|
|
))
|
|
{
|
|
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 without decrementing
|
|
|
|
return;
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
// Decrement refcount for error cases
|
|
|
|
EAPOL_DEREFERENCE_PORT(pPCB);
|
|
|
|
TRACE2 (PORT, "ElReadCompletionRoutine: pPCB= %p, RefCnt = %ld",
|
|
pPCB, pPCB->dwRefCount);
|
|
}
|
|
|
|
|
|
//
|
|
// 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 since it will not be 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)
|
|
{
|
|
if (pPCB->fRemoteEnd8021XD8)
|
|
{
|
|
EAPOL_PACKET *pEapolPkt = NULL;
|
|
EAPOL_PACKET_D8 *pEapolPktD8 = NULL;
|
|
|
|
pEapolPkt = (EAPOL_PACKET *)pBuffer;
|
|
pEapolPktD8 =
|
|
(EAPOL_PACKET_D8 *)((PBYTE)pEapolBuffer->pBuffer +
|
|
sizeof(ETH_HEADER));
|
|
|
|
memcpy (pEapolPktD8->EthernetType, pEapolPkt->EthernetType, 2);
|
|
pEapolPktD8->ProtocolVersion = pEapolPkt->ProtocolVersion;
|
|
pEapolPktD8->PacketType = pEapolPkt->PacketType;
|
|
memcpy (pEapolPktD8->PacketBodyLength, pEapolPkt->PacketBodyLength,
|
|
2);
|
|
HostToWireFormat16 ((WORD)AUTH_Continuing,
|
|
pEapolPktD8->AuthResultCode);
|
|
|
|
memcpy (pEapolPktD8->PacketBody, pEapolPkt->PacketBody,
|
|
dwBufferLength - sizeof(EAPOL_PACKET) + 1);
|
|
|
|
dwBufferLength = dwBufferLength + 2;
|
|
|
|
TRACE0 (PORT, "ElWriteToPort: Writing out a DRAFT 8 type EAPOL packet");
|
|
}
|
|
else
|
|
{
|
|
memcpy ((PBYTE)((PBYTE)pEapolBuffer->pBuffer+sizeof(ETH_HEADER)),
|
|
(PBYTE)pBuffer,
|
|
dwBufferLength);
|
|
}
|
|
}
|
|
|
|
dwTotalBytes = dwBufferLength + sizeof(ETH_HEADER);
|
|
|
|
|
|
// 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;
|
|
|
|
}
|