eap-dynamic: Publish the get_auth() method of the wrapped EAP method
[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 METHOD(eap_method_t, get_auth, auth_cfg_t*,
98 private_eap_dynamic_t *this)
99 {
100 /* get_auth() is only registered if the EAP method supports it */
101 return this->method->get_auth(this->method);
102 }
103
104 /**
105 * Select the first method we can instantiate and is supported by both peers.
106 */
107 static void select_method(private_eap_dynamic_t *this)
108 {
109 eap_vendor_type_t *entry;
110 linked_list_t *outer = this->types, *inner = this->other_types;
111 char *who = "peer";
112
113 if (this->other_types && this->prefer_peer)
114 {
115 outer = this->other_types;
116 inner = this->types;
117 who = "us";
118 }
119
120 while (outer->remove_first(outer, (void*)&entry) == SUCCESS)
121 {
122 if (inner)
123 {
124 if (inner->find_first(inner, (void*)entry_matches,
125 NULL, entry) != SUCCESS)
126 {
127 if (entry->vendor)
128 {
129 DBG2(DBG_IKE, "proposed vendor specific EAP method %d-%d "
130 "not supported by %s, skipped", entry->type,
131 entry->vendor, who);
132 }
133 else
134 {
135 DBG2(DBG_IKE, "proposed %N method not supported by %s, "
136 "skipped", eap_type_names, entry->type, who);
137 }
138 free(entry);
139 continue;
140 }
141 }
142 this->method = load_method(this, entry->type, entry->vendor);
143 if (this->method)
144 {
145 if (this->method->get_auth)
146 {
147 this->public.interface.get_auth = _get_auth;
148 }
149 if (entry->vendor)
150 {
151 DBG1(DBG_IKE, "vendor specific EAP method %d-%d selected",
152 entry->type, entry->vendor);
153 }
154 else
155 {
156 DBG1(DBG_IKE, "%N method selected", eap_type_names,
157 entry->type);
158 }
159 free(entry);
160 break;
161 }
162 free(entry);
163 }
164 }
165
166 METHOD(eap_method_t, initiate, status_t,
167 private_eap_dynamic_t *this, eap_payload_t **out)
168 {
169 if (!this->method)
170 {
171 select_method(this);
172 if (!this->method)
173 {
174 DBG1(DBG_IKE, "no supported EAP method found");
175 return FAILED;
176 }
177 }
178 return this->method->initiate(this->method, out);
179 }
180
181 METHOD(eap_method_t, process, status_t,
182 private_eap_dynamic_t *this, eap_payload_t *in, eap_payload_t **out)
183 {
184 eap_type_t received_type, type;
185 uint32_t received_vendor, vendor;
186
187 received_type = in->get_type(in, &received_vendor);
188 if (received_vendor == 0 && received_type == EAP_NAK)
189 {
190 enumerator_t *enumerator;
191
192 DBG1(DBG_IKE, "received %N, selecting a different EAP method",
193 eap_type_names, EAP_NAK);
194
195 if (this->other_types)
196 { /* we already received a Nak or a proper response before */
197 DBG1(DBG_IKE, "%N is not supported in this state", eap_type_names,
198 EAP_NAK);
199 return FAILED;
200 }
201
202 this->other_types = linked_list_create();
203 enumerator = in->get_types(in);
204 while (enumerator->enumerate(enumerator, &type, &vendor))
205 {
206 eap_vendor_type_t *entry;
207
208 if (!type)
209 {
210 DBG1(DBG_IKE, "peer does not support any other EAP methods");
211 enumerator->destroy(enumerator);
212 return FAILED;
213 }
214 INIT(entry,
215 .type = type,
216 .vendor = vendor,
217 );
218 this->other_types->insert_last(this->other_types, entry);
219 }
220 enumerator->destroy(enumerator);
221
222 /* restart with a different method */
223 this->method->destroy(this->method);
224 this->method = NULL;
225 this->public.interface.get_auth = NULL;
226 return initiate(this, out);
227 }
228 if (!this->other_types)
229 { /* so we don't handle EAP-Naks later */
230 this->other_types = linked_list_create();
231 }
232 if (this->method)
233 {
234 return this->method->process(this->method, in, out);
235 }
236 return FAILED;
237 }
238
239 METHOD(eap_method_t, get_type, eap_type_t,
240 private_eap_dynamic_t *this, uint32_t *vendor)
241 {
242 if (this->method)
243 {
244 return this->method->get_type(this->method, vendor);
245 }
246 *vendor = 0;
247 return EAP_DYNAMIC;
248 }
249
250 METHOD(eap_method_t, get_msk, status_t,
251 private_eap_dynamic_t *this, chunk_t *msk)
252 {
253 if (this->method)
254 {
255 return this->method->get_msk(this->method, msk);
256 }
257 return FAILED;
258 }
259
260 METHOD(eap_method_t, get_identifier, uint8_t,
261 private_eap_dynamic_t *this)
262 {
263 if (this->method)
264 {
265 return this->method->get_identifier(this->method);
266 }
267 return 0;
268 }
269
270 METHOD(eap_method_t, set_identifier, void,
271 private_eap_dynamic_t *this, uint8_t identifier)
272 {
273 if (this->method)
274 {
275 this->method->set_identifier(this->method, identifier);
276 }
277 }
278
279 METHOD(eap_method_t, is_mutual, bool,
280 private_eap_dynamic_t *this)
281 {
282 if (this->method)
283 {
284 return this->method->is_mutual(this->method);
285 }
286 return FALSE;
287 }
288
289 METHOD(eap_method_t, destroy, void,
290 private_eap_dynamic_t *this)
291 {
292 DESTROY_IF(this->method);
293 this->types->destroy_function(this->types, (void*)free);
294 DESTROY_FUNCTION_IF(this->other_types, (void*)free);
295 this->server->destroy(this->server);
296 this->peer->destroy(this->peer);
297 free(this);
298 }
299
300 /**
301 * Parse preferred EAP types
302 */
303 static void handle_preferred_eap_types(private_eap_dynamic_t *this,
304 char *methods)
305 {
306 enumerator_t *enumerator;
307 eap_vendor_type_t *type, *entry;
308 linked_list_t *preferred;
309 char *method;
310
311 /* parse preferred EAP methods, format: type[-vendor], ... */
312 preferred = linked_list_create();
313 enumerator = enumerator_create_token(methods, ",", " ");
314 while (enumerator->enumerate(enumerator, &method))
315 {
316 type = eap_vendor_type_from_string(method);
317 if (type)
318 {
319 preferred->insert_last(preferred, type);
320 }
321 }
322 enumerator->destroy(enumerator);
323
324 enumerator = this->types->create_enumerator(this->types);
325 while (preferred->remove_last(preferred, (void**)&type) == SUCCESS)
326 { /* move (supported) types to the front, maintain the preferred order */
327 this->types->reset_enumerator(this->types, enumerator);
328 while (enumerator->enumerate(enumerator, &entry))
329 {
330 if (entry_matches(entry, type))
331 {
332 this->types->remove_at(this->types, enumerator);
333 this->types->insert_first(this->types, entry);
334 break;
335 }
336 }
337 free(type);
338 }
339 enumerator->destroy(enumerator);
340 preferred->destroy(preferred);
341 }
342
343 /**
344 * Get all supported EAP methods
345 */
346 static void get_supported_eap_types(private_eap_dynamic_t *this)
347 {
348 enumerator_t *enumerator;
349 eap_type_t type;
350 uint32_t vendor;
351
352 enumerator = charon->eap->create_enumerator(charon->eap, EAP_SERVER);
353 while (enumerator->enumerate(enumerator, &type, &vendor))
354 {
355 eap_vendor_type_t *entry;
356
357 INIT(entry,
358 .type = type,
359 .vendor = vendor,
360 );
361 this->types->insert_last(this->types, entry);
362 }
363 enumerator->destroy(enumerator);
364 }
365
366 /*
367 * Defined in header
368 */
369 eap_dynamic_t *eap_dynamic_create(identification_t *server,
370 identification_t *peer)
371 {
372 private_eap_dynamic_t *this;
373 char *preferred;
374
375 INIT(this,
376 .public = {
377 .interface = {
378 .initiate = _initiate,
379 .process = _process,
380 .get_type = _get_type,
381 .is_mutual = _is_mutual,
382 .get_msk = _get_msk,
383 .get_identifier = _get_identifier,
384 .set_identifier = _set_identifier,
385 .destroy = _destroy,
386 },
387 },
388 .peer = peer->clone(peer),
389 .server = server->clone(server),
390 .types = linked_list_create(),
391 .prefer_peer = lib->settings->get_bool(lib->settings,
392 "%s.plugins.eap-dynamic.prefer_peer", FALSE, lib->ns),
393 );
394
395 /* get all supported EAP methods */
396 get_supported_eap_types(this);
397 /* move preferred methods to the front */
398 preferred = lib->settings->get_str(lib->settings,
399 "%s.plugins.eap-dynamic.preferred", NULL, lib->ns);
400 if (preferred)
401 {
402 handle_preferred_eap_types(this, preferred);
403 }
404 return &this->public;
405 }