/* *************************************************************************** * Ralink Tech Inc. * 4F, No. 2 Technology 5th Rd. * Science-based Industrial Park * Hsin-chu, Taiwan, R.O.C. * * (c) Copyright 2002, Ralink Technology, Inc. * * All rights reserved. Ralink's source code is an unpublished work and the * use of a copyright notice does not imply otherwise. This source code * contains confidential trade secret material of Ralink Tech. Any attemp * or participation in deciphering, decoding, reverse engineering or in any * way altering the source code is stricitly prohibited, unless the prior * written consent of Ralink Technology, Inc. is obtained. *************************************************************************** Module Name: wpa.c Abstract: Revision History: Who When What -------- ---------- ---------------------------------------------- Jan Lee 03-07-22 Initial Rory Chen 04-11-29 Add WPA2PSK */ #include "rt_config.h" extern UCHAR EAPOL[]; /* ========================================================================== Description: Port Access Control Inquiry function. Return entry's Privacy and Wpastate. Return: pEntry ========================================================================== */ MAC_TABLE_ENTRY *PACInquiry( IN PRTMP_ADAPTER pAd, IN ULONG Wcid) { MAC_TABLE_ENTRY *pEntry = (MAC_TABLE_ENTRY*)NULL; /* *Privacy = Ndis802_11PrivFilterAcceptAll; */ /* *WpaState = AS_NOTUSE; */ if (Wcid < MAX_LEN_OF_MAC_TABLE) { pEntry = &(pAd->MacTab.Content[Wcid]); /*if (IS_ENTRY_CLIENT(&pAd->MacTab.Content[Wcid])) */ /*ASSERT(IS_ENTRY_CLIENT(&pAd->MacTab.Content[Wcid])); */ /*ASSERT(pEntry->Sst == SST_ASSOC); */ /* *Privacy = pEntry->PrivacyFilter; */ /* *WpaState = pEntry->WpaState; */ } return pEntry; } /* ========================================================================== Description: Check sanity of multicast cipher selector in RSN IE. Return: TRUE if match FALSE otherwise ========================================================================== */ BOOLEAN RTMPCheckMcast( IN PRTMP_ADAPTER pAd, IN PEID_STRUCT eid_ptr, IN MAC_TABLE_ENTRY *pEntry) { UCHAR apidx; ASSERT(pEntry); ASSERT(pEntry->apidx < pAd->ApCfg.BssidNum); apidx = pEntry->apidx; pEntry->AuthMode = pAd->ApCfg.MBSSID[apidx].AuthMode; if (eid_ptr->Len >= 6) { /* WPA and WPA2 format not the same in RSN_IE */ if (eid_ptr->Eid == IE_WPA) { if (pAd->ApCfg.MBSSID[apidx].AuthMode == Ndis802_11AuthModeWPA1WPA2) pEntry->AuthMode = Ndis802_11AuthModeWPA; else if (pAd->ApCfg.MBSSID[apidx].AuthMode == Ndis802_11AuthModeWPA1PSKWPA2PSK) pEntry->AuthMode = Ndis802_11AuthModeWPAPSK; if (NdisEqualMemory(&eid_ptr->Octet[6], &pAd->ApCfg.MBSSID[apidx].RSN_IE[0][6], 4)) return TRUE; } else if (eid_ptr->Eid == IE_WPA2) { UCHAR IE_Idx = 0; /* When WPA1/WPA2 mix mode, the RSN_IE is stored in different structure */ if ((pAd->ApCfg.MBSSID[apidx].AuthMode == Ndis802_11AuthModeWPA1WPA2) || (pAd->ApCfg.MBSSID[apidx].AuthMode == Ndis802_11AuthModeWPA1PSKWPA2PSK)) IE_Idx = 1; if (pAd->ApCfg.MBSSID[apidx].AuthMode == Ndis802_11AuthModeWPA1WPA2) pEntry->AuthMode = Ndis802_11AuthModeWPA2; else if (pAd->ApCfg.MBSSID[apidx].AuthMode == Ndis802_11AuthModeWPA1PSKWPA2PSK) pEntry->AuthMode = Ndis802_11AuthModeWPA2PSK; if (NdisEqualMemory(&eid_ptr->Octet[2], &pAd->ApCfg.MBSSID[apidx].RSN_IE[IE_Idx][2], 4)) return TRUE; } } return FALSE; } /* ========================================================================== Description: Check sanity of unicast cipher selector in RSN IE. Return: TRUE if match FALSE otherwise ========================================================================== */ BOOLEAN RTMPCheckUcast( IN PRTMP_ADAPTER pAd, IN PEID_STRUCT eid_ptr, IN MAC_TABLE_ENTRY *pEntry) { PUCHAR pStaTmp; USHORT Count; UCHAR apidx; ASSERT(pEntry); ASSERT(pEntry->apidx < pAd->ApCfg.BssidNum); apidx = pEntry->apidx; pEntry->WepStatus = pAd->ApCfg.MBSSID[apidx].WepStatus; if (eid_ptr->Len < 16) { DBGPRINT(RT_DEBUG_ERROR, ("[ERROR]RTMPCheckUcast : the length is too short(%d) \n", eid_ptr->Len)); return FALSE; } /* Store STA RSN_IE capability */ pStaTmp = (PUCHAR)&eid_ptr->Octet[0]; if(eid_ptr->Eid == IE_WPA2) { /* skip Version(2),Multicast cipter(4) 2+4==6 */ /* point to number of unicast */ pStaTmp +=6; } else if (eid_ptr->Eid == IE_WPA) { /* skip OUI(4),Vesrion(2),Multicast cipher(4) 4+2+4==10 */ /* point to number of unicast */ pStaTmp += 10; } else { DBGPRINT(RT_DEBUG_ERROR, ("[ERROR]RTMPCheckUcast : invalid IE=%d\n", eid_ptr->Eid)); return FALSE; } /* Store unicast cipher count */ NdisMoveMemory(&Count, pStaTmp, sizeof(USHORT)); Count = cpu2le16(Count); /* pointer to unicast cipher */ pStaTmp += sizeof(USHORT); if (eid_ptr->Len >= 16) { if (eid_ptr->Eid == IE_WPA) { if (pAd->ApCfg.MBSSID[apidx].WepStatus == Ndis802_11Encryption4Enabled) {/* multiple cipher (TKIP/CCMP) */ while (Count > 0) { /* TKIP */ if (MIX_CIPHER_WPA_TKIP_ON(pAd->ApCfg.MBSSID[apidx].WpaMixPairCipher)) { /* Compare if peer STA uses the TKIP as its unicast cipher */ if (RTMPEqualMemory(pStaTmp, &pAd->ApCfg.MBSSID[apidx].RSN_IE[0][12], 4)) { pEntry->WepStatus = Ndis802_11Encryption2Enabled; return TRUE; } /* Our AP uses the AES as the secondary cipher */ /* Compare if the peer STA use AES as its unicast cipher */ if (MIX_CIPHER_WPA_AES_ON(pAd->ApCfg.MBSSID[apidx].WpaMixPairCipher)) { if (RTMPEqualMemory(pStaTmp, &pAd->ApCfg.MBSSID[apidx].RSN_IE[0][16], 4)) { pEntry->WepStatus = Ndis802_11Encryption3Enabled; return TRUE; } } } else { /* AES */ if (RTMPEqualMemory(pStaTmp, &pAd->ApCfg.MBSSID[apidx].RSN_IE[0][12], 4)) { pEntry->WepStatus = Ndis802_11Encryption3Enabled; return TRUE; } } pStaTmp += 4; Count--; } } else {/* single cipher */ while (Count > 0) { if (RTMPEqualMemory(pStaTmp , &pAd->ApCfg.MBSSID[apidx].RSN_IE[0][12], 4)) return TRUE; pStaTmp += 4; Count--; } } } else if (eid_ptr->Eid == IE_WPA2) { UCHAR IE_Idx = 0; /* When WPA1/WPA2 mix mode, the RSN_IE is stored in different structure */ if ((pAd->ApCfg.MBSSID[apidx].AuthMode == Ndis802_11AuthModeWPA1WPA2) || (pAd->ApCfg.MBSSID[apidx].AuthMode == Ndis802_11AuthModeWPA1PSKWPA2PSK)) IE_Idx = 1; if (pAd->ApCfg.MBSSID[apidx].WepStatus == Ndis802_11Encryption4Enabled) {/* multiple cipher (TKIP/CCMP) */ while (Count > 0) { /* WPA2 TKIP */ if (MIX_CIPHER_WPA2_TKIP_ON(pAd->ApCfg.MBSSID[apidx].WpaMixPairCipher)) { /* Compare if peer STA uses the TKIP as its unicast cipher */ if (RTMPEqualMemory(pStaTmp, &pAd->ApCfg.MBSSID[apidx].RSN_IE[IE_Idx][8], 4)) { pEntry->WepStatus = Ndis802_11Encryption2Enabled; return TRUE; } /* Our AP uses the AES as the secondary cipher */ /* Compare if the peer STA use AES as its unicast cipher */ if (MIX_CIPHER_WPA2_AES_ON(pAd->ApCfg.MBSSID[apidx].WpaMixPairCipher)) { if (RTMPEqualMemory(pStaTmp, &pAd->ApCfg.MBSSID[apidx].RSN_IE[IE_Idx][12], 4)) { pEntry->WepStatus = Ndis802_11Encryption3Enabled; return TRUE; } } } else { /* AES */ if (RTMPEqualMemory(pStaTmp, &pAd->ApCfg.MBSSID[apidx].RSN_IE[IE_Idx][8], 4)) { pEntry->WepStatus = Ndis802_11Encryption3Enabled; return TRUE; } } pStaTmp += 4; Count--; } } else {/* single cipher */ while (Count > 0) { if (RTMPEqualMemory(pStaTmp, &pAd->ApCfg.MBSSID[apidx].RSN_IE[IE_Idx][8], 4)) return TRUE; pStaTmp += 4; Count--; } } } } return FALSE; } /* ========================================================================== Description: Check invalidity of authentication method selection in RSN IE. Return: TRUE if match FALSE otherwise ========================================================================== */ BOOLEAN RTMPCheckAKM(PUCHAR sta_akm, PUCHAR ap_rsn_ie, INT iswpa2) { PUCHAR pTmp; USHORT Count; pTmp = ap_rsn_ie; if(iswpa2) /* skip Version(2),Multicast cipter(4) 2+4==6 */ pTmp +=6; else /*skip OUI(4),Vesrion(2),Multicast cipher(4) 4+2+4==10 */ pTmp += 10;/*point to number of unicast */ NdisMoveMemory(&Count, pTmp, sizeof(USHORT)); Count = cpu2le16(Count); pTmp += sizeof(USHORT);/*pointer to unicast cipher */ /* Skip all unicast cipher suite */ while (Count > 0) { /* Skip OUI */ pTmp += 4; Count--; } NdisMoveMemory(&Count, pTmp, sizeof(USHORT)); Count = cpu2le16(Count); pTmp += sizeof(USHORT);/*pointer to AKM cipher */ while (Count > 0) { /*rtmp_hexdump(RT_DEBUG_TRACE,"MBSS WPA_IE AKM ",pTmp,4); */ if(RTMPEqualMemory(sta_akm,pTmp,4)) return TRUE; else { pTmp += 4; Count--; } } return FALSE;/* do not match the AKM */ } /* ========================================================================== Description: Check sanity of authentication method selector in RSN IE. Return: TRUE if match FALSE otherwise ========================================================================== */ BOOLEAN RTMPCheckAUTH( IN PRTMP_ADAPTER pAd, IN PEID_STRUCT eid_ptr, IN MAC_TABLE_ENTRY *pEntry) { PUCHAR pStaTmp; USHORT Count; UCHAR apidx; ASSERT(pEntry); ASSERT(pEntry->apidx < pAd->ApCfg.BssidNum); apidx = pEntry->apidx; if (eid_ptr->Len < 16) { DBGPRINT(RT_DEBUG_ERROR, ("RTMPCheckAUTH ==> WPAIE len is too short(%d) \n", eid_ptr->Len)); return FALSE; } /* Store STA RSN_IE capability */ pStaTmp = (PUCHAR)&eid_ptr->Octet[0]; if(eid_ptr->Eid == IE_WPA2) { /* skip Version(2),Multicast cipter(4) 2+4==6 */ /* point to number of unicast */ pStaTmp +=6; } else if (eid_ptr->Eid == IE_WPA) { /* skip OUI(4),Vesrion(2),Multicast cipher(4) 4+2+4==10 */ /* point to number of unicast */ pStaTmp += 10; } else { DBGPRINT(RT_DEBUG_ERROR, ("RTMPCheckAUTH ==> Unknown WPAIE, WPAIE=%d\n", eid_ptr->Eid)); return FALSE; } /* Store unicast cipher count */ NdisMoveMemory(&Count, pStaTmp, sizeof(USHORT)); Count = cpu2le16(Count); /* pointer to unicast cipher */ pStaTmp += sizeof(USHORT); /* Skip all unicast cipher suite */ while (Count > 0) { /* Skip OUI */ pStaTmp += 4; Count--; } /* Store AKM count */ NdisMoveMemory(&Count, pStaTmp, sizeof(USHORT)); Count = cpu2le16(Count); /*pointer to AKM cipher */ pStaTmp += sizeof(USHORT); if (eid_ptr->Len >= 16) { if (eid_ptr->Eid == IE_WPA) { while (Count > 0) { if (RTMPCheckAKM(pStaTmp, &pAd->ApCfg.MBSSID[apidx].RSN_IE[0][0],0)) return TRUE; pStaTmp += 4; Count--; } } else if (eid_ptr->Eid == IE_WPA2) { UCHAR IE_Idx = 0; /* When WPA1/WPA2 mix mode, the RSN_IE is stored in different structure */ if ((pAd->ApCfg.MBSSID[apidx].AuthMode == Ndis802_11AuthModeWPA1WPA2) || (pAd->ApCfg.MBSSID[apidx].AuthMode == Ndis802_11AuthModeWPA1PSKWPA2PSK)) IE_Idx = 1; while (Count > 0) { if (RTMPCheckAKM(pStaTmp, &pAd->ApCfg.MBSSID[apidx].RSN_IE[IE_Idx][0],1)) return TRUE; pStaTmp += 4; Count--; } } } return FALSE; } /* ========================================================================== Description: Check validity of the received RSNIE. Return: status code ========================================================================== */ UINT APValidateRSNIE( IN PRTMP_ADAPTER pAd, IN PMAC_TABLE_ENTRY pEntry, IN PUCHAR pRsnIe, IN UCHAR rsnie_len) { UINT StatusCode = MLME_SUCCESS; PEID_STRUCT eid_ptr; INT apidx; PMULTISSID_STRUCT pMbss; if (rsnie_len == 0) return MLME_SUCCESS; eid_ptr = (PEID_STRUCT)pRsnIe; if ((eid_ptr->Len + 2) != rsnie_len) { DBGPRINT(RT_DEBUG_ERROR, ("[ERROR]APValidateRSNIE : the len is invalid !!!\n")); return MLME_UNSPECIFY_FAIL; } apidx = pEntry->apidx; pMbss = &pAd->ApCfg.MBSSID[apidx]; #ifdef WAPI_SUPPORT if (eid_ptr->Eid == IE_WAPI) return MLME_SUCCESS; #endif /* WAPI_SUPPORT */ /* check group cipher */ if (!RTMPCheckMcast(pAd, eid_ptr, pEntry)) { DBGPRINT(RT_DEBUG_ERROR, ("[ERROR]APValidateRSNIE : invalid group cipher !!!\n")); StatusCode = MLME_INVALID_GROUP_CIPHER; } /* Check pairwise cipher */ else if (!RTMPCheckUcast(pAd, eid_ptr, pEntry)) { DBGPRINT(RT_DEBUG_ERROR, ("[ERROR]APValidateRSNIE : invalid pairwise cipher !!!\n")); StatusCode = MLME_INVALID_PAIRWISE_CIPHER; } /* Check AKM */ else if (!RTMPCheckAUTH(pAd, eid_ptr, pEntry)) { DBGPRINT(RT_DEBUG_ERROR, ("[ERROR]APValidateRSNIE : invalid AKM !!!\n")); StatusCode = MLME_INVALID_AKMP; } if (StatusCode != MLME_SUCCESS) { /* send wireless event - for RSN IE sanity check fail */ RTMPSendWirelessEvent(pAd, IW_RSNIE_SANITY_FAIL_EVENT_FLAG, pEntry->Addr, 0, 0); DBGPRINT(RT_DEBUG_ERROR, ("%s : invalid status code(%d) !!!\n", __FUNCTION__, StatusCode)); } else { UCHAR CipherAlg = CIPHER_NONE; if (pEntry->WepStatus == Ndis802_11Encryption1Enabled) CipherAlg = CIPHER_WEP64; else if (pEntry->WepStatus == Ndis802_11Encryption2Enabled) CipherAlg = CIPHER_TKIP; else if (pEntry->WepStatus == Ndis802_11Encryption3Enabled) CipherAlg = CIPHER_AES; DBGPRINT(RT_DEBUG_TRACE, ("%s : (AID#%d WepStatus=%s)\n", __FUNCTION__, pEntry->Aid, CipherName[CipherAlg])); } return StatusCode; } /* ========================================================================== Description: Function to handle countermeasures active attack. Init 60-sec timer if necessary. Return: ========================================================================== */ VOID HandleCounterMeasure( IN PRTMP_ADAPTER pAd, IN MAC_TABLE_ENTRY *pEntry) { INT i; BOOLEAN Cancelled; if (!pEntry) return; /* Todo by AlbertY - Not support currently in ApClient-link */ if (IS_ENTRY_APCLI(pEntry)) return; /* if entry not set key done, ignore this RX MIC ERROR */ if ((pEntry->WpaState < AS_PTKINITDONE) || (pEntry->GTKState != REKEY_ESTABLISHED)) return; DBGPRINT(RT_DEBUG_TRACE, ("HandleCounterMeasure ===> \n")); /* record which entry causes this MIC error, if this entry sends disauth/disassoc, AP doesn't need to log the CM */ pEntry->CMTimerRunning = TRUE; pAd->ApCfg.MICFailureCounter++; /* send wireless event - for MIC error */ RTMPSendWirelessEvent(pAd, IW_MIC_ERROR_EVENT_FLAG, pEntry->Addr, 0, 0); if (pAd->ApCfg.CMTimerRunning == TRUE) { DBGPRINT(RT_DEBUG_ERROR, ("Receive CM Attack Twice within 60 seconds ====>>> \n")); /* send wireless event - for counter measures */ RTMPSendWirelessEvent(pAd, IW_COUNTER_MEASURES_EVENT_FLAG, pEntry->Addr, 0, 0); ApLogEvent(pAd, pEntry->Addr, EVENT_COUNTER_M); /* renew GTK */ GenRandom(pAd, pAd->ApCfg.MBSSID[pEntry->apidx].Bssid, pAd->ApCfg.MBSSID[pEntry->apidx].GNonce); /* Cancel CounterMeasure Timer */ RTMPCancelTimer(&pAd->ApCfg.CounterMeasureTimer, &Cancelled); pAd->ApCfg.CMTimerRunning = FALSE; for (i = 0; i < MAX_LEN_OF_MAC_TABLE; i++) { /* happened twice within 60 sec, AP SENDS disaccociate all associated STAs. All STA's transition to State 2 */ if (IS_ENTRY_CLIENT(&pAd->MacTab.Content[i])) { MlmeDeAuthAction(pAd, &pAd->MacTab.Content[i], REASON_MIC_FAILURE, FALSE); } } /* Further, ban all Class 3 DATA transportation for a period 0f 60 sec */ /* disallow new association , too */ pAd->ApCfg.BANClass3Data = TRUE; /* check how many entry left... should be zero */ /*pAd->ApCfg.MBSSID[pEntry->apidx].GKeyDoneStations = pAd->MacTab.Size; */ /*DBGPRINT(RT_DEBUG_TRACE, ("GKeyDoneStations=%d \n", pAd->ApCfg.MBSSID[pEntry->apidx].GKeyDoneStations)); */ } RTMPSetTimer(&pAd->ApCfg.CounterMeasureTimer, 60 * MLME_TASK_EXEC_INTV * MLME_TASK_EXEC_MULTIPLE); pAd->ApCfg.CMTimerRunning = TRUE; pAd->ApCfg.PrevaMICFailTime = pAd->ApCfg.aMICFailTime; RTMP_GetCurrentSystemTime(&pAd->ApCfg.aMICFailTime); } /* ========================================================================== Description: countermeasures active attack timer execution Return: ========================================================================== */ VOID CMTimerExec( IN PVOID SystemSpecific1, IN PVOID FunctionContext, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3) { UINT i,j=0; PRTMP_ADAPTER pAd = (PRTMP_ADAPTER)FunctionContext; pAd->ApCfg.BANClass3Data = FALSE; for (i = 0; i < MAX_LEN_OF_MAC_TABLE; i++) { if (IS_ENTRY_CLIENT(&pAd->MacTab.Content[i]) && (pAd->MacTab.Content[i].CMTimerRunning == TRUE)) { pAd->MacTab.Content[i].CMTimerRunning =FALSE; j++; } } if (j > 1) DBGPRINT(RT_DEBUG_ERROR, ("Find more than one entry which generated MIC Fail .. \n")); pAd->ApCfg.CMTimerRunning = FALSE; } VOID WPARetryExec( IN PVOID SystemSpecific1, IN PVOID FunctionContext, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3) { MAC_TABLE_ENTRY *pEntry = (MAC_TABLE_ENTRY *)FunctionContext; if ((pEntry) && IS_ENTRY_CLIENT(pEntry)) { PRTMP_ADAPTER pAd = (PRTMP_ADAPTER)pEntry->pAd; pEntry->ReTryCounter++; DBGPRINT(RT_DEBUG_TRACE, ("WPARetryExec---> ReTryCounter=%d, WpaState=%d \n", pEntry->ReTryCounter, pEntry->WpaState)); switch (pEntry->AuthMode) { case Ndis802_11AuthModeWPA: case Ndis802_11AuthModeWPAPSK: case Ndis802_11AuthModeWPA2: case Ndis802_11AuthModeWPA2PSK: /* 1. GTK already retried, give up and disconnect client. */ if (pEntry->ReTryCounter > (GROUP_MSG1_RETRY_TIMER_CTR + 1)) { /* send wireless event - for group key handshaking timeout */ RTMPSendWirelessEvent(pAd, IW_GROUP_HS_TIMEOUT_EVENT_FLAG, pEntry->Addr, pEntry->apidx, 0); DBGPRINT(RT_DEBUG_TRACE, ("WPARetryExec::Group Key HS exceed retry count, Disassociate client, pEntry->ReTryCounter %d\n", pEntry->ReTryCounter)); MlmeDeAuthAction(pAd, pEntry, REASON_GROUP_KEY_HS_TIMEOUT, FALSE); } /* 2. Retry GTK. */ else if (pEntry->ReTryCounter > GROUP_MSG1_RETRY_TIMER_CTR) { DBGPRINT(RT_DEBUG_TRACE, ("WPARetryExec::ReTry 2-way group-key Handshake \n")); if (pEntry->GTKState == REKEY_NEGOTIATING) { WPAStart2WayGroupHS(pAd, pEntry); RTMPSetTimer(&pEntry->RetryTimer, PEER_MSG3_RETRY_EXEC_INTV); } } /* 3. 4-way message 1 retried more than three times. Disconnect client */ else if (pEntry->ReTryCounter > (PEER_MSG1_RETRY_TIMER_CTR + 3)) { /* send wireless event - for pairwise key handshaking timeout */ RTMPSendWirelessEvent(pAd, IW_PAIRWISE_HS_TIMEOUT_EVENT_FLAG, pEntry->Addr, pEntry->apidx, 0); DBGPRINT(RT_DEBUG_TRACE, ("WPARetryExec::MSG1 timeout, pEntry->ReTryCounter = %d\n", pEntry->ReTryCounter)); MlmeDeAuthAction(pAd, pEntry, REASON_4_WAY_TIMEOUT, FALSE); } /* 4. Retry 4 way message 1, the last try, the timeout is 3 sec for EAPOL-Start */ else if (pEntry->ReTryCounter == (PEER_MSG1_RETRY_TIMER_CTR + 3)) { DBGPRINT(RT_DEBUG_TRACE, ("WPARetryExec::Retry MSG1, the last try\n")); WPAStart4WayHS(pAd , pEntry, PEER_MSG3_RETRY_EXEC_INTV); } /* 4. Retry 4 way message 1 */ else if (pEntry->ReTryCounter < (PEER_MSG1_RETRY_TIMER_CTR + 3)) { if ((pEntry->WpaState == AS_PTKSTART) || (pEntry->WpaState == AS_INITPSK) || (pEntry->WpaState == AS_INITPMK)) { DBGPRINT(RT_DEBUG_TRACE, ("WPARetryExec::ReTry MSG1 of 4-way Handshake\n")); WPAStart4WayHS(pAd, pEntry, PEER_MSG1_RETRY_EXEC_INTV); } } break; default: break; } } #ifdef APCLI_SUPPORT else if ((pEntry) && IS_ENTRY_APCLI(pEntry)) { if (pEntry->AuthMode == Ndis802_11AuthModeWPA || pEntry->AuthMode == Ndis802_11AuthModeWPAPSK) { PRTMP_ADAPTER pAd = (PRTMP_ADAPTER)pEntry->pAd; if (pEntry->MatchAPCLITabIdx < MAX_APCLI_NUM) { UCHAR ifIndex = pEntry->MatchAPCLITabIdx; DBGPRINT(RT_DEBUG_TRACE, ("(%s) ApCli interface[%d] startdown.\n", __FUNCTION__, ifIndex)); MlmeEnqueue(pAd, APCLI_CTRL_STATE_MACHINE, APCLI_CTRL_DISCONNECT_REQ, 0, NULL, ifIndex); } } } #endif /* APCLI_SUPPORT */ } /* ========================================================================== Description: Timer execution function for periodically updating group key. Return: ========================================================================== */ VOID GREKEYPeriodicExec( IN PVOID SystemSpecific1, IN PVOID FunctionContext, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3) { UINT i, apidx; ULONG temp_counter = 0; PRTMP_ADAPTER pAd = (PRTMP_ADAPTER)FunctionContext; PRALINK_TIMER_STRUCT pTimer = (PRALINK_TIMER_STRUCT) SystemSpecific3; PMULTISSID_STRUCT pMbss = NULL; for (apidx=0; apidxApCfg.BssidNum; apidx++) { if (&pAd->ApCfg.MBSSID[apidx].REKEYTimer == pTimer) break; } if (apidx == pAd->ApCfg.BssidNum) return; else pMbss = &pAd->ApCfg.MBSSID[apidx]; if (pMbss->AuthMode < Ndis802_11AuthModeWPA || pMbss->AuthMode > Ndis802_11AuthModeWPA1PSKWPA2PSK) return; if ((pMbss->WPAREKEY.ReKeyMethod == TIME_REKEY) && (pMbss->REKEYCOUNTER < 0xffffffff)) temp_counter = (++pMbss->REKEYCOUNTER); /* REKEYCOUNTER is incremented every MCAST packets transmitted, */ /* But the unit of Rekeyinterval is 1K packets */ else if (pMbss->WPAREKEY.ReKeyMethod == PKT_REKEY) temp_counter = pMbss->REKEYCOUNTER/1000; else { return; } if (temp_counter > (pMbss->WPAREKEY.ReKeyInterval)) { pMbss->REKEYCOUNTER = 0; pMbss->RekeyCountDown = 3; DBGPRINT(RT_DEBUG_TRACE, ("Rekey Interval Excess, GKeyDoneStations=%d\n", pMbss->StaCount)); /* take turn updating different groupkey index, */ if ((pMbss->StaCount) > 0) { /* change key index */ pMbss->DefaultKeyId = (pMbss->DefaultKeyId == 1) ? 2 : 1; /* Generate GNonce randomly */ GenRandom(pAd, pMbss->Bssid, pMbss->GNonce); /* Update GTK */ WpaDeriveGTK(pMbss->GMK, (UCHAR*)pMbss->GNonce, pMbss->Bssid, pMbss->GTK, LEN_TKIP_GTK); /* Process 2-way handshaking */ for (i = 0; i < MAX_LEN_OF_MAC_TABLE; i++) { MAC_TABLE_ENTRY *pEntry; pEntry = &pAd->MacTab.Content[i]; if (IS_ENTRY_CLIENT(pEntry) && (pEntry->WpaState == AS_PTKINITDONE) && (pEntry->apidx == apidx)) { pEntry->GTKState = REKEY_NEGOTIATING; WPAStart2WayGroupHS(pAd, pEntry); DBGPRINT(RT_DEBUG_TRACE, ("Rekey interval excess, Update Group Key for %x %x %x %x %x %x , DefaultKeyId= %x \n",\ pEntry->Addr[0],pEntry->Addr[1],\ pEntry->Addr[2],pEntry->Addr[3],\ pEntry->Addr[4],pEntry->Addr[5],\ pMbss->DefaultKeyId)); } } } } /* Use countdown to ensure the 2-way handshaking had completed */ if (pMbss->RekeyCountDown > 0) { pMbss->RekeyCountDown--; if (pMbss->RekeyCountDown == 0) { USHORT Wcid; /* Get a specific WCID to record this MBSS key attribute */ GET_GroupKey_WCID(pAd, Wcid, apidx); /* Install shared key table */ WPAInstallSharedKey(pAd, pMbss->GroupKeyWepStatus, apidx, pMbss->DefaultKeyId, Wcid, TRUE, pMbss->GTK, LEN_TKIP_GTK); } } } #ifdef DOT1X_SUPPORT /* ======================================================================== Routine Description: Sending EAP Req. frame to station in authenticating state. These frames come from Authenticator deamon. Arguments: pAdapter Pointer to our adapter pPacket Pointer to outgoing EAP frame body + 8023 Header Len length of pPacket Return Value: None ======================================================================== */ VOID WpaSend( IN PRTMP_ADAPTER pAdapter, IN PUCHAR pPacket, IN ULONG Len) { PEAP_HDR pEapHdr; UCHAR Addr[MAC_ADDR_LEN]; UCHAR Header802_3[LENGTH_802_3]; MAC_TABLE_ENTRY *pEntry; PUCHAR pData; NdisMoveMemory(Addr, pPacket, 6); NdisMoveMemory(Header802_3, pPacket, LENGTH_802_3); pEapHdr = (EAP_HDR*)(pPacket + LENGTH_802_3); pData = (pPacket + LENGTH_802_3); if ((pEntry = MacTableLookup(pAdapter, Addr)) == NULL) { return; } /* Send EAP frame to STA */ if (((pEntry->AuthMode >= Ndis802_11AuthModeWPA) && (pEapHdr->ProType != EAPOLKey)) || (pAdapter->ApCfg.MBSSID[pEntry->apidx].IEEE8021X == TRUE)) RTMPToWirelessSta(pAdapter, pEntry, Header802_3, LENGTH_802_3, pData, Len - LENGTH_802_3, (pEntry->PortSecured == WPA_802_1X_PORT_SECURED) ? FALSE : TRUE); if (RTMPEqualMemory((pPacket+12), EAPOL, 2)) { switch (pEapHdr->code) { case EAP_CODE_REQUEST: if ((pEntry->WpaState >= AS_PTKINITDONE) && (pEapHdr->ProType == EAPPacket)) { pEntry->WpaState = AS_AUTHENTICATION; DBGPRINT(RT_DEBUG_TRACE, ("Start to re-authentication by 802.1x daemon\n")); } break; /* After receiving EAP_SUCCESS, trigger state machine */ case EAP_CODE_SUCCESS: if ((pEntry->AuthMode >= Ndis802_11AuthModeWPA) && (pEapHdr->ProType != EAPOLKey)) { DBGPRINT(RT_DEBUG_TRACE,("Send EAP_CODE_SUCCESS\n\n")); if (pEntry->Sst == SST_ASSOC) { pEntry->WpaState = AS_INITPMK; /* Only set the expire and counters */ pEntry->ReTryCounter = PEER_MSG1_RETRY_TIMER_CTR; WPAStart4WayHS(pAdapter, pEntry, PEER_MSG1_RETRY_EXEC_INTV); } } else { pEntry->PrivacyFilter = Ndis802_11PrivFilterAcceptAll; pEntry->WpaState = AS_PTKINITDONE; pAdapter->ApCfg.MBSSID[pEntry->apidx].PortSecured = WPA_802_1X_PORT_SECURED; pEntry->PortSecured = WPA_802_1X_PORT_SECURED; #ifdef WSC_AP_SUPPORT if (pAdapter->ApCfg.MBSSID[pEntry->apidx].WscControl.WscConfMode != WSC_DISABLE) WscInformFromWPA(pEntry); #endif /* WSC_AP_SUPPORT */ DBGPRINT(RT_DEBUG_TRACE,("IEEE8021X-WEP : Send EAP_CODE_SUCCESS\n\n")); } break; case EAP_CODE_FAILURE: break; default: break; } } else { DBGPRINT(RT_DEBUG_TRACE, ("Send Deauth, Reason : REASON_NO_LONGER_VALID\n")); MlmeDeAuthAction(pAdapter, pEntry, REASON_NO_LONGER_VALID, FALSE); } } VOID RTMPAddPMKIDCache( IN PRTMP_ADAPTER pAd, IN INT apidx, IN PUCHAR pAddr, IN UCHAR *PMKID, IN UCHAR *PMK) { INT i, CacheIdx; /* Update PMKID status */ if ((CacheIdx = RTMPSearchPMKIDCache(pAd, apidx, pAddr)) != -1) { NdisGetSystemUpTime(&(pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[CacheIdx].RefreshTime)); NdisMoveMemory(&pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[CacheIdx].PMKID, PMKID, LEN_PMKID); NdisMoveMemory(&pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[CacheIdx].PMK, PMK, PMK_LEN); DBGPRINT(RT_DEBUG_TRACE, ("RTMPAddPMKIDCache update %02x:%02x:%02x:%02x:%02x:%02x cache(%d) from IF(ra%d)\n", pAddr[0], pAddr[1], pAddr[2], pAddr[3], pAddr[4], pAddr[5], CacheIdx, apidx)); return; } /* Add a new PMKID */ for (i = 0; i < MAX_PMKID_COUNT; i++) { if (!pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[i].Valid) { pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[i].Valid = TRUE; NdisGetSystemUpTime(&(pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[i].RefreshTime)); COPY_MAC_ADDR(&pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[i].MAC, pAddr); NdisMoveMemory(&pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[i].PMKID, PMKID, LEN_PMKID); NdisMoveMemory(&pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[i].PMK, PMK, PMK_LEN); DBGPRINT(RT_DEBUG_TRACE, ("RTMPAddPMKIDCache add %02x:%02x:%02x:%02x:%02x:%02x cache(%d) from IF(ra%d)\n", pAddr[0], pAddr[1], pAddr[2], pAddr[3], pAddr[4], pAddr[5], i, apidx)); break; } } if (i == MAX_PMKID_COUNT) { ULONG timestamp = 0, idx = 0; DBGPRINT(RT_DEBUG_TRACE, ("RTMPAddPMKIDCache(IF(%d) Cache full\n", apidx)); for (i = 0; i < MAX_PMKID_COUNT; i++) { if (pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[i].Valid) { if (((timestamp == 0) && (idx == 0)) || ((timestamp != 0) && timestamp < pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[i].RefreshTime)) { timestamp = pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[i].RefreshTime; idx = i; } } } pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[idx].Valid = TRUE; NdisGetSystemUpTime(&(pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[idx].RefreshTime)); COPY_MAC_ADDR(&pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[idx].MAC, pAddr); NdisMoveMemory(&pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[idx].PMKID, PMKID, LEN_PMKID); NdisMoveMemory(&pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[idx].PMK, PMK, PMK_LEN); DBGPRINT(RT_DEBUG_TRACE, ("RTMPAddPMKIDCache add %02x:%02x:%02x:%02x:%02x:%02x cache(%ld) from IF(ra%d)\n", pAddr[0], pAddr[1], pAddr[2], pAddr[3], pAddr[4], pAddr[5], idx, apidx)); } } INT RTMPSearchPMKIDCache( IN PRTMP_ADAPTER pAd, IN INT apidx, IN PUCHAR pAddr) { INT i = 0; for (i = 0; i < MAX_PMKID_COUNT; i++) { if ((pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[i].Valid) && MAC_ADDR_EQUAL(&pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[i].MAC, pAddr)) { DBGPRINT(RT_DEBUG_TRACE, ("RTMPSearchPMKIDCache %02x:%02x:%02x:%02x:%02x:%02x cache(%d) from IF(ra%d)\n", pAddr[0], pAddr[1], pAddr[2], pAddr[3], pAddr[4], pAddr[5], i, apidx)); break; } } if (i == MAX_PMKID_COUNT) { DBGPRINT(RT_DEBUG_TRACE, ("RTMPSearchPMKIDCache - IF(%d) not found\n", apidx)); return -1; } return i; } VOID RTMPDeletePMKIDCache( IN PRTMP_ADAPTER pAd, IN INT apidx, IN INT idx) { PAP_BSSID_INFO pInfo = &pAd->ApCfg.MBSSID[apidx].PMKIDCache.BSSIDInfo[idx]; if (pInfo->Valid) { pInfo->Valid = FALSE; DBGPRINT(RT_DEBUG_TRACE, ("RTMPDeletePMKIDCache(IF(%d), del PMKID CacheIdx=%d\n", apidx, idx)); } } VOID RTMPMaintainPMKIDCache( IN PRTMP_ADAPTER pAd) { INT i, j; ULONG Now; for (i = 0; i < MAX_MBSSID_NUM(pAd); i++) { PMULTISSID_STRUCT pMbss = &pAd->ApCfg.MBSSID[i]; for (j = 0; j < MAX_PMKID_COUNT; j++) { PAP_BSSID_INFO pBssInfo = &pMbss->PMKIDCache.BSSIDInfo[j]; NdisGetSystemUpTime(&Now); if ((pBssInfo->Valid) && /*((Now - pBssInfo->RefreshTime) >= pMbss->PMKCachePeriod)*/ (RTMP_TIME_AFTER(Now, (pBssInfo->RefreshTime + pMbss->PMKCachePeriod)))) { RTMPDeletePMKIDCache(pAd, i, j); } } } } #endif /* DOT1X_SUPPORT */ VOID RTMPGetTxTscFromAsic( IN PRTMP_ADAPTER pAd, IN UCHAR apidx, OUT PUCHAR pTxTsc) { USHORT Wcid; USHORT offset; UCHAR IvEiv[8]; int i; /* Sanity check of apidx */ if (apidx >= MAX_MBSSID_NUM(pAd)) { DBGPRINT(RT_DEBUG_ERROR, ("RTMPGetTxTscFromAsic : invalid apidx(%d)\n", apidx)); return; } /* Initial value */ NdisZeroMemory(IvEiv, 8); NdisZeroMemory(pTxTsc, 6); /* Get apidx for this BSSID */ GET_GroupKey_WCID(pAd, Wcid, apidx); /* When the group rekey action is triggered, a count-down(3 seconds) is started. During the count-down, use the initial PN as TSC. Otherwise, get the IVEIV from ASIC. */ if (pAd->ApCfg.MBSSID[apidx].RekeyCountDown > 0) { /* In IEEE 802.11-2007 8.3.3.4.3 described : The PN shall be implemented as a 48-bit monotonically incrementing non-negative integer, initialized to 1 when the corresponding temporal key is initialized or refreshed. */ IvEiv[0] = 1; } else { UINT32 temp1, temp2; /* Read IVEIV from Asic */ offset = MAC_IVEIV_TABLE_BASE + (Wcid * HW_IVEIV_ENTRY_SIZE); /* Use Read32 to avoid endian problem */ RTMP_IO_READ32(pAd, offset, &temp1); RTMP_IO_READ32(pAd, offset+4, &temp2); for ( i=0; i<4; i++) { IvEiv[i] = (UCHAR)(temp1 >> (i*8)); IvEiv[i+4] = (UCHAR)(temp2 >> (i*8)); } } /* Record current TxTsc */ if (pAd->ApCfg.MBSSID[apidx].GroupKeyWepStatus == Ndis802_11Encryption3Enabled) { /* AES */ *pTxTsc = IvEiv[0]; *(pTxTsc+1) = IvEiv[1]; *(pTxTsc+2) = IvEiv[4]; *(pTxTsc+3) = IvEiv[5]; *(pTxTsc+4) = IvEiv[6]; *(pTxTsc+5) = IvEiv[7]; } else { /* TKIP */ *pTxTsc = IvEiv[2]; *(pTxTsc+1) = IvEiv[0]; *(pTxTsc+2) = IvEiv[4]; *(pTxTsc+3) = IvEiv[5]; *(pTxTsc+4) = IvEiv[6]; *(pTxTsc+5) = IvEiv[7]; } DBGPRINT(RT_DEBUG_TRACE, ("RTMPGetTxTscFromAsic : WCID(%d) TxTsc 0x%02x-0x%02x-0x%02x-0x%02x-0x%02x-0x%02x \n", Wcid, *pTxTsc, *(pTxTsc+1), *(pTxTsc+2), *(pTxTsc+3), *(pTxTsc+4), *(pTxTsc+5))); } /* ========================================================================== Description: Set group re-key timer Return: ========================================================================== */ VOID WPA_APSetGroupRekeyAction( IN PRTMP_ADAPTER pAd) { UINT8 apidx = 0; for (apidx = 0; apidx < pAd->ApCfg.BssidNum; apidx++) { PMULTISSID_STRUCT pMbss = &pAd->ApCfg.MBSSID[apidx]; if ((pMbss->WepStatus == Ndis802_11Encryption2Enabled) || (pMbss->WepStatus == Ndis802_11Encryption3Enabled) || (pMbss->WepStatus == Ndis802_11Encryption4Enabled)) { /* Group rekey related */ if ((pMbss->WPAREKEY.ReKeyInterval != 0) && ((pMbss->WPAREKEY.ReKeyMethod == TIME_REKEY) || ( pMbss->WPAREKEY.ReKeyMethod == PKT_REKEY))) { /* Regularly check the timer */ if (pMbss->REKEYTimerRunning == FALSE) { RTMPSetTimer(&pMbss->REKEYTimer, GROUP_KEY_UPDATE_EXEC_INTV); pMbss->REKEYTimerRunning = TRUE; pMbss->REKEYCOUNTER = 0; } DBGPRINT(RT_DEBUG_TRACE, (" %s : Group rekey method= %ld , interval = 0x%lx\n", __FUNCTION__, pMbss->WPAREKEY.ReKeyMethod, pMbss->WPAREKEY.ReKeyInterval)); } else pMbss->REKEYTimerRunning = FALSE; } } } #ifdef QOS_DLS_SUPPORT VOID RTMPHandleSTAKey( IN PRTMP_ADAPTER pAd, IN PMAC_TABLE_ENTRY pEntry, IN MLME_QUEUE_ELEM *Elem) { extern UCHAR OUI_WPA2_WEP40[]; ULONG FrameLen = 0; PUCHAR pOutBuffer = NULL; UCHAR Header802_3[14]; UCHAR *mpool; PEAPOL_PACKET pOutPacket; PEAPOL_PACKET pSTAKey; PHEADER_802_11 pHeader; UCHAR Offset = 0; ULONG MICMsgLen; UCHAR DA[MAC_ADDR_LEN]; UCHAR Key_Data[512]; UCHAR key_length; UCHAR mic[LEN_KEY_DESC_MIC]; UCHAR rcv_mic[LEN_KEY_DESC_MIC]; UCHAR digest[80]; UCHAR temp[64]; PMAC_TABLE_ENTRY pDaEntry; /*Benson add for big-endian 20081016--> */ KEY_INFO peerKeyInfo; /*Benson add 20081016 <-- */ DBGPRINT(RT_DEBUG_TRACE, ("==> RTMPHandleSTAKey\n")); if (!pEntry) return; if ((pEntry->WpaState != AS_PTKINITDONE)) { DBGPRINT(RT_DEBUG_ERROR, ("Not expect calling STAKey hand shaking here")); return; } pHeader = (PHEADER_802_11) Elem->Msg; /* QoS control field (2B) is took off */ /* if (pHeader->FC.SubType & 0x08) */ /* Offset += 2; */ pSTAKey = (PEAPOL_PACKET)&Elem->Msg[LENGTH_802_11 + LENGTH_802_1_H + Offset]; /*Benson add for big-endian 20081016--> */ NdisZeroMemory((PUCHAR)&peerKeyInfo, sizeof(peerKeyInfo)); NdisMoveMemory((PUCHAR)&peerKeyInfo, (PUCHAR)&pSTAKey->KeyDesc.KeyInfo, sizeof(KEY_INFO)); *((USHORT *)&peerKeyInfo) = cpu2le16(*((USHORT *)&peerKeyInfo)); /*Benson add 20081016 <-- */ /* Check Replay Counter */ if (!RTMPEqualMemory(pSTAKey->KeyDesc.ReplayCounter, pEntry->R_Counter, LEN_KEY_DESC_REPLAY)) { DBGPRINT(RT_DEBUG_ERROR, ("Replay Counter Different in STAKey handshake!! \n")); DBGPRINT(RT_DEBUG_ERROR, ("Receive : %d %d %d %d \n", pSTAKey->KeyDesc.ReplayCounter[0], pSTAKey->KeyDesc.ReplayCounter[1], pSTAKey->KeyDesc.ReplayCounter[2], pSTAKey->KeyDesc.ReplayCounter[3])); DBGPRINT(RT_DEBUG_ERROR, ("Current : %d %d %d %d \n", pEntry->R_Counter[4],pEntry->R_Counter[5], pEntry->R_Counter[6],pEntry->R_Counter[7])); return; } /* Check MIC, if not valid, discard silently */ NdisMoveMemory(DA, &pSTAKey->KeyDesc.KeyData[6], MAC_ADDR_LEN); if (peerKeyInfo.KeyMic && peerKeyInfo.Secure && peerKeyInfo.Request)/*Benson add for big-endian 20081016 --> */ { pEntry->bDlsInit = TRUE; DBGPRINT(RT_DEBUG_TRACE, ("STAKey Initiator: %02x:%02x:%02x:%02x:%02x:%02x\n", pEntry->Addr[0], pEntry->Addr[1], pEntry->Addr[2], pEntry->Addr[3], pEntry->Addr[4], pEntry->Addr[5])); } MICMsgLen = pSTAKey->Body_Len[1] | ((pSTAKey->Body_Len[0]<<8) && 0xff00); MICMsgLen += LENGTH_EAPOL_H; if (MICMsgLen > (Elem->MsgLen - LENGTH_802_11 - LENGTH_802_1_H)) { DBGPRINT(RT_DEBUG_ERROR, ("Receive wrong format EAPOL packets \n")); return; } /* This is proprietary DLS protocol, it will be adhered when spec. is finished. */ NdisZeroMemory(temp, 64); NdisZeroMemory(pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK, sizeof(pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK)); NdisMoveMemory(temp, "IEEE802.11 WIRELESS ACCESS POINT", 32); WpaDerivePTK(pAd, temp, temp, pAd->ApCfg.MBSSID[pEntry->apidx].Bssid, temp, pEntry->Addr, pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK, LEN_PTK); DBGPRINT(RT_DEBUG_TRACE, ("PTK-%x %x %x %x %x %x %x %x \n", pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[0], pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[1], pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[2], pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[3], pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[4], pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[5], pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[6], pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[7])); /* Record the received MIC for check later */ NdisMoveMemory(rcv_mic, pSTAKey->KeyDesc.KeyMic, LEN_KEY_DESC_MIC); NdisZeroMemory(pSTAKey->KeyDesc.KeyMic, LEN_KEY_DESC_MIC); if (pEntry->WepStatus == Ndis802_11Encryption2Enabled) { RT_HMAC_MD5(pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK, LEN_PTK_KCK, (PUCHAR)pSTAKey, MICMsgLen, mic, MD5_DIGEST_SIZE); } else { RT_HMAC_SHA1(pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK, LEN_PTK_KCK, (PUCHAR)pSTAKey, MICMsgLen, digest, SHA1_DIGEST_SIZE); NdisMoveMemory(mic, digest, LEN_KEY_DESC_MIC); } if (!RTMPEqualMemory(rcv_mic, mic, LEN_KEY_DESC_MIC)) { DBGPRINT(RT_DEBUG_ERROR, ("MIC Different in STAKey handshake!! \n")); return; } else DBGPRINT(RT_DEBUG_TRACE, ("MIC VALID in STAKey handshake!! \n")); /* Receive init STA's STAKey Message-2, and terminate the handshake */ /*if (pEntry->bDlsInit && !pSTAKey->KeyDesc.KeyInfo.Request) */ if (pEntry->bDlsInit && !peerKeyInfo.Request) /*Benson add for big-endian 20081016 --> */ { pEntry->bDlsInit = FALSE; DBGPRINT(RT_DEBUG_TRACE, ("Receive init STA's STAKey Message-2, STAKey handshake finished \n")); return; } /* Receive init STA's STAKey Message-2, and terminate the handshake */ if (RTMPEqualMemory(&pSTAKey->KeyDesc.KeyData[2], OUI_WPA2_WEP40, 3)) { DBGPRINT(RT_DEBUG_WARN, ("Receive a STAKey message which not support currently, just drop it \n")); return; } do { pDaEntry = MacTableLookup(pAd, DA); if (!pDaEntry) break; if ((pDaEntry->WpaState != AS_PTKINITDONE)) { DBGPRINT(RT_DEBUG_ERROR, ("Not expect calling STAKey hand shaking here \n")); break; } MlmeAllocateMemory(pAd, (PUCHAR *)&pOutBuffer); /* allocate memory */ if(pOutBuffer == NULL) break; MAKE_802_3_HEADER(Header802_3, pDaEntry->Addr, pAd->ApCfg.MBSSID[pDaEntry->apidx].Bssid, EAPOL); /* Increment replay counter by 1 */ ADD_ONE_To_64BIT_VAR(pDaEntry->R_Counter); /* Allocate memory for output */ os_alloc_mem(NULL, (PUCHAR *)&mpool, TX_EAPOL_BUFFER); if (mpool == NULL) { MlmeFreeMemory(pAd, (PUCHAR)pOutBuffer); DBGPRINT(RT_DEBUG_ERROR, ("!!!%s : no memory!!!\n", __FUNCTION__)); return; } pOutPacket = (PEAPOL_PACKET)mpool; NdisZeroMemory(pOutPacket, TX_EAPOL_BUFFER); /* 0. init Packet and Fill header */ pOutPacket->ProVer = EAPOL_VER; pOutPacket->ProType = EAPOLKey; pOutPacket->Body_Len[1] = 0x5f; /* 1. Fill replay counter */ /* NdisMoveMemory(pDaEntry->R_Counter, pAd->ApCfg.R_Counter, sizeof(pDaEntry->R_Counter)); */ NdisMoveMemory(pOutPacket->KeyDesc.ReplayCounter, pDaEntry->R_Counter, LEN_KEY_DESC_REPLAY); /* 2. Fill key version, keyinfo, key len */ pOutPacket->KeyDesc.KeyInfo.KeyDescVer= GROUP_KEY; pOutPacket->KeyDesc.KeyInfo.KeyType = GROUPKEY; pOutPacket->KeyDesc.KeyInfo.Install = 1; pOutPacket->KeyDesc.KeyInfo.KeyAck = 1; pOutPacket->KeyDesc.KeyInfo.KeyMic = 1; pOutPacket->KeyDesc.KeyInfo.Secure = 1; pOutPacket->KeyDesc.KeyInfo.EKD_DL = 1; DBGPRINT(RT_DEBUG_TRACE, ("STAKey handshake for peer STA %02x:%02x:%02x:%02x:%02x:%02x\n", DA[0], DA[1], DA[2], DA[3], DA[4], DA[5])); if ((pDaEntry->AuthMode == Ndis802_11AuthModeWPA) || (pDaEntry->AuthMode == Ndis802_11AuthModeWPAPSK)) { pOutPacket->KeyDesc.Type = WPA1_KEY_DESC; DBGPRINT(RT_DEBUG_TRACE, ("pDaEntry->AuthMode == Ndis802_11AuthModeWPA/WPAPSK\n")); } else if ((pDaEntry->AuthMode == Ndis802_11AuthModeWPA2) || (pDaEntry->AuthMode == Ndis802_11AuthModeWPA2PSK)) { pOutPacket->KeyDesc.Type = WPA2_KEY_DESC; pOutPacket->KeyDesc.KeyDataLen[1] = 0; DBGPRINT(RT_DEBUG_TRACE, ("pDaEntry->AuthMode == Ndis802_11AuthModeWPA2/WPA2PSK\n")); } pOutPacket->KeyDesc.KeyLength[1] = LEN_TKIP_TK; pOutPacket->KeyDesc.KeyDataLen[1] = LEN_TKIP_TK; pOutPacket->KeyDesc.KeyInfo.KeyDescVer = KEY_DESC_TKIP; if (pDaEntry->WepStatus == Ndis802_11Encryption3Enabled) { pOutPacket->KeyDesc.KeyLength[1] = LEN_AES_TK; pOutPacket->KeyDesc.KeyDataLen[1] = LEN_AES_TK; pOutPacket->KeyDesc.KeyInfo.KeyDescVer = KEY_DESC_AES; } /* Key Data Encapsulation format, use Ralink OUI to distinguish proprietary and standard. */ Key_Data[0] = 0xDD; Key_Data[1] = 0x00; /* Length (This field will be filled later) */ Key_Data[2] = 0x00; /* OUI */ Key_Data[3] = 0x0C; /* OUI */ Key_Data[4] = 0x43; /* OUI */ Key_Data[5] = 0x02; /* Data Type (STAKey Key Data Encryption) */ /* STAKey Data Encapsulation format */ Key_Data[6] = 0x00; /*Reserved */ Key_Data[7] = 0x00; /*Reserved */ /* STAKey MAC address */ NdisMoveMemory(&Key_Data[8], pEntry->Addr, MAC_ADDR_LEN); /* initiator MAC address */ /* STAKey (Handle the difference between TKIP and AES-CCMP) */ if (pDaEntry->WepStatus == Ndis802_11Encryption3Enabled) { Key_Data[1] = 0x1E; /* 4+2+6+16(OUI+Reserved+STAKey_MAC_Addr+STAKey) */ NdisMoveMemory(&Key_Data[14], pEntry->PairwiseKey.Key, LEN_AES_TK); } else { Key_Data[1] = 0x2E; /* 4+2+6+32(OUI+Reserved+STAKey_MAC_Addr+STAKey) */ NdisMoveMemory(&Key_Data[14], pEntry->PairwiseKey.Key, LEN_TK); NdisMoveMemory(&Key_Data[14+LEN_TK], pEntry->PairwiseKey.TxMic, LEN_TKIP_MIC); NdisMoveMemory(&Key_Data[14+LEN_TK+LEN_TKIP_MIC], pEntry->PairwiseKey.RxMic, LEN_TKIP_MIC); } key_length = Key_Data[1]; pOutPacket->Body_Len[1] = key_length + 0x5f; /* This is proprietary DLS protocol, it will be adhered when spec. is finished. */ NdisZeroMemory(temp, 64); NdisMoveMemory(temp, "IEEE802.11 WIRELESS ACCESS POINT", 32); WpaDerivePTK(pAd, temp, temp, pAd->ApCfg.MBSSID[pEntry->apidx].Bssid, temp, DA, pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK, LEN_PTK); DBGPRINT(RT_DEBUG_TRACE, ("PTK-0-%x %x %x %x %x %x %x %x \n", pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[0], pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[1], pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[2], pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[3], pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[4], pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[5], pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[6], pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK[7])); NdisMoveMemory(pOutPacket->KeyDesc.KeyData, Key_Data, key_length); NdisZeroMemory(mic, sizeof(mic)); *(USHORT *)(&pOutPacket->KeyDesc.KeyInfo) = cpu2le16(*(USHORT *)(&pOutPacket->KeyDesc.KeyInfo)); MakeOutgoingFrame(pOutBuffer, &FrameLen, pOutPacket->Body_Len[1] + 4, pOutPacket, END_OF_ARGS); /* Calculate MIC */ if (pDaEntry->WepStatus == Ndis802_11Encryption3Enabled) { RT_HMAC_SHA1(pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK, LEN_PTK_KCK, pOutBuffer, FrameLen, digest, SHA1_DIGEST_SIZE); NdisMoveMemory(pOutPacket->KeyDesc.KeyMic, digest, LEN_KEY_DESC_MIC); } else { RT_HMAC_MD5(pAd->ApCfg.MBSSID[pEntry->apidx].DlsPTK, LEN_PTK_KCK, pOutBuffer, FrameLen, mic, MD5_DIGEST_SIZE); NdisMoveMemory(pOutPacket->KeyDesc.KeyMic, mic, LEN_KEY_DESC_MIC); } RTMPToWirelessSta(pAd, pDaEntry, Header802_3, LENGTH_802_3, (PUCHAR)pOutPacket, pOutPacket->Body_Len[1] + 4, FALSE); MlmeFreeMemory(pAd, (PUCHAR)pOutBuffer); os_free_mem(NULL, mpool); }while(FALSE); DBGPRINT(RT_DEBUG_TRACE, ("<== RTMPHandleSTAKey: FrameLen=%ld\n", FrameLen)); } #endif /* QOS_DLS_SUPPORT */ #ifdef HOSTAPD_SUPPORT /*for sending an event to notify hostapd about michael failure. */ VOID ieee80211_notify_michael_failure( IN PRTMP_ADAPTER pAd, IN PHEADER_802_11 pHeader, IN UINT keyix, IN INT report) { static const char *tag = "MLME-MICHAELMICFAILURE.indication"; /* struct net_device *dev = pAd->net_dev; */ /* union iwreq_data wrqu; */ char buf[128]; /* XXX */ /* TODO: needed parameters: count, keyid, key type, src address, TSC */ if(report)/*station reports a mic error to this ap. */ { snprintf(buf, sizeof(buf), "%s(keyid=%d %scast addr=%s)", tag, keyix, "uni", ether_sprintf(pHeader->Addr2)); } else/*ap itself receives a mic error. */ { snprintf(buf, sizeof(buf), "%s(keyid=%d %scast addr=%s)", tag, keyix, IEEE80211_IS_MULTICAST(pHeader->Addr1) ? "broad" : "uni", ether_sprintf(pHeader->Addr2)); } RtmpOSWrielessEventSend(pAd->net_dev, RT_WLAN_EVENT_CUSTOM, -1, NULL, NULL, 0); /* NdisZeroMemory(&wrqu, sizeof(wrqu)); */ /* wrqu.data.length = strlen(buf); */ /* wireless_send_event(dev, RT_WLAN_EVENT_CUSTOM, &wrqu, buf); */ } const CHAR* ether_sprintf(const UINT8 *mac) { static char etherbuf[18]; snprintf(etherbuf,sizeof(etherbuf),"%02x:%02x:%02x:%02x:%02x:%02x",mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); return etherbuf; } #endif /* HOSTAPD_SUPPORT */ #ifdef APCLI_SUPPORT #ifdef APCLI_WPA_SUPPLICANT_SUPPORT VOID ApcliWpaSendEapolStart( IN PRTMP_ADAPTER pAd, IN PUCHAR pBssid, IN PMAC_TABLE_ENTRY pMacEntry, IN PAPCLI_STRUCT pApCliEntry) { IEEE8021X_FRAME Packet; UCHAR Header802_3[14]; DBGPRINT(RT_DEBUG_TRACE, ("-----> ApCliWpaSendEapolStart\n")); NdisZeroMemory(Header802_3,sizeof(UCHAR)*14); MAKE_802_3_HEADER(Header802_3, pBssid, &pApCliEntry->CurrentAddress[0], EAPOL); // Zero message 2 body NdisZeroMemory(&Packet, sizeof(Packet)); Packet.Version = EAPOL_VER; Packet.Type = EAPOLStart; Packet.Length = cpu2be16(0); // Copy frame to Tx ring RTMPToWirelessSta((PRTMP_ADAPTER)pAd, pMacEntry, Header802_3, LENGTH_802_3, (PUCHAR)&Packet, 4, TRUE); DBGPRINT(RT_DEBUG_TRACE, ("<----- WpaSendEapolStart\n")); } #define LENGTH_EAP_H 4 // If the received frame is EAP-Packet ,find out its EAP-Code (Request(0x01), Response(0x02), Success(0x03), Failure(0x04)). INT ApcliWpaCheckEapCode( IN PRTMP_ADAPTER pAd, IN PUCHAR pFrame, IN USHORT FrameLen, IN USHORT OffSet) { PUCHAR pData; INT result = 0; if( FrameLen < OffSet + LENGTH_EAPOL_H + LENGTH_EAP_H ) return result; pData = pFrame + OffSet; // skip offset bytes if(*(pData+1) == EAPPacket) // 802.1x header - Packet Type { result = *(pData+4); // EAP header - Code } return result; } #endif /* APCLI_WPA_SUPPLICANT_SUPPORT */ #endif/*APCLI_SUPPORT*/