xref: /freebsd/contrib/wpa/src/eap_peer/eap_ikev2.c (revision c1d255d3ffdbe447de3ab875bf4e7d7accc5bfc5)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * EAP-IKEv2 peer (RFC 5106)
35b9c547cSRui Paulo  * Copyright (c) 2007-2014, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #include "common.h"
1239beb93cSSam Leffler #include "eap_i.h"
1339beb93cSSam Leffler #include "eap_common/eap_ikev2_common.h"
1439beb93cSSam Leffler #include "ikev2.h"
1539beb93cSSam Leffler 
1639beb93cSSam Leffler 
1739beb93cSSam Leffler struct eap_ikev2_data {
1839beb93cSSam Leffler 	struct ikev2_responder_data ikev2;
1939beb93cSSam Leffler 	enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state;
2039beb93cSSam Leffler 	struct wpabuf *in_buf;
2139beb93cSSam Leffler 	struct wpabuf *out_buf;
2239beb93cSSam Leffler 	size_t out_used;
2339beb93cSSam Leffler 	size_t fragment_size;
2439beb93cSSam Leffler 	int keys_ready;
2539beb93cSSam Leffler 	u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN];
2639beb93cSSam Leffler 	int keymat_ok;
2739beb93cSSam Leffler };
2839beb93cSSam Leffler 
2939beb93cSSam Leffler 
eap_ikev2_state_txt(int state)3039beb93cSSam Leffler static const char * eap_ikev2_state_txt(int state)
3139beb93cSSam Leffler {
3239beb93cSSam Leffler 	switch (state) {
3339beb93cSSam Leffler 	case WAIT_START:
3439beb93cSSam Leffler 		return "WAIT_START";
3539beb93cSSam Leffler 	case PROC_MSG:
3639beb93cSSam Leffler 		return "PROC_MSG";
3739beb93cSSam Leffler 	case WAIT_FRAG_ACK:
3839beb93cSSam Leffler 		return "WAIT_FRAG_ACK";
3939beb93cSSam Leffler 	case DONE:
4039beb93cSSam Leffler 		return "DONE";
4139beb93cSSam Leffler 	case FAIL:
4239beb93cSSam Leffler 		return "FAIL";
4339beb93cSSam Leffler 	default:
4439beb93cSSam Leffler 		return "?";
4539beb93cSSam Leffler 	}
4639beb93cSSam Leffler }
4739beb93cSSam Leffler 
4839beb93cSSam Leffler 
eap_ikev2_state(struct eap_ikev2_data * data,int state)4939beb93cSSam Leffler static void eap_ikev2_state(struct eap_ikev2_data *data, int state)
5039beb93cSSam Leffler {
5139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s",
5239beb93cSSam Leffler 		   eap_ikev2_state_txt(data->state),
5339beb93cSSam Leffler 		   eap_ikev2_state_txt(state));
5439beb93cSSam Leffler 	data->state = state;
5539beb93cSSam Leffler }
5639beb93cSSam Leffler 
5739beb93cSSam Leffler 
eap_ikev2_init(struct eap_sm * sm)5839beb93cSSam Leffler static void * eap_ikev2_init(struct eap_sm *sm)
5939beb93cSSam Leffler {
6039beb93cSSam Leffler 	struct eap_ikev2_data *data;
6139beb93cSSam Leffler 	const u8 *identity, *password;
6239beb93cSSam Leffler 	size_t identity_len, password_len;
635b9c547cSRui Paulo 	int fragment_size;
6439beb93cSSam Leffler 
6539beb93cSSam Leffler 	identity = eap_get_config_identity(sm, &identity_len);
6639beb93cSSam Leffler 	if (identity == NULL) {
6739beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-IKEV2: No identity available");
6839beb93cSSam Leffler 		return NULL;
6939beb93cSSam Leffler 	}
7039beb93cSSam Leffler 
7139beb93cSSam Leffler 	data = os_zalloc(sizeof(*data));
7239beb93cSSam Leffler 	if (data == NULL)
7339beb93cSSam Leffler 		return NULL;
7439beb93cSSam Leffler 	data->state = WAIT_START;
755b9c547cSRui Paulo 	fragment_size = eap_get_config_fragment_size(sm);
765b9c547cSRui Paulo 	if (fragment_size <= 0)
7739beb93cSSam Leffler 		data->fragment_size = IKEV2_FRAGMENT_SIZE;
785b9c547cSRui Paulo 	else
795b9c547cSRui Paulo 		data->fragment_size = fragment_size;
8039beb93cSSam Leffler 	data->ikev2.state = SA_INIT;
8139beb93cSSam Leffler 	data->ikev2.peer_auth = PEER_AUTH_SECRET;
8239beb93cSSam Leffler 	data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2");
8339beb93cSSam Leffler 	if (data->ikev2.key_pad == NULL)
8439beb93cSSam Leffler 		goto failed;
8539beb93cSSam Leffler 	data->ikev2.key_pad_len = 21;
8685732ac8SCy Schubert 	data->ikev2.IDr = os_memdup(identity, identity_len);
8739beb93cSSam Leffler 	if (data->ikev2.IDr == NULL)
8839beb93cSSam Leffler 		goto failed;
8939beb93cSSam Leffler 	data->ikev2.IDr_len = identity_len;
9039beb93cSSam Leffler 
9139beb93cSSam Leffler 	password = eap_get_config_password(sm, &password_len);
9239beb93cSSam Leffler 	if (password) {
9385732ac8SCy Schubert 		data->ikev2.shared_secret = os_memdup(password, password_len);
9439beb93cSSam Leffler 		if (data->ikev2.shared_secret == NULL)
9539beb93cSSam Leffler 			goto failed;
9639beb93cSSam Leffler 		data->ikev2.shared_secret_len = password_len;
9739beb93cSSam Leffler 	}
9839beb93cSSam Leffler 
9939beb93cSSam Leffler 	return data;
10039beb93cSSam Leffler 
10139beb93cSSam Leffler failed:
10239beb93cSSam Leffler 	ikev2_responder_deinit(&data->ikev2);
10339beb93cSSam Leffler 	os_free(data);
10439beb93cSSam Leffler 	return NULL;
10539beb93cSSam Leffler }
10639beb93cSSam Leffler 
10739beb93cSSam Leffler 
eap_ikev2_deinit(struct eap_sm * sm,void * priv)10839beb93cSSam Leffler static void eap_ikev2_deinit(struct eap_sm *sm, void *priv)
10939beb93cSSam Leffler {
11039beb93cSSam Leffler 	struct eap_ikev2_data *data = priv;
11139beb93cSSam Leffler 	wpabuf_free(data->in_buf);
11239beb93cSSam Leffler 	wpabuf_free(data->out_buf);
11339beb93cSSam Leffler 	ikev2_responder_deinit(&data->ikev2);
1145b9c547cSRui Paulo 	bin_clear_free(data, sizeof(*data));
11539beb93cSSam Leffler }
11639beb93cSSam Leffler 
11739beb93cSSam Leffler 
eap_ikev2_peer_keymat(struct eap_ikev2_data * data)11839beb93cSSam Leffler static int eap_ikev2_peer_keymat(struct eap_ikev2_data *data)
11939beb93cSSam Leffler {
12039beb93cSSam Leffler 	if (eap_ikev2_derive_keymat(
12139beb93cSSam Leffler 		    data->ikev2.proposal.prf, &data->ikev2.keys,
12239beb93cSSam Leffler 		    data->ikev2.i_nonce, data->ikev2.i_nonce_len,
12339beb93cSSam Leffler 		    data->ikev2.r_nonce, data->ikev2.r_nonce_len,
12439beb93cSSam Leffler 		    data->keymat) < 0) {
12539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to "
12639beb93cSSam Leffler 			   "derive key material");
12739beb93cSSam Leffler 		return -1;
12839beb93cSSam Leffler 	}
12939beb93cSSam Leffler 	data->keymat_ok = 1;
13039beb93cSSam Leffler 	return 0;
13139beb93cSSam Leffler }
13239beb93cSSam Leffler 
13339beb93cSSam Leffler 
eap_ikev2_build_msg(struct eap_ikev2_data * data,struct eap_method_ret * ret,u8 id)13439beb93cSSam Leffler static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data,
13539beb93cSSam Leffler 					   struct eap_method_ret *ret, u8 id)
13639beb93cSSam Leffler {
13739beb93cSSam Leffler 	struct wpabuf *resp;
13839beb93cSSam Leffler 	u8 flags;
13939beb93cSSam Leffler 	size_t send_len, plen, icv_len = 0;
14039beb93cSSam Leffler 
141*c1d255d3SCy Schubert 	ret->ignore = false;
14239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Response");
143*c1d255d3SCy Schubert 	ret->allowNotifications = true;
14439beb93cSSam Leffler 
14539beb93cSSam Leffler 	flags = 0;
14639beb93cSSam Leffler 	send_len = wpabuf_len(data->out_buf) - data->out_used;
14739beb93cSSam Leffler 	if (1 + send_len > data->fragment_size) {
14839beb93cSSam Leffler 		send_len = data->fragment_size - 1;
14939beb93cSSam Leffler 		flags |= IKEV2_FLAGS_MORE_FRAGMENTS;
15039beb93cSSam Leffler 		if (data->out_used == 0) {
15139beb93cSSam Leffler 			flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
15239beb93cSSam Leffler 			send_len -= 4;
15339beb93cSSam Leffler 		}
15439beb93cSSam Leffler 	}
15539beb93cSSam Leffler 
15639beb93cSSam Leffler 	plen = 1 + send_len;
15739beb93cSSam Leffler 	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
15839beb93cSSam Leffler 		plen += 4;
15939beb93cSSam Leffler 	if (data->keys_ready) {
16039beb93cSSam Leffler 		const struct ikev2_integ_alg *integ;
16139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum "
16239beb93cSSam Leffler 			   "Data");
16339beb93cSSam Leffler 		flags |= IKEV2_FLAGS_ICV_INCLUDED;
16439beb93cSSam Leffler 		integ = ikev2_get_integ(data->ikev2.proposal.integ);
16539beb93cSSam Leffler 		if (integ == NULL) {
16639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
16739beb93cSSam Leffler 				   "transform / cannot generate ICV");
16839beb93cSSam Leffler 			return NULL;
16939beb93cSSam Leffler 		}
17039beb93cSSam Leffler 		icv_len = integ->hash_len;
17139beb93cSSam Leffler 
17239beb93cSSam Leffler 		plen += icv_len;
17339beb93cSSam Leffler 	}
17439beb93cSSam Leffler 	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen,
17539beb93cSSam Leffler 			     EAP_CODE_RESPONSE, id);
17639beb93cSSam Leffler 	if (resp == NULL)
17739beb93cSSam Leffler 		return NULL;
17839beb93cSSam Leffler 
17939beb93cSSam Leffler 	wpabuf_put_u8(resp, flags); /* Flags */
18039beb93cSSam Leffler 	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
18139beb93cSSam Leffler 		wpabuf_put_be32(resp, wpabuf_len(data->out_buf));
18239beb93cSSam Leffler 
18339beb93cSSam Leffler 	wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
18439beb93cSSam Leffler 			send_len);
18539beb93cSSam Leffler 	data->out_used += send_len;
18639beb93cSSam Leffler 
18739beb93cSSam Leffler 	if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
18839beb93cSSam Leffler 		const u8 *msg = wpabuf_head(resp);
18939beb93cSSam Leffler 		size_t len = wpabuf_len(resp);
19039beb93cSSam Leffler 		ikev2_integ_hash(data->ikev2.proposal.integ,
19139beb93cSSam Leffler 				 data->ikev2.keys.SK_ar,
19239beb93cSSam Leffler 				 data->ikev2.keys.SK_integ_len,
19339beb93cSSam Leffler 				 msg, len, wpabuf_put(resp, icv_len));
19439beb93cSSam Leffler 	}
19539beb93cSSam Leffler 
19639beb93cSSam Leffler 	ret->methodState = METHOD_MAY_CONT;
19739beb93cSSam Leffler 	ret->decision = DECISION_FAIL;
19839beb93cSSam Leffler 
19939beb93cSSam Leffler 	if (data->out_used == wpabuf_len(data->out_buf)) {
20039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
20139beb93cSSam Leffler 			   "(message sent completely)",
20239beb93cSSam Leffler 			   (unsigned long) send_len);
20339beb93cSSam Leffler 		wpabuf_free(data->out_buf);
20439beb93cSSam Leffler 		data->out_buf = NULL;
20539beb93cSSam Leffler 		data->out_used = 0;
20639beb93cSSam Leffler 		switch (data->ikev2.state) {
20739beb93cSSam Leffler 		case SA_AUTH:
20839beb93cSSam Leffler 			/* SA_INIT was sent out, so message have to be
20939beb93cSSam Leffler 			 * integrity protected from now on. */
21039beb93cSSam Leffler 			data->keys_ready = 1;
21139beb93cSSam Leffler 			break;
21239beb93cSSam Leffler 		case IKEV2_DONE:
21339beb93cSSam Leffler 			ret->methodState = METHOD_DONE;
21439beb93cSSam Leffler 			if (data->state == FAIL)
21539beb93cSSam Leffler 				break;
21639beb93cSSam Leffler 			ret->decision = DECISION_COND_SUCC;
21739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication "
21839beb93cSSam Leffler 				   "completed successfully");
21939beb93cSSam Leffler 			if (eap_ikev2_peer_keymat(data))
22039beb93cSSam Leffler 				break;
22139beb93cSSam Leffler 			eap_ikev2_state(data, DONE);
22239beb93cSSam Leffler 			break;
22339beb93cSSam Leffler 		case IKEV2_FAILED:
22439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication "
22539beb93cSSam Leffler 				   "failed");
22639beb93cSSam Leffler 			ret->methodState = METHOD_DONE;
22739beb93cSSam Leffler 			ret->decision = DECISION_FAIL;
22839beb93cSSam Leffler 			break;
22939beb93cSSam Leffler 		default:
23039beb93cSSam Leffler 			break;
23139beb93cSSam Leffler 		}
23239beb93cSSam Leffler 	} else {
23339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
23439beb93cSSam Leffler 			   "(%lu more to send)", (unsigned long) send_len,
23539beb93cSSam Leffler 			   (unsigned long) wpabuf_len(data->out_buf) -
23639beb93cSSam Leffler 			   data->out_used);
23739beb93cSSam Leffler 		eap_ikev2_state(data, WAIT_FRAG_ACK);
23839beb93cSSam Leffler 	}
23939beb93cSSam Leffler 
24039beb93cSSam Leffler 	return resp;
24139beb93cSSam Leffler }
24239beb93cSSam Leffler 
24339beb93cSSam Leffler 
eap_ikev2_process_icv(struct eap_ikev2_data * data,const struct wpabuf * reqData,u8 flags,const u8 * pos,const u8 ** end,int frag_ack)24439beb93cSSam Leffler static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
24539beb93cSSam Leffler 				 const struct wpabuf *reqData,
2465b9c547cSRui Paulo 				 u8 flags, const u8 *pos, const u8 **end,
2475b9c547cSRui Paulo 				 int frag_ack)
24839beb93cSSam Leffler {
24939beb93cSSam Leffler 	if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
25039beb93cSSam Leffler 		int icv_len = eap_ikev2_validate_icv(
25139beb93cSSam Leffler 			data->ikev2.proposal.integ, &data->ikev2.keys, 1,
25239beb93cSSam Leffler 			reqData, pos, *end);
25339beb93cSSam Leffler 		if (icv_len < 0)
25439beb93cSSam Leffler 			return -1;
25539beb93cSSam Leffler 		/* Hide Integrity Checksum Data from further processing */
25639beb93cSSam Leffler 		*end -= icv_len;
2575b9c547cSRui Paulo 	} else if (data->keys_ready && !frag_ack) {
25839beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
25939beb93cSSam Leffler 			   "included integrity checksum");
26039beb93cSSam Leffler 		return -1;
26139beb93cSSam Leffler 	}
26239beb93cSSam Leffler 
26339beb93cSSam Leffler 	return 0;
26439beb93cSSam Leffler }
26539beb93cSSam Leffler 
26639beb93cSSam Leffler 
eap_ikev2_process_cont(struct eap_ikev2_data * data,const u8 * buf,size_t len)26739beb93cSSam Leffler static int eap_ikev2_process_cont(struct eap_ikev2_data *data,
26839beb93cSSam Leffler 				  const u8 *buf, size_t len)
26939beb93cSSam Leffler {
27039beb93cSSam Leffler 	/* Process continuation of a pending message */
27139beb93cSSam Leffler 	if (len > wpabuf_tailroom(data->in_buf)) {
27239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow");
27339beb93cSSam Leffler 		eap_ikev2_state(data, FAIL);
27439beb93cSSam Leffler 		return -1;
27539beb93cSSam Leffler 	}
27639beb93cSSam Leffler 
27739beb93cSSam Leffler 	wpabuf_put_data(data->in_buf, buf, len);
27839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting "
27939beb93cSSam Leffler 		   "for %lu bytes more", (unsigned long) len,
28039beb93cSSam Leffler 		   (unsigned long) wpabuf_tailroom(data->in_buf));
28139beb93cSSam Leffler 
28239beb93cSSam Leffler 	return 0;
28339beb93cSSam Leffler }
28439beb93cSSam Leffler 
28539beb93cSSam Leffler 
eap_ikev2_process_fragment(struct eap_ikev2_data * data,struct eap_method_ret * ret,u8 id,u8 flags,u32 message_length,const u8 * buf,size_t len)28639beb93cSSam Leffler static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data,
28739beb93cSSam Leffler 						  struct eap_method_ret *ret,
28839beb93cSSam Leffler 						  u8 id, u8 flags,
28939beb93cSSam Leffler 						  u32 message_length,
29039beb93cSSam Leffler 						  const u8 *buf, size_t len)
29139beb93cSSam Leffler {
29239beb93cSSam Leffler 	/* Process a fragment that is not the last one of the message */
29339beb93cSSam Leffler 	if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) {
29439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in "
29539beb93cSSam Leffler 			   "a fragmented packet");
296*c1d255d3SCy Schubert 		ret->ignore = true;
29739beb93cSSam Leffler 		return NULL;
29839beb93cSSam Leffler 	}
29939beb93cSSam Leffler 
30039beb93cSSam Leffler 	if (data->in_buf == NULL) {
30139beb93cSSam Leffler 		/* First fragment of the message */
3025b9c547cSRui Paulo 		if (message_length > 50000) {
3035b9c547cSRui Paulo 			/* Limit maximum memory allocation */
3045b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG,
3055b9c547cSRui Paulo 				   "EAP-IKEV2: Ignore too long message");
306*c1d255d3SCy Schubert 			ret->ignore = true;
3075b9c547cSRui Paulo 			return NULL;
3085b9c547cSRui Paulo 		}
30939beb93cSSam Leffler 		data->in_buf = wpabuf_alloc(message_length);
31039beb93cSSam Leffler 		if (data->in_buf == NULL) {
31139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
31239beb93cSSam Leffler 				   "message");
313*c1d255d3SCy Schubert 			ret->ignore = true;
31439beb93cSSam Leffler 			return NULL;
31539beb93cSSam Leffler 		}
31639beb93cSSam Leffler 		wpabuf_put_data(data->in_buf, buf, len);
31739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first "
31839beb93cSSam Leffler 			   "fragment, waiting for %lu bytes more",
31939beb93cSSam Leffler 			   (unsigned long) len,
32039beb93cSSam Leffler 			   (unsigned long) wpabuf_tailroom(data->in_buf));
32139beb93cSSam Leffler 	}
32239beb93cSSam Leffler 
323*c1d255d3SCy Schubert 	ret->ignore = false;
32439beb93cSSam Leffler 	return eap_ikev2_build_frag_ack(id, EAP_CODE_RESPONSE);
32539beb93cSSam Leffler }
32639beb93cSSam Leffler 
32739beb93cSSam Leffler 
eap_ikev2_process(struct eap_sm * sm,void * priv,struct eap_method_ret * ret,const struct wpabuf * reqData)32839beb93cSSam Leffler static struct wpabuf * eap_ikev2_process(struct eap_sm *sm, void *priv,
32939beb93cSSam Leffler 					 struct eap_method_ret *ret,
33039beb93cSSam Leffler 					 const struct wpabuf *reqData)
33139beb93cSSam Leffler {
33239beb93cSSam Leffler 	struct eap_ikev2_data *data = priv;
33339beb93cSSam Leffler 	const u8 *start, *pos, *end;
33439beb93cSSam Leffler 	size_t len;
33539beb93cSSam Leffler 	u8 flags, id;
33639beb93cSSam Leffler 	u32 message_length = 0;
33739beb93cSSam Leffler 	struct wpabuf tmpbuf;
33839beb93cSSam Leffler 
33939beb93cSSam Leffler 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, reqData, &len);
34039beb93cSSam Leffler 	if (pos == NULL) {
341*c1d255d3SCy Schubert 		ret->ignore = true;
34239beb93cSSam Leffler 		return NULL;
34339beb93cSSam Leffler 	}
34439beb93cSSam Leffler 
34539beb93cSSam Leffler 	id = eap_get_id(reqData);
34639beb93cSSam Leffler 
34739beb93cSSam Leffler 	start = pos;
34839beb93cSSam Leffler 	end = start + len;
34939beb93cSSam Leffler 
35039beb93cSSam Leffler 	if (len == 0)
35139beb93cSSam Leffler 		flags = 0; /* fragment ack */
35239beb93cSSam Leffler 	else
35339beb93cSSam Leffler 		flags = *pos++;
35439beb93cSSam Leffler 
3555b9c547cSRui Paulo 	if (eap_ikev2_process_icv(data, reqData, flags, pos, &end,
3565b9c547cSRui Paulo 				  data->state == WAIT_FRAG_ACK && len == 0) < 0)
3575b9c547cSRui Paulo 	{
358*c1d255d3SCy Schubert 		ret->ignore = true;
35939beb93cSSam Leffler 		return NULL;
36039beb93cSSam Leffler 	}
36139beb93cSSam Leffler 
36239beb93cSSam Leffler 	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) {
36339beb93cSSam Leffler 		if (end - pos < 4) {
36439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow");
365*c1d255d3SCy Schubert 			ret->ignore = true;
36639beb93cSSam Leffler 			return NULL;
36739beb93cSSam Leffler 		}
36839beb93cSSam Leffler 		message_length = WPA_GET_BE32(pos);
36939beb93cSSam Leffler 		pos += 4;
37039beb93cSSam Leffler 
37139beb93cSSam Leffler 		if (message_length < (u32) (end - pos)) {
37239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message "
37339beb93cSSam Leffler 				   "Length (%d; %ld remaining in this msg)",
37439beb93cSSam Leffler 				   message_length, (long) (end - pos));
375*c1d255d3SCy Schubert 			ret->ignore = true;
37639beb93cSSam Leffler 			return NULL;
37739beb93cSSam Leffler 		}
37839beb93cSSam Leffler 	}
37939beb93cSSam Leffler 
38039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x "
38139beb93cSSam Leffler 		   "Message Length %u", flags, message_length);
38239beb93cSSam Leffler 
38339beb93cSSam Leffler 	if (data->state == WAIT_FRAG_ACK) {
3845b9c547cSRui Paulo 		if (len != 0) {
38539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
38639beb93cSSam Leffler 				   "in WAIT_FRAG_ACK state");
387*c1d255d3SCy Schubert 			ret->ignore = true;
38839beb93cSSam Leffler 			return NULL;
38939beb93cSSam Leffler 		}
39039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged");
39139beb93cSSam Leffler 		eap_ikev2_state(data, PROC_MSG);
39239beb93cSSam Leffler 		return eap_ikev2_build_msg(data, ret, id);
39339beb93cSSam Leffler 	}
39439beb93cSSam Leffler 
39539beb93cSSam Leffler 	if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) {
396*c1d255d3SCy Schubert 		ret->ignore = true;
39739beb93cSSam Leffler 		return NULL;
39839beb93cSSam Leffler 	}
39939beb93cSSam Leffler 
40039beb93cSSam Leffler 	if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) {
40139beb93cSSam Leffler 		return eap_ikev2_process_fragment(data, ret, id, flags,
40239beb93cSSam Leffler 						  message_length, pos,
40339beb93cSSam Leffler 						  end - pos);
40439beb93cSSam Leffler 	}
40539beb93cSSam Leffler 
40639beb93cSSam Leffler 	if (data->in_buf == NULL) {
40739beb93cSSam Leffler 		/* Wrap unfragmented messages as wpabuf without extra copy */
40839beb93cSSam Leffler 		wpabuf_set(&tmpbuf, pos, end - pos);
40939beb93cSSam Leffler 		data->in_buf = &tmpbuf;
41039beb93cSSam Leffler 	}
41139beb93cSSam Leffler 
41239beb93cSSam Leffler 	if (ikev2_responder_process(&data->ikev2, data->in_buf) < 0) {
41339beb93cSSam Leffler 		if (data->in_buf == &tmpbuf)
41439beb93cSSam Leffler 			data->in_buf = NULL;
41539beb93cSSam Leffler 		eap_ikev2_state(data, FAIL);
41639beb93cSSam Leffler 		return NULL;
41739beb93cSSam Leffler 	}
41839beb93cSSam Leffler 
41939beb93cSSam Leffler 	if (data->in_buf != &tmpbuf)
42039beb93cSSam Leffler 		wpabuf_free(data->in_buf);
42139beb93cSSam Leffler 	data->in_buf = NULL;
42239beb93cSSam Leffler 
42339beb93cSSam Leffler 	if (data->out_buf == NULL) {
42439beb93cSSam Leffler 		data->out_buf = ikev2_responder_build(&data->ikev2);
42539beb93cSSam Leffler 		if (data->out_buf == NULL) {
42639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to generate "
42739beb93cSSam Leffler 				   "IKEv2 message");
42839beb93cSSam Leffler 			return NULL;
42939beb93cSSam Leffler 		}
43039beb93cSSam Leffler 		data->out_used = 0;
43139beb93cSSam Leffler 	}
43239beb93cSSam Leffler 
43339beb93cSSam Leffler 	eap_ikev2_state(data, PROC_MSG);
43439beb93cSSam Leffler 	return eap_ikev2_build_msg(data, ret, id);
43539beb93cSSam Leffler }
43639beb93cSSam Leffler 
43739beb93cSSam Leffler 
eap_ikev2_isKeyAvailable(struct eap_sm * sm,void * priv)438*c1d255d3SCy Schubert static bool eap_ikev2_isKeyAvailable(struct eap_sm *sm, void *priv)
43939beb93cSSam Leffler {
44039beb93cSSam Leffler 	struct eap_ikev2_data *data = priv;
44139beb93cSSam Leffler 	return data->state == DONE && data->keymat_ok;
44239beb93cSSam Leffler }
44339beb93cSSam Leffler 
44439beb93cSSam Leffler 
eap_ikev2_getKey(struct eap_sm * sm,void * priv,size_t * len)44539beb93cSSam Leffler static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len)
44639beb93cSSam Leffler {
44739beb93cSSam Leffler 	struct eap_ikev2_data *data = priv;
44839beb93cSSam Leffler 	u8 *key;
44939beb93cSSam Leffler 
45039beb93cSSam Leffler 	if (data->state != DONE || !data->keymat_ok)
45139beb93cSSam Leffler 		return NULL;
45239beb93cSSam Leffler 
45339beb93cSSam Leffler 	key = os_malloc(EAP_MSK_LEN);
45439beb93cSSam Leffler 	if (key) {
45539beb93cSSam Leffler 		os_memcpy(key, data->keymat, EAP_MSK_LEN);
45639beb93cSSam Leffler 		*len = EAP_MSK_LEN;
45739beb93cSSam Leffler 	}
45839beb93cSSam Leffler 
45939beb93cSSam Leffler 	return key;
46039beb93cSSam Leffler }
46139beb93cSSam Leffler 
46239beb93cSSam Leffler 
eap_ikev2_get_emsk(struct eap_sm * sm,void * priv,size_t * len)46339beb93cSSam Leffler static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
46439beb93cSSam Leffler {
46539beb93cSSam Leffler 	struct eap_ikev2_data *data = priv;
46639beb93cSSam Leffler 	u8 *key;
46739beb93cSSam Leffler 
46839beb93cSSam Leffler 	if (data->state != DONE || !data->keymat_ok)
46939beb93cSSam Leffler 		return NULL;
47039beb93cSSam Leffler 
47139beb93cSSam Leffler 	key = os_malloc(EAP_EMSK_LEN);
47239beb93cSSam Leffler 	if (key) {
47339beb93cSSam Leffler 		os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN);
47439beb93cSSam Leffler 		*len = EAP_EMSK_LEN;
47539beb93cSSam Leffler 	}
47639beb93cSSam Leffler 
47739beb93cSSam Leffler 	return key;
47839beb93cSSam Leffler }
47939beb93cSSam Leffler 
48039beb93cSSam Leffler 
eap_ikev2_get_session_id(struct eap_sm * sm,void * priv,size_t * len)4815b9c547cSRui Paulo static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
4825b9c547cSRui Paulo {
4835b9c547cSRui Paulo 	struct eap_ikev2_data *data = priv;
4845b9c547cSRui Paulo 	u8 *sid;
4855b9c547cSRui Paulo 	size_t sid_len;
4865b9c547cSRui Paulo 	size_t offset;
4875b9c547cSRui Paulo 
4885b9c547cSRui Paulo 	if (data->state != DONE || !data->keymat_ok)
4895b9c547cSRui Paulo 		return NULL;
4905b9c547cSRui Paulo 
4915b9c547cSRui Paulo 	sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len;
4925b9c547cSRui Paulo 	sid = os_malloc(sid_len);
4935b9c547cSRui Paulo 	if (sid) {
4945b9c547cSRui Paulo 		offset = 0;
4955b9c547cSRui Paulo 		sid[offset] = EAP_TYPE_IKEV2;
4965b9c547cSRui Paulo 		offset++;
4975b9c547cSRui Paulo 		os_memcpy(sid + offset, data->ikev2.i_nonce,
4985b9c547cSRui Paulo 			  data->ikev2.i_nonce_len);
4995b9c547cSRui Paulo 		offset += data->ikev2.i_nonce_len;
5005b9c547cSRui Paulo 		os_memcpy(sid + offset, data->ikev2.r_nonce,
5015b9c547cSRui Paulo 			  data->ikev2.r_nonce_len);
5025b9c547cSRui Paulo 		*len = sid_len;
5035b9c547cSRui Paulo 		wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id",
5045b9c547cSRui Paulo 			    sid, sid_len);
5055b9c547cSRui Paulo 	}
5065b9c547cSRui Paulo 
5075b9c547cSRui Paulo 	return sid;
5085b9c547cSRui Paulo }
5095b9c547cSRui Paulo 
5105b9c547cSRui Paulo 
eap_peer_ikev2_register(void)51139beb93cSSam Leffler int eap_peer_ikev2_register(void)
51239beb93cSSam Leffler {
51339beb93cSSam Leffler 	struct eap_method *eap;
51439beb93cSSam Leffler 
51539beb93cSSam Leffler 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
51639beb93cSSam Leffler 				    EAP_VENDOR_IETF, EAP_TYPE_IKEV2,
51739beb93cSSam Leffler 				    "IKEV2");
51839beb93cSSam Leffler 	if (eap == NULL)
51939beb93cSSam Leffler 		return -1;
52039beb93cSSam Leffler 
52139beb93cSSam Leffler 	eap->init = eap_ikev2_init;
52239beb93cSSam Leffler 	eap->deinit = eap_ikev2_deinit;
52339beb93cSSam Leffler 	eap->process = eap_ikev2_process;
52439beb93cSSam Leffler 	eap->isKeyAvailable = eap_ikev2_isKeyAvailable;
52539beb93cSSam Leffler 	eap->getKey = eap_ikev2_getKey;
52639beb93cSSam Leffler 	eap->get_emsk = eap_ikev2_get_emsk;
5275b9c547cSRui Paulo 	eap->getSessionId = eap_ikev2_get_session_id;
52839beb93cSSam Leffler 
529780fb4a2SCy Schubert 	return eap_peer_method_register(eap);
53039beb93cSSam Leffler }
531