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 	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