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