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
106346981Scy	data->ikev2.IDi = os_memdup(sm->server_id, sm->server_id_len);
107281806Srpaulo	if (data->ikev2.IDi == NULL)
108281806Srpaulo		goto failed;
109281806Srpaulo	data->ikev2.IDi_len = sm->server_id_len;
110214501Srpaulo
111214501Srpaulo	data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret;
112214501Srpaulo	data->ikev2.cb_ctx = sm;
113214501Srpaulo
114214501Srpaulo	return data;
115214501Srpaulo
116214501Srpaulofailed:
117214501Srpaulo	ikev2_initiator_deinit(&data->ikev2);
118214501Srpaulo	os_free(data);
119214501Srpaulo	return NULL;
120214501Srpaulo}
121214501Srpaulo
122214501Srpaulo
123214501Srpaulostatic void eap_ikev2_reset(struct eap_sm *sm, void *priv)
124214501Srpaulo{
125214501Srpaulo	struct eap_ikev2_data *data = priv;
126214501Srpaulo	wpabuf_free(data->in_buf);
127214501Srpaulo	wpabuf_free(data->out_buf);
128214501Srpaulo	ikev2_initiator_deinit(&data->ikev2);
129281806Srpaulo	bin_clear_free(data, sizeof(*data));
130214501Srpaulo}
131214501Srpaulo
132214501Srpaulo
133214501Srpaulostatic struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, u8 id)
134214501Srpaulo{
135214501Srpaulo	struct wpabuf *req;
136214501Srpaulo	u8 flags;
137214501Srpaulo	size_t send_len, plen, icv_len = 0;
138214501Srpaulo
139214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Request");
140214501Srpaulo
141214501Srpaulo	flags = 0;
142214501Srpaulo	send_len = wpabuf_len(data->out_buf) - data->out_used;
143214501Srpaulo	if (1 + send_len > data->fragment_size) {
144214501Srpaulo		send_len = data->fragment_size - 1;
145214501Srpaulo		flags |= IKEV2_FLAGS_MORE_FRAGMENTS;
146214501Srpaulo		if (data->out_used == 0) {
147214501Srpaulo			flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
148214501Srpaulo			send_len -= 4;
149214501Srpaulo		}
150214501Srpaulo	}
151214501Srpaulo
152214501Srpaulo	plen = 1 + send_len;
153214501Srpaulo	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
154214501Srpaulo		plen += 4;
155214501Srpaulo	if (data->keys_ready) {
156214501Srpaulo		const struct ikev2_integ_alg *integ;
157214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum "
158214501Srpaulo			   "Data");
159214501Srpaulo		flags |= IKEV2_FLAGS_ICV_INCLUDED;
160214501Srpaulo		integ = ikev2_get_integ(data->ikev2.proposal.integ);
161214501Srpaulo		if (integ == NULL) {
162214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
163214501Srpaulo				   "transform / cannot generate ICV");
164214501Srpaulo			return NULL;
165214501Srpaulo		}
166214501Srpaulo		icv_len = integ->hash_len;
167214501Srpaulo
168214501Srpaulo		plen += icv_len;
169214501Srpaulo	}
170214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen,
171214501Srpaulo			    EAP_CODE_REQUEST, id);
172214501Srpaulo	if (req == NULL)
173214501Srpaulo		return NULL;
174214501Srpaulo
175214501Srpaulo	wpabuf_put_u8(req, flags); /* Flags */
176214501Srpaulo	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
177214501Srpaulo		wpabuf_put_be32(req, wpabuf_len(data->out_buf));
178214501Srpaulo
179214501Srpaulo	wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
180214501Srpaulo			send_len);
181214501Srpaulo	data->out_used += send_len;
182214501Srpaulo
183214501Srpaulo	if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
184214501Srpaulo		const u8 *msg = wpabuf_head(req);
185214501Srpaulo		size_t len = wpabuf_len(req);
186214501Srpaulo		ikev2_integ_hash(data->ikev2.proposal.integ,
187214501Srpaulo				 data->ikev2.keys.SK_ai,
188214501Srpaulo				 data->ikev2.keys.SK_integ_len,
189214501Srpaulo				 msg, len, wpabuf_put(req, icv_len));
190214501Srpaulo	}
191214501Srpaulo
192214501Srpaulo	if (data->out_used == wpabuf_len(data->out_buf)) {
193214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
194214501Srpaulo			   "(message sent completely)",
195214501Srpaulo			   (unsigned long) send_len);
196214501Srpaulo		wpabuf_free(data->out_buf);
197214501Srpaulo		data->out_buf = NULL;
198214501Srpaulo		data->out_used = 0;
199214501Srpaulo	} else {
200214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
201214501Srpaulo			   "(%lu more to send)", (unsigned long) send_len,
202214501Srpaulo			   (unsigned long) wpabuf_len(data->out_buf) -
203214501Srpaulo			   data->out_used);
204214501Srpaulo		eap_ikev2_state(data, WAIT_FRAG_ACK);
205214501Srpaulo	}
206214501Srpaulo
207214501Srpaulo	return req;
208214501Srpaulo}
209214501Srpaulo
210214501Srpaulo
211214501Srpaulostatic struct wpabuf * eap_ikev2_buildReq(struct eap_sm *sm, void *priv, u8 id)
212214501Srpaulo{
213214501Srpaulo	struct eap_ikev2_data *data = priv;
214214501Srpaulo
215214501Srpaulo	switch (data->state) {
216214501Srpaulo	case MSG:
217214501Srpaulo		if (data->out_buf == NULL) {
218214501Srpaulo			data->out_buf = ikev2_initiator_build(&data->ikev2);
219214501Srpaulo			if (data->out_buf == NULL) {
220214501Srpaulo				wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to "
221214501Srpaulo					   "generate IKEv2 message");
222214501Srpaulo				return NULL;
223214501Srpaulo			}
224214501Srpaulo			data->out_used = 0;
225214501Srpaulo		}
226346981Scy		/* fall through */
227214501Srpaulo	case WAIT_FRAG_ACK:
228214501Srpaulo		return eap_ikev2_build_msg(data, id);
229214501Srpaulo	case FRAG_ACK:
230214501Srpaulo		return eap_ikev2_build_frag_ack(id, EAP_CODE_REQUEST);
231214501Srpaulo	default:
232214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected state %d in "
233214501Srpaulo			   "buildReq", data->state);
234214501Srpaulo		return NULL;
235214501Srpaulo	}
236214501Srpaulo}
237214501Srpaulo
238214501Srpaulo
239214501Srpaulostatic Boolean eap_ikev2_check(struct eap_sm *sm, void *priv,
240214501Srpaulo			       struct wpabuf *respData)
241214501Srpaulo{
242214501Srpaulo	const u8 *pos;
243214501Srpaulo	size_t len;
244214501Srpaulo
245214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData,
246214501Srpaulo			       &len);
247214501Srpaulo	if (pos == NULL) {
248214501Srpaulo		wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid frame");
249214501Srpaulo		return TRUE;
250214501Srpaulo	}
251214501Srpaulo
252214501Srpaulo	return FALSE;
253214501Srpaulo}
254214501Srpaulo
255214501Srpaulo
256214501Srpaulostatic int eap_ikev2_process_icv(struct eap_ikev2_data *data,
257214501Srpaulo				 const struct wpabuf *respData,
258281806Srpaulo				 u8 flags, const u8 *pos, const u8 **end,
259281806Srpaulo				 int frag_ack)
260214501Srpaulo{
261214501Srpaulo	if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
262214501Srpaulo		int icv_len = eap_ikev2_validate_icv(
263214501Srpaulo			data->ikev2.proposal.integ, &data->ikev2.keys, 0,
264214501Srpaulo			respData, pos, *end);
265214501Srpaulo		if (icv_len < 0)
266214501Srpaulo			return -1;
267214501Srpaulo		/* Hide Integrity Checksum Data from further processing */
268214501Srpaulo		*end -= icv_len;
269281806Srpaulo	} else if (data->keys_ready && !frag_ack) {
270214501Srpaulo		wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
271214501Srpaulo			   "included integrity checksum");
272214501Srpaulo		return -1;
273214501Srpaulo	}
274214501Srpaulo
275214501Srpaulo	return 0;
276214501Srpaulo}
277214501Srpaulo
278214501Srpaulo
279214501Srpaulostatic int eap_ikev2_process_cont(struct eap_ikev2_data *data,
280214501Srpaulo				  const u8 *buf, size_t len)
281214501Srpaulo{
282214501Srpaulo	/* Process continuation of a pending message */
283214501Srpaulo	if (len > wpabuf_tailroom(data->in_buf)) {
284214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow");
285214501Srpaulo		eap_ikev2_state(data, FAIL);
286214501Srpaulo		return -1;
287214501Srpaulo	}
288214501Srpaulo
289214501Srpaulo	wpabuf_put_data(data->in_buf, buf, len);
290214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting for %lu "
291214501Srpaulo		   "bytes more", (unsigned long) len,
292214501Srpaulo		   (unsigned long) wpabuf_tailroom(data->in_buf));
293214501Srpaulo
294214501Srpaulo	return 0;
295214501Srpaulo}
296214501Srpaulo
297214501Srpaulo
298214501Srpaulostatic int eap_ikev2_process_fragment(struct eap_ikev2_data *data,
299214501Srpaulo				      u8 flags, u32 message_length,
300214501Srpaulo				      const u8 *buf, size_t len)
301214501Srpaulo{
302214501Srpaulo	/* Process a fragment that is not the last one of the message */
303214501Srpaulo	if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) {
304214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in "
305214501Srpaulo			   "a fragmented packet");
306214501Srpaulo		return -1;
307214501Srpaulo	}
308214501Srpaulo
309214501Srpaulo	if (data->in_buf == NULL) {
310214501Srpaulo		/* First fragment of the message */
311281806Srpaulo		if (message_length > 50000) {
312281806Srpaulo			/* Limit maximum memory allocation */
313281806Srpaulo			wpa_printf(MSG_DEBUG,
314281806Srpaulo				   "EAP-IKEV2: Ignore too long message");
315281806Srpaulo			return -1;
316281806Srpaulo		}
317214501Srpaulo		data->in_buf = wpabuf_alloc(message_length);
318214501Srpaulo		if (data->in_buf == NULL) {
319214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
320214501Srpaulo				   "message");
321214501Srpaulo			return -1;
322214501Srpaulo		}
323214501Srpaulo		wpabuf_put_data(data->in_buf, buf, len);
324214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first "
325214501Srpaulo			   "fragment, waiting for %lu bytes more",
326214501Srpaulo			   (unsigned long) len,
327214501Srpaulo			   (unsigned long) wpabuf_tailroom(data->in_buf));
328214501Srpaulo	}
329214501Srpaulo
330214501Srpaulo	return 0;
331214501Srpaulo}
332214501Srpaulo
333214501Srpaulo
334214501Srpaulostatic int eap_ikev2_server_keymat(struct eap_ikev2_data *data)
335214501Srpaulo{
336214501Srpaulo	if (eap_ikev2_derive_keymat(
337214501Srpaulo		    data->ikev2.proposal.prf, &data->ikev2.keys,
338214501Srpaulo		    data->ikev2.i_nonce, data->ikev2.i_nonce_len,
339214501Srpaulo		    data->ikev2.r_nonce, data->ikev2.r_nonce_len,
340214501Srpaulo		    data->keymat) < 0) {
341214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to derive "
342214501Srpaulo			   "key material");
343214501Srpaulo		return -1;
344214501Srpaulo	}
345214501Srpaulo	data->keymat_ok = 1;
346214501Srpaulo	return 0;
347214501Srpaulo}
348214501Srpaulo
349214501Srpaulo
350214501Srpaulostatic void eap_ikev2_process(struct eap_sm *sm, void *priv,
351214501Srpaulo			      struct wpabuf *respData)
352214501Srpaulo{
353214501Srpaulo	struct eap_ikev2_data *data = priv;
354214501Srpaulo	const u8 *start, *pos, *end;
355214501Srpaulo	size_t len;
356214501Srpaulo	u8 flags;
357214501Srpaulo	u32 message_length = 0;
358214501Srpaulo	struct wpabuf tmpbuf;
359214501Srpaulo
360214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData,
361214501Srpaulo			       &len);
362214501Srpaulo	if (pos == NULL)
363214501Srpaulo		return; /* Should not happen; message already verified */
364214501Srpaulo
365214501Srpaulo	start = pos;
366214501Srpaulo	end = start + len;
367214501Srpaulo
368214501Srpaulo	if (len == 0) {
369214501Srpaulo		/* fragment ack */
370214501Srpaulo		flags = 0;
371214501Srpaulo	} else
372214501Srpaulo		flags = *pos++;
373214501Srpaulo
374281806Srpaulo	if (eap_ikev2_process_icv(data, respData, flags, pos, &end,
375281806Srpaulo				  data->state == WAIT_FRAG_ACK && len == 0) < 0)
376281806Srpaulo	{
377214501Srpaulo		eap_ikev2_state(data, FAIL);
378214501Srpaulo		return;
379214501Srpaulo	}
380214501Srpaulo
381214501Srpaulo	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) {
382214501Srpaulo		if (end - pos < 4) {
383214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow");
384214501Srpaulo			eap_ikev2_state(data, FAIL);
385214501Srpaulo			return;
386214501Srpaulo		}
387214501Srpaulo		message_length = WPA_GET_BE32(pos);
388214501Srpaulo		pos += 4;
389214501Srpaulo
390214501Srpaulo		if (message_length < (u32) (end - pos)) {
391214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message "
392214501Srpaulo				   "Length (%d; %ld remaining in this msg)",
393214501Srpaulo				   message_length, (long) (end - pos));
394214501Srpaulo			eap_ikev2_state(data, FAIL);
395214501Srpaulo			return;
396214501Srpaulo		}
397214501Srpaulo	}
398214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x "
399214501Srpaulo		   "Message Length %u", flags, message_length);
400214501Srpaulo
401214501Srpaulo	if (data->state == WAIT_FRAG_ACK) {
402214501Srpaulo		if (len != 0) {
403214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
404214501Srpaulo				   "in WAIT_FRAG_ACK state");
405214501Srpaulo			eap_ikev2_state(data, FAIL);
406214501Srpaulo			return;
407214501Srpaulo		}
408214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged");
409214501Srpaulo		eap_ikev2_state(data, MSG);
410214501Srpaulo		return;
411214501Srpaulo	}
412214501Srpaulo
413214501Srpaulo	if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) {
414214501Srpaulo		eap_ikev2_state(data, FAIL);
415214501Srpaulo		return;
416214501Srpaulo	}
417214501Srpaulo
418214501Srpaulo	if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) {
419214501Srpaulo		if (eap_ikev2_process_fragment(data, flags, message_length,
420214501Srpaulo					       pos, end - pos) < 0)
421214501Srpaulo			eap_ikev2_state(data, FAIL);
422214501Srpaulo		else
423214501Srpaulo			eap_ikev2_state(data, FRAG_ACK);
424214501Srpaulo		return;
425214501Srpaulo	} else if (data->state == FRAG_ACK) {
426214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
427214501Srpaulo		data->state = MSG;
428214501Srpaulo	}
429214501Srpaulo
430214501Srpaulo	if (data->in_buf == NULL) {
431214501Srpaulo		/* Wrap unfragmented messages as wpabuf without extra copy */
432214501Srpaulo		wpabuf_set(&tmpbuf, pos, end - pos);
433214501Srpaulo		data->in_buf = &tmpbuf;
434214501Srpaulo	}
435214501Srpaulo
436214501Srpaulo	if (ikev2_initiator_process(&data->ikev2, data->in_buf) < 0) {
437214501Srpaulo		if (data->in_buf == &tmpbuf)
438214501Srpaulo			data->in_buf = NULL;
439214501Srpaulo		eap_ikev2_state(data, FAIL);
440214501Srpaulo		return;
441214501Srpaulo	}
442214501Srpaulo
443214501Srpaulo	switch (data->ikev2.state) {
444214501Srpaulo	case SA_AUTH:
445214501Srpaulo		/* SA_INIT was sent out, so message have to be
446214501Srpaulo		 * integrity protected from now on. */
447214501Srpaulo		data->keys_ready = 1;
448214501Srpaulo		break;
449214501Srpaulo	case IKEV2_DONE:
450214501Srpaulo		if (data->state == FAIL)
451214501Srpaulo			break;
452214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication completed "
453214501Srpaulo			   "successfully");
454214501Srpaulo		if (eap_ikev2_server_keymat(data))
455214501Srpaulo			break;
456214501Srpaulo		eap_ikev2_state(data, DONE);
457214501Srpaulo		break;
458214501Srpaulo	default:
459214501Srpaulo		break;
460214501Srpaulo	}
461214501Srpaulo
462214501Srpaulo	if (data->in_buf != &tmpbuf)
463214501Srpaulo		wpabuf_free(data->in_buf);
464214501Srpaulo	data->in_buf = NULL;
465214501Srpaulo}
466214501Srpaulo
467214501Srpaulo
468214501Srpaulostatic Boolean eap_ikev2_isDone(struct eap_sm *sm, void *priv)
469214501Srpaulo{
470214501Srpaulo	struct eap_ikev2_data *data = priv;
471214501Srpaulo	return data->state == DONE || data->state == FAIL;
472214501Srpaulo}
473214501Srpaulo
474214501Srpaulo
475214501Srpaulostatic Boolean eap_ikev2_isSuccess(struct eap_sm *sm, void *priv)
476214501Srpaulo{
477214501Srpaulo	struct eap_ikev2_data *data = priv;
478214501Srpaulo	return data->state == DONE && data->ikev2.state == IKEV2_DONE &&
479214501Srpaulo		data->keymat_ok;
480214501Srpaulo}
481214501Srpaulo
482214501Srpaulo
483214501Srpaulostatic u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len)
484214501Srpaulo{
485214501Srpaulo	struct eap_ikev2_data *data = priv;
486214501Srpaulo	u8 *key;
487214501Srpaulo
488214501Srpaulo	if (data->state != DONE || !data->keymat_ok)
489214501Srpaulo		return NULL;
490214501Srpaulo
491214501Srpaulo	key = os_malloc(EAP_MSK_LEN);
492214501Srpaulo	if (key) {
493214501Srpaulo		os_memcpy(key, data->keymat, EAP_MSK_LEN);
494214501Srpaulo		*len = EAP_MSK_LEN;
495214501Srpaulo	}
496214501Srpaulo
497214501Srpaulo	return key;
498214501Srpaulo}
499214501Srpaulo
500214501Srpaulo
501214501Srpaulostatic u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
502214501Srpaulo{
503214501Srpaulo	struct eap_ikev2_data *data = priv;
504214501Srpaulo	u8 *key;
505214501Srpaulo
506214501Srpaulo	if (data->state != DONE || !data->keymat_ok)
507214501Srpaulo		return NULL;
508214501Srpaulo
509214501Srpaulo	key = os_malloc(EAP_EMSK_LEN);
510214501Srpaulo	if (key) {
511214501Srpaulo		os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN);
512214501Srpaulo		*len = EAP_EMSK_LEN;
513214501Srpaulo	}
514214501Srpaulo
515214501Srpaulo	return key;
516214501Srpaulo}
517214501Srpaulo
518214501Srpaulo
519281806Srpaulostatic u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
520281806Srpaulo{
521281806Srpaulo	struct eap_ikev2_data *data = priv;
522281806Srpaulo	u8 *sid;
523281806Srpaulo	size_t sid_len;
524281806Srpaulo	size_t offset;
525281806Srpaulo
526281806Srpaulo	if (data->state != DONE || !data->keymat_ok)
527281806Srpaulo		return NULL;
528281806Srpaulo
529281806Srpaulo	sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len;
530281806Srpaulo	sid = os_malloc(sid_len);
531281806Srpaulo	if (sid) {
532281806Srpaulo		offset = 0;
533281806Srpaulo		sid[offset] = EAP_TYPE_IKEV2;
534281806Srpaulo		offset++;
535281806Srpaulo		os_memcpy(sid + offset, data->ikev2.i_nonce,
536281806Srpaulo			  data->ikev2.i_nonce_len);
537281806Srpaulo		offset += data->ikev2.i_nonce_len;
538281806Srpaulo		os_memcpy(sid + offset, data->ikev2.r_nonce,
539281806Srpaulo			  data->ikev2.r_nonce_len);
540281806Srpaulo		*len = sid_len;
541281806Srpaulo		wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id",
542281806Srpaulo			    sid, sid_len);
543281806Srpaulo	}
544281806Srpaulo
545281806Srpaulo	return sid;
546281806Srpaulo}
547281806Srpaulo
548281806Srpaulo
549214501Srpauloint eap_server_ikev2_register(void)
550214501Srpaulo{
551214501Srpaulo	struct eap_method *eap;
552214501Srpaulo
553214501Srpaulo	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
554214501Srpaulo				      EAP_VENDOR_IETF, EAP_TYPE_IKEV2,
555214501Srpaulo				      "IKEV2");
556214501Srpaulo	if (eap == NULL)
557214501Srpaulo		return -1;
558214501Srpaulo
559214501Srpaulo	eap->init = eap_ikev2_init;
560214501Srpaulo	eap->reset = eap_ikev2_reset;
561214501Srpaulo	eap->buildReq = eap_ikev2_buildReq;
562214501Srpaulo	eap->check = eap_ikev2_check;
563214501Srpaulo	eap->process = eap_ikev2_process;
564214501Srpaulo	eap->isDone = eap_ikev2_isDone;
565214501Srpaulo	eap->getKey = eap_ikev2_getKey;
566214501Srpaulo	eap->isSuccess = eap_ikev2_isSuccess;
567214501Srpaulo	eap->get_emsk = eap_ikev2_get_emsk;
568281806Srpaulo	eap->getSessionId = eap_ikev2_get_session_id;
569214501Srpaulo
570337817Scy	return eap_server_method_register(eap);
571214501Srpaulo}
572