/src/strongswan/src/libcharon/plugins/eap_radius/eap_radius.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2012-2018 Tobias Brunner |
3 | | * Copyright (C) 2009 Martin Willi |
4 | | * |
5 | | * Copyright (C) secunet Security Networks AG |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or modify it |
8 | | * under the terms of the GNU General Public License as published by the |
9 | | * Free Software Foundation; either version 2 of the License, or (at your |
10 | | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. |
11 | | * |
12 | | * This program is distributed in the hope that it will be useful, but |
13 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
14 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
15 | | * for more details. |
16 | | */ |
17 | | |
18 | | #include "eap_radius.h" |
19 | | #include "eap_radius_plugin.h" |
20 | | #include "eap_radius_forward.h" |
21 | | #include "eap_radius_provider.h" |
22 | | #include "eap_radius_accounting.h" |
23 | | |
24 | | #include <radius_message.h> |
25 | | #include <radius_client.h> |
26 | | #include <bio/bio_writer.h> |
27 | | |
28 | | #include <daemon.h> |
29 | | |
30 | | typedef struct private_eap_radius_t private_eap_radius_t; |
31 | | |
32 | | /** |
33 | | * Private data of an eap_radius_t object. |
34 | | */ |
35 | | struct private_eap_radius_t { |
36 | | |
37 | | /** |
38 | | * Public authenticator_t interface. |
39 | | */ |
40 | | eap_radius_t public; |
41 | | |
42 | | /** |
43 | | * ID of the server |
44 | | */ |
45 | | identification_t *server; |
46 | | |
47 | | /** |
48 | | * ID of the peer |
49 | | */ |
50 | | identification_t *peer; |
51 | | |
52 | | /** |
53 | | * EAP method type we are proxying |
54 | | */ |
55 | | eap_type_t type; |
56 | | |
57 | | /** |
58 | | * EAP vendor, if any |
59 | | */ |
60 | | pen_t vendor; |
61 | | |
62 | | /** |
63 | | * EAP message identifier |
64 | | */ |
65 | | uint8_t identifier; |
66 | | |
67 | | /** |
68 | | * RADIUS client instance |
69 | | */ |
70 | | radius_client_t *client; |
71 | | |
72 | | /** |
73 | | * TRUE to use EAP-Start, FALSE to send EAP-Identity Response directly |
74 | | */ |
75 | | bool eap_start; |
76 | | |
77 | | /** |
78 | | * Prefix to prepend to EAP identity |
79 | | */ |
80 | | char *id_prefix; |
81 | | }; |
82 | | |
83 | | /** |
84 | | * Add EAP-Identity to RADIUS message |
85 | | */ |
86 | | static void add_eap_identity(private_eap_radius_t *this, |
87 | | radius_message_t *request) |
88 | 0 | { |
89 | 0 | struct { |
90 | | /** EAP code (REQUEST/RESPONSE) */ |
91 | 0 | uint8_t code; |
92 | | /** unique message identifier */ |
93 | 0 | uint8_t identifier; |
94 | | /** length of whole message */ |
95 | 0 | uint16_t length; |
96 | | /** EAP type */ |
97 | 0 | uint8_t type; |
98 | | /** identity data */ |
99 | 0 | uint8_t data[]; |
100 | 0 | } __attribute__((__packed__)) *hdr; |
101 | 0 | chunk_t id, prefix; |
102 | 0 | size_t len; |
103 | |
|
104 | 0 | id = this->peer->get_encoding(this->peer); |
105 | 0 | prefix = chunk_create(this->id_prefix, strlen(this->id_prefix)); |
106 | 0 | len = sizeof(*hdr) + prefix.len + id.len; |
107 | |
|
108 | 0 | hdr = alloca(len); |
109 | 0 | hdr->code = EAP_RESPONSE; |
110 | 0 | hdr->identifier = this->identifier; |
111 | 0 | hdr->length = htons(len); |
112 | 0 | hdr->type = EAP_IDENTITY; |
113 | 0 | memcpy(hdr->data, prefix.ptr, prefix.len); |
114 | 0 | memcpy(hdr->data + prefix.len, id.ptr, id.len); |
115 | |
|
116 | 0 | request->add(request, RAT_EAP_MESSAGE, chunk_create((u_char*)hdr, len)); |
117 | 0 | } |
118 | | |
119 | | /** |
120 | | * Copy EAP-Message attribute from RADIUS message to an new EAP payload |
121 | | */ |
122 | | static bool radius2ike(private_eap_radius_t *this, |
123 | | radius_message_t *msg, eap_payload_t **out) |
124 | 0 | { |
125 | 0 | enumerator_t *enumerator; |
126 | 0 | eap_payload_t *payload; |
127 | 0 | chunk_t data, message = chunk_empty; |
128 | 0 | int type; |
129 | |
|
130 | 0 | enumerator = msg->create_enumerator(msg); |
131 | 0 | while (enumerator->enumerate(enumerator, &type, &data)) |
132 | 0 | { |
133 | 0 | if (type == RAT_EAP_MESSAGE && data.len) |
134 | 0 | { |
135 | 0 | message = chunk_cat("mc", message, data); |
136 | 0 | } |
137 | 0 | } |
138 | 0 | enumerator->destroy(enumerator); |
139 | 0 | if (message.len) |
140 | 0 | { |
141 | 0 | *out = payload = eap_payload_create_data(message); |
142 | | |
143 | | /* apply EAP method selected by RADIUS server */ |
144 | 0 | this->type = payload->get_type(payload, &this->vendor); |
145 | |
|
146 | 0 | DBG3(DBG_IKE, "%N payload %B", eap_type_names, this->type, &message); |
147 | 0 | free(message.ptr); |
148 | 0 | return TRUE; |
149 | 0 | } |
150 | 0 | return FALSE; |
151 | 0 | } |
152 | | |
153 | | /** |
154 | | * See header. |
155 | | */ |
156 | | void eap_radius_build_attributes(radius_message_t *request) |
157 | 0 | { |
158 | 0 | ike_sa_t *ike_sa; |
159 | 0 | host_t *host; |
160 | 0 | char buf[40], *station_id_fmt, *session_id; |
161 | 0 | uint32_t value; |
162 | 0 | chunk_t chunk; |
163 | | |
164 | | /* virtual NAS-Port-Type */ |
165 | 0 | value = htonl(5); |
166 | 0 | request->add(request, RAT_NAS_PORT_TYPE, chunk_from_thing(value)); |
167 | | /* framed ServiceType */ |
168 | 0 | value = htonl(2); |
169 | 0 | request->add(request, RAT_SERVICE_TYPE, chunk_from_thing(value)); |
170 | |
|
171 | 0 | ike_sa = charon->bus->get_sa(charon->bus); |
172 | 0 | if (ike_sa) |
173 | 0 | { |
174 | 0 | value = htonl(ike_sa->get_unique_id(ike_sa)); |
175 | 0 | request->add(request, RAT_NAS_PORT, chunk_from_thing(value)); |
176 | 0 | request->add(request, RAT_NAS_PORT_ID, |
177 | 0 | chunk_from_str(ike_sa->get_name(ike_sa))); |
178 | |
|
179 | 0 | host = ike_sa->get_my_host(ike_sa); |
180 | 0 | chunk = host->get_address(host); |
181 | 0 | switch (host->get_family(host)) |
182 | 0 | { |
183 | 0 | case AF_INET: |
184 | 0 | request->add(request, RAT_NAS_IP_ADDRESS, chunk); |
185 | 0 | break; |
186 | 0 | case AF_INET6: |
187 | 0 | request->add(request, RAT_NAS_IPV6_ADDRESS, chunk); |
188 | 0 | default: |
189 | 0 | break; |
190 | 0 | } |
191 | 0 | if (lib->settings->get_bool(lib->settings, |
192 | 0 | "%s.plugins.eap-radius.station_id_with_port", |
193 | 0 | TRUE, lib->ns)) |
194 | 0 | { |
195 | 0 | station_id_fmt = "%#H"; |
196 | 0 | } |
197 | 0 | else |
198 | 0 | { |
199 | 0 | station_id_fmt = "%H"; |
200 | 0 | } |
201 | 0 | snprintf(buf, sizeof(buf), station_id_fmt, host); |
202 | 0 | request->add(request, RAT_CALLED_STATION_ID, chunk_from_str(buf)); |
203 | 0 | host = ike_sa->get_other_host(ike_sa); |
204 | 0 | snprintf(buf, sizeof(buf), station_id_fmt, host); |
205 | 0 | request->add(request, RAT_CALLING_STATION_ID, chunk_from_str(buf)); |
206 | |
|
207 | 0 | session_id = eap_radius_accounting_session_id(ike_sa); |
208 | 0 | if (session_id) |
209 | 0 | { |
210 | 0 | request->add(request, RAT_ACCT_SESSION_ID, |
211 | 0 | chunk_from_str(session_id)); |
212 | 0 | free(session_id); |
213 | 0 | } |
214 | 0 | } |
215 | 0 | } |
216 | | |
217 | | /** |
218 | | * Add a set of RADIUS attributes to a request message |
219 | | */ |
220 | | static void add_radius_request_attrs(private_eap_radius_t *this, |
221 | | radius_message_t *request) |
222 | 0 | { |
223 | 0 | chunk_t chunk; |
224 | |
|
225 | 0 | chunk = chunk_from_str(this->id_prefix); |
226 | 0 | chunk = chunk_cata("cc", chunk, this->peer->get_encoding(this->peer)); |
227 | 0 | request->add(request, RAT_USER_NAME, chunk); |
228 | |
|
229 | 0 | eap_radius_build_attributes(request); |
230 | 0 | eap_radius_forward_from_ike(request); |
231 | 0 | } |
232 | | |
233 | | METHOD(eap_method_t, initiate, status_t, |
234 | | private_eap_radius_t *this, eap_payload_t **out) |
235 | 0 | { |
236 | 0 | radius_message_t *request, *response; |
237 | 0 | status_t status = FAILED; |
238 | |
|
239 | 0 | request = radius_message_create(RMC_ACCESS_REQUEST); |
240 | 0 | add_radius_request_attrs(this, request); |
241 | |
|
242 | 0 | if (this->eap_start) |
243 | 0 | { |
244 | 0 | request->add(request, RAT_EAP_MESSAGE, chunk_empty); |
245 | 0 | } |
246 | 0 | else |
247 | 0 | { |
248 | 0 | add_eap_identity(this, request); |
249 | 0 | } |
250 | |
|
251 | 0 | response = this->client->request(this->client, request); |
252 | 0 | if (response) |
253 | 0 | { |
254 | 0 | eap_radius_forward_to_ike(response); |
255 | 0 | switch (response->get_code(response)) |
256 | 0 | { |
257 | 0 | case RMC_ACCESS_CHALLENGE: |
258 | 0 | if (radius2ike(this, response, out)) |
259 | 0 | { |
260 | 0 | status = NEED_MORE; |
261 | 0 | } |
262 | 0 | break; |
263 | 0 | case RMC_ACCESS_ACCEPT: |
264 | | /* Microsoft RADIUS servers can run in a mode where they respond |
265 | | * like this on the first request (i.e. without authentication), |
266 | | * we treat this as Access-Reject */ |
267 | 0 | case RMC_ACCESS_REJECT: |
268 | 0 | default: |
269 | 0 | DBG1(DBG_IKE, "RADIUS authentication of '%Y' failed", |
270 | 0 | this->peer); |
271 | 0 | break; |
272 | 0 | } |
273 | 0 | response->destroy(response); |
274 | 0 | } |
275 | 0 | else |
276 | 0 | { |
277 | 0 | eap_radius_handle_timeout(NULL); |
278 | 0 | } |
279 | 0 | request->destroy(request); |
280 | 0 | return status; |
281 | 0 | } |
282 | | |
283 | | /** |
284 | | * Handle the Class attribute |
285 | | */ |
286 | | static void process_class(radius_message_t *msg) |
287 | 0 | { |
288 | 0 | enumerator_t *enumerator; |
289 | 0 | ike_sa_t *ike_sa; |
290 | 0 | identification_t *id; |
291 | 0 | auth_cfg_t *auth; |
292 | 0 | chunk_t data; |
293 | 0 | bool class_group, class_send; |
294 | 0 | int type; |
295 | |
|
296 | 0 | class_group = lib->settings->get_bool(lib->settings, |
297 | 0 | "%s.plugins.eap-radius.class_group", FALSE, lib->ns); |
298 | 0 | class_send = lib->settings->get_bool(lib->settings, |
299 | 0 | "%s.plugins.eap-radius.accounting_send_class", FALSE, lib->ns); |
300 | 0 | ike_sa = charon->bus->get_sa(charon->bus); |
301 | |
|
302 | 0 | if ((!class_group && !class_send) || !ike_sa) |
303 | 0 | { |
304 | 0 | return; |
305 | 0 | } |
306 | | |
307 | 0 | enumerator = msg->create_enumerator(msg); |
308 | 0 | while (enumerator->enumerate(enumerator, &type, &data)) |
309 | 0 | { |
310 | 0 | if (type == RAT_CLASS) |
311 | 0 | { |
312 | 0 | if (class_group && data.len < 44) |
313 | 0 | { /* quirk: ignore long class attributes, these are used for |
314 | | * other purposes by some RADIUS servers (such as NPS). */ |
315 | 0 | auth = ike_sa->get_auth_cfg(ike_sa, FALSE); |
316 | 0 | id = identification_create_from_data(data); |
317 | 0 | DBG1(DBG_CFG, "received group membership '%Y' from RADIUS", |
318 | 0 | id); |
319 | 0 | auth->add(auth, AUTH_RULE_GROUP, id); |
320 | 0 | } |
321 | 0 | if (class_send) |
322 | 0 | { |
323 | 0 | eap_radius_accounting_add_class(ike_sa, data); |
324 | 0 | } |
325 | 0 | } |
326 | 0 | } |
327 | 0 | enumerator->destroy(enumerator); |
328 | 0 | } |
329 | | |
330 | | /** |
331 | | * Handle the Filter-Id attribute as IPsec CHILD_SA name |
332 | | */ |
333 | | static void process_filter_id(radius_message_t *msg) |
334 | 0 | { |
335 | 0 | enumerator_t *enumerator; |
336 | 0 | int type; |
337 | 0 | uint8_t tunnel_tag DBG_UNUSED; |
338 | 0 | uint32_t tunnel_type; |
339 | 0 | chunk_t filter_id = chunk_empty, data; |
340 | 0 | bool is_esp_tunnel = FALSE; |
341 | |
|
342 | 0 | enumerator = msg->create_enumerator(msg); |
343 | 0 | while (enumerator->enumerate(enumerator, &type, &data)) |
344 | 0 | { |
345 | 0 | switch (type) |
346 | 0 | { |
347 | 0 | case RAT_TUNNEL_TYPE: |
348 | 0 | if (data.len != 4) |
349 | 0 | { |
350 | 0 | continue; |
351 | 0 | } |
352 | 0 | tunnel_tag = *data.ptr; |
353 | 0 | *data.ptr = 0x00; |
354 | 0 | tunnel_type = untoh32(data.ptr); |
355 | 0 | DBG1(DBG_IKE, "received RADIUS attribute Tunnel-Type: " |
356 | 0 | "tag = %u, value = %u", tunnel_tag, tunnel_type); |
357 | 0 | is_esp_tunnel = (tunnel_type == RADIUS_TUNNEL_TYPE_ESP); |
358 | 0 | break; |
359 | 0 | case RAT_FILTER_ID: |
360 | 0 | filter_id = data; |
361 | 0 | DBG1(DBG_IKE, "received RADIUS attribute Filter-Id: " |
362 | 0 | "'%.*s'", (int)filter_id.len, filter_id.ptr); |
363 | 0 | break; |
364 | 0 | default: |
365 | 0 | break; |
366 | 0 | } |
367 | 0 | } |
368 | 0 | enumerator->destroy(enumerator); |
369 | |
|
370 | 0 | if (is_esp_tunnel && filter_id.len) |
371 | 0 | { |
372 | 0 | identification_t *id; |
373 | 0 | ike_sa_t *ike_sa; |
374 | 0 | auth_cfg_t *auth; |
375 | |
|
376 | 0 | ike_sa = charon->bus->get_sa(charon->bus); |
377 | 0 | if (ike_sa) |
378 | 0 | { |
379 | 0 | auth = ike_sa->get_auth_cfg(ike_sa, FALSE); |
380 | 0 | id = identification_create_from_data(filter_id); |
381 | 0 | auth->add(auth, AUTH_RULE_GROUP, id); |
382 | 0 | } |
383 | 0 | } |
384 | 0 | } |
385 | | |
386 | | /** |
387 | | * Handle Session-Timeout attribute and Interim updates |
388 | | */ |
389 | | static void process_timeout(radius_message_t *msg) |
390 | 0 | { |
391 | 0 | enumerator_t *enumerator; |
392 | 0 | ike_sa_t *ike_sa; |
393 | 0 | chunk_t data; |
394 | 0 | int type; |
395 | |
|
396 | 0 | ike_sa = charon->bus->get_sa(charon->bus); |
397 | 0 | if (ike_sa) |
398 | 0 | { |
399 | 0 | enumerator = msg->create_enumerator(msg); |
400 | 0 | while (enumerator->enumerate(enumerator, &type, &data)) |
401 | 0 | { |
402 | 0 | if (type == RAT_SESSION_TIMEOUT && data.len == 4) |
403 | 0 | { |
404 | 0 | ike_sa->set_auth_lifetime(ike_sa, untoh32(data.ptr)); |
405 | 0 | } |
406 | 0 | else if (type == RAT_ACCT_INTERIM_INTERVAL && data.len == 4) |
407 | 0 | { |
408 | 0 | eap_radius_accounting_start_interim(ike_sa, untoh32(data.ptr)); |
409 | 0 | } |
410 | 0 | } |
411 | 0 | enumerator->destroy(enumerator); |
412 | 0 | } |
413 | 0 | } |
414 | | |
415 | | /** |
416 | | * Add a Cisco Unity configuration attribute |
417 | | */ |
418 | | static void add_unity_attribute(eap_radius_provider_t *provider, uint32_t id, |
419 | | int type, chunk_t data) |
420 | 0 | { |
421 | 0 | switch (type) |
422 | 0 | { |
423 | 0 | case 15: /* CVPN3000-IPSec-Banner1 */ |
424 | 0 | case 36: /* CVPN3000-IPSec-Banner2 */ |
425 | 0 | provider->add_attribute(provider, id, UNITY_BANNER, data); |
426 | 0 | break; |
427 | 0 | case 28: /* CVPN3000-IPSec-Default-Domain */ |
428 | 0 | provider->add_attribute(provider, id, UNITY_DEF_DOMAIN, data); |
429 | 0 | break; |
430 | 0 | case 29: /* CVPN3000-IPSec-Split-DNS-Names */ |
431 | 0 | provider->add_attribute(provider, id, UNITY_SPLITDNS_NAME, data); |
432 | 0 | break; |
433 | 0 | } |
434 | 0 | } |
435 | | |
436 | | /** |
437 | | * Add a DNS/NBNS configuration attribute |
438 | | */ |
439 | | static void add_nameserver_attribute(eap_radius_provider_t *provider, |
440 | | uint32_t id, int type, chunk_t data) |
441 | 0 | { |
442 | | /* these are from different vendors, but there is currently no conflict */ |
443 | 0 | switch (type) |
444 | 0 | { |
445 | 0 | case 5: /* CVPN3000-Primary-DNS */ |
446 | 0 | case 6: /* CVPN3000-Secondary-DNS */ |
447 | 0 | case 28: /* MS-Primary-DNS-Server */ |
448 | 0 | case 29: /* MS-Secondary-DNS-Server */ |
449 | 0 | provider->add_attribute(provider, id, INTERNAL_IP4_DNS, data); |
450 | 0 | break; |
451 | 0 | case 7: /* CVPN3000-Primary-WINS */ |
452 | 0 | case 8: /* CVPN3000-Secondary-WINS */ |
453 | 0 | case 30: /* MS-Primary-NBNS-Server */ |
454 | 0 | case 31: /* MS-Secondary-NBNS-Server */ |
455 | 0 | provider->add_attribute(provider, id, INTERNAL_IP4_NBNS, data); |
456 | 0 | break; |
457 | 0 | case RAT_FRAMED_IPV6_DNS_SERVER: |
458 | 0 | provider->add_attribute(provider, id, INTERNAL_IP6_DNS, data); |
459 | 0 | break; |
460 | 0 | } |
461 | 0 | } |
462 | | |
463 | | /** |
464 | | * Add a UNITY_LOCAL_LAN or UNITY_SPLIT_INCLUDE attribute |
465 | | */ |
466 | | static void add_unity_split_attribute(eap_radius_provider_t *provider, |
467 | | uint32_t id, configuration_attribute_type_t type, |
468 | | chunk_t data) |
469 | 0 | { |
470 | 0 | enumerator_t *enumerator; |
471 | 0 | bio_writer_t *writer; |
472 | 0 | char buffer[256], *token, *slash; |
473 | |
|
474 | 0 | if (snprintf(buffer, sizeof(buffer), "%.*s", (int)data.len, |
475 | 0 | data.ptr) >= sizeof(buffer)) |
476 | 0 | { |
477 | 0 | return; |
478 | 0 | } |
479 | 0 | writer = bio_writer_create(16); /* two IPv4 addresses and 6 bytes padding */ |
480 | 0 | enumerator = enumerator_create_token(buffer, ",", " "); |
481 | 0 | while (enumerator->enumerate(enumerator, &token)) |
482 | 0 | { |
483 | 0 | host_t *net, *mask = NULL; |
484 | 0 | chunk_t padding; |
485 | |
|
486 | 0 | slash = strchr(token, '/'); |
487 | 0 | if (slash) |
488 | 0 | { |
489 | 0 | *slash++ = '\0'; |
490 | 0 | mask = host_create_from_string(slash, 0); |
491 | 0 | } |
492 | 0 | if (!mask) |
493 | 0 | { /* default to /32 */ |
494 | 0 | mask = host_create_from_string("255.255.255.255", 0); |
495 | 0 | } |
496 | 0 | net = host_create_from_string(token, 0); |
497 | 0 | if (!net || net->get_family(net) != AF_INET || |
498 | 0 | mask->get_family(mask) != AF_INET) |
499 | 0 | { |
500 | 0 | mask->destroy(mask); |
501 | 0 | DESTROY_IF(net); |
502 | 0 | continue; |
503 | 0 | } |
504 | 0 | writer->write_data(writer, net->get_address(net)); |
505 | 0 | writer->write_data(writer, mask->get_address(mask)); |
506 | 0 | padding = writer->skip(writer, 6); /* 6 bytes padding */ |
507 | 0 | memset(padding.ptr, 0, padding.len); |
508 | 0 | mask->destroy(mask); |
509 | 0 | net->destroy(net); |
510 | 0 | } |
511 | 0 | enumerator->destroy(enumerator); |
512 | |
|
513 | 0 | data = writer->get_buf(writer); |
514 | 0 | if (data.len) |
515 | 0 | { |
516 | 0 | provider->add_attribute(provider, id, type, data); |
517 | 0 | } |
518 | 0 | writer->destroy(writer); |
519 | 0 | } |
520 | | |
521 | | /** |
522 | | * Handle Framed-IP-Address and other IKE configuration attributes |
523 | | */ |
524 | | static void process_cfg_attributes(radius_message_t *msg) |
525 | 0 | { |
526 | 0 | eap_radius_provider_t *provider; |
527 | 0 | enumerator_t *enumerator; |
528 | 0 | ike_sa_t *ike_sa; |
529 | 0 | host_t *host; |
530 | 0 | chunk_t data; |
531 | 0 | configuration_attribute_type_t split_type = 0; |
532 | 0 | int type, vendor; |
533 | |
|
534 | 0 | ike_sa = charon->bus->get_sa(charon->bus); |
535 | 0 | provider = eap_radius_provider_get(); |
536 | 0 | if (provider && ike_sa) |
537 | 0 | { |
538 | 0 | enumerator = msg->create_enumerator(msg); |
539 | 0 | while (enumerator->enumerate(enumerator, &type, &data)) |
540 | 0 | { |
541 | 0 | if ((type == RAT_FRAMED_IP_ADDRESS && data.len == 4) || |
542 | 0 | (type == RAT_FRAMED_IPV6_ADDRESS && data.len == 16)) |
543 | 0 | { |
544 | 0 | host = host_create_from_chunk(AF_UNSPEC, data, 0); |
545 | 0 | if (host) |
546 | 0 | { |
547 | 0 | provider->add_framed_ip(provider, |
548 | 0 | ike_sa->get_unique_id(ike_sa), host); |
549 | 0 | } |
550 | 0 | } |
551 | 0 | else if (type == RAT_FRAMED_IP_NETMASK && data.len == 4) |
552 | 0 | { |
553 | 0 | provider->add_attribute(provider, ike_sa->get_unique_id(ike_sa), |
554 | 0 | INTERNAL_IP4_NETMASK, data); |
555 | 0 | } |
556 | 0 | else if (type == RAT_FRAMED_IPV6_DNS_SERVER && data.len == 16) |
557 | 0 | { |
558 | 0 | add_nameserver_attribute(provider, |
559 | 0 | ike_sa->get_unique_id(ike_sa), type, data); |
560 | 0 | } |
561 | 0 | } |
562 | 0 | enumerator->destroy(enumerator); |
563 | |
|
564 | 0 | enumerator = msg->create_vendor_enumerator(msg); |
565 | 0 | while (enumerator->enumerate(enumerator, &vendor, &type, &data)) |
566 | 0 | { |
567 | 0 | if (vendor == PEN_ALTIGA /* aka Cisco VPN3000 */) |
568 | 0 | { |
569 | 0 | switch (type) |
570 | 0 | { |
571 | 0 | case 5: /* CVPN3000-Primary-DNS */ |
572 | 0 | case 6: /* CVPN3000-Secondary-DNS */ |
573 | 0 | case 7: /* CVPN3000-Primary-WINS */ |
574 | 0 | case 8: /* CVPN3000-Secondary-WINS */ |
575 | 0 | if (data.len == 4) |
576 | 0 | { |
577 | 0 | add_nameserver_attribute(provider, |
578 | 0 | ike_sa->get_unique_id(ike_sa), type, data); |
579 | 0 | } |
580 | 0 | break; |
581 | 0 | case 15: /* CVPN3000-IPSec-Banner1 */ |
582 | 0 | case 28: /* CVPN3000-IPSec-Default-Domain */ |
583 | 0 | case 29: /* CVPN3000-IPSec-Split-DNS-Names */ |
584 | 0 | case 36: /* CVPN3000-IPSec-Banner2 */ |
585 | 0 | if (ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY)) |
586 | 0 | { |
587 | 0 | add_unity_attribute(provider, |
588 | 0 | ike_sa->get_unique_id(ike_sa), type, data); |
589 | 0 | } |
590 | 0 | break; |
591 | 0 | case 55: /* CVPN3000-IPSec-Split-Tunneling-Policy */ |
592 | 0 | if (data.len) |
593 | 0 | { |
594 | 0 | switch (data.ptr[data.len - 1]) |
595 | 0 | { |
596 | 0 | case 0: /* tunnelall */ |
597 | 0 | default: |
598 | 0 | break; |
599 | 0 | case 1: /* tunnelspecified */ |
600 | 0 | split_type = UNITY_SPLIT_INCLUDE; |
601 | 0 | break; |
602 | 0 | case 2: /* excludespecified */ |
603 | 0 | split_type = UNITY_LOCAL_LAN; |
604 | 0 | break; |
605 | 0 | } |
606 | 0 | } |
607 | 0 | break; |
608 | 0 | default: |
609 | 0 | break; |
610 | 0 | } |
611 | 0 | } |
612 | 0 | if (vendor == PEN_MICROSOFT) |
613 | 0 | { |
614 | 0 | switch (type) |
615 | 0 | { |
616 | 0 | case 28: /* MS-Primary-DNS-Server */ |
617 | 0 | case 29: /* MS-Secondary-DNS-Server */ |
618 | 0 | case 30: /* MS-Primary-NBNS-Server */ |
619 | 0 | case 31: /* MS-Secondary-NBNS-Server */ |
620 | 0 | if (data.len == 4) |
621 | 0 | { |
622 | 0 | add_nameserver_attribute(provider, |
623 | 0 | ike_sa->get_unique_id(ike_sa), type, data); |
624 | 0 | } |
625 | 0 | break; |
626 | 0 | } |
627 | 0 | } |
628 | 0 | } |
629 | 0 | enumerator->destroy(enumerator); |
630 | |
|
631 | 0 | if (split_type != 0 && |
632 | 0 | ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY)) |
633 | 0 | { |
634 | 0 | enumerator = msg->create_vendor_enumerator(msg); |
635 | 0 | while (enumerator->enumerate(enumerator, &vendor, &type, &data)) |
636 | 0 | { |
637 | 0 | if (vendor == PEN_ALTIGA /* aka Cisco VPN3000 */ && |
638 | 0 | type == 27 /* CVPN3000-IPSec-Split-Tunnel-List */) |
639 | 0 | { |
640 | 0 | add_unity_split_attribute(provider, |
641 | 0 | ike_sa->get_unique_id(ike_sa), split_type, data); |
642 | 0 | } |
643 | 0 | } |
644 | 0 | enumerator->destroy(enumerator); |
645 | 0 | } |
646 | 0 | } |
647 | 0 | } |
648 | | |
649 | | /** |
650 | | * See header. |
651 | | */ |
652 | | void eap_radius_process_attributes(radius_message_t *message) |
653 | 0 | { |
654 | 0 | process_class(message); |
655 | 0 | if (lib->settings->get_bool(lib->settings, |
656 | 0 | "%s.plugins.eap-radius.filter_id", FALSE, lib->ns)) |
657 | 0 | { |
658 | 0 | process_filter_id(message); |
659 | 0 | } |
660 | 0 | process_timeout(message); |
661 | 0 | process_cfg_attributes(message); |
662 | 0 | } |
663 | | |
664 | | METHOD(eap_method_t, process, status_t, |
665 | | private_eap_radius_t *this, eap_payload_t *in, eap_payload_t **out) |
666 | 0 | { |
667 | 0 | radius_message_t *request, *response; |
668 | 0 | status_t status = FAILED; |
669 | 0 | chunk_t data; |
670 | |
|
671 | 0 | request = radius_message_create(RMC_ACCESS_REQUEST); |
672 | 0 | add_radius_request_attrs(this, request); |
673 | |
|
674 | 0 | data = in->get_data(in); |
675 | 0 | DBG3(DBG_IKE, "%N payload %B", eap_type_names, this->type, &data); |
676 | | |
677 | | /* fragment data suitable for RADIUS */ |
678 | 0 | while (data.len > MAX_RADIUS_ATTRIBUTE_SIZE) |
679 | 0 | { |
680 | 0 | request->add(request, RAT_EAP_MESSAGE, |
681 | 0 | chunk_create(data.ptr,MAX_RADIUS_ATTRIBUTE_SIZE)); |
682 | 0 | data = chunk_skip(data, MAX_RADIUS_ATTRIBUTE_SIZE); |
683 | 0 | } |
684 | 0 | request->add(request, RAT_EAP_MESSAGE, data); |
685 | |
|
686 | 0 | response = this->client->request(this->client, request); |
687 | 0 | if (response) |
688 | 0 | { |
689 | 0 | eap_radius_forward_to_ike(response); |
690 | 0 | switch (response->get_code(response)) |
691 | 0 | { |
692 | 0 | case RMC_ACCESS_CHALLENGE: |
693 | 0 | if (radius2ike(this, response, out)) |
694 | 0 | { |
695 | 0 | status = NEED_MORE; |
696 | 0 | break; |
697 | 0 | } |
698 | 0 | status = FAILED; |
699 | 0 | break; |
700 | 0 | case RMC_ACCESS_ACCEPT: |
701 | 0 | eap_radius_process_attributes(response); |
702 | 0 | DBG1(DBG_IKE, "RADIUS authentication of '%Y' successful", |
703 | 0 | this->peer); |
704 | 0 | status = SUCCESS; |
705 | 0 | break; |
706 | 0 | case RMC_ACCESS_REJECT: |
707 | 0 | default: |
708 | 0 | DBG1(DBG_IKE, "RADIUS authentication of '%Y' failed", |
709 | 0 | this->peer); |
710 | 0 | status = FAILED; |
711 | 0 | break; |
712 | 0 | } |
713 | 0 | response->destroy(response); |
714 | 0 | } |
715 | 0 | request->destroy(request); |
716 | 0 | return status; |
717 | 0 | } |
718 | | |
719 | | METHOD(eap_method_t, get_type, eap_type_t, |
720 | | private_eap_radius_t *this, pen_t *vendor) |
721 | 0 | { |
722 | 0 | *vendor = this->vendor; |
723 | 0 | return this->type; |
724 | 0 | } |
725 | | |
726 | | METHOD(eap_method_t, get_msk, status_t, |
727 | | private_eap_radius_t *this, chunk_t *out) |
728 | 0 | { |
729 | 0 | chunk_t msk; |
730 | |
|
731 | 0 | msk = this->client->get_msk(this->client); |
732 | 0 | if (msk.len) |
733 | 0 | { |
734 | 0 | *out = msk; |
735 | 0 | return SUCCESS; |
736 | 0 | } |
737 | | /* we assume the selected method did not establish an MSK, if it failed |
738 | | * to establish one, process() would have failed */ |
739 | 0 | return NOT_SUPPORTED; |
740 | 0 | } |
741 | | |
742 | | METHOD(eap_method_t, get_identifier, uint8_t, |
743 | | private_eap_radius_t *this) |
744 | 0 | { |
745 | 0 | return this->identifier; |
746 | 0 | } |
747 | | |
748 | | METHOD(eap_method_t, set_identifier, void, |
749 | | private_eap_radius_t *this, uint8_t identifier) |
750 | 0 | { |
751 | 0 | this->identifier = identifier; |
752 | 0 | } |
753 | | |
754 | | METHOD(eap_method_t, is_mutual, bool, |
755 | | private_eap_radius_t *this) |
756 | 0 | { |
757 | 0 | switch (this->type) |
758 | 0 | { |
759 | 0 | case EAP_AKA: |
760 | 0 | case EAP_SIM: |
761 | 0 | return TRUE; |
762 | 0 | default: |
763 | 0 | return FALSE; |
764 | 0 | } |
765 | 0 | } |
766 | | |
767 | | METHOD(eap_method_t, destroy, void, |
768 | | private_eap_radius_t *this) |
769 | 0 | { |
770 | 0 | this->peer->destroy(this->peer); |
771 | 0 | this->server->destroy(this->server); |
772 | 0 | this->client->destroy(this->client); |
773 | 0 | free(this); |
774 | 0 | } |
775 | | |
776 | | /** |
777 | | * Generic constructor |
778 | | */ |
779 | | eap_radius_t *eap_radius_create(identification_t *server, identification_t *peer) |
780 | 0 | { |
781 | 0 | private_eap_radius_t *this; |
782 | |
|
783 | 0 | INIT(this, |
784 | 0 | .public = { |
785 | 0 | .eap_method = { |
786 | 0 | .initiate = _initiate, |
787 | 0 | .process = _process, |
788 | 0 | .get_type = _get_type, |
789 | 0 | .is_mutual = _is_mutual, |
790 | 0 | .get_msk = _get_msk, |
791 | 0 | .get_identifier = _get_identifier, |
792 | 0 | .set_identifier = _set_identifier, |
793 | 0 | .destroy = _destroy, |
794 | 0 | }, |
795 | 0 | }, |
796 | | /* initially EAP_RADIUS, but is set to the method selected by RADIUS */ |
797 | 0 | .type = EAP_RADIUS, |
798 | 0 | .eap_start = lib->settings->get_bool(lib->settings, |
799 | 0 | "%s.plugins.eap-radius.eap_start", FALSE, |
800 | 0 | lib->ns), |
801 | 0 | .id_prefix = lib->settings->get_str(lib->settings, |
802 | 0 | "%s.plugins.eap-radius.id_prefix", "", |
803 | 0 | lib->ns), |
804 | 0 | ); |
805 | 0 | this->client = eap_radius_create_client(); |
806 | 0 | if (!this->client) |
807 | 0 | { |
808 | 0 | free(this); |
809 | 0 | return NULL; |
810 | 0 | } |
811 | 0 | this->peer = peer->clone(peer); |
812 | 0 | this->server = server->clone(server); |
813 | 0 | return &this->public; |
814 | 0 | } |