d3f438e5fcee6bfaae26b110384501ad49df80db
[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 typedef struct private_eap_sim_server_t private_eap_sim_server_t;
27
28 /**
29 * Private data of an eap_sim_server_t object.
30 */
31 struct private_eap_sim_server_t {
32
33 /**
34 * Public authenticator_t interface.
35 */
36 eap_sim_server_t public;
37
38 /**
39 * permanent ID of peer
40 */
41 identification_t *peer;
42
43 /**
44 * EAP-SIM/AKA crypto helper
45 */
46 simaka_crypto_t *crypto;
47
48 /**
49 * unique EAP identifier
50 */
51 u_int8_t identifier;
52
53 /**
54 * concatenated SRES values
55 */
56 chunk_t sreses;
57
58 /**
59 * MSK, used for EAP-SIM based IKEv2 authentication
60 */
61 chunk_t msk;
62
63 /**
64 * EAP-SIM message we have initiated
65 */
66 simaka_subtype_t pending;
67 };
68
69 /* version of SIM protocol we speak */
70 static chunk_t version = chunk_from_chars(0x00,0x01);
71
72 /**
73 * Fetch a triplet from a provider
74 */
75 static bool get_provider_triplet(private_eap_sim_server_t *this,
76 char *rand, char *sres, char *kc)
77 {
78 enumerator_t *enumerator;
79 sim_provider_t *provider;
80 int tried = 0;
81
82 enumerator = charon->sim->create_provider_enumerator(charon->sim);
83 while (enumerator->enumerate(enumerator, &provider))
84 {
85 if (provider->get_triplet(provider, this->peer, rand, sres, kc))
86 {
87 enumerator->destroy(enumerator);
88 return TRUE;
89 }
90 tried++;
91 }
92 enumerator->destroy(enumerator);
93 DBG1(DBG_IKE, "tried %d SIM providers, but none had a triplet for '%Y'",
94 tried, this->peer);
95 return FALSE;
96 }
97
98 /**
99 * process an EAP-SIM/Response/Start message
100 */
101 static status_t process_start(private_eap_sim_server_t *this,
102 simaka_message_t *in, eap_payload_t **out)
103 {
104 simaka_message_t *message;
105 enumerator_t *enumerator;
106 simaka_attribute_t type;
107 chunk_t data, rands, rand, kcs, kc, sreses, sres, nonce = chunk_empty;
108 bool supported = FALSE;
109 int i;
110
111 if (this->pending != SIM_START)
112 {
113 DBG1(DBG_IKE, "received %N, but not expected",
114 simaka_subtype_names, SIM_START);
115 return FAILED;
116 }
117
118 enumerator = in->create_attribute_enumerator(in);
119 while (enumerator->enumerate(enumerator, &type, &data))
120 {
121 switch (type)
122 {
123 case AT_NONCE_MT:
124 nonce = data;
125 break;
126 case AT_SELECTED_VERSION:
127 if (chunk_equals(data, version))
128 {
129 supported = TRUE;
130 }
131 break;
132 default:
133 if (!simaka_attribute_skippable(type))
134 {
135 enumerator->destroy(enumerator);
136 return FAILED;
137 }
138 break;
139 }
140 }
141 enumerator->destroy(enumerator);
142
143 if (!supported || !nonce.len)
144 {
145 DBG1(DBG_IKE, "received incomplete EAP-SIM/Response/Start");
146 return FAILED;
147 }
148
149 /* read triplets from provider */
150 rand = rands = chunk_alloca(SIM_RAND_LEN * TRIPLET_COUNT);
151 kc = kcs = chunk_alloca(SIM_KC_LEN * TRIPLET_COUNT);
152 sres = sreses = chunk_alloca(SIM_SRES_LEN * TRIPLET_COUNT);
153 rands.len = kcs.len = sreses.len = 0;
154 for (i = 0; i < TRIPLET_COUNT; i++)
155 {
156 if (!get_provider_triplet(this, rand.ptr, sres.ptr, kc.ptr))
157 {
158 DBG1(DBG_IKE, "getting EAP-SIM triplet %d failed", i);
159 return FAILED;
160 }
161 rands.len += SIM_RAND_LEN;
162 sreses.len += SIM_SRES_LEN;
163 kcs.len += SIM_KC_LEN;
164 rand = chunk_skip(rand, SIM_RAND_LEN);
165 sres = chunk_skip(sres, SIM_SRES_LEN);
166 kc = chunk_skip(kc, SIM_KC_LEN);
167 }
168 free(this->sreses.ptr);
169 this->sreses = chunk_clone(sreses);
170
171 data = chunk_cata("cccc", kcs, nonce, version, version);
172 free(this->msk.ptr);
173 this->msk = this->crypto->derive_keys_full(this->crypto, this->peer, data);
174
175 /* build response with AT_MAC, built over "EAP packet | NONCE_MT" */
176 message = simaka_message_create(TRUE, this->identifier++, EAP_SIM,
177 SIM_CHALLENGE, this->crypto);
178 message->add_attribute(message, AT_RAND, rands);
179 *out = message->generate(message, nonce);
180 message->destroy(message);
181
182 this->pending = SIM_CHALLENGE;
183 return NEED_MORE;
184 }
185
186 /**
187 * process an EAP-SIM/Response/Challenge message
188 */
189 static status_t process_challenge(private_eap_sim_server_t *this,
190 simaka_message_t *in, eap_payload_t **out)
191 {
192 enumerator_t *enumerator;
193 simaka_attribute_t type;
194 chunk_t data;
195
196 if (this->pending != SIM_CHALLENGE)
197 {
198 DBG1(DBG_IKE, "received %N, but not expected",
199 simaka_subtype_names, SIM_CHALLENGE);
200 return FAILED;
201 }
202
203 enumerator = in->create_attribute_enumerator(in);
204 while (enumerator->enumerate(enumerator, &type, &data))
205 {
206 if (!simaka_attribute_skippable(type))
207 {
208 enumerator->destroy(enumerator);
209 return FAILED;
210 }
211 }
212 enumerator->destroy(enumerator);
213
214 /* verify AT_MAC attribute, signature is over "EAP packet | n*SRES" */
215 if (!in->verify(in, this->sreses))
216 {
217 DBG1(DBG_IKE, "AT_MAC verification failed");
218 return FAILED;
219 }
220 return SUCCESS;
221 }
222
223 /**
224 * EAP-SIM/Response/ClientErrorCode message
225 */
226 static status_t process_client_error(private_eap_sim_server_t *this,
227 simaka_message_t *in)
228 {
229 enumerator_t *enumerator;
230 simaka_attribute_t type;
231 chunk_t data;
232
233 enumerator = in->create_attribute_enumerator(in);
234 while (enumerator->enumerate(enumerator, &type, &data))
235 {
236 if (type == AT_CLIENT_ERROR_CODE)
237 {
238 u_int16_t code;
239
240 memcpy(&code, data.ptr, sizeof(code));
241 DBG1(DBG_IKE, "received EAP-SIM client error '%N'",
242 simaka_client_error_names, ntohs(code));
243 }
244 else if (!simaka_attribute_skippable(type))
245 {
246 break;
247 }
248 }
249 enumerator->destroy(enumerator);
250 return FAILED;
251 }
252
253 /**
254 * Implementation of eap_method_t.process
255 */
256 static status_t process(private_eap_sim_server_t *this,
257 eap_payload_t *in, eap_payload_t **out)
258 {
259 simaka_message_t *message;
260 status_t status;
261
262 message = simaka_message_create_from_payload(in, this->crypto);
263 if (!message)
264 {
265 return FAILED;
266 }
267 if (!message->parse(message))
268 {
269 message->destroy(message);
270 return FAILED;
271 }
272 switch (message->get_subtype(message))
273 {
274 case SIM_START:
275 status = process_start(this, message, out);
276 break;
277 case SIM_CHALLENGE:
278 status = process_challenge(this, message, out);
279 break;
280 case SIM_CLIENT_ERROR:
281 status = process_client_error(this, message);
282 break;
283 default:
284 DBG1(DBG_IKE, "unable to process EAP-SIM subtype %N",
285 simaka_subtype_names, message->get_subtype(message));
286 status = FAILED;
287 break;
288 }
289 message->destroy(message);
290 return status;
291 }
292
293 /**
294 * Implementation of eap_method_t.initiate
295 */
296 static status_t initiate(private_eap_sim_server_t *this, eap_payload_t **out)
297 {
298 simaka_message_t *message;
299
300 message = simaka_message_create(TRUE, this->identifier++, EAP_SIM,
301 SIM_START, this->crypto);
302 message->add_attribute(message, AT_VERSION_LIST, version);
303 *out = message->generate(message, chunk_empty);
304 message->destroy(message);
305
306 this->pending = SIM_START;
307 return NEED_MORE;
308 }
309
310 /**
311 * Implementation of eap_method_t.get_type.
312 */
313 static eap_type_t get_type(private_eap_sim_server_t *this, u_int32_t *vendor)
314 {
315 *vendor = 0;
316 return EAP_SIM;
317 }
318
319 /**
320 * Implementation of eap_method_t.get_msk.
321 */
322 static status_t get_msk(private_eap_sim_server_t *this, chunk_t *msk)
323 {
324 if (this->msk.ptr)
325 {
326 *msk = this->msk;
327 return SUCCESS;
328 }
329 return FAILED;
330 }
331
332 /**
333 * Implementation of eap_method_t.is_mutual.
334 */
335 static bool is_mutual(private_eap_sim_server_t *this)
336 {
337 return TRUE;
338 }
339
340 /**
341 * Implementation of eap_method_t.destroy.
342 */
343 static void destroy(private_eap_sim_server_t *this)
344 {
345 this->crypto->destroy(this->crypto);
346 this->peer->destroy(this->peer);
347 free(this->sreses.ptr);
348 free(this->msk.ptr);
349 free(this);
350 }
351
352 /*
353 * Described in header.
354 */
355 eap_sim_server_t *eap_sim_server_create(identification_t *server,
356 identification_t *peer)
357 {
358 private_eap_sim_server_t *this = malloc_thing(private_eap_sim_server_t);
359
360 this->public.interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
361 this->public.interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
362 this->public.interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
363 this->public.interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
364 this->public.interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
365 this->public.interface.destroy = (void(*)(eap_method_t*))destroy;
366
367 this->crypto = simaka_crypto_create();
368 if (!this->crypto)
369 {
370 free(this);
371 return NULL;
372 }
373 this->peer = peer->clone(peer);
374 this->sreses = chunk_empty;
375 this->msk = chunk_empty;
376 this->pending = 0;
377 /* generate a non-zero identifier */
378 do {
379 this->identifier = random();
380 } while (!this->identifier);
381
382 return &this->public;
383 }
384