aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Wetzel <alexander.wetzel@web.de>2015-11-01 18:49:42 +0100
committerAlexis La Goutte <alexis.lagoutte@gmail.com>2015-11-09 11:28:14 +0000
commitcb3dd958af31099772c8934179e113929ae0c020 (patch)
treef65bfcfea17b718ad59ea727968c6669453c489b
parentd4985a5acb63460797f4d009366a2038b0797570 (diff)
WPA/WPA2 decoding fixes and improvements
- start decoding when we have eapol1+2 packets Do not insist on a complete captured handshake, decode what we can. - more robust way to detect eapol #2 packets At least Win 10 is violating the spec on rekey by setting the secure bit in #2. Unpatched version shows and handles #2 as #4, breaking decoding after rekey. - fixed eapol rekey key handling Inital patch (see https://code.wireshark.org/review/8268) is adding redundant keys, since it scans all the time and not only once. - ignore tailing garbage after eapol sections in frame See https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9065#c8 Included testcase to test decode for incomplete handshakes and eapol2 packets with secure bit set on rekey. Ping-Bug: 9065 Change-Id: Id775088db9b5aaa80da9efdeed6902d024b5c0cd Reviewed-on: https://code.wireshark.org/review/11484 Reviewed-by: Michael Mann <mmann78@netscape.net> Petri-Dish: Michael Mann <mmann78@netscape.net> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
-rw-r--r--epan/crypt/airpdcap.c367
-rw-r--r--epan/crypt/airpdcap_debug.h2
-rw-r--r--epan/crypt/airpdcap_system.h3
-rw-r--r--epan/crypt/airpdcap_user.h11
-rw-r--r--epan/dissectors/packet-ieee80211.c87
-rw-r--r--test/captures/wpa-test-decode.pcap.gzbin0 -> 167294 bytes
-rw-r--r--test/config/80211_keys.tmpl1
-rwxr-xr-xtest/suite-decryption.sh17
8 files changed, 227 insertions, 261 deletions
diff --git a/epan/crypt/airpdcap.c b/epan/crypt/airpdcap.c
index a5cc0fdf4e..42c67ddf3c 100644
--- a/epan/crypt/airpdcap.c
+++ b/epan/crypt/airpdcap.c
@@ -523,7 +523,7 @@ static INT AirPDcapScanForKeys(
/* get and check the body length (IEEE 802.1X-2004, pg. 25) */
bodyLength=pntoh16(data+offset+2);
- if ((tot_len-offset-4) > bodyLength) {
+ if ((tot_len-offset-4) < bodyLength) { /* Only check if frame is long enough for eapol header, ignore tailing garbage, see bug 9065 */
AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapScanForKeys", "EAPOL body too short", AIRPDCAP_DEBUG_LEVEL_3);
return AIRPDCAP_RET_NO_VALID_HANDSHAKE;
}
@@ -548,6 +548,7 @@ static INT AirPDcapScanForKeys(
/* search for a cached Security Association for current BSSID and AP */
sa = AirPDcapGetSaPtr(ctx, &id);
if (sa == NULL){
+ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapScanForKeys", "No SA for BSSID found", AIRPDCAP_DEBUG_LEVEL_3);
return AIRPDCAP_RET_UNSUCCESS;
}
@@ -618,11 +619,12 @@ INT AirPDcapPacketProcess(
UCHAR *decrypt_data,
guint *decrypt_len,
PAIRPDCAP_KEY_ITEM key,
- gboolean mngHandshake,
- gboolean mngDecrypt)
+ gboolean scanHandshake)
{
const UCHAR *addr;
AIRPDCAP_SEC_ASSOCIATION_ID id;
+ UCHAR tmp_data[AIRPDCAP_MAX_CAPLEN];
+ guint tmp_len;
#ifdef _DEBUG
#define MSGBUF_LEN 255
@@ -657,9 +659,6 @@ INT AirPDcapPacketProcess(
/* get BSSID */
if ( (addr=AirPDcapGetBssidAddress((const AIRPDCAP_MAC_FRAME_ADDR4 *)(data))) != NULL) {
memcpy(id.bssid, addr, AIRPDCAP_MAC_LEN);
-#ifdef _DEBUG
- g_snprintf(msgbuf, MSGBUF_LEN, "BSSID: %2X.%2X.%2X.%2X.%2X.%2X\t", id.bssid[0],id.bssid[1],id.bssid[2],id.bssid[3],id.bssid[4],id.bssid[5]);
-#endif
AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", msgbuf, AIRPDCAP_DEBUG_LEVEL_3);
} else {
AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "BSSID not found", AIRPDCAP_DEBUG_LEVEL_5);
@@ -669,9 +668,6 @@ INT AirPDcapPacketProcess(
/* get STA address */
if ( (addr=AirPDcapGetStaAddress((const AIRPDCAP_MAC_FRAME_ADDR4 *)(data))) != NULL) {
memcpy(id.sta, addr, AIRPDCAP_MAC_LEN);
-#ifdef _DEBUG
- g_snprintf(msgbuf, MSGBUF_LEN, "ST_MAC: %2X.%2X.%2X.%2X.%2X.%2X\t", id.sta[0],id.sta[1],id.sta[2],id.sta[3],id.sta[4],id.sta[5]);
-#endif
AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", msgbuf, AIRPDCAP_DEBUG_LEVEL_3);
} else {
AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "SA not found", AIRPDCAP_DEBUG_LEVEL_5);
@@ -679,77 +675,78 @@ INT AirPDcapPacketProcess(
}
/* check if data is encrypted (use the WEP bit in the Frame Control field) */
- if (AIRPDCAP_WEP(data[1])==0)
- {
- if (mngHandshake) {
+ if (AIRPDCAP_WEP(data[1])==0) {
+ if (scanHandshake) {
/* data is sent in cleartext, check if is an authentication message or end the process */
AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "Unencrypted data", AIRPDCAP_DEBUG_LEVEL_3);
return (AirPDcapScanForKeys(ctx, data, mac_header_len, tot_len, id, key));
}
} else {
- if (mngDecrypt) {
- PAIRPDCAP_SEC_ASSOCIATION sa;
- int offset = 0;
+ PAIRPDCAP_SEC_ASSOCIATION sa;
+ int offset = 0;
- /* get the Security Association structure for the STA and AP */
- sa = AirPDcapGetSaPtr(ctx, &id);
- if (sa == NULL){
- return AIRPDCAP_RET_UNSUCCESS;
- }
+ /* get the Security Association structure for the STA and AP */
+ sa = AirPDcapGetSaPtr(ctx, &id);
+ if (sa == NULL){
+ return AIRPDCAP_RET_UNSUCCESS;
+ }
- /* cache offset in the packet data (to scan encryption data) */
- offset = mac_header_len;
+ /* cache offset in the packet data (to scan encryption data) */
+ offset = mac_header_len;
- if (decrypt_data==NULL)
- return AIRPDCAP_RET_UNSUCCESS;
+ if (decrypt_data==NULL) {
+ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "no decrypt buffer, use local", AIRPDCAP_DEBUG_LEVEL_3);
+ decrypt_data=tmp_data;
+ decrypt_len=&tmp_len;
+ }
- /* create new header and data to modify */
- *decrypt_len = tot_len;
- memcpy(decrypt_data, data, *decrypt_len);
+ /* create new header and data to modify */
+ *decrypt_len = tot_len;
+ memcpy(decrypt_data, data, *decrypt_len);
- /* encrypted data */
- AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "Encrypted data", AIRPDCAP_DEBUG_LEVEL_3);
+ /* encrypted data */
+ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "Encrypted data", AIRPDCAP_DEBUG_LEVEL_3);
- /* check the Extension IV to distinguish between WEP encryption and WPA encryption */
- /* refer to IEEE 802.11i-2004, 8.2.1.2, pag.35 for WEP, */
- /* IEEE 802.11i-2004, 8.3.2.2, pag. 45 for TKIP, */
- /* IEEE 802.11i-2004, 8.3.3.2, pag. 57 for CCMP */
- if (AIRPDCAP_EXTIV(data[offset+3])==0) {
- AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "WEP encryption", AIRPDCAP_DEBUG_LEVEL_3);
- return AirPDcapWepMng(ctx, decrypt_data, mac_header_len, decrypt_len, key, sa, offset);
- } else {
- AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "TKIP or CCMP encryption", AIRPDCAP_DEBUG_LEVEL_3);
+ /* check the Extension IV to distinguish between WEP encryption and WPA encryption */
+ /* refer to IEEE 802.11i-2004, 8.2.1.2, pag.35 for WEP, */
+ /* IEEE 802.11i-2004, 8.3.2.2, pag. 45 for TKIP, */
+ /* IEEE 802.11i-2004, 8.3.3.2, pag. 57 for CCMP */
+ if (AIRPDCAP_EXTIV(data[offset+3])==0) {
+ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "WEP encryption", AIRPDCAP_DEBUG_LEVEL_3);
+ return AirPDcapWepMng(ctx, decrypt_data, mac_header_len, decrypt_len, key, sa, offset);
+ } else {
+ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "TKIP or CCMP encryption", AIRPDCAP_DEBUG_LEVEL_3);
- /* If index >= 1, then use the group key. This will not work if the AP is using
- more than one group key simultaneously. I've not seen this in practice, however.
- Usually an AP will rotate between the two key index values of 1 and 2 whenever
- it needs to change the group key to be used. */
- if (AIRPDCAP_KEY_INDEX(data[offset+3])>=1){
+ /* If index >= 1, then use the group key. This will not work if the AP is using
+ more than one group key simultaneously. I've not seen this in practice, however.
+ Usually an AP will rotate between the two key index values of 1 and 2 whenever
+ it needs to change the group key to be used. */
+ if (AIRPDCAP_KEY_INDEX(data[offset+3])>=1){
- AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "The key index = 1. This is encrypted with a group key.", AIRPDCAP_DEBUG_LEVEL_3);
+ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "The key index = 1. This is encrypted with a group key.", AIRPDCAP_DEBUG_LEVEL_3);
- /* force STA address to broadcast MAC so we load the SA for the groupkey */
- memcpy(id.sta, broadcast_mac, AIRPDCAP_MAC_LEN);
+ /* force STA address to broadcast MAC so we load the SA for the groupkey */
+ memcpy(id.sta, broadcast_mac, AIRPDCAP_MAC_LEN);
#ifdef _DEBUG
- g_snprintf(msgbuf, MSGBUF_LEN, "ST_MAC: %2X.%2X.%2X.%2X.%2X.%2X\t", id.sta[0],id.sta[1],id.sta[2],id.sta[3],id.sta[4],id.sta[5]);
- AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", msgbuf, AIRPDCAP_DEBUG_LEVEL_3);
+ g_snprintf(msgbuf, MSGBUF_LEN, "ST_MAC: %2X.%2X.%2X.%2X.%2X.%2X\t", id.sta[0],id.sta[1],id.sta[2],id.sta[3],id.sta[4],id.sta[5]);
+ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", msgbuf, AIRPDCAP_DEBUG_LEVEL_3);
#endif
- /* search for a cached Security Association for current BSSID and broadcast MAC */
- sa = AirPDcapGetSaPtr(ctx, &id);
- if (sa == NULL){
- return AIRPDCAP_RET_UNSUCCESS;
- }
- }
+ /* search for a cached Security Association for current BSSID and broadcast MAC */
+ sa = AirPDcapGetSaPtr(ctx, &id);
+ if (sa == NULL)
+ return AIRPDCAP_RET_UNSUCCESS;
+ }
- /* Decrypt the packet using the appropriate SA */
- if (AirPDcapRsnaMng(decrypt_data, mac_header_len, decrypt_len, key, sa, offset) == AIRPDCAP_RET_SUCCESS)
- {
- /* If we successfully decrypted a packet, scan it to see if it contains a key handshake.
- The group key handshake could be sent at any time the AP wants to change the key (such as when
- it is using key rotation) and it also could be a rekey for the Pairwise key. So we must scan every packet. */
- AirPDcapScanForKeys(ctx, decrypt_data, mac_header_len, *decrypt_len, id, NULL);
+ /* Decrypt the packet using the appropriate SA */
+ if (AirPDcapRsnaMng(decrypt_data, mac_header_len, decrypt_len, key, sa, offset) == AIRPDCAP_RET_SUCCESS) {
+ /* If we successfully decrypted a packet, scan it to see if it contains a key handshake.
+ The group key handshake could be sent at any time the AP wants to change the key (such as when
+ it is using key rotation) and it also could be a rekey for the Pairwise key. So we must scan every packet. */
+ if (scanHandshake) {
+ return (AirPDcapScanForKeys(ctx, decrypt_data, mac_header_len, *decrypt_len, id, NULL));
+ } else {
return AIRPDCAP_RET_SUCCESS;
}
}
@@ -980,8 +977,9 @@ AirPDcapRsnaMng(
AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsnaMng", "No key associated", AIRPDCAP_DEBUG_LEVEL_3);
return AIRPDCAP_RET_REQ_DATA;
}
- if (sa->validKey==FALSE) {
- AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsnaMng", "Key not yet valid", AIRPDCAP_DEBUG_LEVEL_3);
+
+ if (*decrypt_len > try_data_len) {
+ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsnaMng", "Invalid decryption length", AIRPDCAP_DEBUG_LEVEL_3);
return AIRPDCAP_RET_UNSUCCESS;
}
@@ -991,11 +989,10 @@ AirPDcapRsnaMng(
/* start of loop added by GCS */
for(/* sa */; sa != NULL ;sa=sa->next) {
- if (*decrypt_len > try_data_len) {
- AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsnaMng", "Invalid decryption length", AIRPDCAP_DEBUG_LEVEL_3);
- g_free(try_data);
- return AIRPDCAP_RET_UNSUCCESS;
- }
+ if (sa->validKey==FALSE) {
+ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsnaMng", "Key not yet valid", AIRPDCAP_DEBUG_LEVEL_3);
+ continue;
+ }
/* copy the encrypted data into a temp buffer */
memcpy(try_data, decrypt_data, *decrypt_len);
@@ -1107,8 +1104,7 @@ AirPDcapWepMng(
}
/* obviously, try only WEP keys... */
- if (tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WEP)
- {
+ if (tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WEP) {
AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapWepMng", "Try WEP key...", AIRPDCAP_DEBUG_LEVEL_3);
memset(wep_key, 0, sizeof(wep_key));
@@ -1202,13 +1198,6 @@ AirPDcapRsna4WHandshake(
/* TODO timeouts? */
- /* This saves the sa since we are reauthenticating which will overwrite our current sa GCS*/
- if(sa->handshake == 4) {
- tmp_sa= g_new(AIRPDCAP_SEC_ASSOCIATION, 1);
- memcpy(tmp_sa, sa, sizeof(AIRPDCAP_SEC_ASSOCIATION));
- sa->next=tmp_sa;
- }
-
/* TODO consider key-index */
/* TODO considera Deauthentications */
@@ -1229,6 +1218,14 @@ AirPDcapRsna4WHandshake(
/* local value, the Supplicant discards the message. */
/* -> not checked, the Authenticator will be send another Message 1 (hopefully!) */
+ /* This saves the sa since we are reauthenticating which will overwrite our current sa GCS*/
+ if( sa->handshake >= 2) {
+ tmp_sa= g_new(AIRPDCAP_SEC_ASSOCIATION, 1);
+ memcpy(tmp_sa, sa, sizeof(AIRPDCAP_SEC_ASSOCIATION));
+ sa->validKey=FALSE;
+ sa->next=tmp_sa;
+ }
+
/* save ANonce (from authenticator) to derive the PTK with the SNonce (from the 2 message) */
memcpy(sa->wpa.nonce, data+offset+12, 32);
@@ -1245,139 +1242,117 @@ AirPDcapRsna4WHandshake(
AIRPDCAP_EAP_ACK(data[offset+1])==0 &&
AIRPDCAP_EAP_MIC(data[offset])==1)
{
- if (AIRPDCAP_EAP_SEC(data[offset])==0) {
-
- /* PATCH: some implementations set secure bit to 0 also in the 4th message */
- /* to recognize which message is this check if wep_key data length is 0 */
- /* in the 4th message */
- if (data[offset+92]!=0 || data[offset+93]!=0) {
- /* message 2 */
- AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "4-way handshake message 2", AIRPDCAP_DEBUG_LEVEL_3);
-
- /* On reception of Message 2, the Authenticator checks that the key replay counter corresponds to the */
- /* outstanding Message 1. If not, it silently discards the message. */
- /* If the calculated MIC does not match the MIC that the Supplicant included in the EAPOL-Key frame, */
- /* the Authenticator silently discards Message 2. */
- /* -> not checked; the Supplicant will send another message 2 (hopefully!) */
-
- /* now you can derive the PTK */
- for (key_index=0; key_index<(INT)ctx->keys_nr || useCache; key_index++) {
- /* use the cached one, or try all keys */
- if (!useCache) {
- AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "Try WPA key...", AIRPDCAP_DEBUG_LEVEL_3);
- tmp_key=&ctx->keys[key_index];
+ /* Check nonce to differentiate between message 2 or 4
+ * nonce will be non zero for message 2 and zero for message 4.
+ * At least needed for Windows, since it is setting the secure bit on message 2 when rekeying */
+ if (!memiszero(data+offset+12, 32)) {
+ /* message 2 */
+ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "4-way handshake message 2", AIRPDCAP_DEBUG_LEVEL_3);
+
+ /* On reception of Message 2, the Authenticator checks that the key replay counter corresponds to the */
+ /* outstanding Message 1. If not, it silently discards the message. */
+ /* If the calculated MIC does not match the MIC that the Supplicant included in the EAPOL-Key frame, */
+ /* the Authenticator silently discards Message 2. */
+ /* -> not checked; the Supplicant will send another message 2 (hopefully!) */
+
+ /* now you can derive the PTK */
+ for (key_index=0; key_index<(INT)ctx->keys_nr || useCache; key_index++) {
+ /* use the cached one, or try all keys */
+ if (!useCache) {
+ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "Try WPA key...", AIRPDCAP_DEBUG_LEVEL_3);
+ tmp_key=&ctx->keys[key_index];
+ } else {
+ /* there is a cached key in the security association, if it's a WPA key try it... */
+ if (sa->key!=NULL &&
+ (sa->key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PWD ||
+ sa->key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PSK ||
+ sa->key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PMK)) {
+ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "Try cached WPA key...", AIRPDCAP_DEBUG_LEVEL_3);
+ tmp_key=sa->key;
} else {
- /* there is a cached key in the security association, if it's a WPA key try it... */
- if (sa->key!=NULL &&
- (sa->key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PWD ||
- sa->key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PSK ||
- sa->key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PMK)) {
- AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "Try cached WPA key...", AIRPDCAP_DEBUG_LEVEL_3);
- tmp_key=sa->key;
- } else {
- AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "Cached key is of a wrong type, try WPA key...", AIRPDCAP_DEBUG_LEVEL_3);
- tmp_key=&ctx->keys[key_index];
- }
- }
-
- /* obviously, try only WPA keys... */
- if (tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PWD ||
- tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PSK ||
- tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PMK)
- {
- if (tmp_key->KeyType == AIRPDCAP_KEY_TYPE_WPA_PWD && tmp_key->UserPwd.SsidLen == 0 && ctx->pkt_ssid_len > 0 && ctx->pkt_ssid_len <= AIRPDCAP_WPA_SSID_MAX_LEN) {
- /* We have a "wildcard" SSID. Use the one from the packet. */
- memcpy(&pkt_key, tmp_key, sizeof(pkt_key));
- memcpy(&pkt_key.UserPwd.Ssid, ctx->pkt_ssid, ctx->pkt_ssid_len);
- pkt_key.UserPwd.SsidLen = ctx->pkt_ssid_len;
- AirPDcapRsnaPwd2Psk(pkt_key.UserPwd.Passphrase, pkt_key.UserPwd.Ssid,
- pkt_key.UserPwd.SsidLen, pkt_key.KeyData.Wpa.Psk);
- tmp_pkt_key = &pkt_key;
- } else {
- tmp_pkt_key = tmp_key;
- }
-
- /* derive the PTK from the BSSID, STA MAC, PMK, SNonce, ANonce */
- AirPDcapRsnaPrfX(sa, /* authenticator nonce, bssid, station mac */
- tmp_pkt_key->KeyData.Wpa.Psk, /* PSK == PMK */
- data+offset+12, /* supplicant nonce */
- 512,
- sa->wpa.ptk);
-
- /* verify the MIC (compare the MIC in the packet included in this message with a MIC calculated with the PTK) */
- eapol_len=pntoh16(data+offset-3)+4;
- memcpy(eapol, &data[offset-5], (eapol_len<AIRPDCAP_EAPOL_MAX_LEN?eapol_len:AIRPDCAP_EAPOL_MAX_LEN));
- ret_value=AirPDcapRsnaMicCheck(eapol, /* eapol frame (header also) */
- eapol_len, /* eapol frame length */
- sa->wpa.ptk, /* Key Confirmation Key */
- AIRPDCAP_EAP_KEY_DESCR_VER(data[offset+1])); /* EAPOL-Key description version */
-
- /* If the MIC is valid, the Authenticator checks that the RSN information element bit-wise matches */
- /* that from the (Re)Association Request message. */
- /* i) TODO If these are not exactly the same, the Authenticator uses MLME-DEAUTHENTICATE.request */
- /* primitive to terminate the association. */
- /* ii) If they do match bit-wise, the Authenticator constructs Message 3. */
+ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "Cached key is of a wrong type, try WPA key...", AIRPDCAP_DEBUG_LEVEL_3);
+ tmp_key=&ctx->keys[key_index];
}
+ }
- if (!ret_value &&
- (tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PWD ||
- tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PSK ||
- tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PMK))
- {
- /* the temporary key is the correct one, cached in the Security Association */
-
- sa->key=tmp_key;
-
- if (key!=NULL) {
- memcpy(key, tmp_key, sizeof(AIRPDCAP_KEY_ITEM));
- if (AIRPDCAP_EAP_KEY_DESCR_VER(data[offset+1])==AIRPDCAP_WPA_KEY_VER_NOT_CCMP)
- key->KeyType=AIRPDCAP_KEY_TYPE_TKIP;
- else if (AIRPDCAP_EAP_KEY_DESCR_VER(data[offset+1])==AIRPDCAP_WPA_KEY_VER_AES_CCMP)
- key->KeyType=AIRPDCAP_KEY_TYPE_CCMP;
- }
-
- break;
+ /* obviously, try only WPA keys... */
+ if (tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PWD ||
+ tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PSK ||
+ tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PMK)
+ {
+ if (tmp_key->KeyType == AIRPDCAP_KEY_TYPE_WPA_PWD && tmp_key->UserPwd.SsidLen == 0 && ctx->pkt_ssid_len > 0 && ctx->pkt_ssid_len <= AIRPDCAP_WPA_SSID_MAX_LEN) {
+ /* We have a "wildcard" SSID. Use the one from the packet. */
+ memcpy(&pkt_key, tmp_key, sizeof(pkt_key));
+ memcpy(&pkt_key.UserPwd.Ssid, ctx->pkt_ssid, ctx->pkt_ssid_len);
+ pkt_key.UserPwd.SsidLen = ctx->pkt_ssid_len;
+ AirPDcapRsnaPwd2Psk(pkt_key.UserPwd.Passphrase, pkt_key.UserPwd.Ssid,
+ pkt_key.UserPwd.SsidLen, pkt_key.KeyData.Wpa.Psk);
+ tmp_pkt_key = &pkt_key;
} else {
- /* the cached key was not valid, try other keys */
-
- if (useCache==TRUE) {
- useCache=FALSE;
- key_index--;
- }
+ tmp_pkt_key = tmp_key;
}
- }
- if (ret_value) {
- AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "handshake step failed", AIRPDCAP_DEBUG_LEVEL_3);
- return AIRPDCAP_RET_NO_VALID_HANDSHAKE;
+ /* derive the PTK from the BSSID, STA MAC, PMK, SNonce, ANonce */
+ AirPDcapRsnaPrfX(sa, /* authenticator nonce, bssid, station mac */
+ tmp_pkt_key->KeyData.Wpa.Psk, /* PSK == PMK */
+ data+offset+12, /* supplicant nonce */
+ 512,
+ sa->wpa.ptk);
+
+ /* verify the MIC (compare the MIC in the packet included in this message with a MIC calculated with the PTK) */
+ eapol_len=pntoh16(data+offset-3)+4;
+ memcpy(eapol, &data[offset-5], (eapol_len<AIRPDCAP_EAPOL_MAX_LEN?eapol_len:AIRPDCAP_EAPOL_MAX_LEN));
+ ret_value=AirPDcapRsnaMicCheck(eapol, /* eapol frame (header also) */
+ eapol_len, /* eapol frame length */
+ sa->wpa.ptk, /* Key Confirmation Key */
+ AIRPDCAP_EAP_KEY_DESCR_VER(data[offset+1])); /* EAPOL-Key description version */
+
+ /* If the MIC is valid, the Authenticator checks that the RSN information element bit-wise matches */
+ /* that from the (Re)Association Request message. */
+ /* i) TODO If these are not exactly the same, the Authenticator uses MLME-DEAUTHENTICATE.request */
+ /* primitive to terminate the association. */
+ /* ii) If they do match bit-wise, the Authenticator constructs Message 3. */
}
- sa->handshake=2;
+ if (!ret_value &&
+ (tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PWD ||
+ tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PSK ||
+ tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PMK))
+ {
+ /* the temporary key is the correct one, cached in the Security Association */
- return AIRPDCAP_RET_SUCCESS_HANDSHAKE;
- } else {
- /* message 4 */
+ sa->key=tmp_key;
- /* TODO "Note that when the 4-Way Handshake is first used Message 4 is sent in the clear." */
+ if (key!=NULL) {
+ memcpy(key, tmp_key, sizeof(AIRPDCAP_KEY_ITEM));
+ if (AIRPDCAP_EAP_KEY_DESCR_VER(data[offset+1])==AIRPDCAP_WPA_KEY_VER_NOT_CCMP)
+ key->KeyType=AIRPDCAP_KEY_TYPE_TKIP;
+ else if (AIRPDCAP_EAP_KEY_DESCR_VER(data[offset+1])==AIRPDCAP_WPA_KEY_VER_AES_CCMP)
+ key->KeyType=AIRPDCAP_KEY_TYPE_CCMP;
+ }
- /* TODO check MIC and Replay Counter */
- /* On reception of Message 4, the Authenticator verifies that the Key Replay Counter field value is one */
- /* that it used on this 4-Way Handshake; if it is not, it silently discards the message. */
- /* If the calculated MIC does not match the MIC that the Supplicant included in the EAPOL-Key frame, the */
- /* Authenticator silently discards Message 4. */
+ break;
+ } else {
+ /* the cached key was not valid, try other keys */
- AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "4-way handshake message 4 (patched)", AIRPDCAP_DEBUG_LEVEL_3);
+ if (useCache==TRUE) {
+ useCache=FALSE;
+ key_index--;
+ }
+ }
+ }
- sa->handshake=4;
+ if (ret_value) {
+ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "handshake step failed", AIRPDCAP_DEBUG_LEVEL_3);
+ return AIRPDCAP_RET_NO_VALID_HANDSHAKE;
+ }
- sa->validKey=TRUE;
+ sa->handshake=2;
+ sa->validKey=TRUE; /* we can use the key to decode, even if we have not captured the other eapol packets */
- return AIRPDCAP_RET_SUCCESS_HANDSHAKE;
- }
- /* END OF PATCH */
- /* */
+ return AIRPDCAP_RET_SUCCESS_HANDSHAKE;
} else {
- /* message 4 */
+ /* message 4 */
/* TODO "Note that when the 4-Way Handshake is first used Message 4 is sent in the clear." */
@@ -1391,8 +1366,6 @@ AirPDcapRsna4WHandshake(
sa->handshake=4;
- sa->validKey=TRUE;
-
return AIRPDCAP_RET_SUCCESS_HANDSHAKE;
}
}
@@ -1536,7 +1509,6 @@ AirPDcapGetSa(
AIRPDCAP_SEC_ASSOCIATION_ID *id)
{
INT sa_index;
-
if (ctx->sa_index!=-1) {
/* at least one association was stored */
/* search for the association from sa_index to 0 (most recent added) */
@@ -1559,7 +1531,6 @@ AirPDcapStoreSa(
AIRPDCAP_SEC_ASSOCIATION_ID *id)
{
INT last_free;
-
if (ctx->first_free_index>=AIRPDCAP_MAX_SEC_ASSOCIATIONS_NR) {
/* there is no empty space available. FAILURE */
return -1;
diff --git a/epan/crypt/airpdcap_debug.h b/epan/crypt/airpdcap_debug.h
index 40d82b5424..fbfe28ec02 100644
--- a/epan/crypt/airpdcap_debug.h
+++ b/epan/crypt/airpdcap_debug.h
@@ -89,7 +89,7 @@ void print_debug_line(const CHAR *function, const CHAR *msg, const INT level);
#endif
#endif
-static inline void DEBUG_DUMP(x,y,z)
+static inline void DEBUG_DUMP(const char* x, guint8* y, int z)
{
char* tmp_str = bytes_to_str(NULL, (const guint8 *) y, (z));
g_warning("%s: %s", x, tmp_str);
diff --git a/epan/crypt/airpdcap_system.h b/epan/crypt/airpdcap_system.h
index 08405fda21..0eda91b0a1 100644
--- a/epan/crypt/airpdcap_system.h
+++ b/epan/crypt/airpdcap_system.h
@@ -233,8 +233,7 @@ extern INT AirPDcapPacketProcess(
UCHAR *decrypt_data,
guint32 *decrypt_len,
PAIRPDCAP_KEY_ITEM key,
- gboolean mngHandshake,
- gboolean mngDecrypt)
+ gboolean scanHandshake)
;
/**
diff --git a/epan/crypt/airpdcap_user.h b/epan/crypt/airpdcap_user.h
index aeca3f08c4..1ba30dd76a 100644
--- a/epan/crypt/airpdcap_user.h
+++ b/epan/crypt/airpdcap_user.h
@@ -78,6 +78,17 @@
/* */
/* */
/******************************************************************************/
+static inline gboolean memiszero (const void *ptr, size_t count) {
+ const guint8 *p = (const guint8 *)ptr;
+
+ while (count != 0) {
+ if (*p != 0)
+ return FALSE;
+ p++;
+ count--;
+ }
+ return TRUE;
+}
/******************************************************************************/
/* Type definitions */
diff --git a/epan/dissectors/packet-ieee80211.c b/epan/dissectors/packet-ieee80211.c
index 8102b2d380..fba50b3438 100644
--- a/epan/dissectors/packet-ieee80211.c
+++ b/epan/dissectors/packet-ieee80211.c
@@ -17709,7 +17709,8 @@ dissect_ieee80211_common (tvbuff_t *tvb, packet_info *pinfo,
/* and WPA2 decryption */
if (enable_decryption && !pinfo->fd->flags.visited) {
const guint8 *enc_data = tvb_get_ptr(tvb, 0, hdr_len+reported_len);
- AirPDcapPacketProcess(&airpdcap_ctx, enc_data, hdr_len, hdr_len+reported_len, NULL, 0, NULL, TRUE, FALSE);
+ AirPDcapPacketProcess(&airpdcap_ctx, enc_data, hdr_len, hdr_len+reported_len, NULL, 0, NULL, TRUE);
+
}
/* Davide Schiera -------------------------------------------------------- */
@@ -18532,7 +18533,7 @@ static gint ett_wlan_rsna_eapol_keydes_data = -1;
static const true_false_string keyinfo_key_type_tfs = { "Pairwise Key", "Group Key" };
static int
-dissect_wlan_rsna_eapol_wpa_or_rsn_key(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean is_rsn)
+dissect_wlan_rsna_eapol_wpa_or_rsn_key(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
int offset = 0;
guint16 keyinfo;
@@ -18553,54 +18554,32 @@ dissect_wlan_rsna_eapol_wpa_or_rsn_key(tvbuff_t *tvb, packet_info *pinfo, proto_
col_set_str(pinfo->cinfo, COL_INFO, "Key (Request, Error)");
} else if (keyinfo & KEY_INFO_KEY_TYPE_MASK) {
guint16 masked;
+ /* At least Windows is incorrectly setting Secure bit on message 2 when rekeying
+ * we'll ignore the secure bit also for RSN and use the key nonce to differentiate between message 2 and 4 */
masked = keyinfo &
- (KEY_INFO_INSTALL_MASK | KEY_INFO_KEY_ACK_MASK |
- KEY_INFO_KEY_MIC_MASK | KEY_INFO_SECURE_MASK);
-
- if (!is_rsn) {
- /* WPA */
- switch (masked) {
- case KEY_INFO_KEY_ACK_MASK:
- col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 1 of 4)");
- break;
-
- case KEY_INFO_KEY_MIC_MASK:
- /* Check start of nonce for check if it is message 2 or 4
- According to the IEEE specification, sections 11.6.6.3 and 11.6.6.5
- define the value for the WPA Key Nonce as following:
- Message #2, Key Nonce = SNonce (Supplicant Nonce)
- Message #4, Key Nonce = 0 */
- start_nonce = tvb_get_ntohl(tvb, offset+12);
- if (start_nonce)
- col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 2 of 4)");
- else
- col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 4 of 4)");
- break;
+ (KEY_INFO_INSTALL_MASK | KEY_INFO_KEY_ACK_MASK | KEY_INFO_KEY_MIC_MASK);
- case (KEY_INFO_INSTALL_MASK | KEY_INFO_KEY_ACK_MASK | KEY_INFO_KEY_MIC_MASK):
- col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 3 of 4)");
- break;
- }
- } else {
- /* RSN */
- switch (masked) {
-
- case KEY_INFO_KEY_ACK_MASK:
- col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 1 of 4)");
- break;
+ switch (masked) {
+ case KEY_INFO_KEY_ACK_MASK:
+ col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 1 of 4)");
+ break;
- case KEY_INFO_KEY_MIC_MASK:
+ case KEY_INFO_KEY_MIC_MASK:
+ /* Check start of nonce for check if it is message 2 or 4
+ According to the IEEE specification, sections 11.6.6.3 and 11.6.6.5
+ define the value for the WPA Key Nonce as following:
+ Message #2, Key Nonce = SNonce (Supplicant Nonce)
+ Message #4, Key Nonce = 0 */
+ start_nonce = tvb_get_ntohl(tvb, offset+12);
+ if (start_nonce)
col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 2 of 4)");
- break;
-
- case (KEY_INFO_INSTALL_MASK | KEY_INFO_KEY_ACK_MASK | KEY_INFO_KEY_MIC_MASK | KEY_INFO_SECURE_MASK):
- col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 3 of 4)");
- break;
-
- case (KEY_INFO_KEY_MIC_MASK | KEY_INFO_SECURE_MASK):
+ else
col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 4 of 4)");
- break;
- }
+ break;
+
+ case (KEY_INFO_INSTALL_MASK | KEY_INFO_KEY_ACK_MASK | KEY_INFO_KEY_MIC_MASK):
+ col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 3 of 4)");
+ break;
}
} else {
if (keyinfo & KEY_INFO_KEY_ACK_MASK)
@@ -18671,18 +18650,6 @@ dissect_wlan_rsna_eapol_wpa_or_rsn_key(tvbuff_t *tvb, packet_info *pinfo, proto_
return tvb_captured_length(tvb);
}
-static int
-dissect_wlan_rsna_eapol_wpa_key(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
-{
- return dissect_wlan_rsna_eapol_wpa_or_rsn_key(tvb, pinfo, tree, FALSE);
-}
-
-static int
-dissect_wlan_rsna_eapol_rsn_key(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
-{
- return dissect_wlan_rsna_eapol_wpa_or_rsn_key(tvb, pinfo, tree, TRUE);
-}
-
/* Davide Schiera (2006-11-26): this function will try to decrypt with WEP or */
/* WPA and return a tvb to the caller to add a new tab. It returns the */
/* algorithm used for decryption (WEP, TKIP, CCMP) and the header and */
@@ -18703,7 +18670,7 @@ try_decrypt(tvbuff_t *tvb, guint offset, guint len, guint8 *algorithm, guint32 *
/* process packet with AirPDcap */
if (AirPDcapPacketProcess(&airpdcap_ctx, enc_data, offset, offset+len, dec_data, &dec_caplen,
- used_key, FALSE, TRUE)==AIRPDCAP_RET_SUCCESS)
+ used_key, FALSE)==AIRPDCAP_RET_SUCCESS)
{
guint8 *tmp;
*algorithm=used_key->KeyType;
@@ -27294,10 +27261,10 @@ proto_reg_handoff_ieee80211(void)
/*
* EAPOL key descriptor types.
*/
- wlan_rsna_eapol_wpa_key_handle = new_create_dissector_handle(dissect_wlan_rsna_eapol_wpa_key,
+ wlan_rsna_eapol_wpa_key_handle = new_create_dissector_handle(dissect_wlan_rsna_eapol_wpa_or_rsn_key,
proto_wlan_rsna_eapol);
dissector_add_uint("eapol.keydes.type", EAPOL_WPA_KEY, wlan_rsna_eapol_wpa_key_handle);
- wlan_rsna_eapol_rsn_key_handle = new_create_dissector_handle(dissect_wlan_rsna_eapol_rsn_key,
+ wlan_rsna_eapol_rsn_key_handle = new_create_dissector_handle(dissect_wlan_rsna_eapol_wpa_or_rsn_key,
proto_wlan_rsna_eapol);
dissector_add_uint("eapol.keydes.type", EAPOL_RSN_KEY, wlan_rsna_eapol_rsn_key_handle);
diff --git a/test/captures/wpa-test-decode.pcap.gz b/test/captures/wpa-test-decode.pcap.gz
new file mode 100644
index 0000000000..050f94ec4a
--- /dev/null
+++ b/test/captures/wpa-test-decode.pcap.gz
Binary files differ
diff --git a/test/config/80211_keys.tmpl b/test/config/80211_keys.tmpl
index 450da23791..63593e5849 100644
--- a/test/config/80211_keys.tmpl
+++ b/test/config/80211_keys.tmpl
@@ -1,5 +1,6 @@
# Keys needed for the decryption test suite
"wpa-pwd","Induction"
+"wpa-pwd","test0815"
"wpa-psk","a5001e18e0b3f792278825bc3abff72d7021d7c157b600470ef730e2490835d4"
"wpa-psk","79258f6ceeecedd3482b92deaabdb675f09bcb4003ef5074f5ddb10a94ebe00a"
"wpa-psk","23a9ee58c7810546ae3e7509fda9f97435778d689e53a54891c56d02f18ca162"
diff --git a/test/suite-decryption.sh b/test/suite-decryption.sh
index dd4202dae3..7795c2238d 100755
--- a/test/suite-decryption.sh
+++ b/test/suite-decryption.sh
@@ -89,6 +89,22 @@ decryption_step_80211_wpa_eap() {
fi
test_step_ok
}
+# WPA decode with message1+2 only and secure bit set on message 2
+# Included in git sources test/captures/wpa-test-decode.pcap.gz
+decryption_step_80211_wpa_eapol_incomplete_rekeys() {
+ $TESTS_DIR/run_and_catch_crashes env $TS_DC_ENV $TSHARK $TS_DC_ARGS \
+ -o "wlan.enable_decryption: TRUE" \
+ -r "$CAPTURE_DIR/wpa-test-decode.pcap.gz" \
+ -Y "icmp.resp_to == 4263" \
+ | grep "Echo" > /dev/null 2>&1
+ RETURNVALUE=$?
+ if [ ! $RETURNVALUE -eq $EXIT_OK ]; then
+ test_step_failed "Not able to follow rekey with missing eapol frames"
+ return
+ fi
+ test_step_ok
+}
+
# DTLS
# https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=view&target=snakeoil.tgz
@@ -266,6 +282,7 @@ decryption_step_http2() {
tshark_decryption_suite() {
test_step_add "IEEE 802.11 WPA PSK Decryption" decryption_step_80211_wpa_psk
+ test_step_add "IEEE 802.11 WPA PSK Decryption2 (EAPOL frames missing for Win 10 client)" decryption_step_80211_wpa_eapol_incomplete_rekeys
test_step_add "IEEE 802.11 WPA EAP Decryption" decryption_step_80211_wpa_eap
test_step_add "DTLS Decryption" decryption_step_dtls
test_step_add "SSL Decryption (private key)" decryption_step_ssl