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