EAP-SIM/AKA crypto helper supports key derivation for fast reauthentication
[strongswan.git] / src / charon / plugins / eap_aka / eap_aka_server.c
1 /*
2 * Copyright (C) 2006-2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "eap_aka_server.h"
17
18 #include <daemon.h>
19 #include <library.h>
20
21 #include <simaka_message.h>
22 #include <simaka_crypto.h>
23
24 typedef struct private_eap_aka_server_t private_eap_aka_server_t;
25
26 /**
27 * Private data of an eap_aka_server_t object.
28 */
29 struct private_eap_aka_server_t {
30
31 /**
32 * Public authenticator_t interface.
33 */
34 eap_aka_server_t public;
35
36 /**
37 * EAP-AKA crypto helper
38 */
39 simaka_crypto_t *crypto;
40
41 /**
42 * ID of the peer
43 */
44 identification_t *peer;
45
46 /**
47 * EAP identifier value
48 */
49 u_int8_t identifier;
50
51 /**
52 * MSK
53 */
54 chunk_t msk;
55
56 /**
57 * Expected Result XRES
58 */
59 chunk_t xres;
60
61 /**
62 * Random value RAND
63 */
64 chunk_t rand;
65
66 /**
67 * EAP-AKA message we have initiated
68 */
69 simaka_subtype_t pending;
70
71 /**
72 * Did the client send a synchronize request?
73 */
74 bool synchronized;
75 };
76
77 /**
78 * Check if an unknown attribute is skippable
79 */
80 static bool attribute_skippable(simaka_attribute_t attribute)
81 {
82 if (attribute >= 0 && attribute <= 127)
83 {
84 DBG1(DBG_IKE, "ignoring skippable attribute %N",
85 simaka_attribute_names, attribute);
86 return TRUE;
87 }
88 return FALSE;
89 }
90
91 /**
92 * Implementation of eap_method_t.initiate
93 */
94 static status_t initiate(private_eap_aka_server_t *this, eap_payload_t **out)
95 {
96 simaka_message_t *message;
97 enumerator_t *enumerator;
98 sim_provider_t *provider;
99 char rand[AKA_RAND_LEN], xres[AKA_RES_LEN];
100 char ck[AKA_CK_LEN], ik[AKA_IK_LEN], autn[AKA_AUTN_LEN];
101 chunk_t data, mk;
102 bool found = FALSE;
103
104 enumerator = charon->sim->create_provider_enumerator(charon->sim);
105 while (enumerator->enumerate(enumerator, &provider))
106 {
107 if (provider->get_quintuplet(provider, this->peer,
108 rand, xres, ck, ik, autn))
109 {
110 found = TRUE;
111 break;
112 }
113 }
114 enumerator->destroy(enumerator);
115 if (!found)
116 {
117 DBG1(DBG_IKE, "no AKA provider found with quintuplets for '%Y'",
118 this->peer);
119 return FAILED;
120 }
121
122 data = chunk_cata("cc", chunk_create(ik, AKA_IK_LEN),
123 chunk_create(ck, AKA_CK_LEN));
124 free(this->msk.ptr);
125 this->msk = this->crypto->derive_keys_full(this->crypto, this->peer,
126 data, &mk);
127 free(mk.ptr);
128 this->rand = chunk_clone(chunk_create(rand, AKA_RAND_LEN));
129 this->xres = chunk_clone(chunk_create(xres, AKA_RES_LEN));
130
131 message = simaka_message_create(TRUE, this->identifier++, EAP_AKA,
132 AKA_CHALLENGE, this->crypto);
133 message->add_attribute(message, AT_RAND, this->rand);
134 message->add_attribute(message, AT_AUTN, chunk_create(autn, AKA_AUTN_LEN));
135 *out = message->generate(message, chunk_empty);
136 message->destroy(message);
137
138 this->pending = AKA_CHALLENGE;
139 return NEED_MORE;
140 }
141
142 /**
143 * Process EAP-AKA/Response/Challenge message
144 */
145 static status_t process_challenge(private_eap_aka_server_t *this,
146 simaka_message_t *in)
147 {
148 enumerator_t *enumerator;
149 simaka_attribute_t type;
150 chunk_t data, res = chunk_empty;
151
152 if (this->pending != AKA_CHALLENGE)
153 {
154 DBG1(DBG_IKE, "received %N, but not expected",
155 simaka_subtype_names, AKA_CHALLENGE);
156 return FAILED;
157 }
158 enumerator = in->create_attribute_enumerator(in);
159 while (enumerator->enumerate(enumerator, &type, &data))
160 {
161 switch (type)
162 {
163 case AT_RES:
164 res = data;
165 break;
166 default:
167 if (!attribute_skippable(type))
168 {
169 enumerator->destroy(enumerator);
170 DBG1(DBG_IKE, "found non skippable attribute %N",
171 simaka_attribute_names, type);
172 return FAILED;
173 }
174 break;
175 }
176 }
177 enumerator->destroy(enumerator);
178
179 /* verify MAC of EAP message, AT_MAC */
180 if (!in->verify(in, chunk_empty))
181 {
182 DBG1(DBG_IKE, "AT_MAC verification failed");
183 return FAILED;
184 }
185 /* compare received RES against stored XRES */
186 if (!chunk_equals(res, this->xres))
187 {
188 DBG1(DBG_IKE, "received RES does not match XRES");
189 return FAILED;
190 }
191 return SUCCESS;
192 }
193
194 /**
195 * Process EAP-AKA/Response/SynchronizationFailure message
196 */
197 static status_t process_synchronize(private_eap_aka_server_t *this,
198 simaka_message_t *in, eap_payload_t **out)
199 {
200 sim_provider_t *provider;
201 enumerator_t *enumerator;
202 simaka_attribute_t type;
203 chunk_t data, auts = chunk_empty;
204 bool found = FALSE;
205
206 if (this->synchronized)
207 {
208 DBG1(DBG_IKE, "received %N, but peer did already resynchronize",
209 simaka_subtype_names, AKA_SYNCHRONIZATION_FAILURE);
210 return FAILED;
211 }
212
213 DBG1(DBG_IKE, "received synchronization request, retrying...");
214
215 enumerator = in->create_attribute_enumerator(in);
216 while (enumerator->enumerate(enumerator, &type, &data))
217 {
218 switch (type)
219 {
220 case AT_AUTS:
221 auts = data;
222 break;
223 default:
224 if (!attribute_skippable(type))
225 {
226 enumerator->destroy(enumerator);
227 DBG1(DBG_IKE, "found non skippable attribute %N",
228 simaka_attribute_names, type);
229 return FAILED;
230 }
231 break;
232 }
233 }
234 enumerator->destroy(enumerator);
235
236 if (!auts.len)
237 {
238 DBG1(DBG_IKE, "synchronization request didn't contain usable AUTS");
239 return FAILED;
240 }
241
242 enumerator = charon->sim->create_provider_enumerator(charon->sim);
243 while (enumerator->enumerate(enumerator, &provider))
244 {
245 if (provider->resync(provider, this->peer, this->rand.ptr, auts.ptr))
246 {
247 found = TRUE;
248 break;
249 }
250 }
251 enumerator->destroy(enumerator);
252
253 if (!found)
254 {
255 DBG1(DBG_IKE, "no AKA provider found supporting "
256 "resynchronization for '%Y'", this->peer);
257 return FAILED;
258 }
259 this->synchronized = TRUE;
260 return initiate(this, out);
261 }
262
263 /**
264 * Process EAP-AKA/Response/ClientErrorCode message
265 */
266 static status_t process_client_error(private_eap_aka_server_t *this,
267 simaka_message_t *in)
268 {
269 enumerator_t *enumerator;
270 simaka_attribute_t type;
271 chunk_t data;
272
273 enumerator = in->create_attribute_enumerator(in);
274 while (enumerator->enumerate(enumerator, &type, &data))
275 {
276 if (type == AT_CLIENT_ERROR_CODE)
277 {
278 u_int16_t code;
279
280 memcpy(&code, data.ptr, sizeof(code));
281 DBG1(DBG_IKE, "received EAP-AKA client error '%N'",
282 simaka_client_error_names, ntohs(code));
283 }
284 else if (!simaka_attribute_skippable(type))
285 {
286 break;
287 }
288 }
289 enumerator->destroy(enumerator);
290 return FAILED;
291 }
292
293 /**
294 * Process EAP-AKA/Response/AuthenticationReject message
295 */
296 static status_t process_authentication_reject(private_eap_aka_server_t *this,
297 simaka_message_t *in)
298 {
299 DBG1(DBG_IKE, "received %N, authentication failed",
300 simaka_subtype_names, in->get_subtype(in));
301 return FAILED;
302 }
303
304 /**
305 * Implementation of eap_method_t.process
306 */
307 static status_t process(private_eap_aka_server_t *this,
308 eap_payload_t *in, eap_payload_t **out)
309 {
310 simaka_message_t *message;
311 status_t status;
312
313 message = simaka_message_create_from_payload(in, this->crypto);
314 if (!message)
315 {
316 return FAILED;
317 }
318 if (!message->parse(message))
319 {
320 message->destroy(message);
321 return FAILED;
322 }
323 switch (message->get_subtype(message))
324 {
325 case AKA_CHALLENGE:
326 status = process_challenge(this, message);
327 break;
328 case AKA_SYNCHRONIZATION_FAILURE:
329 status = process_synchronize(this, message, out);
330 break;
331 case AKA_CLIENT_ERROR:
332 status = process_client_error(this, message);
333 break;
334 case AKA_AUTHENTICATION_REJECT:
335 status = process_authentication_reject(this, message);
336 break;
337 default:
338 DBG1(DBG_IKE, "unable to process EAP-AKA subtype %N",
339 simaka_subtype_names, message->get_subtype(message));
340 status = FAILED;
341 break;
342 }
343 message->destroy(message);
344 return status;
345 }
346
347 /**
348 * Implementation of eap_method_t.get_type.
349 */
350 static eap_type_t get_type(private_eap_aka_server_t *this, u_int32_t *vendor)
351 {
352 *vendor = 0;
353 return EAP_AKA;
354 }
355
356 /**
357 * Implementation of eap_method_t.get_msk.
358 */
359 static status_t get_msk(private_eap_aka_server_t *this, chunk_t *msk)
360 {
361 if (this->msk.ptr)
362 {
363 *msk = this->msk;
364 return SUCCESS;
365 }
366 return FAILED;
367 }
368
369 /**
370 * Implementation of eap_method_t.is_mutual.
371 */
372 static bool is_mutual(private_eap_aka_server_t *this)
373 {
374 return TRUE;
375 }
376
377 /**
378 * Implementation of eap_method_t.destroy.
379 */
380 static void destroy(private_eap_aka_server_t *this)
381 {
382 this->crypto->destroy(this->crypto);
383 this->peer->destroy(this->peer);
384 free(this->msk.ptr);
385 free(this->xres.ptr);
386 free(this->rand.ptr);
387 free(this);
388 }
389
390 /*
391 * Described in header.
392 */
393 eap_aka_server_t *eap_aka_server_create(identification_t *server,
394 identification_t *peer)
395 {
396 private_eap_aka_server_t *this = malloc_thing(private_eap_aka_server_t);
397
398 this->public.interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
399 this->public.interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
400 this->public.interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
401 this->public.interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
402 this->public.interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
403 this->public.interface.destroy = (void(*)(eap_method_t*))destroy;
404
405 this->crypto = simaka_crypto_create();
406 if (!this->crypto)
407 {
408 free(this);
409 return NULL;
410 }
411 this->peer = peer->clone(peer);
412 this->msk = chunk_empty;
413 this->xres = chunk_empty;
414 this->rand = chunk_empty;
415 this->pending = 0;
416 this->synchronized = FALSE;
417 /* generate a non-zero identifier */
418 do {
419 this->identifier = random();
420 } while (!this->identifier);
421
422 return &this->public;
423 }
424