Pass SIM/AKA crypto helper to constructor of message
[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;
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, data);
126 this->rand = chunk_clone(chunk_create(rand, AKA_RAND_LEN));
127 this->xres = chunk_clone(chunk_create(xres, AKA_RES_LEN));
128
129 message = simaka_message_create(TRUE, this->identifier++, EAP_AKA,
130 AKA_CHALLENGE, this->crypto);
131 message->add_attribute(message, AT_RAND, this->rand);
132 message->add_attribute(message, AT_AUTN, chunk_create(autn, AKA_AUTN_LEN));
133 *out = message->generate(message, chunk_empty);
134 message->destroy(message);
135
136 this->pending = AKA_CHALLENGE;
137 return NEED_MORE;
138 }
139
140 /**
141 * Process EAP-AKA/Response/Challenge message
142 */
143 static status_t process_challenge(private_eap_aka_server_t *this,
144 simaka_message_t *in)
145 {
146 enumerator_t *enumerator;
147 simaka_attribute_t type;
148 chunk_t data, res = chunk_empty;
149
150 if (this->pending != AKA_CHALLENGE)
151 {
152 DBG1(DBG_IKE, "received %N, but not expected",
153 simaka_subtype_names, AKA_CHALLENGE);
154 return FAILED;
155 }
156 enumerator = in->create_attribute_enumerator(in);
157 while (enumerator->enumerate(enumerator, &type, &data))
158 {
159 switch (type)
160 {
161 case AT_RES:
162 res = data;
163 break;
164 default:
165 if (!attribute_skippable(type))
166 {
167 enumerator->destroy(enumerator);
168 DBG1(DBG_IKE, "found non skippable attribute %N",
169 simaka_attribute_names, type);
170 return FAILED;
171 }
172 break;
173 }
174 }
175 enumerator->destroy(enumerator);
176
177 /* verify MAC of EAP message, AT_MAC */
178 if (!in->verify(in, chunk_empty))
179 {
180 DBG1(DBG_IKE, "AT_MAC verification failed");
181 return FAILED;
182 }
183 /* compare received RES against stored XRES */
184 if (!chunk_equals(res, this->xres))
185 {
186 DBG1(DBG_IKE, "received RES does not match XRES");
187 return FAILED;
188 }
189 return SUCCESS;
190 }
191
192 /**
193 * Process EAP-AKA/Response/SynchronizationFailure message
194 */
195 static status_t process_synchronize(private_eap_aka_server_t *this,
196 simaka_message_t *in, eap_payload_t **out)
197 {
198 sim_provider_t *provider;
199 enumerator_t *enumerator;
200 simaka_attribute_t type;
201 chunk_t data, auts = chunk_empty;
202 bool found = FALSE;
203
204 if (this->synchronized)
205 {
206 DBG1(DBG_IKE, "received %N, but peer did already resynchronize",
207 simaka_subtype_names, AKA_SYNCHRONIZATION_FAILURE);
208 return FAILED;
209 }
210
211 DBG1(DBG_IKE, "received synchronization request, retrying...");
212
213 enumerator = in->create_attribute_enumerator(in);
214 while (enumerator->enumerate(enumerator, &type, &data))
215 {
216 switch (type)
217 {
218 case AT_AUTS:
219 auts = data;
220 break;
221 default:
222 if (!attribute_skippable(type))
223 {
224 enumerator->destroy(enumerator);
225 DBG1(DBG_IKE, "found non skippable attribute %N",
226 simaka_attribute_names, type);
227 return FAILED;
228 }
229 break;
230 }
231 }
232 enumerator->destroy(enumerator);
233
234 if (!auts.len)
235 {
236 DBG1(DBG_IKE, "synchronization request didn't contain usable AUTS");
237 return FAILED;
238 }
239
240 enumerator = charon->sim->create_provider_enumerator(charon->sim);
241 while (enumerator->enumerate(enumerator, &provider))
242 {
243 if (provider->resync(provider, this->peer, this->rand.ptr, auts.ptr))
244 {
245 found = TRUE;
246 break;
247 }
248 }
249 enumerator->destroy(enumerator);
250
251 if (!found)
252 {
253 DBG1(DBG_IKE, "no AKA provider found supporting "
254 "resynchronization for '%Y'", this->peer);
255 return FAILED;
256 }
257 this->synchronized = TRUE;
258 return initiate(this, out);
259 }
260
261 /**
262 * Process EAP-AKA/Response/ClientErrorCode message
263 */
264 static status_t process_client_error(private_eap_aka_server_t *this,
265 simaka_message_t *in)
266 {
267 enumerator_t *enumerator;
268 simaka_attribute_t type;
269 chunk_t data;
270
271 enumerator = in->create_attribute_enumerator(in);
272 while (enumerator->enumerate(enumerator, &type, &data))
273 {
274 if (type == AT_CLIENT_ERROR_CODE)
275 {
276 u_int16_t code;
277
278 memcpy(&code, data.ptr, sizeof(code));
279 DBG1(DBG_IKE, "received EAP-AKA client error '%N'",
280 simaka_client_error_names, ntohs(code));
281 }
282 else if (!simaka_attribute_skippable(type))
283 {
284 break;
285 }
286 }
287 enumerator->destroy(enumerator);
288 return FAILED;
289 }
290
291 /**
292 * Process EAP-AKA/Response/AuthenticationReject message
293 */
294 static status_t process_authentication_reject(private_eap_aka_server_t *this,
295 simaka_message_t *in)
296 {
297 DBG1(DBG_IKE, "received %N, authentication failed",
298 simaka_subtype_names, in->get_subtype(in));
299 return FAILED;
300 }
301
302 /**
303 * Implementation of eap_method_t.process
304 */
305 static status_t process(private_eap_aka_server_t *this,
306 eap_payload_t *in, eap_payload_t **out)
307 {
308 simaka_message_t *message;
309 status_t status;
310
311 message = simaka_message_create_from_payload(in, this->crypto);
312 if (!message)
313 {
314 return FAILED;
315 }
316 if (!message->parse(message))
317 {
318 message->destroy(message);
319 return FAILED;
320 }
321 switch (message->get_subtype(message))
322 {
323 case AKA_CHALLENGE:
324 status = process_challenge(this, message);
325 break;
326 case AKA_SYNCHRONIZATION_FAILURE:
327 status = process_synchronize(this, message, out);
328 break;
329 case AKA_CLIENT_ERROR:
330 status = process_client_error(this, message);
331 break;
332 case AKA_AUTHENTICATION_REJECT:
333 status = process_authentication_reject(this, message);
334 break;
335 default:
336 DBG1(DBG_IKE, "unable to process EAP-AKA subtype %N",
337 simaka_subtype_names, message->get_subtype(message));
338 status = FAILED;
339 break;
340 }
341 message->destroy(message);
342 return status;
343 }
344
345 /**
346 * Implementation of eap_method_t.get_type.
347 */
348 static eap_type_t get_type(private_eap_aka_server_t *this, u_int32_t *vendor)
349 {
350 *vendor = 0;
351 return EAP_AKA;
352 }
353
354 /**
355 * Implementation of eap_method_t.get_msk.
356 */
357 static status_t get_msk(private_eap_aka_server_t *this, chunk_t *msk)
358 {
359 if (this->msk.ptr)
360 {
361 *msk = this->msk;
362 return SUCCESS;
363 }
364 return FAILED;
365 }
366
367 /**
368 * Implementation of eap_method_t.is_mutual.
369 */
370 static bool is_mutual(private_eap_aka_server_t *this)
371 {
372 return TRUE;
373 }
374
375 /**
376 * Implementation of eap_method_t.destroy.
377 */
378 static void destroy(private_eap_aka_server_t *this)
379 {
380 this->crypto->destroy(this->crypto);
381 this->peer->destroy(this->peer);
382 free(this->msk.ptr);
383 free(this->xres.ptr);
384 free(this->rand.ptr);
385 free(this);
386 }
387
388 /*
389 * Described in header.
390 */
391 eap_aka_server_t *eap_aka_server_create(identification_t *server,
392 identification_t *peer)
393 {
394 private_eap_aka_server_t *this = malloc_thing(private_eap_aka_server_t);
395
396 this->public.interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
397 this->public.interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
398 this->public.interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
399 this->public.interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
400 this->public.interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
401 this->public.interface.destroy = (void(*)(eap_method_t*))destroy;
402
403 this->crypto = simaka_crypto_create();
404 if (!this->crypto)
405 {
406 free(this);
407 return NULL;
408 }
409 this->peer = peer->clone(peer);
410 this->msk = chunk_empty;
411 this->xres = chunk_empty;
412 this->rand = chunk_empty;
413 this->pending = 0;
414 this->synchronized = FALSE;
415 /* generate a non-zero identifier */
416 do {
417 this->identifier = random();
418 } while (!this->identifier);
419
420 return &this->public;
421 }
422