1 /* 2 * EAP peer method: EAP-PAX (RFC 4746) 3 * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9 #include "includes.h" 10 11 #include "common.h" 12 #include "crypto/random.h" 13 #include "eap_common/eap_pax_common.h" 14 #include "eap_i.h" 15 16 /* 17 * Note: only PAX_STD subprotocol is currently supported 18 * 19 * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite 20 * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and 21 * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits), 22 * RSAES-OAEP). 23 */ 24 25 struct eap_pax_data { 26 enum { PAX_INIT, PAX_STD_2_SENT, PAX_DONE } state; 27 u8 mac_id, dh_group_id, public_key_id; 28 union { 29 u8 e[2 * EAP_PAX_RAND_LEN]; 30 struct { 31 u8 x[EAP_PAX_RAND_LEN]; /* server rand */ 32 u8 y[EAP_PAX_RAND_LEN]; /* client rand */ 33 } r; 34 } rand; 35 char *cid; 36 size_t cid_len; 37 u8 ak[EAP_PAX_AK_LEN]; 38 u8 mk[EAP_PAX_MK_LEN]; 39 u8 ck[EAP_PAX_CK_LEN]; 40 u8 ick[EAP_PAX_ICK_LEN]; 41 u8 mid[EAP_PAX_MID_LEN]; 42 }; 43 44 45 static void eap_pax_deinit(struct eap_sm *sm, void *priv); 46 47 48 static void * eap_pax_init(struct eap_sm *sm) 49 { 50 struct eap_pax_data *data; 51 const u8 *identity, *password; 52 size_t identity_len, password_len; 53 54 identity = eap_get_config_identity(sm, &identity_len); 55 password = eap_get_config_password(sm, &password_len); 56 if (!identity || !password) { 57 wpa_printf(MSG_INFO, "EAP-PAX: CID (nai) or key (password) " 58 "not configured"); 59 return NULL; 60 } 61 62 if (password_len != EAP_PAX_AK_LEN) { 63 wpa_printf(MSG_INFO, "EAP-PAX: Invalid PSK length"); 64 return NULL; 65 } 66 67 data = os_zalloc(sizeof(*data)); 68 if (data == NULL) 69 return NULL; 70 data->state = PAX_INIT; 71 72 data->cid = os_memdup(identity, identity_len); 73 if (data->cid == NULL) { 74 eap_pax_deinit(sm, data); 75 return NULL; 76 } 77 data->cid_len = identity_len; 78 79 os_memcpy(data->ak, password, EAP_PAX_AK_LEN); 80 81 return data; 82 } 83 84 85 static void eap_pax_deinit(struct eap_sm *sm, void *priv) 86 { 87 struct eap_pax_data *data = priv; 88 os_free(data->cid); 89 bin_clear_free(data, sizeof(*data)); 90 } 91 92 93 static struct wpabuf * eap_pax_alloc_resp(const struct eap_pax_hdr *req, 94 u8 id, u8 op_code, size_t plen) 95 { 96 struct wpabuf *resp; 97 struct eap_pax_hdr *pax; 98 99 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, 100 sizeof(*pax) + plen, EAP_CODE_RESPONSE, id); 101 if (resp == NULL) 102 return NULL; 103 104 pax = wpabuf_put(resp, sizeof(*pax)); 105 pax->op_code = op_code; 106 pax->flags = 0; 107 pax->mac_id = req->mac_id; 108 pax->dh_group_id = req->dh_group_id; 109 pax->public_key_id = req->public_key_id; 110 111 return resp; 112 } 113 114 115 static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data, 116 struct eap_method_ret *ret, u8 id, 117 const struct eap_pax_hdr *req, 118 size_t req_plen) 119 { 120 struct wpabuf *resp; 121 const u8 *pos; 122 u8 *rpos; 123 size_t left, plen; 124 125 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (received)"); 126 127 if (data->state != PAX_INIT) { 128 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 received in " 129 "unexpected state (%d) - ignored", data->state); 130 ret->ignore = TRUE; 131 return NULL; 132 } 133 134 if (req->flags & EAP_PAX_FLAGS_CE) { 135 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with CE flag set - " 136 "ignored"); 137 ret->ignore = TRUE; 138 return NULL; 139 } 140 141 left = req_plen - sizeof(*req); 142 143 if (left < 2 + EAP_PAX_RAND_LEN) { 144 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with too short " 145 "payload"); 146 ret->ignore = TRUE; 147 return NULL; 148 } 149 150 pos = (const u8 *) (req + 1); 151 if (WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) { 152 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with incorrect A " 153 "length %d (expected %d)", 154 WPA_GET_BE16(pos), EAP_PAX_RAND_LEN); 155 ret->ignore = TRUE; 156 return NULL; 157 } 158 159 pos += 2; 160 left -= 2; 161 os_memcpy(data->rand.r.x, pos, EAP_PAX_RAND_LEN); 162 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: X (server rand)", 163 data->rand.r.x, EAP_PAX_RAND_LEN); 164 pos += EAP_PAX_RAND_LEN; 165 left -= EAP_PAX_RAND_LEN; 166 167 if (left > 0) { 168 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", 169 pos, left); 170 } 171 172 if (random_get_bytes(data->rand.r.y, EAP_PAX_RAND_LEN)) { 173 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); 174 ret->ignore = TRUE; 175 return NULL; 176 } 177 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", 178 data->rand.r.y, EAP_PAX_RAND_LEN); 179 180 if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e, 181 data->mk, data->ck, data->ick, 182 data->mid) < 0) { 183 ret->ignore = TRUE; 184 return NULL; 185 } 186 187 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-2 (sending)"); 188 189 plen = 2 + EAP_PAX_RAND_LEN + 2 + data->cid_len + 2 + EAP_PAX_MAC_LEN + 190 EAP_PAX_ICV_LEN; 191 resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_STD_2, plen); 192 if (resp == NULL) 193 return NULL; 194 195 wpabuf_put_be16(resp, EAP_PAX_RAND_LEN); 196 wpabuf_put_data(resp, data->rand.r.y, EAP_PAX_RAND_LEN); 197 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: B = Y (client rand)", 198 data->rand.r.y, EAP_PAX_RAND_LEN); 199 200 wpabuf_put_be16(resp, data->cid_len); 201 wpabuf_put_data(resp, data->cid, data->cid_len); 202 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", 203 (u8 *) data->cid, data->cid_len); 204 205 wpabuf_put_be16(resp, EAP_PAX_MAC_LEN); 206 rpos = wpabuf_put(resp, EAP_PAX_MAC_LEN); 207 eap_pax_mac(req->mac_id, data->ck, EAP_PAX_CK_LEN, 208 data->rand.r.x, EAP_PAX_RAND_LEN, 209 data->rand.r.y, EAP_PAX_RAND_LEN, 210 (u8 *) data->cid, data->cid_len, rpos); 211 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", 212 rpos, EAP_PAX_MAC_LEN); 213 214 /* Optional ADE could be added here, if needed */ 215 216 rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN); 217 eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN, 218 wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, 219 NULL, 0, NULL, 0, rpos); 220 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); 221 222 data->state = PAX_STD_2_SENT; 223 data->mac_id = req->mac_id; 224 data->dh_group_id = req->dh_group_id; 225 data->public_key_id = req->public_key_id; 226 227 return resp; 228 } 229 230 231 static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data, 232 struct eap_method_ret *ret, u8 id, 233 const struct eap_pax_hdr *req, 234 size_t req_plen) 235 { 236 struct wpabuf *resp; 237 u8 *rpos, mac[EAP_PAX_MAC_LEN]; 238 const u8 *pos; 239 size_t left; 240 241 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (received)"); 242 243 if (data->state != PAX_STD_2_SENT) { 244 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 received in " 245 "unexpected state (%d) - ignored", data->state); 246 ret->ignore = TRUE; 247 return NULL; 248 } 249 250 if (req->flags & EAP_PAX_FLAGS_CE) { 251 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with CE flag set - " 252 "ignored"); 253 ret->ignore = TRUE; 254 return NULL; 255 } 256 257 left = req_plen - sizeof(*req); 258 259 if (left < 2 + EAP_PAX_MAC_LEN) { 260 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with too short " 261 "payload"); 262 ret->ignore = TRUE; 263 return NULL; 264 } 265 266 pos = (const u8 *) (req + 1); 267 if (WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) { 268 wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with incorrect " 269 "MAC_CK length %d (expected %d)", 270 WPA_GET_BE16(pos), EAP_PAX_MAC_LEN); 271 ret->ignore = TRUE; 272 return NULL; 273 } 274 pos += 2; 275 left -= 2; 276 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", 277 pos, EAP_PAX_MAC_LEN); 278 if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, 279 data->rand.r.y, EAP_PAX_RAND_LEN, 280 (u8 *) data->cid, data->cid_len, NULL, 0, mac) < 0) { 281 wpa_printf(MSG_INFO, 282 "EAP-PAX: Could not derive MAC_CK(B, CID)"); 283 ret->methodState = METHOD_DONE; 284 ret->decision = DECISION_FAIL; 285 return NULL; 286 } 287 288 if (os_memcmp_const(pos, mac, EAP_PAX_MAC_LEN) != 0) { 289 wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) " 290 "received"); 291 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)", 292 mac, EAP_PAX_MAC_LEN); 293 ret->methodState = METHOD_DONE; 294 ret->decision = DECISION_FAIL; 295 return NULL; 296 } 297 298 pos += EAP_PAX_MAC_LEN; 299 left -= EAP_PAX_MAC_LEN; 300 301 if (left > 0) { 302 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", 303 pos, left); 304 } 305 306 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX-ACK (sending)"); 307 308 resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_ACK, EAP_PAX_ICV_LEN); 309 if (resp == NULL) 310 return NULL; 311 312 /* Optional ADE could be added here, if needed */ 313 314 rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN); 315 if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, 316 wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, 317 NULL, 0, NULL, 0, rpos) < 0) { 318 wpabuf_free(resp); 319 return NULL; 320 } 321 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); 322 323 data->state = PAX_DONE; 324 ret->methodState = METHOD_DONE; 325 ret->decision = DECISION_UNCOND_SUCC; 326 ret->allowNotifications = FALSE; 327 328 return resp; 329 } 330 331 332 static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv, 333 struct eap_method_ret *ret, 334 const struct wpabuf *reqData) 335 { 336 struct eap_pax_data *data = priv; 337 const struct eap_pax_hdr *req; 338 struct wpabuf *resp; 339 u8 icvbuf[EAP_PAX_ICV_LEN], id; 340 const u8 *icv, *pos; 341 size_t len; 342 u16 flen, mlen; 343 344 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len); 345 if (pos == NULL || len < sizeof(*req) + EAP_PAX_ICV_LEN) { 346 ret->ignore = TRUE; 347 return NULL; 348 } 349 id = eap_get_id(reqData); 350 req = (const struct eap_pax_hdr *) pos; 351 flen = len - EAP_PAX_ICV_LEN; 352 mlen = wpabuf_len(reqData) - EAP_PAX_ICV_LEN; 353 354 wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " 355 "flags 0x%x mac_id 0x%x dh_group_id 0x%x " 356 "public_key_id 0x%x", 357 req->op_code, req->flags, req->mac_id, req->dh_group_id, 358 req->public_key_id); 359 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", 360 pos, len - EAP_PAX_ICV_LEN); 361 362 if (data->state != PAX_INIT && data->mac_id != req->mac_id) { 363 wpa_printf(MSG_INFO, "EAP-PAX: MAC ID changed during " 364 "authentication (was 0x%d, is 0x%d)", 365 data->mac_id, req->mac_id); 366 ret->ignore = TRUE; 367 return NULL; 368 } 369 370 if (data->state != PAX_INIT && data->dh_group_id != req->dh_group_id) { 371 wpa_printf(MSG_INFO, "EAP-PAX: DH Group ID changed during " 372 "authentication (was 0x%d, is 0x%d)", 373 data->dh_group_id, req->dh_group_id); 374 ret->ignore = TRUE; 375 return NULL; 376 } 377 378 if (data->state != PAX_INIT && 379 data->public_key_id != req->public_key_id) { 380 wpa_printf(MSG_INFO, "EAP-PAX: Public Key ID changed during " 381 "authentication (was 0x%d, is 0x%d)", 382 data->public_key_id, req->public_key_id); 383 ret->ignore = TRUE; 384 return NULL; 385 } 386 387 /* TODO: add support EAP_PAX_HMAC_SHA256_128 */ 388 if (req->mac_id != EAP_PAX_MAC_HMAC_SHA1_128) { 389 wpa_printf(MSG_INFO, "EAP-PAX: Unsupported MAC ID 0x%x", 390 req->mac_id); 391 ret->ignore = TRUE; 392 return NULL; 393 } 394 395 if (req->dh_group_id != EAP_PAX_DH_GROUP_NONE) { 396 wpa_printf(MSG_INFO, "EAP-PAX: Unsupported DH Group ID 0x%x", 397 req->dh_group_id); 398 ret->ignore = TRUE; 399 return NULL; 400 } 401 402 if (req->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { 403 wpa_printf(MSG_INFO, "EAP-PAX: Unsupported Public Key ID 0x%x", 404 req->public_key_id); 405 ret->ignore = TRUE; 406 return NULL; 407 } 408 409 if (req->flags & EAP_PAX_FLAGS_MF) { 410 /* TODO: add support for reassembling fragments */ 411 wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported - " 412 "ignored packet"); 413 ret->ignore = TRUE; 414 return NULL; 415 } 416 417 icv = pos + len - EAP_PAX_ICV_LEN; 418 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); 419 if (req->op_code == EAP_PAX_OP_STD_1) { 420 eap_pax_mac(req->mac_id, (u8 *) "", 0, 421 wpabuf_head(reqData), mlen, NULL, 0, NULL, 0, 422 icvbuf); 423 } else { 424 eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN, 425 wpabuf_head(reqData), mlen, NULL, 0, NULL, 0, 426 icvbuf); 427 } 428 if (os_memcmp_const(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) { 429 wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the " 430 "message"); 431 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV", 432 icvbuf, EAP_PAX_ICV_LEN); 433 ret->ignore = TRUE; 434 return NULL; 435 } 436 437 ret->ignore = FALSE; 438 ret->methodState = METHOD_MAY_CONT; 439 ret->decision = DECISION_FAIL; 440 ret->allowNotifications = TRUE; 441 442 switch (req->op_code) { 443 case EAP_PAX_OP_STD_1: 444 resp = eap_pax_process_std_1(data, ret, id, req, flen); 445 break; 446 case EAP_PAX_OP_STD_3: 447 resp = eap_pax_process_std_3(data, ret, id, req, flen); 448 break; 449 default: 450 wpa_printf(MSG_DEBUG, "EAP-PAX: ignoring message with unknown " 451 "op_code %d", req->op_code); 452 ret->ignore = TRUE; 453 return NULL; 454 } 455 456 if (ret->methodState == METHOD_DONE) { 457 ret->allowNotifications = FALSE; 458 } 459 460 return resp; 461 } 462 463 464 static Boolean eap_pax_isKeyAvailable(struct eap_sm *sm, void *priv) 465 { 466 struct eap_pax_data *data = priv; 467 return data->state == PAX_DONE; 468 } 469 470 471 static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) 472 { 473 struct eap_pax_data *data = priv; 474 u8 *key; 475 476 if (data->state != PAX_DONE) 477 return NULL; 478 479 key = os_malloc(EAP_MSK_LEN); 480 if (key == NULL) 481 return NULL; 482 483 *len = EAP_MSK_LEN; 484 if (eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, 485 "Master Session Key", 486 data->rand.e, 2 * EAP_PAX_RAND_LEN, 487 EAP_MSK_LEN, key) < 0) { 488 os_free(key); 489 return NULL; 490 } 491 492 return key; 493 } 494 495 496 static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 497 { 498 struct eap_pax_data *data = priv; 499 u8 *key; 500 501 if (data->state != PAX_DONE) 502 return NULL; 503 504 key = os_malloc(EAP_EMSK_LEN); 505 if (key == NULL) 506 return NULL; 507 508 *len = EAP_EMSK_LEN; 509 if (eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, 510 "Extended Master Session Key", 511 data->rand.e, 2 * EAP_PAX_RAND_LEN, 512 EAP_EMSK_LEN, key) < 0) { 513 os_free(key); 514 return NULL; 515 } 516 517 return key; 518 } 519 520 521 static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len) 522 { 523 struct eap_pax_data *data = priv; 524 u8 *sid; 525 526 if (data->state != PAX_DONE) 527 return NULL; 528 529 sid = os_malloc(1 + EAP_PAX_MID_LEN); 530 if (sid == NULL) 531 return NULL; 532 533 *len = 1 + EAP_PAX_MID_LEN; 534 sid[0] = EAP_TYPE_PAX; 535 os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN); 536 537 return sid; 538 } 539 540 541 int eap_peer_pax_register(void) 542 { 543 struct eap_method *eap; 544 545 eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 546 EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); 547 if (eap == NULL) 548 return -1; 549 550 eap->init = eap_pax_init; 551 eap->deinit = eap_pax_deinit; 552 eap->process = eap_pax_process; 553 eap->isKeyAvailable = eap_pax_isKeyAvailable; 554 eap->getKey = eap_pax_getKey; 555 eap->get_emsk = eap_pax_get_emsk; 556 eap->getSessionId = eap_pax_get_session_id; 557 558 return eap_peer_method_register(eap); 559 } 560