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 17 struct 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 33 static 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 52 static 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 61 static 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 82 static 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 90 static 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 116 static 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->cfg->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->cfg->server_id, sm->cfg->server_id_len); 139 140 return msg; 141 } 142 143 144 static 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->cfg->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->cfg->server_id, sm->cfg->server_id_len); 175 176 return msg; 177 } 178 179 180 static 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->cfg->server_id, sm->cfg->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 wpabuf_free(msg); 208 return NULL; 209 } 210 211 return msg; 212 } 213 214 215 static 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 235 static bool 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 287 static 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 301 static 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_memdup(attr.peerid, attr.peerid_len); 330 if (data->peerid == NULL) 331 return; 332 data->peerid_len = attr.peerid_len; 333 } 334 335 if (sm->user == NULL || sm->user->password == NULL || 336 sm->user->password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) { 337 wpa_printf(MSG_INFO, "EAP-SAKE: Plaintext password with " 338 "%d-byte key not configured", 339 2 * EAP_SAKE_ROOT_SECRET_LEN); 340 data->state = FAILURE; 341 return; 342 } 343 if (eap_sake_derive_keys(sm->user->password, 344 sm->user->password + EAP_SAKE_ROOT_SECRET_LEN, 345 data->rand_s, data->rand_p, 346 (u8 *) &data->tek, data->msk, 347 data->emsk) < 0) { 348 wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys"); 349 data->state = FAILURE; 350 return; 351 } 352 353 if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, 354 sm->cfg->server_id, sm->cfg->server_id_len, 355 data->peerid, data->peerid_len, 1, 356 wpabuf_head(respData), wpabuf_len(respData), 357 attr.mic_p, mic_p) < 0) { 358 wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); 359 data->state = FAILURE; 360 return; 361 } 362 if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { 363 wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); 364 eap_sake_state(data, FAILURE); 365 return; 366 } 367 368 eap_sake_state(data, CONFIRM); 369 } 370 371 372 static void eap_sake_process_confirm(struct eap_sm *sm, 373 struct eap_sake_data *data, 374 const struct wpabuf *respData, 375 const u8 *payload, size_t payloadlen) 376 { 377 struct eap_sake_parse_attr attr; 378 u8 mic_p[EAP_SAKE_MIC_LEN]; 379 380 if (data->state != CONFIRM) 381 return; 382 383 wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Confirm"); 384 385 if (eap_sake_parse_attributes(payload, payloadlen, &attr)) 386 return; 387 388 if (!attr.mic_p) { 389 wpa_printf(MSG_INFO, "EAP-SAKE: Response/Confirm did not " 390 "include AT_MIC_P"); 391 return; 392 } 393 394 if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, 395 sm->cfg->server_id, sm->cfg->server_id_len, 396 data->peerid, data->peerid_len, 1, 397 wpabuf_head(respData), wpabuf_len(respData), 398 attr.mic_p, mic_p) < 0) { 399 wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); 400 return; 401 } 402 if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { 403 wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); 404 eap_sake_state(data, FAILURE); 405 } else 406 eap_sake_state(data, SUCCESS); 407 } 408 409 410 static void eap_sake_process_auth_reject(struct eap_sm *sm, 411 struct eap_sake_data *data, 412 const struct wpabuf *respData, 413 const u8 *payload, size_t payloadlen) 414 { 415 wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject"); 416 eap_sake_state(data, FAILURE); 417 } 418 419 420 static void eap_sake_process(struct eap_sm *sm, void *priv, 421 struct wpabuf *respData) 422 { 423 struct eap_sake_data *data = priv; 424 struct eap_sake_hdr *resp; 425 u8 subtype; 426 size_t len; 427 const u8 *pos, *end; 428 429 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len); 430 if (pos == NULL || len < sizeof(struct eap_sake_hdr)) 431 return; 432 433 resp = (struct eap_sake_hdr *) pos; 434 end = pos + len; 435 subtype = resp->subtype; 436 pos = (u8 *) (resp + 1); 437 438 wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes", 439 pos, end - pos); 440 441 switch (subtype) { 442 case EAP_SAKE_SUBTYPE_IDENTITY: 443 eap_sake_process_identity(sm, data, respData, pos, end - pos); 444 break; 445 case EAP_SAKE_SUBTYPE_CHALLENGE: 446 eap_sake_process_challenge(sm, data, respData, pos, end - pos); 447 break; 448 case EAP_SAKE_SUBTYPE_CONFIRM: 449 eap_sake_process_confirm(sm, data, respData, pos, end - pos); 450 break; 451 case EAP_SAKE_SUBTYPE_AUTH_REJECT: 452 eap_sake_process_auth_reject(sm, data, respData, pos, 453 end - pos); 454 break; 455 } 456 } 457 458 459 static bool eap_sake_isDone(struct eap_sm *sm, void *priv) 460 { 461 struct eap_sake_data *data = priv; 462 return data->state == SUCCESS || data->state == FAILURE; 463 } 464 465 466 static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) 467 { 468 struct eap_sake_data *data = priv; 469 u8 *key; 470 471 if (data->state != SUCCESS) 472 return NULL; 473 474 key = os_memdup(data->msk, EAP_MSK_LEN); 475 if (key == NULL) 476 return NULL; 477 *len = EAP_MSK_LEN; 478 479 return key; 480 } 481 482 483 static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 484 { 485 struct eap_sake_data *data = priv; 486 u8 *key; 487 488 if (data->state != SUCCESS) 489 return NULL; 490 491 key = os_memdup(data->emsk, EAP_EMSK_LEN); 492 if (key == NULL) 493 return NULL; 494 *len = EAP_EMSK_LEN; 495 496 return key; 497 } 498 499 500 static bool eap_sake_isSuccess(struct eap_sm *sm, void *priv) 501 { 502 struct eap_sake_data *data = priv; 503 return data->state == SUCCESS; 504 } 505 506 507 static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len) 508 { 509 struct eap_sake_data *data = priv; 510 u8 *id; 511 512 if (data->state != SUCCESS) 513 return NULL; 514 515 *len = 1 + 2 * EAP_SAKE_RAND_LEN; 516 id = os_malloc(*len); 517 if (id == NULL) 518 return NULL; 519 520 id[0] = EAP_TYPE_SAKE; 521 os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN); 522 os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN); 523 wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len); 524 525 return id; 526 } 527 528 529 int eap_server_sake_register(void) 530 { 531 struct eap_method *eap; 532 533 eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 534 EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE"); 535 if (eap == NULL) 536 return -1; 537 538 eap->init = eap_sake_init; 539 eap->reset = eap_sake_reset; 540 eap->buildReq = eap_sake_buildReq; 541 eap->check = eap_sake_check; 542 eap->process = eap_sake_process; 543 eap->isDone = eap_sake_isDone; 544 eap->getKey = eap_sake_getKey; 545 eap->isSuccess = eap_sake_isSuccess; 546 eap->get_emsk = eap_sake_get_emsk; 547 eap->getSessionId = eap_sake_get_session_id; 548 549 return eap_server_method_register(eap); 550 } 551