xref: /freebsd/contrib/wpa/src/eap_server/eap_server_ikev2.c (revision c1d255d3ffdbe447de3ab875bf4e7d7accc5bfc5)
1e28a4053SRui Paulo /*
2e28a4053SRui Paulo  * EAP-IKEv2 server (RFC 5106)
3e28a4053SRui Paulo  * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
4e28a4053SRui Paulo  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
7e28a4053SRui Paulo  */
8e28a4053SRui Paulo 
9e28a4053SRui Paulo #include "includes.h"
10e28a4053SRui Paulo 
11e28a4053SRui Paulo #include "common.h"
12e28a4053SRui Paulo #include "eap_i.h"
13e28a4053SRui Paulo #include "eap_common/eap_ikev2_common.h"
14e28a4053SRui Paulo #include "ikev2.h"
15e28a4053SRui Paulo 
16e28a4053SRui Paulo 
17e28a4053SRui Paulo struct eap_ikev2_data {
18e28a4053SRui Paulo 	struct ikev2_initiator_data ikev2;
19e28a4053SRui Paulo 	enum { MSG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
20e28a4053SRui Paulo 	struct wpabuf *in_buf;
21e28a4053SRui Paulo 	struct wpabuf *out_buf;
22e28a4053SRui Paulo 	size_t out_used;
23e28a4053SRui Paulo 	size_t fragment_size;
24e28a4053SRui Paulo 	int keys_ready;
25e28a4053SRui Paulo 	u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN];
26e28a4053SRui Paulo 	int keymat_ok;
27e28a4053SRui Paulo };
28e28a4053SRui Paulo 
29e28a4053SRui Paulo 
eap_ikev2_get_shared_secret(void * ctx,const u8 * IDr,size_t IDr_len,size_t * secret_len)30e28a4053SRui Paulo static const u8 * eap_ikev2_get_shared_secret(void *ctx, const u8 *IDr,
31e28a4053SRui Paulo 					      size_t IDr_len,
32e28a4053SRui Paulo 					      size_t *secret_len)
33e28a4053SRui Paulo {
34e28a4053SRui Paulo 	struct eap_sm *sm = ctx;
35e28a4053SRui Paulo 
36e28a4053SRui Paulo 	if (IDr == NULL) {
37e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No IDr received - default "
38e28a4053SRui Paulo 			   "to user identity from EAP-Identity");
39e28a4053SRui Paulo 		IDr = sm->identity;
40e28a4053SRui Paulo 		IDr_len = sm->identity_len;
41e28a4053SRui Paulo 	}
42e28a4053SRui Paulo 
43e28a4053SRui Paulo 	if (eap_user_get(sm, IDr, IDr_len, 0) < 0 || sm->user == NULL ||
44e28a4053SRui Paulo 	    sm->user->password == NULL) {
45e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No user entry found");
46e28a4053SRui Paulo 		return NULL;
47e28a4053SRui Paulo 	}
48e28a4053SRui Paulo 
49e28a4053SRui Paulo 	*secret_len = sm->user->password_len;
50e28a4053SRui Paulo 	return sm->user->password;
51e28a4053SRui Paulo }
52e28a4053SRui Paulo 
53e28a4053SRui Paulo 
eap_ikev2_state_txt(int state)54e28a4053SRui Paulo static const char * eap_ikev2_state_txt(int state)
55e28a4053SRui Paulo {
56e28a4053SRui Paulo 	switch (state) {
57e28a4053SRui Paulo 	case MSG:
58e28a4053SRui Paulo 		return "MSG";
59e28a4053SRui Paulo 	case FRAG_ACK:
60e28a4053SRui Paulo 		return "FRAG_ACK";
61e28a4053SRui Paulo 	case WAIT_FRAG_ACK:
62e28a4053SRui Paulo 		return "WAIT_FRAG_ACK";
63e28a4053SRui Paulo 	case DONE:
64e28a4053SRui Paulo 		return "DONE";
65e28a4053SRui Paulo 	case FAIL:
66e28a4053SRui Paulo 		return "FAIL";
67e28a4053SRui Paulo 	default:
68e28a4053SRui Paulo 		return "?";
69e28a4053SRui Paulo 	}
70e28a4053SRui Paulo }
71e28a4053SRui Paulo 
72e28a4053SRui Paulo 
eap_ikev2_state(struct eap_ikev2_data * data,int state)73e28a4053SRui Paulo static void eap_ikev2_state(struct eap_ikev2_data *data, int state)
74e28a4053SRui Paulo {
75e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s",
76e28a4053SRui Paulo 		   eap_ikev2_state_txt(data->state),
77e28a4053SRui Paulo 		   eap_ikev2_state_txt(state));
78e28a4053SRui Paulo 	data->state = state;
79e28a4053SRui Paulo }
80e28a4053SRui Paulo 
81e28a4053SRui Paulo 
eap_ikev2_init(struct eap_sm * sm)82e28a4053SRui Paulo static void * eap_ikev2_init(struct eap_sm *sm)
83e28a4053SRui Paulo {
84e28a4053SRui Paulo 	struct eap_ikev2_data *data;
85e28a4053SRui Paulo 
86e28a4053SRui Paulo 	data = os_zalloc(sizeof(*data));
87e28a4053SRui Paulo 	if (data == NULL)
88e28a4053SRui Paulo 		return NULL;
89e28a4053SRui Paulo 	data->state = MSG;
90*c1d255d3SCy Schubert 	data->fragment_size = sm->cfg->fragment_size > 0 ?
91*c1d255d3SCy Schubert 		sm->cfg->fragment_size : IKEV2_FRAGMENT_SIZE;
92e28a4053SRui Paulo 	data->ikev2.state = SA_INIT;
93e28a4053SRui Paulo 	data->ikev2.peer_auth = PEER_AUTH_SECRET;
94e28a4053SRui Paulo 	data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2");
95e28a4053SRui Paulo 	if (data->ikev2.key_pad == NULL)
96e28a4053SRui Paulo 		goto failed;
97e28a4053SRui Paulo 	data->ikev2.key_pad_len = 21;
98e28a4053SRui Paulo 
99e28a4053SRui Paulo 	/* TODO: make proposals configurable */
100e28a4053SRui Paulo 	data->ikev2.proposal.proposal_num = 1;
101e28a4053SRui Paulo 	data->ikev2.proposal.integ = AUTH_HMAC_SHA1_96;
102e28a4053SRui Paulo 	data->ikev2.proposal.prf = PRF_HMAC_SHA1;
103e28a4053SRui Paulo 	data->ikev2.proposal.encr = ENCR_AES_CBC;
104e28a4053SRui Paulo 	data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP;
105e28a4053SRui Paulo 
106*c1d255d3SCy Schubert 	data->ikev2.IDi = os_memdup(sm->cfg->server_id, sm->cfg->server_id_len);
1075b9c547cSRui Paulo 	if (data->ikev2.IDi == NULL)
1085b9c547cSRui Paulo 		goto failed;
109*c1d255d3SCy Schubert 	data->ikev2.IDi_len = sm->cfg->server_id_len;
110e28a4053SRui Paulo 
111e28a4053SRui Paulo 	data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret;
112e28a4053SRui Paulo 	data->ikev2.cb_ctx = sm;
113e28a4053SRui Paulo 
114e28a4053SRui Paulo 	return data;
115e28a4053SRui Paulo 
116e28a4053SRui Paulo failed:
117e28a4053SRui Paulo 	ikev2_initiator_deinit(&data->ikev2);
118e28a4053SRui Paulo 	os_free(data);
119e28a4053SRui Paulo 	return NULL;
120e28a4053SRui Paulo }
121e28a4053SRui Paulo 
122e28a4053SRui Paulo 
eap_ikev2_reset(struct eap_sm * sm,void * priv)123e28a4053SRui Paulo static void eap_ikev2_reset(struct eap_sm *sm, void *priv)
124e28a4053SRui Paulo {
125e28a4053SRui Paulo 	struct eap_ikev2_data *data = priv;
126e28a4053SRui Paulo 	wpabuf_free(data->in_buf);
127e28a4053SRui Paulo 	wpabuf_free(data->out_buf);
128e28a4053SRui Paulo 	ikev2_initiator_deinit(&data->ikev2);
1295b9c547cSRui Paulo 	bin_clear_free(data, sizeof(*data));
130e28a4053SRui Paulo }
131e28a4053SRui Paulo 
132e28a4053SRui Paulo 
eap_ikev2_build_msg(struct eap_ikev2_data * data,u8 id)133e28a4053SRui Paulo static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, u8 id)
134e28a4053SRui Paulo {
135e28a4053SRui Paulo 	struct wpabuf *req;
136e28a4053SRui Paulo 	u8 flags;
137e28a4053SRui Paulo 	size_t send_len, plen, icv_len = 0;
138e28a4053SRui Paulo 
139e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Request");
140e28a4053SRui Paulo 
141e28a4053SRui Paulo 	flags = 0;
142e28a4053SRui Paulo 	send_len = wpabuf_len(data->out_buf) - data->out_used;
143e28a4053SRui Paulo 	if (1 + send_len > data->fragment_size) {
144e28a4053SRui Paulo 		send_len = data->fragment_size - 1;
145e28a4053SRui Paulo 		flags |= IKEV2_FLAGS_MORE_FRAGMENTS;
146e28a4053SRui Paulo 		if (data->out_used == 0) {
147e28a4053SRui Paulo 			flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
148e28a4053SRui Paulo 			send_len -= 4;
149e28a4053SRui Paulo 		}
150e28a4053SRui Paulo 	}
151e28a4053SRui Paulo 
152e28a4053SRui Paulo 	plen = 1 + send_len;
153e28a4053SRui Paulo 	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
154e28a4053SRui Paulo 		plen += 4;
155e28a4053SRui Paulo 	if (data->keys_ready) {
156e28a4053SRui Paulo 		const struct ikev2_integ_alg *integ;
157e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum "
158e28a4053SRui Paulo 			   "Data");
159e28a4053SRui Paulo 		flags |= IKEV2_FLAGS_ICV_INCLUDED;
160e28a4053SRui Paulo 		integ = ikev2_get_integ(data->ikev2.proposal.integ);
161e28a4053SRui Paulo 		if (integ == NULL) {
162e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
163e28a4053SRui Paulo 				   "transform / cannot generate ICV");
164e28a4053SRui Paulo 			return NULL;
165e28a4053SRui Paulo 		}
166e28a4053SRui Paulo 		icv_len = integ->hash_len;
167e28a4053SRui Paulo 
168e28a4053SRui Paulo 		plen += icv_len;
169e28a4053SRui Paulo 	}
170e28a4053SRui Paulo 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen,
171e28a4053SRui Paulo 			    EAP_CODE_REQUEST, id);
172e28a4053SRui Paulo 	if (req == NULL)
173e28a4053SRui Paulo 		return NULL;
174e28a4053SRui Paulo 
175e28a4053SRui Paulo 	wpabuf_put_u8(req, flags); /* Flags */
176e28a4053SRui Paulo 	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
177e28a4053SRui Paulo 		wpabuf_put_be32(req, wpabuf_len(data->out_buf));
178e28a4053SRui Paulo 
179e28a4053SRui Paulo 	wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
180e28a4053SRui Paulo 			send_len);
181e28a4053SRui Paulo 	data->out_used += send_len;
182e28a4053SRui Paulo 
183e28a4053SRui Paulo 	if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
184e28a4053SRui Paulo 		const u8 *msg = wpabuf_head(req);
185e28a4053SRui Paulo 		size_t len = wpabuf_len(req);
186e28a4053SRui Paulo 		ikev2_integ_hash(data->ikev2.proposal.integ,
187e28a4053SRui Paulo 				 data->ikev2.keys.SK_ai,
188e28a4053SRui Paulo 				 data->ikev2.keys.SK_integ_len,
189e28a4053SRui Paulo 				 msg, len, wpabuf_put(req, icv_len));
190e28a4053SRui Paulo 	}
191e28a4053SRui Paulo 
192e28a4053SRui Paulo 	if (data->out_used == wpabuf_len(data->out_buf)) {
193e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
194e28a4053SRui Paulo 			   "(message sent completely)",
195e28a4053SRui Paulo 			   (unsigned long) send_len);
196e28a4053SRui Paulo 		wpabuf_free(data->out_buf);
197e28a4053SRui Paulo 		data->out_buf = NULL;
198e28a4053SRui Paulo 		data->out_used = 0;
199e28a4053SRui Paulo 	} else {
200e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
201e28a4053SRui Paulo 			   "(%lu more to send)", (unsigned long) send_len,
202e28a4053SRui Paulo 			   (unsigned long) wpabuf_len(data->out_buf) -
203e28a4053SRui Paulo 			   data->out_used);
204e28a4053SRui Paulo 		eap_ikev2_state(data, WAIT_FRAG_ACK);
205e28a4053SRui Paulo 	}
206e28a4053SRui Paulo 
207e28a4053SRui Paulo 	return req;
208e28a4053SRui Paulo }
209e28a4053SRui Paulo 
210e28a4053SRui Paulo 
eap_ikev2_buildReq(struct eap_sm * sm,void * priv,u8 id)211e28a4053SRui Paulo static struct wpabuf * eap_ikev2_buildReq(struct eap_sm *sm, void *priv, u8 id)
212e28a4053SRui Paulo {
213e28a4053SRui Paulo 	struct eap_ikev2_data *data = priv;
214e28a4053SRui Paulo 
215e28a4053SRui Paulo 	switch (data->state) {
216e28a4053SRui Paulo 	case MSG:
217e28a4053SRui Paulo 		if (data->out_buf == NULL) {
218e28a4053SRui Paulo 			data->out_buf = ikev2_initiator_build(&data->ikev2);
219e28a4053SRui Paulo 			if (data->out_buf == NULL) {
220e28a4053SRui Paulo 				wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to "
221e28a4053SRui Paulo 					   "generate IKEv2 message");
222e28a4053SRui Paulo 				return NULL;
223e28a4053SRui Paulo 			}
224e28a4053SRui Paulo 			data->out_used = 0;
225e28a4053SRui Paulo 		}
22685732ac8SCy Schubert 		/* fall through */
227e28a4053SRui Paulo 	case WAIT_FRAG_ACK:
228e28a4053SRui Paulo 		return eap_ikev2_build_msg(data, id);
229e28a4053SRui Paulo 	case FRAG_ACK:
230e28a4053SRui Paulo 		return eap_ikev2_build_frag_ack(id, EAP_CODE_REQUEST);
231e28a4053SRui Paulo 	default:
232e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected state %d in "
233e28a4053SRui Paulo 			   "buildReq", data->state);
234e28a4053SRui Paulo 		return NULL;
235e28a4053SRui Paulo 	}
236e28a4053SRui Paulo }
237e28a4053SRui Paulo 
238e28a4053SRui Paulo 
eap_ikev2_check(struct eap_sm * sm,void * priv,struct wpabuf * respData)239*c1d255d3SCy Schubert static bool eap_ikev2_check(struct eap_sm *sm, void *priv,
240e28a4053SRui Paulo 			    struct wpabuf *respData)
241e28a4053SRui Paulo {
242e28a4053SRui Paulo 	const u8 *pos;
243e28a4053SRui Paulo 	size_t len;
244e28a4053SRui Paulo 
245e28a4053SRui Paulo 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData,
246e28a4053SRui Paulo 			       &len);
247e28a4053SRui Paulo 	if (pos == NULL) {
248e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid frame");
249*c1d255d3SCy Schubert 		return true;
250e28a4053SRui Paulo 	}
251e28a4053SRui Paulo 
252*c1d255d3SCy Schubert 	return false;
253e28a4053SRui Paulo }
254e28a4053SRui Paulo 
255e28a4053SRui Paulo 
eap_ikev2_process_icv(struct eap_ikev2_data * data,const struct wpabuf * respData,u8 flags,const u8 * pos,const u8 ** end,int frag_ack)256e28a4053SRui Paulo static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
257e28a4053SRui Paulo 				 const struct wpabuf *respData,
2585b9c547cSRui Paulo 				 u8 flags, const u8 *pos, const u8 **end,
2595b9c547cSRui Paulo 				 int frag_ack)
260e28a4053SRui Paulo {
261e28a4053SRui Paulo 	if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
262e28a4053SRui Paulo 		int icv_len = eap_ikev2_validate_icv(
263e28a4053SRui Paulo 			data->ikev2.proposal.integ, &data->ikev2.keys, 0,
264e28a4053SRui Paulo 			respData, pos, *end);
265e28a4053SRui Paulo 		if (icv_len < 0)
266e28a4053SRui Paulo 			return -1;
267e28a4053SRui Paulo 		/* Hide Integrity Checksum Data from further processing */
268e28a4053SRui Paulo 		*end -= icv_len;
2695b9c547cSRui Paulo 	} else if (data->keys_ready && !frag_ack) {
270e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
271e28a4053SRui Paulo 			   "included integrity checksum");
272e28a4053SRui Paulo 		return -1;
273e28a4053SRui Paulo 	}
274e28a4053SRui Paulo 
275e28a4053SRui Paulo 	return 0;
276e28a4053SRui Paulo }
277e28a4053SRui Paulo 
278e28a4053SRui Paulo 
eap_ikev2_process_cont(struct eap_ikev2_data * data,const u8 * buf,size_t len)279e28a4053SRui Paulo static int eap_ikev2_process_cont(struct eap_ikev2_data *data,
280e28a4053SRui Paulo 				  const u8 *buf, size_t len)
281e28a4053SRui Paulo {
282e28a4053SRui Paulo 	/* Process continuation of a pending message */
283e28a4053SRui Paulo 	if (len > wpabuf_tailroom(data->in_buf)) {
284e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow");
285e28a4053SRui Paulo 		eap_ikev2_state(data, FAIL);
286e28a4053SRui Paulo 		return -1;
287e28a4053SRui Paulo 	}
288e28a4053SRui Paulo 
289e28a4053SRui Paulo 	wpabuf_put_data(data->in_buf, buf, len);
290e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting for %lu "
291e28a4053SRui Paulo 		   "bytes more", (unsigned long) len,
292e28a4053SRui Paulo 		   (unsigned long) wpabuf_tailroom(data->in_buf));
293e28a4053SRui Paulo 
294e28a4053SRui Paulo 	return 0;
295e28a4053SRui Paulo }
296e28a4053SRui Paulo 
297e28a4053SRui Paulo 
eap_ikev2_process_fragment(struct eap_ikev2_data * data,u8 flags,u32 message_length,const u8 * buf,size_t len)298e28a4053SRui Paulo static int eap_ikev2_process_fragment(struct eap_ikev2_data *data,
299e28a4053SRui Paulo 				      u8 flags, u32 message_length,
300e28a4053SRui Paulo 				      const u8 *buf, size_t len)
301e28a4053SRui Paulo {
302e28a4053SRui Paulo 	/* Process a fragment that is not the last one of the message */
303e28a4053SRui Paulo 	if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) {
304e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in "
305e28a4053SRui Paulo 			   "a fragmented packet");
306e28a4053SRui Paulo 		return -1;
307e28a4053SRui Paulo 	}
308e28a4053SRui Paulo 
309e28a4053SRui Paulo 	if (data->in_buf == NULL) {
310e28a4053SRui Paulo 		/* First fragment of the message */
3115b9c547cSRui Paulo 		if (message_length > 50000) {
3125b9c547cSRui Paulo 			/* Limit maximum memory allocation */
3135b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG,
3145b9c547cSRui Paulo 				   "EAP-IKEV2: Ignore too long message");
3155b9c547cSRui Paulo 			return -1;
3165b9c547cSRui Paulo 		}
317e28a4053SRui Paulo 		data->in_buf = wpabuf_alloc(message_length);
318e28a4053SRui Paulo 		if (data->in_buf == NULL) {
319e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
320e28a4053SRui Paulo 				   "message");
321e28a4053SRui Paulo 			return -1;
322e28a4053SRui Paulo 		}
323e28a4053SRui Paulo 		wpabuf_put_data(data->in_buf, buf, len);
324e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first "
325e28a4053SRui Paulo 			   "fragment, waiting for %lu bytes more",
326e28a4053SRui Paulo 			   (unsigned long) len,
327e28a4053SRui Paulo 			   (unsigned long) wpabuf_tailroom(data->in_buf));
328e28a4053SRui Paulo 	}
329e28a4053SRui Paulo 
330e28a4053SRui Paulo 	return 0;
331e28a4053SRui Paulo }
332e28a4053SRui Paulo 
333e28a4053SRui Paulo 
eap_ikev2_server_keymat(struct eap_ikev2_data * data)334e28a4053SRui Paulo static int eap_ikev2_server_keymat(struct eap_ikev2_data *data)
335e28a4053SRui Paulo {
336e28a4053SRui Paulo 	if (eap_ikev2_derive_keymat(
337e28a4053SRui Paulo 		    data->ikev2.proposal.prf, &data->ikev2.keys,
338e28a4053SRui Paulo 		    data->ikev2.i_nonce, data->ikev2.i_nonce_len,
339e28a4053SRui Paulo 		    data->ikev2.r_nonce, data->ikev2.r_nonce_len,
340e28a4053SRui Paulo 		    data->keymat) < 0) {
341e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to derive "
342e28a4053SRui Paulo 			   "key material");
343e28a4053SRui Paulo 		return -1;
344e28a4053SRui Paulo 	}
345e28a4053SRui Paulo 	data->keymat_ok = 1;
346e28a4053SRui Paulo 	return 0;
347e28a4053SRui Paulo }
348e28a4053SRui Paulo 
349e28a4053SRui Paulo 
eap_ikev2_process(struct eap_sm * sm,void * priv,struct wpabuf * respData)350e28a4053SRui Paulo static void eap_ikev2_process(struct eap_sm *sm, void *priv,
351e28a4053SRui Paulo 			      struct wpabuf *respData)
352e28a4053SRui Paulo {
353e28a4053SRui Paulo 	struct eap_ikev2_data *data = priv;
354e28a4053SRui Paulo 	const u8 *start, *pos, *end;
355e28a4053SRui Paulo 	size_t len;
356e28a4053SRui Paulo 	u8 flags;
357e28a4053SRui Paulo 	u32 message_length = 0;
358e28a4053SRui Paulo 	struct wpabuf tmpbuf;
359e28a4053SRui Paulo 
360e28a4053SRui Paulo 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData,
361e28a4053SRui Paulo 			       &len);
362e28a4053SRui Paulo 	if (pos == NULL)
363e28a4053SRui Paulo 		return; /* Should not happen; message already verified */
364e28a4053SRui Paulo 
365e28a4053SRui Paulo 	start = pos;
366e28a4053SRui Paulo 	end = start + len;
367e28a4053SRui Paulo 
368e28a4053SRui Paulo 	if (len == 0) {
369e28a4053SRui Paulo 		/* fragment ack */
370e28a4053SRui Paulo 		flags = 0;
371e28a4053SRui Paulo 	} else
372e28a4053SRui Paulo 		flags = *pos++;
373e28a4053SRui Paulo 
3745b9c547cSRui Paulo 	if (eap_ikev2_process_icv(data, respData, flags, pos, &end,
3755b9c547cSRui Paulo 				  data->state == WAIT_FRAG_ACK && len == 0) < 0)
3765b9c547cSRui Paulo 	{
377e28a4053SRui Paulo 		eap_ikev2_state(data, FAIL);
378e28a4053SRui Paulo 		return;
379e28a4053SRui Paulo 	}
380e28a4053SRui Paulo 
381e28a4053SRui Paulo 	if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) {
382e28a4053SRui Paulo 		if (end - pos < 4) {
383e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow");
384e28a4053SRui Paulo 			eap_ikev2_state(data, FAIL);
385e28a4053SRui Paulo 			return;
386e28a4053SRui Paulo 		}
387e28a4053SRui Paulo 		message_length = WPA_GET_BE32(pos);
388e28a4053SRui Paulo 		pos += 4;
389e28a4053SRui Paulo 
390e28a4053SRui Paulo 		if (message_length < (u32) (end - pos)) {
391e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message "
392e28a4053SRui Paulo 				   "Length (%d; %ld remaining in this msg)",
393e28a4053SRui Paulo 				   message_length, (long) (end - pos));
394e28a4053SRui Paulo 			eap_ikev2_state(data, FAIL);
395e28a4053SRui Paulo 			return;
396e28a4053SRui Paulo 		}
397e28a4053SRui Paulo 	}
398e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x "
399e28a4053SRui Paulo 		   "Message Length %u", flags, message_length);
400e28a4053SRui Paulo 
401e28a4053SRui Paulo 	if (data->state == WAIT_FRAG_ACK) {
402e28a4053SRui Paulo 		if (len != 0) {
403e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
404e28a4053SRui Paulo 				   "in WAIT_FRAG_ACK state");
405e28a4053SRui Paulo 			eap_ikev2_state(data, FAIL);
406e28a4053SRui Paulo 			return;
407e28a4053SRui Paulo 		}
408e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged");
409e28a4053SRui Paulo 		eap_ikev2_state(data, MSG);
410e28a4053SRui Paulo 		return;
411e28a4053SRui Paulo 	}
412e28a4053SRui Paulo 
413e28a4053SRui Paulo 	if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) {
414e28a4053SRui Paulo 		eap_ikev2_state(data, FAIL);
415e28a4053SRui Paulo 		return;
416e28a4053SRui Paulo 	}
417e28a4053SRui Paulo 
418e28a4053SRui Paulo 	if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) {
419e28a4053SRui Paulo 		if (eap_ikev2_process_fragment(data, flags, message_length,
420e28a4053SRui Paulo 					       pos, end - pos) < 0)
421e28a4053SRui Paulo 			eap_ikev2_state(data, FAIL);
422e28a4053SRui Paulo 		else
423e28a4053SRui Paulo 			eap_ikev2_state(data, FRAG_ACK);
424e28a4053SRui Paulo 		return;
425e28a4053SRui Paulo 	} else if (data->state == FRAG_ACK) {
426e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
427e28a4053SRui Paulo 		data->state = MSG;
428e28a4053SRui Paulo 	}
429e28a4053SRui Paulo 
430e28a4053SRui Paulo 	if (data->in_buf == NULL) {
431e28a4053SRui Paulo 		/* Wrap unfragmented messages as wpabuf without extra copy */
432e28a4053SRui Paulo 		wpabuf_set(&tmpbuf, pos, end - pos);
433e28a4053SRui Paulo 		data->in_buf = &tmpbuf;
434e28a4053SRui Paulo 	}
435e28a4053SRui Paulo 
436e28a4053SRui Paulo 	if (ikev2_initiator_process(&data->ikev2, data->in_buf) < 0) {
437e28a4053SRui Paulo 		if (data->in_buf == &tmpbuf)
438e28a4053SRui Paulo 			data->in_buf = NULL;
439e28a4053SRui Paulo 		eap_ikev2_state(data, FAIL);
440e28a4053SRui Paulo 		return;
441e28a4053SRui Paulo 	}
442e28a4053SRui Paulo 
443e28a4053SRui Paulo 	switch (data->ikev2.state) {
444e28a4053SRui Paulo 	case SA_AUTH:
445e28a4053SRui Paulo 		/* SA_INIT was sent out, so message have to be
446e28a4053SRui Paulo 		 * integrity protected from now on. */
447e28a4053SRui Paulo 		data->keys_ready = 1;
448e28a4053SRui Paulo 		break;
449e28a4053SRui Paulo 	case IKEV2_DONE:
450e28a4053SRui Paulo 		if (data->state == FAIL)
451e28a4053SRui Paulo 			break;
452e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication completed "
453e28a4053SRui Paulo 			   "successfully");
454e28a4053SRui Paulo 		if (eap_ikev2_server_keymat(data))
455e28a4053SRui Paulo 			break;
456e28a4053SRui Paulo 		eap_ikev2_state(data, DONE);
457e28a4053SRui Paulo 		break;
458e28a4053SRui Paulo 	default:
459e28a4053SRui Paulo 		break;
460e28a4053SRui Paulo 	}
461e28a4053SRui Paulo 
462e28a4053SRui Paulo 	if (data->in_buf != &tmpbuf)
463e28a4053SRui Paulo 		wpabuf_free(data->in_buf);
464e28a4053SRui Paulo 	data->in_buf = NULL;
465e28a4053SRui Paulo }
466e28a4053SRui Paulo 
467e28a4053SRui Paulo 
eap_ikev2_isDone(struct eap_sm * sm,void * priv)468*c1d255d3SCy Schubert static bool eap_ikev2_isDone(struct eap_sm *sm, void *priv)
469e28a4053SRui Paulo {
470e28a4053SRui Paulo 	struct eap_ikev2_data *data = priv;
471e28a4053SRui Paulo 	return data->state == DONE || data->state == FAIL;
472e28a4053SRui Paulo }
473e28a4053SRui Paulo 
474e28a4053SRui Paulo 
eap_ikev2_isSuccess(struct eap_sm * sm,void * priv)475*c1d255d3SCy Schubert static bool eap_ikev2_isSuccess(struct eap_sm *sm, void *priv)
476e28a4053SRui Paulo {
477e28a4053SRui Paulo 	struct eap_ikev2_data *data = priv;
478e28a4053SRui Paulo 	return data->state == DONE && data->ikev2.state == IKEV2_DONE &&
479e28a4053SRui Paulo 		data->keymat_ok;
480e28a4053SRui Paulo }
481e28a4053SRui Paulo 
482e28a4053SRui Paulo 
eap_ikev2_getKey(struct eap_sm * sm,void * priv,size_t * len)483e28a4053SRui Paulo static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len)
484e28a4053SRui Paulo {
485e28a4053SRui Paulo 	struct eap_ikev2_data *data = priv;
486e28a4053SRui Paulo 	u8 *key;
487e28a4053SRui Paulo 
488e28a4053SRui Paulo 	if (data->state != DONE || !data->keymat_ok)
489e28a4053SRui Paulo 		return NULL;
490e28a4053SRui Paulo 
491e28a4053SRui Paulo 	key = os_malloc(EAP_MSK_LEN);
492e28a4053SRui Paulo 	if (key) {
493e28a4053SRui Paulo 		os_memcpy(key, data->keymat, EAP_MSK_LEN);
494e28a4053SRui Paulo 		*len = EAP_MSK_LEN;
495e28a4053SRui Paulo 	}
496e28a4053SRui Paulo 
497e28a4053SRui Paulo 	return key;
498e28a4053SRui Paulo }
499e28a4053SRui Paulo 
500e28a4053SRui Paulo 
eap_ikev2_get_emsk(struct eap_sm * sm,void * priv,size_t * len)501e28a4053SRui Paulo static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
502e28a4053SRui Paulo {
503e28a4053SRui Paulo 	struct eap_ikev2_data *data = priv;
504e28a4053SRui Paulo 	u8 *key;
505e28a4053SRui Paulo 
506e28a4053SRui Paulo 	if (data->state != DONE || !data->keymat_ok)
507e28a4053SRui Paulo 		return NULL;
508e28a4053SRui Paulo 
509e28a4053SRui Paulo 	key = os_malloc(EAP_EMSK_LEN);
510e28a4053SRui Paulo 	if (key) {
511e28a4053SRui Paulo 		os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN);
512e28a4053SRui Paulo 		*len = EAP_EMSK_LEN;
513e28a4053SRui Paulo 	}
514e28a4053SRui Paulo 
515e28a4053SRui Paulo 	return key;
516e28a4053SRui Paulo }
517e28a4053SRui Paulo 
518e28a4053SRui Paulo 
eap_ikev2_get_session_id(struct eap_sm * sm,void * priv,size_t * len)5195b9c547cSRui Paulo static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
5205b9c547cSRui Paulo {
5215b9c547cSRui Paulo 	struct eap_ikev2_data *data = priv;
5225b9c547cSRui Paulo 	u8 *sid;
5235b9c547cSRui Paulo 	size_t sid_len;
5245b9c547cSRui Paulo 	size_t offset;
5255b9c547cSRui Paulo 
5265b9c547cSRui Paulo 	if (data->state != DONE || !data->keymat_ok)
5275b9c547cSRui Paulo 		return NULL;
5285b9c547cSRui Paulo 
5295b9c547cSRui Paulo 	sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len;
5305b9c547cSRui Paulo 	sid = os_malloc(sid_len);
5315b9c547cSRui Paulo 	if (sid) {
5325b9c547cSRui Paulo 		offset = 0;
5335b9c547cSRui Paulo 		sid[offset] = EAP_TYPE_IKEV2;
5345b9c547cSRui Paulo 		offset++;
5355b9c547cSRui Paulo 		os_memcpy(sid + offset, data->ikev2.i_nonce,
5365b9c547cSRui Paulo 			  data->ikev2.i_nonce_len);
5375b9c547cSRui Paulo 		offset += data->ikev2.i_nonce_len;
5385b9c547cSRui Paulo 		os_memcpy(sid + offset, data->ikev2.r_nonce,
5395b9c547cSRui Paulo 			  data->ikev2.r_nonce_len);
5405b9c547cSRui Paulo 		*len = sid_len;
5415b9c547cSRui Paulo 		wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id",
5425b9c547cSRui Paulo 			    sid, sid_len);
5435b9c547cSRui Paulo 	}
5445b9c547cSRui Paulo 
5455b9c547cSRui Paulo 	return sid;
5465b9c547cSRui Paulo }
5475b9c547cSRui Paulo 
5485b9c547cSRui Paulo 
eap_server_ikev2_register(void)549e28a4053SRui Paulo int eap_server_ikev2_register(void)
550e28a4053SRui Paulo {
551e28a4053SRui Paulo 	struct eap_method *eap;
552e28a4053SRui Paulo 
553e28a4053SRui Paulo 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
554e28a4053SRui Paulo 				      EAP_VENDOR_IETF, EAP_TYPE_IKEV2,
555e28a4053SRui Paulo 				      "IKEV2");
556e28a4053SRui Paulo 	if (eap == NULL)
557e28a4053SRui Paulo 		return -1;
558e28a4053SRui Paulo 
559e28a4053SRui Paulo 	eap->init = eap_ikev2_init;
560e28a4053SRui Paulo 	eap->reset = eap_ikev2_reset;
561e28a4053SRui Paulo 	eap->buildReq = eap_ikev2_buildReq;
562e28a4053SRui Paulo 	eap->check = eap_ikev2_check;
563e28a4053SRui Paulo 	eap->process = eap_ikev2_process;
564e28a4053SRui Paulo 	eap->isDone = eap_ikev2_isDone;
565e28a4053SRui Paulo 	eap->getKey = eap_ikev2_getKey;
566e28a4053SRui Paulo 	eap->isSuccess = eap_ikev2_isSuccess;
567e28a4053SRui Paulo 	eap->get_emsk = eap_ikev2_get_emsk;
5685b9c547cSRui Paulo 	eap->getSessionId = eap_ikev2_get_session_id;
569e28a4053SRui Paulo 
570780fb4a2SCy Schubert 	return eap_server_method_register(eap);
571e28a4053SRui Paulo }
572