2 * Copyright (C) 2013 Martin Willi
3 * Copyright (C) 2013 revosec AG
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_xauth.h"
17 #include "eap_radius_plugin.h"
18 #include "eap_radius.h"
19 #include "eap_radius_forward.h"
22 #include <radius_client.h>
23 #include <collections/array.h>
26 typedef struct private_eap_radius_xauth_t private_eap_radius_xauth_t
;
27 typedef struct xauth_round_t xauth_round_t
;
30 * Configuration for an XAuth authentication exchange
32 struct xauth_round_t
{
33 /** XAuth message type to send */
34 configuration_attribute_type_t type
;
35 /** Message to present to user */
40 * Private data of an eap_radius_xauth_t object.
42 struct private_eap_radius_xauth_t
{
47 eap_radius_xauth_t
public;
52 identification_t
*server
;
57 identification_t
*peer
;
62 radius_client_t
*client
;
65 * XAuth authentication rounds, as xauth_round_t
70 * XAuth round currently in progress
75 * Concatentated password of all rounds
81 * Fetch next XAuth round, add attributes to CP payload
83 static bool build_round(private_eap_radius_xauth_t
*this, cp_payload_t
*cp
)
85 if (!array_remove(this->rounds
, ARRAY_HEAD
, &this->round
))
89 cp
->add_attribute(cp
, configuration_attribute_create_chunk(
90 CONFIGURATION_ATTRIBUTE_V1
, this->round
.type
, chunk_empty
));
92 if (this->round
.message
&& strlen(this->round
.message
))
94 cp
->add_attribute(cp
, configuration_attribute_create_chunk(
95 CONFIGURATION_ATTRIBUTE_V1
, XAUTH_MESSAGE
,
96 chunk_from_str(this->round
.message
)));
101 METHOD(xauth_method_t
, initiate
, status_t
,
102 private_eap_radius_xauth_t
*this, cp_payload_t
**out
)
106 cp
= cp_payload_create_type(CONFIGURATION_V1
, CFG_REQUEST
);
107 /* first message always comes with username */
108 cp
->add_attribute(cp
, configuration_attribute_create_chunk(
109 CONFIGURATION_ATTRIBUTE_V1
, XAUTH_USER_NAME
, chunk_empty
));
111 if (build_round(this, cp
))
121 * Verify a password using RADIUS User-Name/User-Password attributes
123 static status_t
verify_radius(private_eap_radius_xauth_t
*this)
125 radius_message_t
*request
, *response
;
126 status_t status
= FAILED
;
128 request
= radius_message_create(RMC_ACCESS_REQUEST
);
129 request
->add(request
, RAT_USER_NAME
, this->peer
->get_encoding(this->peer
));
130 request
->add(request
, RAT_USER_PASSWORD
, this->pass
);
132 eap_radius_build_attributes(request
);
133 eap_radius_forward_from_ike(request
);
135 response
= this->client
->request(this->client
, request
);
138 eap_radius_forward_to_ike(response
);
139 switch (response
->get_code(response
))
141 case RMC_ACCESS_ACCEPT
:
142 eap_radius_process_attributes(response
);
145 case RMC_ACCESS_CHALLENGE
:
146 DBG1(DBG_IKE
, "RADIUS Access-Challenge not supported");
148 case RMC_ACCESS_REJECT
:
150 DBG1(DBG_IKE
, "RADIUS authentication of '%Y' failed",
154 response
->destroy(response
);
158 eap_radius_handle_timeout(NULL
);
160 request
->destroy(request
);
164 METHOD(xauth_method_t
, process
, status_t
,
165 private_eap_radius_xauth_t
*this, cp_payload_t
*in
, cp_payload_t
**out
)
167 configuration_attribute_t
*attr
;
168 enumerator_t
*enumerator
;
169 identification_t
*id
;
171 chunk_t user
= chunk_empty
, pass
= chunk_empty
;
173 enumerator
= in
->create_attribute_enumerator(in
);
174 while (enumerator
->enumerate(enumerator
, &attr
))
176 if (attr
->get_type(attr
) == XAUTH_USER_NAME
)
178 user
= attr
->get_chunk(attr
);
180 else if (attr
->get_type(attr
) == this->round
.type
)
182 pass
= attr
->get_chunk(attr
);
183 /* trim password to any null termination. As User-Password
184 * uses null padding, we can't have any null in it, and some
185 * clients actually send null terminated strings (Android). */
186 pass
.len
= strnlen(pass
.ptr
, pass
.len
);
189 enumerator
->destroy(enumerator
);
193 DBG1(DBG_IKE
, "peer did not respond to our XAuth %N request",
194 configuration_attribute_type_names
, this->round
.type
);
197 this->pass
= chunk_cat("mc", this->pass
, pass
);
200 id
= identification_create_from_data(user
);
203 DBG1(DBG_IKE
, "failed to parse provided XAuth username");
206 this->peer
->destroy(this->peer
);
210 if (array_count(this->rounds
) == 0)
212 return verify_radius(this);
214 cp
= cp_payload_create_type(CONFIGURATION_V1
, CFG_REQUEST
);
215 if (build_round(this, cp
))
224 METHOD(xauth_method_t
, get_identity
, identification_t
*,
225 private_eap_radius_xauth_t
*this)
231 * Parse XAuth round configuration
233 static bool parse_rounds(private_eap_radius_xauth_t
*this, char *profile
)
237 configuration_attribute_type_t type
;
239 { "password", XAUTH_USER_PASSWORD
, },
240 { "passcode", XAUTH_PASSCODE
, },
241 { "nextpin", XAUTH_NEXT_PIN
, },
242 { "answer", XAUTH_ANSWER
, },
244 enumerator_t
*enumerator
;
245 char *type
, *message
;
249 if (!profile
|| strlen(profile
) == 0)
251 /* no config, fallback to password */
252 round
.type
= XAUTH_USER_PASSWORD
;
253 round
.message
= NULL
;
254 array_insert(this->rounds
, ARRAY_TAIL
, &round
);
258 enumerator
= lib
->settings
->create_key_value_enumerator(lib
->settings
,
259 "%s.plugins.eap-radius.xauth.%s", lib
->ns
, profile
);
260 while (enumerator
->enumerate(enumerator
, &type
, &message
))
264 for (i
= 0; i
< countof(map
); i
++)
266 if (strcaseeq(map
[i
].str
, type
))
268 round
.type
= map
[i
].type
;
269 round
.message
= message
;
270 array_insert(this->rounds
, ARRAY_TAIL
, &round
);
277 DBG1(DBG_CFG
, "invalid XAuth round type: '%s'", type
);
278 enumerator
->destroy(enumerator
);
282 enumerator
->destroy(enumerator
);
284 if (array_count(this->rounds
) == 0)
286 DBG1(DBG_CFG
, "XAuth configuration profile '%s' invalid", profile
);
292 METHOD(xauth_method_t
, destroy
, void,
293 private_eap_radius_xauth_t
*this)
295 DESTROY_IF(this->client
);
296 chunk_clear(&this->pass
);
297 array_destroy(this->rounds
);
298 this->server
->destroy(this->server
);
299 this->peer
->destroy(this->peer
);
304 * Described in header.
306 eap_radius_xauth_t
*eap_radius_xauth_create_server(identification_t
*server
,
307 identification_t
*peer
,
310 private_eap_radius_xauth_t
*this;
315 .initiate
= _initiate
,
317 .get_identity
= _get_identity
,
321 .server
= server
->clone(server
),
322 .peer
= peer
->clone(peer
),
323 .client
= eap_radius_create_client(),
324 .rounds
= array_create(sizeof(xauth_round_t
), 0),
327 if (!parse_rounds(this, profile
))
337 return &this->public;