/*++ Copyright (c) 1999, Microsoft Corporation Module Name: elprotocol.c Abstract: This module implements functions related to EAPOL protocol Revision History: sachins, Apr 30 2000, Created --*/ #include "pcheapol.h" #pragma hdrstop // // ElProcessReceivedPacket // // Description: // // Function called to process data received from the NDISUIO driver. // The EAPOL packet is extracted and further processing is done. // // // Arguments: // pvContext - Context buffer which is a pointer to EAPOL_BUFFER structure // // Return Values: // DWORD WINAPI ElProcessReceivedPacket ( IN PVOID pvContext ) { EAPOL_PCB *pPCB = NULL; EAPOL_BUFFER *pEapolBuffer = NULL; DWORD dwLength = 0; ETH_HEADER *pEthHdr = NULL; EAPOL_PACKET *pEapolPkt = NULL; DWORD dw8021PSize = 0; PPP_EAP_PACKET *pEapPkt = NULL; BYTE *pBuffer; BOOLEAN ReqId = FALSE; // EAPOL state machine local variables BOOLEAN ReqAuth = FALSE; BOOLEAN EapSuccess = FALSE; BOOLEAN EapFail = FALSE; BOOLEAN RxKey = FALSE; GUID DeviceGuid; DWORD dwRetCode = NO_ERROR; if (pvContext == NULL) { TRACE0 (EAPOL, "ProcessReceivedPacket: Critical error, Context is NULL"); return 0; } pEapolBuffer = (EAPOL_BUFFER *)pvContext; pPCB = (EAPOL_PCB *)pEapolBuffer->pvContext; dwLength = pEapolBuffer->dwBytesTransferred; pBuffer = (BYTE *)pEapolBuffer->pBuffer; TRACE1 (EAPOL, "ProcessReceivedPacket entered, length = %ld", dwLength); ElParsePacket (pBuffer, dwLength, TRUE); ACQUIRE_WRITE_LOCK (&(pPCB->rwLock)); do { // The Port was verified to be active before the workitem // was queued. But do a double-check // Validate packet length // Should be atleast ETH_HEADER and first 4 required bytes of // EAPOL_PACKET if (dwLength < (sizeof(ETH_HEADER) + 4)) { TRACE2 (EAPOL, "ProcessReceivedPacket: Packet length %ld is less than minimum required %d. Ignoring packet", dwLength, (sizeof(ETH_HEADER) + 4)); dwRetCode = ERROR_INVALID_PACKET_LENGTH_OR_ID; break; } // If the source address is same as the local MAC address, it is a // multicast packet copy sent out being received pEthHdr = (ETH_HEADER *)pBuffer; if ((memcmp ((BYTE *)pEthHdr->bSrcAddr, (BYTE *)pPCB->bSrcMacAddr, SIZE_MAC_ADDR)) == 0) { TRACE0 (EAPOL, "ProcessReceivedPacket: Src MAC address of packet matches local address. Ignoring packet"); dwRetCode = ERROR_INVALID_ADDRESS; break; } // Verify if the packet contains a 802.1P tag. If so, skip the 4 bytes // after the src+dest mac addresses if ((WireToHostFormat16(pBuffer + sizeof(ETH_HEADER)) == EAPOL_8021P_TAG_TYPE)) { pEapolPkt = (EAPOL_PACKET *)(pBuffer + sizeof(ETH_HEADER) + 4); dw8021PSize = 4; } else { pEapolPkt = (EAPOL_PACKET *)(pBuffer + sizeof(ETH_HEADER)); } // Validate Ethernet type in the incoming packet // It should be the same as the one defined for the // current port if (memcmp ((BYTE *)pEapolPkt->EthernetType, (BYTE *)pPCB->bEtherType, SIZE_ETHERNET_TYPE) != 0) { TRACE0 (EAPOL, "ProcessReceivedPacket: Packet Ethernet type does not match expected type. Ignoring packet"); TRACE0 (EAPOL, "Incoming:"); EAPOL_DUMPBA ((BYTE *)pEapolPkt->EthernetType, SIZE_ETHERNET_TYPE); TRACE0 (EAPOL, "Expected:"); EAPOL_DUMPBA ((BYTE *)pPCB->bEtherType, SIZE_ETHERNET_TYPE); dwRetCode = ERROR_INVALID_PACKET_LENGTH_OR_ID; break; } // EAPOL packet type should be valid if ((pEapolPkt->PacketType != EAP_Packet) && (pEapolPkt->PacketType != EAPOL_Start) && (pEapolPkt->PacketType != EAPOL_Logoff) && (pEapolPkt->PacketType != EAPOL_Key)) { TRACE1 (EAPOL, "ProcessReceivedPacket: Invalid EAPOL packet type %d. Ignoring packet", pEapolPkt->PacketType); dwRetCode = ERROR_INVALID_PACKET; break; } if ((WireToHostFormat16(pEapolPkt->PacketBodyLength) > (MAX_PACKET_SIZE - (SIZE_ETHERNET_CRC + sizeof(ETH_HEADER) + dw8021PSize + FIELD_OFFSET (EAPOL_PACKET, PacketBody))))) // || // (WireToHostFormat16(pEapolPkt->PacketBodyLength) != (dwLength - (sizeof(ETH_HEADER) + dw8021PSize + FIELD_OFFSET (EAPOL_PACKET, PacketBody))))) { TRACE3 (EAPOL, "ProcessReceivedPacket: Invalid length in EAPOL packet (%ld), Max length (%ld), Exact length (%ld), Ignoring packet", WireToHostFormat16(pEapolPkt->PacketBodyLength), (MAX_PACKET_SIZE - (SIZE_ETHERNET_CRC + sizeof(ETH_HEADER) + dw8021PSize + FIELD_OFFSET (EAPOL_PACKET, PacketBody))), (dwLength - (sizeof(ETH_HEADER) + dw8021PSize + FIELD_OFFSET (EAPOL_PACKET, PacketBody))) ); dwRetCode = ERROR_INVALID_PACKET; break; } // Determine the value of local EAPOL state variables if (pEapolPkt->PacketType == EAP_Packet) { TRACE0 (EAPOL, "ProcessReceivedPacket: EAP_Packet"); // Validate length of packet for EAP // Should be atleast (ETH_HEADER+EAPOL_PACKET) if (dwLength < (sizeof (ETH_HEADER) + dw8021PSize + FIELD_OFFSET (EAPOL_PACKET, PacketBody) + FIELD_OFFSET(PPP_EAP_PACKET, Data))) { TRACE1 (EAPOL, "ProcessReceivedPacket: Invalid length of EAP packet %d. Ignoring packet", dwLength); dwRetCode = ERROR_INVALID_PACKET; break; } pEapPkt = (PPP_EAP_PACKET *)pEapolPkt->PacketBody; if (WireToHostFormat16(pEapolPkt->PacketBodyLength) != WireToHostFormat16 (pEapPkt->Length)) { TRACE2 (EAPOL, "ProcessReceivedPacket: Invalid length in EAPOL packet (%ld) not matching EAP length (%ld), Ignoring packet", WireToHostFormat16(pEapolPkt->PacketBodyLength), WireToHostFormat16 (pEapPkt->Length)); dwRetCode = ERROR_INVALID_PACKET; break; } if (pEapPkt->Code == EAPCODE_Request) { // Validate length of packet for EAP-Request packet // Should be atleast (ETH_HEADER+EAPOL_PACKET-1+PPP_EAP_PACKET) if (dwLength < (sizeof (ETH_HEADER) + sizeof(EAPOL_PACKET)-1 + sizeof (PPP_EAP_PACKET))) { TRACE1 (EAPOL, "ProcessReceivedPacket: Invalid length of EAP Request packet %d. Ignoring packet", dwLength); dwRetCode = ERROR_INVALID_PACKET; break; } if (pEapPkt->Data[0] == EAPTYPE_Identity) { pPCB->fIsRemoteEndEAPOLAware = TRUE; switch (pPCB->dwSupplicantMode) { case SUPPLICANT_MODE_0: case SUPPLICANT_MODE_1: // ignore break; case SUPPLICANT_MODE_2: case SUPPLICANT_MODE_3: pPCB->fEAPOLTransmissionFlag = TRUE; break; } ReqId = TRUE; } else { ReqAuth = TRUE; } } else if (pEapPkt->Code == EAPCODE_Success) { EapSuccess = TRUE; } else if (pEapPkt->Code == EAPCODE_Failure) { EapFail = TRUE; } else { // Invalid type TRACE1 (EAPOL, "ProcessReceivedPacket: Invalid EAP packet type %d. Ignoring packet", pEapPkt->Code); dwRetCode = ERROR_INVALID_PACKET; break; } } else { TRACE0 (EAPOL, "ProcessReceivedPacket: != EAP_Packet"); if (pEapolPkt->PacketType == EAPOL_Key) { TRACE0 (EAPOL, "ProcessReceivedPacket: == EAPOL_Key"); RxKey = TRUE; } else { TRACE0 (EAPOL, "ProcessReceivedPacket: Invalid packet type"); } } // State machine does not accept packets for inactive/disabled ports if (!EAPOL_PORT_ACTIVE(pPCB)) { TRACE1 (EAPOL, "ProcessReceivedPacket: Port %ws not active", pPCB->pwszDeviceGUID); if (EAPOL_PORT_DISABLED(pPCB)) { DbLogPCBEvent (DBLOG_CATEG_WARN, pPCB, EAPOL_NOT_ENABLED_PACKET_REJECTED); } break; } if (RxKey) { if ((dwRetCode = FSMKeyReceive (pPCB, pEapolPkt)) != NO_ERROR) { break; } } switch (pPCB->State) { // ReqId, ReqAuth, EapSuccess, EapFail, RxKey are inherently // mutually exclusive // No checks will be made to verify this // Also, assumption is being made that in any state, maximum // one timer may be active on the port. case EAPOLSTATE_LOGOFF: // Only a User Logon event can get the port out of // LOGOFF state TRACE0 (EAPOL, "ProcessReceivedPacket: LOGOFF state, Ignoring packet"); break; case EAPOLSTATE_DISCONNECTED: // Only a Media Connect / User logon / System reset event // can get the port out of DISCONNECTED state TRACE0 (EAPOL, "ProcessReceivedPacket: DISCONNECTED state, Ignoring packet"); break; case EAPOLSTATE_CONNECTING: TRACE0 (EAPOL, "ProcessReceivedPacket: EAPOLSTATE_CONNECTING"); if (EapSuccess) { if (!pPCB->fLocalEAPAuthSuccess) { TRACE0 (EAPOL, "ProcessReceivedPacket: Dropping invalid EAP-Success packet"); dwRetCode = ERROR_INVALID_PACKET; break; } } if (ReqId | EapSuccess | EapFail) { // Deactivate current timer RESTART_TIMER (pPCB->hTimer, INFINITE_SECONDS, "PCB", &dwRetCode); if (dwRetCode != NO_ERROR) { break; } } if (EapSuccess) { if ((dwRetCode = ElProcessEapSuccess (pPCB, pEapolPkt)) != NO_ERROR) { break; } } else if (EapFail) { if ((dwRetCode = ElProcessEapFail (pPCB, pEapolPkt)) != NO_ERROR) { break; } } else if (ReqId) { if ((dwRetCode = FSMAcquired (pPCB, pEapolPkt)) != NO_ERROR) { break; } } break; case EAPOLSTATE_ACQUIRED: TRACE0 (EAPOL, "ProcessReceivedPacket: EAPOLSTATE_ACQUIRED"); if (EapSuccess) { if (!pPCB->fLocalEAPAuthSuccess) { TRACE0 (EAPOL, "ProcessReceivedPacket: Dropping invalid EAP-Success packet"); dwRetCode = ERROR_INVALID_PACKET; break; } } if (ReqId | ReqAuth | EapSuccess | EapFail) { // Deactivate current timer RESTART_TIMER (pPCB->hTimer, INFINITE_SECONDS, "PCB", &dwRetCode); if (dwRetCode != NO_ERROR) { break; } // Reset EapUI state if (!ReqId) { pPCB->EapUIState &= ~EAPUISTATE_WAITING_FOR_IDENTITY; } } if (EapSuccess) { if ((dwRetCode = ElProcessEapSuccess (pPCB, pEapolPkt)) != NO_ERROR) { break; } } else if (EapFail) { if ((dwRetCode = ElProcessEapFail (pPCB, pEapolPkt)) != NO_ERROR) { break; } } else if (ReqId) { if ((dwRetCode = FSMAcquired (pPCB, pEapolPkt)) != NO_ERROR) { break; } } else if (ReqAuth) { if ((dwRetCode = FSMAuthenticating (pPCB, pEapolPkt)) != NO_ERROR) { break; } } break; case EAPOLSTATE_AUTHENTICATING: TRACE0 (EAPOL, "ProcessReceivedPacket: EAPOLSTATE_AUTHENTICATING"); // Common timer deletion if (ReqAuth | ReqId | EapSuccess | EapFail) { // Deactivate current timer RESTART_TIMER (pPCB->hTimer, INFINITE_SECONDS, "PCB", &dwRetCode); if (dwRetCode != NO_ERROR) { break; } if (ReqId) { if ((dwRetCode = FSMAcquired (pPCB, pEapolPkt)) != NO_ERROR) { break; } } else { if ((dwRetCode = FSMAuthenticating (pPCB, pEapolPkt)) != NO_ERROR) { break; } } // Reset EapUI state if (!ReqAuth) { pPCB->EapUIState &= ~EAPUISTATE_WAITING_FOR_UI_RESPONSE; } } // Continue further processing if (EapSuccess | EapFail) { if (EapSuccess) { if (!pPCB->fLocalEAPAuthSuccess) { TRACE0 (EAPOL, "ProcessReceivedPacket: Dropping invalid EAP-Success packet"); dwRetCode = ERROR_INVALID_PACKET; break; } } // Auth timer will have restarted in FSMAuthenticating // Deactivate the timer RESTART_TIMER (pPCB->hTimer, INFINITE_SECONDS, "PCB", &dwRetCode); if (dwRetCode != NO_ERROR) { break; } // If the packet received was a EAP-Success, go into // AUTHENTICATED state if (EapSuccess) { if ((dwRetCode = ElProcessEapSuccess (pPCB, pEapolPkt)) != NO_ERROR) { break; } } else // If the packet received was a EAP-Failure, go into // HELD state if (EapFail) { if ((dwRetCode = ElProcessEapFail (pPCB, pEapolPkt)) != NO_ERROR) { break; } } } break; case EAPOLSTATE_HELD: TRACE0 (EAPOL, "ProcessReceivedPacket: HELD state, Ignoring packet"); if (ReqId) { // Deactivate current timer RESTART_TIMER (pPCB->hTimer, INFINITE_SECONDS, "PCB", &dwRetCode); if (dwRetCode != NO_ERROR) { break; } if ((dwRetCode = FSMAcquired (pPCB, pEapolPkt)) != NO_ERROR) { break; } } break; case EAPOLSTATE_AUTHENTICATED: TRACE0 (EAPOL, "ProcessReceivedPacket: STATE_AUTHENTICATED"); if (ReqId) { if ((dwRetCode = FSMAcquired (pPCB, pEapolPkt)) != NO_ERROR) { break; } } else { if (EapFail) { if ((dwRetCode = ElProcessEapFail (pPCB, pEapolPkt)) != NO_ERROR) { break; } } } break; default: TRACE0 (EAPOL, "ProcessReceivedPacket: Critical Error. Invalid state, Ignoring packet"); break; } } while (FALSE); if (pEapolBuffer != NULL) { FREE (pEapolBuffer); } // Post a new read request, ignoring errors if (EAPOL_PORT_DELETED(pPCB)) { TRACE1 (EAPOL, "ProcessReceivedPacket: Port %ws deleted, not reposting read request", pPCB->pwszDeviceGUID); } else { TRACE1 (EAPOL, "ProcessReceivedPacket: Reposting buffer on port %ws", pPCB->pwszDeviceGUID); // ElReadFromPort creates a new context buffer, adds a ref count, // and posts the read request if ((dwRetCode = ElReadFromPort ( pPCB, NULL, 0 )) != NO_ERROR) { TRACE1 (EAPOL, "ProcessReceivedPacket: Critical error: ElReadFromPort error %d", dwRetCode); } } RELEASE_WRITE_LOCK (&(pPCB->rwLock)); TRACE2 (EAPOL, "ProcessReceivedPacket: pPCB= %p, RefCnt = %ld", pPCB, pPCB->dwRefCount); // Dereference ref count held for the read that was just processed EAPOL_DEREFERENCE_PORT(pPCB); TRACE0 (EAPOL, "ProcessReceivedPacket exit"); InterlockedDecrement (&g_lWorkerThreads); return 0; } // // FSMDisconnected // // Description: // Function called when media disconnect occurs // // Arguments: // pPCB - Pointer to PCB for the port on which media disconnect occurs // // Return values: // NO_ERROR - success // non-zero - error // DWORD FSMDisconnected ( IN EAPOL_PCB *pPCB, IN EAPOL_PACKET *pEapolPkt ) { DWORD dwRetCode = NO_ERROR; TRACE1 (EAPOL, "FSMDisconnected entered for port %ws", pPCB->pwszFriendlyName); do { } while (FALSE); TRACE1 (EAPOL, "Setting state DISCONNECTED for port %ws", pPCB->pwszFriendlyName); DbLogPCBEvent (DBLOG_CATEG_INFO, pPCB, EAPOL_STATE_TRANSITION, EAPOLStates[((pPCB->State < EAPOLSTATE_LOGOFF) || (pPCB->State > EAPOLSTATE_AUTHENTICATED))?EAPOLSTATE_UNDEFINED:pPCB->State], EAPOLStates[EAPOLSTATE_DISCONNECTED]); pPCB->State = EAPOLSTATE_DISCONNECTED; pPCB->EapUIState = 0; // Free Identity buffer if (pPCB->pszIdentity != NULL) { FREE (pPCB->pszIdentity); pPCB->pszIdentity = NULL; } // Free Password buffer if (pPCB->PasswordBlob.pbData != NULL) { FREE (pPCB->PasswordBlob.pbData); pPCB->PasswordBlob.pbData = NULL; pPCB->PasswordBlob.cbData = 0; } // Free user-specific data in the PCB if (pPCB->pCustomAuthUserData != NULL) { FREE (pPCB->pCustomAuthUserData); pPCB->pCustomAuthUserData = NULL; } // Free connection data, though it is common to all users if (pPCB->pCustomAuthConnData != NULL) { FREE (pPCB->pCustomAuthConnData); pPCB->pCustomAuthConnData = NULL; } pPCB->dwAuthFailCount = 0; pPCB->fGotUserIdentity = FALSE; if (pPCB->hUserToken != NULL) { if (!CloseHandle (pPCB->hUserToken)) { dwRetCode = GetLastError (); TRACE1 (EAPOL, "FSMDisconnected: CloseHandle failed with error %ld", dwRetCode); dwRetCode = NO_ERROR; } } pPCB->hUserToken = NULL; TRACE1 (EAPOL, "FSMDisconnected completed for port %ws", pPCB->pwszFriendlyName); return dwRetCode; } // // FSMLogoff // // Description: // Function called to send out EAPOL_Logoff packet. Usually triggered by // user logging off. // // Arguments: // pPCB - Pointer to PCB for the port on which logoff packet is to be // sent out // // Return values: // NO_ERROR - success // non-zero - error // DWORD FSMLogoff ( IN EAPOL_PCB *pPCB, IN EAPOL_PACKET *pDummy ) { EAPOL_PACKET *pEapolPkt = NULL; BOOLEAN fAuthSendPacket = FALSE; BOOLEAN fSupplicantSendPacket = FALSE; DWORD dwRetCode = NO_ERROR; TRACE1 (EAPOL, "FSMLogoff entered for port %ws", pPCB->pwszFriendlyName); do { // End EAP session ElEapEnd (pPCB); // Send out EAPOL_Logoff conditionally if ( ((pPCB->dwSupplicantMode == SUPPLICANT_MODE_2) && (pPCB->fEAPOLTransmissionFlag)) || (pPCB->dwSupplicantMode == SUPPLICANT_MODE_3)) { fSupplicantSendPacket = TRUE; } switch (pPCB->dwEAPOLAuthMode) { case EAPOL_AUTH_MODE_0: fAuthSendPacket = TRUE; break; case EAPOL_AUTH_MODE_1: fAuthSendPacket = FALSE; break; case EAPOL_AUTH_MODE_2: fAuthSendPacket = FALSE; break; } if ((fSupplicantSendPacket) && (fAuthSendPacket)) { // Allocate new buffer pEapolPkt = (EAPOL_PACKET *) MALLOC (sizeof (EAPOL_PACKET)); if (pEapolPkt == NULL) { TRACE0 (EAPOL, "FSMLogoff: Error in allocating memory for EAPOL packet"); dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break; } // Fill in fields memcpy ((BYTE *)pEapolPkt->EthernetType, (BYTE *)pPCB->bEtherType, SIZE_ETHERNET_TYPE); pEapolPkt->ProtocolVersion = pPCB->bProtocolVersion; pEapolPkt->PacketType = EAPOL_Logoff; HostToWireFormat16 ((WORD)0, (BYTE *)pEapolPkt->PacketBodyLength); // Send packet out on the port dwRetCode = ElWriteToPort (pPCB, (CHAR *)pEapolPkt, sizeof (EAPOL_PACKET)); if (dwRetCode != NO_ERROR) { TRACE1 (EAPOL, "FSMLogoff: Error in writing Logoff pkt to port %ld", dwRetCode); break; } // Mark that EAPOL_Logoff was sent out on the port pPCB->dwLogoffSent = 1; } } while (FALSE); TRACE1 (EAPOL, "Setting state LOGOFF for port %ws", pPCB->pwszFriendlyName); DbLogPCBEvent (DBLOG_CATEG_INFO, pPCB, EAPOL_STATE_TRANSITION, EAPOLStates[((pPCB->State < EAPOLSTATE_LOGOFF) || (pPCB->State > EAPOLSTATE_AUTHENTICATED))?EAPOLSTATE_UNDEFINED:pPCB->State], EAPOLStates[EAPOLSTATE_LOGOFF]); pPCB->State = EAPOLSTATE_LOGOFF; pPCB->EapUIState = 0; // Release user token if (pPCB->hUserToken != NULL) { if (!CloseHandle (pPCB->hUserToken)) { dwRetCode = GetLastError (); TRACE1 (EAPOL, "FSMLogoff: CloseHandle failed with error %ld", dwRetCode); dwRetCode = NO_ERROR; } } pPCB->hUserToken = NULL; // Free Identity buffer if (pPCB->pszIdentity != NULL) { FREE (pPCB->pszIdentity); pPCB->pszIdentity = NULL; } // Free Password buffer if (pPCB->PasswordBlob.pbData != NULL) { FREE (pPCB->PasswordBlob.pbData); pPCB->PasswordBlob.pbData = NULL; pPCB->PasswordBlob.cbData = 0; } // Free user-specific data in the PCB if (pPCB->pCustomAuthUserData != NULL) { FREE (pPCB->pCustomAuthUserData); pPCB->pCustomAuthUserData = NULL; } pPCB->fGotUserIdentity = FALSE; if (pEapolPkt != NULL) { FREE (pEapolPkt); pEapolPkt = NULL; } TRACE1 (EAPOL, "FSMLogoff completed for port %ws", pPCB->pwszFriendlyName); return dwRetCode; } // // FSMConnecting // // Description: // // Funtion called to send out EAPOL_Start packet. If MaxStart EAPOL_Start // packets have been sent out, State Machine moves to Authenticated state // // Arguments: // pPCB - Pointer to the PCB for the port on which Start packet is // to be sent out // // Return values: // NO_ERROR - success // non-zero - error // DWORD FSMConnecting ( IN EAPOL_PCB *pPCB, IN EAPOL_PACKET *pDummy ) { EAPOL_PACKET *pEapolPkt = NULL; DWORD dwStartInterval = 0; GUID DeviceGuid; DWORD dwRetCode = NO_ERROR; TRACE1 (EAPOL, "FSMConnecting entered for port %ws", pPCB->pwszFriendlyName); do { // Flag that authentication has not completed in the EAP module // on the client-side. pPCB->fLocalEAPAuthSuccess = FALSE; pPCB->dwLocalEAPAuthResult = NO_ERROR; if (pPCB->State == EAPOLSTATE_CONNECTING) { // If PCB->State was Connecting earlier, increment ulStartCount // else set ulStartCount to zero // Did not receive Req/Id if ((++(pPCB->ulStartCount)) > pPCB->EapolConfig.dwmaxStart) { // Deactivate start timer RESTART_TIMER (pPCB->hTimer, INFINITE_SECONDS, "PCB", &dwRetCode); if (dwRetCode != NO_ERROR) { break; } TRACE0 (EAPOL, "FSMConnecting: Sent out maxStart with no response, Setting AUTHENTICATED state"); // Sent out enough EAPOL_Starts // Go into authenticated state if ((dwRetCode = FSMAuthenticated (pPCB, pEapolPkt)) != NO_ERROR) { TRACE1 (EAPOL, "FSMConnecting: Error in FSMAuthenticated %ld", dwRetCode); break; } // No need to send out more EAPOL_Start packets // Reset start packet count pPCB->ulStartCount = 0; pPCB->fIsRemoteEndEAPOLAware = FALSE; break; } } else { pPCB->ulStartCount++; } // Initialize the address of previously associated AP // Only if the reauthentication goes through without getting // into CONNECTING state, will a IP Renew *not* be done ZeroMemory (pPCB->bPreviousDestMacAddr, SIZE_MAC_ADDR); // If user is not logged in, send out EAPOL_Start packets // at intervals of 1 second each. This is used to detect if the // interface is on a secure network or not. // If user is logged in, use the configured value for the // StartPeriod as the interval if (!g_fUserLoggedOn) { dwStartInterval = EAPOL_INIT_START_PERIOD; // 1 second } else { dwStartInterval = pPCB->EapolConfig.dwstartPeriod; } // Restart timer with startPeriod // Even if error occurs, timeout will happen // Else, we won't be able to get out of connecting state RESTART_TIMER (pPCB->hTimer, dwStartInterval, "PCB", &dwRetCode); if (dwRetCode != NO_ERROR) { TRACE1 (EAPOL, "FSMConnecting: Error in RESTART_TIMER %ld", dwRetCode); break; } // Send out EAPOL_Start conditionally if (((pPCB->dwSupplicantMode == SUPPLICANT_MODE_2) && (pPCB->fEAPOLTransmissionFlag)) || (pPCB->dwSupplicantMode == SUPPLICANT_MODE_3)) { // Send out EAPOL_Start // Allocate new buffer pEapolPkt = (EAPOL_PACKET *) MALLOC (sizeof(EAPOL_PACKET)); if (pEapolPkt == NULL) { TRACE0 (EAPOL, "FSMConnecting: Error in allocating memory for EAPOL packet"); dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break; } memcpy ((BYTE *)pEapolPkt->EthernetType, (BYTE *)pPCB->bEtherType, SIZE_ETHERNET_TYPE); pEapolPkt->ProtocolVersion = pPCB->bProtocolVersion; pEapolPkt->PacketType = EAPOL_Start; HostToWireFormat16 ((WORD)0, (BYTE *)pEapolPkt->PacketBodyLength); // Send packet out on the port dwRetCode = ElWriteToPort (pPCB, (CHAR *)pEapolPkt, sizeof (EAPOL_PACKET)); if (dwRetCode != NO_ERROR) { TRACE1 (EAPOL, "FSMConnecting: Error in writing Start Pkt to port %ld", dwRetCode); break; } } TRACE1 (EAPOL, "Setting state CONNECTING for port %ws", pPCB->pwszFriendlyName); DbLogPCBEvent (DBLOG_CATEG_INFO, pPCB, EAPOL_STATE_TRANSITION, EAPOLStates[((pPCB->State < EAPOLSTATE_LOGOFF) || (pPCB->State > EAPOLSTATE_AUTHENTICATED))?EAPOLSTATE_UNDEFINED:pPCB->State], EAPOLStates[EAPOLSTATE_CONNECTING]); pPCB->State = EAPOLSTATE_CONNECTING; SET_EAPOL_START_TIMER(pPCB); // Reset UI interaction state pPCB->EapUIState = 0; } while (FALSE); if (pEapolPkt != NULL) { FREE (pEapolPkt); } TRACE1 (EAPOL, "FSMConnecting completed for port %ws", pPCB->pwszFriendlyName); return dwRetCode; } // // FSMAcquired // // Description: // Function called when the port receives a EAP-Request/Identity packet. // EAP processing of the packet occurs and a EAP-Response/Identity may // be sent out by EAP if required. // // // Arguments: // pPCB - Pointer to the PCB for the port on which data is being // processed // pEapolPkt - Pointer to EAPOL packet that was received // // Return values: // NO_ERROR - success // non-zero - error // DWORD FSMAcquired ( IN EAPOL_PCB *pPCB, IN EAPOL_PACKET *pEapolPkt ) { DWORD dwComputerNameLen = 0; GUID DeviceGuid; DWORD dwRetCode= NO_ERROR; TRACE1 (EAPOL, "FSMAcquired entered for port %ws", pPCB->pwszFriendlyName); do { // Flag that authentication has not completed in the EAP module // on the client-side. pPCB->fLocalEAPAuthSuccess = FALSE; pPCB->dwLocalEAPAuthResult = NO_ERROR; // Restart timer with authPeriod // Even if there is error in processing, the authtimer timeout // should happen RESTART_TIMER (pPCB->hTimer, pPCB->EapolConfig.dwauthPeriod, "PCB", &dwRetCode); if (dwRetCode != NO_ERROR) { TRACE1 (EAPOL, "FSMAcquired: Error in RESTART_TIMER %ld", dwRetCode); break; } // Since an EAP Req-ID was received, reset EAPOL_Start count pPCB->ulStartCount = 0; // Flag that no EAPOL-Key transmit key was received pPCB->fTransmitKeyReceived = FALSE; // If current received EAP Id is the same the previous EAP Id // send the last EAPOL packet again if (((PPP_EAP_PACKET *)pEapolPkt->PacketBody)->Id == pPCB->dwPreviousId) { TRACE0 (EAPOL, "FSMAcquired: Re-xmitting EAP_Packet to port"); dwRetCode = ElWriteToPort (pPCB, (CHAR *)pPCB->pbPreviousEAPOLPkt, pPCB->dwSizeOfPreviousEAPOLPkt); if (dwRetCode != NO_ERROR) { TRACE1 (EAPOL, "FSMAcquired: Error in writing re-xmitted EAP_Packet to port %ld", dwRetCode); break; } } else { // Indicate to EAP-Dll to cleanup any leftovers from earlier // authentication. This is to take care of cases where errors // occured in the earlier authentication and cleanup wasn't done if ((dwRetCode = ElEapEnd (pPCB)) != NO_ERROR) { TRACE1 (EAPOL, "FSMAcquired: Error in ElEapEnd = %ld", dwRetCode); break; } // Process the EAP packet // ElEapWork will send out response if required if (( dwRetCode = ElEapWork ( pPCB, (PPP_EAP_PACKET *)pEapolPkt->PacketBody )) != NO_ERROR) { // Ignore error if UI is waiting for input if (dwRetCode != ERROR_IO_PENDING) { TRACE1 (EAPOL, "FSMAcquired: Error in ElEapWork %ld", dwRetCode); break; } else { dwRetCode = NO_ERROR; } } } TRACE1 (EAPOL, "Setting state ACQUIRED for port %ws", pPCB->pwszFriendlyName); SET_EAPOL_AUTH_TIMER(pPCB); DbLogPCBEvent (DBLOG_CATEG_INFO, pPCB, EAPOL_STATE_TRANSITION, EAPOLStates[((pPCB->State < EAPOLSTATE_LOGOFF) || (pPCB->State > EAPOLSTATE_AUTHENTICATED))?EAPOLSTATE_UNDEFINED:pPCB->State], EAPOLStates[EAPOLSTATE_ACQUIRED]); pPCB->State = EAPOLSTATE_ACQUIRED; // ElNetmanNotify (pPCB, EAPOL_NCS_CRED_REQUIRED, NULL); } while (FALSE); TRACE1 (EAPOL, "FSMAcquired completed for port %ws", pPCB->pwszFriendlyName); return dwRetCode; } // // FSMAuthenticating // // Description: // // Function called when an non EAP-Request/Identity packet is received on the // port. EAP processing of the data occurs. // // Arguments: // pPCB - Pointer to the PCB for the port on which data is being // processed // pEapolPkt - Pointer to EAPOL packet that was received // // Return values: // NO_ERROR - success // non-zero - error // DWORD FSMAuthenticating ( IN EAPOL_PCB *pPCB, IN EAPOL_PACKET *pEapolPkt ) { GUID DeviceGuid; DWORD dwRetCode = NO_ERROR; TRACE1 (EAPOL, "FSMAuthenticating entered for port %ws", pPCB->pwszFriendlyName); do { // Restart timer with authPeriod // Even if there is error in ElEapWork, the authtimer timeout // should happen RESTART_TIMER (pPCB->hTimer, pPCB->EapolConfig.dwauthPeriod, "PCB", &dwRetCode); if (dwRetCode != NO_ERROR) { TRACE1 (EAPOL, "FSMAuthenticating: Error in RESTART_TIMER %ld", dwRetCode); break; } // If current received EAP Id is the same the previous EAP Id // send the last EAPOL packet again // For EAPCODE_Success and EAPCODE_Failure, the value of id field // will not be increment, Refer to EAP RFC if ((((PPP_EAP_PACKET *)pEapolPkt->PacketBody)->Id == pPCB->dwPreviousId) && (((PPP_EAP_PACKET *)pEapolPkt->PacketBody)->Code != EAPCODE_Success) && (((PPP_EAP_PACKET *)pEapolPkt->PacketBody)->Code != EAPCODE_Failure)) { TRACE0 (EAPOL, "FSMAuthenticating: Re-xmitting EAP_Packet to port"); dwRetCode = ElWriteToPort (pPCB, (CHAR *)pPCB->pbPreviousEAPOLPkt, pPCB->dwSizeOfPreviousEAPOLPkt); if (dwRetCode != NO_ERROR) { TRACE1 (EAPOL, "FSMAuthenticating: Error in writing re-xmitted EAP_Packet to port = %ld", dwRetCode); break; } } else { // Process the EAP packet // ElEapWork will send out response if required if (( dwRetCode = ElEapWork ( pPCB, (PPP_EAP_PACKET *)pEapolPkt->PacketBody )) != NO_ERROR) { TRACE1 (EAPOL, "FSMAuthenticating: Error in ElEapWork %ld", dwRetCode); break; } } TRACE1 (EAPOL, "Setting state AUTHENTICATING for port %ws", pPCB->pwszFriendlyName); SET_EAPOL_AUTH_TIMER(pPCB); DbLogPCBEvent (DBLOG_CATEG_INFO, pPCB, EAPOL_STATE_TRANSITION, EAPOLStates[((pPCB->State < EAPOLSTATE_LOGOFF) || (pPCB->State > EAPOLSTATE_AUTHENTICATED))?EAPOLSTATE_UNDEFINED:pPCB->State], EAPOLStates[EAPOLSTATE_AUTHENTICATING]); pPCB->State = EAPOLSTATE_AUTHENTICATING; ElNetmanNotify (pPCB, EAPOL_NCS_AUTHENTICATING, NULL); } while (FALSE); TRACE1 (EAPOL, "FSMAuthenticating completed for port %ws", pPCB->pwszFriendlyName); return dwRetCode; } // // FSMHeld // // Description: // Function called when a EAP-Failure packet is received in the // Authenticating state. State machine is held for heldPeriod before // re-authentication can occur. // // Arguments: // pPCB - Pointer to the PCB for the port on which data is being // processed // // Return values: // NO_ERROR - success // non-zero - error // DWORD FSMHeld ( IN EAPOL_PCB *pPCB, IN EAPOL_PACKET *pEapolPkt ) { DWORD dwRetCode = NO_ERROR; TRACE1 (EAPOL, "FSMHeld entered for port %ws", pPCB->pwszFriendlyName); do { TRACE1 (EAPOL, "FSMHeld: EAP authentication failed with error 0x%x", pPCB->dwLocalEAPAuthResult); // Delete current credentials only if there is actually an error // in the EAP module during processing. // Ignore EAP-Failures arising out of session time-outs on AP, // backend, etc. if (pPCB->dwLocalEAPAuthResult != NO_ERROR) { pPCB->dwAuthFailCount++; TRACE1 (EAPOL, "Restarting Held timer with time value = %ld", pPCB->EapolConfig.dwheldPeriod); TRACE1 (EAPOL, "FSMHeld: Setting state HELD for port %ws", pPCB->pwszFriendlyName); // Free Identity buffer if (pPCB->pszIdentity != NULL) { FREE (pPCB->pszIdentity); pPCB->pszIdentity = NULL; } // Free Password buffer if (pPCB->PasswordBlob.pbData != NULL) { FREE (pPCB->PasswordBlob.pbData); pPCB->PasswordBlob.pbData = NULL; pPCB->PasswordBlob.cbData = 0; } // Free user-specific data in the PCB if (pPCB->pCustomAuthUserData != NULL) { FREE (pPCB->pCustomAuthUserData); pPCB->pCustomAuthUserData = NULL; } // Free connection data if (pPCB->pCustomAuthConnData != NULL) { FREE (pPCB->pCustomAuthConnData); pPCB->pCustomAuthConnData = NULL; } // Delete User Data stored in registry since it is invalid if (pPCB->pSSID != NULL) { if ((dwRetCode = ElDeleteEapUserInfo ( pPCB->hUserToken, pPCB->pwszDeviceGUID, pPCB->dwEapTypeToBeUsed, pPCB->pSSID->SsidLength, pPCB->pSSID->Ssid )) != NO_ERROR) { TRACE1 (EAPOL, "FSMHeld: ElDeleteEapUserInfo failed with error %ld", dwRetCode); dwRetCode = NO_ERROR; } } else { if ((dwRetCode = ElDeleteEapUserInfo ( pPCB->hUserToken, pPCB->pwszDeviceGUID, pPCB->dwEapTypeToBeUsed, 0, NULL )) != NO_ERROR) { TRACE1 (EAPOL, "FSMHeld: ElDeleteEapUserInfo failed with error %ld", dwRetCode); dwRetCode = NO_ERROR; } } // Since there has been an error in credentials, start afresh // the authentication. Credentials may have changed e.g. certs // may be renewed, MD5 credentials corrected etc. pPCB->fGotUserIdentity = FALSE; if (pPCB->hUserToken != NULL) { if (!CloseHandle (pPCB->hUserToken)) { dwRetCode = GetLastError (); TRACE1 (EAPOL, "FSMHeld: CloseHandle failed with error %ld", dwRetCode); dwRetCode = NO_ERROR; } } pPCB->hUserToken = NULL; DbLogPCBEvent (DBLOG_CATEG_ERR, pPCB, EAPOL_EAP_AUTHENTICATION_FAILED, pPCB->dwLocalEAPAuthResult); } else { if (pPCB->State == EAPOLSTATE_ACQUIRED) { DbLogPCBEvent (DBLOG_CATEG_ERR, pPCB, EAPOL_EAP_AUTHENTICATION_FAILED_ACQUIRED); } else { DbLogPCBEvent (DBLOG_CATEG_ERR, pPCB, EAPOL_EAP_AUTHENTICATION_FAILED_DEFAULT); } } DbLogPCBEvent (DBLOG_CATEG_INFO, pPCB, EAPOL_STATE_TRANSITION, EAPOLStates[((pPCB->State < EAPOLSTATE_LOGOFF) || (pPCB->State > EAPOLSTATE_AUTHENTICATED))?EAPOLSTATE_UNDEFINED:pPCB->State], EAPOLStates[EAPOLSTATE_HELD]); pPCB->State = EAPOLSTATE_HELD; TRACE1 (EAPOL, "FSMHeld: Port %ws set to HELD state", pPCB->pwszDeviceGUID); if (pPCB->dwLocalEAPAuthResult != NO_ERROR) { // If authfailed limit reached, go to Disconnected state if (pPCB->dwAuthFailCount >= pPCB->dwTotalMaxAuthFailCount) { TRACE2 (EAPOL, "FSMHeld: Fail count (%ld) > Max fail count (%ld)", pPCB->dwAuthFailCount, pPCB->dwTotalMaxAuthFailCount); FSMDisconnected (pPCB, NULL); break; } } SET_EAPOL_HELD_TIMER(pPCB); // Restart timer with heldPeriod RESTART_TIMER (pPCB->hTimer, pPCB->EapolConfig.dwheldPeriod, "PCB", &dwRetCode); if (dwRetCode != NO_ERROR) { TRACE1 (EAPOL, "FSMHeld: Error in RESTART_TIMER %ld", dwRetCode); break; } } while (FALSE); TRACE1 (EAPOL, "FSMHeld completed for port %ws", pPCB->pwszFriendlyName); return dwRetCode; } // // FSMAuthenticated // // Description: // // Function called when a EAP-Success packet is received or MaxStart // EAPOL_Startpackets have been sent out, but no EAP-Request/Identity // packets were received. If EAP-Success packet is request, DHCP client // is restarted to get a new IP address. // // Arguments: // pPCB - Pointer to the PCB for the port on which data is being // processed // pEapolPkt - Pointer to EAPOL packet that was received // // Return values: // NO_ERROR - success // non-zero - error // DWORD FSMAuthenticated ( IN EAPOL_PCB *pPCB, IN EAPOL_PACKET *pEapolPkt ) { DHCP_PNP_CHANGE DhcpPnpChange; WCHAR *pwszGUIDBuffer = NULL; BOOLEAN fReAuthenticatedWithSamePeer = FALSE; DWORD dwRetCode = NO_ERROR; TRACE1 (EAPOL, "FSMAuthenticated entered for port %ws", pPCB->pwszFriendlyName); do { // Shutdown earlier EAP session ElEapEnd (pPCB); // Call DHCP only if state machine went through authentication // If FSM is getting AUTHENTICATED by default, don't renew address // Also, if reauthentication is happening with same peer, namely in // wireless, don't renew address #if 0 if (pPCB->PhysicalMediumType == NdisPhysicalMediumWirelessLan) { if (!memcmp (pPCB->bDestMacAddr, pPCB->bPreviousDestMacAddr, SIZE_MAC_ADDR)) { fReAuthenticatedWithSamePeer = TRUE; } else { memcpy (pPCB->bPreviousDestMacAddr, pPCB->bDestMacAddr, SIZE_MAC_ADDR); } } #endif if ((pPCB->ulStartCount < pPCB->EapolConfig.dwmaxStart) && (!fReAuthenticatedWithSamePeer)) { if ((pwszGUIDBuffer = MALLOC ((wcslen(pPCB->pwszDeviceGUID) + 1)*sizeof(WCHAR))) == NULL) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break; } wcscpy (pwszGUIDBuffer, pPCB->pwszDeviceGUID); InterlockedIncrement (&g_lWorkerThreads); if (!QueueUserWorkItem ( (LPTHREAD_START_ROUTINE)ElIPPnPWorker, (PVOID)pwszGUIDBuffer, WT_EXECUTELONGFUNCTION )) { InterlockedDecrement (&g_lWorkerThreads); FREE (pwszGUIDBuffer); dwRetCode = GetLastError(); TRACE1 (PORT, "FSMAuthenticated: Critical error: QueueUserWorkItem failed with error %ld", dwRetCode); // Ignore DHCP error, it's outside 802.1X logic dwRetCode = NO_ERROR; } else { TRACE0 (PORT, "FSMAuthenticated: Queued ElIPPnPWorker"); } } TRACE1 (EAPOL, "Setting state AUTHENTICATED for port %ws", pPCB->pwszFriendlyName); DbLogPCBEvent (DBLOG_CATEG_INFO, pPCB, EAPOL_STATE_TRANSITION, EAPOLStates[((pPCB->State < EAPOLSTATE_LOGOFF) || (pPCB->State > EAPOLSTATE_AUTHENTICATED))?EAPOLSTATE_UNDEFINED:pPCB->State], EAPOLStates[EAPOLSTATE_AUTHENTICATED]); if (pPCB->fLocalEAPAuthSuccess) { DbLogPCBEvent (DBLOG_CATEG_INFO, pPCB, EAPOL_EAP_AUTHENTICATION_SUCCEEDED); } else { DbLogPCBEvent (DBLOG_CATEG_WARN, pPCB, EAPOL_EAP_AUTHENTICATION_DEFAULT); } pPCB->State = EAPOLSTATE_AUTHENTICATED; // In case of Wireless LAN ensure that there is EAPOL_Key packets // received for transmit key if (pPCB->PhysicalMediumType == NdisPhysicalMediumWirelessLan) { if ((dwRetCode = ElSetEAPOLKeyReceivedTimer (pPCB)) != NO_ERROR) { TRACE1 (EAPOL, "FSMAuthenticated: ElSetEAPOLKeyReceivedTimer failed with error %ld", dwRetCode); break; } } } while (FALSE); TRACE1 (EAPOL, "FSMAuthenticated completed for port %ws", pPCB->pwszFriendlyName); return dwRetCode; } // // FSMKeyReceive // // Description: // Function called when an EAPOL-Key packet is received. // The WEP key is decrypted and plumbed down to the NIC driver. // // Arguments: // pPCB - Pointer to the PCB for the port on which data is being // processed // pEapolPkt - Pointer to EAPOL packet that was received // // Return values: // NO_ERROR - success // non-zero - error // DWORD FSMKeyReceive ( IN EAPOL_PCB *pPCB, IN EAPOL_PACKET *pEapolPkt ) { EAPOL_KEY_DESC *pKeyDesc = NULL; DWORD dwRetCode = NO_ERROR; TRACE1 (EAPOL, "FSMKeyReceive entered for port %ws", pPCB->pwszFriendlyName); do { pKeyDesc = (EAPOL_KEY_DESC *)pEapolPkt->PacketBody; switch (pKeyDesc->DescriptorType) { case EAPOL_KEY_DESC_RC4: if ((dwRetCode = ElKeyReceiveRC4 (pPCB, pEapolPkt)) != NO_ERROR) { TRACE1 (EAPOL, "FSMKeyReceive: ElKeyReceiveRC4 failed with error %ld", dwRetCode); } break; #if 0 case EAPOL_KEY_DESC_PER_STA: if ((dwRetCode = ElKeyReceivePerSTA (pPCB, pEapolPkt)) != NO_ERROR) { TRACE1 (EAPOL, "FSMKeyReceive: ElKeyReceivePerSTA failed with error %ld", dwRetCode); } break; #endif default: dwRetCode = ERROR_INVALID_PARAMETER; TRACE1 (EAPOL, "FSMKeyReceive: Invalid DescriptorType (%ld)", pKeyDesc->DescriptorType); break; } } while (FALSE); if (dwRetCode != NO_ERROR) { DbLogPCBEvent (DBLOG_CATEG_ERR, pPCB, EAPOL_ERROR_PROCESSING_EAPOL_KEY, dwRetCode); } TRACE1 (EAPOL, "FSMKeyReceive completed for port %ws", pPCB->pwszFriendlyName); return dwRetCode; } // // ElKeyReceiveRC4 // // Description: // Function called when an EAPOL-Key packet is received // with RC4 DescriptorType // // Arguments: // pPCB - Pointer to the PCB for the port on which data is being // processed // pEapolPkt - Pointer to EAPOL packet that was received // // Return values: // NO_ERROR - success // non-zero - error // DWORD ElKeyReceiveRC4 ( IN EAPOL_PCB *pPCB, IN EAPOL_PACKET *pEapolPkt ) { EAPOL_KEY_DESC *pKeyDesc = NULL; ULONGLONG ullReplayCheck = 0; BYTE bReplayCheck[8]; BYTE *pbMD5EapolPkt = NULL; DWORD dwMD5EapolPktLen = 0; DWORD dwEapPktLen = 0; DWORD dwIndex = 0; BYTE bHMACMD5HashBuffer[MD5DIGESTLEN]; RC4_KEYSTRUCT rc4key; BYTE bKeyBuffer[48]; BYTE *pbKeyToBePlumbed = NULL; DWORD dwKeyLength = 0; NDIS_802_11_WEP *pNdisWEPKey = NULL; BYTE *pbMPPESendKey = NULL, *pbMPPERecvKey = NULL; DWORD dwMPPESendKeyLength = 0, dwMPPERecvKeyLength = 0; DWORD dwRetCode = NO_ERROR; TRACE1 (EAPOL, "ElKeyReceiveRC4 entered for port %ws", pPCB->pwszFriendlyName); do { if (WireToHostFormat16 (pEapolPkt->PacketBodyLength) < FIELD_OFFSET (EAPOL_KEY_DESC, Key)) { TRACE0 (EAPOL, "ElKeyReceiveRC4: Invalid EAPOL-Key packet"); dwRetCode = ERROR_INVALID_PACKET; break; } pKeyDesc = (EAPOL_KEY_DESC *)pEapolPkt->PacketBody; dwKeyLength = WireToHostFormat16 (pKeyDesc->KeyLength); if (WireToHostFormat16 (pEapolPkt->PacketBodyLength) > sizeof(EAPOL_KEY_DESC)) { if (dwKeyLength != (WireToHostFormat16 (pEapolPkt->PacketBodyLength) - FIELD_OFFSET(EAPOL_KEY_DESC, Key))) { TRACE1 (EAPOL, "ElKeyReceiveRC4: Invalid Key Length in packet (%ld", dwKeyLength); dwRetCode = ERROR_INVALID_PACKET; break; } } TRACE2 (EAPOL, "KeyLength = %ld, \n KeyIndex = %ld", dwKeyLength, pKeyDesc->KeyIndex ); memcpy ((BYTE *)bReplayCheck, (BYTE *)pKeyDesc->ReplayCounter, 8*sizeof(BYTE)); ullReplayCheck = ((((ULONGLONG)(*((PBYTE)(bReplayCheck)+0))) << 56) + (((ULONGLONG)(*((PBYTE)(bReplayCheck)+1))) << 48) + (((ULONGLONG)(*((PBYTE)(bReplayCheck)+2))) << 40) + (((ULONGLONG)(*((PBYTE)(bReplayCheck)+3))) << 32) + (((ULONGLONG)(*((PBYTE)(bReplayCheck)+4))) << 24) + (((ULONGLONG)(*((PBYTE)(bReplayCheck)+5))) << 16) + (((ULONGLONG)(*((PBYTE)(bReplayCheck)+6))) << 8) + (((ULONGLONG)(*((PBYTE)(bReplayCheck)+7))))); // // Check validity of Key message using the ReplayCounter field // Verify if it is in sync with the last ReplayCounter value // received // // TRACE0 (EAPOL, "ElKeyReceiveRC4: Original replay counter in desc ======"); // EAPOL_DUMPBA (pKeyDesc->ReplayCounter, 8); // TRACE0 (EAPOL, "ElKeyReceiveRC4: Converted incoming Replay counter ======= "); // EAPOL_DUMPBA ((BYTE *)&ullReplayCheck, 8); // TRACE0 (EAPOL, "ElKeyReceiveRC4: Last Replay counter ======= "); // EAPOL_DUMPBA ((BYTE *)&(pPCB->ullLastReplayCounter), 8); if (ullReplayCheck <= pPCB->ullLastReplayCounter) { TRACE0 (EAPOL, "ElKeyReceiveRC4: Replay counter is not in sync, something is wrong"); DbLogPCBEvent (DBLOG_CATEG_ERR, pPCB, EAPOL_INVALID_EAPOL_KEY); break; } // If valid ReplayCounter, save it in the PCB for future check pPCB->ullLastReplayCounter = ullReplayCheck; // // Verify if the MD5 hash generated on the EAPOL packet, // with Signature nulled out, is the same as the signature // Use the MPPERecv key as the secret // dwEapPktLen = WireToHostFormat16 (pEapolPkt->PacketBodyLength); dwMD5EapolPktLen = sizeof (EAPOL_PACKET) - sizeof(pEapolPkt->EthernetType) - 1 + dwEapPktLen; if ((pbMD5EapolPkt = (BYTE *) MALLOC (dwMD5EapolPktLen)) == NULL) { TRACE0 (EAPOL, "ElKeyReceiveRC4: Error in MALLOC for pbMD5EapolPkt"); dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break; } memcpy ((BYTE *)pbMD5EapolPkt, (BYTE *)pEapolPkt+sizeof(pEapolPkt->EthernetType), dwMD5EapolPktLen); // Access the Master Send and Recv key stored locally if ((dwRetCode = ElSecureDecodePw ( &(pPCB->MasterSecretSend), &(pbMPPESendKey), &dwMPPESendKeyLength )) != NO_ERROR) { TRACE1 (EAPOL, "ElKeyReceiveRC4: ElSecureDecodePw failed for MasterSecretSend with error %ld", dwRetCode); break; } if ((dwRetCode = ElSecureDecodePw ( &(pPCB->MasterSecretRecv), &(pbMPPERecvKey), &dwMPPERecvKeyLength )) != NO_ERROR) { TRACE1 (EAPOL, "ElKeyReceiveRC4: ElSecureDecodePw failed for MasterSecretRecv with error %ld", dwRetCode); break; } // // Null out the signature in the key descriptor copy, to calculate // the hash on the supplicant side // ZeroMemory ((BYTE *)(pbMD5EapolPkt - sizeof(pEapolPkt->EthernetType) + sizeof(EAPOL_PACKET) - 1 + // pEapolPkt->Body sizeof(EAPOL_KEY_DESC)- // End of EAPOL_KEY_DESC MD5DIGESTLEN-1), // Signature field MD5DIGESTLEN); (VOID) ElGetHMACMD5Digest ( pbMD5EapolPkt, dwMD5EapolPktLen, pbMPPERecvKey, dwMPPERecvKeyLength, bHMACMD5HashBuffer ); // TRACE0 (EAPOL, "ElKeyReceiveRC4: MD5 Hash body =="); // EAPOL_DUMPBA (pbMD5EapolPkt, dwMD5EapolPktLen); // TRACE0 (EAPOL, "ElKeyReceiveRC4: MD5 Hash secret =="); // EAPOL_DUMPBA (pbMPPERecvKey, dwMPPERecvKeyLength); // TRACE0 (EAPOL, "ElKeyReceiveRC4: MD5 Hash generated by Supplicant"); // EAPOL_DUMPBA (bHMACMD5HashBuffer, MD5DIGESTLEN); // TRACE0 (EAPOL, "ElKeyReceiveRC4: Signature sent in EAPOL_KEY_DESC"); // EAPOL_DUMPBA (pKeyDesc->KeySignature, MD5DIGESTLEN); // // Check if HMAC-MD5 hash in received packet is what is expected // if (memcmp (bHMACMD5HashBuffer, pKeyDesc->KeySignature, MD5DIGESTLEN) != 0) { TRACE0 (EAPOL, "ElKeyReceiveRC4: Signature in Key Desc does not match"); DbLogPCBEvent (DBLOG_CATEG_ERR, pPCB, EAPOL_INVALID_EAPOL_KEY); break; } // // Decrypt the multicast WEP key if it has been provided // // Check if there is Key Material (5/16 bytes) at the end of // the Key Descriptor if (WireToHostFormat16 (pEapolPkt->PacketBodyLength) > sizeof (EAPOL_KEY_DESC)) { memcpy ((BYTE *)bKeyBuffer, (BYTE *)pKeyDesc->Key_IV, 16); memcpy ((BYTE *)&bKeyBuffer[16], (BYTE *)pbMPPESendKey, dwMPPESendKeyLength); rc4_key (&rc4key, 16 + dwMPPESendKeyLength, bKeyBuffer); rc4 (&rc4key, dwKeyLength, pKeyDesc->Key); // TRACE0 (EAPOL, " ========= The multicast key is ============= "); // EAPOL_DUMPBA (pKeyDesc->Key, dwKeyLength); // Use the unencrypted key in the Key Desc as the encryption key pbKeyToBePlumbed = pKeyDesc->Key; } else { if (dwKeyLength > dwMPPESendKeyLength) { TRACE1 (EAPOL, "ElKeyReceiveRC4: Invalid Key Length in packet (%ld", dwKeyLength); dwRetCode = ERROR_INVALID_PACKET; break; } // Use the MPPESend key as the encryption key pbKeyToBePlumbed = (BYTE *)pbMPPESendKey; } if ((pNdisWEPKey = MALLOC ( sizeof(NDIS_802_11_WEP)-1+dwKeyLength )) == NULL) { TRACE0 (EAPOL, "ElKeyReceiveRC4: MALLOC failed for pNdisWEPKey"); dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break; } pNdisWEPKey->Length = sizeof(NDIS_802_11_WEP) - 1 + dwKeyLength; memcpy ((BYTE *)pNdisWEPKey->KeyMaterial, (BYTE *)pbKeyToBePlumbed, dwKeyLength); pNdisWEPKey->KeyLength = dwKeyLength; // Create the long index out of the byte index got from AP // If MSB in byte is set, set MSB in ulong format if (pKeyDesc->KeyIndex & 0x80) { pNdisWEPKey->KeyIndex = 0x80000000; } else { pNdisWEPKey->KeyIndex = 0x00000000; } pNdisWEPKey->KeyIndex |= (pKeyDesc->KeyIndex & 0x03); // TRACE1 (ANY, "ElKeyReceiveRC4: Key Index is %x", pNdisWEPKey->KeyIndex); // Flag that transmit key was received if (pKeyDesc->KeyIndex & 0x80) { pPCB->fTransmitKeyReceived = TRUE; } // Use NDISUIO to plumb the key to the driver if ((dwRetCode = ElNdisuioSetOIDValue ( pPCB->hPort, OID_802_11_ADD_WEP, (BYTE *)pNdisWEPKey, pNdisWEPKey->Length)) != NO_ERROR) { TRACE1 (PORT, "ElKeyReceiveRC4: ElNdisuioSetOIDValue failed with error %ld", dwRetCode); } } while (FALSE); if (dwRetCode != NO_ERROR) { DbLogPCBEvent (DBLOG_CATEG_ERR, pPCB, EAPOL_ERROR_PROCESSING_EAPOL_KEY, dwRetCode); } if (pbMD5EapolPkt != NULL) { FREE (pbMD5EapolPkt); pbMD5EapolPkt = NULL; } if (pNdisWEPKey != NULL) { FREE (pNdisWEPKey); pNdisWEPKey = NULL; } if (pbMPPESendKey != NULL) { FREE (pbMPPESendKey); } if (pbMPPERecvKey != NULL) { FREE (pbMPPERecvKey); } TRACE1 (EAPOL, "ElKeyReceiveRC4 completed for port %ws", pPCB->pwszFriendlyName); return dwRetCode; } #if 0 // // ElKeyReceivePerSTA // // Description: // Function called when an EAPOL-Key packet is received // with PerSTA DescriptorType // // Arguments: // pPCB - Pointer to the PCB for the port on which data is being // processed // pEapolPkt - Pointer to EAPOL packet that was received // // Return values: // NO_ERROR - success // non-zero - error // DWORD ElKeyReceivePerSTA ( IN EAPOL_PCB *pPCB, IN EAPOL_PACKET *pEapolPkt ) { EAPOL_KEY_DESC *pKeyDesc = NULL; ULONGLONG ullReplayCheck = 0; BYTE bReplayCheck[8]; BYTE *pbMD5EapolPkt = NULL; DWORD dwMD5EapolPktLen = 0; DWORD dwEapPktLen = 0; DWORD dwIndex = 0; BYTE bHMACMD5HashBuffer[MD5DIGESTLEN]; RC4_KEYSTRUCT rc4key; BYTE bKeyBuffer[48]; BYTE *pbKeyToBePlumbed = NULL; DWORD dwRandomLength = 0; NDIS_802_11_WEP *pNdisWEPKey = NULL; BYTE *pbMasterSecretSend = NULL; DWORD dwMasterSecretSendLength = 0; BYTE *pbMasterSecretRecv = NULL; DWORD dwMasterSecretRecvLength = 0; BYTE *pbDynamicSendKey = NULL, *pbDynamicRecvKey = NULL; DWORD dwDynamicKeyLength = 0; EAPOL_KEY_MATERIAL *pEapolKeyMaterial = NULL; PBYTE pbPaddedKeyMaterial = NULL; BOOLEAN fIsUnicastKey = FALSE; SESSION_KEYS OldSessionKeys = {0}; SESSION_KEYS NewSessionKeys = {0}; DWORD dwRetCode = NO_ERROR; TRACE1 (EAPOL, "ElKeyReceivePerSTA entered for port %ws", pPCB->pwszFriendlyName); do { pKeyDesc = (EAPOL_KEY_DESC *)pEapolPkt->PacketBody; dwDynamicKeyLength = WireToHostFormat16 (pKeyDesc->KeyLength); // TRACE2 (EAPOL, "ElKeyReceivePerSTA: KeyLength = %ld, \n KeyIndex = %0x", // dwDynamicKeyLength, // pKeyDesc->KeyIndex // ); memcpy ((BYTE *)bReplayCheck, (BYTE *)pKeyDesc->ReplayCounter, 8*sizeof(BYTE)); ullReplayCheck = ((((ULONGLONG)(*((PBYTE)(bReplayCheck)+0))) << 56) + (((ULONGLONG)(*((PBYTE)(bReplayCheck)+1))) << 48) + (((ULONGLONG)(*((PBYTE)(bReplayCheck)+2))) << 40) + (((ULONGLONG)(*((PBYTE)(bReplayCheck)+3))) << 32) + (((ULONGLONG)(*((PBYTE)(bReplayCheck)+4))) << 24) + (((ULONGLONG)(*((PBYTE)(bReplayCheck)+5))) << 16) + (((ULONGLONG)(*((PBYTE)(bReplayCheck)+6))) << 8) + (((ULONGLONG)(*((PBYTE)(bReplayCheck)+7))))); // Check validity of Key message using the ReplayCounter field // Verify if it is in sync with the last ReplayCounter value // received // TRACE0 (EAPOL, "Original replay counter in desc ======"); // EAPOL_DUMPBA (pKeyDesc->ReplayCounter, 8); // TRACE0 (EAPOL, "Converted incoming Replay counter ======= "); // EAPOL_DUMPBA ((BYTE *)&ullReplayCheck, 8); // TRACE0 (EAPOL, "Last Replay counter ======= "); // EAPOL_DUMPBA ((BYTE *)&(pPCB->ullLastReplayCounter), 8); if (ullReplayCheck <= pPCB->ullLastReplayCounter) { TRACE0 (EAPOL, "ElKeyReceivePerSTA: Replay counter is not in sync, something is wrong"); DbLogPCBEvent (DBLOG_CATEG_ERR, pPCB, EAPOL_INVALID_EAPOL_KEY); break; } // If valid ReplayCounter, save it in the PCB for future check pPCB->ullLastReplayCounter = ullReplayCheck; // Verify if the MD5 hash generated on the EAPOL packet, // with Signature nulled out, is the same as the signature // Use the MPPERecv key as the secret dwEapPktLen = WireToHostFormat16 (pEapolPkt->PacketBodyLength); dwMD5EapolPktLen = sizeof (EAPOL_PACKET) - sizeof(pEapolPkt->EthernetType) - 1 + dwEapPktLen; if ((pbMD5EapolPkt = (BYTE *) MALLOC (dwMD5EapolPktLen)) == NULL) { TRACE0 (EAPOL, "ElKeyReceivePerSTA: Error in MALLOC for pbMD5EapolPkt"); dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break; } memcpy ((BYTE *)pbMD5EapolPkt, (BYTE *)pEapolPkt+sizeof(pEapolPkt->EthernetType), dwMD5EapolPktLen); // Query Master Secrets if (dwRetCode = ElQueryMasterKeys ( pPCB, &OldSessionKeys ) != NO_ERROR) { TRACE1 (EAPOL, "ElKeyReceivePerSTA: ElQueryMasterKeys failed with error %ld", dwRetCode); break; } pbMasterSecretSend = OldSessionKeys.bSendKey; pbMasterSecretRecv = OldSessionKeys.bReceiveKey; dwMasterSecretSendLength = OldSessionKeys.dwKeyLength; dwMasterSecretRecvLength = OldSessionKeys.dwKeyLength; // Null out the signature in the key descriptor copy, to calculate // the hash on the supplicant side ZeroMemory ((BYTE *)(pbMD5EapolPkt - sizeof(pEapolPkt->EthernetType) + sizeof(EAPOL_PACKET) - 1 + // pEapolPkt->Body sizeof(EAPOL_KEY_DESC)- // End of EAPOL_KEY_DESC MD5DIGESTLEN-1), // Signature field MD5DIGESTLEN); (VOID) ElGetHMACMD5Digest ( pbMD5EapolPkt, dwMD5EapolPktLen, pbMasterSecretRecv, dwMasterSecretRecvLength, bHMACMD5HashBuffer ); // TRACE0 (EAPOL, "ElKeyReceivePerSTA: MD5 Hash body =="); // EAPOL_DUMPBA (pbMD5EapolPkt, dwMD5EapolPktLen); // TRACE0 (EAPOL, "ElKeyReceivePerSTA: MD5 Hash secret =="); // EAPOL_DUMPBA (pbMasterSecretRecv, dwMasterSecretRecvLength); // TRACE0 (EAPOL, "ElKeyReceivePerSTA: MD5 Hash generated by Supplicant"); // EAPOL_DUMPBA (bHMACMD5HashBuffer, MD5DIGESTLEN); // TRACE0 (EAPOL, "ElKeyReceivePerSTA: Signature sent in EAPOL_KEY_DESC"); // EAPOL_DUMPBA (pKeyDesc->KeySignature, MD5DIGESTLEN); // Check if HMAC-MD5 hash in received packet is what is expected if (memcmp (bHMACMD5HashBuffer, pKeyDesc->KeySignature, MD5DIGESTLEN) != 0) { TRACE0 (EAPOL, "ElKeyReceivePerSTA: Signature in Key Descriptor does not match"); DbLogPCBEvent (DBLOG_CATEG_ERR, pPCB, EAPOL_INVALID_EAPOL_KEY); break; } if (pKeyDesc->KeyIndex & 0x80) { fIsUnicastKey = TRUE; } // Decrypt the random value if it has been provided if (WireToHostFormat16 (pEapolPkt->PacketBodyLength) > sizeof (EAPOL_KEY_DESC)) { DWORD dwKeyMaterialLength = 0; dwKeyMaterialLength = WireToHostFormat16 (pEapolPkt->PacketBodyLength) - FIELD_OFFSET(EAPOL_KEY_DESC, Key); // TRACE1 (EAPOL, "ElKeyReceivePerSTA: KeyMaterialLength = %ld", // dwKeyMaterialLength); memcpy ((BYTE *)bKeyBuffer, (BYTE *)pKeyDesc->Key_IV, KEY_IV_LENGTH); memcpy ((BYTE *)&bKeyBuffer[KEY_IV_LENGTH], (BYTE *)pbMasterSecretSend, dwMasterSecretSendLength); pEapolKeyMaterial = (PEAPOL_KEY_MATERIAL)pKeyDesc->Key; dwRandomLength = WireToHostFormat16 (pEapolKeyMaterial->KeyMaterialLength); if ((pbPaddedKeyMaterial = (PBYTE)MALLOC (RC4_PAD_LENGTH + dwKeyMaterialLength)) == NULL) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break; } memcpy (pbPaddedKeyMaterial+RC4_PAD_LENGTH, pEapolKeyMaterial->KeyMaterial, dwKeyMaterialLength); rc4_key (&rc4key, KEY_IV_LENGTH+dwMasterSecretSendLength, bKeyBuffer); rc4 (&rc4key, dwKeyMaterialLength+RC4_PAD_LENGTH, pbPaddedKeyMaterial); // Ignore leading padded RC4_PAD_LENGTH bytes memcpy (pEapolKeyMaterial->KeyMaterial, pbPaddedKeyMaterial+RC4_PAD_LENGTH, dwKeyMaterialLength); // TRACE1 (EAPOL, "ElKeyReceivePerSTA: Randomlength = %ld", // dwRandomLength); // TRACE0 (EAPOL, "ElKeyReceivePerSTA: ========= The random material is ============= "); // EAPOL_DUMPBA (pEapolKeyMaterial->KeyMaterial, dwRandomLength); } else { // No random material sent TRACE0 (EAPOL, "ElKeyReceivePerSTA: Did not find random material: Exiting"); dwRetCode = ERROR_INVALID_PARAMETER; break; } if (fIsUnicastKey) { TRACE0 (EAPOL, "ElKeyReceivePerSTA: Received Per-STA Unicast key material Random"); // Generate dynamic keys if (dwRetCode = GenerateDynamicKeys ( pbMasterSecretSend, dwMasterSecretSendLength, pEapolKeyMaterial->KeyMaterial, dwRandomLength, dwDynamicKeyLength, &NewSessionKeys ) != NO_ERROR) { TRACE1 (EAPOL, "ElKeyReceivePerSTA: ElGenerateDynamicKeys failed with error %ld", dwRetCode); break; } pbDynamicSendKey = NewSessionKeys.bSendKey; pbDynamicRecvKey = NewSessionKeys.bReceiveKey; // TRACE0 (EAPOL, "ElKeyReceivePerSTA: Derived Send Key"); // EAPOL_DUMPBA (pbDynamicSendKey, dwDynamicKeyLength); // TRACE0 (EAPOL, "ElKeyReceivePerSTA: Derived Recv Key"); // EAPOL_DUMPBA (pbDynamicRecvKey, dwDynamicKeyLength); // Update Master Secrets if (dwRetCode = ElSetMasterKeys ( pPCB, &NewSessionKeys ) != NO_ERROR) { // Cannot do much about this error than proceed TRACE1 (EAPOL, "ElKeyReceivePerSTA: ElSetMasterKeys failed with error %ld", dwRetCode); dwRetCode = NO_ERROR; } pbKeyToBePlumbed = pbDynamicSendKey; } else { TRACE0 (EAPOL, "ElKeyReceivePerSTA: Received Per-STA BROADCAST key material"); if (dwRandomLength != dwDynamicKeyLength) { TRACE2 (EAPOL, "ElKeyReceivePerSTA: KeyLength (%ld) != KeyMaterialLength (%ld), Inconsistent. Will consider only KeyMaterial length !", dwDynamicKeyLength, dwRandomLength); } dwDynamicKeyLength = dwRandomLength; pbKeyToBePlumbed = pEapolKeyMaterial->KeyMaterial; } if ((pNdisWEPKey = MALLOC ( sizeof(NDIS_802_11_WEP)-1+dwDynamicKeyLength )) == NULL) { TRACE0 (EAPOL, "ElKeyReceivePerSTA: MALLOC failed for pNdisWEPKey"); dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break; } pNdisWEPKey->Length = sizeof(NDIS_802_11_WEP) - 1 + dwDynamicKeyLength; memcpy ((BYTE *)pNdisWEPKey->KeyMaterial, (BYTE *)pbKeyToBePlumbed, dwDynamicKeyLength); pNdisWEPKey->KeyLength = dwDynamicKeyLength; // Create the long index out of the byte index got from AP // If MSB in byte is set, set MSB in ulong format if (pKeyDesc->KeyIndex & 0x80) { pNdisWEPKey->KeyIndex = 0x80000000; } else { pNdisWEPKey->KeyIndex = 0x00000000; } pNdisWEPKey->KeyIndex |= (pKeyDesc->KeyIndex & 0x03); // Use NDISUIO to plumb the key to the driver if ((dwRetCode = ElNdisuioSetOIDValue ( pPCB->hPort, OID_802_11_ADD_WEP, (BYTE *)pNdisWEPKey, pNdisWEPKey->Length)) != NO_ERROR) { TRACE1 (PORT, "ElKeyReceivePerSTA: ElNdisuioSetOIDValue failed with error %ld", dwRetCode); } } while (FALSE); if (dwRetCode != NO_ERROR) { DbLogPCBEvent (DBLOG_CATEG_ERR, pPCB, EAPOL_ERROR_PROCESSING_EAPOL_KEY, dwRetCode); } if (pbMD5EapolPkt != NULL) { FREE (pbMD5EapolPkt); pbMD5EapolPkt = NULL; } if (pNdisWEPKey != NULL) { FREE (pNdisWEPKey); pNdisWEPKey = NULL; } if (pbPaddedKeyMaterial != NULL) { FREE (pbPaddedKeyMaterial); } TRACE1 (EAPOL, "ElKeyReceivePerSTA completed for port %ws", pPCB->pwszFriendlyName); return dwRetCode; } #endif // // ElTimeoutCallbackRoutine // // Description: // // Function called when any timer work item queued on the global timer // queue expires. Depending on the state in which the port is when the timer // expires, the port moves to the next state. // // Arguments: // pvContext - Pointer to context. In this case, it is pointer to a PCB // fTimerOfWaitFired - Unused // // Return values: // VOID ElTimeoutCallbackRoutine ( IN PVOID pvContext, IN BOOLEAN fTimerOfWaitFired ) { EAPOL_PCB *pPCB; TRACE0 (EAPOL, "ElTimeoutCallbackRoutine entered"); do { // Context should not be NULL if (pvContext == NULL) { TRACE0 (EAPOL, "ElTimeoutCallbackRoutine: pvContext is NULL. Invalid timeout callback"); break; } // PCB is guaranteed to exist until all timers are fired // Verify if Port is still active pPCB = (EAPOL_PCB *)pvContext; ACQUIRE_WRITE_LOCK (&(pPCB->rwLock)); if (!EAPOL_PORT_ACTIVE(pPCB)) { // Port is not active RELEASE_WRITE_LOCK (&(pPCB->rwLock)); TRACE1 (PORT, "ElTimeoutCallbackRoutine: Port %ws is inactive", pPCB->pwszDeviceGUID); break; } DbLogPCBEvent (DBLOG_CATEG_INFO, pPCB, EAPOL_STATE_TIMEOUT, EAPOLStates[((pPCB->State < EAPOLSTATE_LOGOFF) || (pPCB->State > EAPOLSTATE_AUTHENTICATED))?EAPOLSTATE_UNDEFINED:pPCB->State]); // Check the current state of the state machine // We can do additional checks such as flagging which timer was fired // and in the timeout checking if the PCB state has remained the same // Else bail out switch (pPCB->State) { case EAPOLSTATE_CONNECTING: if (!EAPOL_START_TIMER_SET(pPCB)) { TRACE1 (EAPOL, "ElTimeoutCallbackRoutine: Wrong timeout %ld in Connecting state", CHECK_EAPOL_TIMER(pPCB)); break; } pPCB->dwTimerFlags &= ~EAPOL_START_TIMER; FSMConnecting(pPCB, NULL); break; case EAPOLSTATE_ACQUIRED: if (!EAPOL_AUTH_TIMER_SET(pPCB)) { TRACE1 (EAPOL, "ElTimeoutCallbackRoutine: Wrong timeout %ld in Acquired state", CHECK_EAPOL_TIMER(pPCB)); break; } pPCB->dwTimerFlags &= ~EAPOL_AUTH_TIMER; FSMConnecting(pPCB, NULL); break; case EAPOLSTATE_AUTHENTICATING: if (!EAPOL_AUTH_TIMER_SET(pPCB)) { TRACE1 (EAPOL, "ElTimeoutCallbackRoutine: Wrong timeout %ld in Authenticating state", CHECK_EAPOL_TIMER(pPCB)); break; } pPCB->dwTimerFlags &= ~EAPOL_AUTH_TIMER; FSMConnecting(pPCB, NULL); break; case EAPOLSTATE_AUTHENTICATED: if (!EAPOL_TRANSMIT_KEY_TIMER_SET(pPCB)) { TRACE1 (EAPOL, "ElTimeoutCallbackRoutine: Wrong timeout %ld in Authenticated state", CHECK_EAPOL_TIMER(pPCB)); break; } pPCB->dwTimerFlags &= ~EAPOL_TRANSMIT_KEY_TIMER; ElVerifyEAPOLKeyReceived(pPCB); break; case EAPOLSTATE_HELD: if (!EAPOL_HELD_TIMER_SET(pPCB)) { TRACE1 (EAPOL, "ElTimeoutCallbackRoutine: Wrong timeout %ld in Held state", CHECK_EAPOL_TIMER(pPCB)); break; } // Go through logoff, since new user will be tried // for next cycle // Debatable ! if (!(pPCB->dwAuthFailCount % EAPOL_MAX_AUTH_FAIL_COUNT)) { // FSMLogoff (pPCB, NULL); } FSMConnecting(pPCB, NULL); break; case EAPOLSTATE_DISCONNECTED: TRACE0 (EAPOL, "ElTimeoutCallbackRoutine: No action in Disconnected state"); break; case EAPOLSTATE_LOGOFF: TRACE0 (EAPOL, "ElTimeoutCallbackRoutine: No action in Logoff state"); break; default: TRACE0 (EAPOL, "ElTimeoutCallbackRoutine: Critical Error. Invalid state after timer expires "); break; } RELEASE_WRITE_LOCK (&(pPCB->rwLock)); } while (FALSE); TRACE0 (EAPOL, "ElTimeoutCallbackRoutine completed"); return; } // // ElEapWork // // Description: // // Function called when an EAPOL packet of type EAP_Packet is received // The EAP packet is passed to the EAP module for processing. // Depending on the result of the processing, a EAP Response packet // is sent or the incoming packet is ignored. // // Input arguments: // pPCB - Pointer to PCB for the port on which data is being processed // pRecvPkt - Pointer to EAP packet in the data received from the remote end // // Return values: // NO_ERROR - success // non-zero - error // // // ISSUE: Rewrite with do {} while(FALSE) // DWORD ElEapWork ( IN EAPOL_PCB *pPCB, IN PPP_EAP_PACKET *pRecvPkt ) { DWORD dwLength = 0; ELEAP_RESULT EapResult; PPP_EAP_PACKET *pSendPkt; EAPOL_PACKET *pEapolPkt; GUID DeviceGuid; DWORD dwReceivedId = 0; DWORD cbData = 0; BYTE *pbAuthData = NULL; DWORD dwRetCode = NO_ERROR; // // If the protocol has not been started yet, call ElEapBegin // if (!(pPCB->fEapInitialized)) { if ((dwRetCode = ElEapBegin (pPCB)) != NO_ERROR) { TRACE1 (EAPOL, "ElEapWork: Error in ElEapBegin = %ld", dwRetCode); return dwRetCode; } } ZeroMemory(&EapResult, sizeof(EapResult)); // Create buffer for EAPOL + EAP and pass pointer to EAP header pEapolPkt = (EAPOL_PACKET *) MALLOC (MAX_EAPOL_BUFFER_SIZE); TRACE1 (EAPOL, "ElEapWork: EapolPkt created at %p", pEapolPkt); if (pEapolPkt == NULL) { TRACE0 (EAPOL, "ElEapWork: Error allocating EAP buffer"); dwRetCode = ERROR_NOT_ENOUGH_MEMORY; return dwRetCode; } // Point to EAP header pSendPkt = (PPP_EAP_PACKET *)((PBYTE)pEapolPkt + sizeof (EAPOL_PACKET) - 1); if (pRecvPkt != NULL) { dwReceivedId = pRecvPkt->Id; } dwRetCode = ElEapMakeMessage (pPCB, pRecvPkt, pSendPkt, MAX_EAPOL_BUFFER_SIZE - sizeof(EAPOL_PACKET) - 1, &EapResult ); // Notification message for the user if (NULL != EapResult.pszReplyMessage) { // Free earlier notication with the PCB if (pPCB->pwszEapReplyMessage != NULL) { FREE (pPCB->pwszEapReplyMessage); pPCB->pwszEapReplyMessage = NULL; } pPCB->pwszEapReplyMessage = (WCHAR *)MALLOC ((strlen(EapResult.pszReplyMessage)+1) * sizeof(WCHAR)); if (pPCB->pwszEapReplyMessage == NULL) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; TRACE0 (EAPOL, "ElEapWork: MALLOC failed for pwszEapReplyMessage"); FREE (EapResult.pszReplyMessage); FREE (pEapolPkt); pEapolPkt = NULL; return dwRetCode; } if (0 == MultiByteToWideChar ( CP_ACP, 0, EapResult.pszReplyMessage, -1, pPCB->pwszEapReplyMessage, strlen(EapResult.pszReplyMessage)+1)) { dwRetCode = GetLastError(); TRACE2 (EAPOL,"ElEapWork: MultiByteToWideChar(%s) failed for pwszEapReplyMessage with error (%ld)", EapResult.pszReplyMessage, dwRetCode); FREE (EapResult.pszReplyMessage); FREE (pEapolPkt); pEapolPkt = NULL; return dwRetCode; } ElNetmanNotify (pPCB, EAPOL_NCS_NOTIFICATION, NULL); TRACE1 (EAPOL, "ElEapWork: Notified user of EAP data = %ws", pPCB->pwszEapReplyMessage); FREE (EapResult.pszReplyMessage); } if (dwRetCode != NO_ERROR) { switch (dwRetCode) { case ERROR_PPP_INVALID_PACKET: TRACE0 (EAPOL, "ElEapWork: Silently discarding invalid auth packet"); break; default: TRACE1 (EAPOL, "ElEapWork: ElEapMakeMessage returned error %ld", dwRetCode); // NotifyCallerOfFailure (pPCB, dwRetCode); break; } // Free up memory reserved for packet FREE (pEapolPkt); pEapolPkt = NULL; return dwRetCode; } // // Check to see if we have to save any user data // if (EapResult.fSaveUserData) { // Save to Registry if ((dwRetCode = ElSetEapUserInfo ( pPCB->hUserToken, pPCB->pwszDeviceGUID, pPCB->dwEapTypeToBeUsed, (pPCB->pSSID)?pPCB->pSSID->SsidLength:0, (pPCB->pSSID)?pPCB->pSSID->Ssid:NULL, EapResult.pUserData, EapResult.dwSizeOfUserData)) != NO_ERROR) { TRACE1 (EAPOL, "ElEapWork: ElSetEapUserInfo failed with error = %d", dwRetCode); if (pEapolPkt != NULL) { FREE (pEapolPkt); pEapolPkt = NULL; } return dwRetCode; } // Save to PCB context if (pPCB->pCustomAuthUserData != NULL) { FREE (pPCB->pCustomAuthUserData); pPCB->pCustomAuthUserData = NULL; } pPCB->pCustomAuthUserData = MALLOC (EapResult.dwSizeOfUserData + sizeof (DWORD)); if (pPCB->pCustomAuthUserData == NULL) { TRACE1 (EAPOL, "ElEapWork: Error in allocating memory for pCustomAuthUserData = %ld", dwRetCode); dwRetCode = ERROR_NOT_ENOUGH_MEMORY; return dwRetCode; } pPCB->pCustomAuthUserData->dwSizeOfCustomAuthData = EapResult.dwSizeOfUserData; if ((EapResult.dwSizeOfUserData != 0) && (EapResult.pUserData != NULL)) { memcpy ((BYTE *)pPCB->pCustomAuthUserData->pbCustomAuthData, (BYTE *)EapResult.pUserData, EapResult.dwSizeOfUserData); } TRACE0 (EAPOL, "ElEapWork: Saved EAP data for user"); } // // Check to see if we have to save any connection data // pbAuthData = EapResult.SetCustomAuthData.pConnectionData; cbData = EapResult.SetCustomAuthData.dwSizeOfConnectionData; if ((EapResult.fSaveConnectionData ) && ( 0 != cbData ) ) { // Save to registry if ((dwRetCode = ElSetCustomAuthData ( pPCB->pwszDeviceGUID, pPCB->dwEapTypeToBeUsed, (pPCB->pSSID)?pPCB->pSSID->SsidLength:0, (pPCB->pSSID)?pPCB->pSSID->Ssid:NULL, pbAuthData, &cbData )) != NO_ERROR) { TRACE1 ( EAPOL, "ElEapWork: ElSetCustomAuthData failed with error = %d", dwRetCode); FREE (pEapolPkt); pEapolPkt = NULL; return dwRetCode; } // Save to PCB context if (pPCB->pCustomAuthConnData != NULL) { FREE (pPCB->pCustomAuthConnData); pPCB->pCustomAuthConnData = NULL; } pPCB->pCustomAuthConnData = MALLOC (cbData + sizeof (DWORD)); if (pPCB->pCustomAuthConnData == NULL) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; TRACE1 (EAPOL, "ElEapWork: Error in allocating memory for pCustomAuthConnData = %ld", dwRetCode); return dwRetCode; } pPCB->pCustomAuthConnData->dwSizeOfCustomAuthData = cbData; if ((cbData != 0) && (pbAuthData != NULL)) { memcpy ((BYTE *)pPCB->pCustomAuthConnData->pbCustomAuthData, (BYTE *)pbAuthData, cbData); } TRACE0 (EAPOL, "ElEapWork: Saved EAP data for connection"); } switch( EapResult.Action ) { case ELEAP_Send: case ELEAP_SendAndDone: // Send out EAPOL packet memcpy ((BYTE *)pEapolPkt->EthernetType, (BYTE *)pPCB->bEtherType, SIZE_ETHERNET_TYPE); pEapolPkt->ProtocolVersion = pPCB->bProtocolVersion; pEapolPkt->PacketType = EAP_Packet; // The EAP packet length is in the packet returned back by // the Dll MakeMessage // In case of Notification and Identity Response, it is in // EapResult.wSizeOfEapPkt if (EapResult.wSizeOfEapPkt == 0) { EapResult.wSizeOfEapPkt = WireToHostFormat16 (pSendPkt->Length); } HostToWireFormat16 ((WORD) EapResult.wSizeOfEapPkt, (BYTE *)pEapolPkt->PacketBodyLength); // Make a copy of the EAPOL packet in the PCB // Will be used during retransmission if (pPCB->pbPreviousEAPOLPkt != NULL) { FREE (pPCB->pbPreviousEAPOLPkt); pPCB->pbPreviousEAPOLPkt = NULL; } pPCB->pbPreviousEAPOLPkt = MALLOC (sizeof (EAPOL_PACKET)+EapResult.wSizeOfEapPkt-1); if (pPCB->pbPreviousEAPOLPkt == NULL) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; TRACE0 (EAPOL, "ElEapWork: MALLOC failed for pbPreviousEAPOLPkt"); if (pEapolPkt != NULL) { FREE (pEapolPkt); pEapolPkt = NULL; } return dwRetCode; } memcpy (pPCB->pbPreviousEAPOLPkt, pEapolPkt, sizeof (EAPOL_PACKET)+EapResult.wSizeOfEapPkt-1); pPCB->dwSizeOfPreviousEAPOLPkt = sizeof (EAPOL_PACKET)+EapResult.wSizeOfEapPkt-1; pPCB->dwPreviousId = dwReceivedId; // Send packet out on the port dwRetCode = ElWriteToPort (pPCB, (CHAR *)pEapolPkt, sizeof (EAPOL_PACKET)+EapResult.wSizeOfEapPkt-1); if (dwRetCode != NO_ERROR) { TRACE1 (EAPOL, "ElEapWork: Error in writing EAP_Packet to port %ld", dwRetCode); if (pEapolPkt != NULL) { FREE (pEapolPkt); pEapolPkt = NULL; } return dwRetCode; } if (pEapolPkt != NULL) { FREE (pEapolPkt); pEapolPkt = NULL; } // More processing to be done? // Supplicant side should not ever receive ELEAP_SendAndDone // result code if (EapResult.Action != ELEAP_SendAndDone) { break; } else { TRACE0 (EAPOL, "ElEapWork: ELEAP_SendAndDone wrong result received"); } case ELEAP_Done: // Retrieve MPPE keys from the attributes information // returned by EAP-TLS switch (EapResult.dwError) { case NO_ERROR: TRACE0 (EAPOL, "ElEapWork: Authentication was successful"); pPCB->fLocalEAPAuthSuccess = TRUE; // // If authentication was successful // dwRetCode = ElExtractMPPESendRecvKeys ( pPCB, EapResult.pUserAttributes, (BYTE*)&(EapResult.abChallenge), (BYTE*)&(EapResult.abResponse)); if (dwRetCode != NO_ERROR) { FREE (pEapolPkt); //NotifyCallerOfFailure (pPcb, dwRetCode); return dwRetCode; } // ISSUE: // Do we want to retain UserAttributes // pPCB->pAuthProtocolAttributes = EapResult.pUserAttributes; break; default: if (pEapolPkt != NULL) { FREE (pEapolPkt); pEapolPkt = NULL; } TRACE0 (EAPOL, "ElEapWork: Authentication FAILED"); pPCB->dwLocalEAPAuthResult = EapResult.dwError; break; } // Free memory allocated for the packet, since no response // is going to be sent out if (pEapolPkt != NULL) { FREE (pEapolPkt); pEapolPkt = NULL; } break; case ELEAP_NoAction: // Free memory allocated for the packet, since nothing // is being done with it if (pEapolPkt != NULL) { FREE (pEapolPkt); pEapolPkt = NULL; } break; default: break; } if (pEapolPkt != NULL) { FREE (pEapolPkt); pEapolPkt = NULL; } // // Check to see if we have to bring up the InteractiveUI for EAP // i.e. Server cert confirmation etc. // if (EapResult.fInvokeEapUI) { ElInvokeInteractiveUI (pPCB, &(EapResult.InvokeEapUIData)); } return dwRetCode; } // // // ElExtractMPPESendRecvKeys // // Description: // Function called if authentication was successful. The MPPE Send & // Recv keys are extracted from the RAS_AUTH_ATTRIBUTE passed from // the EAP DLL and stored in the PCB. The keys are used to decrypt // the multicast WEP key and also are used for media-based encrypting. // // Return values // // NO_ERROR - Success // Non-zero - Failure // DWORD ElExtractMPPESendRecvKeys ( IN EAPOL_PCB *pPCB, IN RAS_AUTH_ATTRIBUTE * pUserAttributes, IN BYTE * pChallenge, IN BYTE * pResponse ) { RAS_AUTH_ATTRIBUTE * pAttribute; RAS_AUTH_ATTRIBUTE * pAttributeSendKey; RAS_AUTH_ATTRIBUTE * pAttributeRecvKey; DWORD dwRetCode = NO_ERROR; DWORD dwEncryptionPolicy = 0; DWORD dwEncryptionTypes = 0; do { pAttribute = ElAuthAttributeGetVendorSpecific ( 311, 12, pUserAttributes); pAttributeSendKey = ElAuthAttributeGetVendorSpecific ( 311, 16, pUserAttributes); pAttributeRecvKey = ElAuthAttributeGetVendorSpecific ( 311, 17, pUserAttributes); if ((pAttributeSendKey != NULL) && (pAttributeRecvKey != NULL)) { // Set the MS-MPPE-Send-Key and MS-MPPE-Recv-Key with // the ethernet driver ULONG ulSendKeyLength = 0; ULONG ulRecvKeyLength = 0; // Based on PPP code ulSendKeyLength = *(((BYTE*)(pAttributeSendKey->Value))+8); ulRecvKeyLength = *(((BYTE*)(pAttributeRecvKey->Value))+8); // TRACE0 (EAPOL, "Send key = "); // EAPOL_DUMPBA (((BYTE*)(pAttributeSendKey->Value))+9, // ulSendKeyLength); // TRACE0 (EAPOL, "Recv key = "); // EAPOL_DUMPBA (((BYTE*)(pAttributeRecvKey->Value))+9, // ulRecvKeyLength); // // Copy MPPE Send and Receive Keys into the PCB for later usage // These keys will be used to decrypt keys sent by NAS (if any). // Save the keys as the MasterSecret for dynamic rekeying (if any). // if (ulSendKeyLength != 0) { if (pPCB->MasterSecretSend.cbData != 0) { FREE (pPCB->MasterSecretSend.pbData); pPCB->MasterSecretSend.cbData = 0; pPCB->MasterSecretSend.pbData = NULL; } if ((dwRetCode = ElSecureEncodePw ( ((BYTE*)(pAttributeSendKey->Value))+9, ulSendKeyLength, &(pPCB->MasterSecretSend) )) != NO_ERROR) { TRACE1 (EAPOL, "ElExtractMPPESendRecvKeys: ElSecureEncodePw for Master Send failed with error %ld", dwRetCode); break; } if (pPCB->MPPESendKey.cbData != 0) { FREE (pPCB->MPPESendKey.pbData); pPCB->MPPESendKey.cbData = 0; pPCB->MPPESendKey.pbData = NULL; } if ((dwRetCode = ElSecureEncodePw ( ((BYTE*)(pAttributeSendKey->Value))+9, ulSendKeyLength, &(pPCB->MPPESendKey) )) != NO_ERROR) { TRACE1 (EAPOL, "ElExtractMPPESendRecvKeys: ElSecureEncodePw for MPPESend failed with error %ld", dwRetCode); break; } } if (ulRecvKeyLength != 0) { if (pPCB->MasterSecretRecv.cbData != 0) { FREE (pPCB->MasterSecretRecv.pbData); pPCB->MasterSecretRecv.cbData = 0; pPCB->MasterSecretRecv.pbData = NULL; } if ((dwRetCode = ElSecureEncodePw ( ((BYTE*)(pAttributeRecvKey->Value))+9, ulRecvKeyLength, &(pPCB->MasterSecretRecv) )) != NO_ERROR) { TRACE1 (EAPOL, "ElExtractMPPESendRecvKeys: ElSecureEncodePw for Master Recv failed with error %ld", dwRetCode); break; } if (pPCB->MPPERecvKey.cbData != 0) { FREE (pPCB->MPPERecvKey.pbData); pPCB->MPPERecvKey.cbData = 0; pPCB->MPPERecvKey.pbData = NULL; } if ((dwRetCode = ElSecureEncodePw ( ((BYTE*)(pAttributeRecvKey->Value))+9, ulRecvKeyLength, &(pPCB->MPPERecvKey) )) != NO_ERROR) { TRACE1 (EAPOL, "ElExtractMPPESendRecvKeys: ElSecureEncodePw for MPPERecv failed with error %ld", dwRetCode); break; } } TRACE0 (EAPOL,"MPPE-Send/Recv-Keys derived by supplicant"); } else { TRACE0 (EAPOL, "ElExtractMPPESendRecvKeys: pAttributeSendKey or pAttributeRecvKey == NULL"); } } while (FALSE); if (dwRetCode != NO_ERROR) { 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; } } return( dwRetCode ); } // // ElProcessEapSuccess // // Description: // // Function called when an EAP_Success is received in any state // // Input arguments: // pPCB - Pointer to PCB for the port on which data is being processed // pEapolPkt - Pointer to EAPOL packet that was received // // Return values: // NO_ERROR - success // non-zero - error // DWORD ElProcessEapSuccess ( IN EAPOL_PCB *pPCB, IN EAPOL_PACKET *pEapolPkt ) { EAPOL_ZC_INTF ZCData; DWORD dwRetCode = NO_ERROR; TRACE0 (EAPOL, "ElProcessEapSuccess: Got EAPCODE_Success"); do { // Indicate to EAP=Dll to cleanup completed session if ((dwRetCode = ElEapEnd (pPCB)) != NO_ERROR) { TRACE1 (EAPOL, "ProcessReceivedPacket: EapSuccess: Error in ElEapEnd = %ld", dwRetCode); break; } TRACE0 (EAPOL, "ElProcessEapSuccess: Authentication successful"); // Complete remaining processing i.e. DHCP if ((dwRetCode = FSMAuthenticated (pPCB, pEapolPkt)) != NO_ERROR) { break; } #ifdef ZEROCONFIG_LINKED // Indicate to WZC that authentication succeeded and // reset the blob it stores for the current SSID ZeroMemory ((PVOID)&ZCData, sizeof(EAPOL_ZC_INTF)); ZCData.dwAuthFailCount = 0; ZCData.PreviousAuthenticationType = EAPOL_UNAUTHENTICATED_ACCESS; if (pPCB->pSSID != NULL) { memcpy (ZCData.bSSID, pPCB->pSSID->Ssid, pPCB->pSSID->SsidLength); ZCData.dwSizeOfSSID = pPCB->pSSID->SsidLength; } if ((dwRetCode = ElZeroConfigNotify ( pPCB->dwZeroConfigId, WZCCMD_CFG_SETDATA, pPCB->pwszDeviceGUID, &ZCData )) != NO_ERROR) { TRACE1 (EAPOL, "ElProcessEapSuccess: ElZeroConfigNotify failed with error %ld", dwRetCode); dwRetCode = NO_ERROR; } TRACE1 (EAPOL, "ElProcessEapSuccess: Called ElZeroConfigNotify with type=(%ld)", WZCCMD_CFG_SETDATA); #endif // ZEROCONFIG_LINKED ElNetmanNotify (pPCB, EAPOL_NCS_AUTHENTICATION_SUCCEEDED, NULL); } while (FALSE); return dwRetCode; } // // ElProcessEapFail // // Description: // // Function called when an EAP_Fail is received in any state // // Input arguments: // pPCB - Pointer to PCB for the port on which data is being processed // pEapolPkt - Pointer to EAPOL packet that was received // // Return values: // NO_ERROR - success // non-zero - error // DWORD ElProcessEapFail ( IN EAPOL_PCB *pPCB, IN EAPOL_PACKET *pEapolPkt ) { EAPOL_ZC_INTF ZCData; DWORD dwRetCode = NO_ERROR; TRACE0 (EAPOL, "ElProcessEapFail: Got EAPCODE_Failure"); do { // Indicate to EAP-Dll to cleanup completed session if ((dwRetCode = ElEapEnd (pPCB)) != NO_ERROR) { TRACE1 (EAPOL, "ElProcessEapFail: EapFail: Error in ElEapEnd = %ld", dwRetCode); break; } // Show failure balloon before notifying ZeroConfig // ZeroConfig may require to pop-up its own balloon, and that has // to be given preference ElNetmanNotify (pPCB, EAPOL_NCS_AUTHENTICATION_FAILED, NULL); #ifdef ZEROCONFIG_LINKED // Indicate to WZC that authentication failed ZeroMemory ((PVOID)&ZCData, sizeof(EAPOL_ZC_INTF)); ZCData.dwAuthFailCount = pPCB->dwAuthFailCount + 1; ZCData.PreviousAuthenticationType = pPCB->PreviousAuthenticationType; if (pPCB->pSSID != NULL) { memcpy (ZCData.bSSID, pPCB->pSSID->Ssid, pPCB->pSSID->SsidLength); ZCData.dwSizeOfSSID = pPCB->pSSID->SsidLength; } // We notify ZC before going through held state, where fail count is // upped. Hence, here we explicitly up it by one if ((dwRetCode = ElZeroConfigNotify ( pPCB->dwZeroConfigId, ((pPCB->dwAuthFailCount+1) < pPCB->dwTotalMaxAuthFailCount)?WZCCMD_CFG_NEXT:WZCCMD_CFG_DELETE, pPCB->pwszDeviceGUID, &ZCData )) != NO_ERROR) { TRACE1 (EAPOL, "ElProcessEapFail: ElZeroConfigNotify failed with error %ld", dwRetCode); dwRetCode = NO_ERROR; } TRACE3 (EAPOL, "ElProcessEapFail: Called ElZeroConfigNotify with failcount = %ld, prevauthtype = %ld, type=(%ld)", ZCData.dwAuthFailCount, ZCData.PreviousAuthenticationType, ((pPCB->dwAuthFailCount+1) < pPCB->dwTotalMaxAuthFailCount)?WZCCMD_CFG_NEXT:WZCCMD_CFG_DELETE ); #endif // ZEROCONFIG_LINKED if ((dwRetCode = FSMHeld (pPCB, NULL)) != NO_ERROR) { break; } } while (FALSE); return dwRetCode; } // // ElSetEAPOLKeyReceivedTimer // // Description: // // Function called for wireless interface when it enter AUTHENTICATED state // If no EAPOL-Key message is received for the transmit key in the meanwhile // the association should be negated to Zero-Config // // Input arguments: // pPCB - Pointer to PCB for the port which entered AUTHENTICATED state // // Return values: // NO_ERROR - success // non-zero - error // DWORD ElSetEAPOLKeyReceivedTimer ( IN EAPOL_PCB *pPCB ) { DWORD dwRetCode = NO_ERROR; do { if (pPCB->fTransmitKeyReceived) { TRACE0 (EAPOL, "EAPOL-Key for transmit key received before entering AUTHENTICATED state"); break; } RESTART_TIMER (pPCB->hTimer, EAPOL_TRANSMIT_KEY_INTERVAL, "PCB", &dwRetCode); if (dwRetCode != NO_ERROR) { TRACE1 (EAPOL, "ElSetEAPOLKeyReceivedTimer: Error in RESTART_TIMER %ld", dwRetCode); break; } SET_TRANSMIT_KEY_TIMER(pPCB); } while (FALSE); return dwRetCode; } // // ElVerifyEAPOLKeyReceived // // Description: // // Function called on timeout to verify if EAPOL-transmit key was received // If no EAPOL-Key message is received for the transmit key in the meanwhile // the association should be negated to Zero-Config // // Input arguments: // pPCB - Pointer to PCB for the port which entered AUTHENTICATED state // // Return values: // NO_ERROR - success // non-zero - error // DWORD ElVerifyEAPOLKeyReceived ( IN EAPOL_PCB *pPCB ) { EAPOL_ZC_INTF ZCData; DWORD dwRetCode = NO_ERROR; do { if (!pPCB->fTransmitKeyReceived) { TRACE1 (EAPOL, "EAPOL-Key for transmit key *NOT* received within %ld seconds in AUTHENTICATED state", EAPOL_TRANSMIT_KEY_INTERVAL ); DbLogPCBEvent (DBLOG_CATEG_ERR, pPCB, EAPOL_NOT_RECEIVED_XMIT_KEY); #ifdef ZEROCONFIG_LINKED // Indicate to WZC that authentication didn't really complete // since there was EAPOL-Key packet for the transmit key // Fail the entire configuration ZeroMemory ((PVOID)&ZCData, sizeof(EAPOL_ZC_INTF)); ZCData.dwAuthFailCount = pPCB->dwTotalMaxAuthFailCount; pPCB->dwAuthFailCount = pPCB->dwTotalMaxAuthFailCount; ZCData.PreviousAuthenticationType = pPCB->PreviousAuthenticationType; if (pPCB->pSSID != NULL) { memcpy (ZCData.bSSID, pPCB->pSSID->Ssid, pPCB->pSSID->SsidLength); ZCData.dwSizeOfSSID = pPCB->pSSID->SsidLength; } if ((dwRetCode = ElZeroConfigNotify ( pPCB->dwZeroConfigId, ((pPCB->dwAuthFailCount) < pPCB->dwTotalMaxAuthFailCount)?WZCCMD_CFG_NEXT:WZCCMD_CFG_DELETE, pPCB->pwszDeviceGUID, &ZCData )) != NO_ERROR) { TRACE1 (EAPOL, "ElVerifyEAPOLKeyReceived: ElZeroConfigNotify failed with error %ld", dwRetCode); dwRetCode = NO_ERROR; } TRACE3 (EAPOL, "ElVerifyEAPOLKeyReceived: Called ElZeroConfigNotify with failcount = %ld, prevauthtype = %ld, type=(%ld)", ZCData.dwAuthFailCount, ZCData.PreviousAuthenticationType, ((pPCB->dwAuthFailCount+1) < pPCB->dwTotalMaxAuthFailCount)?WZCCMD_CFG_NEXT:WZCCMD_CFG_DELETE ); // If authfailed limit reached, go to Disconnected state if (pPCB->dwAuthFailCount >= pPCB->dwTotalMaxAuthFailCount) { TRACE2 (EAPOL, "ElVerifyEAPOLKeyReceived: Pushing into disconnected state: Fail count (%ld) > Max fail count (%ld)", pPCB->dwAuthFailCount, pPCB->dwTotalMaxAuthFailCount); FSMDisconnected (pPCB, NULL); } #endif // ZEROCONFIG_LINKED } else { TRACE1 (EAPOL, "EAPOL-Key for transmit key received within %ld seconds in AUTHENTICATED state", EAPOL_TRANSMIT_KEY_INTERVAL ); } } while (FALSE); return dwRetCode; }