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