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