summaryrefslogtreecommitdiffstats
path: root/contrib/wpa_supplicant/eap_fast.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/wpa_supplicant/eap_fast.c')
-rw-r--r--contrib/wpa_supplicant/eap_fast.c343
1 files changed, 205 insertions, 138 deletions
diff --git a/contrib/wpa_supplicant/eap_fast.c b/contrib/wpa_supplicant/eap_fast.c
index 1c9c3ff..ff8f76c 100644
--- a/contrib/wpa_supplicant/eap_fast.c
+++ b/contrib/wpa_supplicant/eap_fast.c
@@ -24,10 +24,12 @@
#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
@@ -36,7 +38,8 @@
#define TLS_EXT_PAC_OPAQUE 35
-static char *pac_file_hdr = "wpa_supplicant EAP-FAST PAC file - version 1";
+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);
@@ -58,28 +61,18 @@ struct pac_tlv_hdr {
};
-/* draft-cam-winget-eap-fast-00.txt, 6.2 EAP-FAST Authentication Phase 1 */
+/* draft-cam-winget-eap-fast-02.txt:
+ * 6.2 EAP-FAST Authentication Phase 1: Key Derivations */
struct eap_fast_key_block_auth {
- /* RC4-128-SHA */
- u8 client_write_MAC_secret[20];
- u8 server_write_MAC_secret[20];
- u8 client_write_key[16];
- u8 server_write_key[16];
- /* u8 client_write_IV[0]; */
- /* u8 server_write_IV[0]; */
+ /* Extra key material after TLS key_block */
u8 session_key_seed[40];
};
-/* draft-cam-winget-eap-fast-00.txt, 7.3 EAP-FAST Provisioning Exchange */
+/* 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 {
- /* AES128-SHA */
- u8 client_write_MAC_secret[20];
- u8 server_write_MAC_secret[20];
- u8 client_write_key[16];
- u8 server_write_key[16];
- u8 client_write_IV[16];
- u8 server_write_IV[16];
+ /* Extra key material after TLS key_block */
u8 session_key_seed[40];
u8 server_challenge[16];
u8 client_challenge[16];
@@ -249,12 +242,34 @@ static int eap_fast_add_pac(struct eap_fast_data *data,
}
-static int eap_fast_read_line(FILE *f, char *buf, size_t buf_len)
+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 (fgets(buf, buf_len, f) == NULL) {
- return -1;
+ 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;
+ memcpy(buf, rc->pos, len);
+ buf[len] = '\0';
+ rc->pos = l_end + 1;
}
buf[buf_len - 1] = '\0';
@@ -293,9 +308,10 @@ static u8 * eap_fast_parse_hex(const char *value, size_t *len)
}
-static int eap_fast_load_pac(struct eap_fast_data *data, const char *pac_file)
+static int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_data *data,
+ const char *pac_file)
{
- FILE *f;
+ struct eap_fast_read_ctx rc;
struct eap_fast_pac *pac = NULL;
int count = 0;
char *buf, *pos;
@@ -305,11 +321,27 @@ static int eap_fast_load_pac(struct eap_fast_data *data, const char *pac_file)
if (pac_file == NULL)
return -1;
- f = fopen(pac_file, "r");
- if (f == NULL) {
- wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - assume no "
- "PAC entries have been provisioned", pac_file);
- return 0;
+ memset(&rc, 0, sizeof(rc));
+
+ if (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 = malloc(buf_len);
@@ -318,16 +350,17 @@ static int eap_fast_load_pac(struct eap_fast_data *data, const char *pac_file)
}
line++;
- if (eap_fast_read_line(f, buf, buf_len) < 0 ||
+ if (eap_fast_read_line(&rc, buf, buf_len) < 0 ||
strcmp(pac_file_hdr, buf) != 0) {
wpa_printf(MSG_INFO, "EAP-FAST: Unrecognized header line in "
"PAC file '%s'", pac_file);
free(buf);
- fclose(f);
+ if (rc.f)
+ fclose(rc.f);
return -1;
}
- while (eap_fast_read_line(f, buf, buf_len) == 0) {
+ while (eap_fast_read_line(&rc, buf, buf_len) == 0) {
line++;
pos = strchr(buf, '=');
if (pos) {
@@ -423,7 +456,8 @@ static int eap_fast_load_pac(struct eap_fast_data *data, const char *pac_file)
}
free(buf);
- fclose(f);
+ if (rc.f)
+ fclose(rc.f);
if (ret == 0) {
wpa_printf(MSG_DEBUG, "EAP-FAST: read %d PAC entries from "
@@ -434,67 +468,124 @@ static int eap_fast_load_pac(struct eap_fast_data *data, const char *pac_file)
}
-static void eap_fast_write(FILE *f, const char *field, const u8 *data,
+static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
+ const char *field, const u8 *data,
size_t len, int txt)
{
int i;
+ size_t need;
- if (data == NULL)
+ if (data == NULL || *buf == NULL)
return;
- fprintf(f, "%s=", field);
+ need = strlen(field) + len * 2 + 30;
+ if (txt)
+ need += strlen(field) + len + 20;
+
+ if (*pos - *buf + need > *buf_len) {
+ char *nbuf = realloc(*buf, *buf_len + need);
+ if (nbuf == NULL) {
+ free(*buf);
+ *buf = NULL;
+ return;
+ }
+ *buf = nbuf;
+ *buf_len += need;
+ }
+
+ *pos += snprintf(*pos, *buf + *buf_len - *pos, "%s=", field);
for (i = 0; i < len; i++) {
- fprintf(f, "%02x", data[i]);
+ *pos += snprintf(*pos, *buf + *buf_len - *pos,
+ "%02x", data[i]);
}
- fprintf(f, "\n");
+ *pos += snprintf(*pos, *buf + *buf_len - *pos, "\n");
if (txt) {
- fprintf(f, "%s-txt=", field);
+ *pos += snprintf(*pos, *buf + *buf_len - *pos,
+ "%s-txt=", field);
for (i = 0; i < len; i++) {
- fprintf(f, "%c", data[i]);
+ *pos += snprintf(*pos, *buf + *buf_len - *pos,
+ "%c", data[i]);
}
- fprintf(f, "\n");
+ *pos += snprintf(*pos, *buf + *buf_len - *pos, "\n");
}
}
-static int eap_fast_save_pac(struct eap_fast_data *data, const char *pac_file)
+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;
+ char *buf, *pos;
+ size_t buf_len;
if (pac_file == NULL)
return -1;
- f = fopen(pac_file, "w");
- if (f == NULL) {
- wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC file '%s' "
- "for writing", pac_file);
+ buf_len = 1024;
+ pos = buf = malloc(buf_len);
+ if (buf == NULL)
return -1;
- }
- fprintf(f, "%s\n", pac_file_hdr);
+ pos += snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
pac = data->pac;
while (pac) {
- fprintf(f, "START\n");
- eap_fast_write(f, "PAC-Key", pac->pac_key,
+ pos += snprintf(pos, buf + buf_len - pos, "START\n");
+ eap_fast_write(&buf, &pos, &buf_len, "PAC-Key", pac->pac_key,
EAP_FAST_PAC_KEY_LEN, 0);
- eap_fast_write(f, "PAC-Opaque", pac->pac_opaque,
- pac->pac_opaque_len, 0);
- eap_fast_write(f, "PAC-Info", pac->pac_info,
+ 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(f, "A-ID", pac->a_id, pac->a_id_len, 0);
- eap_fast_write(f, "I-ID", pac->i_id, pac->i_id_len, 1);
- eap_fast_write(f, "A-ID-Info", pac->a_id_info,
- pac->a_id_info_len, 1);
- fprintf(f, "END\n");
+ 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);
+ pos += snprintf(pos, buf + buf_len - pos, "END\n");
count++;
pac = pac->next;
+
+ if (buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
+ "data");
+ return -1;
+ }
}
- fclose(f);
+ if (strncmp(pac_file, "blob://", 7) == 0) {
+ struct wpa_config_blob *blob;
+ blob = malloc(sizeof(*blob));
+ if (blob == NULL) {
+ free(buf);
+ return -1;
+ }
+ memset(blob, 0, sizeof(*blob));
+ blob->data = (u8 *) buf;
+ blob->len = pos - buf;
+ buf = NULL;
+ blob->name = strdup(pac_file + 7);
+ if (blob->name == NULL) {
+ wpa_config_free_blob(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);
+ free(buf);
+ return -1;
+ }
+ fprintf(f, "%s", buf);
+ free(buf);
+ fclose(f);
+ }
wpa_printf(MSG_DEBUG, "EAP-FAST: wrote %d PAC entries into '%s'",
count, pac_file);
@@ -590,7 +681,7 @@ static void * eap_fast_init(struct eap_sm *sm)
* TODO: consider making this configurable */
tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn);
- if (eap_fast_load_pac(data, config->pac_file) < 0) {
+ if (eap_fast_load_pac(sm, data, config->pac_file) < 0) {
eap_fast_deinit(sm, data);
return NULL;
}
@@ -632,7 +723,7 @@ static void eap_fast_deinit(struct eap_sm *sm, void *priv)
static int eap_fast_encrypt(struct eap_sm *sm, struct eap_fast_data *data,
- int id, u8 *plain, size_t plain_len,
+ int id, const u8 *plain, size_t plain_len,
u8 **out_data, size_t *out_len)
{
int res;
@@ -799,32 +890,37 @@ 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 *random;
+ u8 *rnd;
u8 *out;
+ int block_size;
if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
return NULL;
- out = malloc(len);
- random = malloc(keys.client_random_len + keys.server_random_len);
- if (out == NULL || random == NULL) {
+ block_size = tls_connection_get_keyblock_size(sm->ssl_ctx, data->conn);
+ if (block_size < 0)
+ return NULL;
+ out = malloc(block_size + len);
+ rnd = malloc(keys.client_random_len + keys.server_random_len);
+ if (out == NULL || rnd == NULL) {
free(out);
- free(random);
+ free(rnd);
return NULL;
}
- memcpy(random, keys.server_random, keys.server_random_len);
- memcpy(random + keys.server_random_len, keys.client_random,
+ memcpy(rnd, keys.server_random, keys.server_random_len);
+ 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, random, keys.client_random_len +
- keys.server_random_len, out, len)) {
- free(random);
+ label, rnd, keys.client_random_len +
+ keys.server_random_len, out, block_size + len)) {
+ free(rnd);
free(out);
return NULL;
}
- free(random);
+ free(rnd);
+ memmove(out, out + block_size, len);
return out;
}
@@ -836,6 +932,11 @@ static void eap_fast_derive_key_auth(struct eap_sm *sm,
data->key_block_a = (struct eap_fast_key_block_auth *)
eap_fast_derive_key(sm, &data->ssl, "key expansion",
sizeof(*data->key_block_a));
+ if (data->key_block_a == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
+ "session_key_seed");
+ return;
+ }
wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: session_key_seed",
data->key_block_a->session_key_seed,
sizeof(data->key_block_a->session_key_seed));
@@ -878,7 +979,7 @@ static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data)
static int eap_fast_phase2_request(struct eap_sm *sm,
struct eap_fast_data *data,
struct eap_method_ret *ret,
- struct eap_hdr *req,
+ const struct eap_hdr *req,
struct eap_hdr *hdr,
u8 **resp, size_t *resp_len)
{
@@ -1333,7 +1434,7 @@ static u8 * eap_fast_process_pac(struct eap_sm *sm, struct eap_fast_data *data,
}
eap_fast_add_pac(data, &entry);
- eap_fast_save_pac(data, config->pac_file);
+ eap_fast_save_pac(sm, data, config->pac_file);
if (data->provisioning) {
/* EAP-FAST provisioning does not provide keying material and
@@ -1356,12 +1457,13 @@ static u8 * eap_fast_process_pac(struct eap_sm *sm, struct eap_fast_data *data,
static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
- struct eap_method_ret *ret, struct eap_hdr *req,
- u8 *in_data, size_t in_len,
+ 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 buf_len, len_decrypted, len, res;
+ int buf_len, len_decrypted, len;
struct eap_hdr *hdr;
u8 *resp = NULL;
size_t resp_len;
@@ -1371,13 +1473,17 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
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;
wpa_printf(MSG_DEBUG, "EAP-FAST: received %lu bytes encrypted data for"
" Phase 2", (unsigned long) in_len);
- res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len);
- if (res < 0 || res == 1)
- return res;
+ 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)
@@ -1393,7 +1499,7 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
}
len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
- in_data, in_len,
+ msg, msg_len,
in_decrypted, buf_len);
free(data->ssl.tls_in);
data->ssl.tls_in = NULL;
@@ -1417,11 +1523,12 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
pos = in_decrypted;
end = in_decrypted + len_decrypted;
- while (pos < end) {
+ while (pos + 4 < end) {
mandatory = pos[0] & 0x80;
- tlv_type = ((pos[0] & 0x3f) << 8) | pos[1];
- len = (pos[2] << 8) | pos[3];
- pos += 4;
+ tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+ pos += 2;
+ len = WPA_GET_BE16(pos);
+ pos += 2;
if (pos + len > end) {
free(in_decrypted);
wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
@@ -1447,7 +1554,7 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
result = EAP_TLV_RESULT_FAILURE;
break;
}
- result = (pos[0] << 16) | pos[1];
+ result = WPA_GET_BE16(pos);
if (result != EAP_TLV_RESULT_SUCCESS &&
result != EAP_TLV_RESULT_FAILURE) {
wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown "
@@ -1467,7 +1574,7 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
iresult = EAP_TLV_RESULT_FAILURE;
break;
}
- iresult = (pos[0] << 16) | pos[1];
+ iresult = WPA_GET_BE16(pos);
if (iresult != EAP_TLV_RESULT_SUCCESS &&
iresult != EAP_TLV_RESULT_FAILURE) {
wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown "
@@ -1584,7 +1691,7 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
if (!resp && pac && result != EAP_TLV_RESULT_SUCCESS) {
wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV "
- "acnowledging success");
+ "acknowledging success");
resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0,
&resp_len);
if (!resp) {
@@ -1607,7 +1714,7 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
if (resp == NULL) {
wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send "
"empty response packet");
- resp = malloc(0);
+ resp = malloc(1);
if (resp == NULL)
return 0;
resp_len = 0;
@@ -1628,65 +1735,25 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
static u8 * eap_fast_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
- u8 *reqData, size_t reqDataLen,
+ const u8 *reqData, size_t reqDataLen,
size_t *respDataLen)
{
- struct eap_hdr *req;
- int left, res;
- unsigned int tls_msg_len;
- u8 flags, *pos, *resp, id;
+ const struct eap_hdr *req;
+ size_t left;
+ int res;
+ u8 flags, *resp, id;
+ const u8 *pos;
struct eap_fast_data *data = priv;
- if (tls_get_errors(sm->ssl_ctx)) {
- wpa_printf(MSG_INFO, "EAP-FAST: TLS errors detected");
- ret->ignore = TRUE;
- return NULL;
- }
-
- req = (struct eap_hdr *) reqData;
- pos = (u8 *) (req + 1);
- if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_FAST ||
- (left = host_to_be16(req->length)) > reqDataLen) {
- wpa_printf(MSG_INFO, "EAP-FAST: Invalid frame");
- ret->ignore = TRUE;
+ pos = eap_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret,
+ reqData, reqDataLen, &left, &flags);
+ if (pos == NULL)
return NULL;
- }
- left -= sizeof(struct eap_hdr);
+ req = (const struct eap_hdr *) reqData;
id = req->identifier;
- pos++;
- flags = *pos++;
- left -= 2;
- wpa_printf(MSG_DEBUG, "EAP-FAST: Received packet(len=%lu) - "
- "Flags 0x%02x", (unsigned long) reqDataLen, flags);
- if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
- if (left < 4) {
- wpa_printf(MSG_INFO, "EAP-FAST: Short frame with TLS "
- "length");
- ret->ignore = TRUE;
- return NULL;
- }
- tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) |
- pos[3];
- wpa_printf(MSG_DEBUG, "EAP-FAST: TLS Message Length: %d",
- tls_msg_len);
- if (data->ssl.tls_in_left == 0) {
- data->ssl.tls_in_total = tls_msg_len;
- data->ssl.tls_in_left = tls_msg_len;
- free(data->ssl.tls_in);
- data->ssl.tls_in = NULL;
- data->ssl.tls_in_len = 0;
- }
- pos += 4;
- left -= 4;
- }
-
- ret->ignore = FALSE;
- ret->methodState = METHOD_CONT;
- ret->decision = DECISION_FAIL;
- ret->allowNotifications = TRUE;
if (flags & EAP_TLS_FLAGS_START) {
- u8 *a_id;
+ const u8 *a_id;
size_t a_id_len;
struct pac_tlv_hdr *hdr;
OpenPOWER on IntegriCloud