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