123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569 |
- /*
- * hostapd / EAP-PAX (RFC 4746) server
- * Copyright (c) 2005-2007, 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_server/eap_i.h"
- #include "eap_common/eap_pax_common.h"
- /*
- * Note: only PAX_STD subprotocol is currently supported
- *
- * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
- * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
- * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
- * RSAES-OAEP).
- */
- struct eap_pax_data {
- enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state;
- u8 mac_id;
- union {
- u8 e[2 * EAP_PAX_RAND_LEN];
- struct {
- u8 x[EAP_PAX_RAND_LEN]; /* server rand */
- u8 y[EAP_PAX_RAND_LEN]; /* client rand */
- } r;
- } rand;
- u8 ak[EAP_PAX_AK_LEN];
- u8 mk[EAP_PAX_MK_LEN];
- u8 ck[EAP_PAX_CK_LEN];
- u8 ick[EAP_PAX_ICK_LEN];
- int keys_set;
- char *cid;
- size_t cid_len;
- };
- static void * eap_pax_init(struct eap_sm *sm)
- {
- struct eap_pax_data *data;
- data = os_zalloc(sizeof(*data));
- if (data == NULL)
- return NULL;
- data->state = PAX_STD_1;
- /*
- * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is
- * supported
- */
- data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128;
- return data;
- }
- static void eap_pax_reset(struct eap_sm *sm, void *priv)
- {
- struct eap_pax_data *data = priv;
- os_free(data->cid);
- os_free(data);
- }
- static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm,
- struct eap_pax_data *data, u8 id)
- {
- struct wpabuf *req;
- struct eap_pax_hdr *pax;
- u8 *pos;
- wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)");
- if (os_get_random(data->rand.r.x, EAP_PAX_RAND_LEN)) {
- wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
- data->state = FAILURE;
- return NULL;
- }
- req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
- sizeof(*pax) + 2 + EAP_PAX_RAND_LEN +
- EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
- if (req == NULL) {
- wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
- "request");
- data->state = FAILURE;
- return NULL;
- }
- pax = wpabuf_put(req, sizeof(*pax));
- pax->op_code = EAP_PAX_OP_STD_1;
- pax->flags = 0;
- pax->mac_id = data->mac_id;
- pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
- pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
- wpabuf_put_be16(req, EAP_PAX_RAND_LEN);
- wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN);
- wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)",
- data->rand.r.x, EAP_PAX_RAND_LEN);
- pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
- eap_pax_mac(data->mac_id, (u8 *) "", 0,
- wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
- NULL, 0, NULL, 0, pos);
- wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
- return req;
- }
- static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm,
- struct eap_pax_data *data, u8 id)
- {
- struct wpabuf *req;
- struct eap_pax_hdr *pax;
- u8 *pos;
- wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)");
- req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
- sizeof(*pax) + 2 + EAP_PAX_MAC_LEN +
- EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
- if (req == NULL) {
- wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
- "request");
- data->state = FAILURE;
- return NULL;
- }
- pax = wpabuf_put(req, sizeof(*pax));
- pax->op_code = EAP_PAX_OP_STD_3;
- pax->flags = 0;
- pax->mac_id = data->mac_id;
- pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
- pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
- wpabuf_put_be16(req, EAP_PAX_MAC_LEN);
- pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
- eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
- data->rand.r.y, EAP_PAX_RAND_LEN,
- (u8 *) data->cid, data->cid_len, NULL, 0, pos);
- wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
- pos, EAP_PAX_MAC_LEN);
- pos += EAP_PAX_MAC_LEN;
- /* Optional ADE could be added here, if needed */
- pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
- eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
- wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
- NULL, 0, NULL, 0, pos);
- wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
- return req;
- }
- static struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id)
- {
- struct eap_pax_data *data = priv;
- switch (data->state) {
- case PAX_STD_1:
- return eap_pax_build_std_1(sm, data, id);
- case PAX_STD_3:
- return eap_pax_build_std_3(sm, data, id);
- default:
- wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq",
- data->state);
- break;
- }
- return NULL;
- }
- static Boolean eap_pax_check(struct eap_sm *sm, void *priv,
- struct wpabuf *respData)
- {
- struct eap_pax_data *data = priv;
- struct eap_pax_hdr *resp;
- const u8 *pos;
- size_t len, mlen;
- u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
- pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
- if (pos == NULL || len < sizeof(*resp)) {
- wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
- return TRUE;
- }
- mlen = sizeof(struct eap_hdr) + 1 + len;
- resp = (struct eap_pax_hdr *) pos;
- wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
- "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
- "public_key_id 0x%x",
- resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id,
- resp->public_key_id);
- wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
- (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN);
- if (data->state == PAX_STD_1 &&
- resp->op_code != EAP_PAX_OP_STD_2) {
- wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - "
- "ignore op %d", resp->op_code);
- return TRUE;
- }
- if (data->state == PAX_STD_3 &&
- resp->op_code != EAP_PAX_OP_ACK) {
- wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - "
- "ignore op %d", resp->op_code);
- return TRUE;
- }
- if (resp->op_code != EAP_PAX_OP_STD_2 &&
- resp->op_code != EAP_PAX_OP_ACK) {
- wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x",
- resp->op_code);
- }
- if (data->mac_id != resp->mac_id) {
- wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, "
- "received 0x%x", data->mac_id, resp->mac_id);
- return TRUE;
- }
- if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
- wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, "
- "received 0x%x", EAP_PAX_DH_GROUP_NONE,
- resp->dh_group_id);
- return TRUE;
- }
- if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
- wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, "
- "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE,
- resp->public_key_id);
- return TRUE;
- }
- if (resp->flags & EAP_PAX_FLAGS_MF) {
- /* TODO: add support for reassembling fragments */
- wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported");
- return TRUE;
- }
- if (resp->flags & EAP_PAX_FLAGS_CE) {
- wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag");
- return TRUE;
- }
- if (data->keys_set) {
- if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) {
- wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet");
- return TRUE;
- }
- icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN;
- wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
- eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
- wpabuf_mhead(respData),
- wpabuf_len(respData) - EAP_PAX_ICV_LEN,
- NULL, 0, NULL, 0, icvbuf);
- if (os_memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
- wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
- wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
- icvbuf, EAP_PAX_ICV_LEN);
- return TRUE;
- }
- }
- return FALSE;
- }
- static void eap_pax_process_std_2(struct eap_sm *sm,
- struct eap_pax_data *data,
- struct wpabuf *respData)
- {
- struct eap_pax_hdr *resp;
- u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN];
- const u8 *pos;
- size_t len, left;
- int i;
- if (data->state != PAX_STD_1)
- return;
- wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2");
- pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
- if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN)
- return;
- resp = (struct eap_pax_hdr *) pos;
- pos = (u8 *) (resp + 1);
- left = len - sizeof(*resp);
- if (left < 2 + EAP_PAX_RAND_LEN ||
- WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
- wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)");
- return;
- }
- pos += 2;
- left -= 2;
- os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN);
- wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
- data->rand.r.y, EAP_PAX_RAND_LEN);
- pos += EAP_PAX_RAND_LEN;
- left -= EAP_PAX_RAND_LEN;
- if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) {
- wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)");
- return;
- }
- data->cid_len = WPA_GET_BE16(pos);
- os_free(data->cid);
- data->cid = os_malloc(data->cid_len);
- if (data->cid == NULL) {
- wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
- "CID");
- return;
- }
- os_memcpy(data->cid, pos + 2, data->cid_len);
- pos += 2 + data->cid_len;
- left -= 2 + data->cid_len;
- wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
- (u8 *) data->cid, data->cid_len);
- if (left < 2 + EAP_PAX_MAC_LEN ||
- WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
- wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)");
- return;
- }
- pos += 2;
- left -= 2;
- wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
- pos, EAP_PAX_MAC_LEN);
- if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) {
- wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID",
- (u8 *) data->cid, data->cid_len);
- data->state = FAILURE;
- return;
- }
- for (i = 0;
- i < EAP_MAX_METHODS &&
- (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
- sm->user->methods[i].method != EAP_TYPE_NONE);
- i++) {
- if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
- sm->user->methods[i].method == EAP_TYPE_PAX)
- break;
- }
- if (i >= EAP_MAX_METHODS ||
- sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
- sm->user->methods[i].method != EAP_TYPE_PAX) {
- wpa_hexdump_ascii(MSG_DEBUG,
- "EAP-PAX: EAP-PAX not enabled for CID",
- (u8 *) data->cid, data->cid_len);
- data->state = FAILURE;
- return;
- }
- if (sm->user->password == NULL ||
- sm->user->password_len != EAP_PAX_AK_LEN) {
- wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in "
- "user database for CID",
- (u8 *) data->cid, data->cid_len);
- data->state = FAILURE;
- return;
- }
- os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN);
- if (eap_pax_initial_key_derivation(data->mac_id, data->ak,
- data->rand.e, data->mk, data->ck,
- data->ick) < 0) {
- wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
- "key derivation");
- data->state = FAILURE;
- return;
- }
- data->keys_set = 1;
- eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
- data->rand.r.x, EAP_PAX_RAND_LEN,
- data->rand.r.y, EAP_PAX_RAND_LEN,
- (u8 *) data->cid, data->cid_len, mac);
- if (os_memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) {
- wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
- "PAX_STD-2");
- wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)",
- mac, EAP_PAX_MAC_LEN);
- data->state = FAILURE;
- return;
- }
- pos += EAP_PAX_MAC_LEN;
- left -= EAP_PAX_MAC_LEN;
- if (left < EAP_PAX_ICV_LEN) {
- wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in "
- "PAX_STD-2", (unsigned long) left);
- return;
- }
- wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
- eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
- wpabuf_head(respData),
- wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0,
- icvbuf);
- if (os_memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
- wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
- wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
- icvbuf, EAP_PAX_ICV_LEN);
- return;
- }
- pos += EAP_PAX_ICV_LEN;
- left -= EAP_PAX_ICV_LEN;
- if (left > 0) {
- wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
- pos, left);
- }
- data->state = PAX_STD_3;
- }
- static void eap_pax_process_ack(struct eap_sm *sm,
- struct eap_pax_data *data,
- struct wpabuf *respData)
- {
- if (data->state != PAX_STD_3)
- return;
- wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication "
- "completed successfully");
- data->state = SUCCESS;
- }
- static void eap_pax_process(struct eap_sm *sm, void *priv,
- struct wpabuf *respData)
- {
- struct eap_pax_data *data = priv;
- struct eap_pax_hdr *resp;
- const u8 *pos;
- size_t len;
- if (sm->user == NULL || sm->user->password == NULL) {
- wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not "
- "configured");
- data->state = FAILURE;
- return;
- }
- pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
- if (pos == NULL || len < sizeof(*resp))
- return;
- resp = (struct eap_pax_hdr *) pos;
- switch (resp->op_code) {
- case EAP_PAX_OP_STD_2:
- eap_pax_process_std_2(sm, data, respData);
- break;
- case EAP_PAX_OP_ACK:
- eap_pax_process_ack(sm, data, respData);
- break;
- }
- }
- static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv)
- {
- struct eap_pax_data *data = priv;
- return data->state == SUCCESS || data->state == FAILURE;
- }
- static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
- {
- struct eap_pax_data *data = priv;
- u8 *key;
- if (data->state != SUCCESS)
- return NULL;
- key = os_malloc(EAP_MSK_LEN);
- if (key == NULL)
- return NULL;
- *len = EAP_MSK_LEN;
- eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
- "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
- EAP_MSK_LEN, key);
- return key;
- }
- static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
- {
- struct eap_pax_data *data = priv;
- u8 *key;
- if (data->state != SUCCESS)
- return NULL;
- key = os_malloc(EAP_EMSK_LEN);
- if (key == NULL)
- return NULL;
- *len = EAP_EMSK_LEN;
- eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
- "Extended Master Session Key",
- data->rand.e, 2 * EAP_PAX_RAND_LEN,
- EAP_EMSK_LEN, key);
- return key;
- }
- static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv)
- {
- struct eap_pax_data *data = priv;
- return data->state == SUCCESS;
- }
- int eap_server_pax_register(void)
- {
- struct eap_method *eap;
- int ret;
- eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
- EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
- if (eap == NULL)
- return -1;
- eap->init = eap_pax_init;
- eap->reset = eap_pax_reset;
- eap->buildReq = eap_pax_buildReq;
- eap->check = eap_pax_check;
- eap->process = eap_pax_process;
- eap->isDone = eap_pax_isDone;
- eap->getKey = eap_pax_getKey;
- eap->isSuccess = eap_pax_isSuccess;
- eap->get_emsk = eap_pax_get_emsk;
- ret = eap_server_method_register(eap);
- if (ret)
- eap_server_method_free(eap);
- return ret;
- }
|