1/* 2 * hostapd / EAP-PAX (RFC 4746) server 3 * Copyright (c) 2005-2007, 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_server/eap_i.h" 14#include "eap_common/eap_pax_common.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 25struct eap_pax_data { 26 enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state; 27 u8 mac_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 u8 ak[EAP_PAX_AK_LEN]; 36 u8 mk[EAP_PAX_MK_LEN]; 37 u8 ck[EAP_PAX_CK_LEN]; 38 u8 ick[EAP_PAX_ICK_LEN]; 39 int keys_set; 40 char *cid; 41 size_t cid_len; 42}; 43 44 45static void * eap_pax_init(struct eap_sm *sm) 46{ 47 struct eap_pax_data *data; 48 49 data = os_zalloc(sizeof(*data)); 50 if (data == NULL) 51 return NULL; 52 data->state = PAX_STD_1; 53 /* 54 * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is 55 * supported 56 */ 57 data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128; 58 59 return data; 60} 61 62 63static void eap_pax_reset(struct eap_sm *sm, void *priv) 64{ 65 struct eap_pax_data *data = priv; 66 os_free(data->cid); 67 os_free(data); 68} 69 70 71static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm, 72 struct eap_pax_data *data, u8 id) 73{ 74 struct wpabuf *req; 75 struct eap_pax_hdr *pax; 76 u8 *pos; 77 78 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)"); 79 80 if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) { 81 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); 82 data->state = FAILURE; 83 return NULL; 84 } 85 86 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, 87 sizeof(*pax) + 2 + EAP_PAX_RAND_LEN + 88 EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id); 89 if (req == NULL) { 90 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " 91 "request"); 92 data->state = FAILURE; 93 return NULL; 94 } 95 96 pax = wpabuf_put(req, sizeof(*pax)); 97 pax->op_code = EAP_PAX_OP_STD_1; 98 pax->flags = 0; 99 pax->mac_id = data->mac_id; 100 pax->dh_group_id = EAP_PAX_DH_GROUP_NONE; 101 pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; 102 103 wpabuf_put_be16(req, EAP_PAX_RAND_LEN); 104 wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN); 105 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)", 106 data->rand.r.x, EAP_PAX_RAND_LEN); 107 108 pos = wpabuf_put(req, EAP_PAX_MAC_LEN); 109 eap_pax_mac(data->mac_id, (u8 *) "", 0, 110 wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, 111 NULL, 0, NULL, 0, pos); 112 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); 113 114 return req; 115} 116 117 118static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm, 119 struct eap_pax_data *data, u8 id) 120{ 121 struct wpabuf *req; 122 struct eap_pax_hdr *pax; 123 u8 *pos; 124 125 wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)"); 126 127 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, 128 sizeof(*pax) + 2 + EAP_PAX_MAC_LEN + 129 EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id); 130 if (req == NULL) { 131 wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " 132 "request"); 133 data->state = FAILURE; 134 return NULL; 135 } 136 137 pax = wpabuf_put(req, sizeof(*pax)); 138 pax->op_code = EAP_PAX_OP_STD_3; 139 pax->flags = 0; 140 pax->mac_id = data->mac_id; 141 pax->dh_group_id = EAP_PAX_DH_GROUP_NONE; 142 pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; 143 144 wpabuf_put_be16(req, EAP_PAX_MAC_LEN); 145 pos = wpabuf_put(req, EAP_PAX_MAC_LEN); 146 eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, 147 data->rand.r.y, EAP_PAX_RAND_LEN, 148 (u8 *) data->cid, data->cid_len, NULL, 0, pos); 149 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", 150 pos, EAP_PAX_MAC_LEN); 151 pos += EAP_PAX_MAC_LEN; 152 153 /* Optional ADE could be added here, if needed */ 154 155 pos = wpabuf_put(req, EAP_PAX_MAC_LEN); 156 eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, 157 wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, 158 NULL, 0, NULL, 0, pos); 159 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); 160 161 return req; 162} 163 164 165static struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id) 166{ 167 struct eap_pax_data *data = priv; 168 169 switch (data->state) { 170 case PAX_STD_1: 171 return eap_pax_build_std_1(sm, data, id); 172 case PAX_STD_3: 173 return eap_pax_build_std_3(sm, data, id); 174 default: 175 wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq", 176 data->state); 177 break; 178 } 179 return NULL; 180} 181 182 183static Boolean eap_pax_check(struct eap_sm *sm, void *priv, 184 struct wpabuf *respData) 185{ 186 struct eap_pax_data *data = priv; 187 struct eap_pax_hdr *resp; 188 const u8 *pos; 189 size_t len, mlen; 190 u8 icvbuf[EAP_PAX_ICV_LEN], *icv; 191 192 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); 193 if (pos == NULL || len < sizeof(*resp)) { 194 wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame"); 195 return TRUE; 196 } 197 198 mlen = sizeof(struct eap_hdr) + 1 + len; 199 resp = (struct eap_pax_hdr *) pos; 200 201 wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " 202 "flags 0x%x mac_id 0x%x dh_group_id 0x%x " 203 "public_key_id 0x%x", 204 resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id, 205 resp->public_key_id); 206 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", 207 (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN); 208 209 if (data->state == PAX_STD_1 && 210 resp->op_code != EAP_PAX_OP_STD_2) { 211 wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - " 212 "ignore op %d", resp->op_code); 213 return TRUE; 214 } 215 216 if (data->state == PAX_STD_3 && 217 resp->op_code != EAP_PAX_OP_ACK) { 218 wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - " 219 "ignore op %d", resp->op_code); 220 return TRUE; 221 } 222 223 if (resp->op_code != EAP_PAX_OP_STD_2 && 224 resp->op_code != EAP_PAX_OP_ACK) { 225 wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x", 226 resp->op_code); 227 } 228 229 if (data->mac_id != resp->mac_id) { 230 wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, " 231 "received 0x%x", data->mac_id, resp->mac_id); 232 return TRUE; 233 } 234 235 if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) { 236 wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, " 237 "received 0x%x", EAP_PAX_DH_GROUP_NONE, 238 resp->dh_group_id); 239 return TRUE; 240 } 241 242 if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { 243 wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, " 244 "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE, 245 resp->public_key_id); 246 return TRUE; 247 } 248 249 if (resp->flags & EAP_PAX_FLAGS_MF) { 250 /* TODO: add support for reassembling fragments */ 251 wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported"); 252 return TRUE; 253 } 254 255 if (resp->flags & EAP_PAX_FLAGS_CE) { 256 wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag"); 257 return TRUE; 258 } 259 260 if (data->keys_set) { 261 if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) { 262 wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet"); 263 return TRUE; 264 } 265 icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN; 266 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); 267 eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, 268 wpabuf_mhead(respData), 269 wpabuf_len(respData) - EAP_PAX_ICV_LEN, 270 NULL, 0, NULL, 0, icvbuf); 271 if (os_memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { 272 wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV"); 273 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", 274 icvbuf, EAP_PAX_ICV_LEN); 275 return TRUE; 276 } 277 } 278 279 return FALSE; 280} 281 282 283static void eap_pax_process_std_2(struct eap_sm *sm, 284 struct eap_pax_data *data, 285 struct wpabuf *respData) 286{ 287 struct eap_pax_hdr *resp; 288 u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN]; 289 const u8 *pos; 290 size_t len, left; 291 int i; 292 293 if (data->state != PAX_STD_1) 294 return; 295 296 wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2"); 297 298 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); 299 if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) 300 return; 301 302 resp = (struct eap_pax_hdr *) pos; 303 pos = (u8 *) (resp + 1); 304 left = len - sizeof(*resp); 305 306 if (left < 2 + EAP_PAX_RAND_LEN || 307 WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) { 308 wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)"); 309 return; 310 } 311 pos += 2; 312 left -= 2; 313 os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN); 314 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", 315 data->rand.r.y, EAP_PAX_RAND_LEN); 316 pos += EAP_PAX_RAND_LEN; 317 left -= EAP_PAX_RAND_LEN; 318 319 if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) { 320 wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)"); 321 return; 322 } 323 data->cid_len = WPA_GET_BE16(pos); 324 os_free(data->cid); 325 data->cid = os_malloc(data->cid_len); 326 if (data->cid == NULL) { 327 wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for " 328 "CID"); 329 return; 330 } 331 os_memcpy(data->cid, pos + 2, data->cid_len); 332 pos += 2 + data->cid_len; 333 left -= 2 + data->cid_len; 334 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", 335 (u8 *) data->cid, data->cid_len); 336 337 if (left < 2 + EAP_PAX_MAC_LEN || 338 WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) { 339 wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)"); 340 return; 341 } 342 pos += 2; 343 left -= 2; 344 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", 345 pos, EAP_PAX_MAC_LEN); 346 347 if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) { 348 wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID", 349 (u8 *) data->cid, data->cid_len); 350 data->state = FAILURE; 351 return; 352 } 353 354 for (i = 0; 355 i < EAP_MAX_METHODS && 356 (sm->user->methods[i].vendor != EAP_VENDOR_IETF || 357 sm->user->methods[i].method != EAP_TYPE_NONE); 358 i++) { 359 if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && 360 sm->user->methods[i].method == EAP_TYPE_PAX) 361 break; 362 } 363 364 if (i >= EAP_MAX_METHODS || 365 sm->user->methods[i].vendor != EAP_VENDOR_IETF || 366 sm->user->methods[i].method != EAP_TYPE_PAX) { 367 wpa_hexdump_ascii(MSG_DEBUG, 368 "EAP-PAX: EAP-PAX not enabled for CID", 369 (u8 *) data->cid, data->cid_len); 370 data->state = FAILURE; 371 return; 372 } 373 374 if (sm->user->password == NULL || 375 sm->user->password_len != EAP_PAX_AK_LEN) { 376 wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in " 377 "user database for CID", 378 (u8 *) data->cid, data->cid_len); 379 data->state = FAILURE; 380 return; 381 } 382 os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN); 383 384 if (eap_pax_initial_key_derivation(data->mac_id, data->ak, 385 data->rand.e, data->mk, data->ck, 386 data->ick) < 0) { 387 wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial " 388 "key derivation"); 389 data->state = FAILURE; 390 return; 391 } 392 data->keys_set = 1; 393 394 eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, 395 data->rand.r.x, EAP_PAX_RAND_LEN, 396 data->rand.r.y, EAP_PAX_RAND_LEN, 397 (u8 *) data->cid, data->cid_len, mac); 398 if (os_memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) { 399 wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in " 400 "PAX_STD-2"); 401 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)", 402 mac, EAP_PAX_MAC_LEN); 403 data->state = FAILURE; 404 return; 405 } 406 407 pos += EAP_PAX_MAC_LEN; 408 left -= EAP_PAX_MAC_LEN; 409 410 if (left < EAP_PAX_ICV_LEN) { 411 wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in " 412 "PAX_STD-2", (unsigned long) left); 413 return; 414 } 415 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); 416 eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, 417 wpabuf_head(respData), 418 wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, 419 icvbuf); 420 if (os_memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { 421 wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2"); 422 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", 423 icvbuf, EAP_PAX_ICV_LEN); 424 return; 425 } 426 pos += EAP_PAX_ICV_LEN; 427 left -= EAP_PAX_ICV_LEN; 428 429 if (left > 0) { 430 wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", 431 pos, left); 432 } 433 434 data->state = PAX_STD_3; 435} 436 437 438static void eap_pax_process_ack(struct eap_sm *sm, 439 struct eap_pax_data *data, 440 struct wpabuf *respData) 441{ 442 if (data->state != PAX_STD_3) 443 return; 444 445 wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication " 446 "completed successfully"); 447 data->state = SUCCESS; 448} 449 450 451static void eap_pax_process(struct eap_sm *sm, void *priv, 452 struct wpabuf *respData) 453{ 454 struct eap_pax_data *data = priv; 455 struct eap_pax_hdr *resp; 456 const u8 *pos; 457 size_t len; 458 459 if (sm->user == NULL || sm->user->password == NULL) { 460 wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not " 461 "configured"); 462 data->state = FAILURE; 463 return; 464 } 465 466 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); 467 if (pos == NULL || len < sizeof(*resp)) 468 return; 469 470 resp = (struct eap_pax_hdr *) pos; 471 472 switch (resp->op_code) { 473 case EAP_PAX_OP_STD_2: 474 eap_pax_process_std_2(sm, data, respData); 475 break; 476 case EAP_PAX_OP_ACK: 477 eap_pax_process_ack(sm, data, respData); 478 break; 479 } 480} 481 482 483static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv) 484{ 485 struct eap_pax_data *data = priv; 486 return data->state == SUCCESS || data->state == FAILURE; 487} 488 489 490static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) 491{ 492 struct eap_pax_data *data = priv; 493 u8 *key; 494 495 if (data->state != SUCCESS) 496 return NULL; 497 498 key = os_malloc(EAP_MSK_LEN); 499 if (key == NULL) 500 return NULL; 501 502 *len = EAP_MSK_LEN; 503 eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, 504 "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, 505 EAP_MSK_LEN, key); 506 507 return key; 508} 509 510 511static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 512{ 513 struct eap_pax_data *data = priv; 514 u8 *key; 515 516 if (data->state != SUCCESS) 517 return NULL; 518 519 key = os_malloc(EAP_EMSK_LEN); 520 if (key == NULL) 521 return NULL; 522 523 *len = EAP_EMSK_LEN; 524 eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, 525 "Extended Master Session Key", 526 data->rand.e, 2 * EAP_PAX_RAND_LEN, 527 EAP_EMSK_LEN, key); 528 529 return key; 530} 531 532 533static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv) 534{ 535 struct eap_pax_data *data = priv; 536 return data->state == SUCCESS; 537} 538 539 540int eap_server_pax_register(void) 541{ 542 struct eap_method *eap; 543 int ret; 544 545 eap = eap_server_method_alloc(EAP_SERVER_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->reset = eap_pax_reset; 552 eap->buildReq = eap_pax_buildReq; 553 eap->check = eap_pax_check; 554 eap->process = eap_pax_process; 555 eap->isDone = eap_pax_isDone; 556 eap->getKey = eap_pax_getKey; 557 eap->isSuccess = eap_pax_isSuccess; 558 eap->get_emsk = eap_pax_get_emsk; 559 560 ret = eap_server_method_register(eap); 561 if (ret) 562 eap_server_method_free(eap); 563 return ret; 564} 565