/* MODULE -- EAP On Line 802.1x authentication packets for wifi Copyright (c) Alberto Ornaghi $Id: wifi_eapol.c 2911 2010-09-23 13:56:58Z alor $ */ #include #include #include #include /* globals */ #define EAPOL_4WAY_MESSAGE_ZERO 0 #define EAPOL_4WAY_MESSAGE_ONE 1 #define EAPOL_4WAY_MESSAGE_TWO 2 #define EAPOL_4WAY_MESSAGE_THREE 3 #define EAPOL_4WAY_MESSAGE_FOUR 4 #define EAPOL_4WAY_MESSAGE_GROUP 5 /* protos */ FUNC_DECODER(decode_eapol); void eapol_init(void); static int EAPOL_4WAY_handshake(struct eapol_key_header *eapol_key); static int eapol_enc_algo(struct eapol_key_header *eapol_key); /*******************************************/ /* * this function is the initializer. * it adds the entry in the table of registered decoder */ void __init eapol_init(void) { add_decoder(NET_LAYER, LL_TYPE_8021x, decode_eapol); } FUNC_DECODER(decode_eapol) { struct eapol_header *eapol; struct eapol_key_header *eapol_key; struct rsn_ie_header *rsn_ie; char sta_address[ETH_ASCII_ADDR_LEN]; u_char sta[ETH_ADDR_LEN], bssid[ETH_ADDR_LEN]; int message, algo; struct wpa_sa sa; char tmp[512]; DEBUG_MSG(D_EXCESSIVE, "%s", __FUNCTION__); /* analyze these only if we have a wpa key */ if (GBL_NET->wifi_schema != WIFI_WPA) return NULL; /* get the EAPOL packet */ DECODED_LEN = sizeof(struct eapol_header); eapol = (struct eapol_header *)DECODE_DATA; /* only interested in EAPOL KEY packets */ if (eapol->type != EAPOL_KEY) return NULL; /* get the EAPOL KEY packet */ eapol_key = (struct eapol_key_header *)(eapol + 1); DECODED_LEN += sizeof(struct eapol_key_header) + ntohs(eapol_key->key_data_len); /* only interested in RSN KEY (for CCMP) or WPA KEY (for TKIP) packets */ if (eapol_key->type != EAPOL_KEY_RSN && eapol_key->type != EAPOL_KEY_WPA) return NULL; /* check the encryption algorithm */ algo = eapol_enc_algo(eapol_key); /* check the 4-way handshake message number */ message = EAPOL_4WAY_handshake(eapol_key); /* invalid packet */ if (message <= 0) { return NULL; } /* * message 1 and 3 are from the AP * message 2 and 4 are from the STA * group keys come from AP */ if (message == EAPOL_4WAY_MESSAGE_GROUP) { memcpy(sta, PACKET->L2.dst, ETH_ADDR_LEN); memcpy(bssid, PACKET->L2.src, ETH_ADDR_LEN); } if (message % 2) { memcpy(sta, PACKET->L2.dst, ETH_ADDR_LEN); memcpy(bssid, PACKET->L2.src, ETH_ADDR_LEN); } else { memcpy(sta, PACKET->L2.src, ETH_ADDR_LEN); memcpy(bssid, PACKET->L2.dst, ETH_ADDR_LEN); } mac_addr_ntoa(sta, sta_address); /* handle the Group Key message */ if (message == EAPOL_4WAY_MESSAGE_GROUP) { DEBUG_MSG(D_INFO, "EAPOL: group key detected..."); /* * the group key is encrypted and we need a valid completed sa to decrypt it * the sa must be at state four */ if (wpa_sess_get(sta, &sa) == ESUCCESS && sa.state == EAPOL_4WAY_MESSAGE_FOUR) { rsn_ie = (struct rsn_ie_header *)(eapol_key + 1); wpa_decrypt_broadcast_key(eapol_key, rsn_ie, &sa); } return NULL; } DEBUG_MSG(D_INFO, "EAPOL packet: [%s] 4-way handshake %d [%s]", sta_address, message, (algo == WPA_KEY_TKIP) ? "TKIP (WPA)" : "CCMP (WPA2)"); switch (message) { case EAPOL_4WAY_MESSAGE_ONE: /* * On reception of Message 1, the Supplicant determines whether the Key Replay Counter field value has been * used before with the current PMKSA. If the Key Replay Counter field value is less than or equal to the current * local value, the Supplicant discards the message. */ /* save ANonce (from authenticator) to derive the PTK with the SNonce (from the 2 message) */ memcpy(sa.ANonce, eapol_key->key_nonce, WPA_NONCE_LEN); DEBUG_MSG(D_VERBOSE, "WPA ANonce : %s", str_tohex(sa.ANonce, WPA_NONCE_LEN, tmp, sizeof(tmp))); /* get the Key Descriptor Version (to select algorithm used in decryption -CCMP or TKIP-) */ sa.algo = algo; /* remember the state of the 4-way handshake */ sa.state = EAPOL_4WAY_MESSAGE_ONE; /* save the status of the SA, overwrite it if it was an old one */ wpa_sess_add(sta, &sa); break; case EAPOL_4WAY_MESSAGE_TWO: /* retrieve the session for this STA and check that the sate is consistent */ if (wpa_sess_get(sta, &sa) != ESUCCESS || sa.state != EAPOL_4WAY_MESSAGE_ONE) break; /* * 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. */ memcpy(sa.SNonce, eapol_key->key_nonce, WPA_NONCE_LEN); DEBUG_MSG(D_VERBOSE, "WPA SNonce : %s", str_tohex(sa.SNonce, WPA_NONCE_LEN, tmp, sizeof(tmp))); /* derive the PTK from the BSSID, STA MAC, PMK (WPA-PSK), SNonce, ANonce */ wpa_generate_PTK(bssid, sta, GBL_NET->wkey, sa.SNonce, sa.ANonce, (sa.algo == WPA_KEY_TKIP) ? 512 : 384, sa.ptk); DEBUG_MSG(D_VERBOSE, "WPA PTK : %s", str_tohex(sa.ptk, WPA_PTK_LEN, tmp, sizeof(tmp))); /* verify the MIC (compare the MIC in the packet included in this message with a MIC calculated with the PTK) */ if (wpa_check_MIC(eapol, eapol_key, DECODED_LEN, sa.ptk, sa.algo) != ESUCCESS) { DEBUG_MSG(D_INFO, "WPA MIC does not match"); break; } DEBUG_MSG(D_VERBOSE, "WPA MIC : %s", str_tohex(eapol_key->key_MIC, WPA_MICKEY_LEN, tmp, sizeof(tmp))); /* remember the state of the 4-way handshake */ sa.state = EAPOL_4WAY_MESSAGE_TWO; /* save the status of the SA, overwrite it if it was an old one */ wpa_sess_add(sta, &sa); break; case EAPOL_4WAY_MESSAGE_THREE: /* retrieve the session for this STA and check that the sate is consistent */ if (wpa_sess_get(sta, &sa) != ESUCCESS || sa.state != EAPOL_4WAY_MESSAGE_TWO) break; /* * On reception of Message 3, the Supplicant silently discards the message if the Key Replay Counter field * value has already been used or if the ANonce value in Message 3 differs from the ANonce value in Message 1 * If using WPA2 PSK, message 3 will contain an RSN for the group key (GTK KDE). * In order to properly support decrypting WPA2-PSK packets, we need to parse this to get the group key. */ rsn_ie = (struct rsn_ie_header *)(eapol_key + 1); wpa_decrypt_broadcast_key(eapol_key, rsn_ie, &sa); /* remember the state of the 4-way handshake */ sa.state = EAPOL_4WAY_MESSAGE_THREE; /* save the status of the SA, overwrite it if it was an old one */ wpa_sess_add(sta, &sa); break; case EAPOL_4WAY_MESSAGE_FOUR: /* retrieve the session for this STA and check that the sate is consistent */ if (wpa_sess_get(sta, &sa) != ESUCCESS || sa.state != EAPOL_4WAY_MESSAGE_THREE) break; /* * 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. */ /* we are done ! just copy the decryption key from PTK */ memcpy(sa.decryption_key, sa.ptk + 32, WPA_DEC_KEY_LEN); DEBUG_MSG(D_INFO, "WPA KEY : %s", str_tohex(sa.decryption_key, WPA_DEC_KEY_LEN, tmp, sizeof(tmp))); /* remember the state of the 4-way handshake */ sa.state = EAPOL_4WAY_MESSAGE_FOUR; /* save the status of the SA, overwrite it if it was an old one */ wpa_sess_add(sta, &sa); break; } return NULL; } static int EAPOL_4WAY_handshake(struct eapol_key_header *eapol_key) { u_int16 key_info = ntohs(eapol_key->key_info); /* IEEE-802.11i-2004 ¤ 8.5.3 */ /* * all the 4-way handshake packets have the pairwise bit set * if not, it is a Group Key, and should be handled in a different way */ if ((key_info & WPA_KEY_PAIRWISE) == 0) { /* Group Key (Sec=0, Mic=0, Ack=1) */ if ((key_info & WPA_KEY_SECURE) == 0 && (key_info & WPA_KEY_MIC) == 0 && (key_info & WPA_KEY_ACK) != 0) { return EAPOL_4WAY_MESSAGE_GROUP; } return -ENOTHANDLED; } /* message 1: Authenticator->Supplicant (Sec=0, Mic=0, Ack=1, Inst=0, Key=1(pairwise), KeyRSC=0, Nonce=ANonce, MIC=0) */ if ((key_info & WPA_KEY_SECURE) == 0 && (key_info & WPA_KEY_MIC) == 0 && (key_info & WPA_KEY_ACK) != 0 && (key_info & WPA_KEY_INSTALL) == 0) return EAPOL_4WAY_MESSAGE_ONE; /* message 2: Supplicant->Authenticator (Sec=0, Mic=1, Ack=0, Inst=0, Key=1(pairwise), KeyRSC=0, Nonce=SNonce, MIC=MIC(KCK,EAPOL)) * to distinguish between message 2 and 4, check the len of the key_data_len (should be != 0) */ if ((key_info & WPA_KEY_SECURE) == 0 && (key_info & WPA_KEY_MIC) != 0 && (key_info & WPA_KEY_ACK) == 0 && (key_info & WPA_KEY_INSTALL) == 0 && eapol_key->key_data_len != 0) return EAPOL_4WAY_MESSAGE_TWO; /* message 3: * CCMP: Authenticator->Supplicant (Sec=1/0, Mic=1, Ack=1, Inst=1, Key=1(pairwise), KeyRSC=???, Nonce=ANonce, MIC=1) * TKIP: Authenticator->Supplicant (Sec=0, Mic=1, Ack=1, Inst=1, Key=1(pairwise), KeyRSC=???, Nonce=ANonce, MIC=1) */ if ( /* ((key_info & WPA_KEY_TKIP) || (key_info & WPA_KEY_SECURE) != 0) && */ (key_info & WPA_KEY_MIC) != 0 && (key_info & WPA_KEY_ACK) != 0 && (key_info & WPA_KEY_INSTALL) != 0) return EAPOL_4WAY_MESSAGE_THREE; /* message 4: * CCMP: Supplicant->Authenticator (Sec=1/0, Mic=1, Ack=0, Inst=0, Key=1(pairwise), KeyRSC=0, Nonce=0, MIC=MIC(KCK,EAPOL)) * TKIP: Supplicant->Authenticator (Sec=0, Mic=1, Ack=0, Inst=0, Key=1(pairwise), KeyRSC=0, Nonce=0, MIC=MIC(KCK,EAPOL)) * to distinguish between message 2 and 4, check the len of the key_data_len (should be == 0) */ if (/* ((key_info & WPA_KEY_TKIP) || (key_info & WPA_KEY_SECURE) != 0) && */ (key_info & WPA_KEY_MIC) != 0 && (key_info & WPA_KEY_ACK) == 0 && (key_info & WPA_KEY_INSTALL) == 0 && eapol_key->key_data_len == 0) return EAPOL_4WAY_MESSAGE_FOUR; /* invalid packet */ return -ENOTHANDLED; } static int eapol_enc_algo(struct eapol_key_header *eapol_key) { u_int16 key_info = ntohs(eapol_key->key_info); if ((key_info & WPA_KEY_TKIP) != 0) return WPA_KEY_TKIP; if ((key_info & WPA_KEY_CCMP) != 0) return WPA_KEY_CCMP; return 0; } /* EOF */ // vim:ts=3:expandtab .