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_forward.h"
19 #include "radius_message.h"
20 #include "radius_client.h"
24 #define TUNNEL_TYPE_ESP 9
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 if (radius2ike(this, response
, out
))
189 response
->destroy(response
);
193 charon
->bus
->alert(charon
->bus
, ALERT_RADIUS_NOT_RESPONDING
);
195 request
->destroy(request
);
200 * Handle the Class attribute as group membership information
202 static void process_class(private_eap_radius_t
*this, radius_message_t
*msg
)
204 enumerator_t
*enumerator
;
208 enumerator
= msg
->create_enumerator(msg
);
209 while (enumerator
->enumerate(enumerator
, &type
, &data
))
211 if (type
== RAT_CLASS
)
213 identification_t
*id
;
218 { /* quirk: ignore long class attributes, these are used for
219 * other purposes by some RADIUS servers (such as NPS). */
223 ike_sa
= charon
->bus
->get_sa(charon
->bus
);
226 auth
= ike_sa
->get_auth_cfg(ike_sa
, FALSE
);
227 id
= identification_create_from_data(data
);
228 DBG1(DBG_CFG
, "received group membership '%Y' from RADIUS", id
);
229 auth
->add(auth
, AUTH_RULE_GROUP
, id
);
233 enumerator
->destroy(enumerator
);
237 * Handle the Filter-Id attribute as IPsec CHILD_SA name
239 static void process_filter_id(private_eap_radius_t
*this, radius_message_t
*msg
)
241 enumerator_t
*enumerator
;
244 u_int32_t tunnel_type
;
245 chunk_t filter_id
= chunk_empty
, data
;
246 bool is_esp_tunnel
= FALSE
;
248 enumerator
= msg
->create_enumerator(msg
);
249 while (enumerator
->enumerate(enumerator
, &type
, &data
))
253 case RAT_TUNNEL_TYPE
:
258 tunnel_tag
= *data
.ptr
;
260 tunnel_type
= untoh32(data
.ptr
);
261 DBG1(DBG_IKE
, "received RADIUS attribute Tunnel-Type: "
262 "tag = %u, value = %u", tunnel_tag
, tunnel_type
);
263 is_esp_tunnel
= (tunnel_type
== TUNNEL_TYPE_ESP
);
267 DBG1(DBG_IKE
, "received RADIUS attribute Filter-Id: "
268 "'%.*s'", filter_id
.len
, filter_id
.ptr
);
274 enumerator
->destroy(enumerator
);
276 if (is_esp_tunnel
&& filter_id
.len
)
278 identification_t
*id
;
282 ike_sa
= charon
->bus
->get_sa(charon
->bus
);
285 auth
= ike_sa
->get_auth_cfg(ike_sa
, FALSE
);
286 id
= identification_create_from_data(filter_id
);
287 auth
->add(auth
, AUTH_RULE_GROUP
, id
);
293 * Handle Session-Timeout attribte
295 static void process_timeout(private_eap_radius_t
*this, radius_message_t
*msg
)
297 enumerator_t
*enumerator
;
302 enumerator
= msg
->create_enumerator(msg
);
303 while (enumerator
->enumerate(enumerator
, &type
, &data
))
305 if (type
== RAT_SESSION_TIMEOUT
&& data
.len
== 4)
307 ike_sa
= charon
->bus
->get_sa(charon
->bus
);
310 ike_sa
->set_auth_lifetime(ike_sa
, untoh32(data
.ptr
));
314 enumerator
->destroy(enumerator
);
317 METHOD(eap_method_t
, process
, status_t
,
318 private_eap_radius_t
*this, eap_payload_t
*in
, eap_payload_t
**out
)
320 radius_message_t
*request
, *response
;
321 status_t status
= FAILED
;
324 request
= radius_message_create(RMC_ACCESS_REQUEST
);
325 request
->add(request
, RAT_USER_NAME
, this->peer
->get_encoding(this->peer
));
326 data
= in
->get_data(in
);
327 DBG3(DBG_IKE
, "%N payload %B", eap_type_names
, this->type
, &data
);
329 /* fragment data suitable for RADIUS (not more than 253 bytes) */
330 while (data
.len
> 253)
332 request
->add(request
, RAT_EAP_MESSAGE
, chunk_create(data
.ptr
, 253));
333 data
= chunk_skip(data
, 253);
335 request
->add(request
, RAT_EAP_MESSAGE
, data
);
337 eap_radius_forward_from_ike(request
);
338 response
= this->client
->request(this->client
, request
);
341 eap_radius_forward_to_ike(response
);
342 switch (response
->get_code(response
))
344 case RMC_ACCESS_CHALLENGE
:
345 if (radius2ike(this, response
, out
))
352 case RMC_ACCESS_ACCEPT
:
353 if (this->class_group
)
355 process_class(this, response
);
359 process_filter_id(this, response
);
361 process_timeout(this, response
);
362 DBG1(DBG_IKE
, "RADIUS authentication of '%Y' successful",
366 case RMC_ACCESS_REJECT
:
368 DBG1(DBG_IKE
, "RADIUS authentication of '%Y' failed", this->peer
);
372 response
->destroy(response
);
374 request
->destroy(request
);
378 METHOD(eap_method_t
, get_type
, eap_type_t
,
379 private_eap_radius_t
*this, u_int32_t
*vendor
)
381 *vendor
= this->vendor
;
385 METHOD(eap_method_t
, get_msk
, status_t
,
386 private_eap_radius_t
*this, chunk_t
*out
)
390 msk
= this->client
->get_msk(this->client
);
399 METHOD(eap_method_t
, get_identifier
, u_int8_t
,
400 private_eap_radius_t
*this)
402 return this->identifier
;
405 METHOD(eap_method_t
, set_identifier
, void,
406 private_eap_radius_t
*this, u_int8_t identifier
)
408 this->identifier
= identifier
;
411 METHOD(eap_method_t
, is_mutual
, bool,
412 private_eap_radius_t
*this)
424 METHOD(eap_method_t
, destroy
, void,
425 private_eap_radius_t
*this)
427 this->peer
->destroy(this->peer
);
428 this->server
->destroy(this->server
);
429 this->client
->destroy(this->client
);
434 * Generic constructor
436 eap_radius_t
*eap_radius_create(identification_t
*server
, identification_t
*peer
)
438 private_eap_radius_t
*this;
443 .initiate
= _initiate
,
445 .get_type
= _get_type
,
446 .is_mutual
= _is_mutual
,
448 .get_identifier
= _get_identifier
,
449 .set_identifier
= _set_identifier
,
453 /* initially EAP_RADIUS, but is set to the method selected by RADIUS */
455 .eap_start
= lib
->settings
->get_bool(lib
->settings
,
456 "charon.plugins.eap-radius.eap_start", FALSE
),
457 .id_prefix
= lib
->settings
->get_str(lib
->settings
,
458 "charon.plugins.eap-radius.id_prefix", ""),
459 .class_group
= lib
->settings
->get_bool(lib
->settings
,
460 "charon.plugins.eap-radius.class_group", FALSE
),
461 .filter_id
= lib
->settings
->get_bool(lib
->settings
,
462 "charon.plugins.eap-radius.filter_id", FALSE
),
465 this->client
= radius_client_create();
471 this->peer
= peer
->clone(peer
);
472 this->server
= server
->clone(server
);
473 return &this->public;