eap_server_ikev2.c revision 337817
1214501Srpaulo/*
2214501Srpaulo * EAP-IKEv2 server (RFC 5106)
3214501Srpaulo * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7214501Srpaulo */
8214501Srpaulo
9214501Srpaulo#include "includes.h"
10214501Srpaulo
11214501Srpaulo#include "common.h"
12214501Srpaulo#include "eap_i.h"
13214501Srpaulo#include "eap_common/eap_ikev2_common.h"
14214501Srpaulo#include "ikev2.h"
15214501Srpaulo
16214501Srpaulo
17214501Srpaulostruct eap_ikev2_data {
18214501Srpaulo	struct ikev2_initiator_data ikev2;
19214501Srpaulo	enum { MSG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
20214501Srpaulo	struct wpabuf *in_buf;
21214501Srpaulo	struct wpabuf *out_buf;
22214501Srpaulo	size_t out_used;
23214501Srpaulo	size_t fragment_size;
24214501Srpaulo	int keys_ready;
25214501Srpaulo	u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN];
26214501Srpaulo	int keymat_ok;
27214501Srpaulo};
28214501Srpaulo
29214501Srpaulo
30214501Srpaulostatic const u8 * eap_ikev2_get_shared_secret(void *ctx, const u8 *IDr,
31214501Srpaulo					      size_t IDr_len,
32214501Srpaulo					      size_t *secret_len)
33214501Srpaulo{
34214501Srpaulo	struct eap_sm *sm = ctx;
35214501Srpaulo
36214501Srpaulo	if (IDr == NULL) {
37214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No IDr received - default "
38214501Srpaulo			   "to user identity from EAP-Identity");
39214501Srpaulo		IDr = sm->identity;
40214501Srpaulo		IDr_len = sm->identity_len;
41214501Srpaulo	}
42214501Srpaulo
43214501Srpaulo	if (eap_user_get(sm, IDr, IDr_len, 0) < 0 || sm->user == NULL ||
44214501Srpaulo	    sm->user->password == NULL) {
45214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No user entry found");
46214501Srpaulo		return NULL;
47214501Srpaulo	}
48214501Srpaulo
49214501Srpaulo	*secret_len = sm->user->password_len;
50214501Srpaulo	return sm->user->password;
51214501Srpaulo}
52214501Srpaulo
53214501Srpaulo
54214501Srpaulostatic const char * eap_ikev2_state_txt(int state)
55214501Srpaulo{
56214501Srpaulo	switch (state) {
57214501Srpaulo	case MSG:
58214501Srpaulo		return "MSG";
59214501Srpaulo	case FRAG_ACK:
60214501Srpaulo		return "FRAG_ACK";
61214501Srpaulo	case WAIT_FRAG_ACK:
62214501Srpaulo		return "WAIT_FRAG_ACK";
63214501Srpaulo	case DONE:
64214501Srpaulo		return "DONE";
65214501Srpaulo	case FAIL:
66214501Srpaulo		return "FAIL";
67214501Srpaulo	default:
68214501Srpaulo		return "?";
69214501Srpaulo	}
70214501Srpaulo}
71214501Srpaulo
72214501Srpaulo
73214501Srpaulostatic void eap_ikev2_state(struct eap_ikev2_data *data, int state)
74214501Srpaulo{
75214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s",
76214501Srpaulo		   eap_ikev2_state_txt(data->state),
77214501Srpaulo		   eap_ikev2_state_txt(state));
78214501Srpaulo	data->state = state;
79214501Srpaulo}
80214501Srpaulo
81214501Srpaulo
82214501Srpaulostatic void * eap_ikev2_init(struct eap_sm *sm)
83214501Srpaulo{
84214501Srpaulo	struct eap_ikev2_data *data;
85214501Srpaulo
86214501Srpaulo	data = os_zalloc(sizeof(*data));
87214501Srpaulo	if (data == NULL)
88214501Srpaulo		return NULL;
89214501Srpaulo	data->state = MSG;
90252726Srpaulo	data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size :
91252726Srpaulo		IKEV2_FRAGMENT_SIZE;
92214501Srpaulo	data->ikev2.state = SA_INIT;
93214501Srpaulo	data->ikev2.peer_auth = PEER_AUTH_SECRET;
94214501Srpaulo	data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2");
95214501Srpaulo	if (data->ikev2.key_pad == NULL)
96214501Srpaulo		goto failed;
97214501Srpaulo	data->ikev2.key_pad_len = 21;
98214501Srpaulo
99214501Srpaulo	/* TODO: make proposals configurable */
100214501Srpaulo	data->ikev2.proposal.proposal_num = 1;
101214501Srpaulo	data->ikev2.proposal.integ = AUTH_HMAC_SHA1_96;
102214501Srpaulo	data->ikev2.proposal.prf = PRF_HMAC_SHA1;
103214501Srpaulo	data->ikev2.proposal.encr = ENCR_AES_CBC;
104214501Srpaulo	data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP;
105214501Srpaulo
106281806Srpaulo	data->ikev2.IDi = os_malloc(sm->server_id_len);
107281806Srpaulo	if (data->ikev2.IDi == NULL)
108281806Srpaulo		goto failed;
109281806Srpaulo	os_memcpy(data->ikev2.IDi, sm->server_id, sm->server_id_len);
110281806Srpaulo	data->ikev2.IDi_len = sm->server_id_len;
111214501Srpaulo
112214501Srpaulo	data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret;
113214501Srpaulo	data->ikev2.cb_ctx = sm;
114214501Srpaulo
115214501Srpaulo	return data;
116214501Srpaulo
117214501Srpaulofailed:
118214501Srpaulo	ikev2_initiator_deinit(&data->ikev2);
119214501Srpaulo	os_free(data);
120214501Srpaulo	return NULL;
121214501Srpaulo}
122214501Srpaulo
123214501Srpaulo
124214501Srpaulostatic void eap_ikev2_reset(struct eap_sm *sm, void *priv)
125214501Srpaulo{
126214501Srpaulo	struct eap_ikev2_data *data = priv;
127214501Srpaulo	wpabuf_free(data->in_buf);
128214501Srpaulo	wpabuf_free(data->out_buf);
129214501Srpaulo	ikev2_initiator_deinit(&data->ikev2);
130281806Srpaulo	bin_clear_free(data, sizeof(*data));
131214501Srpaulo}
132214501Srpaulo
133214501Srpaulo
134214501Srpaulostatic struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, u8 id)
135214501Srpaulo{
136214501Srpaulo	struct wpabuf *req;
137214501Srpaulo	u8 flags;
138214501Srpaulo	size_t send_len, plen, icv_len = 0;
139214501Srpaulo
140214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Request");
141214501Srpaulo
142214501Srpaulo	flags = 0;
143214501Srpaulo	send_len = wpabuf_len(data->out_buf) - data->out_used;
144214501Srpaulo	if (1 + send_len > data->fragment_size) {
145214501Srpaulo		send_len = data->fragment_size - 1;
146214501Srpaulo		flags |= IKEV2_FLAGS_MORE_FRAGMENTS;
147214501Srpaulo		if (data->out_used == 0) {
148214501Srpaulo			flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
149214501Srpaulo			send_len -= 4;
150214501Srpaulo		}
151214501Srpaulo	}
152214501Srpaulo
153214501Srpaulo	plen = 1 + send_len;
154214501Srpaulo	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
155214501Srpaulo		plen += 4;
156214501Srpaulo	if (data->keys_ready) {
157214501Srpaulo		const struct ikev2_integ_alg *integ;
158214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum "
159214501Srpaulo			   "Data");
160214501Srpaulo		flags |= IKEV2_FLAGS_ICV_INCLUDED;
161214501Srpaulo		integ = ikev2_get_integ(data->ikev2.proposal.integ);
162214501Srpaulo		if (integ == NULL) {
163214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
164214501Srpaulo				   "transform / cannot generate ICV");
165214501Srpaulo			return NULL;
166214501Srpaulo		}
167214501Srpaulo		icv_len = integ->hash_len;
168214501Srpaulo
169214501Srpaulo		plen += icv_len;
170214501Srpaulo	}
171214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen,
172214501Srpaulo			    EAP_CODE_REQUEST, id);
173214501Srpaulo	if (req == NULL)
174214501Srpaulo		return NULL;
175214501Srpaulo
176214501Srpaulo	wpabuf_put_u8(req, flags); /* Flags */
177214501Srpaulo	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
178214501Srpaulo		wpabuf_put_be32(req, wpabuf_len(data->out_buf));
179214501Srpaulo
180214501Srpaulo	wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
181214501Srpaulo			send_len);
182214501Srpaulo	data->out_used += send_len;
183214501Srpaulo
184214501Srpaulo	if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
185214501Srpaulo		const u8 *msg = wpabuf_head(req);
186214501Srpaulo		size_t len = wpabuf_len(req);
187214501Srpaulo		ikev2_integ_hash(data->ikev2.proposal.integ,
188214501Srpaulo				 data->ikev2.keys.SK_ai,
189214501Srpaulo				 data->ikev2.keys.SK_integ_len,
190214501Srpaulo				 msg, len, wpabuf_put(req, icv_len));
191214501Srpaulo	}
192214501Srpaulo
193214501Srpaulo	if (data->out_used == wpabuf_len(data->out_buf)) {
194214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
195214501Srpaulo			   "(message sent completely)",
196214501Srpaulo			   (unsigned long) send_len);
197214501Srpaulo		wpabuf_free(data->out_buf);
198214501Srpaulo		data->out_buf = NULL;
199214501Srpaulo		data->out_used = 0;
200214501Srpaulo	} else {
201214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
202214501Srpaulo			   "(%lu more to send)", (unsigned long) send_len,
203214501Srpaulo			   (unsigned long) wpabuf_len(data->out_buf) -
204214501Srpaulo			   data->out_used);
205214501Srpaulo		eap_ikev2_state(data, WAIT_FRAG_ACK);
206214501Srpaulo	}
207214501Srpaulo
208214501Srpaulo	return req;
209214501Srpaulo}
210214501Srpaulo
211214501Srpaulo
212214501Srpaulostatic struct wpabuf * eap_ikev2_buildReq(struct eap_sm *sm, void *priv, u8 id)
213214501Srpaulo{
214214501Srpaulo	struct eap_ikev2_data *data = priv;
215214501Srpaulo
216214501Srpaulo	switch (data->state) {
217214501Srpaulo	case MSG:
218214501Srpaulo		if (data->out_buf == NULL) {
219214501Srpaulo			data->out_buf = ikev2_initiator_build(&data->ikev2);
220214501Srpaulo			if (data->out_buf == NULL) {
221214501Srpaulo				wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to "
222214501Srpaulo					   "generate IKEv2 message");
223214501Srpaulo				return NULL;
224214501Srpaulo			}
225214501Srpaulo			data->out_used = 0;
226214501Srpaulo		}
227214501Srpaulo		/* pass through */
228214501Srpaulo	case WAIT_FRAG_ACK:
229214501Srpaulo		return eap_ikev2_build_msg(data, id);
230214501Srpaulo	case FRAG_ACK:
231214501Srpaulo		return eap_ikev2_build_frag_ack(id, EAP_CODE_REQUEST);
232214501Srpaulo	default:
233214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected state %d in "
234214501Srpaulo			   "buildReq", data->state);
235214501Srpaulo		return NULL;
236214501Srpaulo	}
237214501Srpaulo}
238214501Srpaulo
239214501Srpaulo
240214501Srpaulostatic Boolean eap_ikev2_check(struct eap_sm *sm, void *priv,
241214501Srpaulo			       struct wpabuf *respData)
242214501Srpaulo{
243214501Srpaulo	const u8 *pos;
244214501Srpaulo	size_t len;
245214501Srpaulo
246214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData,
247214501Srpaulo			       &len);
248214501Srpaulo	if (pos == NULL) {
249214501Srpaulo		wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid frame");
250214501Srpaulo		return TRUE;
251214501Srpaulo	}
252214501Srpaulo
253214501Srpaulo	return FALSE;
254214501Srpaulo}
255214501Srpaulo
256214501Srpaulo
257214501Srpaulostatic int eap_ikev2_process_icv(struct eap_ikev2_data *data,
258214501Srpaulo				 const struct wpabuf *respData,
259281806Srpaulo				 u8 flags, const u8 *pos, const u8 **end,
260281806Srpaulo				 int frag_ack)
261214501Srpaulo{
262214501Srpaulo	if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
263214501Srpaulo		int icv_len = eap_ikev2_validate_icv(
264214501Srpaulo			data->ikev2.proposal.integ, &data->ikev2.keys, 0,
265214501Srpaulo			respData, pos, *end);
266214501Srpaulo		if (icv_len < 0)
267214501Srpaulo			return -1;
268214501Srpaulo		/* Hide Integrity Checksum Data from further processing */
269214501Srpaulo		*end -= icv_len;
270281806Srpaulo	} else if (data->keys_ready && !frag_ack) {
271214501Srpaulo		wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
272214501Srpaulo			   "included integrity checksum");
273214501Srpaulo		return -1;
274214501Srpaulo	}
275214501Srpaulo
276214501Srpaulo	return 0;
277214501Srpaulo}
278214501Srpaulo
279214501Srpaulo
280214501Srpaulostatic int eap_ikev2_process_cont(struct eap_ikev2_data *data,
281214501Srpaulo				  const u8 *buf, size_t len)
282214501Srpaulo{
283214501Srpaulo	/* Process continuation of a pending message */
284214501Srpaulo	if (len > wpabuf_tailroom(data->in_buf)) {
285214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow");
286214501Srpaulo		eap_ikev2_state(data, FAIL);
287214501Srpaulo		return -1;
288214501Srpaulo	}
289214501Srpaulo
290214501Srpaulo	wpabuf_put_data(data->in_buf, buf, len);
291214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting for %lu "
292214501Srpaulo		   "bytes more", (unsigned long) len,
293214501Srpaulo		   (unsigned long) wpabuf_tailroom(data->in_buf));
294214501Srpaulo
295214501Srpaulo	return 0;
296214501Srpaulo}
297214501Srpaulo
298214501Srpaulo
299214501Srpaulostatic int eap_ikev2_process_fragment(struct eap_ikev2_data *data,
300214501Srpaulo				      u8 flags, u32 message_length,
301214501Srpaulo				      const u8 *buf, size_t len)
302214501Srpaulo{
303214501Srpaulo	/* Process a fragment that is not the last one of the message */
304214501Srpaulo	if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) {
305214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in "
306214501Srpaulo			   "a fragmented packet");
307214501Srpaulo		return -1;
308214501Srpaulo	}
309214501Srpaulo
310214501Srpaulo	if (data->in_buf == NULL) {
311214501Srpaulo		/* First fragment of the message */
312281806Srpaulo		if (message_length > 50000) {
313281806Srpaulo			/* Limit maximum memory allocation */
314281806Srpaulo			wpa_printf(MSG_DEBUG,
315281806Srpaulo				   "EAP-IKEV2: Ignore too long message");
316281806Srpaulo			return -1;
317281806Srpaulo		}
318214501Srpaulo		data->in_buf = wpabuf_alloc(message_length);
319214501Srpaulo		if (data->in_buf == NULL) {
320214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
321214501Srpaulo				   "message");
322214501Srpaulo			return -1;
323214501Srpaulo		}
324214501Srpaulo		wpabuf_put_data(data->in_buf, buf, len);
325214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first "
326214501Srpaulo			   "fragment, waiting for %lu bytes more",
327214501Srpaulo			   (unsigned long) len,
328214501Srpaulo			   (unsigned long) wpabuf_tailroom(data->in_buf));
329214501Srpaulo	}
330214501Srpaulo
331214501Srpaulo	return 0;
332214501Srpaulo}
333214501Srpaulo
334214501Srpaulo
335214501Srpaulostatic int eap_ikev2_server_keymat(struct eap_ikev2_data *data)
336214501Srpaulo{
337214501Srpaulo	if (eap_ikev2_derive_keymat(
338214501Srpaulo		    data->ikev2.proposal.prf, &data->ikev2.keys,
339214501Srpaulo		    data->ikev2.i_nonce, data->ikev2.i_nonce_len,
340214501Srpaulo		    data->ikev2.r_nonce, data->ikev2.r_nonce_len,
341214501Srpaulo		    data->keymat) < 0) {
342214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to derive "
343214501Srpaulo			   "key material");
344214501Srpaulo		return -1;
345214501Srpaulo	}
346214501Srpaulo	data->keymat_ok = 1;
347214501Srpaulo	return 0;
348214501Srpaulo}
349214501Srpaulo
350214501Srpaulo
351214501Srpaulostatic void eap_ikev2_process(struct eap_sm *sm, void *priv,
352214501Srpaulo			      struct wpabuf *respData)
353214501Srpaulo{
354214501Srpaulo	struct eap_ikev2_data *data = priv;
355214501Srpaulo	const u8 *start, *pos, *end;
356214501Srpaulo	size_t len;
357214501Srpaulo	u8 flags;
358214501Srpaulo	u32 message_length = 0;
359214501Srpaulo	struct wpabuf tmpbuf;
360214501Srpaulo
361214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData,
362214501Srpaulo			       &len);
363214501Srpaulo	if (pos == NULL)
364214501Srpaulo		return; /* Should not happen; message already verified */
365214501Srpaulo
366214501Srpaulo	start = pos;
367214501Srpaulo	end = start + len;
368214501Srpaulo
369214501Srpaulo	if (len == 0) {
370214501Srpaulo		/* fragment ack */
371214501Srpaulo		flags = 0;
372214501Srpaulo	} else
373214501Srpaulo		flags = *pos++;
374214501Srpaulo
375281806Srpaulo	if (eap_ikev2_process_icv(data, respData, flags, pos, &end,
376281806Srpaulo				  data->state == WAIT_FRAG_ACK && len == 0) < 0)
377281806Srpaulo	{
378214501Srpaulo		eap_ikev2_state(data, FAIL);
379214501Srpaulo		return;
380214501Srpaulo	}
381214501Srpaulo
382214501Srpaulo	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) {
383214501Srpaulo		if (end - pos < 4) {
384214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow");
385214501Srpaulo			eap_ikev2_state(data, FAIL);
386214501Srpaulo			return;
387214501Srpaulo		}
388214501Srpaulo		message_length = WPA_GET_BE32(pos);
389214501Srpaulo		pos += 4;
390214501Srpaulo
391214501Srpaulo		if (message_length < (u32) (end - pos)) {
392214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message "
393214501Srpaulo				   "Length (%d; %ld remaining in this msg)",
394214501Srpaulo				   message_length, (long) (end - pos));
395214501Srpaulo			eap_ikev2_state(data, FAIL);
396214501Srpaulo			return;
397214501Srpaulo		}
398214501Srpaulo	}
399214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x "
400214501Srpaulo		   "Message Length %u", flags, message_length);
401214501Srpaulo
402214501Srpaulo	if (data->state == WAIT_FRAG_ACK) {
403214501Srpaulo		if (len != 0) {
404214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
405214501Srpaulo				   "in WAIT_FRAG_ACK state");
406214501Srpaulo			eap_ikev2_state(data, FAIL);
407214501Srpaulo			return;
408214501Srpaulo		}
409214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged");
410214501Srpaulo		eap_ikev2_state(data, MSG);
411214501Srpaulo		return;
412214501Srpaulo	}
413214501Srpaulo
414214501Srpaulo	if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) {
415214501Srpaulo		eap_ikev2_state(data, FAIL);
416214501Srpaulo		return;
417214501Srpaulo	}
418214501Srpaulo
419214501Srpaulo	if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) {
420214501Srpaulo		if (eap_ikev2_process_fragment(data, flags, message_length,
421214501Srpaulo					       pos, end - pos) < 0)
422214501Srpaulo			eap_ikev2_state(data, FAIL);
423214501Srpaulo		else
424214501Srpaulo			eap_ikev2_state(data, FRAG_ACK);
425214501Srpaulo		return;
426214501Srpaulo	} else if (data->state == FRAG_ACK) {
427214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
428214501Srpaulo		data->state = MSG;
429214501Srpaulo	}
430214501Srpaulo
431214501Srpaulo	if (data->in_buf == NULL) {
432214501Srpaulo		/* Wrap unfragmented messages as wpabuf without extra copy */
433214501Srpaulo		wpabuf_set(&tmpbuf, pos, end - pos);
434214501Srpaulo		data->in_buf = &tmpbuf;
435214501Srpaulo	}
436214501Srpaulo
437214501Srpaulo	if (ikev2_initiator_process(&data->ikev2, data->in_buf) < 0) {
438214501Srpaulo		if (data->in_buf == &tmpbuf)
439214501Srpaulo			data->in_buf = NULL;
440214501Srpaulo		eap_ikev2_state(data, FAIL);
441214501Srpaulo		return;
442214501Srpaulo	}
443214501Srpaulo
444214501Srpaulo	switch (data->ikev2.state) {
445214501Srpaulo	case SA_AUTH:
446214501Srpaulo		/* SA_INIT was sent out, so message have to be
447214501Srpaulo		 * integrity protected from now on. */
448214501Srpaulo		data->keys_ready = 1;
449214501Srpaulo		break;
450214501Srpaulo	case IKEV2_DONE:
451214501Srpaulo		if (data->state == FAIL)
452214501Srpaulo			break;
453214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication completed "
454214501Srpaulo			   "successfully");
455214501Srpaulo		if (eap_ikev2_server_keymat(data))
456214501Srpaulo			break;
457214501Srpaulo		eap_ikev2_state(data, DONE);
458214501Srpaulo		break;
459214501Srpaulo	default:
460214501Srpaulo		break;
461214501Srpaulo	}
462214501Srpaulo
463214501Srpaulo	if (data->in_buf != &tmpbuf)
464214501Srpaulo		wpabuf_free(data->in_buf);
465214501Srpaulo	data->in_buf = NULL;
466214501Srpaulo}
467214501Srpaulo
468214501Srpaulo
469214501Srpaulostatic Boolean eap_ikev2_isDone(struct eap_sm *sm, void *priv)
470214501Srpaulo{
471214501Srpaulo	struct eap_ikev2_data *data = priv;
472214501Srpaulo	return data->state == DONE || data->state == FAIL;
473214501Srpaulo}
474214501Srpaulo
475214501Srpaulo
476214501Srpaulostatic Boolean eap_ikev2_isSuccess(struct eap_sm *sm, void *priv)
477214501Srpaulo{
478214501Srpaulo	struct eap_ikev2_data *data = priv;
479214501Srpaulo	return data->state == DONE && data->ikev2.state == IKEV2_DONE &&
480214501Srpaulo		data->keymat_ok;
481214501Srpaulo}
482214501Srpaulo
483214501Srpaulo
484214501Srpaulostatic u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len)
485214501Srpaulo{
486214501Srpaulo	struct eap_ikev2_data *data = priv;
487214501Srpaulo	u8 *key;
488214501Srpaulo
489214501Srpaulo	if (data->state != DONE || !data->keymat_ok)
490214501Srpaulo		return NULL;
491214501Srpaulo
492214501Srpaulo	key = os_malloc(EAP_MSK_LEN);
493214501Srpaulo	if (key) {
494214501Srpaulo		os_memcpy(key, data->keymat, EAP_MSK_LEN);
495214501Srpaulo		*len = EAP_MSK_LEN;
496214501Srpaulo	}
497214501Srpaulo
498214501Srpaulo	return key;
499214501Srpaulo}
500214501Srpaulo
501214501Srpaulo
502214501Srpaulostatic u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
503214501Srpaulo{
504214501Srpaulo	struct eap_ikev2_data *data = priv;
505214501Srpaulo	u8 *key;
506214501Srpaulo
507214501Srpaulo	if (data->state != DONE || !data->keymat_ok)
508214501Srpaulo		return NULL;
509214501Srpaulo
510214501Srpaulo	key = os_malloc(EAP_EMSK_LEN);
511214501Srpaulo	if (key) {
512214501Srpaulo		os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN);
513214501Srpaulo		*len = EAP_EMSK_LEN;
514214501Srpaulo	}
515214501Srpaulo
516214501Srpaulo	return key;
517214501Srpaulo}
518214501Srpaulo
519214501Srpaulo
520281806Srpaulostatic u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
521281806Srpaulo{
522281806Srpaulo	struct eap_ikev2_data *data = priv;
523281806Srpaulo	u8 *sid;
524281806Srpaulo	size_t sid_len;
525281806Srpaulo	size_t offset;
526281806Srpaulo
527281806Srpaulo	if (data->state != DONE || !data->keymat_ok)
528281806Srpaulo		return NULL;
529281806Srpaulo
530281806Srpaulo	sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len;
531281806Srpaulo	sid = os_malloc(sid_len);
532281806Srpaulo	if (sid) {
533281806Srpaulo		offset = 0;
534281806Srpaulo		sid[offset] = EAP_TYPE_IKEV2;
535281806Srpaulo		offset++;
536281806Srpaulo		os_memcpy(sid + offset, data->ikev2.i_nonce,
537281806Srpaulo			  data->ikev2.i_nonce_len);
538281806Srpaulo		offset += data->ikev2.i_nonce_len;
539281806Srpaulo		os_memcpy(sid + offset, data->ikev2.r_nonce,
540281806Srpaulo			  data->ikev2.r_nonce_len);
541281806Srpaulo		*len = sid_len;
542281806Srpaulo		wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id",
543281806Srpaulo			    sid, sid_len);
544281806Srpaulo	}
545281806Srpaulo
546281806Srpaulo	return sid;
547281806Srpaulo}
548281806Srpaulo
549281806Srpaulo
550214501Srpauloint eap_server_ikev2_register(void)
551214501Srpaulo{
552214501Srpaulo	struct eap_method *eap;
553214501Srpaulo
554214501Srpaulo	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
555214501Srpaulo				      EAP_VENDOR_IETF, EAP_TYPE_IKEV2,
556214501Srpaulo				      "IKEV2");
557214501Srpaulo	if (eap == NULL)
558214501Srpaulo		return -1;
559214501Srpaulo
560214501Srpaulo	eap->init = eap_ikev2_init;
561214501Srpaulo	eap->reset = eap_ikev2_reset;
562214501Srpaulo	eap->buildReq = eap_ikev2_buildReq;
563214501Srpaulo	eap->check = eap_ikev2_check;
564214501Srpaulo	eap->process = eap_ikev2_process;
565214501Srpaulo	eap->isDone = eap_ikev2_isDone;
566214501Srpaulo	eap->getKey = eap_ikev2_getKey;
567214501Srpaulo	eap->isSuccess = eap_ikev2_isSuccess;
568214501Srpaulo	eap->get_emsk = eap_ikev2_get_emsk;
569281806Srpaulo	eap->getSessionId = eap_ikev2_get_session_id;
570214501Srpaulo
571337817Scy	return eap_server_method_register(eap);
572214501Srpaulo}
573