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