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