Removed strayed code fragment
[strongswan.git] / src / charon / plugins / eap_sim / eap_sim_server.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_server.h"
17
18 #include <daemon.h>
19
20 #include <simaka_message.h>
21 #include <simaka_crypto.h>
22
23 /* number of triplets for one authentication */
24 #define TRIPLET_COUNT 3
25
26 /** length of the AT_NONCE_S value */
27 #define NONCE_LEN 16
28
29 typedef struct private_eap_sim_server_t private_eap_sim_server_t;
30
31 /**
32 * Private data of an eap_sim_server_t object.
33 */
34 struct private_eap_sim_server_t {
35
36 /**
37 * Public authenticator_t interface.
38 */
39 eap_sim_server_t public;
40
41 /**
42 * permanent ID of peer
43 */
44 identification_t *permanent;
45
46 /**
47 * pseudonym ID of peer
48 */
49 identification_t *pseudonym;
50
51 /**
52 * reauthentication ID of peer
53 */
54 identification_t *reauth;
55
56 /**
57 * EAP-SIM/AKA crypto helper
58 */
59 simaka_crypto_t *crypto;
60
61 /**
62 * unique EAP identifier
63 */
64 u_int8_t identifier;
65
66 /**
67 * concatenated SRES values
68 */
69 chunk_t sreses;
70
71 /**
72 * Nonce value used in AT_NONCE_S
73 */
74 chunk_t nonce;
75
76 /**
77 * Counter value negotiated, network order
78 */
79 chunk_t counter;
80
81 /**
82 * MSK, used for EAP-SIM based IKEv2 authentication
83 */
84 chunk_t msk;
85
86 /**
87 * Do we request fast reauthentication?
88 */
89 bool use_reauth;
90
91 /**
92 * Do we request pseudonym identities?
93 */
94 bool use_pseudonym;
95
96 /**
97 * Do we request permanent identities?
98 */
99 bool use_permanent;
100
101 /**
102 * EAP-SIM message we have initiated
103 */
104 simaka_subtype_t pending;
105 };
106
107 /* version of SIM protocol we speak */
108 static chunk_t version = chunk_from_chars(0x00,0x01);
109
110 /**
111 * Implementation of eap_method_t.initiate
112 */
113 static status_t initiate(private_eap_sim_server_t *this, eap_payload_t **out)
114 {
115 simaka_message_t *message;
116
117 message = simaka_message_create(TRUE, this->identifier++, EAP_SIM,
118 SIM_START, this->crypto);
119 message->add_attribute(message, AT_VERSION_LIST, version);
120 if (this->use_reauth)
121 {
122 message->add_attribute(message, AT_ANY_ID_REQ, chunk_empty);
123 }
124 else if (this->use_pseudonym)
125 {
126 message->add_attribute(message, AT_FULLAUTH_ID_REQ, chunk_empty);
127 }
128 else if (this->use_permanent)
129 {
130 message->add_attribute(message, AT_PERMANENT_ID_REQ, chunk_empty);
131 }
132 *out = message->generate(message, chunk_empty);
133 message->destroy(message);
134
135 this->pending = SIM_START;
136 return NEED_MORE;
137 }
138
139 /**
140 * Initiate EAP-SIM/Request/Re-authentication message
141 */
142 static status_t reauthenticate(private_eap_sim_server_t *this,
143 char mk[HASH_SIZE_SHA1], u_int16_t counter,
144 eap_payload_t **out)
145 {
146 simaka_message_t *message;
147 identification_t *next;
148 chunk_t mkc;
149 rng_t *rng;
150
151 DBG1(DBG_IKE, "initiating EAP-SIM reauthentication");
152
153 rng = this->crypto->get_rng(this->crypto);
154 rng->allocate_bytes(rng, NONCE_LEN, &this->nonce);
155
156 mkc = chunk_create(mk, HASH_SIZE_SHA1);
157 counter = htons(counter);
158 this->counter = chunk_clone(chunk_create((char*)&counter, sizeof(counter)));
159
160 this->crypto->derive_keys_reauth(this->crypto, mkc);
161 this->msk = this->crypto->derive_keys_reauth_msk(this->crypto,
162 this->reauth, this->counter, this->nonce, mkc);
163
164 message = simaka_message_create(TRUE, this->identifier++, EAP_SIM,
165 SIM_REAUTHENTICATION, this->crypto);
166 message->add_attribute(message, AT_COUNTER, this->counter);
167 message->add_attribute(message, AT_NONCE_S, this->nonce);
168 next = charon->sim->provider_gen_reauth(charon->sim, this->permanent, mk);
169 if (next)
170 {
171 message->add_attribute(message, AT_NEXT_REAUTH_ID,
172 next->get_encoding(next));
173 next->destroy(next);
174 }
175 *out = message->generate(message, chunk_empty);
176 message->destroy(message);
177
178 this->pending = SIM_REAUTHENTICATION;
179 return NEED_MORE;
180 }
181
182 /**
183 * process an EAP-SIM/Response/Reauthentication message
184 */
185 static status_t process_reauthentication(private_eap_sim_server_t *this,
186 simaka_message_t *in, eap_payload_t **out)
187 {
188 enumerator_t *enumerator;
189 simaka_attribute_t type;
190 chunk_t data, counter = chunk_empty;
191 bool too_small = FALSE;
192
193 if (this->pending != SIM_REAUTHENTICATION)
194 {
195 DBG1(DBG_IKE, "received %N, but not expected",
196 simaka_subtype_names, SIM_REAUTHENTICATION);
197 return FAILED;
198 }
199 /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_S" */
200 if (!in->verify(in, this->nonce))
201 {
202 return FAILED;
203 }
204
205 enumerator = in->create_attribute_enumerator(in);
206 while (enumerator->enumerate(enumerator, &type, &data))
207 {
208 switch (type)
209 {
210 case AT_COUNTER:
211 counter = data;
212 break;
213 case AT_COUNTER_TOO_SMALL:
214 too_small = TRUE;
215 break;
216 default:
217 if (!simaka_attribute_skippable(type))
218 {
219 enumerator->destroy(enumerator);
220 return FAILED;
221 }
222 break;
223 }
224 }
225 enumerator->destroy(enumerator);
226
227 if (too_small)
228 {
229 DBG1(DBG_IKE, "received %N, initiating full authentication",
230 simaka_attribute_names, AT_COUNTER_TOO_SMALL);
231 this->use_reauth = FALSE;
232 this->crypto->clear_keys(this->crypto);
233 return initiate(this, out);
234 }
235 if (!chunk_equals(counter, this->counter))
236 {
237 DBG1(DBG_IKE, "received counter does not match");
238 return FAILED;
239 }
240 return SUCCESS;
241 }
242
243 /**
244 * process an EAP-SIM/Response/Start message
245 */
246 static status_t process_start(private_eap_sim_server_t *this,
247 simaka_message_t *in, eap_payload_t **out)
248 {
249 simaka_message_t *message;
250 enumerator_t *enumerator;
251 simaka_attribute_t type;
252 chunk_t data, identity = chunk_empty, nonce = chunk_empty, mk;
253 chunk_t rands, rand, kcs, kc, sreses, sres;
254 bool supported = FALSE;
255 identification_t *id;
256 int i;
257
258 if (this->pending != SIM_START)
259 {
260 DBG1(DBG_IKE, "received %N, but not expected",
261 simaka_subtype_names, SIM_START);
262 return FAILED;
263 }
264
265 enumerator = in->create_attribute_enumerator(in);
266 while (enumerator->enumerate(enumerator, &type, &data))
267 {
268 switch (type)
269 {
270 case AT_NONCE_MT:
271 nonce = data;
272 break;
273 case AT_SELECTED_VERSION:
274 if (chunk_equals(data, version))
275 {
276 supported = TRUE;
277 }
278 break;
279 case AT_IDENTITY:
280 identity = data;
281 break;
282 default:
283 if (!simaka_attribute_skippable(type))
284 {
285 enumerator->destroy(enumerator);
286 return FAILED;
287 }
288 break;
289 }
290 }
291 enumerator->destroy(enumerator);
292
293 if (identity.len)
294 {
295 identification_t *permanent;
296
297 id = identification_create_from_data(identity);
298 if (this->use_reauth && !nonce.len)
299 {
300 char mk[HASH_SIZE_SHA1];
301 u_int16_t counter;
302
303 permanent = charon->sim->provider_is_reauth(charon->sim, id,
304 mk, &counter);
305 if (permanent)
306 {
307 this->permanent->destroy(this->permanent);
308 this->permanent = permanent;
309 this->reauth = id;
310 return reauthenticate(this, mk, counter, out);
311 }
312 DBG1(DBG_IKE, "received unknown reauthentication identity '%Y', "
313 "initiating full authentication", id);
314 this->use_reauth = FALSE;
315 id->destroy(id);
316 return initiate(this, out);
317 }
318 if (this->use_pseudonym)
319 {
320 permanent = charon->sim->provider_is_pseudonym(charon->sim, id);
321 if (permanent)
322 {
323 this->permanent->destroy(this->permanent);
324 this->permanent = permanent;
325 this->pseudonym = id->clone(id);
326 /* we already have a new permanent identity now */
327 this->use_permanent = FALSE;
328 }
329 }
330 if (!this->pseudonym && this->use_permanent)
331 {
332 DBG1(DBG_IKE, "received %spermanent identity '%Y'",
333 this->use_pseudonym ? "pseudonym or " : "", id);
334 this->permanent->destroy(this->permanent);
335 this->permanent = id->clone(id);
336 }
337 id->destroy(id);
338 }
339
340 if (!supported || !nonce.len)
341 {
342 DBG1(DBG_IKE, "received incomplete EAP-SIM/Response/Start");
343 return FAILED;
344 }
345
346 /* read triplets from provider */
347 rand = rands = chunk_alloca(SIM_RAND_LEN * TRIPLET_COUNT);
348 kc = kcs = chunk_alloca(SIM_KC_LEN * TRIPLET_COUNT);
349 sres = sreses = chunk_alloca(SIM_SRES_LEN * TRIPLET_COUNT);
350 rands.len = kcs.len = sreses.len = 0;
351 for (i = 0; i < TRIPLET_COUNT; i++)
352 {
353 if (!charon->sim->provider_get_triplet(charon->sim, this->permanent,
354 rand.ptr, sres.ptr, kc.ptr))
355 {
356 if (this->use_pseudonym)
357 {
358 /* probably received a pseudonym we couldn't map */
359 DBG1(DBG_IKE, "failed to map pseudonym identity '%Y', "
360 "fallback to permanent identity request", this->permanent);
361 this->use_pseudonym = FALSE;
362 DESTROY_IF(this->pseudonym);
363 this->pseudonym = NULL;
364 return initiate(this, out);
365 }
366 return FAILED;
367 }
368 rands.len += SIM_RAND_LEN;
369 sreses.len += SIM_SRES_LEN;
370 kcs.len += SIM_KC_LEN;
371 rand = chunk_skip(rand, SIM_RAND_LEN);
372 sres = chunk_skip(sres, SIM_SRES_LEN);
373 kc = chunk_skip(kc, SIM_KC_LEN);
374 }
375 free(this->sreses.ptr);
376 this->sreses = chunk_clone(sreses);
377
378 data = chunk_cata("cccc", kcs, nonce, version, version);
379 free(this->msk.ptr);
380 id = this->permanent;
381 if (this->pseudonym)
382 {
383 id = this->pseudonym;
384 }
385 this->msk = this->crypto->derive_keys_full(this->crypto, id, data, &mk);
386
387 /* build response with AT_MAC, built over "EAP packet | NONCE_MT" */
388 message = simaka_message_create(TRUE, this->identifier++, EAP_SIM,
389 SIM_CHALLENGE, this->crypto);
390 message->add_attribute(message, AT_RAND, rands);
391 id = charon->sim->provider_gen_reauth(charon->sim, this->permanent, mk.ptr);
392 if (id)
393 {
394 message->add_attribute(message, AT_NEXT_REAUTH_ID,
395 id->get_encoding(id));
396 id->destroy(id);
397 }
398 else
399 {
400 id = charon->sim->provider_gen_pseudonym(charon->sim, this->permanent);
401 if (id)
402 {
403 message->add_attribute(message, AT_NEXT_PSEUDONYM,
404 id->get_encoding(id));
405 id->destroy(id);
406 }
407 }
408 *out = message->generate(message, nonce);
409 message->destroy(message);
410
411 free(mk.ptr);
412 this->pending = SIM_CHALLENGE;
413 return NEED_MORE;
414 }
415
416 /**
417 * process an EAP-SIM/Response/Challenge message
418 */
419 static status_t process_challenge(private_eap_sim_server_t *this,
420 simaka_message_t *in, eap_payload_t **out)
421 {
422 enumerator_t *enumerator;
423 simaka_attribute_t type;
424 chunk_t data;
425
426 if (this->pending != SIM_CHALLENGE)
427 {
428 DBG1(DBG_IKE, "received %N, but not expected",
429 simaka_subtype_names, SIM_CHALLENGE);
430 return FAILED;
431 }
432 /* verify AT_MAC attribute, signature is over "EAP packet | n*SRES" */
433 if (!in->verify(in, this->sreses))
434 {
435 return FAILED;
436 }
437
438 enumerator = in->create_attribute_enumerator(in);
439 while (enumerator->enumerate(enumerator, &type, &data))
440 {
441 if (!simaka_attribute_skippable(type))
442 {
443 enumerator->destroy(enumerator);
444 return FAILED;
445 }
446 }
447 enumerator->destroy(enumerator);
448
449 return SUCCESS;
450 }
451
452 /**
453 * EAP-SIM/Response/ClientErrorCode message
454 */
455 static status_t process_client_error(private_eap_sim_server_t *this,
456 simaka_message_t *in)
457 {
458 enumerator_t *enumerator;
459 simaka_attribute_t type;
460 chunk_t data;
461
462 enumerator = in->create_attribute_enumerator(in);
463 while (enumerator->enumerate(enumerator, &type, &data))
464 {
465 if (type == AT_CLIENT_ERROR_CODE)
466 {
467 u_int16_t code;
468
469 memcpy(&code, data.ptr, sizeof(code));
470 DBG1(DBG_IKE, "received EAP-SIM client error '%N'",
471 simaka_client_error_names, ntohs(code));
472 }
473 else if (!simaka_attribute_skippable(type))
474 {
475 break;
476 }
477 }
478 enumerator->destroy(enumerator);
479 return FAILED;
480 }
481
482 /**
483 * Implementation of eap_method_t.process
484 */
485 static status_t process(private_eap_sim_server_t *this,
486 eap_payload_t *in, eap_payload_t **out)
487 {
488 simaka_message_t *message;
489 status_t status;
490
491 message = simaka_message_create_from_payload(in, this->crypto);
492 if (!message)
493 {
494 return FAILED;
495 }
496 if (!message->parse(message))
497 {
498 message->destroy(message);
499 return FAILED;
500 }
501 switch (message->get_subtype(message))
502 {
503 case SIM_START:
504 status = process_start(this, message, out);
505 break;
506 case SIM_CHALLENGE:
507 status = process_challenge(this, message, out);
508 break;
509 case SIM_REAUTHENTICATION:
510 status = process_reauthentication(this, message, out);
511 break;
512 case SIM_CLIENT_ERROR:
513 status = process_client_error(this, message);
514 break;
515 default:
516 DBG1(DBG_IKE, "unable to process EAP-SIM subtype %N",
517 simaka_subtype_names, message->get_subtype(message));
518 status = FAILED;
519 break;
520 }
521 message->destroy(message);
522 return status;
523 }
524
525 /**
526 * Implementation of eap_method_t.get_type.
527 */
528 static eap_type_t get_type(private_eap_sim_server_t *this, u_int32_t *vendor)
529 {
530 *vendor = 0;
531 return EAP_SIM;
532 }
533
534 /**
535 * Implementation of eap_method_t.get_msk.
536 */
537 static status_t get_msk(private_eap_sim_server_t *this, chunk_t *msk)
538 {
539 if (this->msk.ptr)
540 {
541 *msk = this->msk;
542 return SUCCESS;
543 }
544 return FAILED;
545 }
546
547 /**
548 * Implementation of eap_method_t.is_mutual.
549 */
550 static bool is_mutual(private_eap_sim_server_t *this)
551 {
552 return TRUE;
553 }
554
555 /**
556 * Implementation of eap_method_t.destroy.
557 */
558 static void destroy(private_eap_sim_server_t *this)
559 {
560 this->crypto->destroy(this->crypto);
561 this->permanent->destroy(this->permanent);
562 DESTROY_IF(this->pseudonym);
563 DESTROY_IF(this->reauth);
564 free(this->sreses.ptr);
565 free(this->nonce.ptr);
566 free(this->msk.ptr);
567 free(this->counter.ptr);
568 free(this);
569 }
570
571 /*
572 * Described in header.
573 */
574 eap_sim_server_t *eap_sim_server_create(identification_t *server,
575 identification_t *peer)
576 {
577 private_eap_sim_server_t *this = malloc_thing(private_eap_sim_server_t);
578
579 this->public.interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
580 this->public.interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
581 this->public.interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
582 this->public.interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
583 this->public.interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
584 this->public.interface.destroy = (void(*)(eap_method_t*))destroy;
585
586 this->crypto = simaka_crypto_create();
587 if (!this->crypto)
588 {
589 free(this);
590 return NULL;
591 }
592 this->permanent = peer->clone(peer);
593 this->pseudonym = NULL;
594 this->reauth = NULL;
595 this->sreses = chunk_empty;
596 this->nonce = chunk_empty;
597 this->msk = chunk_empty;
598 this->counter = chunk_empty;
599 this->pending = 0;
600 this->use_reauth = this->use_pseudonym = this->use_permanent =
601 lib->settings->get_bool(lib->settings,
602 "charon.plugins.eap-sim.request_identity", TRUE);
603
604 /* generate a non-zero identifier */
605 do {
606 this->identifier = random();
607 } while (!this->identifier);
608
609 return &this->public;
610 }
611