2014-12-19 01:10:10 +01:00
|
|
|
|
/*
|
|
|
|
|
*
|
|
|
|
|
* Wireless daemon for Linux
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
2015-02-26 14:31:26 +01:00
|
|
|
|
#include <linux/if_ether.h>
|
2014-12-19 01:10:10 +01:00
|
|
|
|
#include <ell/ell.h>
|
|
|
|
|
|
2015-02-19 03:51:54 +01:00
|
|
|
|
#include "crypto.h"
|
2014-12-19 01:10:10 +01:00
|
|
|
|
#include "eapol.h"
|
2015-02-25 00:11:56 +01:00
|
|
|
|
#include "ie.h"
|
2015-03-26 05:26:31 +01:00
|
|
|
|
#include "util.h"
|
2015-03-30 04:00:00 +02:00
|
|
|
|
#include "mpdu.h"
|
2015-10-30 11:12:20 +01:00
|
|
|
|
#include "eap.h"
|
2016-11-02 23:46:19 +01:00
|
|
|
|
#include "handshake.h"
|
2017-09-07 23:04:45 +02:00
|
|
|
|
#include "watchlist.h"
|
2014-12-19 01:10:10 +01:00
|
|
|
|
|
2015-03-20 02:55:25 +01:00
|
|
|
|
struct l_queue *state_machines;
|
2017-04-29 03:53:26 +02:00
|
|
|
|
struct l_queue *preauths;
|
2017-09-07 23:04:45 +02:00
|
|
|
|
struct watchlist frame_watches;
|
2018-04-02 20:47:43 +02:00
|
|
|
|
static uint32_t eapol_4way_handshake_time = 2;
|
2016-09-12 17:02:04 +02:00
|
|
|
|
|
2016-06-23 00:49:15 +02:00
|
|
|
|
eapol_rekey_offload_func_t rekey_offload = NULL;
|
2015-02-24 18:08:03 +01:00
|
|
|
|
|
2016-09-12 17:02:04 +02:00
|
|
|
|
eapol_tx_packet_func_t tx_packet = NULL;
|
|
|
|
|
void *tx_user_data;
|
|
|
|
|
|
2017-09-07 23:04:45 +02:00
|
|
|
|
uint32_t next_frame_watch_id;
|
|
|
|
|
|
2017-02-21 23:45:41 +01:00
|
|
|
|
#define VERIFY_IS_ZERO(field) \
|
|
|
|
|
do { \
|
|
|
|
|
if (!util_mem_is_zero((field), sizeof((field)))) \
|
|
|
|
|
return false; \
|
|
|
|
|
} while (false) \
|
2014-12-28 05:31:03 +01:00
|
|
|
|
|
2014-12-28 05:37:39 +01:00
|
|
|
|
/*
|
|
|
|
|
* MIC calculation depends on the selected hash function. The has function
|
|
|
|
|
* is given in the EAPoL Key Descriptor Version field.
|
|
|
|
|
*
|
|
|
|
|
* The MIC length is always 16 bytes for currently known Key Descriptor
|
|
|
|
|
* Versions.
|
|
|
|
|
*
|
|
|
|
|
* The input struct eapol_key *frame should have a zero-d MIC field
|
|
|
|
|
*/
|
2018-08-09 20:13:55 +02:00
|
|
|
|
bool eapol_calculate_mic(enum ie_rsn_akm_suite akm, const uint8_t *kck,
|
|
|
|
|
const struct eapol_key *frame, uint8_t *mic)
|
2014-12-28 05:37:39 +01:00
|
|
|
|
{
|
|
|
|
|
size_t frame_len = sizeof(struct eapol_key);
|
|
|
|
|
|
|
|
|
|
frame_len += L_BE16_TO_CPU(frame->key_data_len);
|
|
|
|
|
|
|
|
|
|
switch (frame->key_descriptor_version) {
|
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_MD5_ARC4:
|
2015-02-13 21:31:23 +01:00
|
|
|
|
return hmac_md5(kck, 16, frame, frame_len, mic, 16);
|
2014-12-28 05:37:39 +01:00
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_SHA1_AES:
|
2015-02-13 21:31:23 +01:00
|
|
|
|
return hmac_sha1(kck, 16, frame, frame_len, mic, 16);
|
2014-12-28 05:37:39 +01:00
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_AES_128_CMAC_AES:
|
2015-02-13 21:52:48 +01:00
|
|
|
|
return cmac_aes(kck, 16, frame, frame_len, mic, 16);
|
2018-08-09 20:13:55 +02:00
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_AKM_DEFINED:
|
|
|
|
|
switch (akm) {
|
|
|
|
|
case IE_RSN_AKM_SUITE_SAE_SHA256:
|
|
|
|
|
return cmac_aes(kck, 16, frame, frame_len, mic, 16);
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-12-28 05:37:39 +01:00
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-09 20:13:55 +02:00
|
|
|
|
bool eapol_verify_mic(enum ie_rsn_akm_suite akm, const uint8_t *kck,
|
|
|
|
|
const struct eapol_key *frame)
|
2015-02-19 04:13:09 +01:00
|
|
|
|
{
|
|
|
|
|
size_t frame_len = sizeof(struct eapol_key);
|
|
|
|
|
uint8_t mic[16];
|
|
|
|
|
struct iovec iov[3];
|
|
|
|
|
struct l_checksum *checksum = NULL;
|
|
|
|
|
|
|
|
|
|
iov[0].iov_base = (void *) frame;
|
|
|
|
|
iov[0].iov_len = offsetof(struct eapol_key, key_mic_data);
|
|
|
|
|
|
|
|
|
|
memset(mic, 0, sizeof(mic));
|
|
|
|
|
iov[1].iov_base = mic;
|
|
|
|
|
iov[1].iov_len = sizeof(mic);
|
|
|
|
|
|
|
|
|
|
iov[2].iov_base = ((void *) frame) +
|
|
|
|
|
offsetof(struct eapol_key, key_data_len);
|
2015-02-24 17:42:20 +01:00
|
|
|
|
iov[2].iov_len = frame_len - offsetof(struct eapol_key, key_data_len) +
|
2015-02-19 04:13:09 +01:00
|
|
|
|
L_BE16_TO_CPU(frame->key_data_len);
|
|
|
|
|
|
|
|
|
|
switch (frame->key_descriptor_version) {
|
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_MD5_ARC4:
|
|
|
|
|
checksum = l_checksum_new_hmac(L_CHECKSUM_MD5, kck, 16);
|
|
|
|
|
break;
|
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_SHA1_AES:
|
|
|
|
|
checksum = l_checksum_new_hmac(L_CHECKSUM_SHA1, kck, 16);
|
|
|
|
|
break;
|
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_AES_128_CMAC_AES:
|
|
|
|
|
checksum = l_checksum_new_cmac_aes(kck, 16);
|
2018-08-09 20:13:55 +02:00
|
|
|
|
break;
|
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_AKM_DEFINED:
|
|
|
|
|
switch (akm) {
|
|
|
|
|
case IE_RSN_AKM_SUITE_SAE_SHA256:
|
|
|
|
|
checksum = l_checksum_new_cmac_aes(kck, 16);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-19 04:13:09 +01:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (checksum == NULL)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
l_checksum_updatev(checksum, iov, 3);
|
|
|
|
|
l_checksum_get_digest(checksum, mic, 16);
|
2016-10-04 05:47:42 +02:00
|
|
|
|
l_checksum_free(checksum);
|
2015-02-19 04:13:09 +01:00
|
|
|
|
|
|
|
|
|
if (!memcmp(frame->key_mic_data, mic, 16))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-09 20:13:55 +02:00
|
|
|
|
uint8_t *eapol_decrypt_key_data(enum ie_rsn_akm_suite akm, const uint8_t *kek,
|
2015-02-24 22:59:36 +01:00
|
|
|
|
const struct eapol_key *frame,
|
|
|
|
|
size_t *decrypted_size)
|
2015-02-14 03:37:17 +01:00
|
|
|
|
{
|
|
|
|
|
size_t key_data_len = L_BE16_TO_CPU(frame->key_data_len);
|
2015-02-18 00:39:23 +01:00
|
|
|
|
const uint8_t *key_data = frame->key_data;
|
|
|
|
|
size_t expected_len;
|
2015-02-14 03:37:17 +01:00
|
|
|
|
uint8_t *buf;
|
|
|
|
|
|
|
|
|
|
switch (frame->key_descriptor_version) {
|
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_MD5_ARC4:
|
2015-02-18 00:39:23 +01:00
|
|
|
|
expected_len = key_data_len;
|
2015-02-14 03:37:17 +01:00
|
|
|
|
break;
|
2018-08-09 20:13:55 +02:00
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_AKM_DEFINED:
|
|
|
|
|
/*
|
|
|
|
|
* TODO: for now, only SAE is supported under the AKM_DEFINED
|
|
|
|
|
* key descriptor version. Once 8021x suites are added for this
|
|
|
|
|
* type this will need to be expanded to handle the AKM types in
|
|
|
|
|
* its own switch.
|
|
|
|
|
*/
|
|
|
|
|
if (akm != IE_RSN_AKM_SUITE_SAE_SHA256)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* Fall through */
|
2015-02-14 03:37:17 +01:00
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_SHA1_AES:
|
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_AES_128_CMAC_AES:
|
2018-07-16 17:51:13 +02:00
|
|
|
|
if (key_data_len < 24 || key_data_len % 8)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2015-02-18 00:39:23 +01:00
|
|
|
|
expected_len = key_data_len - 8;
|
2015-02-14 03:37:17 +01:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return NULL;
|
|
|
|
|
};
|
|
|
|
|
|
2015-02-18 00:39:23 +01:00
|
|
|
|
buf = l_new(uint8_t, expected_len);
|
2015-02-14 03:37:17 +01:00
|
|
|
|
|
2015-02-18 00:39:23 +01:00
|
|
|
|
switch (frame->key_descriptor_version) {
|
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_MD5_ARC4:
|
|
|
|
|
{
|
|
|
|
|
uint8_t key[32];
|
|
|
|
|
bool ret;
|
|
|
|
|
|
|
|
|
|
memcpy(key, frame->eapol_key_iv, 16);
|
|
|
|
|
memcpy(key + 16, kek, 16);
|
|
|
|
|
|
|
|
|
|
ret = arc4_skip(key, 32, 256, key_data, key_data_len, buf);
|
|
|
|
|
memset(key, 0, sizeof(key));
|
|
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_SHA1_AES:
|
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_AES_128_CMAC_AES:
|
2018-08-09 20:13:55 +02:00
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_AKM_DEFINED:
|
2015-02-18 00:39:23 +01:00
|
|
|
|
if (!aes_unwrap(kek, key_data, key_data_len, buf))
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
break;
|
2015-02-14 03:37:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-02-24 22:59:36 +01:00
|
|
|
|
if (decrypted_size)
|
|
|
|
|
*decrypted_size = expected_len;
|
|
|
|
|
|
2015-02-14 03:37:17 +01:00
|
|
|
|
return buf;
|
2015-02-18 00:39:23 +01:00
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
l_free(buf);
|
|
|
|
|
return NULL;
|
2015-02-14 03:37:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-31 04:04:50 +02:00
|
|
|
|
/*
|
|
|
|
|
* Pad and encrypt the plaintext Key Data contents in @key_data using
|
|
|
|
|
* the encryption scheme required by @out_frame->key_descriptor_version,
|
|
|
|
|
* write results to @out_frame->key_data and @out_frame->key_data_len.
|
|
|
|
|
*
|
|
|
|
|
* Note that for efficiency @key_data is being modified, including in
|
|
|
|
|
* case of failure, so it must be sufficiently larger than @key_data_len.
|
|
|
|
|
*/
|
|
|
|
|
bool eapol_encrypt_key_data(const uint8_t *kek, uint8_t *key_data,
|
|
|
|
|
size_t key_data_len,
|
|
|
|
|
struct eapol_key *out_frame)
|
|
|
|
|
{
|
|
|
|
|
switch (out_frame->key_descriptor_version) {
|
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_MD5_ARC4:
|
|
|
|
|
/* Not supported */
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_SHA1_AES:
|
|
|
|
|
case EAPOL_KEY_DESCRIPTOR_VERSION_AES_128_CMAC_AES:
|
|
|
|
|
if (key_data_len < 16 || key_data_len % 8)
|
|
|
|
|
key_data[key_data_len++] = 0xdd;
|
|
|
|
|
while (key_data_len < 16 || key_data_len % 8)
|
|
|
|
|
key_data[key_data_len++] = 0x00;
|
|
|
|
|
|
|
|
|
|
if (!aes_wrap(kek, key_data, key_data_len, out_frame->key_data))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
key_data_len += 8;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out_frame->key_data_len = L_CPU_TO_BE16(key_data_len);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-22 05:06:40 +02:00
|
|
|
|
void eapol_key_data_append(struct eapol_key *ek, enum handshake_kde selector,
|
|
|
|
|
const uint8_t *data, size_t data_len)
|
|
|
|
|
{
|
|
|
|
|
uint16_t key_data_len = L_BE16_TO_CPU(ek->key_data_len);
|
|
|
|
|
|
|
|
|
|
ek->key_data[key_data_len++] = IE_TYPE_VENDOR_SPECIFIC;
|
|
|
|
|
ek->key_data[key_data_len++] = 4 + data_len; /* OUI + Data type + len */
|
|
|
|
|
l_put_be32(selector, ek->key_data + key_data_len);
|
|
|
|
|
key_data_len += 4;
|
|
|
|
|
|
|
|
|
|
memcpy(ek->key_data + key_data_len, data, data_len);
|
|
|
|
|
key_data_len += data_len;
|
|
|
|
|
|
|
|
|
|
ek->key_data_len = L_CPU_TO_BE16(key_data_len);
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 22:39:26 +01:00
|
|
|
|
#define VERIFY_PTK_COMMON(ek) \
|
|
|
|
|
if (!ek->key_type) \
|
|
|
|
|
return false; \
|
|
|
|
|
if (ek->smk_message) \
|
|
|
|
|
return false; \
|
|
|
|
|
if (ek->request) \
|
|
|
|
|
return false; \
|
|
|
|
|
if (ek->error) \
|
|
|
|
|
return false \
|
|
|
|
|
|
|
|
|
|
bool eapol_verify_ptk_1_of_4(const struct eapol_key *ek)
|
2014-12-28 05:31:03 +01:00
|
|
|
|
{
|
|
|
|
|
/* Verify according to 802.11, Section 11.6.6.2 */
|
2015-02-23 22:39:26 +01:00
|
|
|
|
VERIFY_PTK_COMMON(ek);
|
2014-12-28 05:31:03 +01:00
|
|
|
|
|
|
|
|
|
if (ek->install)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2014-12-28 05:31:03 +01:00
|
|
|
|
|
|
|
|
|
if (!ek->key_ack)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2014-12-28 05:31:03 +01:00
|
|
|
|
|
|
|
|
|
if (ek->key_mic)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2014-12-28 05:31:03 +01:00
|
|
|
|
|
|
|
|
|
if (ek->secure)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2014-12-28 05:31:03 +01:00
|
|
|
|
|
|
|
|
|
if (ek->encrypted_key_data)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2014-12-28 05:31:03 +01:00
|
|
|
|
|
2015-05-06 05:26:11 +02:00
|
|
|
|
if (ek->wpa_key_id)
|
|
|
|
|
return false;
|
|
|
|
|
|
2014-12-28 05:31:03 +01:00
|
|
|
|
VERIFY_IS_ZERO(ek->key_rsc);
|
|
|
|
|
VERIFY_IS_ZERO(ek->reserved);
|
|
|
|
|
VERIFY_IS_ZERO(ek->key_mic_data);
|
|
|
|
|
|
2015-03-23 12:17:44 +01:00
|
|
|
|
return true;
|
2014-12-28 05:31:03 +01:00
|
|
|
|
}
|
2014-12-28 05:32:07 +01:00
|
|
|
|
|
2015-02-23 22:39:26 +01:00
|
|
|
|
bool eapol_verify_ptk_2_of_4(const struct eapol_key *ek)
|
2014-12-28 05:32:07 +01:00
|
|
|
|
{
|
|
|
|
|
uint16_t key_len;
|
|
|
|
|
|
2015-02-13 23:12:43 +01:00
|
|
|
|
/* Verify according to 802.11, Section 11.6.6.3 */
|
2015-02-23 22:39:26 +01:00
|
|
|
|
VERIFY_PTK_COMMON(ek);
|
2014-12-28 05:32:07 +01:00
|
|
|
|
|
|
|
|
|
if (ek->install)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2014-12-28 05:32:07 +01:00
|
|
|
|
|
|
|
|
|
if (ek->key_ack)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2014-12-28 05:32:07 +01:00
|
|
|
|
|
|
|
|
|
if (!ek->key_mic)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2014-12-28 05:32:07 +01:00
|
|
|
|
|
|
|
|
|
if (ek->secure)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2014-12-28 05:32:07 +01:00
|
|
|
|
|
|
|
|
|
if (ek->encrypted_key_data)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2014-12-28 05:32:07 +01:00
|
|
|
|
|
2015-05-06 05:26:11 +02:00
|
|
|
|
if (ek->wpa_key_id)
|
|
|
|
|
return false;
|
|
|
|
|
|
2018-06-22 20:13:29 +02:00
|
|
|
|
if (ek->request)
|
|
|
|
|
return false;
|
|
|
|
|
|
2014-12-28 05:32:07 +01:00
|
|
|
|
key_len = L_BE16_TO_CPU(ek->key_length);
|
|
|
|
|
if (key_len != 0)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2014-12-28 05:32:07 +01:00
|
|
|
|
|
2015-02-13 23:36:24 +01:00
|
|
|
|
VERIFY_IS_ZERO(ek->eapol_key_iv);
|
|
|
|
|
VERIFY_IS_ZERO(ek->key_rsc);
|
|
|
|
|
VERIFY_IS_ZERO(ek->reserved);
|
|
|
|
|
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return true;
|
2014-12-28 05:32:07 +01:00
|
|
|
|
}
|
2014-12-28 05:33:46 +01:00
|
|
|
|
|
2015-05-06 01:48:35 +02:00
|
|
|
|
bool eapol_verify_ptk_3_of_4(const struct eapol_key *ek, bool is_wpa)
|
2015-02-13 23:36:52 +01:00
|
|
|
|
{
|
|
|
|
|
uint16_t key_len;
|
|
|
|
|
|
|
|
|
|
/* Verify according to 802.11, Section 11.6.6.4 */
|
2015-02-23 22:39:26 +01:00
|
|
|
|
VERIFY_PTK_COMMON(ek);
|
2015-02-13 23:36:52 +01:00
|
|
|
|
|
2015-05-06 04:50:35 +02:00
|
|
|
|
/*
|
|
|
|
|
* TODO: Handle cases where install might be 0:
|
|
|
|
|
* For PTK generation, 0 only if the AP does not support key mapping
|
|
|
|
|
* keys, or if the STA has the No Pairwise bit (in the RSN Capabilities
|
|
|
|
|
* field) equal to 1 and only the group key is used.
|
|
|
|
|
*/
|
|
|
|
|
if (!ek->install)
|
|
|
|
|
return false;
|
|
|
|
|
|
2015-02-13 23:36:52 +01:00
|
|
|
|
if (!ek->key_ack)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2015-02-13 23:36:52 +01:00
|
|
|
|
|
|
|
|
|
if (!ek->key_mic)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2015-02-13 23:36:52 +01:00
|
|
|
|
|
2015-05-06 01:48:35 +02:00
|
|
|
|
if (ek->secure != !is_wpa)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2015-02-13 23:36:52 +01:00
|
|
|
|
|
2015-05-06 01:48:35 +02:00
|
|
|
|
/* Must be encrypted when GTK is present but reserved in WPA */
|
|
|
|
|
if (!ek->encrypted_key_data && !is_wpa)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2015-02-13 23:36:52 +01:00
|
|
|
|
|
2015-05-06 05:26:11 +02:00
|
|
|
|
if (ek->wpa_key_id)
|
|
|
|
|
return false;
|
|
|
|
|
|
2015-02-13 23:36:52 +01:00
|
|
|
|
key_len = L_BE16_TO_CPU(ek->key_length);
|
2015-05-18 13:31:39 +02:00
|
|
|
|
if (key_len != 16 && key_len != 32)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2015-02-13 23:36:52 +01:00
|
|
|
|
|
|
|
|
|
VERIFY_IS_ZERO(ek->reserved);
|
|
|
|
|
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return true;
|
2015-02-13 23:36:52 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-06 01:48:35 +02:00
|
|
|
|
bool eapol_verify_ptk_4_of_4(const struct eapol_key *ek, bool is_wpa)
|
2015-02-14 01:38:10 +01:00
|
|
|
|
{
|
|
|
|
|
uint16_t key_len;
|
|
|
|
|
|
|
|
|
|
/* Verify according to 802.11, Section 11.6.6.5 */
|
2015-02-23 22:39:26 +01:00
|
|
|
|
VERIFY_PTK_COMMON(ek);
|
2015-02-14 01:38:10 +01:00
|
|
|
|
|
2015-05-06 04:50:35 +02:00
|
|
|
|
if (ek->install)
|
|
|
|
|
return false;
|
|
|
|
|
|
2015-02-14 01:38:10 +01:00
|
|
|
|
if (ek->key_ack)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2015-02-14 01:38:10 +01:00
|
|
|
|
|
|
|
|
|
if (!ek->key_mic)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2015-02-14 01:38:10 +01:00
|
|
|
|
|
2015-05-06 01:48:35 +02:00
|
|
|
|
if (ek->secure != !is_wpa)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2015-02-14 01:38:10 +01:00
|
|
|
|
|
2015-05-06 05:01:53 +02:00
|
|
|
|
if (ek->encrypted_key_data)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2015-02-14 01:38:10 +01:00
|
|
|
|
|
2015-05-06 05:26:11 +02:00
|
|
|
|
if (ek->wpa_key_id)
|
|
|
|
|
return false;
|
|
|
|
|
|
2018-06-22 20:13:29 +02:00
|
|
|
|
if (ek->request)
|
|
|
|
|
return false;
|
|
|
|
|
|
2015-02-14 01:38:10 +01:00
|
|
|
|
key_len = L_BE16_TO_CPU(ek->key_length);
|
|
|
|
|
if (key_len != 0)
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return false;
|
2015-02-14 01:38:10 +01:00
|
|
|
|
|
|
|
|
|
VERIFY_IS_ZERO(ek->key_nonce);
|
|
|
|
|
VERIFY_IS_ZERO(ek->eapol_key_iv);
|
|
|
|
|
VERIFY_IS_ZERO(ek->key_rsc);
|
|
|
|
|
VERIFY_IS_ZERO(ek->reserved);
|
|
|
|
|
|
2015-02-23 22:39:26 +01:00
|
|
|
|
return true;
|
2015-02-14 01:38:10 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-06 05:04:21 +02:00
|
|
|
|
#define VERIFY_GTK_COMMON(ek) \
|
|
|
|
|
if (ek->key_type) \
|
|
|
|
|
return false; \
|
|
|
|
|
if (ek->smk_message) \
|
|
|
|
|
return false; \
|
|
|
|
|
if (ek->request) \
|
|
|
|
|
return false; \
|
|
|
|
|
if (ek->error) \
|
|
|
|
|
return false; \
|
|
|
|
|
if (ek->install) \
|
|
|
|
|
return false \
|
|
|
|
|
|
2015-05-06 01:48:34 +02:00
|
|
|
|
bool eapol_verify_gtk_1_of_2(const struct eapol_key *ek, bool is_wpa)
|
|
|
|
|
{
|
|
|
|
|
uint16_t key_len;
|
|
|
|
|
|
2015-05-06 05:04:21 +02:00
|
|
|
|
VERIFY_GTK_COMMON(ek);
|
2015-05-06 04:50:35 +02:00
|
|
|
|
|
2015-05-06 01:48:34 +02:00
|
|
|
|
if (!ek->key_ack)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!ek->key_mic)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!ek->secure)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/* Must be encrypted when GTK is present but reserved in WPA */
|
|
|
|
|
if (!ek->encrypted_key_data && !is_wpa)
|
|
|
|
|
return false;
|
|
|
|
|
|
2017-02-08 01:38:43 +01:00
|
|
|
|
/*
|
2017-03-14 16:11:32 +01:00
|
|
|
|
* In P802.11i/D3.0 the Key Length should be 16 for WPA but hostapd
|
|
|
|
|
* uses 16 for CCMP and 32 for TKIP. Since 802.11i-2004 there's
|
|
|
|
|
* inconsistency in the required value, for example 0 is clearly
|
|
|
|
|
* specified in 802.11-2012 11.6.7.2 but 11.6.2 doesn't list 0 and
|
|
|
|
|
* makes the value depend on the pairwise key type.
|
2017-02-08 01:38:43 +01:00
|
|
|
|
*/
|
2015-05-06 01:48:34 +02:00
|
|
|
|
key_len = L_BE16_TO_CPU(ek->key_length);
|
2017-03-14 16:11:32 +01:00
|
|
|
|
if (key_len != 0 && key_len != 16 && key_len != 32)
|
2015-05-06 01:48:34 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
VERIFY_IS_ZERO(ek->reserved);
|
|
|
|
|
|
2015-05-06 05:26:11 +02:00
|
|
|
|
/*
|
|
|
|
|
* WPA_80211_v3_1, Section 2.2.4:
|
|
|
|
|
* "Key Index (bits 4 and 5): specifies the key id of the temporal
|
|
|
|
|
* key of the key derived from the message. The value of this shall be
|
|
|
|
|
* zero (0) if the value of Key Type (bit 4) is Pairwise (1). The Key
|
|
|
|
|
* Type and Key Index shall not both be 0 in the same message.
|
|
|
|
|
*
|
|
|
|
|
* Group keys shall not use key id 0. This means that key ids 1 to 3
|
|
|
|
|
* are available to be used to identify Group keys. This document
|
|
|
|
|
* recommends that implementations reserve key ids 1 and 2 for Group
|
|
|
|
|
* Keys, and that key id 3 is not used.
|
|
|
|
|
*/
|
2015-05-06 01:48:34 +02:00
|
|
|
|
if (is_wpa && !ek->wpa_key_id)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-06 05:04:21 +02:00
|
|
|
|
bool eapol_verify_gtk_2_of_2(const struct eapol_key *ek, bool is_wpa)
|
|
|
|
|
{
|
|
|
|
|
uint16_t key_len;
|
|
|
|
|
|
|
|
|
|
/* Verify according to 802.11, Section 11.6.7.3 */
|
|
|
|
|
VERIFY_GTK_COMMON(ek);
|
|
|
|
|
|
|
|
|
|
if (ek->key_ack)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!ek->key_mic)
|
|
|
|
|
return false;
|
|
|
|
|
|
2015-05-18 13:31:41 +02:00
|
|
|
|
if (!ek->secure)
|
2015-05-06 05:04:21 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (ek->encrypted_key_data)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
key_len = L_BE16_TO_CPU(ek->key_length);
|
|
|
|
|
if (key_len != 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
VERIFY_IS_ZERO(ek->key_nonce);
|
|
|
|
|
VERIFY_IS_ZERO(ek->eapol_key_iv);
|
|
|
|
|
VERIFY_IS_ZERO(ek->key_rsc);
|
|
|
|
|
VERIFY_IS_ZERO(ek->reserved);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-13 21:10:28 +01:00
|
|
|
|
static struct eapol_key *eapol_create_common(
|
2014-12-28 05:33:46 +01:00
|
|
|
|
enum eapol_protocol_version protocol,
|
|
|
|
|
enum eapol_key_descriptor_version version,
|
2015-02-13 21:10:28 +01:00
|
|
|
|
bool secure,
|
2015-02-13 20:54:50 +01:00
|
|
|
|
uint64_t key_replay_counter,
|
2014-12-28 05:33:46 +01:00
|
|
|
|
const uint8_t snonce[],
|
|
|
|
|
size_t extra_len,
|
2015-05-06 01:48:34 +02:00
|
|
|
|
const uint8_t *extra_data,
|
2015-05-06 01:48:35 +02:00
|
|
|
|
int key_type,
|
|
|
|
|
bool is_wpa)
|
2014-12-28 05:33:46 +01:00
|
|
|
|
{
|
|
|
|
|
size_t to_alloc = sizeof(struct eapol_key);
|
|
|
|
|
struct eapol_key *out_frame = l_malloc(to_alloc + extra_len);
|
|
|
|
|
|
|
|
|
|
memset(out_frame, 0, to_alloc + extra_len);
|
|
|
|
|
|
2015-10-30 11:12:18 +01:00
|
|
|
|
out_frame->header.protocol_version = protocol;
|
|
|
|
|
out_frame->header.packet_type = 0x3;
|
|
|
|
|
out_frame->header.packet_len = L_CPU_TO_BE16(to_alloc + extra_len - 4);
|
2015-05-06 01:48:35 +02:00
|
|
|
|
out_frame->descriptor_type = is_wpa ? EAPOL_DESCRIPTOR_TYPE_WPA :
|
|
|
|
|
EAPOL_DESCRIPTOR_TYPE_80211;
|
2014-12-28 05:33:46 +01:00
|
|
|
|
out_frame->key_descriptor_version = version;
|
2015-05-06 01:48:34 +02:00
|
|
|
|
out_frame->key_type = key_type;
|
2014-12-28 05:33:46 +01:00
|
|
|
|
out_frame->install = false;
|
|
|
|
|
out_frame->key_ack = false;
|
|
|
|
|
out_frame->key_mic = true;
|
2015-02-13 21:10:28 +01:00
|
|
|
|
out_frame->secure = secure;
|
2014-12-28 05:33:46 +01:00
|
|
|
|
out_frame->error = false;
|
|
|
|
|
out_frame->request = false;
|
|
|
|
|
out_frame->encrypted_key_data = false;
|
|
|
|
|
out_frame->smk_message = false;
|
2015-02-13 20:54:50 +01:00
|
|
|
|
out_frame->key_length = 0;
|
|
|
|
|
out_frame->key_replay_counter = L_CPU_TO_BE64(key_replay_counter);
|
2014-12-28 05:33:46 +01:00
|
|
|
|
memcpy(out_frame->key_nonce, snonce, sizeof(out_frame->key_nonce));
|
|
|
|
|
out_frame->key_data_len = L_CPU_TO_BE16(extra_len);
|
|
|
|
|
memcpy(out_frame->key_data, extra_data, extra_len);
|
|
|
|
|
|
|
|
|
|
return out_frame;
|
|
|
|
|
}
|
2015-02-13 21:10:28 +01:00
|
|
|
|
|
|
|
|
|
struct eapol_key *eapol_create_ptk_2_of_4(
|
|
|
|
|
enum eapol_protocol_version protocol,
|
|
|
|
|
enum eapol_key_descriptor_version version,
|
|
|
|
|
uint64_t key_replay_counter,
|
|
|
|
|
const uint8_t snonce[],
|
|
|
|
|
size_t extra_len,
|
2015-05-06 01:48:35 +02:00
|
|
|
|
const uint8_t *extra_data,
|
|
|
|
|
bool is_wpa)
|
2015-02-13 21:10:28 +01:00
|
|
|
|
{
|
|
|
|
|
return eapol_create_common(protocol, version, false, key_replay_counter,
|
2015-05-06 01:48:35 +02:00
|
|
|
|
snonce, extra_len, extra_data, 1,
|
|
|
|
|
is_wpa);
|
2015-02-13 21:10:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct eapol_key *eapol_create_ptk_4_of_4(
|
|
|
|
|
enum eapol_protocol_version protocol,
|
|
|
|
|
enum eapol_key_descriptor_version version,
|
2015-05-06 01:48:35 +02:00
|
|
|
|
uint64_t key_replay_counter,
|
|
|
|
|
bool is_wpa)
|
2015-02-13 21:10:28 +01:00
|
|
|
|
{
|
2015-02-24 17:52:12 +01:00
|
|
|
|
uint8_t snonce[32];
|
|
|
|
|
|
|
|
|
|
memset(snonce, 0, sizeof(snonce));
|
2015-05-06 01:48:35 +02:00
|
|
|
|
return eapol_create_common(protocol, version,
|
|
|
|
|
is_wpa ? false : true,
|
|
|
|
|
key_replay_counter, snonce, 0, NULL,
|
|
|
|
|
1, is_wpa);
|
2015-05-06 01:48:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-06 01:48:35 +02:00
|
|
|
|
struct eapol_key *eapol_create_gtk_2_of_2(
|
2015-05-06 01:48:34 +02:00
|
|
|
|
enum eapol_protocol_version protocol,
|
|
|
|
|
enum eapol_key_descriptor_version version,
|
2015-05-06 01:48:35 +02:00
|
|
|
|
uint64_t key_replay_counter,
|
2015-05-18 21:31:11 +02:00
|
|
|
|
bool is_wpa, uint8_t wpa_key_id)
|
2015-05-06 01:48:34 +02:00
|
|
|
|
{
|
|
|
|
|
uint8_t snonce[32];
|
2015-05-18 21:31:11 +02:00
|
|
|
|
struct eapol_key *step2;
|
2015-05-06 01:48:34 +02:00
|
|
|
|
|
|
|
|
|
memset(snonce, 0, sizeof(snonce));
|
2015-05-18 21:31:11 +02:00
|
|
|
|
step2 = eapol_create_common(protocol, version, true,
|
2015-05-06 01:48:34 +02:00
|
|
|
|
key_replay_counter, snonce, 0, NULL,
|
2015-05-06 01:48:35 +02:00
|
|
|
|
0, is_wpa);
|
2015-05-18 21:31:11 +02:00
|
|
|
|
|
|
|
|
|
if (!step2)
|
|
|
|
|
return step2;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* WPA_80211_v3_1, Section 2.2.4:
|
|
|
|
|
* "The Key Type and Key Index shall not both be 0 in the same message"
|
|
|
|
|
*
|
|
|
|
|
* The above means that even though sending the key index back to the
|
|
|
|
|
* AP has no practical value, we must still do so.
|
|
|
|
|
*/
|
|
|
|
|
if (is_wpa)
|
|
|
|
|
step2->wpa_key_id = wpa_key_id;
|
|
|
|
|
|
|
|
|
|
return step2;
|
2015-02-13 21:10:28 +01:00
|
|
|
|
}
|
2015-02-24 17:54:23 +01:00
|
|
|
|
|
|
|
|
|
struct eapol_sm {
|
2016-11-02 23:46:19 +01:00
|
|
|
|
struct handshake_state *handshake;
|
2016-08-10 23:32:45 +02:00
|
|
|
|
enum eapol_protocol_version protocol_version;
|
2015-02-24 17:54:23 +01:00
|
|
|
|
uint64_t replay_counter;
|
2016-09-13 20:30:54 +02:00
|
|
|
|
eapol_sm_event_func_t event_func;
|
2015-03-26 04:34:05 +01:00
|
|
|
|
void *user_data;
|
2015-03-30 05:44:08 +02:00
|
|
|
|
struct l_timeout *timeout;
|
2016-10-11 08:36:48 +02:00
|
|
|
|
struct l_timeout *eapol_start_timeout;
|
2018-06-22 20:13:29 +02:00
|
|
|
|
unsigned int frame_retry;
|
|
|
|
|
uint16_t listen_interval;
|
2015-02-24 17:54:23 +01:00
|
|
|
|
bool have_replay:1;
|
2016-10-04 05:48:08 +02:00
|
|
|
|
bool started:1;
|
2016-10-11 08:36:48 +02:00
|
|
|
|
bool use_eapol_start:1;
|
2017-08-18 14:22:55 +02:00
|
|
|
|
bool require_handshake:1;
|
2017-05-03 19:53:50 +02:00
|
|
|
|
bool eap_exchanged:1;
|
2015-10-30 11:12:20 +01:00
|
|
|
|
struct eap_state *eap;
|
2017-09-30 04:28:20 +02:00
|
|
|
|
struct eapol_frame *early_frame;
|
|
|
|
|
uint32_t watch_id;
|
2017-10-19 23:41:39 +02:00
|
|
|
|
uint8_t installed_gtk_len;
|
|
|
|
|
uint8_t installed_gtk[CRYPTO_MAX_GTK_LEN];
|
|
|
|
|
uint8_t installed_igtk_len;
|
|
|
|
|
uint8_t installed_igtk[CRYPTO_MAX_IGTK_LEN];
|
2015-02-24 17:54:23 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void eapol_sm_destroy(void *value)
|
|
|
|
|
{
|
|
|
|
|
struct eapol_sm *sm = value;
|
|
|
|
|
|
2015-03-30 05:44:08 +02:00
|
|
|
|
l_timeout_remove(sm->timeout);
|
2016-10-11 08:36:48 +02:00
|
|
|
|
l_timeout_remove(sm->eapol_start_timeout);
|
2015-03-30 05:44:08 +02:00
|
|
|
|
|
2015-10-30 11:12:20 +01:00
|
|
|
|
if (sm->eap)
|
|
|
|
|
eap_free(sm->eap);
|
|
|
|
|
|
2016-10-04 05:48:08 +02:00
|
|
|
|
l_free(sm->early_frame);
|
|
|
|
|
|
2017-09-30 04:28:20 +02:00
|
|
|
|
eapol_frame_watch_remove(sm->watch_id);
|
|
|
|
|
|
2017-10-19 23:41:39 +02:00
|
|
|
|
sm->installed_gtk_len = 0;
|
|
|
|
|
memset(sm->installed_gtk, 0, sizeof(sm->installed_gtk));
|
|
|
|
|
sm->installed_igtk_len = 0;
|
|
|
|
|
memset(sm->installed_igtk, 0, sizeof(sm->installed_igtk));
|
|
|
|
|
|
2015-02-24 17:54:23 +01:00
|
|
|
|
l_free(sm);
|
2016-11-02 23:46:19 +01:00
|
|
|
|
|
|
|
|
|
l_queue_remove(state_machines, sm);
|
2015-02-24 17:54:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
struct eapol_sm *eapol_sm_new(struct handshake_state *hs)
|
2015-02-24 17:54:23 +01:00
|
|
|
|
{
|
|
|
|
|
struct eapol_sm *sm;
|
|
|
|
|
|
|
|
|
|
sm = l_new(struct eapol_sm, 1);
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
sm->handshake = hs;
|
2017-02-24 03:27:38 +01:00
|
|
|
|
|
|
|
|
|
if (hs->settings_8021x)
|
|
|
|
|
sm->use_eapol_start = true;
|
2016-11-02 23:46:19 +01:00
|
|
|
|
|
2017-08-18 14:22:55 +02:00
|
|
|
|
sm->require_handshake = true;
|
|
|
|
|
|
2015-02-24 17:54:23 +01:00
|
|
|
|
return sm;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void eapol_sm_free(struct eapol_sm *sm)
|
|
|
|
|
{
|
|
|
|
|
eapol_sm_destroy(sm);
|
|
|
|
|
}
|
2015-02-24 17:55:50 +01:00
|
|
|
|
|
2016-08-10 23:32:45 +02:00
|
|
|
|
void eapol_sm_set_protocol_version(struct eapol_sm *sm,
|
|
|
|
|
enum eapol_protocol_version protocol_version)
|
|
|
|
|
{
|
|
|
|
|
sm->protocol_version = protocol_version;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-22 20:13:29 +02:00
|
|
|
|
void eapol_sm_set_listen_interval(struct eapol_sm *sm, uint16_t interval)
|
|
|
|
|
{
|
|
|
|
|
sm->listen_interval = interval;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-26 04:34:05 +01:00
|
|
|
|
void eapol_sm_set_user_data(struct eapol_sm *sm, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
sm->user_data = user_data;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 20:30:54 +02:00
|
|
|
|
void eapol_sm_set_event_func(struct eapol_sm *sm, eapol_sm_event_func_t func)
|
|
|
|
|
{
|
|
|
|
|
sm->event_func = func;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-01 18:01:51 +02:00
|
|
|
|
static void eapol_sm_write(struct eapol_sm *sm, const struct eapol_frame *ef,
|
|
|
|
|
bool noencrypt)
|
|
|
|
|
{
|
|
|
|
|
__eapol_tx_packet(sm->handshake->ifindex, sm->handshake->aa, ETH_P_PAE,
|
|
|
|
|
ef, noencrypt);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
static inline void handshake_failed(struct eapol_sm *sm, uint16_t reason_code)
|
2015-03-30 04:00:00 +02:00
|
|
|
|
{
|
2018-06-22 20:13:27 +02:00
|
|
|
|
handshake_event(sm->handshake, HANDSHAKE_EVENT_FAILED, &reason_code);
|
|
|
|
|
|
2015-03-30 04:00:00 +02:00
|
|
|
|
eapol_sm_free(sm);
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-30 05:44:08 +02:00
|
|
|
|
static void eapol_timeout(struct l_timeout *timeout, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct eapol_sm *sm = user_data;
|
|
|
|
|
|
2017-01-31 03:42:49 +01:00
|
|
|
|
l_timeout_remove(sm->timeout);
|
2016-10-15 23:24:29 +02:00
|
|
|
|
sm->timeout = NULL;
|
2017-01-31 03:42:49 +01:00
|
|
|
|
|
2017-08-31 04:04:41 +02:00
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_4WAY_HANDSHAKE_TIMEOUT);
|
2016-11-02 23:46:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-19 23:41:39 +02:00
|
|
|
|
static void eapol_install_gtk(struct eapol_sm *sm, uint8_t gtk_key_index,
|
|
|
|
|
const uint8_t *gtk, size_t gtk_len,
|
|
|
|
|
const uint8_t *rsc)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Don't install the same GTK. On older kernels this resets the
|
|
|
|
|
* replay counters, etc and can lead to various attacks
|
|
|
|
|
*/
|
|
|
|
|
if (sm->installed_gtk_len == gtk_len &&
|
|
|
|
|
!memcmp(sm->installed_gtk, gtk, gtk_len))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
handshake_state_install_gtk(sm->handshake, gtk_key_index,
|
|
|
|
|
gtk, gtk_len, rsc, 6);
|
|
|
|
|
memcpy(sm->installed_gtk, gtk, gtk_len);
|
|
|
|
|
sm->installed_gtk_len = gtk_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void eapol_install_igtk(struct eapol_sm *sm, uint8_t igtk_key_index,
|
|
|
|
|
const uint8_t *igtk, size_t igtk_len)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Don't install the same IGTK. On older kernels this resets the
|
|
|
|
|
* replay counters, etc and can lead to various attacks
|
|
|
|
|
*/
|
|
|
|
|
if (sm->installed_igtk_len == igtk_len - 6 &&
|
|
|
|
|
!memcmp(sm->installed_igtk, igtk + 6, igtk_len - 6))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
handshake_state_install_igtk(sm->handshake, igtk_key_index,
|
|
|
|
|
igtk + 6, igtk_len - 6, igtk);
|
|
|
|
|
memcpy(sm->installed_igtk, igtk + 6, igtk_len - 6);
|
|
|
|
|
sm->installed_igtk_len = igtk_len - 6;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-11 08:36:48 +02:00
|
|
|
|
static void send_eapol_start(struct l_timeout *timeout, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct eapol_sm *sm = user_data;
|
|
|
|
|
uint8_t buf[sizeof(struct eapol_frame)];
|
|
|
|
|
struct eapol_frame *frame = (struct eapol_frame *) buf;
|
|
|
|
|
|
2018-06-22 20:13:27 +02:00
|
|
|
|
handshake_event(sm->handshake, HANDSHAKE_EVENT_STARTED, NULL);
|
|
|
|
|
|
2016-10-11 08:36:48 +02:00
|
|
|
|
l_timeout_remove(sm->eapol_start_timeout);
|
|
|
|
|
sm->eapol_start_timeout = NULL;
|
|
|
|
|
|
2016-10-11 09:33:46 +02:00
|
|
|
|
if (!sm->protocol_version)
|
|
|
|
|
sm->protocol_version = EAPOL_PROTOCOL_VERSION_2001;
|
|
|
|
|
|
2016-10-11 08:36:48 +02:00
|
|
|
|
frame->header.protocol_version = sm->protocol_version;
|
|
|
|
|
frame->header.packet_type = 1;
|
|
|
|
|
l_put_be16(0, &frame->header.packet_len);
|
|
|
|
|
|
2018-05-01 18:01:51 +02:00
|
|
|
|
eapol_sm_write(sm, frame, false);
|
2016-10-11 08:36:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-22 20:13:29 +02:00
|
|
|
|
static void eapol_set_key_timeout(struct eapol_sm *sm,
|
|
|
|
|
l_timeout_notify_cb_t cb)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* 802.11-2016 12.7.6.6: "The retransmit timeout value shall be
|
|
|
|
|
* 100 ms for the first timeout, half the listen interval for the
|
|
|
|
|
* second timeout, and the listen interval for subsequent timeouts.
|
|
|
|
|
* If there is no listen interval or the listen interval is zero,
|
|
|
|
|
* then 100 ms shall be used for all timeout values."
|
|
|
|
|
*/
|
|
|
|
|
unsigned int timeout_ms = 100;
|
|
|
|
|
unsigned int beacon_us = 100 * 1024;
|
|
|
|
|
|
|
|
|
|
sm->frame_retry++;
|
|
|
|
|
|
|
|
|
|
if (sm->frame_retry == 2 &&
|
|
|
|
|
sm->listen_interval != 0)
|
|
|
|
|
timeout_ms = sm->listen_interval * beacon_us / 2000;
|
|
|
|
|
else if (sm->frame_retry > 2 &&
|
|
|
|
|
sm->listen_interval != 0)
|
|
|
|
|
timeout_ms = sm->listen_interval * beacon_us / 1000;
|
|
|
|
|
|
|
|
|
|
if (sm->frame_retry > 1)
|
|
|
|
|
l_timeout_modify_ms(sm->timeout, timeout_ms);
|
|
|
|
|
else {
|
|
|
|
|
if (sm->timeout)
|
|
|
|
|
l_timeout_remove(sm->timeout);
|
|
|
|
|
|
|
|
|
|
sm->timeout = l_timeout_create_ms(timeout_ms, cb, sm,
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 802.11-2016 Section 12.7.6.2 */
|
|
|
|
|
static void eapol_send_ptk_1_of_4(struct eapol_sm *sm)
|
|
|
|
|
{
|
|
|
|
|
uint32_t ifindex = sm->handshake->ifindex;
|
|
|
|
|
const uint8_t *aa = sm->handshake->aa;
|
|
|
|
|
uint8_t frame_buf[512];
|
|
|
|
|
struct eapol_key *ek = (struct eapol_key *) frame_buf;
|
|
|
|
|
enum crypto_cipher cipher = ie_rsn_cipher_suite_to_cipher(
|
|
|
|
|
sm->handshake->pairwise_cipher);
|
|
|
|
|
uint8_t pmkid[16];
|
|
|
|
|
|
|
|
|
|
handshake_state_new_anonce(sm->handshake);
|
|
|
|
|
|
|
|
|
|
sm->handshake->ptk_complete = false;
|
|
|
|
|
|
|
|
|
|
sm->replay_counter++;
|
|
|
|
|
|
|
|
|
|
memset(ek, 0, sizeof(struct eapol_key));
|
|
|
|
|
ek->header.protocol_version = sm->protocol_version;
|
|
|
|
|
ek->header.packet_type = 0x3;
|
|
|
|
|
ek->descriptor_type = EAPOL_DESCRIPTOR_TYPE_80211;
|
|
|
|
|
/* Must be HMAC-SHA1-128 + AES when using CCMP with PSK or 8021X */
|
|
|
|
|
ek->key_descriptor_version = EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_SHA1_AES;
|
|
|
|
|
ek->key_type = true;
|
|
|
|
|
ek->key_ack = true;
|
|
|
|
|
ek->key_length = L_CPU_TO_BE16(crypto_cipher_key_len(cipher));
|
|
|
|
|
ek->key_replay_counter = L_CPU_TO_BE64(sm->replay_counter);
|
|
|
|
|
memcpy(ek->key_nonce, sm->handshake->anonce, sizeof(ek->key_nonce));
|
|
|
|
|
|
|
|
|
|
/* Write the PMKID KDE into Key Data field unencrypted */
|
|
|
|
|
crypto_derive_pmkid(sm->handshake->pmk, sm->handshake->spa, aa,
|
|
|
|
|
pmkid, false);
|
|
|
|
|
eapol_key_data_append(ek, HANDSHAKE_KDE_PMKID, pmkid, 16);
|
|
|
|
|
|
|
|
|
|
ek->header.packet_len = L_CPU_TO_BE16(sizeof(struct eapol_key) +
|
|
|
|
|
L_BE16_TO_CPU(ek->key_data_len) - 4);
|
|
|
|
|
|
|
|
|
|
l_debug("STA: "MAC" retries=%u", MAC_STR(sm->handshake->spa),
|
|
|
|
|
sm->frame_retry);
|
|
|
|
|
|
|
|
|
|
__eapol_tx_packet(ifindex, sm->handshake->spa, ETH_P_PAE,
|
|
|
|
|
(struct eapol_frame *) ek, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void eapol_ptk_1_of_4_retry(struct l_timeout *timeout, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct eapol_sm *sm = user_data;
|
|
|
|
|
|
|
|
|
|
if (sm->frame_retry >= 3) {
|
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_4WAY_HANDSHAKE_TIMEOUT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
eapol_send_ptk_1_of_4(sm);
|
|
|
|
|
|
|
|
|
|
eapol_set_key_timeout(sm, eapol_ptk_1_of_4_retry);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
static void eapol_handle_ptk_1_of_4(struct eapol_sm *sm,
|
2015-10-30 11:12:19 +01:00
|
|
|
|
const struct eapol_key *ek)
|
2015-02-24 18:13:57 +01:00
|
|
|
|
{
|
2016-11-02 23:46:19 +01:00
|
|
|
|
const struct crypto_ptk *ptk;
|
2015-02-24 18:13:57 +01:00
|
|
|
|
struct eapol_key *step2;
|
|
|
|
|
uint8_t mic[16];
|
2016-11-02 23:46:13 +01:00
|
|
|
|
uint8_t *ies;
|
|
|
|
|
size_t ies_len;
|
2018-08-25 03:54:24 +02:00
|
|
|
|
const uint8_t *own_ie = sm->handshake->supplicant_ie;
|
2017-04-21 19:09:53 +02:00
|
|
|
|
const uint8_t *pmkid;
|
|
|
|
|
struct ie_rsn_info rsn_info;
|
2015-02-24 18:13:57 +01:00
|
|
|
|
|
2018-06-22 20:13:29 +02:00
|
|
|
|
l_debug("ifindex=%u", sm->handshake->ifindex);
|
|
|
|
|
|
2016-11-02 23:46:13 +01:00
|
|
|
|
if (!eapol_verify_ptk_1_of_4(ek))
|
|
|
|
|
goto error_unspecified;
|
2015-02-24 18:13:57 +01:00
|
|
|
|
|
2017-04-21 19:09:53 +02:00
|
|
|
|
pmkid = handshake_util_find_pmkid_kde(ek->key_data,
|
|
|
|
|
L_BE16_TO_CPU(ek->key_data_len));
|
|
|
|
|
|
2018-03-15 12:06:54 +01:00
|
|
|
|
ie_parse_rsne_from_data(own_ie, own_ie[1] + 2, &rsn_info);
|
|
|
|
|
|
2017-04-21 19:09:53 +02:00
|
|
|
|
/*
|
|
|
|
|
* Require the PMKID KDE whenever we've sent a list of PMKIDs in
|
2017-05-03 19:53:50 +02:00
|
|
|
|
* our RSNE and we've haven't seen any EAPOL-EAP frame since
|
|
|
|
|
* (sm->eap_exchanged is false), otherwise treat it as optional and
|
|
|
|
|
* only validate it against our PMK. Some 802.11-2012 sections
|
|
|
|
|
* show message 1/4 without a PMKID KDE and there are APs that
|
|
|
|
|
* send no PMKID KDE.
|
2017-04-21 19:09:53 +02:00
|
|
|
|
*/
|
2017-05-03 19:53:50 +02:00
|
|
|
|
if (!sm->eap_exchanged && !sm->handshake->wpa_ie &&
|
2017-04-21 19:09:53 +02:00
|
|
|
|
rsn_info.num_pmkids) {
|
|
|
|
|
bool found = false;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (!pmkid)
|
|
|
|
|
goto error_unspecified;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < rsn_info.num_pmkids; i++)
|
|
|
|
|
if (!memcmp(rsn_info.pmkids + i * 16, pmkid, 16)) {
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
|
goto error_unspecified;
|
|
|
|
|
} else if (pmkid) {
|
|
|
|
|
uint8_t own_pmkid[16];
|
|
|
|
|
|
|
|
|
|
if (handshake_state_get_pmkid(sm->handshake, own_pmkid) &&
|
|
|
|
|
memcmp(pmkid, own_pmkid, 16)) {
|
2018-07-02 03:40:40 +02:00
|
|
|
|
l_debug("Authenticator sent a PMKID that didn't match");
|
|
|
|
|
|
2017-04-21 19:09:53 +02:00
|
|
|
|
/*
|
|
|
|
|
* If the AP has a different PMKSA from ours and we
|
|
|
|
|
* have means to create a new PMKSA through EAP then
|
|
|
|
|
* try that, otherwise give up.
|
|
|
|
|
*/
|
|
|
|
|
if (sm->eap) {
|
|
|
|
|
send_eapol_start(NULL, sm);
|
|
|
|
|
return;
|
2018-07-02 03:40:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Some APs are known to send a PMKID KDE with all
|
|
|
|
|
* zeros for the PMKID. Likely we can still
|
|
|
|
|
* successfully negotiate a handshake, so ignore this
|
|
|
|
|
* for now and treat it as if the PMKID KDE was not
|
|
|
|
|
* included
|
|
|
|
|
*/
|
|
|
|
|
if (util_mem_is_zero(pmkid, 16))
|
|
|
|
|
l_debug("PMKID is all zero, ignoring");
|
|
|
|
|
else
|
2017-04-21 19:09:53 +02:00
|
|
|
|
goto error_unspecified;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-19 03:40:34 +02:00
|
|
|
|
/*
|
|
|
|
|
* If we're in a state where we have successfully processed Message 3,
|
|
|
|
|
* then assume that the new message 1 is a PTK rekey and start a new
|
|
|
|
|
* handshake
|
|
|
|
|
*/
|
|
|
|
|
if (!sm->handshake->have_snonce ||
|
|
|
|
|
memcmp(sm->handshake->anonce,
|
|
|
|
|
ek->key_nonce, sizeof(ek->key_nonce)) ||
|
|
|
|
|
sm->handshake->ptk_complete) {
|
|
|
|
|
handshake_state_new_snonce(sm->handshake);
|
|
|
|
|
handshake_state_set_anonce(sm->handshake, ek->key_nonce);
|
|
|
|
|
|
|
|
|
|
if (!handshake_state_derive_ptk(sm->handshake))
|
|
|
|
|
goto error_unspecified;
|
|
|
|
|
}
|
2016-10-04 05:47:41 +02:00
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (sm->handshake->akm_suite &
|
|
|
|
|
(IE_RSN_AKM_SUITE_FT_OVER_8021X |
|
|
|
|
|
IE_RSN_AKM_SUITE_FT_USING_PSK |
|
|
|
|
|
IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256)) {
|
|
|
|
|
const uint8_t *mde = sm->handshake->mde;
|
|
|
|
|
const uint8_t *fte = sm->handshake->fte;
|
2016-11-02 23:46:13 +01:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Rebuild the RSNE to include the PMKR1Name and append
|
|
|
|
|
* MDE + FTE.
|
|
|
|
|
*/
|
|
|
|
|
ies = alloca(512);
|
|
|
|
|
|
|
|
|
|
rsn_info.num_pmkids = 1;
|
2016-11-02 23:46:19 +01:00
|
|
|
|
rsn_info.pmkids = sm->handshake->pmk_r1_name;
|
2016-11-02 23:46:13 +01:00
|
|
|
|
|
|
|
|
|
ie_build_rsne(&rsn_info, ies);
|
|
|
|
|
ies_len = ies[1] + 2;
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
memcpy(ies + ies_len, mde, mde[1] + 2);
|
|
|
|
|
ies_len += mde[1] + 2;
|
2016-11-02 23:46:13 +01:00
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
memcpy(ies + ies_len, fte, fte[1] + 2);
|
|
|
|
|
ies_len += fte[1] + 2;
|
2016-11-02 23:46:13 +01:00
|
|
|
|
} else {
|
2016-11-02 23:46:19 +01:00
|
|
|
|
ies_len = own_ie[1] + 2;
|
|
|
|
|
ies = (uint8_t *) own_ie;
|
2016-11-02 23:46:13 +01:00
|
|
|
|
}
|
2015-02-24 18:13:57 +01:00
|
|
|
|
|
2016-08-10 23:32:45 +02:00
|
|
|
|
step2 = eapol_create_ptk_2_of_4(sm->protocol_version,
|
2015-02-24 18:13:57 +01:00
|
|
|
|
ek->key_descriptor_version,
|
2017-10-19 03:31:49 +02:00
|
|
|
|
L_BE64_TO_CPU(ek->key_replay_counter),
|
2016-11-02 23:46:19 +01:00
|
|
|
|
sm->handshake->snonce, ies_len, ies,
|
|
|
|
|
sm->handshake->wpa_ie);
|
|
|
|
|
|
|
|
|
|
ptk = handshake_state_get_ptk(sm->handshake);
|
2015-02-24 18:13:57 +01:00
|
|
|
|
|
2018-08-09 20:13:55 +02:00
|
|
|
|
if (!eapol_calculate_mic(sm->handshake->akm_suite, ptk->kck,
|
|
|
|
|
step2, mic)) {
|
2015-03-20 20:52:49 +01:00
|
|
|
|
l_info("MIC calculation failed. "
|
|
|
|
|
"Ensure Kernel Crypto is available.");
|
2015-03-30 04:00:00 +02:00
|
|
|
|
l_free(step2);
|
2017-08-31 04:04:41 +02:00
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_UNSPECIFIED);
|
2015-03-30 04:00:00 +02:00
|
|
|
|
|
|
|
|
|
return;
|
2015-03-20 20:52:49 +01:00
|
|
|
|
}
|
2015-02-24 18:13:57 +01:00
|
|
|
|
|
|
|
|
|
memcpy(step2->key_mic_data, mic, sizeof(mic));
|
2018-05-01 18:01:51 +02:00
|
|
|
|
eapol_sm_write(sm, (struct eapol_frame *) step2, false);
|
2015-02-24 18:13:57 +01:00
|
|
|
|
l_free(step2);
|
2015-03-30 05:44:08 +02:00
|
|
|
|
|
2018-02-13 19:37:57 +01:00
|
|
|
|
l_timeout_remove(sm->eapol_start_timeout);
|
|
|
|
|
sm->eapol_start_timeout = NULL;
|
2016-11-02 23:46:13 +01:00
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
error_unspecified:
|
2017-08-31 04:04:41 +02:00
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_UNSPECIFIED);
|
2015-02-24 18:13:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-22 20:13:29 +02:00
|
|
|
|
#define EAPOL_PAIRWISE_UPDATE_COUNT 3
|
|
|
|
|
|
|
|
|
|
/* 802.11-2016 Section 12.7.6.4 */
|
|
|
|
|
static void eapol_send_ptk_3_of_4(struct eapol_sm *sm)
|
|
|
|
|
{
|
|
|
|
|
uint32_t ifindex = sm->handshake->ifindex;
|
|
|
|
|
uint8_t frame_buf[512];
|
|
|
|
|
uint8_t key_data_buf[128];
|
|
|
|
|
struct eapol_key *ek = (struct eapol_key *) frame_buf;
|
|
|
|
|
size_t key_data_len;
|
|
|
|
|
enum crypto_cipher cipher = ie_rsn_cipher_suite_to_cipher(
|
|
|
|
|
sm->handshake->pairwise_cipher);
|
|
|
|
|
const struct crypto_ptk *ptk = (struct crypto_ptk *) sm->handshake->ptk;
|
|
|
|
|
struct ie_rsn_info rsn;
|
|
|
|
|
|
|
|
|
|
sm->replay_counter++;
|
|
|
|
|
|
|
|
|
|
memset(ek, 0, sizeof(struct eapol_key));
|
|
|
|
|
ek->header.protocol_version = sm->protocol_version;
|
|
|
|
|
ek->header.packet_type = 0x3;
|
|
|
|
|
ek->descriptor_type = EAPOL_DESCRIPTOR_TYPE_80211;
|
|
|
|
|
/* Must be HMAC-SHA1-128 + AES when using CCMP with PSK or 8021X */
|
|
|
|
|
ek->key_descriptor_version = EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_SHA1_AES;
|
|
|
|
|
ek->key_type = true;
|
|
|
|
|
ek->install = true;
|
|
|
|
|
ek->key_ack = true;
|
|
|
|
|
ek->key_mic = true;
|
|
|
|
|
ek->secure = true;
|
|
|
|
|
ek->encrypted_key_data = true;
|
|
|
|
|
ek->key_length = L_CPU_TO_BE16(crypto_cipher_key_len(cipher));
|
|
|
|
|
ek->key_replay_counter = L_CPU_TO_BE64(sm->replay_counter);
|
|
|
|
|
memcpy(ek->key_nonce, sm->handshake->anonce, sizeof(ek->key_nonce));
|
|
|
|
|
/*
|
|
|
|
|
* We don't currently handle group traffic, to support that we'd need
|
|
|
|
|
* to provide the NL80211_ATTR_KEY_SEQ value from NL80211_CMD_GET_KEY
|
|
|
|
|
* here.
|
|
|
|
|
*/
|
|
|
|
|
l_put_be64(1, ek->key_rsc);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Just one RSNE in Key Data as we only set one cipher in ap->ciphers
|
|
|
|
|
* currently.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
memset(&rsn, 0, sizeof(rsn));
|
|
|
|
|
rsn.akm_suites = IE_RSN_AKM_SUITE_PSK;
|
|
|
|
|
rsn.pairwise_ciphers = sm->handshake->pairwise_cipher;
|
|
|
|
|
rsn.group_cipher = IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC;
|
|
|
|
|
|
|
|
|
|
if (!ie_build_rsne(&rsn, key_data_buf))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!eapol_encrypt_key_data(ptk->kek, key_data_buf,
|
|
|
|
|
2 + key_data_buf[1], ek))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
key_data_len = L_BE16_TO_CPU(ek->key_data_len);
|
|
|
|
|
ek->header.packet_len = L_CPU_TO_BE16(sizeof(struct eapol_key) +
|
|
|
|
|
key_data_len - 4);
|
|
|
|
|
|
2018-08-09 20:13:55 +02:00
|
|
|
|
if (!eapol_calculate_mic(sm->handshake->akm_suite, ptk->kck, ek,
|
|
|
|
|
ek->key_mic_data))
|
2018-06-22 20:13:29 +02:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
l_debug("STA: "MAC" retries=%u", MAC_STR(sm->handshake->spa),
|
|
|
|
|
sm->frame_retry);
|
|
|
|
|
|
|
|
|
|
__eapol_tx_packet(ifindex, sm->handshake->spa, ETH_P_PAE,
|
|
|
|
|
(struct eapol_frame *) ek, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void eapol_ptk_3_of_4_retry(struct l_timeout *timeout,
|
|
|
|
|
void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct eapol_sm *sm = user_data;
|
|
|
|
|
|
|
|
|
|
if (sm->frame_retry >= EAPOL_PAIRWISE_UPDATE_COUNT) {
|
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_4WAY_HANDSHAKE_TIMEOUT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
eapol_send_ptk_3_of_4(sm);
|
|
|
|
|
|
|
|
|
|
eapol_set_key_timeout(sm, eapol_ptk_3_of_4_retry);
|
|
|
|
|
|
|
|
|
|
l_debug("attempt %i", sm->frame_retry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 802.11-2016 Section 12.7.6.3 */
|
|
|
|
|
static void eapol_handle_ptk_2_of_4(struct eapol_sm *sm,
|
|
|
|
|
const struct eapol_key *ek)
|
|
|
|
|
{
|
|
|
|
|
const uint8_t *rsne;
|
|
|
|
|
enum crypto_cipher cipher;
|
|
|
|
|
size_t ptk_size;
|
|
|
|
|
uint8_t ptk_buf[64];
|
|
|
|
|
struct crypto_ptk *ptk = (struct crypto_ptk *) ptk_buf;
|
|
|
|
|
const uint8_t *aa = sm->handshake->aa;
|
|
|
|
|
|
|
|
|
|
l_debug("ifindex=%u", sm->handshake->ifindex);
|
|
|
|
|
|
|
|
|
|
if (!eapol_verify_ptk_2_of_4(ek))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (L_BE64_TO_CPU(ek->key_replay_counter) != sm->replay_counter)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
cipher = ie_rsn_cipher_suite_to_cipher(sm->handshake->pairwise_cipher);
|
|
|
|
|
ptk_size = sizeof(struct crypto_ptk) + crypto_cipher_key_len(cipher);
|
|
|
|
|
|
|
|
|
|
if (!crypto_derive_pairwise_ptk(sm->handshake->pmk, sm->handshake->spa,
|
|
|
|
|
aa, sm->handshake->anonce,
|
|
|
|
|
ek->key_nonce, ptk, ptk_size, false))
|
|
|
|
|
return;
|
|
|
|
|
|
2018-08-09 20:13:55 +02:00
|
|
|
|
if (!eapol_verify_mic(sm->handshake->akm_suite, ptk->kck, ek))
|
2018-06-22 20:13:29 +02:00
|
|
|
|
return;
|
|
|
|
|
|
2018-08-24 03:37:52 +02:00
|
|
|
|
/*
|
|
|
|
|
* 12.7.6.3 b) 2) "the Authenticator checks that the RSNE bitwise
|
|
|
|
|
* matches that from the (Re)Association Request frame.
|
|
|
|
|
*/
|
2018-06-22 20:13:29 +02:00
|
|
|
|
rsne = eapol_find_rsne(ek->key_data,
|
|
|
|
|
L_BE16_TO_CPU(ek->key_data_len), NULL);
|
2018-08-25 03:54:24 +02:00
|
|
|
|
if (!rsne || rsne[1] != sm->handshake->supplicant_ie[1] ||
|
|
|
|
|
memcmp(rsne + 2, sm->handshake->supplicant_ie + 2,
|
|
|
|
|
rsne[1])) {
|
2018-06-22 20:13:29 +02:00
|
|
|
|
|
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_IE_DIFFERENT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(sm->handshake->ptk, ptk_buf, ptk_size);
|
|
|
|
|
memcpy(sm->handshake->snonce, ek->key_nonce,
|
|
|
|
|
sizeof(sm->handshake->snonce));
|
|
|
|
|
sm->handshake->have_snonce = true;
|
|
|
|
|
sm->handshake->ptk_complete = true;
|
|
|
|
|
|
|
|
|
|
sm->frame_retry = 0;
|
|
|
|
|
|
|
|
|
|
eapol_ptk_3_of_4_retry(NULL, sm);
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-12 02:45:42 +02:00
|
|
|
|
const uint8_t *eapol_find_rsne(const uint8_t *data, size_t data_len,
|
|
|
|
|
const uint8_t **optional)
|
2015-02-25 05:09:10 +01:00
|
|
|
|
{
|
|
|
|
|
struct ie_tlv_iter iter;
|
2015-05-19 04:52:53 +02:00
|
|
|
|
const uint8_t *first = NULL;
|
2015-02-25 05:09:10 +01:00
|
|
|
|
|
|
|
|
|
ie_tlv_iter_init(&iter, data, data_len);
|
|
|
|
|
|
|
|
|
|
while (ie_tlv_iter_next(&iter)) {
|
|
|
|
|
if (ie_tlv_iter_get_tag(&iter) != IE_TYPE_RSN)
|
|
|
|
|
continue;
|
|
|
|
|
|
2015-05-19 04:52:53 +02:00
|
|
|
|
if (!first) {
|
|
|
|
|
first = ie_tlv_iter_get_data(&iter) - 2;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (optional)
|
|
|
|
|
*optional = ie_tlv_iter_get_data(&iter) - 2;
|
|
|
|
|
|
|
|
|
|
return first;
|
2015-02-25 05:09:10 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-19 04:52:53 +02:00
|
|
|
|
return first;
|
2015-02-25 05:09:10 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-06 01:48:35 +02:00
|
|
|
|
static const uint8_t *eapol_find_wpa_ie(const uint8_t *data, size_t data_len)
|
|
|
|
|
{
|
|
|
|
|
struct ie_tlv_iter iter;
|
|
|
|
|
|
|
|
|
|
ie_tlv_iter_init(&iter, data, data_len);
|
|
|
|
|
|
|
|
|
|
while (ie_tlv_iter_next(&iter)) {
|
|
|
|
|
if (ie_tlv_iter_get_tag(&iter) != IE_TYPE_VENDOR_SPECIFIC)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (is_ie_wpa_ie(ie_tlv_iter_get_data(&iter),
|
|
|
|
|
ie_tlv_iter_get_length(&iter)))
|
|
|
|
|
return ie_tlv_iter_get_data(&iter) - 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
static void eapol_handle_ptk_3_of_4(struct eapol_sm *sm,
|
2015-02-24 18:14:11 +01:00
|
|
|
|
const struct eapol_key *ek,
|
2015-02-24 23:02:32 +01:00
|
|
|
|
const uint8_t *decrypted_key_data,
|
2015-10-30 11:12:19 +01:00
|
|
|
|
size_t decrypted_key_data_size)
|
2015-02-24 18:14:11 +01:00
|
|
|
|
{
|
2016-11-02 23:46:19 +01:00
|
|
|
|
const struct crypto_ptk *ptk;
|
2015-02-24 18:14:11 +01:00
|
|
|
|
struct eapol_key *step4;
|
|
|
|
|
uint8_t mic[16];
|
2017-10-20 14:41:56 +02:00
|
|
|
|
const uint8_t *gtk = NULL;
|
2015-02-25 00:11:56 +01:00
|
|
|
|
size_t gtk_len;
|
2017-10-20 14:41:56 +02:00
|
|
|
|
const uint8_t *igtk = NULL;
|
2016-10-25 23:45:16 +02:00
|
|
|
|
size_t igtk_len;
|
2015-02-25 05:09:10 +01:00
|
|
|
|
const uint8_t *rsne;
|
2015-05-19 04:52:53 +02:00
|
|
|
|
const uint8_t *optional_rsne = NULL;
|
2015-03-26 05:26:31 +01:00
|
|
|
|
uint8_t gtk_key_index;
|
2016-10-25 23:45:16 +02:00
|
|
|
|
uint8_t igtk_key_index;
|
2015-02-24 18:14:11 +01:00
|
|
|
|
|
2018-06-22 20:13:29 +02:00
|
|
|
|
l_debug("ifindex=%u", sm->handshake->ifindex);
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (!eapol_verify_ptk_3_of_4(ek, sm->handshake->wpa_ie)) {
|
2017-08-31 04:04:41 +02:00
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_UNSPECIFIED);
|
2015-02-24 18:14:11 +01:00
|
|
|
|
return;
|
2015-03-30 04:00:00 +02:00
|
|
|
|
}
|
2015-02-24 18:14:11 +01:00
|
|
|
|
|
|
|
|
|
/*
|
2017-10-19 03:31:49 +02:00
|
|
|
|
* 802.11-2016, Section 12.7.6.4:
|
|
|
|
|
* "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."
|
2015-02-24 18:14:11 +01:00
|
|
|
|
*/
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (memcmp(sm->handshake->anonce, ek->key_nonce, sizeof(ek->key_nonce)))
|
2015-02-24 18:14:11 +01:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 11.6.6.4: "Verifies the RSNE. If it is part of a Fast BSS Transition
|
|
|
|
|
* Initial Mobility Domain Association, see 12.4.2. Otherwise, if it is
|
|
|
|
|
* not identical to that the STA received in the Beacon or Probe
|
|
|
|
|
* Response frame, the STA shall disassociate.
|
|
|
|
|
*/
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (!sm->handshake->wpa_ie)
|
2015-05-06 01:48:35 +02:00
|
|
|
|
rsne = eapol_find_rsne(decrypted_key_data,
|
2015-05-19 04:52:53 +02:00
|
|
|
|
decrypted_key_data_size,
|
|
|
|
|
&optional_rsne);
|
2015-05-06 01:48:35 +02:00
|
|
|
|
else
|
|
|
|
|
rsne = eapol_find_wpa_ie(decrypted_key_data,
|
|
|
|
|
decrypted_key_data_size);
|
2015-05-06 05:18:35 +02:00
|
|
|
|
|
2016-11-02 23:46:14 +01:00
|
|
|
|
if (!rsne)
|
|
|
|
|
goto error_ie_different;
|
2015-02-25 05:09:10 +01:00
|
|
|
|
|
2018-08-25 03:54:24 +02:00
|
|
|
|
if (!handshake_util_ap_ie_matches(rsne, sm->handshake->authenticator_ie,
|
2016-11-02 23:46:19 +01:00
|
|
|
|
sm->handshake->wpa_ie))
|
2016-11-02 23:46:14 +01:00
|
|
|
|
goto error_ie_different;
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (sm->handshake->akm_suite &
|
|
|
|
|
(IE_RSN_AKM_SUITE_FT_OVER_8021X |
|
|
|
|
|
IE_RSN_AKM_SUITE_FT_USING_PSK |
|
|
|
|
|
IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256)) {
|
2016-11-02 23:46:14 +01:00
|
|
|
|
struct ie_tlv_iter iter;
|
|
|
|
|
struct ie_rsn_info ie_info;
|
2016-11-02 23:46:19 +01:00
|
|
|
|
const uint8_t *mde = sm->handshake->mde;
|
|
|
|
|
const uint8_t *fte = sm->handshake->fte;
|
2016-11-02 23:46:14 +01:00
|
|
|
|
|
|
|
|
|
ie_parse_rsne_from_data(rsne, rsne[1] + 2, &ie_info);
|
|
|
|
|
|
|
|
|
|
if (ie_info.num_pmkids != 1 || memcmp(ie_info.pmkids,
|
2016-11-02 23:46:19 +01:00
|
|
|
|
sm->handshake->pmk_r1_name, 16))
|
2016-11-02 23:46:14 +01:00
|
|
|
|
goto error_ie_different;
|
|
|
|
|
|
|
|
|
|
ie_tlv_iter_init(&iter, decrypted_key_data,
|
|
|
|
|
decrypted_key_data_size);
|
|
|
|
|
|
|
|
|
|
while (ie_tlv_iter_next(&iter))
|
|
|
|
|
switch (ie_tlv_iter_get_tag(&iter)) {
|
|
|
|
|
case IE_TYPE_MOBILITY_DOMAIN:
|
|
|
|
|
if (memcmp(ie_tlv_iter_get_data(&iter) - 2,
|
2016-11-02 23:46:19 +01:00
|
|
|
|
mde, mde[1] + 2))
|
2016-11-02 23:46:14 +01:00
|
|
|
|
goto error_ie_different;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IE_TYPE_FAST_BSS_TRANSITION:
|
|
|
|
|
if (memcmp(ie_tlv_iter_get_data(&iter) - 2,
|
2016-11-02 23:46:19 +01:00
|
|
|
|
fte, fte[1] + 2))
|
2016-11-02 23:46:14 +01:00
|
|
|
|
goto error_ie_different;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-03-30 04:00:00 +02:00
|
|
|
|
}
|
2015-02-24 18:14:11 +01:00
|
|
|
|
|
2017-10-19 03:40:34 +02:00
|
|
|
|
/*
|
|
|
|
|
* If ptk_complete is set, then we are receiving Message 3 again.
|
|
|
|
|
* It must be a retransmission, otherwise the anonce wouldn't match
|
|
|
|
|
* and we wouldn't get here. Skip processing the rest of the message
|
|
|
|
|
* and send our reply. Do not install the keys again.
|
|
|
|
|
*/
|
|
|
|
|
if (sm->handshake->ptk_complete)
|
|
|
|
|
goto retransmit;
|
|
|
|
|
|
2015-02-24 18:14:11 +01:00
|
|
|
|
/*
|
|
|
|
|
* 11.6.6.4: "If a second RSNE is provided in the message, the
|
|
|
|
|
* Supplicant uses the pairwise cipher suite specified in the second
|
|
|
|
|
* RSNE or deauthenticates."
|
|
|
|
|
*/
|
2015-05-19 04:52:53 +02:00
|
|
|
|
if (optional_rsne) {
|
|
|
|
|
struct ie_rsn_info info1;
|
|
|
|
|
struct ie_rsn_info info2;
|
|
|
|
|
uint16_t override;
|
|
|
|
|
|
2016-11-02 23:46:14 +01:00
|
|
|
|
if (ie_parse_rsne_from_data(rsne, rsne[1] + 2, &info1) < 0)
|
|
|
|
|
goto error_ie_different;
|
2015-05-19 04:52:53 +02:00
|
|
|
|
|
|
|
|
|
if (ie_parse_rsne_from_data(optional_rsne, optional_rsne[1] + 2,
|
2016-11-02 23:46:14 +01:00
|
|
|
|
&info2) < 0)
|
|
|
|
|
goto error_ie_different;
|
2015-05-19 04:52:53 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 11.6.2:
|
|
|
|
|
* It may happen, for example, that a Supplicant selects a
|
|
|
|
|
* pairwise cipher suite which is advertised by an AP, but
|
|
|
|
|
* which policy disallows for this particular STA. An
|
|
|
|
|
* Authenticator may, therefore, insert a second RSNE to
|
|
|
|
|
* overrule the STA’s selection. An Authenticator’s SME shall
|
|
|
|
|
* insert the second RSNE, after the first RSNE, only for this
|
|
|
|
|
* purpose. The pairwise cipher suite in the second RSNE
|
|
|
|
|
* included shall be one of the ciphers advertised by the
|
|
|
|
|
* Authenticator. All other fields in the second RSNE shall be
|
|
|
|
|
* identical to the first RSNE.
|
|
|
|
|
*
|
|
|
|
|
* - Check that akm_suites and group_cipher are the same
|
|
|
|
|
* between rsne1 and rsne2
|
|
|
|
|
* - Check that pairwise_ciphers is not the same between rsne1
|
|
|
|
|
* and rsne2
|
|
|
|
|
* - Check that rsne2 pairwise_ciphers is a subset of rsne
|
|
|
|
|
*/
|
|
|
|
|
if (info1.akm_suites != info2.akm_suites ||
|
2016-11-02 23:46:14 +01:00
|
|
|
|
info1.group_cipher != info2.group_cipher)
|
|
|
|
|
goto error_ie_different;
|
2015-05-19 04:52:53 +02:00
|
|
|
|
|
|
|
|
|
override = info2.pairwise_ciphers;
|
|
|
|
|
|
|
|
|
|
if (override == info1.pairwise_ciphers ||
|
|
|
|
|
!(info1.pairwise_ciphers & override) ||
|
|
|
|
|
__builtin_popcount(override) != 1) {
|
2016-11-02 23:46:19 +01:00
|
|
|
|
handshake_failed(sm,
|
2017-08-31 04:04:41 +02:00
|
|
|
|
MMPDU_REASON_CODE_INVALID_PAIRWISE_CIPHER);
|
2015-05-19 04:52:53 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2015-05-22 04:10:21 +02:00
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
handshake_state_override_pairwise_cipher(sm->handshake,
|
|
|
|
|
override);
|
2015-05-19 04:52:53 +02:00
|
|
|
|
}
|
2015-02-25 00:11:56 +01:00
|
|
|
|
|
2017-10-21 01:27:19 +02:00
|
|
|
|
if (!sm->handshake->wpa_ie && sm->handshake->group_cipher !=
|
|
|
|
|
IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC) {
|
2016-11-02 23:46:19 +01:00
|
|
|
|
gtk = handshake_util_find_gtk_kde(decrypted_key_data,
|
|
|
|
|
decrypted_key_data_size,
|
|
|
|
|
>k_len);
|
2017-10-19 21:40:15 +02:00
|
|
|
|
if (!gtk) {
|
2017-08-31 04:04:41 +02:00
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_UNSPECIFIED);
|
2015-05-06 01:48:35 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2015-03-26 05:26:31 +01:00
|
|
|
|
|
2015-05-06 01:48:35 +02:00
|
|
|
|
/* TODO: Handle tx bit */
|
2015-03-26 05:26:31 +01:00
|
|
|
|
|
2015-05-06 01:48:35 +02:00
|
|
|
|
gtk_key_index = util_bit_field(gtk[0], 0, 2);
|
|
|
|
|
gtk += 2;
|
|
|
|
|
gtk_len -= 2;
|
|
|
|
|
} else
|
|
|
|
|
gtk = NULL;
|
2015-03-26 05:26:31 +01:00
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (sm->handshake->mfp) {
|
|
|
|
|
igtk = handshake_util_find_igtk_kde(decrypted_key_data,
|
|
|
|
|
decrypted_key_data_size,
|
|
|
|
|
&igtk_len);
|
2017-10-19 21:40:15 +02:00
|
|
|
|
if (!igtk) {
|
2017-08-31 04:04:41 +02:00
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_UNSPECIFIED);
|
2016-10-25 23:45:16 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-13 16:25:21 +01:00
|
|
|
|
igtk_key_index = l_get_le16(igtk);;
|
2016-10-25 23:45:16 +02:00
|
|
|
|
igtk += 2;
|
|
|
|
|
igtk_len -= 2;
|
|
|
|
|
} else
|
|
|
|
|
igtk = NULL;
|
|
|
|
|
|
2017-10-19 03:40:34 +02:00
|
|
|
|
retransmit:
|
2017-10-19 03:31:49 +02:00
|
|
|
|
/*
|
|
|
|
|
* 802.11-2016, Section 12.7.6.4:
|
|
|
|
|
* "b) Verifies the message 3 MIC. If the calculated MIC does not match
|
|
|
|
|
* the MIC that the Authenticator included in the EAPOL-Key frame, the
|
|
|
|
|
* Supplicant silently discards message 3."
|
|
|
|
|
* "c) Updates the last-seen value of the Key Replay Counter field."
|
|
|
|
|
*
|
|
|
|
|
* Note that part b was done in eapol_key_handle
|
|
|
|
|
*/
|
|
|
|
|
sm->replay_counter = L_BE64_TO_CPU(ek->key_replay_counter);
|
|
|
|
|
sm->have_replay = true;
|
|
|
|
|
|
2016-08-10 23:32:45 +02:00
|
|
|
|
step4 = eapol_create_ptk_4_of_4(sm->protocol_version,
|
2015-02-24 18:14:11 +01:00
|
|
|
|
ek->key_descriptor_version,
|
2016-11-02 23:46:19 +01:00
|
|
|
|
sm->replay_counter,
|
|
|
|
|
sm->handshake->wpa_ie);
|
|
|
|
|
|
|
|
|
|
ptk = handshake_state_get_ptk(sm->handshake);
|
2015-02-24 18:14:11 +01:00
|
|
|
|
|
2018-08-09 20:13:55 +02:00
|
|
|
|
if (!eapol_calculate_mic(sm->handshake->akm_suite, ptk->kck,
|
|
|
|
|
step4, mic)) {
|
2017-10-19 03:34:35 +02:00
|
|
|
|
l_free(step4);
|
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_UNSPECIFIED);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-02-24 18:14:11 +01:00
|
|
|
|
|
|
|
|
|
memcpy(step4->key_mic_data, mic, sizeof(mic));
|
2018-05-01 18:01:51 +02:00
|
|
|
|
eapol_sm_write(sm, (struct eapol_frame *) step4, false);
|
2017-10-19 03:34:35 +02:00
|
|
|
|
l_free(step4);
|
2015-02-24 18:14:11 +01:00
|
|
|
|
|
2017-10-19 03:40:34 +02:00
|
|
|
|
if (sm->handshake->ptk_complete)
|
|
|
|
|
return;
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
handshake_state_install_ptk(sm->handshake);
|
2017-10-20 14:41:56 +02:00
|
|
|
|
|
|
|
|
|
if (gtk)
|
|
|
|
|
eapol_install_gtk(sm, gtk_key_index, gtk, gtk_len, ek->key_rsc);
|
|
|
|
|
|
|
|
|
|
if (igtk)
|
|
|
|
|
eapol_install_igtk(sm, igtk_key_index, igtk, igtk_len);
|
2016-10-25 23:45:16 +02:00
|
|
|
|
|
2016-06-23 00:49:15 +02:00
|
|
|
|
if (rekey_offload)
|
2016-11-02 23:46:19 +01:00
|
|
|
|
rekey_offload(sm->handshake->ifindex, ptk->kek, ptk->kck,
|
|
|
|
|
sm->replay_counter, sm->user_data);
|
2016-06-23 00:49:15 +02:00
|
|
|
|
|
2018-02-13 19:37:57 +01:00
|
|
|
|
l_timeout_remove(sm->timeout);
|
|
|
|
|
sm->timeout = NULL;
|
|
|
|
|
|
2016-11-02 23:46:14 +01:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
error_ie_different:
|
2017-08-31 04:04:41 +02:00
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_IE_DIFFERENT);
|
2015-02-24 18:14:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-22 20:13:29 +02:00
|
|
|
|
/* 802.11-2016 Section 12.7.6.5 */
|
|
|
|
|
static void eapol_handle_ptk_4_of_4(struct eapol_sm *sm,
|
|
|
|
|
const struct eapol_key *ek)
|
|
|
|
|
{
|
|
|
|
|
const struct crypto_ptk *ptk = (struct crypto_ptk *) sm->handshake->ptk;
|
|
|
|
|
|
|
|
|
|
l_debug("ifindex=%u", sm->handshake->ifindex);
|
|
|
|
|
|
|
|
|
|
if (!eapol_verify_ptk_4_of_4(ek, false))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (L_BE64_TO_CPU(ek->key_replay_counter) != sm->replay_counter)
|
|
|
|
|
return;
|
|
|
|
|
|
2018-08-09 20:13:55 +02:00
|
|
|
|
if (!eapol_verify_mic(sm->handshake->akm_suite, ptk->kck, ek))
|
2018-06-22 20:13:29 +02:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
l_timeout_remove(sm->timeout);
|
|
|
|
|
sm->timeout = NULL;
|
|
|
|
|
|
|
|
|
|
handshake_state_install_ptk(sm->handshake);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
static void eapol_handle_gtk_1_of_2(struct eapol_sm *sm,
|
2015-05-06 01:48:34 +02:00
|
|
|
|
const struct eapol_key *ek,
|
|
|
|
|
const uint8_t *decrypted_key_data,
|
2015-10-30 11:12:19 +01:00
|
|
|
|
size_t decrypted_key_data_size)
|
2015-05-06 01:48:34 +02:00
|
|
|
|
{
|
2016-11-02 23:46:19 +01:00
|
|
|
|
const struct crypto_ptk *ptk;
|
2015-05-06 01:48:34 +02:00
|
|
|
|
struct eapol_key *step2;
|
|
|
|
|
uint8_t mic[16];
|
|
|
|
|
const uint8_t *gtk;
|
|
|
|
|
size_t gtk_len;
|
|
|
|
|
uint8_t gtk_key_index;
|
2016-10-25 23:45:16 +02:00
|
|
|
|
const uint8_t *igtk;
|
|
|
|
|
size_t igtk_len;
|
|
|
|
|
uint8_t igtk_key_index;
|
2015-05-06 01:48:34 +02:00
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (!eapol_verify_gtk_1_of_2(ek, sm->handshake->wpa_ie)) {
|
2017-08-31 04:04:41 +02:00
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_UNSPECIFIED);
|
2015-05-06 01:48:34 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (!sm->handshake->wpa_ie) {
|
|
|
|
|
gtk = handshake_util_find_gtk_kde(decrypted_key_data,
|
|
|
|
|
decrypted_key_data_size,
|
|
|
|
|
>k_len);
|
2017-10-19 21:40:15 +02:00
|
|
|
|
if (!gtk)
|
2015-05-06 01:48:35 +02:00
|
|
|
|
return;
|
2017-10-19 21:40:15 +02:00
|
|
|
|
|
|
|
|
|
gtk_key_index = util_bit_field(gtk[0], 0, 2);
|
|
|
|
|
gtk += 2;
|
|
|
|
|
gtk_len -= 2;
|
2015-05-06 01:48:35 +02:00
|
|
|
|
} else {
|
|
|
|
|
gtk = decrypted_key_data;
|
|
|
|
|
gtk_len = decrypted_key_data_size;
|
|
|
|
|
|
2017-10-19 21:40:15 +02:00
|
|
|
|
if (!gtk || gtk_len < CRYPTO_MIN_GTK_LEN ||
|
|
|
|
|
gtk_len > CRYPTO_MAX_GTK_LEN)
|
2015-05-06 01:48:35 +02:00
|
|
|
|
return;
|
2015-05-06 01:48:34 +02:00
|
|
|
|
|
2015-05-06 01:48:35 +02:00
|
|
|
|
gtk_key_index = ek->wpa_key_id;
|
2017-10-19 21:40:15 +02:00
|
|
|
|
}
|
2015-05-06 01:48:34 +02:00
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (sm->handshake->mfp) {
|
|
|
|
|
igtk = handshake_util_find_igtk_kde(decrypted_key_data,
|
|
|
|
|
decrypted_key_data_size,
|
|
|
|
|
&igtk_len);
|
2017-10-19 21:40:15 +02:00
|
|
|
|
if (!igtk)
|
2016-10-25 23:45:16 +02:00
|
|
|
|
return;
|
|
|
|
|
|
2016-12-13 05:07:31 +01:00
|
|
|
|
igtk_key_index = l_get_le16(igtk);;
|
2016-10-25 23:45:16 +02:00
|
|
|
|
igtk += 2;
|
|
|
|
|
igtk_len -= 2;
|
|
|
|
|
} else
|
|
|
|
|
igtk = NULL;
|
|
|
|
|
|
2017-10-19 03:31:49 +02:00
|
|
|
|
/*
|
|
|
|
|
* 802.11-2016, Section 12.7.7.2:
|
|
|
|
|
* "
|
|
|
|
|
* a) Verifies that the Key Replay Counter field value has not yet been
|
|
|
|
|
* seen before, i.e., its value is strictly larger than that in any
|
|
|
|
|
* other EAPOL-Key frame received thus far during this session.
|
|
|
|
|
* b) Verifies that the MIC is valid, i.e., it uses the KCK that is
|
|
|
|
|
* part of the PTK to verify that there is no data integrity error.
|
|
|
|
|
* c) Uses the MLME-SETKEYS.request primitive to configure the temporal
|
|
|
|
|
* GTK and, when present, IGTK into its IEEE 802.11 MAC.
|
|
|
|
|
* d) Responds by creating and sending message 2 of the group key
|
|
|
|
|
* handshake to the Authenticator and incrementing the replay counter.
|
|
|
|
|
* "
|
|
|
|
|
* Note: steps a & b are performed in eapol_key_handle
|
|
|
|
|
*/
|
|
|
|
|
sm->replay_counter = L_BE64_TO_CPU(ek->key_replay_counter);
|
|
|
|
|
sm->have_replay = true;
|
|
|
|
|
|
2016-08-10 23:32:45 +02:00
|
|
|
|
step2 = eapol_create_gtk_2_of_2(sm->protocol_version,
|
2015-05-06 01:48:34 +02:00
|
|
|
|
ek->key_descriptor_version,
|
2016-11-02 23:46:19 +01:00
|
|
|
|
sm->replay_counter,
|
|
|
|
|
sm->handshake->wpa_ie, ek->wpa_key_id);
|
2015-05-06 01:48:34 +02:00
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
ptk = handshake_state_get_ptk(sm->handshake);
|
2015-05-06 01:48:34 +02:00
|
|
|
|
|
2018-08-09 20:13:55 +02:00
|
|
|
|
if (!eapol_calculate_mic(sm->handshake->akm_suite, ptk->kck,
|
|
|
|
|
step2, mic)) {
|
2017-10-19 03:34:35 +02:00
|
|
|
|
l_free(step2);
|
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_UNSPECIFIED);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-05-06 01:48:34 +02:00
|
|
|
|
|
|
|
|
|
memcpy(step2->key_mic_data, mic, sizeof(mic));
|
2018-05-01 18:01:51 +02:00
|
|
|
|
eapol_sm_write(sm, (struct eapol_frame *) step2, false);
|
2017-10-19 03:34:35 +02:00
|
|
|
|
l_free(step2);
|
2015-05-06 01:48:34 +02:00
|
|
|
|
|
2017-10-19 23:41:39 +02:00
|
|
|
|
eapol_install_gtk(sm, gtk_key_index, gtk, gtk_len, ek->key_rsc);
|
2017-10-20 14:41:56 +02:00
|
|
|
|
|
|
|
|
|
if (igtk)
|
|
|
|
|
eapol_install_igtk(sm, igtk_key_index, igtk, igtk_len);
|
2015-05-06 01:48:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-12 17:02:04 +02:00
|
|
|
|
static struct eapol_sm *eapol_find_sm(uint32_t ifindex, const uint8_t *aa)
|
2015-03-20 02:55:25 +01:00
|
|
|
|
{
|
|
|
|
|
const struct l_queue_entry *entry;
|
|
|
|
|
struct eapol_sm *sm;
|
|
|
|
|
|
|
|
|
|
for (entry = l_queue_get_entries(state_machines); entry;
|
|
|
|
|
entry = entry->next) {
|
|
|
|
|
sm = entry->data;
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (sm->handshake->ifindex != ifindex)
|
2015-03-20 02:55:25 +01:00
|
|
|
|
continue;
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (memcmp(sm->handshake->aa, aa, ETH_ALEN))
|
2015-03-20 02:55:25 +01:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
return sm;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
static void eapol_key_handle(struct eapol_sm *sm,
|
2017-09-30 04:28:20 +02:00
|
|
|
|
const struct eapol_frame *frame)
|
2015-02-24 18:13:19 +01:00
|
|
|
|
{
|
|
|
|
|
const struct eapol_key *ek;
|
2016-11-02 23:46:19 +01:00
|
|
|
|
const struct crypto_ptk *ptk;
|
2015-02-24 18:13:19 +01:00
|
|
|
|
uint8_t *decrypted_key_data = NULL;
|
2015-05-06 01:48:35 +02:00
|
|
|
|
size_t key_data_len = 0;
|
2015-02-24 18:13:19 +01:00
|
|
|
|
uint64_t replay_counter;
|
|
|
|
|
|
2017-09-30 04:28:20 +02:00
|
|
|
|
ek = eapol_key_validate((const uint8_t *) frame,
|
|
|
|
|
sizeof(struct eapol_header) +
|
|
|
|
|
L_BE16_TO_CPU(frame->header.packet_len));
|
2015-02-24 18:13:19 +01:00
|
|
|
|
if (!ek)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Wrong direction */
|
|
|
|
|
if (!ek->key_ack)
|
|
|
|
|
return;
|
|
|
|
|
|
2015-05-06 01:48:35 +02:00
|
|
|
|
/* Further Descriptor Type check */
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (!sm->handshake->wpa_ie &&
|
|
|
|
|
ek->descriptor_type != EAPOL_DESCRIPTOR_TYPE_80211)
|
2015-05-06 01:48:35 +02:00
|
|
|
|
return;
|
2016-11-02 23:46:19 +01:00
|
|
|
|
else if (sm->handshake->wpa_ie &&
|
|
|
|
|
ek->descriptor_type != EAPOL_DESCRIPTOR_TYPE_WPA)
|
2015-05-06 01:48:35 +02:00
|
|
|
|
return;
|
|
|
|
|
|
2015-02-24 18:13:19 +01:00
|
|
|
|
replay_counter = L_BE64_TO_CPU(ek->key_replay_counter);
|
|
|
|
|
|
|
|
|
|
/*
|
2017-10-19 03:31:49 +02:00
|
|
|
|
* 802.11-2016, Section 12.7.2:
|
|
|
|
|
* "The Supplicant and Authenticator shall track the key replay counter
|
|
|
|
|
* per security association. The key replay counter shall be
|
|
|
|
|
* initialized to 0 on (re)association. The Authenticator shall
|
|
|
|
|
* increment the key replay counter on each successive EAPOL-Key frame."
|
|
|
|
|
*
|
|
|
|
|
* and
|
2015-02-24 18:13:19 +01:00
|
|
|
|
*
|
2017-10-19 03:31:49 +02:00
|
|
|
|
* "The Supplicant should also use the key replay counter and ignore
|
|
|
|
|
* EAPOL-Key frames with a Key Replay Counter field value smaller than
|
|
|
|
|
* or equal to any received in a valid message. The local Key Replay
|
|
|
|
|
* Counter field should not be updated until after the EAPOL-Key MIC is
|
|
|
|
|
* checked and is found to be valid. In other words, the Supplicant
|
|
|
|
|
* never updates the Key Replay Counter field for message 1 in the
|
|
|
|
|
* 4-way handshake, as it includes no MIC. This implies the Supplicant
|
|
|
|
|
* needs to allow for retransmission of message 1 when checking for
|
|
|
|
|
* the key replay counter of message 3."
|
|
|
|
|
*
|
|
|
|
|
* Note: The latter condition implies that Message 1 and Message 3
|
|
|
|
|
* can have the same replay counter, though other parts of the spec
|
|
|
|
|
* mandate that the Authenticator has to increment the replay counter
|
|
|
|
|
* for each frame sent. Contradictory.
|
2015-02-24 18:13:19 +01:00
|
|
|
|
*/
|
|
|
|
|
if (sm->have_replay && sm->replay_counter >= replay_counter)
|
|
|
|
|
return;
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
ptk = handshake_state_get_ptk(sm->handshake);
|
2015-02-24 18:13:19 +01:00
|
|
|
|
|
|
|
|
|
if (ek->key_mic) {
|
|
|
|
|
/* Haven't received step 1 yet, so no ptk */
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (!sm->handshake->have_snonce)
|
2015-02-24 18:13:19 +01:00
|
|
|
|
return;
|
|
|
|
|
|
2018-08-09 20:13:55 +02:00
|
|
|
|
if (!eapol_verify_mic(sm->handshake->akm_suite, ptk->kck, ek))
|
2015-02-24 18:13:19 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if ((ek->encrypted_key_data && !sm->handshake->wpa_ie) ||
|
|
|
|
|
(ek->key_type == 0 && sm->handshake->wpa_ie)) {
|
2015-02-24 18:13:19 +01:00
|
|
|
|
/* Haven't received step 1 yet, so no ptk */
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (!sm->handshake->have_snonce)
|
2015-02-24 18:13:19 +01:00
|
|
|
|
return;
|
|
|
|
|
|
2018-08-09 20:13:55 +02:00
|
|
|
|
decrypted_key_data = eapol_decrypt_key_data(
|
|
|
|
|
sm->handshake->akm_suite, ptk->kek,
|
|
|
|
|
ek, &key_data_len);
|
2015-02-24 18:13:19 +01:00
|
|
|
|
if (!decrypted_key_data)
|
|
|
|
|
return;
|
2015-05-06 01:48:35 +02:00
|
|
|
|
} else
|
|
|
|
|
key_data_len = L_BE16_TO_CPU(ek->key_data_len);
|
2015-02-24 18:13:19 +01:00
|
|
|
|
|
2015-05-06 01:48:34 +02:00
|
|
|
|
if (ek->key_type == 0) {
|
2017-10-19 03:39:31 +02:00
|
|
|
|
/* GTK handshake allowed only after PTK handshake complete */
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (!sm->handshake->ptk_complete)
|
2015-05-06 01:48:35 +02:00
|
|
|
|
goto done;
|
|
|
|
|
|
2017-10-21 01:27:19 +02:00
|
|
|
|
if (sm->handshake->group_cipher ==
|
|
|
|
|
IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC)
|
|
|
|
|
goto done;
|
|
|
|
|
|
2015-05-06 01:48:35 +02:00
|
|
|
|
if (!decrypted_key_data)
|
2015-05-06 01:48:34 +02:00
|
|
|
|
goto done;
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
eapol_handle_gtk_1_of_2(sm, ek, decrypted_key_data,
|
2015-10-30 11:12:19 +01:00
|
|
|
|
key_data_len);
|
2015-02-24 18:13:19 +01:00
|
|
|
|
goto done;
|
2015-05-06 01:48:34 +02:00
|
|
|
|
}
|
2015-02-24 18:13:19 +01:00
|
|
|
|
|
|
|
|
|
/* If no MIC, then assume packet 1, otherwise packet 3 */
|
|
|
|
|
if (!ek->key_mic)
|
2016-11-02 23:46:19 +01:00
|
|
|
|
eapol_handle_ptk_1_of_4(sm, ek);
|
2015-05-06 01:48:35 +02:00
|
|
|
|
else {
|
|
|
|
|
if (!key_data_len)
|
|
|
|
|
goto done;
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
eapol_handle_ptk_3_of_4(sm, ek,
|
2015-05-06 01:48:35 +02:00
|
|
|
|
decrypted_key_data ?: ek->key_data,
|
2015-10-30 11:12:19 +01:00
|
|
|
|
key_data_len);
|
2015-05-06 01:48:35 +02:00
|
|
|
|
}
|
2015-02-24 18:13:19 +01:00
|
|
|
|
|
|
|
|
|
done:
|
|
|
|
|
l_free(decrypted_key_data);
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-30 11:12:20 +01:00
|
|
|
|
/* This respresentes the eapMsg message in 802.1X Figure 8-1 */
|
|
|
|
|
static void eapol_eap_msg_cb(const uint8_t *eap_data, size_t len,
|
|
|
|
|
void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct eapol_sm *sm = user_data;
|
|
|
|
|
uint8_t buf[sizeof(struct eapol_frame) + len];
|
|
|
|
|
struct eapol_frame *frame = (struct eapol_frame *) buf;
|
|
|
|
|
|
2016-08-10 23:37:39 +02:00
|
|
|
|
frame->header.protocol_version = sm->protocol_version;
|
2015-10-30 11:12:20 +01:00
|
|
|
|
frame->header.packet_type = 0;
|
|
|
|
|
l_put_be16(len, &frame->header.packet_len);
|
|
|
|
|
|
|
|
|
|
memcpy(frame->data, eap_data, len);
|
|
|
|
|
|
2018-05-01 18:01:51 +02:00
|
|
|
|
eapol_sm_write(sm, frame, false);
|
2015-10-30 11:12:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This respresentes the eapTimout, eapFail and eapSuccess messages */
|
|
|
|
|
static void eapol_eap_complete_cb(enum eap_result result, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct eapol_sm *sm = user_data;
|
|
|
|
|
|
|
|
|
|
l_info("EAP completed with %s", result == EAP_RESULT_SUCCESS ?
|
|
|
|
|
"eapSuccess" : (result == EAP_RESULT_FAIL ?
|
|
|
|
|
"eapFail" : "eapTimeout"));
|
|
|
|
|
|
2018-05-30 22:08:08 +02:00
|
|
|
|
if (result != EAP_RESULT_SUCCESS) {
|
|
|
|
|
eap_free(sm->eap);
|
|
|
|
|
sm->eap = NULL;
|
2017-08-31 04:04:41 +02:00
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_IEEE8021X_FAILED);
|
2018-05-30 22:08:08 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
eap_reset(sm->eap);
|
2015-10-30 11:12:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This respresentes the eapResults message */
|
|
|
|
|
static void eapol_eap_results_cb(const uint8_t *msk_data, size_t msk_len,
|
|
|
|
|
const uint8_t *emsk_data, size_t emsk_len,
|
|
|
|
|
const uint8_t *iv, size_t iv_len,
|
|
|
|
|
void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct eapol_sm *sm = user_data;
|
|
|
|
|
|
|
|
|
|
l_debug("EAP key material received");
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 802.11i 8.5.1.2:
|
|
|
|
|
* "When not using a PSK, the PMK is derived from the AAA key.
|
|
|
|
|
* The PMK shall be computed as the first 256 bits (bits 0–255)
|
|
|
|
|
* of the AAA key: PMK ← L(PTK, 0, 256)."
|
2018-03-15 12:06:53 +01:00
|
|
|
|
* 802.11-2016 12.7.1.3:
|
2015-10-30 11:12:20 +01:00
|
|
|
|
* "When not using a PSK, the PMK is derived from the MSK.
|
2018-03-15 12:06:53 +01:00
|
|
|
|
* The PMK shall be computed as the first PMK_bits bits
|
|
|
|
|
* (bits 0 to PMK_bits–1) of the MSK: PMK = L(MSK, 0, PMK_bits)."
|
2015-10-30 11:12:20 +01:00
|
|
|
|
* RFC5247 explains AAA-Key refers to the MSK and confirms the
|
|
|
|
|
* first 32 bytes of the MSK are used. MSK is at least 64 octets
|
|
|
|
|
* long per RFC3748. Note WEP derives the PTK from MSK differently.
|
2016-09-06 23:43:43 +02:00
|
|
|
|
*
|
|
|
|
|
* In a Fast Transition initial mobility domain association the PMK
|
2018-03-15 12:06:53 +01:00
|
|
|
|
* maps to the XXKey, except with EAP:
|
|
|
|
|
* 802.11-2016 12.7.1.7.3:
|
|
|
|
|
* "If the AKM negotiated is 00-0F-AC:3, then [...] XXKey shall be
|
|
|
|
|
* the second 256 bits of the MSK (which is derived from the IEEE
|
2016-09-06 23:43:43 +02:00
|
|
|
|
* 802.1X authentication), i.e., XXKey = L(MSK, 256, 256)."
|
2018-03-15 12:06:53 +01:00
|
|
|
|
* So we need to save the first 64 bytes at minimum.
|
2015-10-30 11:12:20 +01:00
|
|
|
|
*/
|
|
|
|
|
|
2017-01-06 08:51:18 +01:00
|
|
|
|
if (sm->handshake->akm_suite == IE_RSN_AKM_SUITE_FT_OVER_8021X) {
|
2018-03-15 12:06:53 +01:00
|
|
|
|
if (msk_len < 64)
|
|
|
|
|
goto msk_short;
|
2017-01-06 08:51:18 +01:00
|
|
|
|
} else {
|
2018-03-15 12:06:53 +01:00
|
|
|
|
if (msk_len < 32)
|
|
|
|
|
goto msk_short;
|
2017-01-06 08:51:18 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-15 12:06:53 +01:00
|
|
|
|
if (msk_len > sizeof(sm->handshake->pmk))
|
|
|
|
|
msk_len = sizeof(sm->handshake->pmk);
|
2017-01-06 08:51:18 +01:00
|
|
|
|
|
2018-03-15 12:06:53 +01:00
|
|
|
|
handshake_state_set_pmk(sm->handshake, msk_data, msk_len);
|
2017-01-06 08:51:18 +01:00
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
msk_short:
|
|
|
|
|
l_error("EAP method's MSK too short for AKM suite %u",
|
|
|
|
|
sm->handshake->akm_suite);
|
|
|
|
|
|
2017-08-31 04:04:41 +02:00
|
|
|
|
handshake_failed(sm, MMPDU_REASON_CODE_IEEE8021X_FAILED);
|
2015-10-30 11:12:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 20:30:54 +02:00
|
|
|
|
static void eapol_eap_event_cb(unsigned int event,
|
|
|
|
|
const void *event_data, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct eapol_sm *sm = user_data;
|
|
|
|
|
|
|
|
|
|
if (!sm->event_func)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
sm->event_func(event, event_data, sm->user_data);
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-11 08:36:48 +02:00
|
|
|
|
void eapol_sm_set_use_eapol_start(struct eapol_sm *sm, bool enabled)
|
|
|
|
|
{
|
|
|
|
|
sm->use_eapol_start = enabled;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-18 14:22:55 +02:00
|
|
|
|
void eapol_sm_set_require_handshake(struct eapol_sm *sm, bool enabled)
|
|
|
|
|
{
|
|
|
|
|
sm->require_handshake = enabled;
|
|
|
|
|
|
|
|
|
|
if (!sm->require_handshake)
|
|
|
|
|
sm->use_eapol_start = false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-22 20:13:29 +02:00
|
|
|
|
static void eapol_auth_key_handle(struct eapol_sm *sm,
|
|
|
|
|
const struct eapol_frame *frame)
|
|
|
|
|
{
|
|
|
|
|
size_t frame_len = 4 + L_BE16_TO_CPU(frame->header.packet_len);
|
|
|
|
|
const struct eapol_key *ek = eapol_key_validate((const void *) frame,
|
|
|
|
|
frame_len);
|
|
|
|
|
|
|
|
|
|
if (!ek)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Wrong direction */
|
|
|
|
|
if (ek->key_ack)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (ek->request)
|
|
|
|
|
return; /* Not supported */
|
|
|
|
|
|
|
|
|
|
if (!sm->handshake->have_anonce)
|
|
|
|
|
return; /* Not expecting an EAPoL-Key yet */
|
|
|
|
|
|
|
|
|
|
if (!sm->handshake->ptk_complete)
|
|
|
|
|
eapol_handle_ptk_2_of_4(sm, ek);
|
|
|
|
|
else
|
|
|
|
|
eapol_handle_ptk_4_of_4(sm, ek);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void eapol_rx_auth_packet(uint16_t proto, const uint8_t *from,
|
|
|
|
|
const struct eapol_frame *frame,
|
|
|
|
|
void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct eapol_sm *sm = user_data;
|
|
|
|
|
|
|
|
|
|
if (!sm->protocol_version)
|
|
|
|
|
sm->protocol_version = frame->header.protocol_version;
|
|
|
|
|
|
|
|
|
|
switch (frame->header.packet_type) {
|
|
|
|
|
case 3: /* EAPOL-Key */
|
|
|
|
|
eapol_auth_key_handle(sm, frame);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
l_error("Authenticator received unknown packet type %i from %s",
|
|
|
|
|
frame->header.packet_type,
|
|
|
|
|
util_address_to_string(from));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-30 04:28:20 +02:00
|
|
|
|
static void eapol_rx_packet(uint16_t proto, const uint8_t *from,
|
|
|
|
|
const struct eapol_frame *frame,
|
|
|
|
|
void *user_data)
|
2015-10-30 11:12:20 +01:00
|
|
|
|
{
|
2017-09-30 04:28:20 +02:00
|
|
|
|
struct eapol_sm *sm = user_data;
|
|
|
|
|
|
|
|
|
|
if (proto != ETH_P_PAE || memcmp(from, sm->handshake->aa, 6))
|
|
|
|
|
return;
|
2015-10-30 11:12:20 +01:00
|
|
|
|
|
2016-10-04 05:48:08 +02:00
|
|
|
|
if (!sm->started) {
|
2017-09-30 04:28:20 +02:00
|
|
|
|
size_t len = sizeof(struct eapol_header) +
|
|
|
|
|
L_BE16_TO_CPU(frame->header.packet_len);
|
2016-10-04 05:48:08 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the state machine hasn't started yet save the frame
|
|
|
|
|
* for processing later.
|
|
|
|
|
*/
|
|
|
|
|
if (sm->early_frame) /* Is the 1-element queue full */
|
|
|
|
|
return;
|
|
|
|
|
|
2017-09-30 04:28:20 +02:00
|
|
|
|
sm->early_frame = l_memdup(frame, len);
|
2016-10-04 05:48:08 +02:00
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-10 23:32:45 +02:00
|
|
|
|
if (!sm->protocol_version)
|
2017-09-30 04:28:20 +02:00
|
|
|
|
sm->protocol_version = frame->header.protocol_version;
|
2016-08-10 23:32:45 +02:00
|
|
|
|
|
2017-09-30 04:28:20 +02:00
|
|
|
|
switch (frame->header.packet_type) {
|
2015-10-30 11:12:20 +01:00
|
|
|
|
case 0: /* EAPOL-EAP */
|
2016-10-11 08:36:48 +02:00
|
|
|
|
l_timeout_remove(sm->eapol_start_timeout);
|
|
|
|
|
sm->eapol_start_timeout = 0;
|
|
|
|
|
|
2015-10-30 11:12:20 +01:00
|
|
|
|
if (!sm->eap) {
|
2015-11-03 23:26:34 +01:00
|
|
|
|
/* If we're not configured for EAP, send a NAK */
|
2015-10-30 11:12:20 +01:00
|
|
|
|
sm->eap = eap_new(eapol_eap_msg_cb,
|
|
|
|
|
eapol_eap_complete_cb, sm);
|
|
|
|
|
|
|
|
|
|
if (!sm->eap)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
eap_set_key_material_func(sm->eap,
|
|
|
|
|
eapol_eap_results_cb);
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-03 19:53:50 +02:00
|
|
|
|
sm->eap_exchanged = true;
|
|
|
|
|
|
2017-09-30 04:28:20 +02:00
|
|
|
|
eap_rx_packet(sm->eap, frame->data,
|
|
|
|
|
L_BE16_TO_CPU(frame->header.packet_len));
|
2015-10-30 11:12:20 +01:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 3: /* EAPOL-Key */
|
2017-04-21 19:09:53 +02:00
|
|
|
|
if (!sm->handshake->have_pmk) {
|
|
|
|
|
if (!sm->eap)
|
|
|
|
|
return;
|
|
|
|
|
|
2016-11-18 01:24:53 +01:00
|
|
|
|
/*
|
|
|
|
|
* Either this is an error (EAP negotiation in
|
|
|
|
|
* progress) or the server is giving us a chance to
|
|
|
|
|
* use a cached PMK. We don't yet cache PMKs so
|
|
|
|
|
* send an EAPOL-Start if we haven't sent one yet.
|
|
|
|
|
*/
|
|
|
|
|
if (sm->eapol_start_timeout)
|
|
|
|
|
send_eapol_start(NULL, sm);
|
|
|
|
|
|
2015-10-30 11:12:20 +01:00
|
|
|
|
return;
|
2016-11-18 01:24:53 +01:00
|
|
|
|
}
|
2015-10-30 11:12:20 +01:00
|
|
|
|
|
2017-09-30 04:28:20 +02:00
|
|
|
|
eapol_key_handle(sm, frame);
|
2015-10-30 11:12:20 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-23 00:49:15 +02:00
|
|
|
|
void __eapol_update_replay_counter(uint32_t ifindex, const uint8_t *spa,
|
|
|
|
|
const uint8_t *aa, uint64_t replay_counter)
|
|
|
|
|
{
|
|
|
|
|
struct eapol_sm *sm;
|
|
|
|
|
|
2016-09-12 17:02:04 +02:00
|
|
|
|
sm = eapol_find_sm(ifindex, aa);
|
2016-06-23 00:49:15 +02:00
|
|
|
|
|
|
|
|
|
if (!sm)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (sm->replay_counter >= replay_counter)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
sm->replay_counter = replay_counter;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-24 18:10:42 +01:00
|
|
|
|
void __eapol_set_tx_packet_func(eapol_tx_packet_func_t func)
|
|
|
|
|
{
|
|
|
|
|
tx_packet = func;
|
|
|
|
|
}
|
2015-02-24 18:09:42 +01:00
|
|
|
|
|
2016-09-12 17:02:04 +02:00
|
|
|
|
void __eapol_set_tx_user_data(void *user_data)
|
|
|
|
|
{
|
|
|
|
|
tx_user_data = user_data;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-23 00:49:15 +02:00
|
|
|
|
void __eapol_set_rekey_offload_func(eapol_rekey_offload_func_t func)
|
|
|
|
|
{
|
|
|
|
|
rekey_offload = func;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-02 23:46:19 +01:00
|
|
|
|
void eapol_register(struct eapol_sm *sm)
|
2015-03-20 18:31:14 +01:00
|
|
|
|
{
|
2016-10-04 05:48:08 +02:00
|
|
|
|
l_queue_push_head(state_machines, sm);
|
2017-09-30 04:28:20 +02:00
|
|
|
|
|
2018-08-15 19:36:19 +02:00
|
|
|
|
if (sm->handshake->authenticator) {
|
|
|
|
|
sm->watch_id = eapol_frame_watch_add(sm->handshake->ifindex,
|
|
|
|
|
eapol_rx_auth_packet, sm);
|
2018-06-22 20:13:29 +02:00
|
|
|
|
|
2018-08-15 19:36:19 +02:00
|
|
|
|
sm->started = true;
|
2018-06-22 20:13:29 +02:00
|
|
|
|
|
2018-08-15 19:36:19 +02:00
|
|
|
|
/* kick off handshake */
|
|
|
|
|
eapol_ptk_1_of_4_retry(NULL, sm);
|
|
|
|
|
} else
|
|
|
|
|
sm->watch_id = eapol_frame_watch_add(sm->handshake->ifindex,
|
|
|
|
|
eapol_rx_packet, sm);
|
2018-06-22 20:13:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-27 23:39:53 +02:00
|
|
|
|
bool eapol_start(struct eapol_sm *sm)
|
2016-10-04 05:48:08 +02:00
|
|
|
|
{
|
2016-11-02 23:46:19 +01:00
|
|
|
|
if (sm->handshake->settings_8021x) {
|
|
|
|
|
sm->eap = eap_new(eapol_eap_msg_cb, eapol_eap_complete_cb, sm);
|
|
|
|
|
|
|
|
|
|
if (!sm->eap)
|
|
|
|
|
goto eap_error;
|
|
|
|
|
|
|
|
|
|
if (!eap_load_settings(sm->eap, sm->handshake->settings_8021x,
|
|
|
|
|
"EAP-")) {
|
|
|
|
|
eap_free(sm->eap);
|
|
|
|
|
sm->eap = NULL;
|
|
|
|
|
|
|
|
|
|
goto eap_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
eap_set_key_material_func(sm->eap, eapol_eap_results_cb);
|
|
|
|
|
eap_set_event_func(sm->eap, eapol_eap_event_cb);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-27 23:39:53 +02:00
|
|
|
|
sm->started = true;
|
|
|
|
|
|
|
|
|
|
if (sm->require_handshake)
|
2018-04-02 20:47:43 +02:00
|
|
|
|
sm->timeout = l_timeout_create(eapol_4way_handshake_time,
|
|
|
|
|
eapol_timeout, sm, NULL);
|
2017-10-27 23:39:53 +02:00
|
|
|
|
|
|
|
|
|
if (sm->use_eapol_start) {
|
|
|
|
|
/*
|
|
|
|
|
* We start a short timeout, if EAP packets are not received
|
|
|
|
|
* from AP, then we send the EAPoL-Start
|
|
|
|
|
*/
|
|
|
|
|
sm->eapol_start_timeout =
|
|
|
|
|
l_timeout_create(1, send_eapol_start, sm, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-04 05:48:08 +02:00
|
|
|
|
/* Process any frames received early due to scheduling */
|
|
|
|
|
if (sm->early_frame) {
|
2017-09-30 04:28:20 +02:00
|
|
|
|
eapol_rx_packet(ETH_P_PAE, sm->handshake->aa,
|
|
|
|
|
sm->early_frame, sm);
|
|
|
|
|
l_free(sm->early_frame);
|
2016-10-04 05:48:08 +02:00
|
|
|
|
sm->early_frame = NULL;
|
|
|
|
|
}
|
2016-06-28 23:56:56 +02:00
|
|
|
|
|
2017-10-27 23:39:53 +02:00
|
|
|
|
return true;
|
2016-11-02 23:46:19 +01:00
|
|
|
|
|
|
|
|
|
eap_error:
|
|
|
|
|
l_error("Error initializing EAP for ifindex %i",
|
|
|
|
|
(int) sm->handshake->ifindex);
|
2017-10-27 23:39:53 +02:00
|
|
|
|
|
|
|
|
|
return false;
|
2016-06-28 23:56:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-07 23:04:45 +02:00
|
|
|
|
struct eapol_frame_watch {
|
|
|
|
|
uint32_t ifindex;
|
|
|
|
|
struct watchlist_item super;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void eapol_frame_watch_free(struct watchlist_item *item)
|
|
|
|
|
{
|
|
|
|
|
struct eapol_frame_watch *efw =
|
|
|
|
|
container_of(item, struct eapol_frame_watch, super);
|
|
|
|
|
|
|
|
|
|
l_free(efw);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct watchlist_ops eapol_frame_watch_ops = {
|
|
|
|
|
.item_free = eapol_frame_watch_free,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
uint32_t eapol_frame_watch_add(uint32_t ifindex,
|
|
|
|
|
eapol_frame_watch_func_t handler,
|
|
|
|
|
void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct eapol_frame_watch *efw;
|
|
|
|
|
|
|
|
|
|
efw = l_new(struct eapol_frame_watch, 1);
|
|
|
|
|
efw->ifindex = ifindex;
|
|
|
|
|
|
|
|
|
|
return watchlist_link(&frame_watches, &efw->super,
|
|
|
|
|
handler, user_data, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool eapol_frame_watch_remove(uint32_t id)
|
|
|
|
|
{
|
|
|
|
|
return watchlist_remove(&frame_watches, id);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-29 03:53:26 +02:00
|
|
|
|
struct preauth_sm {
|
|
|
|
|
uint32_t ifindex;
|
|
|
|
|
uint8_t aa[6];
|
|
|
|
|
uint8_t spa[6];
|
|
|
|
|
struct eap_state *eap;
|
|
|
|
|
uint8_t pmk[32];
|
|
|
|
|
eapol_preauth_cb_t cb;
|
|
|
|
|
eapol_preauth_destroy_func_t destroy;
|
|
|
|
|
void *user_data;
|
2017-08-25 01:48:04 +02:00
|
|
|
|
struct l_timeout *timeout;
|
2017-09-30 04:28:21 +02:00
|
|
|
|
uint32_t watch_id;
|
2017-08-25 01:48:04 +02:00
|
|
|
|
bool initial_rx:1;
|
2017-04-29 03:53:26 +02:00
|
|
|
|
};
|
|
|
|
|
|
2017-08-25 01:48:04 +02:00
|
|
|
|
#define EAPOL_TIMEOUT_SEC 1
|
|
|
|
|
|
2017-04-29 03:53:26 +02:00
|
|
|
|
static void preauth_sm_destroy(void *value)
|
2017-04-15 13:58:47 +02:00
|
|
|
|
{
|
2017-04-29 03:53:26 +02:00
|
|
|
|
struct preauth_sm *sm = value;
|
|
|
|
|
|
|
|
|
|
if (sm->destroy)
|
|
|
|
|
sm->destroy(sm->user_data);
|
2017-04-15 13:58:47 +02:00
|
|
|
|
|
2017-04-29 03:53:26 +02:00
|
|
|
|
eap_free(sm->eap);
|
2017-08-25 01:48:04 +02:00
|
|
|
|
l_timeout_remove(sm->timeout);
|
2017-09-30 04:28:21 +02:00
|
|
|
|
eapol_frame_watch_remove(sm->watch_id);
|
2017-04-29 03:53:26 +02:00
|
|
|
|
l_free(sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void preauth_frame(struct preauth_sm *sm, uint8_t packet_type,
|
|
|
|
|
const uint8_t *data, size_t data_len)
|
|
|
|
|
{
|
|
|
|
|
uint8_t buf[sizeof(struct eapol_frame) + data_len];
|
|
|
|
|
struct eapol_frame *frame = (struct eapol_frame *) buf;
|
|
|
|
|
|
|
|
|
|
frame->header.protocol_version = EAPOL_PROTOCOL_VERSION_2001;
|
|
|
|
|
frame->header.packet_type = packet_type;
|
|
|
|
|
l_put_be16(data_len, &frame->header.packet_len);
|
|
|
|
|
|
|
|
|
|
if (data_len)
|
|
|
|
|
memcpy(frame->data, data, data_len);
|
|
|
|
|
|
2018-05-01 18:01:51 +02:00
|
|
|
|
__eapol_tx_packet(sm->ifindex, sm->aa, 0x88c7, frame, false);
|
2017-04-29 03:53:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-30 04:28:21 +02:00
|
|
|
|
static void preauth_rx_packet(uint16_t proto, const uint8_t *from,
|
|
|
|
|
const struct eapol_frame *frame,
|
|
|
|
|
void *user_data)
|
2017-04-29 03:53:26 +02:00
|
|
|
|
{
|
2017-09-30 04:28:21 +02:00
|
|
|
|
struct preauth_sm *sm = user_data;
|
|
|
|
|
|
|
|
|
|
if (proto != 0x88c7 || memcmp(from, sm->aa, 6))
|
|
|
|
|
return;
|
2017-04-29 03:53:26 +02:00
|
|
|
|
|
2017-09-30 04:28:21 +02:00
|
|
|
|
if (frame->header.packet_type != 0) /* EAPOL-EAP */
|
2017-04-29 03:53:26 +02:00
|
|
|
|
return;
|
|
|
|
|
|
2017-08-25 01:48:04 +02:00
|
|
|
|
if (!sm->initial_rx) {
|
|
|
|
|
sm->initial_rx = true;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Initial frame from authenticator received, it's alive
|
|
|
|
|
* so set a longer timeout. The timeout is for the whole
|
|
|
|
|
* EAP exchange as we have no way to monitor the
|
|
|
|
|
* negotiation progress and keep rearming the timer each
|
|
|
|
|
* time progress is made.
|
|
|
|
|
*/
|
|
|
|
|
l_timeout_modify(sm->timeout, EAPOL_TIMEOUT_SEC * 3);
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-30 04:28:21 +02:00
|
|
|
|
eap_rx_packet(sm->eap, frame->data,
|
|
|
|
|
L_BE16_TO_CPU(frame->header.packet_len));
|
2017-04-29 03:53:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void preauth_eap_msg_cb(const uint8_t *eap_data, size_t len,
|
|
|
|
|
void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct preauth_sm *sm = user_data;
|
|
|
|
|
|
|
|
|
|
preauth_frame(sm, 0, eap_data, len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void preauth_eap_complete_cb(enum eap_result result, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct preauth_sm *sm = user_data;
|
|
|
|
|
|
|
|
|
|
l_info("Preauthentication completed with %s",
|
|
|
|
|
result == EAP_RESULT_SUCCESS ? "eapSuccess" :
|
|
|
|
|
(result == EAP_RESULT_FAIL ? "eapFail" : "eapTimeout"));
|
|
|
|
|
|
|
|
|
|
l_queue_remove(preauths, sm);
|
|
|
|
|
|
|
|
|
|
if (result == EAP_RESULT_SUCCESS)
|
|
|
|
|
sm->cb(sm->pmk, sm->user_data);
|
|
|
|
|
else
|
|
|
|
|
sm->cb(NULL, sm->user_data);
|
|
|
|
|
|
|
|
|
|
preauth_sm_destroy(sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* See eapol_eap_results_cb for documentation */
|
|
|
|
|
static void preauth_eap_results_cb(const uint8_t *msk_data, size_t msk_len,
|
|
|
|
|
const uint8_t *emsk_data, size_t emsk_len,
|
|
|
|
|
const uint8_t *iv, size_t iv_len,
|
|
|
|
|
void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct preauth_sm *sm = user_data;
|
|
|
|
|
|
|
|
|
|
l_debug("Preauthentication EAP key material received");
|
|
|
|
|
|
|
|
|
|
if (msk_len < 32)
|
|
|
|
|
goto msk_short;
|
|
|
|
|
|
|
|
|
|
memcpy(sm->pmk, msk_data, 32);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
msk_short:
|
|
|
|
|
l_error("Preauthentication MSK too short");
|
|
|
|
|
|
|
|
|
|
l_queue_remove(preauths, sm);
|
|
|
|
|
|
|
|
|
|
sm->cb(NULL, sm->user_data);
|
|
|
|
|
|
|
|
|
|
preauth_sm_destroy(sm);
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-25 01:48:04 +02:00
|
|
|
|
static void preauth_timeout(struct l_timeout *timeout, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct preauth_sm *sm = user_data;
|
|
|
|
|
|
|
|
|
|
l_error("Preauthentication timeout");
|
|
|
|
|
|
|
|
|
|
l_queue_remove(preauths, sm);
|
|
|
|
|
|
|
|
|
|
sm->cb(NULL, sm->user_data);
|
|
|
|
|
|
|
|
|
|
preauth_sm_destroy(sm);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-29 03:53:26 +02:00
|
|
|
|
struct preauth_sm *eapol_preauth_start(const uint8_t *aa,
|
|
|
|
|
const struct handshake_state *hs,
|
|
|
|
|
eapol_preauth_cb_t cb, void *user_data,
|
|
|
|
|
eapol_preauth_destroy_func_t destroy)
|
|
|
|
|
{
|
|
|
|
|
struct preauth_sm *sm;
|
|
|
|
|
|
|
|
|
|
sm = l_new(struct preauth_sm, 1);
|
|
|
|
|
|
|
|
|
|
sm->ifindex = hs->ifindex;
|
|
|
|
|
memcpy(sm->aa, aa, 6);
|
|
|
|
|
memcpy(sm->spa, hs->spa, 6);
|
|
|
|
|
sm->cb = cb;
|
|
|
|
|
sm->destroy = destroy;
|
|
|
|
|
sm->user_data = user_data;
|
|
|
|
|
|
|
|
|
|
sm->eap = eap_new(preauth_eap_msg_cb, preauth_eap_complete_cb, sm);
|
|
|
|
|
if (!sm->eap)
|
|
|
|
|
goto err_free_sm;
|
|
|
|
|
|
|
|
|
|
if (!eap_load_settings(sm->eap, hs->settings_8021x, "EAP-"))
|
|
|
|
|
goto err_free_eap;
|
|
|
|
|
|
|
|
|
|
eap_set_key_material_func(sm->eap, preauth_eap_results_cb);
|
|
|
|
|
|
2017-08-25 01:48:04 +02:00
|
|
|
|
sm->timeout = l_timeout_create(EAPOL_TIMEOUT_SEC, preauth_timeout,
|
|
|
|
|
sm, NULL);
|
|
|
|
|
|
2017-09-30 04:28:21 +02:00
|
|
|
|
sm->watch_id = eapol_frame_watch_add(sm->ifindex,
|
|
|
|
|
preauth_rx_packet, sm);
|
|
|
|
|
|
2017-04-29 03:53:26 +02:00
|
|
|
|
l_queue_push_head(preauths, sm);
|
|
|
|
|
|
|
|
|
|
/* Send EAPOL-Start */
|
|
|
|
|
preauth_frame(sm, 1, NULL, 0);
|
|
|
|
|
|
|
|
|
|
return sm;
|
|
|
|
|
|
|
|
|
|
err_free_eap:
|
|
|
|
|
eap_free(sm->eap);
|
|
|
|
|
err_free_sm:
|
|
|
|
|
l_free(sm);
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool preauth_remove_by_ifindex(void *data, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct preauth_sm *sm = data;
|
|
|
|
|
|
|
|
|
|
if (sm->ifindex != L_PTR_TO_UINT(user_data))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
preauth_sm_destroy(sm);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void eapol_preauth_cancel(uint32_t ifindex)
|
|
|
|
|
{
|
|
|
|
|
l_queue_foreach_remove(preauths, preauth_remove_by_ifindex,
|
|
|
|
|
L_UINT_TO_PTR(ifindex));
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-07 23:04:45 +02:00
|
|
|
|
static bool eapol_frame_watch_match_ifindex(const void *a, const void *b)
|
|
|
|
|
{
|
|
|
|
|
struct eapol_frame_watch *efw =
|
|
|
|
|
container_of(a, struct eapol_frame_watch, super);
|
|
|
|
|
|
|
|
|
|
return efw->ifindex == L_PTR_TO_UINT(b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void __eapol_rx_packet(uint32_t ifindex, const uint8_t *src, uint16_t proto,
|
2018-05-01 18:01:51 +02:00
|
|
|
|
const uint8_t *frame, size_t len,
|
|
|
|
|
bool noencrypt)
|
2017-04-29 03:53:26 +02:00
|
|
|
|
{
|
|
|
|
|
const struct eapol_header *eh;
|
|
|
|
|
|
|
|
|
|
/* Validate Header */
|
|
|
|
|
if (len < sizeof(struct eapol_header))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
eh = (const struct eapol_header *) frame;
|
|
|
|
|
|
|
|
|
|
switch (eh->protocol_version) {
|
|
|
|
|
case EAPOL_PROTOCOL_VERSION_2001:
|
|
|
|
|
case EAPOL_PROTOCOL_VERSION_2004:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-30 04:28:20 +02:00
|
|
|
|
if (len < sizeof(struct eapol_header) + L_BE16_TO_CPU(eh->packet_len))
|
2017-04-29 03:53:26 +02:00
|
|
|
|
return;
|
|
|
|
|
|
2017-09-07 23:04:45 +02:00
|
|
|
|
WATCHLIST_NOTIFY_MATCHES(&frame_watches,
|
|
|
|
|
eapol_frame_watch_match_ifindex,
|
|
|
|
|
L_UINT_TO_PTR(ifindex),
|
|
|
|
|
eapol_frame_watch_func_t, proto, src,
|
|
|
|
|
(const struct eapol_frame *) eh);
|
2017-04-15 13:58:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-01 18:01:51 +02:00
|
|
|
|
void __eapol_tx_packet(uint32_t ifindex, const uint8_t *dst, uint16_t proto,
|
|
|
|
|
const struct eapol_frame *frame, bool noencrypt)
|
|
|
|
|
{
|
|
|
|
|
if (!tx_packet)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
tx_packet(ifindex, dst, proto, frame, noencrypt, tx_user_data);
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-02 20:47:43 +02:00
|
|
|
|
void __eapol_set_config(struct l_settings *config)
|
|
|
|
|
{
|
|
|
|
|
if (!l_settings_get_uint(config, "EAPoL",
|
|
|
|
|
"max_4way_handshake_time", &eapol_4way_handshake_time))
|
|
|
|
|
eapol_4way_handshake_time = 2;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-24 18:08:03 +01:00
|
|
|
|
bool eapol_init()
|
|
|
|
|
{
|
2015-03-20 02:55:25 +01:00
|
|
|
|
state_machines = l_queue_new();
|
2017-04-29 03:53:26 +02:00
|
|
|
|
preauths = l_queue_new();
|
2017-09-07 23:04:45 +02:00
|
|
|
|
watchlist_init(&frame_watches, &eapol_frame_watch_ops);
|
2015-02-24 18:08:03 +01:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool eapol_exit()
|
|
|
|
|
{
|
2016-07-20 22:34:21 +02:00
|
|
|
|
if (!l_queue_isempty(state_machines))
|
|
|
|
|
l_warn("stale eapol state machines found");
|
|
|
|
|
|
2015-03-20 02:55:25 +01:00
|
|
|
|
l_queue_destroy(state_machines, eapol_sm_destroy);
|
2015-02-24 18:08:03 +01:00
|
|
|
|
|
2017-04-29 03:53:26 +02:00
|
|
|
|
if (!l_queue_isempty(preauths))
|
|
|
|
|
l_warn("stale preauth state machines found");
|
|
|
|
|
|
|
|
|
|
l_queue_destroy(preauths, preauth_sm_destroy);
|
|
|
|
|
|
2017-09-07 23:04:45 +02:00
|
|
|
|
watchlist_destroy(&frame_watches);
|
|
|
|
|
|
2015-02-24 18:08:03 +01:00
|
|
|
|
return true;
|
|
|
|
|
}
|