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