Home | History | Annotate | Download | only in eap_server
      1 /*
      2  * hostapd / EAP-SAKE (RFC 4763) server
      3  * Copyright (c) 2006-2007, Jouni Malinen <j (at) 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->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 
    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->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 
    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->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 
    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 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 
    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 	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, data->emsk);
    347 
    348 	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
    349 			     sm->server_id, sm->server_id_len,
    350 			     data->peerid, data->peerid_len, 1,
    351 			     wpabuf_head(respData), wpabuf_len(respData),
    352 			     attr.mic_p, mic_p);
    353 	if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
    354 		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
    355 		eap_sake_state(data, FAILURE);
    356 		return;
    357 	}
    358 
    359 	eap_sake_state(data, CONFIRM);
    360 }
    361 
    362 
    363 static void eap_sake_process_confirm(struct eap_sm *sm,
    364 				     struct eap_sake_data *data,
    365 				     const struct wpabuf *respData,
    366 				     const u8 *payload, size_t payloadlen)
    367 {
    368 	struct eap_sake_parse_attr attr;
    369 	u8 mic_p[EAP_SAKE_MIC_LEN];
    370 
    371 	if (data->state != CONFIRM)
    372 		return;
    373 
    374 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Confirm");
    375 
    376 	if (eap_sake_parse_attributes(payload, payloadlen, &attr))
    377 		return;
    378 
    379 	if (!attr.mic_p) {
    380 		wpa_printf(MSG_INFO, "EAP-SAKE: Response/Confirm did not "
    381 			   "include AT_MIC_P");
    382 		return;
    383 	}
    384 
    385 	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
    386 			     sm->server_id, sm->server_id_len,
    387 			     data->peerid, data->peerid_len, 1,
    388 			     wpabuf_head(respData), wpabuf_len(respData),
    389 			     attr.mic_p, mic_p);
    390 	if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
    391 		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
    392 		eap_sake_state(data, FAILURE);
    393 	} else
    394 		eap_sake_state(data, SUCCESS);
    395 }
    396 
    397 
    398 static void eap_sake_process_auth_reject(struct eap_sm *sm,
    399 					 struct eap_sake_data *data,
    400 					 const struct wpabuf *respData,
    401 					 const u8 *payload, size_t payloadlen)
    402 {
    403 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject");
    404 	eap_sake_state(data, FAILURE);
    405 }
    406 
    407 
    408 static void eap_sake_process(struct eap_sm *sm, void *priv,
    409 			     struct wpabuf *respData)
    410 {
    411 	struct eap_sake_data *data = priv;
    412 	struct eap_sake_hdr *resp;
    413 	u8 subtype;
    414 	size_t len;
    415 	const u8 *pos, *end;
    416 
    417 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
    418 	if (pos == NULL || len < sizeof(struct eap_sake_hdr))
    419 		return;
    420 
    421 	resp = (struct eap_sake_hdr *) pos;
    422 	end = pos + len;
    423 	subtype = resp->subtype;
    424 	pos = (u8 *) (resp + 1);
    425 
    426 	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
    427 		    pos, end - pos);
    428 
    429 	switch (subtype) {
    430 	case EAP_SAKE_SUBTYPE_IDENTITY:
    431 		eap_sake_process_identity(sm, data, respData, pos, end - pos);
    432 		break;
    433 	case EAP_SAKE_SUBTYPE_CHALLENGE:
    434 		eap_sake_process_challenge(sm, data, respData, pos, end - pos);
    435 		break;
    436 	case EAP_SAKE_SUBTYPE_CONFIRM:
    437 		eap_sake_process_confirm(sm, data, respData, pos, end - pos);
    438 		break;
    439 	case EAP_SAKE_SUBTYPE_AUTH_REJECT:
    440 		eap_sake_process_auth_reject(sm, data, respData, pos,
    441 					     end - pos);
    442 		break;
    443 	}
    444 }
    445 
    446 
    447 static Boolean eap_sake_isDone(struct eap_sm *sm, void *priv)
    448 {
    449 	struct eap_sake_data *data = priv;
    450 	return data->state == SUCCESS || data->state == FAILURE;
    451 }
    452 
    453 
    454 static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
    455 {
    456 	struct eap_sake_data *data = priv;
    457 	u8 *key;
    458 
    459 	if (data->state != SUCCESS)
    460 		return NULL;
    461 
    462 	key = os_memdup(data->msk, EAP_MSK_LEN);
    463 	if (key == NULL)
    464 		return NULL;
    465 	*len = EAP_MSK_LEN;
    466 
    467 	return key;
    468 }
    469 
    470 
    471 static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
    472 {
    473 	struct eap_sake_data *data = priv;
    474 	u8 *key;
    475 
    476 	if (data->state != SUCCESS)
    477 		return NULL;
    478 
    479 	key = os_memdup(data->emsk, EAP_EMSK_LEN);
    480 	if (key == NULL)
    481 		return NULL;
    482 	*len = EAP_EMSK_LEN;
    483 
    484 	return key;
    485 }
    486 
    487 
    488 static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv)
    489 {
    490 	struct eap_sake_data *data = priv;
    491 	return data->state == SUCCESS;
    492 }
    493 
    494 
    495 static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
    496 {
    497 	struct eap_sake_data *data = priv;
    498 	u8 *id;
    499 
    500 	if (data->state != SUCCESS)
    501 		return NULL;
    502 
    503 	*len = 1 + 2 * EAP_SAKE_RAND_LEN;
    504 	id = os_malloc(*len);
    505 	if (id == NULL)
    506 		return NULL;
    507 
    508 	id[0] = EAP_TYPE_SAKE;
    509 	os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN);
    510 	os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN);
    511 	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len);
    512 
    513 	return id;
    514 }
    515 
    516 
    517 int eap_server_sake_register(void)
    518 {
    519 	struct eap_method *eap;
    520 
    521 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
    522 				      EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
    523 	if (eap == NULL)
    524 		return -1;
    525 
    526 	eap->init = eap_sake_init;
    527 	eap->reset = eap_sake_reset;
    528 	eap->buildReq = eap_sake_buildReq;
    529 	eap->check = eap_sake_check;
    530 	eap->process = eap_sake_process;
    531 	eap->isDone = eap_sake_isDone;
    532 	eap->getKey = eap_sake_getKey;
    533 	eap->isSuccess = eap_sake_isSuccess;
    534 	eap->get_emsk = eap_sake_get_emsk;
    535 	eap->getSessionId = eap_sake_get_session_id;
    536 
    537 	return eap_server_method_register(eap);
    538 }
    539