|
|
/*++
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;
}
|