EAP-SIM/AKA crypto helper supports key derivation for fast reauthentication
[strongswan.git] / src / charon / plugins / eap_aka / eap_aka_peer.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_peer.h"
17
18 #include <library.h>
19 #include <daemon.h>
20
21 #include <simaka_message.h>
22 #include <simaka_crypto.h>
23
24 typedef struct private_eap_aka_peer_t private_eap_aka_peer_t;
25
26 /**
27 * Private data of an eap_aka_peer_t object.
28 */
29 struct private_eap_aka_peer_t {
30
31 /**
32 * Public authenticator_t interface.
33 */
34 eap_aka_peer_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 * MSK
48 */
49 chunk_t msk;
50 };
51
52 /**
53 * Create a AKA_CLIENT_ERROR: "Unable to process"
54 */
55 static eap_payload_t* create_client_error(private_eap_aka_peer_t *this,
56 u_int8_t identifier)
57 {
58 simaka_message_t *message;
59 eap_payload_t *out;
60 u_int16_t encoded;
61
62 DBG1(DBG_IKE, "sending client error '%N'",
63 simaka_client_error_names, AKA_UNABLE_TO_PROCESS);
64
65 message = simaka_message_create(FALSE, identifier, EAP_AKA,
66 AKA_CLIENT_ERROR, this->crypto);
67 encoded = htons(AKA_UNABLE_TO_PROCESS);
68 message->add_attribute(message, AT_CLIENT_ERROR_CODE,
69 chunk_create((char*)&encoded, sizeof(encoded)));
70 out = message->generate(message, chunk_empty);
71 message->destroy(message);
72 return out;
73 }
74
75 /**
76 * Process an EAP-AKA/Request/Challenge message
77 */
78 static status_t process_challenge(private_eap_aka_peer_t *this,
79 simaka_message_t *in, eap_payload_t **out)
80 {
81 simaka_message_t *message;
82 enumerator_t *enumerator;
83 simaka_attribute_t type;
84 sim_card_t *card;
85 chunk_t data, rand = chunk_empty, autn = chunk_empty, mk;
86 u_char res[AKA_RES_LEN], ck[AKA_CK_LEN], ik[AKA_IK_LEN], auts[AKA_AUTS_LEN];
87 status_t status = NOT_FOUND;
88
89 enumerator = in->create_attribute_enumerator(in);
90 while (enumerator->enumerate(enumerator, &type, &data))
91 {
92 switch (type)
93 {
94 case AT_RAND:
95 rand = data;
96 break;
97 case AT_AUTN:
98 autn = data;
99 break;
100 default:
101 if (!simaka_attribute_skippable(type))
102 {
103 *out = create_client_error(this, in->get_identifier(in));
104 enumerator->destroy(enumerator);
105 return NEED_MORE;
106 }
107 break;
108 }
109 }
110 enumerator->destroy(enumerator);
111
112 if (!rand.len || !autn.len)
113 {
114 DBG1(DBG_IKE, "received invalid EAP-AKA challenge message");
115 *out = create_client_error(this, in->get_identifier(in));
116 return NEED_MORE;
117 }
118
119 enumerator = charon->sim->create_card_enumerator(charon->sim);
120 while (enumerator->enumerate(enumerator, &card))
121 {
122 status = card->get_quintuplet(card, this->peer, rand.ptr, autn.ptr,
123 ck, ik, res);
124 if (status != FAILED)
125 { /* try next on error */
126 break;
127 }
128 }
129 enumerator->destroy(enumerator);
130
131 if (status == INVALID_STATE &&
132 card->resync(card, this->peer, rand.ptr, auts))
133 {
134 DBG1(DBG_IKE, "received SQN invalid, sending %N",
135 simaka_subtype_names, AKA_SYNCHRONIZATION_FAILURE);
136 message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
137 AKA_SYNCHRONIZATION_FAILURE, this->crypto);
138 message->add_attribute(message, AT_AUTS,
139 chunk_create(auts, AKA_AUTS_LEN));
140 *out = message->generate(message, chunk_empty);
141 message->destroy(message);
142 return NEED_MORE;
143 }
144 if (status != SUCCESS)
145 {
146 DBG1(DBG_IKE, "no USIM found with quintuplets for '%Y', sending %N",
147 this->peer, simaka_subtype_names, AKA_AUTHENTICATION_REJECT);
148 message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
149 AKA_AUTHENTICATION_REJECT, this->crypto);
150 *out = message->generate(message, chunk_empty);
151 message->destroy(message);
152 return NEED_MORE;
153 }
154
155 data = chunk_cata("cc", chunk_create(ik, AKA_IK_LEN),
156 chunk_create(ck, AKA_CK_LEN));
157 free(this->msk.ptr);
158 this->msk = this->crypto->derive_keys_full(this->crypto, this->peer,
159 data, &mk);
160 free(mk.ptr);
161
162 /* verify EAP message MAC AT_MAC */
163 if (!in->verify(in, chunk_empty))
164 {
165 DBG1(DBG_IKE, "AT_MAC verification failed ");
166 *out = create_client_error(this, in->get_identifier(in));
167 return NEED_MORE;
168 }
169
170 message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
171 AKA_CHALLENGE, this->crypto);
172 message->add_attribute(message, AT_RES, chunk_create(res, AKA_RES_LEN));
173 *out = message->generate(message, chunk_empty);
174 message->destroy(message);
175 return NEED_MORE;
176 }
177
178 /**
179 * Process an EAP-AKA/Request/Identity message
180 */
181 static status_t process_identity(private_eap_aka_peer_t *this,
182 simaka_message_t *in, eap_payload_t **out)
183 {
184 simaka_message_t *message;
185 enumerator_t *enumerator;
186 simaka_attribute_t type;
187 chunk_t data;
188
189 enumerator = in->create_attribute_enumerator(in);
190 while (enumerator->enumerate(enumerator, &type, &data))
191 {
192 switch (type)
193 {
194 case AT_PERMANENT_ID_REQ:
195 case AT_FULLAUTH_ID_REQ:
196 case AT_ANY_ID_REQ:
197 DBG1(DBG_IKE, "server requested %N, sending '%Y'",
198 simaka_attribute_names, type, this->peer);
199 /* we reply with our permanent identity in any case */
200 break;
201 default:
202 if (!simaka_attribute_skippable(type))
203 {
204 *out = create_client_error(this, in->get_identifier(in));
205 enumerator->destroy(enumerator);
206 return NEED_MORE;
207 }
208 break;
209 }
210 }
211 enumerator->destroy(enumerator);
212
213 message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
214 AKA_IDENTITY, this->crypto);
215 message->add_attribute(message, AT_IDENTITY,
216 this->peer->get_encoding(this->peer));
217 *out = message->generate(message, chunk_empty);
218 message->destroy(message);
219 return NEED_MORE;
220 }
221
222 /**
223 * Process an EAP-AKA/Request/Notification message
224 */
225 static status_t process_notification(private_eap_aka_peer_t *this,
226 simaka_message_t *in, eap_payload_t **out)
227 {
228 simaka_message_t *message;
229 enumerator_t *enumerator;
230 simaka_attribute_t type;
231 chunk_t data;
232 bool success = TRUE;
233
234 enumerator = in->create_attribute_enumerator(in);
235 while (enumerator->enumerate(enumerator, &type, &data))
236 {
237 if (type == AT_NOTIFICATION)
238 {
239 u_int16_t code;
240
241 memcpy(&code, data.ptr, sizeof(code));
242 code = ntohs(code);
243
244 /* test success bit */
245 if (!(data.ptr[0] & 0x80))
246 {
247 success = FALSE;
248 DBG1(DBG_IKE, "received EAP-AKA notification error '%N'",
249 simaka_notification_names, code);
250 }
251 else
252 {
253 DBG1(DBG_IKE, "received EAP-AKA notification '%N'",
254 simaka_notification_names, code);
255 }
256 }
257 else if (!simaka_attribute_skippable(type))
258 {
259 success = FALSE;
260 break;
261 }
262 }
263 enumerator->destroy(enumerator);
264
265 if (success)
266 { /* empty notification reply */
267 message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
268 AKA_NOTIFICATION, this->crypto);
269 *out = message->generate(message, chunk_empty);
270 message->destroy(message);
271 }
272 else
273 {
274 *out = create_client_error(this, in->get_identifier(in));
275 }
276 return NEED_MORE;
277 }
278
279
280 /**
281 * Implementation of eap_method_t.process
282 */
283 static status_t process(private_eap_aka_peer_t *this,
284 eap_payload_t *in, eap_payload_t **out)
285 {
286 simaka_message_t *message;
287 status_t status;
288
289 message = simaka_message_create_from_payload(in, this->crypto);
290 if (!message)
291 {
292 *out = create_client_error(this, in->get_identifier(in));
293 return NEED_MORE;
294 }
295 if (!message->parse(message))
296 {
297 message->destroy(message);
298 *out = create_client_error(this, in->get_identifier(in));
299 return NEED_MORE;
300 }
301 switch (message->get_subtype(message))
302 {
303 case AKA_IDENTITY:
304 status = process_identity(this, message, out);
305 break;
306 case AKA_CHALLENGE:
307 status = process_challenge(this, message, out);
308 break;
309 case AKA_NOTIFICATION:
310 status = process_notification(this, message, out);
311 break;
312 default:
313 DBG1(DBG_IKE, "unable to process EAP-AKA subtype %N",
314 simaka_subtype_names, message->get_subtype(message));
315 *out = create_client_error(this, in->get_identifier(in));
316 status = NEED_MORE;
317 break;
318 }
319 message->destroy(message);
320 return status;
321 }
322
323 /**
324 * Implementation of eap_method_t.initiate
325 */
326 static status_t initiate(private_eap_aka_peer_t *this, eap_payload_t **out)
327 {
328 /* peer never initiates */
329 return FAILED;
330 }
331
332 /**
333 * Implementation of eap_method_t.get_type.
334 */
335 static eap_type_t get_type(private_eap_aka_peer_t *this, u_int32_t *vendor)
336 {
337 *vendor = 0;
338 return EAP_AKA;
339 }
340
341 /**
342 * Implementation of eap_method_t.get_msk.
343 */
344 static status_t get_msk(private_eap_aka_peer_t *this, chunk_t *msk)
345 {
346 if (this->msk.ptr)
347 {
348 *msk = this->msk;
349 return SUCCESS;
350 }
351 return FAILED;
352 }
353
354 /**
355 * Implementation of eap_method_t.is_mutual.
356 */
357 static bool is_mutual(private_eap_aka_peer_t *this)
358 {
359 return TRUE;
360 }
361
362 /**
363 * Implementation of eap_method_t.destroy.
364 */
365 static void destroy(private_eap_aka_peer_t *this)
366 {
367 this->crypto->destroy(this->crypto);
368 this->peer->destroy(this->peer);
369 free(this->msk.ptr);
370 free(this);
371 }
372
373 /*
374 * Described in header.
375 */
376 eap_aka_peer_t *eap_aka_peer_create(identification_t *server,
377 identification_t *peer)
378 {
379 private_eap_aka_peer_t *this = malloc_thing(private_eap_aka_peer_t);
380
381 this->public.interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
382 this->public.interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
383 this->public.interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
384 this->public.interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
385 this->public.interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
386 this->public.interface.destroy = (void(*)(eap_method_t*))destroy;
387
388 this->crypto = simaka_crypto_create();
389 if (!this->crypto)
390 {
391 free(this);
392 return NULL;
393 }
394 this->peer = peer->clone(peer);
395 this->msk = chunk_empty;
396
397 return &this->public;
398 }
399