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