Pass SIM/AKA crypto helper to constructor of message
[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;
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, data);
159
160 /* verify EAP message MAC AT_MAC */
161 if (!in->verify(in, chunk_empty))
162 {
163 DBG1(DBG_IKE, "AT_MAC verification failed ");
164 *out = create_client_error(this, in->get_identifier(in));
165 return NEED_MORE;
166 }
167
168 message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
169 AKA_CHALLENGE, this->crypto);
170 message->add_attribute(message, AT_RES, chunk_create(res, AKA_RES_LEN));
171 *out = message->generate(message, chunk_empty);
172 message->destroy(message);
173 return NEED_MORE;
174 }
175
176 /**
177 * Process an EAP-AKA/Request/Identity message
178 */
179 static status_t process_identity(private_eap_aka_peer_t *this,
180 simaka_message_t *in, eap_payload_t **out)
181 {
182 simaka_message_t *message;
183 enumerator_t *enumerator;
184 simaka_attribute_t type;
185 chunk_t data;
186
187 enumerator = in->create_attribute_enumerator(in);
188 while (enumerator->enumerate(enumerator, &type, &data))
189 {
190 switch (type)
191 {
192 case AT_PERMANENT_ID_REQ:
193 case AT_FULLAUTH_ID_REQ:
194 case AT_ANY_ID_REQ:
195 DBG1(DBG_IKE, "server requested %N, sending '%Y'",
196 simaka_attribute_names, type, this->peer);
197 /* we reply with our permanent identity in any case */
198 break;
199 default:
200 if (!simaka_attribute_skippable(type))
201 {
202 *out = create_client_error(this, in->get_identifier(in));
203 enumerator->destroy(enumerator);
204 return NEED_MORE;
205 }
206 break;
207 }
208 }
209 enumerator->destroy(enumerator);
210
211 message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
212 AKA_IDENTITY, this->crypto);
213 message->add_attribute(message, AT_IDENTITY,
214 this->peer->get_encoding(this->peer));
215 *out = message->generate(message, chunk_empty);
216 message->destroy(message);
217 return NEED_MORE;
218 }
219
220 /**
221 * Process an EAP-AKA/Request/Notification message
222 */
223 static status_t process_notification(private_eap_aka_peer_t *this,
224 simaka_message_t *in, eap_payload_t **out)
225 {
226 simaka_message_t *message;
227 enumerator_t *enumerator;
228 simaka_attribute_t type;
229 chunk_t data;
230 bool success = TRUE;
231
232 enumerator = in->create_attribute_enumerator(in);
233 while (enumerator->enumerate(enumerator, &type, &data))
234 {
235 if (type == AT_NOTIFICATION)
236 {
237 u_int16_t code;
238
239 memcpy(&code, data.ptr, sizeof(code));
240 code = ntohs(code);
241
242 /* test success bit */
243 if (!(data.ptr[0] & 0x80))
244 {
245 success = FALSE;
246 DBG1(DBG_IKE, "received EAP-AKA notification error '%N'",
247 simaka_notification_names, code);
248 }
249 else
250 {
251 DBG1(DBG_IKE, "received EAP-AKA notification '%N'",
252 simaka_notification_names, code);
253 }
254 }
255 else if (!simaka_attribute_skippable(type))
256 {
257 success = FALSE;
258 break;
259 }
260 }
261 enumerator->destroy(enumerator);
262
263 if (success)
264 { /* empty notification reply */
265 message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
266 AKA_NOTIFICATION, this->crypto);
267 *out = message->generate(message, chunk_empty);
268 message->destroy(message);
269 }
270 else
271 {
272 *out = create_client_error(this, in->get_identifier(in));
273 }
274 return NEED_MORE;
275 }
276
277
278 /**
279 * Implementation of eap_method_t.process
280 */
281 static status_t process(private_eap_aka_peer_t *this,
282 eap_payload_t *in, eap_payload_t **out)
283 {
284 simaka_message_t *message;
285 status_t status;
286
287 message = simaka_message_create_from_payload(in, this->crypto);
288 if (!message)
289 {
290 *out = create_client_error(this, in->get_identifier(in));
291 return NEED_MORE;
292 }
293 if (!message->parse(message))
294 {
295 message->destroy(message);
296 *out = create_client_error(this, in->get_identifier(in));
297 return NEED_MORE;
298 }
299 switch (message->get_subtype(message))
300 {
301 case AKA_IDENTITY:
302 status = process_identity(this, message, out);
303 break;
304 case AKA_CHALLENGE:
305 status = process_challenge(this, message, out);
306 break;
307 case AKA_NOTIFICATION:
308 status = process_notification(this, message, out);
309 break;
310 default:
311 DBG1(DBG_IKE, "unable to process EAP-AKA subtype %N",
312 simaka_subtype_names, message->get_subtype(message));
313 *out = create_client_error(this, in->get_identifier(in));
314 status = NEED_MORE;
315 break;
316 }
317 message->destroy(message);
318 return status;
319 }
320
321 /**
322 * Implementation of eap_method_t.initiate
323 */
324 static status_t initiate(private_eap_aka_peer_t *this, eap_payload_t **out)
325 {
326 /* peer never initiates */
327 return FAILED;
328 }
329
330 /**
331 * Implementation of eap_method_t.get_type.
332 */
333 static eap_type_t get_type(private_eap_aka_peer_t *this, u_int32_t *vendor)
334 {
335 *vendor = 0;
336 return EAP_AKA;
337 }
338
339 /**
340 * Implementation of eap_method_t.get_msk.
341 */
342 static status_t get_msk(private_eap_aka_peer_t *this, chunk_t *msk)
343 {
344 if (this->msk.ptr)
345 {
346 *msk = this->msk;
347 return SUCCESS;
348 }
349 return FAILED;
350 }
351
352 /**
353 * Implementation of eap_method_t.is_mutual.
354 */
355 static bool is_mutual(private_eap_aka_peer_t *this)
356 {
357 return TRUE;
358 }
359
360 /**
361 * Implementation of eap_method_t.destroy.
362 */
363 static void destroy(private_eap_aka_peer_t *this)
364 {
365 this->crypto->destroy(this->crypto);
366 this->peer->destroy(this->peer);
367 free(this->msk.ptr);
368 free(this);
369 }
370
371 /*
372 * Described in header.
373 */
374 eap_aka_peer_t *eap_aka_peer_create(identification_t *server,
375 identification_t *peer)
376 {
377 private_eap_aka_peer_t *this = malloc_thing(private_eap_aka_peer_t);
378
379 this->public.interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
380 this->public.interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
381 this->public.interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
382 this->public.interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
383 this->public.interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
384 this->public.interface.destroy = (void(*)(eap_method_t*))destroy;
385
386 this->crypto = simaka_crypto_create();
387 if (!this->crypto)
388 {
389 free(this);
390 return NULL;
391 }
392 this->peer = peer->clone(peer);
393 this->msk = chunk_empty;
394
395 return &this->public;
396 }
397