aboutsummaryrefslogtreecommitdiff
path: root/eap_fast.c
diff options
context:
space:
mode:
authorUpstream <upstream-import@none>1970-01-12 13:46:40 +0000
committerUpstream <upstream-import@none>1970-01-12 13:46:40 +0000
commit10356433b643a2a5266f84a11ed4ac32d8c5c845 (patch)
tree36872c4afb99d760e598019d585519bbe19e0a66 /eap_fast.c
downloadwpa_supplicant-10356433b643a2a5266f84a11ed4ac32d8c5c845.tar.gz
external/wpa_supplicant 0.5.10upstream/0.5.10
Diffstat (limited to 'eap_fast.c')
-rw-r--r--eap_fast.c2091
1 files changed, 2091 insertions, 0 deletions
diff --git a/eap_fast.c b/eap_fast.c
new file mode 100644
index 0000000..d9347a9
--- /dev/null
+++ b/eap_fast.c
@@ -0,0 +1,2091 @@
+/*
+ * EAP peer method: EAP-FAST (draft-cam-winget-eap-fast-03.txt)
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "config_ssid.h"
+#include "tls.h"
+#include "eap_tlv.h"
+#include "sha1.h"
+#include "config.h"
+
+/* TODO:
+ * - encrypt PAC-Key in the PAC file
+ * - test session resumption and enable it if it interoperates
+ * - password change (pending mschapv2 packet; replay decrypted packet)
+ */
+
+#define EAP_FAST_VERSION 1
+#define EAP_FAST_KEY_LEN 64
+#define EAP_FAST_PAC_KEY_LEN 32
+#define EAP_FAST_SIMCK_LEN 40
+#define EAP_FAST_SKS_LEN 40
+
+#define TLS_EXT_PAC_OPAQUE 35
+
+static const char *pac_file_hdr =
+ "wpa_supplicant EAP-FAST PAC file - version 1";
+
+
+static void eap_fast_deinit(struct eap_sm *sm, void *priv);
+
+
+#define PAC_TYPE_PAC_KEY 1
+#define PAC_TYPE_PAC_OPAQUE 2
+#define PAC_TYPE_CRED_LIFETIME 3
+#define PAC_TYPE_A_ID 4
+#define PAC_TYPE_I_ID 5
+#define PAC_TYPE_SERVER_PROTECTED_DATA 6
+#define PAC_TYPE_A_ID_INFO 7
+#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8
+#define PAC_TYPE_PAC_INFO 9
+
+struct pac_tlv_hdr {
+ u16 type;
+ u16 len;
+};
+
+
+/* draft-cam-winget-eap-fast-provisioning-01.txt:
+ * 3.4 Key Derivations Used in the EAP-FAST Provisioning Exchange */
+struct eap_fast_key_block_provisioning {
+ /* Extra key material after TLS key_block */
+ u8 session_key_seed[EAP_FAST_SKS_LEN];
+ u8 server_challenge[16];
+ u8 client_challenge[16];
+};
+
+
+struct eap_fast_pac {
+ struct eap_fast_pac *next;
+
+ u8 pac_key[EAP_FAST_PAC_KEY_LEN];
+ u8 *pac_opaque;
+ size_t pac_opaque_len;
+ u8 *pac_info;
+ size_t pac_info_len;
+ u8 *a_id;
+ size_t a_id_len;
+ u8 *i_id;
+ size_t i_id_len;
+ u8 *a_id_info;
+ size_t a_id_info_len;
+};
+
+
+struct eap_fast_data {
+ struct eap_ssl_data ssl;
+
+ int fast_version;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+
+ struct eap_method_type phase2_type;
+ struct eap_method_type *phase2_types;
+ size_t num_phase2_types;
+ int resuming; /* starting a resumed session */
+ struct eap_fast_key_block_provisioning *key_block_p;
+ int provisioning_allowed; /* is PAC provisioning allowed */
+ int provisioning; /* doing PAC provisioning (not the normal auth) */
+
+ u8 key_data[EAP_FAST_KEY_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+ int success;
+
+ struct eap_fast_pac *pac;
+ struct eap_fast_pac *current_pac;
+
+ int tls_master_secret_set;
+
+ u8 simck[EAP_FAST_SIMCK_LEN];
+ int simck_idx;
+};
+
+
+static void eap_fast_free_pac(struct eap_fast_pac *pac)
+{
+ os_free(pac->pac_opaque);
+ os_free(pac->pac_info);
+ os_free(pac->a_id);
+ os_free(pac->i_id);
+ os_free(pac->a_id_info);
+ os_free(pac);
+}
+
+
+static struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_data *data,
+ const u8 *a_id, size_t a_id_len)
+{
+ struct eap_fast_pac *pac = data->pac;
+
+ while (pac) {
+ if (pac->a_id_len == a_id_len &&
+ os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
+ return pac;
+ }
+ pac = pac->next;
+ }
+ return NULL;
+}
+
+
+static int eap_fast_add_pac(struct eap_fast_data *data,
+ struct eap_fast_pac *entry)
+{
+ struct eap_fast_pac *pac, *prev;
+
+ if (entry == NULL || entry->a_id == NULL)
+ return -1;
+
+ /* Remove a possible old entry for the matching A-ID. */
+ pac = data->pac;
+ prev = NULL;
+ while (pac) {
+ if (pac->a_id_len == entry->a_id_len &&
+ os_memcmp(pac->a_id, entry->a_id, pac->a_id_len) == 0) {
+ if (prev == NULL) {
+ data->pac = pac->next;
+ } else {
+ prev->next = pac->next;
+ }
+ if (data->current_pac == pac)
+ data->current_pac = NULL;
+ eap_fast_free_pac(pac);
+ break;
+ }
+ prev = pac;
+ pac = pac->next;
+ }
+
+ /* Allocate a new entry and add it to the list of PACs. */
+ pac = os_zalloc(sizeof(*pac));
+ if (pac == NULL)
+ return -1;
+
+ os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
+ if (entry->pac_opaque) {
+ pac->pac_opaque = os_malloc(entry->pac_opaque_len);
+ if (pac->pac_opaque == NULL) {
+ eap_fast_free_pac(pac);
+ return -1;
+ }
+ os_memcpy(pac->pac_opaque, entry->pac_opaque,
+ entry->pac_opaque_len);
+ pac->pac_opaque_len = entry->pac_opaque_len;
+ }
+ if (entry->pac_info) {
+ pac->pac_info = os_malloc(entry->pac_info_len);
+ if (pac->pac_info == NULL) {
+ eap_fast_free_pac(pac);
+ return -1;
+ }
+ os_memcpy(pac->pac_info, entry->pac_info,
+ entry->pac_info_len);
+ pac->pac_info_len = entry->pac_info_len;
+ }
+ if (entry->a_id) {
+ pac->a_id = os_malloc(entry->a_id_len);
+ if (pac->a_id == NULL) {
+ eap_fast_free_pac(pac);
+ return -1;
+ }
+ os_memcpy(pac->a_id, entry->a_id,
+ entry->a_id_len);
+ pac->a_id_len = entry->a_id_len;
+ }
+ if (entry->i_id) {
+ pac->i_id = os_malloc(entry->i_id_len);
+ if (pac->i_id == NULL) {
+ eap_fast_free_pac(pac);
+ return -1;
+ }
+ os_memcpy(pac->i_id, entry->i_id,
+ entry->i_id_len);
+ pac->i_id_len = entry->i_id_len;
+ }
+ if (entry->a_id_info) {
+ pac->a_id_info = os_malloc(entry->a_id_info_len);
+ if (pac->a_id_info == NULL) {
+ eap_fast_free_pac(pac);
+ return -1;
+ }
+ os_memcpy(pac->a_id_info, entry->a_id_info,
+ entry->a_id_info_len);
+ pac->a_id_info_len = entry->a_id_info_len;
+ }
+ pac->next = data->pac;
+ data->pac = pac;
+ return 0;
+}
+
+
+struct eap_fast_read_ctx {
+ FILE *f;
+ const char *pos;
+ const char *end;
+};
+
+static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char *buf,
+ size_t buf_len)
+{
+ char *pos;
+
+ if (rc->f) {
+ if (fgets(buf, buf_len, rc->f) == NULL)
+ return -1;
+ } else {
+ const char *l_end;
+ size_t len;
+ if (rc->pos >= rc->end)
+ return -1;
+ l_end = rc->pos;
+ while (l_end < rc->end && *l_end != '\n')
+ l_end++;
+ len = l_end - rc->pos;
+ if (len >= buf_len)
+ len = buf_len - 1;
+ os_memcpy(buf, rc->pos, len);
+ buf[len] = '\0';
+ rc->pos = l_end + 1;
+ }
+
+ buf[buf_len - 1] = '\0';
+ pos = buf;
+ while (*pos != '\0') {
+ if (*pos == '\n' || *pos == '\r') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+
+ return 0;
+}
+
+
+static u8 * eap_fast_parse_hex(const char *value, size_t *len)
+{
+ int hlen;
+ u8 *buf;
+
+ if (value == NULL)
+ return NULL;
+ hlen = os_strlen(value);
+ if (hlen & 1)
+ return NULL;
+ *len = hlen / 2;
+ buf = os_malloc(*len);
+ if (buf == NULL)
+ return NULL;
+ if (hexstr2bin(value, buf, *len)) {
+ os_free(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+
+static int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_data *data,
+ const char *pac_file)
+{
+ struct eap_fast_read_ctx rc;
+ struct eap_fast_pac *pac = NULL;
+ int count = 0;
+ char *buf, *pos;
+ const int buf_len = 2048;
+ int ret = 0, line = 0;
+
+ if (pac_file == NULL)
+ return -1;
+
+ os_memset(&rc, 0, sizeof(rc));
+
+ if (os_strncmp(pac_file, "blob://", 7) == 0) {
+ const struct wpa_config_blob *blob;
+ blob = eap_get_config_blob(sm, pac_file + 7);
+ if (blob == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
+ "assume no PAC entries have been "
+ "provisioned", pac_file + 7);
+ return 0;
+ }
+ rc.pos = (char *) blob->data;
+ rc.end = (char *) blob->data + blob->len;
+ } else {
+ rc.f = fopen(pac_file, "r");
+ if (rc.f == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
+ "assume no PAC entries have been "
+ "provisioned", pac_file);
+ return 0;
+ }
+ }
+
+ buf = os_malloc(buf_len);
+ if (buf == NULL) {
+ return -1;
+ }
+
+ line++;
+ if (eap_fast_read_line(&rc, buf, buf_len) < 0 ||
+ os_strcmp(pac_file_hdr, buf) != 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Unrecognized header line in "
+ "PAC file '%s'", pac_file);
+ os_free(buf);
+ if (rc.f)
+ fclose(rc.f);
+ return -1;
+ }
+
+ while (eap_fast_read_line(&rc, buf, buf_len) == 0) {
+ line++;
+ pos = os_strchr(buf, '=');
+ if (pos) {
+ *pos++ = '\0';
+ }
+
+ if (os_strcmp(buf, "START") == 0) {
+ if (pac) {
+ wpa_printf(MSG_INFO, "EAP-FAST: START line "
+ "without END in '%s:%d'",
+ pac_file, line);
+ ret = -1;
+ break;
+ }
+ pac = os_zalloc(sizeof(*pac));
+ if (pac == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No memory for "
+ "PAC entry");
+ ret = -1;
+ break;
+ }
+ } else if (os_strcmp(buf, "END") == 0) {
+ if (pac == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: END line "
+ "without START in '%s:%d'",
+ pac_file, line);
+ ret = -1;
+ break;
+ }
+ pac->next = data->pac;
+ data->pac = pac;
+ pac = NULL;
+ count++;
+ } else if (pac && os_strcmp(buf, "PAC-Key") == 0) {
+ u8 *key;
+ size_t key_len;
+ key = eap_fast_parse_hex(pos, &key_len);
+ if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid "
+ "PAC-Key '%s:%d'", pac_file, line);
+ ret = -1;
+ os_free(key);
+ break;
+ }
+
+ os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
+ os_free(key);
+ } else if (pac && os_strcmp(buf, "PAC-Opaque") == 0) {
+ os_free(pac->pac_opaque);
+ pac->pac_opaque =
+ eap_fast_parse_hex(pos, &pac->pac_opaque_len);
+ if (pac->pac_opaque == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid "
+ "PAC-Opaque '%s:%d'",
+ pac_file, line);
+ ret = -1;
+ break;
+ }
+ } else if (pac && os_strcmp(buf, "A-ID") == 0) {
+ os_free(pac->a_id);
+ pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
+ if (pac->a_id == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid "
+ "A-ID '%s:%d'", pac_file, line);
+ ret = -1;
+ break;
+ }
+ } else if (pac && os_strcmp(buf, "I-ID") == 0) {
+ os_free(pac->i_id);
+ pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
+ if (pac->i_id == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid "
+ "I-ID '%s:%d'", pac_file, line);
+ ret = -1;
+ break;
+ }
+ } else if (pac && os_strcmp(buf, "A-ID-Info") == 0) {
+ os_free(pac->a_id_info);
+ pac->a_id_info =
+ eap_fast_parse_hex(pos, &pac->a_id_info_len);
+ if (pac->a_id_info == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid "
+ "A-ID-Info '%s:%d'",
+ pac_file, line);
+ ret = -1;
+ break;
+ }
+ }
+ }
+
+ if (pac) {
+ wpa_printf(MSG_INFO, "EAP-FAST: PAC block not terminated with "
+ "END in '%s'", pac_file);
+ eap_fast_free_pac(pac);
+ ret = -1;
+ }
+
+ os_free(buf);
+ if (rc.f)
+ fclose(rc.f);
+
+ if (ret == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: read %d PAC entries from "
+ "'%s'", count, pac_file);
+ }
+
+ return ret;
+}
+
+
+static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
+ const char *field, const u8 *data,
+ size_t len, int txt)
+{
+ size_t i, need;
+ int ret;
+
+ if (data == NULL || *buf == NULL)
+ return;
+
+ need = os_strlen(field) + len * 2 + 30;
+ if (txt)
+ need += os_strlen(field) + len + 20;
+
+ if (*pos - *buf + need > *buf_len) {
+ char *nbuf = os_realloc(*buf, *buf_len + need);
+ if (nbuf == NULL) {
+ os_free(*buf);
+ *buf = NULL;
+ return;
+ }
+ *buf = nbuf;
+ *buf_len += need;
+ }
+
+ ret = os_snprintf(*pos, *buf + *buf_len - *pos, "%s=", field);
+ if (ret < 0 || ret >= *buf + *buf_len - *pos)
+ return;
+ *pos += ret;
+ *pos += wpa_snprintf_hex(*pos, *buf + *buf_len - *pos, data, len);
+ ret = os_snprintf(*pos, *buf + *buf_len - *pos, "\n");
+ if (ret < 0 || ret >= *buf + *buf_len - *pos)
+ return;
+ *pos += ret;
+
+ if (txt) {
+ ret = os_snprintf(*pos, *buf + *buf_len - *pos,
+ "%s-txt=", field);
+ if (ret < 0 || ret >= *buf + *buf_len - *pos)
+ return;
+ *pos += ret;
+ for (i = 0; i < len; i++) {
+ ret = os_snprintf(*pos, *buf + *buf_len - *pos,
+ "%c", data[i]);
+ if (ret < 0 || ret >= *buf + *buf_len - *pos)
+ return;
+ *pos += ret;
+ }
+ ret = os_snprintf(*pos, *buf + *buf_len - *pos, "\n");
+ if (ret < 0 || ret >= *buf + *buf_len - *pos)
+ return;
+ *pos += ret;
+ }
+}
+
+
+static int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_data *data,
+ const char *pac_file)
+{
+ FILE *f;
+ struct eap_fast_pac *pac;
+ int count = 0, ret;
+ char *buf, *pos;
+ size_t buf_len;
+
+ if (pac_file == NULL)
+ return -1;
+
+ buf_len = 1024;
+ pos = buf = os_malloc(buf_len);
+ if (buf == NULL)
+ return -1;
+
+ ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
+ if (ret < 0 || ret >= buf + buf_len - pos) {
+ os_free(buf);
+ return -1;
+ }
+ pos += ret;
+
+ pac = data->pac;
+ while (pac) {
+ ret = os_snprintf(pos, buf + buf_len - pos, "START\n");
+ if (ret < 0 || ret >= buf + buf_len - pos) {
+ os_free(buf);
+ return -1;
+ }
+ pos += ret;
+ eap_fast_write(&buf, &pos, &buf_len, "PAC-Key", pac->pac_key,
+ EAP_FAST_PAC_KEY_LEN, 0);
+ eap_fast_write(&buf, &pos, &buf_len, "PAC-Opaque",
+ pac->pac_opaque, pac->pac_opaque_len, 0);
+ eap_fast_write(&buf, &pos, &buf_len, "PAC-Info", pac->pac_info,
+ pac->pac_info_len, 0);
+ eap_fast_write(&buf, &pos, &buf_len, "A-ID", pac->a_id,
+ pac->a_id_len, 0);
+ eap_fast_write(&buf, &pos, &buf_len, "I-ID", pac->i_id,
+ pac->i_id_len, 1);
+ eap_fast_write(&buf, &pos, &buf_len, "A-ID-Info",
+ pac->a_id_info, pac->a_id_info_len, 1);
+ if (buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
+ "data");
+ return -1;
+ }
+ ret = os_snprintf(pos, buf + buf_len - pos, "END\n");
+ if (ret < 0 || ret >= buf + buf_len - pos) {
+ os_free(buf);
+ return -1;
+ }
+ pos += ret;
+ count++;
+ pac = pac->next;
+ }
+
+ if (os_strncmp(pac_file, "blob://", 7) == 0) {
+ struct wpa_config_blob *blob;
+ blob = os_zalloc(sizeof(*blob));
+ if (blob == NULL) {
+ os_free(buf);
+ return -1;
+ }
+ blob->data = (u8 *) buf;
+ blob->len = pos - buf;
+ buf = NULL;
+ blob->name = os_strdup(pac_file + 7);
+ if (blob->name == NULL) {
+ os_free(blob->data);
+ os_free(blob);
+ return -1;
+ }
+ eap_set_config_blob(sm, blob);
+ } else {
+ f = fopen(pac_file, "w");
+ if (f == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC "
+ "file '%s' for writing", pac_file);
+ os_free(buf);
+ return -1;
+ }
+ fprintf(f, "%s", buf);
+ os_free(buf);
+ fclose(f);
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: wrote %d PAC entries into '%s'",
+ count, pac_file);
+
+ return 0;
+}
+
+
+static void * eap_fast_init(struct eap_sm *sm)
+{
+ struct eap_fast_data *data;
+ struct wpa_ssid *config = eap_get_config(sm);
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->fast_version = EAP_FAST_VERSION;
+
+ if (config && config->phase1) {
+ if (os_strstr(config->phase1, "fast_provisioning=1")) {
+ data->provisioning_allowed = 1;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Automatic PAC "
+ "provisioning is allowed");
+ }
+ }
+
+ if (config && config->phase2) {
+ char *start, *pos, *buf;
+ struct eap_method_type *methods = NULL, *_methods;
+ u8 method;
+ size_t num_methods = 0;
+ start = buf = os_strdup(config->phase2);
+ if (buf == NULL) {
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+ while (start && *start != '\0') {
+ int vendor;
+ pos = os_strstr(start, "auth=");
+ if (pos == NULL)
+ break;
+ if (start != pos && *(pos - 1) != ' ') {
+ start = pos + 5;
+ continue;
+ }
+
+ start = pos + 5;
+ pos = os_strchr(start, ' ');
+ if (pos)
+ *pos++ = '\0';
+ method = eap_get_phase2_type(start, &vendor);
+ if (vendor == EAP_VENDOR_IETF &&
+ method == EAP_TYPE_NONE) {
+ wpa_printf(MSG_ERROR, "EAP-FAST: Unsupported "
+ "Phase2 method '%s'", start);
+ } else {
+ num_methods++;
+ _methods = os_realloc(
+ methods,
+ num_methods * sizeof(*methods));
+ if (_methods == NULL) {
+ os_free(methods);
+ os_free(buf);
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+ methods = _methods;
+ methods[num_methods - 1].vendor = vendor;
+ methods[num_methods - 1].method = method;
+ }
+
+ start = pos;
+ }
+ os_free(buf);
+ data->phase2_types = methods;
+ data->num_phase2_types = num_methods;
+ }
+ if (data->phase2_types == NULL) {
+ data->phase2_types =
+ eap_get_phase2_types(config, &data->num_phase2_types);
+ }
+ if (data->phase2_types == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-FAST: No Phase2 method available");
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: Phase2 EAP types",
+ (u8 *) data->phase2_types,
+ data->num_phase2_types * sizeof(struct eap_method_type));
+ data->phase2_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_type.method = EAP_TYPE_NONE;
+
+ if (eap_tls_ssl_init(sm, &data->ssl, config)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ /* The local RADIUS server in a Cisco AP does not seem to like empty
+ * fragments before data, so disable that workaround for CBC.
+ * TODO: consider making this configurable */
+ tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn);
+
+ if (eap_fast_load_pac(sm, data, config->pac_file) < 0) {
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ if (data->pac == NULL && !data->provisioning_allowed) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No PAC configured and "
+ "provisioning disabled");
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_fast_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ struct eap_fast_pac *pac, *prev;
+
+ if (data == NULL)
+ return;
+ if (data->phase2_priv && data->phase2_method)
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ os_free(data->phase2_types);
+ os_free(data->key_block_p);
+ eap_tls_ssl_deinit(sm, &data->ssl);
+
+ pac = data->pac;
+ prev = NULL;
+ while (pac) {
+ prev = pac;
+ pac = pac->next;
+ eap_fast_free_pac(prev);
+ }
+ os_free(data);
+}
+
+
+static int eap_fast_encrypt(struct eap_sm *sm, struct eap_fast_data *data,
+ int id, const u8 *plain, size_t plain_len,
+ u8 **out_data, size_t *out_len)
+{
+ int res;
+ u8 *pos;
+ struct eap_hdr *resp;
+
+ /* TODO: add support for fragmentation, if needed. This will need to
+ * add TLS Message Length field, if the frame is fragmented. */
+ resp = os_malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit);
+ if (resp == NULL)
+ return 0;
+
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = id;
+
+ pos = (u8 *) (resp + 1);
+ *pos++ = EAP_TYPE_FAST;
+ *pos++ = data->fast_version;
+
+ res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn,
+ plain, plain_len,
+ pos, data->ssl.tls_out_limit);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt Phase 2 "
+ "data");
+ os_free(resp);
+ return 0;
+ }
+
+ *out_len = sizeof(struct eap_hdr) + 2 + res;
+ resp->length = host_to_be16(*out_len);
+ *out_data = (u8 *) resp;
+ return 0;
+}
+
+
+static int eap_fast_phase2_nak(struct eap_fast_data *data,
+ struct eap_hdr *hdr,
+ u8 **resp, size_t *resp_len)
+{
+ struct eap_hdr *resp_hdr;
+ u8 *pos = (u8 *) (hdr + 1);
+ size_t i;
+
+ /* TODO: add support for expanded Nak */
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: Nak type=%d", *pos);
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: Allowed Phase2 EAP types",
+ (u8 *) data->phase2_types,
+ data->num_phase2_types * sizeof(struct eap_method_type));
+ *resp_len = sizeof(struct eap_hdr) + 1;
+ *resp = os_malloc(*resp_len + data->num_phase2_types);
+ if (*resp == NULL)
+ return -1;
+
+ resp_hdr = (struct eap_hdr *) (*resp);
+ resp_hdr->code = EAP_CODE_RESPONSE;
+ resp_hdr->identifier = hdr->identifier;
+ pos = (u8 *) (resp_hdr + 1);
+ *pos++ = EAP_TYPE_NAK;
+ for (i = 0; i < data->num_phase2_types; i++) {
+ if (data->phase2_types[i].vendor == EAP_VENDOR_IETF &&
+ data->phase2_types[i].method < 256) {
+ (*resp_len)++;
+ *pos++ = data->phase2_types[i].method;
+ }
+ }
+ resp_hdr->length = host_to_be16(*resp_len);
+
+ return 0;
+}
+
+
+static int eap_fast_derive_msk(struct eap_fast_data *data)
+{
+ /* Derive EAP Master Session Keys (section 5.4) */
+ sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
+ "Session Key Generating Function", (u8 *) "", 0,
+ data->key_data, EAP_FAST_KEY_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)",
+ data->key_data, EAP_FAST_KEY_LEN);
+
+ sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
+ "Extended Session Key Generating Function",
+ (u8 *) "", 0, data->emsk, EAP_EMSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)",
+ data->emsk, EAP_EMSK_LEN);
+
+ data->success = 1;
+
+ return 0;
+}
+
+
+static int eap_fast_set_tls_master_secret(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ const u8 *tls, size_t tls_len)
+{
+ struct tls_keys keys;
+ u8 master_secret[48], *seed;
+ const u8 *server_random;
+ size_t seed_len, server_random_len;
+
+ if (data->tls_master_secret_set || !data->current_pac ||
+ tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
+ keys.client_random == NULL) {
+ return 0;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random",
+ keys.client_random, keys.client_random_len);
+
+ /* TLS master secret is needed before TLS library has processed this
+ * message which includes both ServerHello and an encrypted handshake
+ * message, so we need to parse server_random from this message before
+ * passing it to TLS library.
+ *
+ * Example TLS packet header:
+ * (16 03 01 00 2a 02 00 00 26 03 01 <32 bytes server_random>)
+ * Content Type: Handshake: 0x16
+ * Version: TLS 1.0 (0x0301)
+ * Lenghth: 42 (0x002a)
+ * Handshake Type: Server Hello: 0x02
+ * Length: 38 (0x000026)
+ * Version TLS 1.0 (0x0301)
+ * Random: 32 bytes
+ */
+ if (tls_len < 43 || tls[0] != 0x16 ||
+ tls[1] != 0x03 || tls[2] != 0x01 ||
+ tls[5] != 0x02 || tls[9] != 0x03 || tls[10] != 0x01) {
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: unrecognized TLS "
+ "ServerHello", tls, tls_len);
+ return -1;
+ }
+ server_random = tls + 11;
+ server_random_len = 32;
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random",
+ server_random, server_random_len);
+
+ seed_len = keys.client_random_len + server_random_len;
+ seed = os_malloc(seed_len);
+ if (seed == NULL)
+ return -1;
+ os_memcpy(seed, server_random, server_random_len);
+ os_memcpy(seed + server_random_len,
+ keys.client_random, keys.client_random_len);
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: T-PRF seed", seed, seed_len);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: PAC-Key",
+ data->current_pac->pac_key, EAP_FAST_PAC_KEY_LEN);
+ /* master_secret = T-PRF(PAC-Key, "PAC to master secret label hash",
+ * server_random + client_random, 48) */
+ sha1_t_prf(data->current_pac->pac_key, EAP_FAST_PAC_KEY_LEN,
+ "PAC to master secret label hash",
+ seed, seed_len, master_secret, sizeof(master_secret));
+ os_free(seed);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: TLS pre-master-secret",
+ master_secret, sizeof(master_secret));
+
+ data->tls_master_secret_set = 1;
+
+ return tls_connection_set_master_key(sm->ssl_ctx, data->ssl.conn,
+ master_secret,
+ sizeof(master_secret));
+}
+
+
+static u8 * eap_fast_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+ char *label, size_t len)
+{
+ struct tls_keys keys;
+ u8 *rnd = NULL, *out;
+ int block_size;
+
+ block_size = tls_connection_get_keyblock_size(sm->ssl_ctx, data->conn);
+ if (block_size < 0)
+ return NULL;
+
+ out = os_malloc(block_size + len);
+ if (out == NULL)
+ return NULL;
+
+ if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 1, out,
+ block_size + len) == 0) {
+ os_memmove(out, out + block_size, len);
+ return out;
+ }
+
+ if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+ goto fail;
+
+ rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+ if (rnd == NULL)
+ goto fail;
+
+ os_memcpy(rnd, keys.server_random, keys.server_random_len);
+ os_memcpy(rnd + keys.server_random_len, keys.client_random,
+ keys.client_random_len);
+
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key "
+ "expansion", keys.master_key, keys.master_key_len);
+ if (tls_prf(keys.master_key, keys.master_key_len,
+ label, rnd, keys.client_random_len +
+ keys.server_random_len, out, block_size + len))
+ goto fail;
+ os_free(rnd);
+ os_memmove(out, out + block_size, len);
+ return out;
+
+fail:
+ os_free(rnd);
+ os_free(out);
+ return NULL;
+}
+
+
+static void eap_fast_derive_key_auth(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ u8 *sks;
+
+ /* draft-cam-winget-eap-fast-05.txt:
+ * 5.1 EAP-FAST Authentication Phase 1: Key Derivations
+ * Extra key material after TLS key_block: session_ket_seed[40]
+ */
+
+ sks = eap_fast_derive_key(sm, &data->ssl, "key expansion",
+ EAP_FAST_SKS_LEN);
+ if (sks == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
+ "session_key_seed");
+ return;
+ }
+
+ /*
+ * draft-cam-winget-eap-fast-05.txt, 5.2:
+ * S-IMCK[0] = session_key_seed
+ */
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+ sks, EAP_FAST_SKS_LEN);
+ data->simck_idx = 0;
+ os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN);
+ os_free(sks);
+}
+
+
+static void eap_fast_derive_key_provisioning(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ os_free(data->key_block_p);
+ data->key_block_p = (struct eap_fast_key_block_provisioning *)
+ eap_fast_derive_key(sm, &data->ssl, "key expansion",
+ sizeof(*data->key_block_p));
+ if (data->key_block_p == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
+ return;
+ }
+ /*
+ * draft-cam-winget-eap-fast-05.txt, 5.2:
+ * S-IMCK[0] = session_key_seed
+ */
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+ data->key_block_p->session_key_seed,
+ sizeof(data->key_block_p->session_key_seed));
+ data->simck_idx = 0;
+ os_memcpy(data->simck, data->key_block_p->session_key_seed,
+ EAP_FAST_SIMCK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge",
+ data->key_block_p->server_challenge,
+ sizeof(data->key_block_p->server_challenge));
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge",
+ data->key_block_p->client_challenge,
+ sizeof(data->key_block_p->client_challenge));
+}
+
+
+static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data)
+{
+ if (data->current_pac) {
+ eap_fast_derive_key_auth(sm, data);
+ } else {
+ eap_fast_derive_key_provisioning(sm, data);
+ }
+}
+
+
+static int eap_fast_phase2_request(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr,
+ u8 **resp, size_t *resp_len)
+{
+ size_t len = be_to_host16(hdr->length);
+ u8 *pos;
+ struct eap_method_ret iret;
+
+ if (len <= sizeof(struct eap_hdr)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: too short "
+ "Phase 2 request (len=%lu)", (unsigned long) len);
+ return -1;
+ }
+ pos = (u8 *) (hdr + 1);
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%d", *pos);
+ switch (*pos) {
+ case EAP_TYPE_IDENTITY:
+ *resp = eap_sm_buildIdentity(sm, hdr->identifier, resp_len, 1);
+ break;
+ default:
+ if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
+ data->phase2_type.method == EAP_TYPE_NONE) {
+ size_t i;
+ for (i = 0; i < data->num_phase2_types; i++) {
+ if (data->phase2_types[i].vendor !=
+ EAP_VENDOR_IETF ||
+ data->phase2_types[i].method != *pos)
+ continue;
+
+ data->phase2_type.vendor =
+ data->phase2_types[i].vendor;
+ data->phase2_type.method =
+ data->phase2_types[i].method;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Selected "
+ "Phase 2 EAP vendor %d method %d",
+ data->phase2_type.vendor,
+ data->phase2_type.method);
+ break;
+ }
+ }
+ if (*pos != data->phase2_type.method ||
+ *pos == EAP_TYPE_NONE) {
+ if (eap_fast_phase2_nak(data, hdr, resp, resp_len))
+ return -1;
+ return 0;
+ }
+
+ if (data->phase2_priv == NULL) {
+ data->phase2_method = eap_sm_get_eap_methods(
+ data->phase2_type.vendor,
+ data->phase2_type.method);
+ if (data->phase2_method) {
+ if (data->key_block_p) {
+ sm->auth_challenge =
+ data->key_block_p->
+ server_challenge;
+ sm->peer_challenge =
+ data->key_block_p->
+ client_challenge;
+ }
+ sm->init_phase2 = 1;
+ sm->mschapv2_full_key = 1;
+ data->phase2_priv =
+ data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ sm->mschapv2_full_key = 0;
+ sm->auth_challenge = NULL;
+ sm->peer_challenge = NULL;
+ }
+ }
+ if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: failed to initialize "
+ "Phase 2 EAP method %d", *pos);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+ os_memset(&iret, 0, sizeof(iret));
+ *resp = data->phase2_method->process(sm, data->phase2_priv,
+ &iret, (u8 *) hdr, len,
+ resp_len);
+ if (*resp == NULL ||
+ (iret.methodState == METHOD_DONE &&
+ iret.decision == DECISION_FAIL)) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ } else if ((iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) &&
+ (iret.decision == DECISION_UNCOND_SUCC ||
+ iret.decision == DECISION_COND_SUCC)) {
+ data->phase2_success = 1;
+ }
+ if (*resp == NULL)
+ return -1;
+ break;
+ }
+ return 0;
+}
+
+
+static u8 * eap_fast_tlv_nak(int vendor_id, int tlv_type, size_t *len)
+{
+ struct eap_tlv_nak_tlv *nak;
+ *len = sizeof(*nak);
+ nak = os_malloc(*len);
+ if (nak == NULL)
+ return NULL;
+ nak->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_NAK_TLV);
+ nak->length = host_to_be16(6);
+ nak->vendor_id = host_to_be32(vendor_id);
+ nak->nak_type = host_to_be16(tlv_type);
+ return (u8 *) nak;
+}
+
+
+static u8 * eap_fast_tlv_result(int status, int intermediate, size_t *len)
+{
+ struct eap_tlv_intermediate_result_tlv *result;
+ *len = sizeof(*result);
+ result = os_malloc(*len);
+ if (result == NULL)
+ return NULL;
+ result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ (intermediate ?
+ EAP_TLV_INTERMEDIATE_RESULT_TLV :
+ EAP_TLV_RESULT_TLV));
+ result->length = host_to_be16(2);
+ result->status = host_to_be16(status);
+ return (u8 *) result;
+}
+
+
+static u8 * eap_fast_tlv_pac_ack(size_t *len)
+{
+ struct eap_tlv_result_tlv *res;
+ struct eap_tlv_pac_ack_tlv *ack;
+
+ *len = sizeof(*res) + sizeof(*ack);
+ res = os_zalloc(*len);
+ if (res == NULL)
+ return NULL;
+
+ res->tlv_type = host_to_be16(EAP_TLV_RESULT_TLV |
+ EAP_TLV_TYPE_MANDATORY);
+ res->length = host_to_be16(sizeof(*res) - sizeof(struct eap_tlv_hdr));
+ res->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+
+ ack = (struct eap_tlv_pac_ack_tlv *) (res + 1);
+ ack->tlv_type = host_to_be16(EAP_TLV_PAC_TLV |
+ EAP_TLV_TYPE_MANDATORY);
+ ack->length = host_to_be16(sizeof(*ack) - sizeof(struct eap_tlv_hdr));
+ ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT);
+ ack->pac_len = host_to_be16(2);
+ ack->result = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+
+ return (u8 *) res;
+}
+
+
+static u8 * eap_fast_tlv_eap_payload(u8 *buf, size_t *len)
+{
+ struct eap_tlv_hdr *tlv;
+
+ /* Encapsulate EAP packet in EAP Payload TLV */
+ tlv = os_malloc(sizeof(*tlv) + *len);
+ if (tlv == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to "
+ "allocate memory for TLV "
+ "encapsulation");
+ os_free(buf);
+ return NULL;
+ }
+ tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ EAP_TLV_EAP_PAYLOAD_TLV);
+ tlv->length = host_to_be16(*len);
+ os_memcpy(tlv + 1, buf, *len);
+ os_free(buf);
+ *len += sizeof(*tlv);
+ return (u8 *) tlv;
+}
+
+
+static u8 * eap_fast_process_crypto_binding(
+ struct eap_sm *sm, struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ struct eap_tlv_crypto_binding__tlv *_bind, size_t bind_len,
+ size_t *resp_len, int final)
+{
+ u8 *resp;
+ struct eap_tlv_intermediate_result_tlv *rresult;
+ struct eap_tlv_crypto_binding__tlv *rbind;
+ u8 isk[32], imck[60], *cmk, cmac[20], *key;
+ size_t key_len;
+ int res;
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV: Version %d "
+ "Received Version %d SubType %d",
+ _bind->version, _bind->received_version, _bind->subtype);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+ _bind->nonce, sizeof(_bind->nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+ _bind->compound_mac, sizeof(_bind->compound_mac));
+
+ if (_bind->version != EAP_FAST_VERSION ||
+ _bind->received_version != EAP_FAST_VERSION ||
+ _bind->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid version/subtype in "
+ "Crypto-Binding TLV: Version %d "
+ "Received Version %d SubType %d",
+ _bind->version, _bind->received_version,
+ _bind->subtype);
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1,
+ resp_len);
+ return resp;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Determining CMK[%d] for Compound MIC "
+ "calculation", data->simck_idx + 1);
+
+ /*
+ * draft-cam-winget-eap-fast-05.txt, 5.2:
+ * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys",
+ * MSK[j], 60)
+ * S-IMCK[j] = first 40 octets of IMCK[j]
+ * CMK[j] = last 20 octets of IMCK[j]
+ */
+
+ os_memset(isk, 0, sizeof(isk));
+ if (data->phase2_method == NULL || data->phase2_priv == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not "
+ "available");
+ return NULL;
+ }
+ if (data->phase2_method->isKeyAvailable && data->phase2_method->getKey)
+ {
+ if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv)
+ ||
+ (key = data->phase2_method->getKey(sm, data->phase2_priv,
+ &key_len)) == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key "
+ "material from Phase 2");
+ return NULL;
+ }
+ if (key_len > sizeof(isk))
+ key_len = sizeof(isk);
+ os_memcpy(isk, key, key_len);
+ os_free(key);
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk));
+ sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
+ "Inner Methods Compound Keys",
+ isk, sizeof(isk), imck, sizeof(imck));
+ data->simck_idx++;
+ os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]",
+ data->simck, EAP_FAST_SIMCK_LEN);
+ cmk = imck + EAP_FAST_SIMCK_LEN;
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]", cmk, 20);
+
+ os_memcpy(cmac, _bind->compound_mac, sizeof(cmac));
+ os_memset(_bind->compound_mac, 0, sizeof(cmac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for Compound "
+ "MAC calculation", (u8 *) _bind, bind_len);
+ hmac_sha1(cmk, 20, (u8 *) _bind, bind_len, _bind->compound_mac);
+ res = os_memcmp(cmac, _bind->compound_mac, sizeof(cmac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Received Compound MAC",
+ cmac, sizeof(cmac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC",
+ _bind->compound_mac, sizeof(cmac));
+ if (res != 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match");
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1,
+ resp_len);
+ os_memcpy(_bind->compound_mac, cmac, sizeof(cmac));
+ return resp;
+ }
+
+ *resp_len = sizeof(*rresult) + sizeof(*rbind);
+ resp = os_zalloc(*resp_len);
+ if (resp == NULL)
+ return NULL;
+
+ /* Both intermediate and final Result TLVs are identical, so ok to use
+ * the same structure definition for them. */
+ rresult = (struct eap_tlv_intermediate_result_tlv *) resp;
+ rresult->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ (final ? EAP_TLV_RESULT_TLV :
+ EAP_TLV_INTERMEDIATE_RESULT_TLV));
+ rresult->length = host_to_be16(2);
+ rresult->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+
+ if (!data->provisioning && data->phase2_success &&
+ eap_fast_derive_msk(data) < 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ rresult->status = host_to_be16(EAP_TLV_RESULT_FAILURE);
+ data->phase2_success = 0;
+ }
+
+ rbind = (struct eap_tlv_crypto_binding__tlv *) (rresult + 1);
+ rbind->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ EAP_TLV_CRYPTO_BINDING_TLV_);
+ rbind->length = host_to_be16(sizeof(*rbind) -
+ sizeof(struct eap_tlv_hdr));
+ rbind->version = EAP_FAST_VERSION;
+ rbind->received_version = _bind->version;
+ rbind->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE;
+ os_memcpy(rbind->nonce, _bind->nonce, sizeof(_bind->nonce));
+ inc_byte_array(rbind->nonce, sizeof(rbind->nonce));
+ hmac_sha1(cmk, 20, (u8 *) rbind, sizeof(*rbind), rbind->compound_mac);
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: Version %d "
+ "Received Version %d SubType %d",
+ rbind->version, rbind->received_version, rbind->subtype);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+ rbind->nonce, sizeof(rbind->nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+ rbind->compound_mac, sizeof(rbind->compound_mac));
+
+ if (final && data->phase2_success) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication completed "
+ "successfully.");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
+
+ return resp;
+}
+
+
+static u8 * eap_fast_process_pac(struct eap_sm *sm, struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ u8 *pac, size_t pac_len, size_t *resp_len)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct pac_tlv_hdr *hdr;
+ u8 *pos;
+ size_t left, len;
+ int type, pac_key_found = 0;
+ struct eap_fast_pac entry;
+
+ os_memset(&entry, 0, sizeof(entry));
+ pos = pac;
+ left = pac_len;
+ while (left > sizeof(*hdr)) {
+ hdr = (struct pac_tlv_hdr *) pos;
+ type = be_to_host16(hdr->type);
+ len = be_to_host16(hdr->len);
+ pos += sizeof(*hdr);
+ left -= sizeof(*hdr);
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV overrun "
+ "(type=%d len=%lu left=%lu)",
+ type, (unsigned long) len,
+ (unsigned long) left);
+ return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0,
+ resp_len);
+ }
+ switch (type) {
+ case PAC_TYPE_PAC_KEY:
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key",
+ pos, len);
+ if (len != EAP_FAST_PAC_KEY_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
+ "PAC-Key length %lu",
+ (unsigned long) len);
+ break;
+ }
+ pac_key_found = 1;
+ os_memcpy(entry.pac_key, pos, len);
+ break;
+ case PAC_TYPE_PAC_OPAQUE:
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque",
+ pos, len);
+ entry.pac_opaque = pos;
+ entry.pac_opaque_len = len;
+ break;
+ case PAC_TYPE_PAC_INFO:
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info",
+ pos, len);
+ entry.pac_info = pos;
+ entry.pac_info_len = len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC "
+ "type %d", type);
+ break;
+ }
+
+ pos += len;
+ left -= len;
+ }
+
+ if (!pac_key_found || !entry.pac_opaque || !entry.pac_info) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV does not include "
+ "all the required fields");
+ return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0,
+ resp_len);
+ }
+
+ pos = entry.pac_info;
+ left = entry.pac_info_len;
+ while (left > sizeof(*hdr)) {
+ hdr = (struct pac_tlv_hdr *) pos;
+ type = be_to_host16(hdr->type);
+ len = be_to_host16(hdr->len);
+ pos += sizeof(*hdr);
+ left -= sizeof(*hdr);
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info overrun "
+ "(type=%d len=%lu left=%lu)",
+ type, (unsigned long) len,
+ (unsigned long) left);
+ return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0,
+ resp_len);
+ }
+ switch (type) {
+ case PAC_TYPE_A_ID:
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - "
+ "A-ID", pos, len);
+ entry.a_id = pos;
+ entry.a_id_len = len;
+ break;
+ case PAC_TYPE_I_ID:
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - "
+ "I-ID", pos, len);
+ entry.i_id = pos;
+ entry.i_id_len = len;
+ break;
+ case PAC_TYPE_A_ID_INFO:
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - "
+ "A-ID-Info", pos, len);
+ entry.a_id_info = pos;
+ entry.a_id_info_len = len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown "
+ "PAC-Info type %d", type);
+ break;
+ }
+
+ pos += len;
+ left -= len;
+ }
+
+ if (entry.a_id == NULL || entry.a_id_info == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info does not include "
+ "all the required fields");
+ return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0,
+ resp_len);
+ }
+
+ eap_fast_add_pac(data, &entry);
+ eap_fast_save_pac(sm, data, config->pac_file);
+
+ if (data->provisioning) {
+ /* EAP-FAST provisioning does not provide keying material and
+ * must end with an EAP-Failure. Authentication will be done
+ * separately after this. */
+ data->success = 0;
+ ret->decision = DECISION_FAIL;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
+ "- Provisioning completed successfully");
+ } else {
+ /* This is PAC refreshing, i.e., normal authentication that is
+ * expected to be completed with an EAP-Success. */
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
+ "- PAC refreshing completed successfully");
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
+ ret->methodState = METHOD_DONE;
+ return eap_fast_tlv_pac_ack(resp_len);
+}
+
+
+static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ const struct eap_hdr *req,
+ const u8 *in_data, size_t in_len,
+ u8 **out_data, size_t *out_len)
+{
+ u8 *in_decrypted, *pos, *end;
+ int len_decrypted, len;
+ struct eap_hdr *hdr;
+ u8 *resp = NULL;
+ size_t buf_len, resp_len;
+ int mandatory, tlv_type;
+ u8 *eap_payload_tlv = NULL, *pac = NULL;
+ size_t eap_payload_tlv_len = 0, pac_len = 0;
+ int iresult = 0, result = 0;
+ struct eap_tlv_crypto_binding__tlv *crypto_binding = NULL;
+ size_t crypto_binding_len = 0;
+ const u8 *msg;
+ size_t msg_len;
+ int need_more_input, stop;
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: received %lu bytes encrypted data for"
+ " Phase 2", (unsigned long) in_len);
+
+ msg = eap_tls_data_reassemble(sm, &data->ssl, in_data, in_len,
+ &msg_len, &need_more_input);
+ if (msg == NULL)
+ return need_more_input ? 1 : -1;
+
+ buf_len = in_len;
+ if (data->ssl.tls_in_total > buf_len)
+ buf_len = data->ssl.tls_in_total;
+ in_decrypted = os_malloc(buf_len);
+ if (in_decrypted == NULL) {
+ os_free(data->ssl.tls_in);
+ data->ssl.tls_in = NULL;
+ data->ssl.tls_in_len = 0;
+ wpa_printf(MSG_WARNING, "EAP-FAST: failed to allocate memory "
+ "for decryption");
+ return -1;
+ }
+
+ len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+ msg, msg_len,
+ in_decrypted, buf_len);
+ os_free(data->ssl.tls_in);
+ data->ssl.tls_in = NULL;
+ data->ssl.tls_in_len = 0;
+ if (len_decrypted < 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 "
+ "data");
+ os_free(in_decrypted);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Decrypted Phase 2 TLV(s)",
+ in_decrypted, len_decrypted);
+
+ if (len_decrypted < 4) {
+ os_free(in_decrypted);
+ wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 "
+ "TLV frame (len=%d)", len_decrypted);
+ return -1;
+ }
+
+ pos = in_decrypted;
+ end = in_decrypted + len_decrypted;
+ stop = 0;
+ while (pos + 4 < end && !stop) {
+ mandatory = pos[0] & 0x80;
+ tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+ pos += 2;
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (pos + len > end) {
+ os_free(in_decrypted);
+ wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: received Phase 2: "
+ "TLV type %d length %d%s",
+ tlv_type, len, mandatory ? " (mandatory)" : "");
+
+ switch (tlv_type) {
+ case EAP_TLV_EAP_PAYLOAD_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP Payload TLV",
+ pos, len);
+ eap_payload_tlv = pos;
+ eap_payload_tlv_len = len;
+ break;
+ case EAP_TLV_RESULT_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV",
+ pos, len);
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Result TLV");
+ result = EAP_TLV_RESULT_FAILURE;
+ break;
+ }
+ result = WPA_GET_BE16(pos);
+ if (result != EAP_TLV_RESULT_SUCCESS &&
+ result != EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown "
+ "Result %d", result);
+ result = EAP_TLV_RESULT_FAILURE;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s",
+ result == EAP_TLV_RESULT_SUCCESS ?
+ "Success" : "Failure");
+ break;
+ case EAP_TLV_INTERMEDIATE_RESULT_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate "
+ "Result TLV", pos, len);
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Intermediate Result TLV");
+ iresult = EAP_TLV_RESULT_FAILURE;
+ break;
+ }
+ iresult = WPA_GET_BE16(pos);
+ if (iresult != EAP_TLV_RESULT_SUCCESS &&
+ iresult != EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown "
+ "Intermediate Result %d", iresult);
+ iresult = EAP_TLV_RESULT_FAILURE;
+ }
+ wpa_printf(MSG_DEBUG,
+ "EAP-FAST: Intermediate Result: %s",
+ iresult == EAP_TLV_RESULT_SUCCESS ?
+ "Success" : "Failure");
+ break;
+ case EAP_TLV_CRYPTO_BINDING_TLV_:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding "
+ "TLV", pos, len);
+ crypto_binding_len = sizeof(struct eap_tlv_hdr) + len;
+ if (crypto_binding_len < sizeof(*crypto_binding)) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Crypto-Binding TLV");
+ iresult = EAP_TLV_RESULT_FAILURE;
+ pos = end;
+ break;
+ }
+ crypto_binding =
+ (struct eap_tlv_crypto_binding__tlv *)
+ (pos - sizeof(struct eap_tlv_hdr));
+ break;
+ case EAP_TLV_PAC_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV",
+ pos, len);
+ pac = pos;
+ pac_len = len;
+ break;
+ default:
+ if (mandatory) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown "
+ "mandatory TLV type %d", tlv_type);
+ resp = eap_fast_tlv_nak(0, tlv_type,
+ &resp_len);
+ stop = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: ignored "
+ "unknown optional TLV type %d",
+ tlv_type);
+ }
+ break;
+ }
+
+ pos += len;
+ }
+
+ if (!resp && result == EAP_TLV_RESULT_FAILURE) {
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0,
+ &resp_len);
+ if (!resp) {
+ os_free(in_decrypted);
+ return 0;
+ }
+ }
+
+ if (!resp && iresult == EAP_TLV_RESULT_FAILURE) {
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1,
+ &resp_len);
+ if (!resp) {
+ os_free(in_decrypted);
+ return 0;
+ }
+ }
+
+ if (!resp && eap_payload_tlv) {
+ if (eap_payload_tlv_len < sizeof(*hdr)) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: too short EAP "
+ "Payload TLV (len=%lu)",
+ (unsigned long) eap_payload_tlv_len);
+ os_free(in_decrypted);
+ return 0;
+ }
+ hdr = (struct eap_hdr *) eap_payload_tlv;
+ if (be_to_host16(hdr->length) > eap_payload_tlv_len) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: EAP packet overflow "
+ "in EAP Payload TLV");
+ os_free(in_decrypted);
+ return 0;
+ }
+ if (hdr->code == EAP_CODE_REQUEST) {
+ if (eap_fast_phase2_request(sm, data, ret, hdr,
+ &resp, &resp_len)) {
+ os_free(in_decrypted);
+ wpa_printf(MSG_INFO, "EAP-FAST: Phase2 "
+ "Request processing failed");
+ return 0;
+ }
+ resp = eap_fast_tlv_eap_payload(resp, &resp_len);
+ if (resp == NULL) {
+ os_free(in_decrypted);
+ return 0;
+ }
+ } else {
+ wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ os_free(in_decrypted);
+ return 0;
+ }
+ }
+
+ if (!resp && crypto_binding) {
+ int final = result == EAP_TLV_RESULT_SUCCESS;
+ resp = eap_fast_process_crypto_binding(sm, data, ret,
+ crypto_binding,
+ crypto_binding_len,
+ &resp_len, final);
+ if (!resp) {
+ os_free(in_decrypted);
+ return 0;
+ }
+ }
+
+ if (!resp && pac && result != EAP_TLV_RESULT_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV "
+ "acknowledging success");
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0,
+ &resp_len);
+ if (!resp) {
+ os_free(in_decrypted);
+ return 0;
+ }
+ }
+
+ if (!resp && pac && result == EAP_TLV_RESULT_SUCCESS) {
+ resp = eap_fast_process_pac(sm, data, ret, pac, pac_len,
+ &resp_len);
+ if (!resp) {
+ os_free(in_decrypted);
+ return 0;
+ }
+ }
+
+ os_free(in_decrypted);
+
+ if (resp == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send "
+ "empty response packet");
+ resp = os_malloc(1);
+ if (resp == NULL)
+ return 0;
+ resp_len = 0;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 data",
+ resp, resp_len);
+ if (eap_fast_encrypt(sm, data, req->identifier, resp, resp_len,
+ out_data, out_len)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 "
+ "frame");
+ }
+ os_free(resp);
+
+ return 0;
+}
+
+
+static u8 * eap_fast_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ const struct eap_hdr *req;
+ size_t left;
+ int res;
+ u8 flags, *resp, id;
+ const u8 *pos;
+ struct eap_fast_data *data = priv;
+
+ pos = eap_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret,
+ reqData, reqDataLen, &left, &flags);
+ if (pos == NULL)
+ return NULL;
+ req = (const struct eap_hdr *) reqData;
+ id = req->identifier;
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ const u8 *a_id;
+ size_t a_id_len;
+ struct pac_tlv_hdr *hdr;
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own "
+ "ver=%d)", flags & EAP_PEAP_VERSION_MASK,
+ data->fast_version);
+ if ((flags & EAP_PEAP_VERSION_MASK) < data->fast_version)
+ data->fast_version = flags & EAP_PEAP_VERSION_MASK;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Using FAST version %d",
+ data->fast_version);
+
+ a_id = pos;
+ a_id_len = left;
+ if (left > sizeof(*hdr)) {
+ int tlen;
+ hdr = (struct pac_tlv_hdr *) pos;
+ tlen = be_to_host16(hdr->len);
+ if (be_to_host16(hdr->type) == PAC_TYPE_A_ID &&
+ sizeof(*hdr) + tlen <= left) {
+ a_id = (u8 *) (hdr + 1);
+ a_id_len = tlen;
+ }
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: A-ID", a_id, a_id_len);
+
+ data->current_pac = eap_fast_get_pac(data, a_id, a_id_len);
+ if (data->current_pac) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC found for this "
+ "A-ID");
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-FAST: A-ID-Info",
+ data->current_pac->a_id_info,
+ data->current_pac->a_id_info_len);
+ }
+
+ if (data->resuming && data->current_pac) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Trying to resume "
+ "session - do not add PAC-Opaque to TLS "
+ "ClientHello");
+ if (tls_connection_client_hello_ext(
+ sm->ssl_ctx, data->ssl.conn,
+ TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to "
+ "remove PAC-Opaque TLS extension");
+ return NULL;
+ }
+
+ } else if (data->current_pac) {
+ u8 *tlv;
+ size_t tlv_len, olen;
+ struct eap_tlv_hdr *ehdr;
+ olen = data->current_pac->pac_opaque_len;
+ tlv_len = sizeof(*ehdr) + olen;
+ tlv = os_malloc(tlv_len);
+ if (tlv) {
+ ehdr = (struct eap_tlv_hdr *) tlv;
+ ehdr->tlv_type =
+ host_to_be16(PAC_TYPE_PAC_OPAQUE);
+ ehdr->length = host_to_be16(olen);
+ os_memcpy(ehdr + 1,
+ data->current_pac->pac_opaque, olen);
+ }
+ if (tlv == NULL ||
+ tls_connection_client_hello_ext(
+ sm->ssl_ctx, data->ssl.conn,
+ TLS_EXT_PAC_OPAQUE, tlv, tlv_len) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to "
+ "add PAC-Opaque TLS extension");
+ os_free(tlv);
+ return NULL;
+ }
+ os_free(tlv);
+ } else {
+ u8 ciphers[2];
+ if (!data->provisioning_allowed) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found "
+ "and provisioning disabled");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found - "
+ "starting provisioning");
+ ciphers[0] = TLS_CIPHER_ANON_DH_AES128_SHA;
+ ciphers[1] = TLS_CIPHER_NONE;
+ if (tls_connection_set_cipher_list(sm->ssl_ctx,
+ data->ssl.conn,
+ ciphers)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Could not "
+ "configure anonymous DH for TLS "
+ "connection");
+ return NULL;
+ }
+ if (tls_connection_client_hello_ext(
+ sm->ssl_ctx, data->ssl.conn,
+ TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to "
+ "remove PAC-Opaque TLS extension");
+ return NULL;
+ }
+ data->provisioning = 1;
+ }
+
+ left = 0; /* A-ID is not used in further packet processing */
+ }
+
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
+ res = eap_fast_decrypt(sm, data, ret, req, pos, left,
+ &resp, respDataLen);
+ if (res < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ /* Ack possible Alert that may have caused failure in
+ * decryption */
+ res = 1;
+ }
+ } else {
+ if (eap_fast_set_tls_master_secret(sm, data, pos, left) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to configure "
+ "TLS master secret");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+
+ res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_FAST,
+ data->fast_version, id, pos, left,
+ &resp, respDataLen);
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-FAST: TLS done, proceed to Phase 2");
+ data->resuming = 0;
+ eap_fast_derive_keys(sm, data);
+ }
+ }
+
+ if (res == 1)
+ return eap_tls_build_ack(&data->ssl, respDataLen, id,
+ EAP_TYPE_FAST, data->fast_version);
+ return resp;
+}
+
+
+#if 0 /* FIX */
+static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+}
+
+
+static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ os_free(data->key_block_p);
+ data->key_block_p = NULL;
+}
+
+
+static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ if (eap_tls_reauth_init(sm, &data->ssl)) {
+ os_free(data);
+ return NULL;
+ }
+ if (data->phase2_priv && data->phase2_method &&
+ data->phase2_method->init_for_reauth)
+ data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+ data->phase2_success = 0;
+ data->resuming = 1;
+ data->provisioning = 0;
+ data->simck_idx = 0;
+ return priv;
+}
+#endif
+
+
+static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_fast_data *data = priv;
+ int len, ret;
+
+ len = eap_tls_status(sm, &data->ssl, buf, buflen, verbose);
+ if (data->phase2_method) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "EAP-FAST Phase2 method=%s\n",
+ data->phase2_method->name);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+ }
+ return len;
+}
+
+
+static Boolean eap_fast_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ return data->success;
+}
+
+
+static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_fast_data *data = priv;
+ u8 *key;
+
+ if (!data->success)
+ return NULL;
+
+ key = os_malloc(EAP_FAST_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_FAST_KEY_LEN;
+ os_memcpy(key, data->key_data, EAP_FAST_KEY_LEN);
+
+ return key;
+}
+
+
+static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_fast_data *data = priv;
+ u8 *key;
+
+ if (!data->success)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+ return key;
+}
+
+
+int eap_peer_fast_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_fast_init;
+ eap->deinit = eap_fast_deinit;
+ eap->process = eap_fast_process;
+ eap->isKeyAvailable = eap_fast_isKeyAvailable;
+ eap->getKey = eap_fast_getKey;
+ eap->get_status = eap_fast_get_status;
+#if 0
+ eap->has_reauth_data = eap_fast_has_reauth_data;
+ eap->deinit_for_reauth = eap_fast_deinit_for_reauth;
+ eap->init_for_reauth = eap_fast_init_for_reauth;
+#endif
+ eap->get_emsk = eap_fast_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}