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