d0f0595de2f2c87e68421050d43ed577efaab78d
[strongswan.git] / src / libcharon / plugins / eap_dynamic / eap_dynamic.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
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_dynamic.h"
17
18 #include <daemon.h>
19 #include <library.h>
20
21 typedef struct private_eap_dynamic_t private_eap_dynamic_t;
22
23 /**
24 * Private data of an eap_dynamic_t object.
25 */
26 struct private_eap_dynamic_t {
27
28 /**
29 * Public authenticator_t interface.
30 */
31 eap_dynamic_t public;
32
33 /**
34 * ID of the server
35 */
36 identification_t *server;
37
38 /**
39 * ID of the peer
40 */
41 identification_t *peer;
42
43 /**
44 * Our supported EAP types (as eap_vendor_type_t*)
45 */
46 linked_list_t *types;
47
48 /**
49 * EAP types supported by peer, if any
50 */
51 linked_list_t *other_types;
52
53 /**
54 * Prefer types sent by peer
55 */
56 bool prefer_peer;
57
58 /**
59 * The proxied EAP method
60 */
61 eap_method_t *method;
62 };
63
64 /**
65 * Compare two eap_vendor_type_t objects
66 */
67 static bool entry_matches(eap_vendor_type_t *item, eap_vendor_type_t *other)
68 {
69 return item->type == other->type && item->vendor == other->vendor;
70 }
71
72 /**
73 * Load the given EAP method
74 */
75 static eap_method_t *load_method(private_eap_dynamic_t *this,
76 eap_type_t type, uint32_t vendor)
77 {
78 eap_method_t *method;
79
80 method = charon->eap->create_instance(charon->eap, type, vendor, EAP_SERVER,
81 this->server, this->peer);
82 if (!method)
83 {
84 if (vendor)
85 {
86 DBG1(DBG_IKE, "loading vendor specific EAP method %d-%d failed",
87 type, vendor);
88 }
89 else
90 {
91 DBG1(DBG_IKE, "loading %N method failed", eap_type_names, type);
92 }
93 }
94 return method;
95 }
96
97 /**
98 * Select the first method we can instantiate and is supported by both peers.
99 */
100 static void select_method(private_eap_dynamic_t *this)
101 {
102 eap_vendor_type_t *entry;
103 linked_list_t *outer = this->types, *inner = this->other_types;
104 char *who = "peer";
105
106 if (this->other_types && this->prefer_peer)
107 {
108 outer = this->other_types;
109 inner = this->types;
110 who = "us";
111 }
112
113 while (outer->remove_first(outer, (void*)&entry) == SUCCESS)
114 {
115 if (inner)
116 {
117 if (inner->find_first(inner, (void*)entry_matches,
118 NULL, entry) != SUCCESS)
119 {
120 if (entry->vendor)
121 {
122 DBG2(DBG_IKE, "proposed vendor specific EAP method %d-%d "
123 "not supported by %s, skipped", entry->type,
124 entry->vendor, who);
125 }
126 else
127 {
128 DBG2(DBG_IKE, "proposed %N method not supported by %s, "
129 "skipped", eap_type_names, entry->type, who);
130 }
131 free(entry);
132 continue;
133 }
134 }
135 this->method = load_method(this, entry->type, entry->vendor);
136 if (this->method)
137 {
138 if (entry->vendor)
139 {
140 DBG1(DBG_IKE, "vendor specific EAP method %d-%d selected",
141 entry->type, entry->vendor);
142 }
143 else
144 {
145 DBG1(DBG_IKE, "%N method selected", eap_type_names,
146 entry->type);
147 }
148 free(entry);
149 break;
150 }
151 free(entry);
152 }
153 }
154
155 METHOD(eap_method_t, initiate, status_t,
156 private_eap_dynamic_t *this, eap_payload_t **out)
157 {
158 if (!this->method)
159 {
160 select_method(this);
161 if (!this->method)
162 {
163 DBG1(DBG_IKE, "no supported EAP method found");
164 return FAILED;
165 }
166 }
167 return this->method->initiate(this->method, out);
168 }
169
170 METHOD(eap_method_t, process, status_t,
171 private_eap_dynamic_t *this, eap_payload_t *in, eap_payload_t **out)
172 {
173 eap_type_t received_type, type;
174 uint32_t received_vendor, vendor;
175
176 received_type = in->get_type(in, &received_vendor);
177 if (received_vendor == 0 && received_type == EAP_NAK)
178 {
179 enumerator_t *enumerator;
180
181 DBG1(DBG_IKE, "received %N, selecting a different EAP method",
182 eap_type_names, EAP_NAK);
183
184 if (this->other_types)
185 { /* we already received a Nak or a proper response before */
186 DBG1(DBG_IKE, "%N is not supported in this state", eap_type_names,
187 EAP_NAK);
188 return FAILED;
189 }
190
191 this->other_types = linked_list_create();
192 enumerator = in->get_types(in);
193 while (enumerator->enumerate(enumerator, &type, &vendor))
194 {
195 eap_vendor_type_t *entry;
196
197 if (!type)
198 {
199 DBG1(DBG_IKE, "peer does not support any other EAP methods");
200 enumerator->destroy(enumerator);
201 return FAILED;
202 }
203 INIT(entry,
204 .type = type,
205 .vendor = vendor,
206 );
207 this->other_types->insert_last(this->other_types, entry);
208 }
209 enumerator->destroy(enumerator);
210
211 /* restart with a different method */
212 this->method->destroy(this->method);
213 this->method = NULL;
214 return initiate(this, out);
215 }
216 if (!this->other_types)
217 { /* so we don't handle EAP-Naks later */
218 this->other_types = linked_list_create();
219 }
220 if (this->method)
221 {
222 return this->method->process(this->method, in, out);
223 }
224 return FAILED;
225 }
226
227 METHOD(eap_method_t, get_type, eap_type_t,
228 private_eap_dynamic_t *this, uint32_t *vendor)
229 {
230 if (this->method)
231 {
232 return this->method->get_type(this->method, vendor);
233 }
234 *vendor = 0;
235 return EAP_DYNAMIC;
236 }
237
238 METHOD(eap_method_t, get_msk, status_t,
239 private_eap_dynamic_t *this, chunk_t *msk)
240 {
241 if (this->method)
242 {
243 return this->method->get_msk(this->method, msk);
244 }
245 return FAILED;
246 }
247
248 METHOD(eap_method_t, get_identifier, uint8_t,
249 private_eap_dynamic_t *this)
250 {
251 if (this->method)
252 {
253 return this->method->get_identifier(this->method);
254 }
255 return 0;
256 }
257
258 METHOD(eap_method_t, set_identifier, void,
259 private_eap_dynamic_t *this, uint8_t identifier)
260 {
261 if (this->method)
262 {
263 this->method->set_identifier(this->method, identifier);
264 }
265 }
266
267 METHOD(eap_method_t, is_mutual, bool,
268 private_eap_dynamic_t *this)
269 {
270 if (this->method)
271 {
272 return this->method->is_mutual(this->method);
273 }
274 return FALSE;
275 }
276
277 METHOD(eap_method_t, destroy, void,
278 private_eap_dynamic_t *this)
279 {
280 DESTROY_IF(this->method);
281 this->types->destroy_function(this->types, (void*)free);
282 DESTROY_FUNCTION_IF(this->other_types, (void*)free);
283 this->server->destroy(this->server);
284 this->peer->destroy(this->peer);
285 free(this);
286 }
287
288 /**
289 * Parse preferred EAP types
290 */
291 static void handle_preferred_eap_types(private_eap_dynamic_t *this,
292 char *methods)
293 {
294 enumerator_t *enumerator;
295 eap_vendor_type_t *type, *entry;
296 linked_list_t *preferred;
297 char *method;
298
299 /* parse preferred EAP methods, format: type[-vendor], ... */
300 preferred = linked_list_create();
301 enumerator = enumerator_create_token(methods, ",", " ");
302 while (enumerator->enumerate(enumerator, &method))
303 {
304 type = eap_vendor_type_from_string(method);
305 if (type)
306 {
307 preferred->insert_last(preferred, type);
308 }
309 }
310 enumerator->destroy(enumerator);
311
312 enumerator = this->types->create_enumerator(this->types);
313 while (preferred->remove_last(preferred, (void**)&type) == SUCCESS)
314 { /* move (supported) types to the front, maintain the preferred order */
315 this->types->reset_enumerator(this->types, enumerator);
316 while (enumerator->enumerate(enumerator, &entry))
317 {
318 if (entry_matches(entry, type))
319 {
320 this->types->remove_at(this->types, enumerator);
321 this->types->insert_first(this->types, entry);
322 break;
323 }
324 }
325 free(type);
326 }
327 enumerator->destroy(enumerator);
328 preferred->destroy(preferred);
329 }
330
331 /**
332 * Get all supported EAP methods
333 */
334 static void get_supported_eap_types(private_eap_dynamic_t *this)
335 {
336 enumerator_t *enumerator;
337 eap_type_t type;
338 uint32_t vendor;
339
340 enumerator = charon->eap->create_enumerator(charon->eap, EAP_SERVER);
341 while (enumerator->enumerate(enumerator, &type, &vendor))
342 {
343 eap_vendor_type_t *entry;
344
345 INIT(entry,
346 .type = type,
347 .vendor = vendor,
348 );
349 this->types->insert_last(this->types, entry);
350 }
351 enumerator->destroy(enumerator);
352 }
353
354 /*
355 * Defined in header
356 */
357 eap_dynamic_t *eap_dynamic_create(identification_t *server,
358 identification_t *peer)
359 {
360 private_eap_dynamic_t *this;
361 char *preferred;
362
363 INIT(this,
364 .public = {
365 .interface = {
366 .initiate = _initiate,
367 .process = _process,
368 .get_type = _get_type,
369 .is_mutual = _is_mutual,
370 .get_msk = _get_msk,
371 .get_identifier = _get_identifier,
372 .set_identifier = _set_identifier,
373 .destroy = _destroy,
374 },
375 },
376 .peer = peer->clone(peer),
377 .server = server->clone(server),
378 .types = linked_list_create(),
379 .prefer_peer = lib->settings->get_bool(lib->settings,
380 "%s.plugins.eap-dynamic.prefer_peer", FALSE, lib->ns),
381 );
382
383 /* get all supported EAP methods */
384 get_supported_eap_types(this);
385 /* move preferred methods to the front */
386 preferred = lib->settings->get_str(lib->settings,
387 "%s.plugins.eap-dynamic.preferred", NULL, lib->ns);
388 if (preferred)
389 {
390 handle_preferred_eap_types(this, preferred);
391 }
392 return &this->public;
393 }