1 /* 2 * EAP peer method: EAP-SAKE (RFC 4763) 3 * Copyright (c) 2006-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_sake_common.h" 20 21 struct eap_sake_data { 22 enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state; 23 u8 root_secret_a[EAP_SAKE_ROOT_SECRET_LEN]; 24 u8 root_secret_b[EAP_SAKE_ROOT_SECRET_LEN]; 25 u8 rand_s[EAP_SAKE_RAND_LEN]; 26 u8 rand_p[EAP_SAKE_RAND_LEN]; 27 struct { 28 u8 auth[EAP_SAKE_TEK_AUTH_LEN]; 29 u8 cipher[EAP_SAKE_TEK_CIPHER_LEN]; 30 } tek; 31 u8 msk[EAP_MSK_LEN]; 32 u8 emsk[EAP_EMSK_LEN]; 33 u8 session_id; 34 int session_id_set; 35 u8 *peerid; 36 size_t peerid_len; 37 u8 *serverid; 38 size_t serverid_len; 39 }; 40 41 42 static const char * eap_sake_state_txt(int state) 43 { 44 switch (state) { 45 case IDENTITY: 46 return "IDENTITY"; 47 case CHALLENGE: 48 return "CHALLENGE"; 49 case CONFIRM: 50 return "CONFIRM"; 51 case SUCCESS: 52 return "SUCCESS"; 53 case FAILURE: 54 return "FAILURE"; 55 default: 56 return "?"; 57 } 58 } 59 60 61 static void eap_sake_state(struct eap_sake_data *data, int state) 62 { 63 wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s", 64 eap_sake_state_txt(data->state), 65 eap_sake_state_txt(state)); 66 data->state = state; 67 } 68 69 70 static void eap_sake_deinit(struct eap_sm *sm, void *priv); 71 72 73 static void * eap_sake_init(struct eap_sm *sm) 74 { 75 struct eap_sake_data *data; 76 const u8 *identity, *password; 77 size_t identity_len, password_len; 78 79 password = eap_get_config_password(sm, &password_len); 80 if (!password || password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) { 81 wpa_printf(MSG_INFO, "EAP-SAKE: No key of correct length " 82 "configured"); 83 return NULL; 84 } 85 86 data = os_zalloc(sizeof(*data)); 87 if (data == NULL) 88 return NULL; 89 data->state = IDENTITY; 90 91 identity = eap_get_config_identity(sm, &identity_len); 92 if (identity) { 93 data->peerid = os_malloc(identity_len); 94 if (data->peerid == NULL) { 95 eap_sake_deinit(sm, data); 96 return NULL; 97 } 98 os_memcpy(data->peerid, identity, identity_len); 99 data->peerid_len = identity_len; 100 } 101 102 os_memcpy(data->root_secret_a, password, EAP_SAKE_ROOT_SECRET_LEN); 103 os_memcpy(data->root_secret_b, 104 password + EAP_SAKE_ROOT_SECRET_LEN, 105 EAP_SAKE_ROOT_SECRET_LEN); 106 107 return data; 108 } 109 110 111 static void eap_sake_deinit(struct eap_sm *sm, void *priv) 112 { 113 struct eap_sake_data *data = priv; 114 os_free(data->serverid); 115 os_free(data->peerid); 116 os_free(data); 117 } 118 119 120 static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data, 121 int id, size_t length, u8 subtype) 122 { 123 struct eap_sake_hdr *sake; 124 struct wpabuf *msg; 125 size_t plen; 126 127 plen = length + sizeof(struct eap_sake_hdr); 128 129 msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen, 130 EAP_CODE_RESPONSE, id); 131 if (msg == NULL) { 132 wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory " 133 "request"); 134 return NULL; 135 } 136 137 sake = wpabuf_put(msg, sizeof(*sake)); 138 sake->version = EAP_SAKE_VERSION; 139 sake->session_id = data->session_id; 140 sake->subtype = subtype; 141 142 return msg; 143 } 144 145 146 static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm, 147 struct eap_sake_data *data, 148 struct eap_method_ret *ret, 149 const struct wpabuf *reqData, 150 const u8 *payload, 151 size_t payload_len) 152 { 153 struct eap_sake_parse_attr attr; 154 struct wpabuf *resp; 155 156 if (data->state != IDENTITY) { 157 ret->ignore = TRUE; 158 return NULL; 159 } 160 161 wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Identity"); 162 163 if (eap_sake_parse_attributes(payload, payload_len, &attr)) 164 return NULL; 165 166 if (!attr.perm_id_req && !attr.any_id_req) { 167 wpa_printf(MSG_INFO, "EAP-SAKE: No AT_PERM_ID_REQ or " 168 "AT_ANY_ID_REQ in Request/Identity"); 169 return NULL; 170 } 171 172 wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity"); 173 174 resp = eap_sake_build_msg(data, eap_get_id(reqData), 175 2 + data->peerid_len, 176 EAP_SAKE_SUBTYPE_IDENTITY); 177 if (resp == NULL) 178 return NULL; 179 180 wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID"); 181 eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID, 182 data->peerid, data->peerid_len); 183 184 eap_sake_state(data, CHALLENGE); 185 186 return resp; 187 } 188 189 190 static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, 191 struct eap_sake_data *data, 192 struct eap_method_ret *ret, 193 const struct wpabuf *reqData, 194 const u8 *payload, 195 size_t payload_len) 196 { 197 struct eap_sake_parse_attr attr; 198 struct wpabuf *resp; 199 u8 *rpos; 200 size_t rlen; 201 202 if (data->state != IDENTITY && data->state != CHALLENGE) { 203 wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge received " 204 "in unexpected state (%d)", data->state); 205 ret->ignore = TRUE; 206 return NULL; 207 } 208 if (data->state == IDENTITY) 209 eap_sake_state(data, CHALLENGE); 210 211 wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Challenge"); 212 213 if (eap_sake_parse_attributes(payload, payload_len, &attr)) 214 return NULL; 215 216 if (!attr.rand_s) { 217 wpa_printf(MSG_INFO, "EAP-SAKE: Request/Challenge did not " 218 "include AT_RAND_S"); 219 return NULL; 220 } 221 222 os_memcpy(data->rand_s, attr.rand_s, EAP_SAKE_RAND_LEN); 223 wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)", 224 data->rand_s, EAP_SAKE_RAND_LEN); 225 226 if (os_get_random(data->rand_p, EAP_SAKE_RAND_LEN)) { 227 wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); 228 return NULL; 229 } 230 wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_P (peer rand)", 231 data->rand_p, EAP_SAKE_RAND_LEN); 232 233 os_free(data->serverid); 234 data->serverid = NULL; 235 data->serverid_len = 0; 236 if (attr.serverid) { 237 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID", 238 attr.serverid, attr.serverid_len); 239 data->serverid = os_malloc(attr.serverid_len); 240 if (data->serverid == NULL) 241 return NULL; 242 os_memcpy(data->serverid, attr.serverid, attr.serverid_len); 243 data->serverid_len = attr.serverid_len; 244 } 245 246 eap_sake_derive_keys(data->root_secret_a, data->root_secret_b, 247 data->rand_s, data->rand_p, 248 (u8 *) &data->tek, data->msk, data->emsk); 249 250 wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge"); 251 252 rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN; 253 if (data->peerid) 254 rlen += 2 + data->peerid_len; 255 resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen, 256 EAP_SAKE_SUBTYPE_CHALLENGE); 257 if (resp == NULL) 258 return NULL; 259 260 wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_P"); 261 eap_sake_add_attr(resp, EAP_SAKE_AT_RAND_P, 262 data->rand_p, EAP_SAKE_RAND_LEN); 263 264 if (data->peerid) { 265 wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID"); 266 eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID, 267 data->peerid, data->peerid_len); 268 } 269 270 wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P"); 271 wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P); 272 wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN); 273 rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN); 274 if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, 275 data->serverid, data->serverid_len, 276 data->peerid, data->peerid_len, 1, 277 wpabuf_head(resp), wpabuf_len(resp), rpos, 278 rpos)) { 279 wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); 280 wpabuf_free(resp); 281 return NULL; 282 } 283 284 eap_sake_state(data, CONFIRM); 285 286 return resp; 287 } 288 289 290 static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm, 291 struct eap_sake_data *data, 292 struct eap_method_ret *ret, 293 const struct wpabuf *reqData, 294 const u8 *payload, 295 size_t payload_len) 296 { 297 struct eap_sake_parse_attr attr; 298 u8 mic_s[EAP_SAKE_MIC_LEN]; 299 struct wpabuf *resp; 300 u8 *rpos; 301 302 if (data->state != CONFIRM) { 303 ret->ignore = TRUE; 304 return NULL; 305 } 306 307 wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Confirm"); 308 309 if (eap_sake_parse_attributes(payload, payload_len, &attr)) 310 return NULL; 311 312 if (!attr.mic_s) { 313 wpa_printf(MSG_INFO, "EAP-SAKE: Request/Confirm did not " 314 "include AT_MIC_S"); 315 return NULL; 316 } 317 318 eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, 319 data->serverid, data->serverid_len, 320 data->peerid, data->peerid_len, 0, 321 wpabuf_head(reqData), wpabuf_len(reqData), 322 attr.mic_s, mic_s); 323 if (os_memcmp(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) { 324 wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S"); 325 eap_sake_state(data, FAILURE); 326 ret->methodState = METHOD_DONE; 327 ret->decision = DECISION_FAIL; 328 ret->allowNotifications = FALSE; 329 wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending " 330 "Response/Auth-Reject"); 331 return eap_sake_build_msg(data, eap_get_id(reqData), 0, 332 EAP_SAKE_SUBTYPE_AUTH_REJECT); 333 } 334 335 wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm"); 336 337 resp = eap_sake_build_msg(data, eap_get_id(reqData), 338 2 + EAP_SAKE_MIC_LEN, 339 EAP_SAKE_SUBTYPE_CONFIRM); 340 if (resp == NULL) 341 return NULL; 342 343 wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P"); 344 wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P); 345 wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN); 346 rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN); 347 if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, 348 data->serverid, data->serverid_len, 349 data->peerid, data->peerid_len, 1, 350 wpabuf_head(resp), wpabuf_len(resp), rpos, 351 rpos)) { 352 wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); 353 wpabuf_free(resp); 354 return NULL; 355 } 356 357 eap_sake_state(data, SUCCESS); 358 ret->methodState = METHOD_DONE; 359 ret->decision = DECISION_UNCOND_SUCC; 360 ret->allowNotifications = FALSE; 361 362 return resp; 363 } 364 365 366 static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv, 367 struct eap_method_ret *ret, 368 const struct wpabuf *reqData) 369 { 370 struct eap_sake_data *data = priv; 371 const struct eap_sake_hdr *req; 372 struct wpabuf *resp; 373 const u8 *pos, *end; 374 size_t len; 375 u8 subtype, session_id; 376 377 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len); 378 if (pos == NULL || len < sizeof(struct eap_sake_hdr)) { 379 ret->ignore = TRUE; 380 return NULL; 381 } 382 383 req = (const struct eap_sake_hdr *) pos; 384 end = pos + len; 385 subtype = req->subtype; 386 session_id = req->session_id; 387 pos = (const u8 *) (req + 1); 388 389 wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype %d " 390 "session_id %d", subtype, session_id); 391 wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes", 392 pos, end - pos); 393 394 if (data->session_id_set && data->session_id != session_id) { 395 wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)", 396 session_id, data->session_id); 397 ret->ignore = TRUE; 398 return NULL; 399 } 400 data->session_id = session_id; 401 data->session_id_set = 1; 402 403 ret->ignore = FALSE; 404 ret->methodState = METHOD_MAY_CONT; 405 ret->decision = DECISION_FAIL; 406 ret->allowNotifications = TRUE; 407 408 switch (subtype) { 409 case EAP_SAKE_SUBTYPE_IDENTITY: 410 resp = eap_sake_process_identity(sm, data, ret, reqData, 411 pos, end - pos); 412 break; 413 case EAP_SAKE_SUBTYPE_CHALLENGE: 414 resp = eap_sake_process_challenge(sm, data, ret, reqData, 415 pos, end - pos); 416 break; 417 case EAP_SAKE_SUBTYPE_CONFIRM: 418 resp = eap_sake_process_confirm(sm, data, ret, reqData, 419 pos, end - pos); 420 break; 421 default: 422 wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring message with " 423 "unknown subtype %d", subtype); 424 ret->ignore = TRUE; 425 return NULL; 426 } 427 428 if (ret->methodState == METHOD_DONE) 429 ret->allowNotifications = FALSE; 430 431 return resp; 432 } 433 434 435 static Boolean eap_sake_isKeyAvailable(struct eap_sm *sm, void *priv) 436 { 437 struct eap_sake_data *data = priv; 438 return data->state == SUCCESS; 439 } 440 441 442 static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) 443 { 444 struct eap_sake_data *data = priv; 445 u8 *key; 446 447 if (data->state != SUCCESS) 448 return NULL; 449 450 key = os_malloc(EAP_MSK_LEN); 451 if (key == NULL) 452 return NULL; 453 os_memcpy(key, data->msk, EAP_MSK_LEN); 454 *len = EAP_MSK_LEN; 455 456 return key; 457 } 458 459 460 static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 461 { 462 struct eap_sake_data *data = priv; 463 u8 *key; 464 465 if (data->state != SUCCESS) 466 return NULL; 467 468 key = os_malloc(EAP_EMSK_LEN); 469 if (key == NULL) 470 return NULL; 471 os_memcpy(key, data->emsk, EAP_EMSK_LEN); 472 *len = EAP_EMSK_LEN; 473 474 return key; 475 } 476 477 478 int eap_peer_sake_register(void) 479 { 480 struct eap_method *eap; 481 int ret; 482 483 eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 484 EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE"); 485 if (eap == NULL) 486 return -1; 487 488 eap->init = eap_sake_init; 489 eap->deinit = eap_sake_deinit; 490 eap->process = eap_sake_process; 491 eap->isKeyAvailable = eap_sake_isKeyAvailable; 492 eap->getKey = eap_sake_getKey; 493 eap->get_emsk = eap_sake_get_emsk; 494 495 ret = eap_peer_method_register(eap); 496 if (ret) 497 eap_peer_method_free(eap); 498 return ret; 499 } 500