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