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