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