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