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