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