2 * Copyright (C) 2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
16 #include "eap_radius.h"
17 #include "eap_radius_plugin.h"
18 #include "eap_radius_forward.h"
19 #include "eap_radius_provider.h"
20 #include "eap_radius_accounting.h"
22 #include <radius_message.h>
23 #include <radius_client.h>
27 typedef struct private_eap_radius_t private_eap_radius_t
;
30 * Private data of an eap_radius_t object.
32 struct private_eap_radius_t
{
35 * Public authenticator_t interface.
42 identification_t
*server
;
47 identification_t
*peer
;
50 * EAP method type we are proxying
60 * EAP message identifier
65 * RADIUS client instance
67 radius_client_t
*client
;
70 * TRUE to use EAP-Start, FALSE to send EAP-Identity Response directly
75 * Prefix to prepend to EAP identity
80 * Handle the Class attribute as group membership information?
85 * Handle the Filter-Id attribute as IPsec CHILD_SA name?
90 * Format string we use for Called/Calling-Station-Id for a host
96 * Add EAP-Identity to RADIUS message
98 static void add_eap_identity(private_eap_radius_t
*this,
99 radius_message_t
*request
)
102 /** EAP code (REQUEST/RESPONSE) */
104 /** unique message identifier */
106 /** length of whole message */
112 } __attribute__((__packed__
)) *hdr
;
116 id
= this->peer
->get_encoding(this->peer
);
117 prefix
= chunk_create(this->id_prefix
, strlen(this->id_prefix
));
118 len
= sizeof(*hdr
) + prefix
.len
+ id
.len
;
121 hdr
->code
= EAP_RESPONSE
;
122 hdr
->identifier
= this->identifier
;
123 hdr
->length
= htons(len
);
124 hdr
->type
= EAP_IDENTITY
;
125 memcpy(hdr
->data
, prefix
.ptr
, prefix
.len
);
126 memcpy(hdr
->data
+ prefix
.len
, id
.ptr
, id
.len
);
128 request
->add(request
, RAT_EAP_MESSAGE
, chunk_create((u_char
*)hdr
, len
));
132 * Copy EAP-Message attribute from RADIUS message to an new EAP payload
134 static bool radius2ike(private_eap_radius_t
*this,
135 radius_message_t
*msg
, eap_payload_t
**out
)
137 enumerator_t
*enumerator
;
138 eap_payload_t
*payload
;
139 chunk_t data
, message
= chunk_empty
;
142 enumerator
= msg
->create_enumerator(msg
);
143 while (enumerator
->enumerate(enumerator
, &type
, &data
))
145 if (type
== RAT_EAP_MESSAGE
&& data
.len
)
147 message
= chunk_cat("mc", message
, data
);
150 enumerator
->destroy(enumerator
);
153 *out
= payload
= eap_payload_create_data(message
);
155 /* apply EAP method selected by RADIUS server */
156 this->type
= payload
->get_type(payload
, &this->vendor
);
158 DBG3(DBG_IKE
, "%N payload %B", eap_type_names
, this->type
, &message
);
166 * Add a set of RADIUS attributes to a request message
168 static void add_radius_request_attrs(private_eap_radius_t
*this,
169 radius_message_t
*request
)
177 chunk
= chunk_from_str(this->id_prefix
);
178 chunk
= chunk_cata("cc", chunk
, this->peer
->get_encoding(this->peer
));
179 request
->add(request
, RAT_USER_NAME
, chunk
);
181 /* virtual NAS-Port-Type */
183 request
->add(request
, RAT_NAS_PORT_TYPE
, chunk_from_thing(value
));
184 /* framed ServiceType */
186 request
->add(request
, RAT_SERVICE_TYPE
, chunk_from_thing(value
));
188 ike_sa
= charon
->bus
->get_sa(charon
->bus
);
191 value
= htonl(ike_sa
->get_unique_id(ike_sa
));
192 request
->add(request
, RAT_NAS_PORT
, chunk_from_thing(value
));
193 request
->add(request
, RAT_NAS_PORT_ID
,
194 chunk_from_str(ike_sa
->get_name(ike_sa
)));
196 host
= ike_sa
->get_my_host(ike_sa
);
197 chunk
= host
->get_address(host
);
198 switch (host
->get_family(host
))
201 request
->add(request
, RAT_NAS_IP_ADDRESS
, chunk
);
204 request
->add(request
, RAT_NAS_IPV6_ADDRESS
, chunk
);
208 snprintf(buf
, sizeof(buf
), this->station_id_fmt
, host
);
209 request
->add(request
, RAT_CALLED_STATION_ID
, chunk_from_str(buf
));
210 host
= ike_sa
->get_other_host(ike_sa
);
211 snprintf(buf
, sizeof(buf
), this->station_id_fmt
, host
);
212 request
->add(request
, RAT_CALLING_STATION_ID
, chunk_from_str(buf
));
215 eap_radius_forward_from_ike(request
);
218 METHOD(eap_method_t
, initiate
, status_t
,
219 private_eap_radius_t
*this, eap_payload_t
**out
)
221 radius_message_t
*request
, *response
;
222 status_t status
= FAILED
;
224 request
= radius_message_create(RMC_ACCESS_REQUEST
);
225 add_radius_request_attrs(this, request
);
229 request
->add(request
, RAT_EAP_MESSAGE
, chunk_empty
);
233 add_eap_identity(this, request
);
236 response
= this->client
->request(this->client
, request
);
239 eap_radius_forward_to_ike(response
);
240 switch (response
->get_code(response
))
242 case RMC_ACCESS_CHALLENGE
:
243 if (radius2ike(this, response
, out
))
248 case RMC_ACCESS_ACCEPT
:
249 /* Microsoft RADIUS servers can run in a mode where they respond
250 * like this on the first request (i.e. without authentication),
251 * we treat this as Access-Reject */
252 case RMC_ACCESS_REJECT
:
254 DBG1(DBG_IKE
, "RADIUS authentication of '%Y' failed",
258 response
->destroy(response
);
262 eap_radius_handle_timeout(NULL
);
264 request
->destroy(request
);
269 * Handle the Class attribute as group membership information
271 static void process_class(private_eap_radius_t
*this, radius_message_t
*msg
)
273 enumerator_t
*enumerator
;
277 enumerator
= msg
->create_enumerator(msg
);
278 while (enumerator
->enumerate(enumerator
, &type
, &data
))
280 if (type
== RAT_CLASS
)
282 identification_t
*id
;
287 { /* quirk: ignore long class attributes, these are used for
288 * other purposes by some RADIUS servers (such as NPS). */
292 ike_sa
= charon
->bus
->get_sa(charon
->bus
);
295 auth
= ike_sa
->get_auth_cfg(ike_sa
, FALSE
);
296 id
= identification_create_from_data(data
);
297 DBG1(DBG_CFG
, "received group membership '%Y' from RADIUS", id
);
298 auth
->add(auth
, AUTH_RULE_GROUP
, id
);
302 enumerator
->destroy(enumerator
);
306 * Handle the Filter-Id attribute as IPsec CHILD_SA name
308 static void process_filter_id(private_eap_radius_t
*this, radius_message_t
*msg
)
310 enumerator_t
*enumerator
;
313 u_int32_t tunnel_type
;
314 chunk_t filter_id
= chunk_empty
, data
;
315 bool is_esp_tunnel
= FALSE
;
317 enumerator
= msg
->create_enumerator(msg
);
318 while (enumerator
->enumerate(enumerator
, &type
, &data
))
322 case RAT_TUNNEL_TYPE
:
327 tunnel_tag
= *data
.ptr
;
329 tunnel_type
= untoh32(data
.ptr
);
330 DBG1(DBG_IKE
, "received RADIUS attribute Tunnel-Type: "
331 "tag = %u, value = %u", tunnel_tag
, tunnel_type
);
332 is_esp_tunnel
= (tunnel_type
== RADIUS_TUNNEL_TYPE_ESP
);
336 DBG1(DBG_IKE
, "received RADIUS attribute Filter-Id: "
337 "'%.*s'", (int)filter_id
.len
, filter_id
.ptr
);
343 enumerator
->destroy(enumerator
);
345 if (is_esp_tunnel
&& filter_id
.len
)
347 identification_t
*id
;
351 ike_sa
= charon
->bus
->get_sa(charon
->bus
);
354 auth
= ike_sa
->get_auth_cfg(ike_sa
, FALSE
);
355 id
= identification_create_from_data(filter_id
);
356 auth
->add(auth
, AUTH_RULE_GROUP
, id
);
362 * Handle Session-Timeout attribte and Interim updates
364 static void process_timeout(private_eap_radius_t
*this, radius_message_t
*msg
)
366 enumerator_t
*enumerator
;
371 ike_sa
= charon
->bus
->get_sa(charon
->bus
);
374 enumerator
= msg
->create_enumerator(msg
);
375 while (enumerator
->enumerate(enumerator
, &type
, &data
))
377 if (type
== RAT_SESSION_TIMEOUT
&& data
.len
== 4)
379 ike_sa
->set_auth_lifetime(ike_sa
, untoh32(data
.ptr
));
381 else if (type
== RAT_ACCT_INTERIM_INTERVAL
&& data
.len
== 4)
383 eap_radius_accounting_start_interim(ike_sa
, untoh32(data
.ptr
));
386 enumerator
->destroy(enumerator
);
391 * Handle Framed-IP-Address and other IKE configuration attributes
393 static void process_cfg_attributes(private_eap_radius_t
*this,
394 radius_message_t
*msg
)
396 eap_radius_provider_t
*provider
;
397 enumerator_t
*enumerator
;
403 ike_sa
= charon
->bus
->get_sa(charon
->bus
);
404 provider
= eap_radius_provider_get();
405 if (provider
&& ike_sa
)
407 enumerator
= msg
->create_enumerator(msg
);
408 while (enumerator
->enumerate(enumerator
, &type
, &data
))
410 if (type
== RAT_FRAMED_IP_ADDRESS
&& data
.len
== 4)
412 host
= host_create_from_chunk(AF_INET
, data
, 0);
415 provider
->add_framed_ip(provider
, this->peer
, host
);
419 enumerator
->destroy(enumerator
);
421 enumerator
= msg
->create_vendor_enumerator(msg
);
422 while (enumerator
->enumerate(enumerator
, &vendor
, &type
, &data
))
424 if (vendor
== PEN_ALTIGA
/* aka Cisco VPN3000 */)
428 case 15: /* CVPN3000-IPSec-Banner1 */
429 case 36: /* CVPN3000-IPSec-Banner2 */
430 if (ike_sa
->supports_extension(ike_sa
, EXT_CISCO_UNITY
))
432 provider
->add_attribute(provider
, this->peer
,
441 enumerator
->destroy(enumerator
);
445 METHOD(eap_method_t
, process
, status_t
,
446 private_eap_radius_t
*this, eap_payload_t
*in
, eap_payload_t
**out
)
448 radius_message_t
*request
, *response
;
449 status_t status
= FAILED
;
452 request
= radius_message_create(RMC_ACCESS_REQUEST
);
453 add_radius_request_attrs(this, request
);
455 data
= in
->get_data(in
);
456 DBG3(DBG_IKE
, "%N payload %B", eap_type_names
, this->type
, &data
);
458 /* fragment data suitable for RADIUS */
459 while (data
.len
> MAX_RADIUS_ATTRIBUTE_SIZE
)
461 request
->add(request
, RAT_EAP_MESSAGE
,
462 chunk_create(data
.ptr
,MAX_RADIUS_ATTRIBUTE_SIZE
));
463 data
= chunk_skip(data
, MAX_RADIUS_ATTRIBUTE_SIZE
);
465 request
->add(request
, RAT_EAP_MESSAGE
, data
);
467 response
= this->client
->request(this->client
, request
);
470 eap_radius_forward_to_ike(response
);
471 switch (response
->get_code(response
))
473 case RMC_ACCESS_CHALLENGE
:
474 if (radius2ike(this, response
, out
))
481 case RMC_ACCESS_ACCEPT
:
482 if (this->class_group
)
484 process_class(this, response
);
488 process_filter_id(this, response
);
490 process_timeout(this, response
);
491 process_cfg_attributes(this, response
);
492 DBG1(DBG_IKE
, "RADIUS authentication of '%Y' successful",
496 case RMC_ACCESS_REJECT
:
498 DBG1(DBG_IKE
, "RADIUS authentication of '%Y' failed",
503 response
->destroy(response
);
505 request
->destroy(request
);
509 METHOD(eap_method_t
, get_type
, eap_type_t
,
510 private_eap_radius_t
*this, u_int32_t
*vendor
)
512 *vendor
= this->vendor
;
516 METHOD(eap_method_t
, get_msk
, status_t
,
517 private_eap_radius_t
*this, chunk_t
*out
)
521 msk
= this->client
->get_msk(this->client
);
530 METHOD(eap_method_t
, get_identifier
, u_int8_t
,
531 private_eap_radius_t
*this)
533 return this->identifier
;
536 METHOD(eap_method_t
, set_identifier
, void,
537 private_eap_radius_t
*this, u_int8_t identifier
)
539 this->identifier
= identifier
;
542 METHOD(eap_method_t
, is_mutual
, bool,
543 private_eap_radius_t
*this)
555 METHOD(eap_method_t
, destroy
, void,
556 private_eap_radius_t
*this)
558 this->peer
->destroy(this->peer
);
559 this->server
->destroy(this->server
);
560 this->client
->destroy(this->client
);
565 * Generic constructor
567 eap_radius_t
*eap_radius_create(identification_t
*server
, identification_t
*peer
)
569 private_eap_radius_t
*this;
574 .initiate
= _initiate
,
576 .get_type
= _get_type
,
577 .is_mutual
= _is_mutual
,
579 .get_identifier
= _get_identifier
,
580 .set_identifier
= _set_identifier
,
584 /* initially EAP_RADIUS, but is set to the method selected by RADIUS */
586 .eap_start
= lib
->settings
->get_bool(lib
->settings
,
587 "%s.plugins.eap-radius.eap_start", FALSE
,
589 .id_prefix
= lib
->settings
->get_str(lib
->settings
,
590 "%s.plugins.eap-radius.id_prefix", "",
592 .class_group
= lib
->settings
->get_bool(lib
->settings
,
593 "%s.plugins.eap-radius.class_group", FALSE
,
595 .filter_id
= lib
->settings
->get_bool(lib
->settings
,
596 "%s.plugins.eap-radius.filter_id", FALSE
,
599 if (lib
->settings
->get_bool(lib
->settings
,
600 "%s.plugins.eap-radius.station_id_with_port", TRUE
, charon
->name
))
602 this->station_id_fmt
= "%#H";
606 this->station_id_fmt
= "%H";
608 this->client
= eap_radius_create_client();
614 this->peer
= peer
->clone(peer
);
615 this->server
= server
->clone(server
);
616 return &this->public;