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