📄 eap_fast.c
字号:
/* * EAP peer method: EAP-FAST (RFC 4851) * Copyright (c) 2004-2008, 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 "eap_config.h"#include "tls.h"#include "eap_common/eap_tlv_common.h"#include "sha1.h"#include "eap_fast_pac.h"#ifdef EAP_FAST_DYNAMIC#include "eap_fast_pac.c"#endif /* EAP_FAST_DYNAMIC *//* TODO: * - test session resumption and enable it if it interoperates * - password change (pending mschapv2 packet; replay decrypted packet) */static void eap_fast_deinit(struct eap_sm *sm, void *priv);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;#define EAP_FAST_PROV_UNAUTH 1#define EAP_FAST_PROV_AUTH 2 int provisioning_allowed; /* Allowed PAC provisioning modes */ int provisioning; /* doing PAC provisioning (not the normal auth) */ int anon_provisioning; /* doing anonymous (unauthenticated) * provisioning */ int session_ticket_used; 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; size_t max_pac_list_len; int use_pac_binary_format; u8 simck[EAP_FAST_SIMCK_LEN]; int simck_idx; struct wpabuf *pending_phase2_req;};static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, const u8 *server_random, u8 *master_secret){ struct eap_fast_data *data = ctx; wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback"); if (client_random == NULL || server_random == NULL || master_secret == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket failed - fall " "back to full TLS handshake"); data->session_ticket_used = 0; if (data->provisioning_allowed) { wpa_printf(MSG_DEBUG, "EAP-FAST: Try to provision a " "new PAC-Key"); data->provisioning = 1; data->current_pac = NULL; } return 0; } wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket", ticket, len); if (data->current_pac == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key available for " "using SessionTicket"); data->session_ticket_used = 0; return 0; } eap_fast_derive_master_secret(data->current_pac->pac_key, server_random, client_random, master_secret); data->session_ticket_used = 1; return 1;}static int eap_fast_parse_phase1(struct eap_fast_data *data, const char *phase1){ const char *pos; pos = os_strstr(phase1, "fast_provisioning="); if (pos) { data->provisioning_allowed = atoi(pos + 18); wpa_printf(MSG_DEBUG, "EAP-FAST: Automatic PAC provisioning " "mode: %d", data->provisioning_allowed); } pos = os_strstr(phase1, "fast_max_pac_list_len="); if (pos) { data->max_pac_list_len = atoi(pos + 22); if (data->max_pac_list_len == 0) data->max_pac_list_len = 1; wpa_printf(MSG_DEBUG, "EAP-FAST: Maximum PAC list length: %lu", (unsigned long) data->max_pac_list_len); } pos = os_strstr(phase1, "fast_pac_format=binary"); if (pos) { data->use_pac_binary_format = 1; wpa_printf(MSG_DEBUG, "EAP-FAST: Using binary format for PAC " "list"); } return 0;}static void * eap_fast_init(struct eap_sm *sm){ struct eap_fast_data *data; struct eap_peer_config *config = eap_get_config(sm); data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->fast_version = EAP_FAST_VERSION; data->max_pac_list_len = 10; if (config && config->phase1 && eap_fast_parse_phase1(data, config->phase1) < 0) { eap_fast_deinit(sm, data); return NULL; } if (eap_peer_select_phase2_methods(config, "auth=", &data->phase2_types, &data->num_phase2_types) < 0) { eap_fast_deinit(sm, data); return NULL; } data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); eap_fast_deinit(sm, data); return NULL; } if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn, eap_fast_session_ticket_cb, data) < 0) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket " "callback"); 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 */ if (tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to enable TLS " "workarounds"); } if (data->use_pac_binary_format && eap_fast_load_pac_bin(sm, &data->pac, config->pac_file) < 0) { eap_fast_deinit(sm, data); return NULL; } if (!data->use_pac_binary_format && eap_fast_load_pac(sm, &data->pac, config->pac_file) < 0) { eap_fast_deinit(sm, data); return NULL; } eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len); 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_peer_tls_ssl_deinit(sm, &data->ssl); pac = data->pac; prev = NULL; while (pac) { prev = pac; pac = pac->next; eap_fast_free_pac(prev); } wpabuf_free(data->pending_phase2_req); os_free(data);}static int eap_fast_derive_msk(struct eap_fast_data *data){ eap_fast_derive_eap_msk(data->simck, data->key_data); eap_fast_derive_eap_emsk(data->simck, data->emsk); data->success = 1; return 0;}static void eap_fast_derive_key_auth(struct eap_sm *sm, struct eap_fast_data *data){ u8 *sks; /* RFC 4851, Section 5.1: * Extra key material after TLS key_block: session_key_seed[40] */ sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion", EAP_FAST_SKS_LEN); if (sks == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " "session_key_seed"); return; } /* * RFC 4851, Section 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->ssl_ctx, data->ssl.conn, "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; } /* * RFC 4851, Section 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->anon_provisioning) eap_fast_derive_key_provisioning(sm, data); else eap_fast_derive_key_auth(sm, data);}static int eap_fast_init_phase2_method(struct eap_sm *sm, struct eap_fast_data *data){ data->phase2_method = eap_peer_get_eap_method(data->phase2_type.vendor, data->phase2_type.method); if (data->phase2_method == NULL) return -1; 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; data->phase2_priv = data->phase2_method->init(sm); sm->init_phase2 = 0; sm->auth_challenge = NULL; sm->peer_challenge = NULL; return data->phase2_priv == NULL ? -1 : 0;}static int eap_fast_select_phase2_method(struct eap_fast_data *data, u8 type){ size_t i; /* TODO: TNC with anonymous provisioning; need to require both * completed MSCHAPv2 and TNC */ if (data->anon_provisioning && type != EAP_TYPE_MSCHAPV2) { wpa_printf(MSG_INFO, "EAP-FAST: Only EAP-MSCHAPv2 is allowed " "during unauthenticated provisioning; reject phase2" " type %d", type); return -1; }#ifdef EAP_TNC if (type == EAP_TYPE_TNC) { data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_TNC; wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP " "vendor %d method %d for TNC", data->phase2_type.vendor, data->phase2_type.method); return 0; }#endif /* EAP_TNC */ for (i = 0; i < data->num_phase2_types; i++) { if (data->phase2_types[i].vendor != EAP_VENDOR_IETF || data->phase2_types[i].method != type) 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 (type != data->phase2_type.method || type == EAP_TYPE_NONE) return -1; return 0;}static int eap_fast_phase2_request(struct eap_sm *sm, struct eap_fast_data *data, struct eap_method_ret *ret, struct eap_hdr *hdr, struct wpabuf **resp){ size_t len = be_to_host16(hdr->length); u8 *pos; struct eap_method_ret iret; struct eap_peer_config *config = eap_get_config(sm); struct wpabuf msg; 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); if (*pos == EAP_TYPE_IDENTITY) { *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); return 0; } if (data->phase2_priv && data->phase2_method && *pos != data->phase2_type.method) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 EAP sequence - " "deinitialize previous method"); data->phase2_method->deinit(sm, data->phase2_priv); data->phase2_method = NULL; data->phase2_priv = NULL; data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; } if (data->phase2_type.vendor == EAP_VENDOR_IETF && data->phase2_type.method == EAP_TYPE_NONE && eap_fast_select_phase2_method(data, *pos) < 0) { if (eap_peer_tls_phase2_nak(data->phase2_types, data->num_phase2_types, hdr, resp)) return -1; return 0; } if (data->phase2_priv == NULL && eap_fast_init_phase2_method(sm, data) < 0) { 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)); wpabuf_set(&msg, hdr, len); *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, &msg); 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 && config && (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || config->pending_req_new_password)) { wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); } else if (*resp == NULL) return -1; return 0;}static struct wpabuf * eap_fast_tlv_nak(int vendor_id, int tlv_type){ struct wpabuf *buf; struct eap_tlv_nak_tlv *nak; buf = wpabuf_alloc(sizeof(*nak)); if (buf == NULL) return NULL; nak = wpabuf_put(buf, sizeof(*nak)); 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 buf;}static struct wpabuf * eap_fast_tlv_result(int status, int intermediate){ struct wpabuf *buf; struct eap_tlv_intermediate_result_tlv *result; buf = wpabuf_alloc(sizeof(*result)); if (buf == NULL) return NULL; wpa_printf(MSG_DEBUG, "EAP-FAST: Add %sResult TLV(status=%d)", intermediate ? "Intermediate " : "", status); result = wpabuf_put(buf, sizeof(*result)); 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 buf;}static struct wpabuf * eap_fast_tlv_pac_ack(void){ struct wpabuf *buf; struct eap_tlv_result_tlv *res; struct eap_tlv_pac_ack_tlv *ack; buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack)); if (buf == NULL) return NULL; wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV (ack)"); ack = wpabuf_put(buf, sizeof(*ack)); 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 buf;}static struct wpabuf * eap_fast_process_eap_payload_tlv( struct eap_sm *sm, struct eap_fast_data *data, struct eap_method_ret *ret, const struct eap_hdr *req, u8 *eap_payload_tlv, size_t eap_payload_tlv_len){ struct eap_hdr *hdr; struct wpabuf *resp = NULL; 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); return NULL; } 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"); return NULL; } if (hdr->code != EAP_CODE_REQUEST) { wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in " "Phase 2 EAP header", hdr->code); return NULL; } if (eap_fast_phase2_request(sm, data, ret, hdr, &resp)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -