Use existing triplet length definitions
[strongswan.git] / src / charon / plugins / eap_sim / eap_sim_peer.c
1 /*
2 * Copyright (C) 2007-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_sim_peer.h"
17
18 #include <daemon.h>
19
20 #include <simaka_message.h>
21
22 /* number of tries we do authenticate */
23 #define MAX_TRIES 3
24
25 /* number of triplets for one authentication */
26 #define TRIPLET_COUNT 3
27
28 /** length of the AT_NONCE_MT/AT_NONCE_S nonce value */
29 #define NONCE_LEN 16
30
31 typedef struct private_eap_sim_peer_t private_eap_sim_peer_t;
32
33 /**
34 * Private data of an eap_sim_peer_t object.
35 */
36 struct private_eap_sim_peer_t {
37
38 /**
39 * Public authenticator_t interface.
40 */
41 eap_sim_peer_t public;
42
43 /**
44 * permanent ID of peer
45 */
46 identification_t *peer;
47
48 /**
49 * EAP-SIM crypto helper
50 */
51 simaka_crypto_t *crypto;
52
53 /**
54 * how many times we try to authenticate
55 */
56 int tries;
57
58 /**
59 * version list received from server
60 */
61 chunk_t version_list;
62
63 /**
64 * Nonce value used in AT_NONCE_MT/AT_NONCE_S
65 */
66 chunk_t nonce;
67
68 /**
69 * MSK, used for EAP-SIM based IKEv2 authentication
70 */
71 chunk_t msk;
72 };
73
74 /* version of SIM protocol we speak */
75 static chunk_t version = chunk_from_chars(0x00,0x01);
76
77 /**
78 * Read a triplet from the SIM card
79 */
80 static bool get_card_triplet(private_eap_sim_peer_t *this,
81 char *rand, char *sres, char *kc)
82 {
83 enumerator_t *enumerator;
84 sim_card_t *card;
85 bool success = FALSE;
86
87 enumerator = charon->sim->create_card_enumerator(charon->sim);
88 while (enumerator->enumerate(enumerator, &card))
89 {
90 if (card->get_triplet(card, this->peer, rand, sres, kc))
91 {
92 success = TRUE;
93 break;
94 }
95 }
96 enumerator->destroy(enumerator);
97 if (!success)
98 {
99 DBG1(DBG_IKE, "no SIM card found with triplets for '%Y'", this->peer);
100 }
101 return success;
102 }
103
104 /**
105 * Create a SIM_CLIENT_ERROR
106 */
107 static eap_payload_t* create_client_error(private_eap_sim_peer_t *this,
108 u_int8_t identifier, simaka_client_error_t code)
109 {
110 simaka_message_t *message;
111 eap_payload_t *out;
112 u_int16_t encoded;
113
114 DBG1(DBG_IKE, "sending client error '%N'", simaka_client_error_names, code);
115
116 message = simaka_message_create(FALSE, identifier,
117 EAP_SIM, SIM_CLIENT_ERROR);
118 encoded = htons(code);
119 message->add_attribute(message, AT_CLIENT_ERROR_CODE,
120 chunk_create((char*)&encoded, sizeof(encoded)));
121 out = message->generate(message, this->crypto, chunk_empty);
122 message->destroy(message);
123 return out;
124 }
125
126 /**
127 * process an EAP-SIM/Request/Start message
128 */
129 static status_t process_start(private_eap_sim_peer_t *this,
130 simaka_message_t *in, eap_payload_t **out)
131 {
132 simaka_message_t *message;
133 enumerator_t *enumerator;
134 simaka_attribute_t type;
135 chunk_t data;
136 rng_t *rng;
137 bool supported = FALSE;
138
139 enumerator = in->create_attribute_enumerator(in);
140 while (enumerator->enumerate(enumerator, &type, &data))
141 {
142 switch (type)
143 {
144 case AT_VERSION_LIST:
145 {
146 free(this->version_list.ptr);
147 this->version_list = chunk_clone(data);
148 while (data.len >= version.len)
149 {
150 if (memeq(data.ptr, version.ptr, version.len))
151 {
152 supported = TRUE;
153 break;
154 }
155 }
156 break;
157 }
158 default:
159 if (!simaka_attribute_skippable(type))
160 {
161 *out = create_client_error(this, in->get_identifier(in),
162 SIM_UNABLE_TO_PROCESS);
163 enumerator->destroy(enumerator);
164 return NEED_MORE;
165 }
166 break;
167 }
168 }
169 enumerator->destroy(enumerator);
170
171 if (!supported)
172 {
173 DBG1(DBG_IKE, "server does not support EAP-SIM version number 1");
174 *out = create_client_error(this, in->get_identifier(in),
175 SIM_UNSUPPORTED_VERSION);
176 return NEED_MORE;
177 }
178
179 /* generate AT_NONCE_MT value */
180 rng = this->crypto->get_rng(this->crypto);
181 free(this->nonce.ptr);
182 rng->allocate_bytes(rng, NONCE_LEN, &this->nonce);
183
184 message = simaka_message_create(FALSE, in->get_identifier(in),
185 EAP_SIM, SIM_START);
186 message->add_attribute(message, AT_SELECTED_VERSION, version);
187 message->add_attribute(message, AT_NONCE_MT, this->nonce);
188 *out = message->generate(message, this->crypto, chunk_empty);
189 message->destroy(message);
190
191 return NEED_MORE;
192 }
193
194 /**
195 * process an EAP-SIM/Request/Challenge message
196 */
197 static status_t process_challenge(private_eap_sim_peer_t *this,
198 simaka_message_t *in, eap_payload_t **out)
199 {
200 simaka_message_t *message;
201 enumerator_t *enumerator;
202 simaka_attribute_t type;
203 chunk_t data, rands = chunk_empty, kcs, kc, sreses, sres;
204
205 if (this->tries-- <= 0)
206 {
207 /* give up without notification. This hack is required as some buggy
208 * server implementations won't respect our client-error. */
209 return FAILED;
210 }
211
212 enumerator = in->create_attribute_enumerator(in);
213 while (enumerator->enumerate(enumerator, &type, &data))
214 {
215 switch (type)
216 {
217 case AT_RAND:
218 rands = data;
219 break;
220 default:
221 if (!simaka_attribute_skippable(type))
222 {
223 *out = create_client_error(this, in->get_identifier(in),
224 SIM_UNABLE_TO_PROCESS);
225 enumerator->destroy(enumerator);
226 return NEED_MORE;
227 }
228 break;
229 }
230 }
231 enumerator->destroy(enumerator);
232
233 /* excepting two or three RAND, each 16 bytes. We require two valid
234 * and different RANDs */
235 if ((rands.len != 2 * SIM_RAND_LEN && rands.len != 3 * SIM_RAND_LEN) ||
236 memeq(rands.ptr, rands.ptr + SIM_RAND_LEN, SIM_RAND_LEN))
237 {
238 DBG1(DBG_IKE, "no valid AT_RAND received");
239 *out = create_client_error(this, in->get_identifier(in),
240 SIM_INSUFFICIENT_CHALLENGES);
241 return NEED_MORE;
242 }
243 /* get two or three KCs/SRESes from SIM using RANDs */
244 kcs = kc = chunk_alloca(rands.len / 2);
245 sreses = sres = chunk_alloca(rands.len / 4);
246 while (rands.len >= SIM_RAND_LEN)
247 {
248 if (!get_card_triplet(this, rands.ptr, sres.ptr, kc.ptr))
249 {
250 DBG1(DBG_IKE, "unable to get EAP-SIM triplet");
251 *out = create_client_error(this, in->get_identifier(in),
252 SIM_UNABLE_TO_PROCESS);
253 return NEED_MORE;
254 }
255 DBG3(DBG_IKE, "got triplet for RAND %b\n Kc %b\n SRES %b",
256 rands.ptr, SIM_RAND_LEN, sres.ptr, SIM_SRES_LEN, kc.ptr, SIM_KC_LEN);
257 kc = chunk_skip(kc, SIM_KC_LEN);
258 sres = chunk_skip(sres, SIM_SRES_LEN);
259 rands = chunk_skip(rands, SIM_RAND_LEN);
260 }
261
262 data = chunk_cata("cccc", kcs, this->nonce, this->version_list, version);
263 free(this->msk.ptr);
264 this->msk = this->crypto->derive_keys_full(this->crypto, this->peer, data);
265
266 /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT" */
267 if (!in->verify(in, this->crypto, this->nonce))
268 {
269 DBG1(DBG_IKE, "AT_MAC verification failed");
270 *out = create_client_error(this, in->get_identifier(in),
271 SIM_UNABLE_TO_PROCESS);
272 return NEED_MORE;
273 }
274
275 /* build response with AT_MAC, built over "EAP packet | n*SRES" */
276 message = simaka_message_create(FALSE, in->get_identifier(in),
277 EAP_SIM, SIM_CHALLENGE);
278 *out = message->generate(message, this->crypto, sreses);
279 message->destroy(message);
280 return NEED_MORE;
281 }
282
283 /**
284 * process an EAP-SIM/Request/Notification message
285 */
286 static status_t process_notification(private_eap_sim_peer_t *this,
287 simaka_message_t *in, eap_payload_t **out)
288 {
289 simaka_message_t *message;
290 enumerator_t *enumerator;
291 simaka_attribute_t type;
292 chunk_t data;
293 bool success = TRUE;
294
295 enumerator = in->create_attribute_enumerator(in);
296 while (enumerator->enumerate(enumerator, &type, &data))
297 {
298 if (type == AT_NOTIFICATION)
299 {
300 u_int16_t code;
301
302 memcpy(&code, data.ptr, sizeof(code));
303 code = ntohs(code);
304
305 /* test success bit */
306 if (!(data.ptr[0] & 0x80))
307 {
308 success = FALSE;
309 DBG1(DBG_IKE, "received EAP-SIM notification error '%N'",
310 simaka_notification_names, code);
311 }
312 else
313 {
314 DBG1(DBG_IKE, "received EAP-SIM notification '%N'",
315 simaka_notification_names, code);
316 }
317 }
318 else if (!simaka_attribute_skippable(type))
319 {
320 success = FALSE;
321 break;
322 }
323 }
324 enumerator->destroy(enumerator);
325
326 if (success)
327 { /* empty notification reply */
328 message = simaka_message_create(FALSE, in->get_identifier(in),
329 EAP_SIM, SIM_NOTIFICATION);
330 *out = message->generate(message, this->crypto, chunk_empty);
331 message->destroy(message);
332 }
333 else
334 {
335 *out = create_client_error(this, in->get_identifier(in),
336 SIM_UNABLE_TO_PROCESS);
337 }
338 return NEED_MORE;
339 }
340
341 /**
342 * Implementation of eap_method_t.process
343 */
344 static status_t process(private_eap_sim_peer_t *this,
345 eap_payload_t *in, eap_payload_t **out)
346 {
347 simaka_message_t *message;
348 status_t status;
349
350 message = simaka_message_create_from_payload(in);
351 if (!message)
352 {
353 *out = create_client_error(this, in->get_identifier(in),
354 SIM_UNABLE_TO_PROCESS);
355 return NEED_MORE;
356 }
357 if (!message->parse(message, this->crypto))
358 {
359 message->destroy(message);
360 *out = create_client_error(this, in->get_identifier(in),
361 SIM_UNABLE_TO_PROCESS);
362 return NEED_MORE;
363 }
364 switch (message->get_subtype(message))
365 {
366 case SIM_START:
367 status = process_start(this, message, out);
368 break;
369 case SIM_CHALLENGE:
370 status = process_challenge(this, message, out);
371 break;
372 case SIM_NOTIFICATION:
373 status = process_notification(this, message, out);
374 break;
375 default:
376 DBG1(DBG_IKE, "unable to process EAP-SIM subtype %N",
377 simaka_subtype_names, message->get_subtype(message));
378 *out = create_client_error(this, in->get_identifier(in),
379 SIM_UNABLE_TO_PROCESS);
380 status = NEED_MORE;
381 break;
382 }
383 message->destroy(message);
384 return status;
385 }
386
387 /**
388 * Implementation of eap_method_t.initiate
389 */
390 static status_t initiate(private_eap_sim_peer_t *this, eap_payload_t **out)
391 {
392 /* peer never initiates */
393 return FAILED;
394 }
395
396 /**
397 * Implementation of eap_method_t.get_type.
398 */
399 static eap_type_t get_type(private_eap_sim_peer_t *this, u_int32_t *vendor)
400 {
401 *vendor = 0;
402 return EAP_SIM;
403 }
404
405 /**
406 * Implementation of eap_method_t.get_msk.
407 */
408 static status_t get_msk(private_eap_sim_peer_t *this, chunk_t *msk)
409 {
410 if (this->msk.ptr)
411 {
412 *msk = this->msk;
413 return SUCCESS;
414 }
415 return FAILED;
416 }
417
418 /**
419 * Implementation of eap_method_t.is_mutual.
420 */
421 static bool is_mutual(private_eap_sim_peer_t *this)
422 {
423 return TRUE;
424 }
425
426 /**
427 * Implementation of eap_method_t.destroy.
428 */
429 static void destroy(private_eap_sim_peer_t *this)
430 {
431 this->peer->destroy(this->peer);
432 this->crypto->destroy(this->crypto);
433 free(this->version_list.ptr);
434 free(this->nonce.ptr);
435 free(this->msk.ptr);
436 free(this);
437 }
438
439 /*
440 * Described in header.
441 */
442 eap_sim_peer_t *eap_sim_peer_create(identification_t *server,
443 identification_t *peer)
444 {
445 private_eap_sim_peer_t *this = malloc_thing(private_eap_sim_peer_t);
446
447 this->public.interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
448 this->public.interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
449 this->public.interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
450 this->public.interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
451 this->public.interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
452 this->public.interface.destroy = (void(*)(eap_method_t*))destroy;
453
454 this->crypto = simaka_crypto_create();
455 if (!this->crypto)
456 {
457 free(this);
458 return NULL;
459 }
460 this->peer = peer->clone(peer);
461 this->tries = MAX_TRIES;
462 this->version_list = chunk_empty;
463 this->nonce = chunk_empty;
464 this->msk = chunk_empty;
465
466 return &this->public;
467 }
468