$search
00001 /* 00002 * EAP peer method: EAP-PAX (RFC 4746) 00003 * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi> 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License version 2 as 00007 * published by the Free Software Foundation. 00008 * 00009 * Alternatively, this software may be distributed under the terms of BSD 00010 * license. 00011 * 00012 * See README and COPYING for more details. 00013 */ 00014 00015 #include "includes.h" 00016 00017 #include "common.h" 00018 #include "eap_common/eap_pax_common.h" 00019 #include "eap_i.h" 00020 00021 /* 00022 * Note: only PAX_STD subprotocol is currently supported 00023 * 00024 * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite 00025 * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and 00026 * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits), 00027 * RSAES-OAEP). 00028 */ 00029 00030 struct eap_pax_data { 00031 enum { PAX_INIT, PAX_STD_2_SENT, PAX_DONE } state; 00032 u8 mac_id, dh_group_id, public_key_id; 00033 union { 00034 u8 e[2 * EAP_PAX_RAND_LEN]; 00035 struct { 00036 u8 x[EAP_PAX_RAND_LEN]; /* server rand */ 00037 u8 y[EAP_PAX_RAND_LEN]; /* client rand */ 00038 } r; 00039 } rand; 00040 char *cid; 00041 size_t cid_len; 00042 u8 ak[EAP_PAX_AK_LEN]; 00043 u8 mk[EAP_PAX_MK_LEN]; 00044 u8 ck[EAP_PAX_CK_LEN]; 00045 u8 ick[EAP_PAX_ICK_LEN]; 00046 }; 00047 00048 00049 static void eap_pax_deinit(struct eap_sm *sm, void *priv); 00050 00051 00052 static void * eap_pax_init(struct eap_sm *sm) 00053 { 00054 struct eap_pax_data *data; 00055 const u8 *identity, *password; 00056 size_t identity_len, password_len; 00057 00058 identity = eap_get_config_identity(sm, &identity_len); 00059 password = eap_get_config_password(sm, &password_len); 00060 if (!identity || !password) { 00061 wpa_printf(MSG_INFO, "EAP-PAX: CID (nai) or key (password) " 00062 "not configured"); 00063 return NULL; 00064 } 00065 00066 if (password_len != EAP_PAX_AK_LEN) { 00067 wpa_printf(MSG_INFO, "EAP-PAX: Invalid PSK length"); 00068 return NULL; 00069 } 00070 00071 data = os_zalloc(sizeof(*data)); 00072 if (data == NULL) 00073 return NULL; 00074 data->state = PAX_INIT; 00075 00076 data->cid = os_malloc(identity_len); 00077 if (data->cid == NULL) { 00078 eap_pax_deinit(sm, data); 00079 return NULL; 00080 } 00081 os_memcpy(data->cid, identity, identity_len); 00082 data->cid_len = identity_len; 00083 00084 os_memcpy(data->ak, password, EAP_PAX_AK_LEN); 00085 00086 return data; 00087 } 00088 00089 00090 static void eap_pax_deinit(struct eap_sm *sm, void *priv) 00091 { 00092 struct eap_pax_data *data = priv; 00093 os_free(data->cid); 00094 os_free(data); 00095 } 00096 00097 00098 static struct wpabuf * eap_pax_alloc_resp(const struct eap_pax_hdr *req, 00099 u8 id, u8 op_code, size_t plen) 00100 { 00101 struct wpabuf *resp; 00102 struct eap_pax_hdr *pax; 00103 00104 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, 00105 sizeof(*pax) + plen, EAP_CODE_RESPONSE, id); 00106 if (resp == NULL) 00107 return NULL; 00108 00109 pax = wpabuf_put(resp, sizeof(*pax)); 00110 pax->op_code = op_code; 00111 pax->flags = 0; 00112 pax->mac_id = req->mac_id; 00113 pax->dh_group_id = req->dh_group_id; 00114 pax->public_key_id = req->public_key_id; 00115 00116 return resp; 00117 } 00118 00119 00120 static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data, 00121 struct eap_method_ret *ret, u8 id, 00122 const struct eap_pax_hdr *req, 00123 size_t req_plen) 00124 { 00125 struct wpabuf *resp; 00126 const u8 *pos; 00127 u8 *rpos; 00128 size_t left, plen; 00129 00130 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (received)"); 00131 00132 if (data->state != PAX_INIT) { 00133 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 received in " 00134 "unexpected state (%d) - ignored", data->state); 00135 ret->ignore = TRUE; 00136 return NULL; 00137 } 00138 00139 if (req->flags & EAP_PAX_FLAGS_CE) { 00140 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with CE flag set - " 00141 "ignored"); 00142 ret->ignore = TRUE; 00143 return NULL; 00144 } 00145 00146 left = req_plen - sizeof(*req); 00147 00148 if (left < 2 + EAP_PAX_RAND_LEN) { 00149 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with too short " 00150 "payload"); 00151 ret->ignore = TRUE; 00152 return NULL; 00153 } 00154 00155 pos = (const u8 *) (req + 1); 00156 if (WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) { 00157 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with incorrect A " 00158 "length %d (expected %d)", 00159 WPA_GET_BE16(pos), EAP_PAX_RAND_LEN); 00160 ret->ignore = TRUE; 00161 return NULL; 00162 } 00163 00164 pos += 2; 00165 left -= 2; 00166 os_memcpy(data->rand.r.x, pos, EAP_PAX_RAND_LEN); 00167 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: X (server rand)", 00168 data->rand.r.x, EAP_PAX_RAND_LEN); 00169 pos += EAP_PAX_RAND_LEN; 00170 left -= EAP_PAX_RAND_LEN; 00171 00172 if (left > 0) { 00173 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", 00174 pos, left); 00175 } 00176 00177 if (os_get_random(data->rand.r.y, EAP_PAX_RAND_LEN)) { 00178 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); 00179 ret->ignore = TRUE; 00180 return NULL; 00181 } 00182 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", 00183 data->rand.r.y, EAP_PAX_RAND_LEN); 00184 00185 if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e, 00186 data->mk, data->ck, data->ick) < 0) 00187 { 00188 ret->ignore = TRUE; 00189 return NULL; 00190 } 00191 00192 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-2 (sending)"); 00193 00194 plen = 2 + EAP_PAX_RAND_LEN + 2 + data->cid_len + 2 + EAP_PAX_MAC_LEN + 00195 EAP_PAX_ICV_LEN; 00196 resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_STD_2, plen); 00197 if (resp == NULL) 00198 return NULL; 00199 00200 wpabuf_put_be16(resp, EAP_PAX_RAND_LEN); 00201 wpabuf_put_data(resp, data->rand.r.y, EAP_PAX_RAND_LEN); 00202 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: B = Y (client rand)", 00203 data->rand.r.y, EAP_PAX_RAND_LEN); 00204 00205 wpabuf_put_be16(resp, data->cid_len); 00206 wpabuf_put_data(resp, data->cid, data->cid_len); 00207 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", 00208 (u8 *) data->cid, data->cid_len); 00209 00210 wpabuf_put_be16(resp, EAP_PAX_MAC_LEN); 00211 rpos = wpabuf_put(resp, EAP_PAX_MAC_LEN); 00212 eap_pax_mac(req->mac_id, data->ck, EAP_PAX_CK_LEN, 00213 data->rand.r.x, EAP_PAX_RAND_LEN, 00214 data->rand.r.y, EAP_PAX_RAND_LEN, 00215 (u8 *) data->cid, data->cid_len, rpos); 00216 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", 00217 rpos, EAP_PAX_MAC_LEN); 00218 00219 /* Optional ADE could be added here, if needed */ 00220 00221 rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN); 00222 eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN, 00223 wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, 00224 NULL, 0, NULL, 0, rpos); 00225 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); 00226 00227 data->state = PAX_STD_2_SENT; 00228 data->mac_id = req->mac_id; 00229 data->dh_group_id = req->dh_group_id; 00230 data->public_key_id = req->public_key_id; 00231 00232 return resp; 00233 } 00234 00235 00236 static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data, 00237 struct eap_method_ret *ret, u8 id, 00238 const struct eap_pax_hdr *req, 00239 size_t req_plen) 00240 { 00241 struct wpabuf *resp; 00242 u8 *rpos, mac[EAP_PAX_MAC_LEN]; 00243 const u8 *pos; 00244 size_t left; 00245 00246 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (received)"); 00247 00248 if (data->state != PAX_STD_2_SENT) { 00249 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 received in " 00250 "unexpected state (%d) - ignored", data->state); 00251 ret->ignore = TRUE; 00252 return NULL; 00253 } 00254 00255 if (req->flags & EAP_PAX_FLAGS_CE) { 00256 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with CE flag set - " 00257 "ignored"); 00258 ret->ignore = TRUE; 00259 return NULL; 00260 } 00261 00262 left = req_plen - sizeof(*req); 00263 00264 if (left < 2 + EAP_PAX_MAC_LEN) { 00265 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with too short " 00266 "payload"); 00267 ret->ignore = TRUE; 00268 return NULL; 00269 } 00270 00271 pos = (const u8 *) (req + 1); 00272 if (WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) { 00273 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with incorrect " 00274 "MAC_CK length %d (expected %d)", 00275 WPA_GET_BE16(pos), EAP_PAX_MAC_LEN); 00276 ret->ignore = TRUE; 00277 return NULL; 00278 } 00279 pos += 2; 00280 left -= 2; 00281 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", 00282 pos, EAP_PAX_MAC_LEN); 00283 eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, 00284 data->rand.r.y, EAP_PAX_RAND_LEN, 00285 (u8 *) data->cid, data->cid_len, NULL, 0, mac); 00286 if (os_memcmp(pos, mac, EAP_PAX_MAC_LEN) != 0) { 00287 wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) " 00288 "received"); 00289 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)", 00290 mac, EAP_PAX_MAC_LEN); 00291 ret->methodState = METHOD_DONE; 00292 ret->decision = DECISION_FAIL; 00293 return NULL; 00294 } 00295 00296 pos += EAP_PAX_MAC_LEN; 00297 left -= EAP_PAX_MAC_LEN; 00298 00299 if (left > 0) { 00300 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", 00301 pos, left); 00302 } 00303 00304 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX-ACK (sending)"); 00305 00306 resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_ACK, EAP_PAX_ICV_LEN); 00307 if (resp == NULL) 00308 return NULL; 00309 00310 /* Optional ADE could be added here, if needed */ 00311 00312 rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN); 00313 eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, 00314 wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, 00315 NULL, 0, NULL, 0, rpos); 00316 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); 00317 00318 data->state = PAX_DONE; 00319 ret->methodState = METHOD_DONE; 00320 ret->decision = DECISION_UNCOND_SUCC; 00321 ret->allowNotifications = FALSE; 00322 00323 return resp; 00324 } 00325 00326 00327 static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv, 00328 struct eap_method_ret *ret, 00329 const struct wpabuf *reqData) 00330 { 00331 struct eap_pax_data *data = priv; 00332 const struct eap_pax_hdr *req; 00333 struct wpabuf *resp; 00334 u8 icvbuf[EAP_PAX_ICV_LEN], id; 00335 const u8 *icv, *pos; 00336 size_t len; 00337 u16 flen, mlen; 00338 00339 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len); 00340 if (pos == NULL || len < EAP_PAX_ICV_LEN) { 00341 ret->ignore = TRUE; 00342 return NULL; 00343 } 00344 id = eap_get_id(reqData); 00345 req = (const struct eap_pax_hdr *) pos; 00346 flen = len - EAP_PAX_ICV_LEN; 00347 mlen = wpabuf_len(reqData) - EAP_PAX_ICV_LEN; 00348 00349 wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " 00350 "flags 0x%x mac_id 0x%x dh_group_id 0x%x " 00351 "public_key_id 0x%x", 00352 req->op_code, req->flags, req->mac_id, req->dh_group_id, 00353 req->public_key_id); 00354 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", 00355 pos, len - EAP_PAX_ICV_LEN); 00356 00357 if (data->state != PAX_INIT && data->mac_id != req->mac_id) { 00358 wpa_printf(MSG_INFO, "EAP-PAX: MAC ID changed during " 00359 "authentication (was 0x%d, is 0x%d)", 00360 data->mac_id, req->mac_id); 00361 ret->ignore = TRUE; 00362 return NULL; 00363 } 00364 00365 if (data->state != PAX_INIT && data->dh_group_id != req->dh_group_id) { 00366 wpa_printf(MSG_INFO, "EAP-PAX: DH Group ID changed during " 00367 "authentication (was 0x%d, is 0x%d)", 00368 data->dh_group_id, req->dh_group_id); 00369 ret->ignore = TRUE; 00370 return NULL; 00371 } 00372 00373 if (data->state != PAX_INIT && 00374 data->public_key_id != req->public_key_id) { 00375 wpa_printf(MSG_INFO, "EAP-PAX: Public Key ID changed during " 00376 "authentication (was 0x%d, is 0x%d)", 00377 data->public_key_id, req->public_key_id); 00378 ret->ignore = TRUE; 00379 return NULL; 00380 } 00381 00382 /* TODO: add support EAP_PAX_HMAC_SHA256_128 */ 00383 if (req->mac_id != EAP_PAX_MAC_HMAC_SHA1_128) { 00384 wpa_printf(MSG_INFO, "EAP-PAX: Unsupported MAC ID 0x%x", 00385 req->mac_id); 00386 ret->ignore = TRUE; 00387 return NULL; 00388 } 00389 00390 if (req->dh_group_id != EAP_PAX_DH_GROUP_NONE) { 00391 wpa_printf(MSG_INFO, "EAP-PAX: Unsupported DH Group ID 0x%x", 00392 req->dh_group_id); 00393 ret->ignore = TRUE; 00394 return NULL; 00395 } 00396 00397 if (req->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { 00398 wpa_printf(MSG_INFO, "EAP-PAX: Unsupported Public Key ID 0x%x", 00399 req->public_key_id); 00400 ret->ignore = TRUE; 00401 return NULL; 00402 } 00403 00404 if (req->flags & EAP_PAX_FLAGS_MF) { 00405 /* TODO: add support for reassembling fragments */ 00406 wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported - " 00407 "ignored packet"); 00408 ret->ignore = TRUE; 00409 return NULL; 00410 } 00411 00412 icv = pos + len - EAP_PAX_ICV_LEN; 00413 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); 00414 if (req->op_code == EAP_PAX_OP_STD_1) { 00415 eap_pax_mac(req->mac_id, (u8 *) "", 0, 00416 wpabuf_head(reqData), mlen, NULL, 0, NULL, 0, 00417 icvbuf); 00418 } else { 00419 eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN, 00420 wpabuf_head(reqData), mlen, NULL, 0, NULL, 0, 00421 icvbuf); 00422 } 00423 if (os_memcmp(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) { 00424 wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the " 00425 "message"); 00426 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV", 00427 icvbuf, EAP_PAX_ICV_LEN); 00428 ret->ignore = TRUE; 00429 return NULL; 00430 } 00431 00432 ret->ignore = FALSE; 00433 ret->methodState = METHOD_MAY_CONT; 00434 ret->decision = DECISION_FAIL; 00435 ret->allowNotifications = TRUE; 00436 00437 switch (req->op_code) { 00438 case EAP_PAX_OP_STD_1: 00439 resp = eap_pax_process_std_1(data, ret, id, req, flen); 00440 break; 00441 case EAP_PAX_OP_STD_3: 00442 resp = eap_pax_process_std_3(data, ret, id, req, flen); 00443 break; 00444 default: 00445 wpa_printf(MSG_DEBUG, "EAP-PAX: ignoring message with unknown " 00446 "op_code %d", req->op_code); 00447 ret->ignore = TRUE; 00448 return NULL; 00449 } 00450 00451 if (ret->methodState == METHOD_DONE) { 00452 ret->allowNotifications = FALSE; 00453 } 00454 00455 return resp; 00456 } 00457 00458 00459 static Boolean eap_pax_isKeyAvailable(struct eap_sm *sm, void *priv) 00460 { 00461 struct eap_pax_data *data = priv; 00462 return data->state == PAX_DONE; 00463 } 00464 00465 00466 static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) 00467 { 00468 struct eap_pax_data *data = priv; 00469 u8 *key; 00470 00471 if (data->state != PAX_DONE) 00472 return NULL; 00473 00474 key = os_malloc(EAP_MSK_LEN); 00475 if (key == NULL) 00476 return NULL; 00477 00478 *len = EAP_MSK_LEN; 00479 eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, 00480 "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, 00481 EAP_MSK_LEN, key); 00482 00483 return key; 00484 } 00485 00486 00487 static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 00488 { 00489 struct eap_pax_data *data = priv; 00490 u8 *key; 00491 00492 if (data->state != PAX_DONE) 00493 return NULL; 00494 00495 key = os_malloc(EAP_EMSK_LEN); 00496 if (key == NULL) 00497 return NULL; 00498 00499 *len = EAP_EMSK_LEN; 00500 eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, 00501 "Extended Master Session Key", 00502 data->rand.e, 2 * EAP_PAX_RAND_LEN, 00503 EAP_EMSK_LEN, key); 00504 00505 return key; 00506 } 00507 00508 00509 int eap_peer_pax_register(void) 00510 { 00511 struct eap_method *eap; 00512 int ret; 00513 00514 eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 00515 EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); 00516 if (eap == NULL) 00517 return -1; 00518 00519 eap->init = eap_pax_init; 00520 eap->deinit = eap_pax_deinit; 00521 eap->process = eap_pax_process; 00522 eap->isKeyAvailable = eap_pax_isKeyAvailable; 00523 eap->getKey = eap_pax_getKey; 00524 eap->get_emsk = eap_pax_get_emsk; 00525 00526 ret = eap_peer_method_register(eap); 00527 if (ret) 00528 eap_peer_method_free(eap); 00529 return ret; 00530 }