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"
21 #include <radius_message.h>
22 #include <radius_client.h>
26 typedef struct private_eap_radius_t private_eap_radius_t
;
29 * Private data of an eap_radius_t object.
31 struct private_eap_radius_t
{
34 * Public authenticator_t interface.
41 identification_t
*server
;
46 identification_t
*peer
;
49 * EAP method type we are proxying
59 * EAP message identifier
64 * RADIUS client instance
66 radius_client_t
*client
;
69 * TRUE to use EAP-Start, FALSE to send EAP-Identity Response directly
74 * Prefix to prepend to EAP identity
79 * Handle the Class attribute as group membership information?
84 * Handle the Filter-Id attribute as IPsec CHILD_SA name?
90 * Add EAP-Identity to RADIUS message
92 static void add_eap_identity(private_eap_radius_t
*this,
93 radius_message_t
*request
)
96 /** EAP code (REQUEST/RESPONSE) */
98 /** unique message identifier */
100 /** length of whole message */
106 } __attribute__((__packed__
)) *hdr
;
110 id
= this->peer
->get_encoding(this->peer
);
111 prefix
= chunk_create(this->id_prefix
, strlen(this->id_prefix
));
112 len
= sizeof(*hdr
) + prefix
.len
+ id
.len
;
115 hdr
->code
= EAP_RESPONSE
;
116 hdr
->identifier
= this->identifier
;
117 hdr
->length
= htons(len
);
118 hdr
->type
= EAP_IDENTITY
;
119 memcpy(hdr
->data
, prefix
.ptr
, prefix
.len
);
120 memcpy(hdr
->data
+ prefix
.len
, id
.ptr
, id
.len
);
122 request
->add(request
, RAT_EAP_MESSAGE
, chunk_create((u_char
*)hdr
, len
));
126 * Copy EAP-Message attribute from RADIUS message to an new EAP payload
128 static bool radius2ike(private_eap_radius_t
*this,
129 radius_message_t
*msg
, eap_payload_t
**out
)
131 enumerator_t
*enumerator
;
132 eap_payload_t
*payload
;
133 chunk_t data
, message
= chunk_empty
;
136 enumerator
= msg
->create_enumerator(msg
);
137 while (enumerator
->enumerate(enumerator
, &type
, &data
))
139 if (type
== RAT_EAP_MESSAGE
&& data
.len
)
141 message
= chunk_cat("mc", message
, data
);
144 enumerator
->destroy(enumerator
);
147 *out
= payload
= eap_payload_create_data(message
);
149 /* apply EAP method selected by RADIUS server */
150 this->type
= payload
->get_type(payload
, &this->vendor
);
152 DBG3(DBG_IKE
, "%N payload %B", eap_type_names
, this->type
, &message
);
159 METHOD(eap_method_t
, initiate
, status_t
,
160 private_eap_radius_t
*this, eap_payload_t
**out
)
162 radius_message_t
*request
, *response
;
163 status_t status
= FAILED
;
166 request
= radius_message_create(RMC_ACCESS_REQUEST
);
167 username
= chunk_create(this->id_prefix
, strlen(this->id_prefix
));
168 username
= chunk_cata("cc", username
, this->peer
->get_encoding(this->peer
));
169 request
->add(request
, RAT_USER_NAME
, username
);
173 request
->add(request
, RAT_EAP_MESSAGE
, chunk_empty
);
177 add_eap_identity(this, request
);
179 eap_radius_forward_from_ike(request
);
181 response
= this->client
->request(this->client
, request
);
184 eap_radius_forward_to_ike(response
);
185 switch (response
->get_code(response
))
187 case RMC_ACCESS_CHALLENGE
:
188 if (radius2ike(this, response
, out
))
193 case RMC_ACCESS_ACCEPT
:
194 /* Microsoft RADIUS servers can run in a mode where they respond
195 * like this on the first request (i.e. without authentication),
196 * we treat this as Access-Reject */
197 case RMC_ACCESS_REJECT
:
199 DBG1(DBG_IKE
, "RADIUS authentication of '%Y' failed",
203 response
->destroy(response
);
207 charon
->bus
->alert(charon
->bus
, ALERT_RADIUS_NOT_RESPONDING
);
209 request
->destroy(request
);
214 * Handle the Class attribute as group membership information
216 static void process_class(private_eap_radius_t
*this, radius_message_t
*msg
)
218 enumerator_t
*enumerator
;
222 enumerator
= msg
->create_enumerator(msg
);
223 while (enumerator
->enumerate(enumerator
, &type
, &data
))
225 if (type
== RAT_CLASS
)
227 identification_t
*id
;
232 { /* quirk: ignore long class attributes, these are used for
233 * other purposes by some RADIUS servers (such as NPS). */
237 ike_sa
= charon
->bus
->get_sa(charon
->bus
);
240 auth
= ike_sa
->get_auth_cfg(ike_sa
, FALSE
);
241 id
= identification_create_from_data(data
);
242 DBG1(DBG_CFG
, "received group membership '%Y' from RADIUS", id
);
243 auth
->add(auth
, AUTH_RULE_GROUP
, id
);
247 enumerator
->destroy(enumerator
);
251 * Handle the Filter-Id attribute as IPsec CHILD_SA name
253 static void process_filter_id(private_eap_radius_t
*this, radius_message_t
*msg
)
255 enumerator_t
*enumerator
;
258 u_int32_t tunnel_type
;
259 chunk_t filter_id
= chunk_empty
, data
;
260 bool is_esp_tunnel
= FALSE
;
262 enumerator
= msg
->create_enumerator(msg
);
263 while (enumerator
->enumerate(enumerator
, &type
, &data
))
267 case RAT_TUNNEL_TYPE
:
272 tunnel_tag
= *data
.ptr
;
274 tunnel_type
= untoh32(data
.ptr
);
275 DBG1(DBG_IKE
, "received RADIUS attribute Tunnel-Type: "
276 "tag = %u, value = %u", tunnel_tag
, tunnel_type
);
277 is_esp_tunnel
= (tunnel_type
== RADIUS_TUNNEL_TYPE_ESP
);
281 DBG1(DBG_IKE
, "received RADIUS attribute Filter-Id: "
282 "'%.*s'", (int)filter_id
.len
, filter_id
.ptr
);
288 enumerator
->destroy(enumerator
);
290 if (is_esp_tunnel
&& filter_id
.len
)
292 identification_t
*id
;
296 ike_sa
= charon
->bus
->get_sa(charon
->bus
);
299 auth
= ike_sa
->get_auth_cfg(ike_sa
, FALSE
);
300 id
= identification_create_from_data(filter_id
);
301 auth
->add(auth
, AUTH_RULE_GROUP
, id
);
307 * Handle Session-Timeout attribte
309 static void process_timeout(private_eap_radius_t
*this, radius_message_t
*msg
)
311 enumerator_t
*enumerator
;
316 enumerator
= msg
->create_enumerator(msg
);
317 while (enumerator
->enumerate(enumerator
, &type
, &data
))
319 if (type
== RAT_SESSION_TIMEOUT
&& data
.len
== 4)
321 ike_sa
= charon
->bus
->get_sa(charon
->bus
);
324 ike_sa
->set_auth_lifetime(ike_sa
, untoh32(data
.ptr
));
328 enumerator
->destroy(enumerator
);
332 * Handle Framed-IP-Address and other IKE configuration attributes
334 static void process_cfg_attributes(private_eap_radius_t
*this,
335 radius_message_t
*msg
)
337 eap_radius_provider_t
*provider
;
338 enumerator_t
*enumerator
;
343 provider
= eap_radius_provider_get();
346 enumerator
= msg
->create_enumerator(msg
);
347 while (enumerator
->enumerate(enumerator
, &type
, &data
))
349 if (type
== RAT_FRAMED_IP_ADDRESS
&& data
.len
== 4)
351 host
= host_create_from_chunk(AF_INET
, data
, 0);
354 provider
->add_framed_ip(provider
, this->peer
, host
);
358 enumerator
->destroy(enumerator
);
362 METHOD(eap_method_t
, process
, status_t
,
363 private_eap_radius_t
*this, eap_payload_t
*in
, eap_payload_t
**out
)
365 radius_message_t
*request
, *response
;
366 status_t status
= FAILED
;
369 request
= radius_message_create(RMC_ACCESS_REQUEST
);
370 request
->add(request
, RAT_USER_NAME
, this->peer
->get_encoding(this->peer
));
371 data
= in
->get_data(in
);
372 DBG3(DBG_IKE
, "%N payload %B", eap_type_names
, this->type
, &data
);
374 /* fragment data suitable for RADIUS */
375 while (data
.len
> MAX_RADIUS_ATTRIBUTE_SIZE
)
377 request
->add(request
, RAT_EAP_MESSAGE
,
378 chunk_create(data
.ptr
,MAX_RADIUS_ATTRIBUTE_SIZE
));
379 data
= chunk_skip(data
, MAX_RADIUS_ATTRIBUTE_SIZE
);
381 request
->add(request
, RAT_EAP_MESSAGE
, data
);
383 eap_radius_forward_from_ike(request
);
384 response
= this->client
->request(this->client
, request
);
387 eap_radius_forward_to_ike(response
);
388 switch (response
->get_code(response
))
390 case RMC_ACCESS_CHALLENGE
:
391 if (radius2ike(this, response
, out
))
398 case RMC_ACCESS_ACCEPT
:
399 if (this->class_group
)
401 process_class(this, response
);
405 process_filter_id(this, response
);
407 process_timeout(this, response
);
408 process_cfg_attributes(this, response
);
409 DBG1(DBG_IKE
, "RADIUS authentication of '%Y' successful",
413 case RMC_ACCESS_REJECT
:
415 DBG1(DBG_IKE
, "RADIUS authentication of '%Y' failed",
420 response
->destroy(response
);
422 request
->destroy(request
);
426 METHOD(eap_method_t
, get_type
, eap_type_t
,
427 private_eap_radius_t
*this, u_int32_t
*vendor
)
429 *vendor
= this->vendor
;
433 METHOD(eap_method_t
, get_msk
, status_t
,
434 private_eap_radius_t
*this, chunk_t
*out
)
438 msk
= this->client
->get_msk(this->client
);
447 METHOD(eap_method_t
, get_identifier
, u_int8_t
,
448 private_eap_radius_t
*this)
450 return this->identifier
;
453 METHOD(eap_method_t
, set_identifier
, void,
454 private_eap_radius_t
*this, u_int8_t identifier
)
456 this->identifier
= identifier
;
459 METHOD(eap_method_t
, is_mutual
, bool,
460 private_eap_radius_t
*this)
472 METHOD(eap_method_t
, destroy
, void,
473 private_eap_radius_t
*this)
475 this->peer
->destroy(this->peer
);
476 this->server
->destroy(this->server
);
477 this->client
->destroy(this->client
);
482 * Generic constructor
484 eap_radius_t
*eap_radius_create(identification_t
*server
, identification_t
*peer
)
486 private_eap_radius_t
*this;
491 .initiate
= _initiate
,
493 .get_type
= _get_type
,
494 .is_mutual
= _is_mutual
,
496 .get_identifier
= _get_identifier
,
497 .set_identifier
= _set_identifier
,
501 /* initially EAP_RADIUS, but is set to the method selected by RADIUS */
503 .eap_start
= lib
->settings
->get_bool(lib
->settings
,
504 "%s.plugins.eap-radius.eap_start", FALSE
,
506 .id_prefix
= lib
->settings
->get_str(lib
->settings
,
507 "%s.plugins.eap-radius.id_prefix", "",
509 .class_group
= lib
->settings
->get_bool(lib
->settings
,
510 "%s.plugins.eap-radius.class_group", FALSE
,
512 .filter_id
= lib
->settings
->get_bool(lib
->settings
,
513 "%s.plugins.eap-radius.filter_id", FALSE
,
516 this->client
= eap_radius_create_client();
522 this->peer
= peer
->clone(peer
);
523 this->server
= server
->clone(server
);
524 return &this->public;