Moving charon to libcharon.
[strongswan.git] / src / libcharon / plugins / eap_radius / eap_radius.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
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>.
9 *
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
13 * for more details.
14 */
15
16 #include "eap_radius.h"
17
18 #include "radius_message.h"
19 #include "radius_client.h"
20
21 #include <daemon.h>
22
23 typedef struct private_eap_radius_t private_eap_radius_t;
24
25 /**
26 * Private data of an eap_radius_t object.
27 */
28 struct private_eap_radius_t {
29
30 /**
31 * Public authenticator_t interface.
32 */
33 eap_radius_t public;
34
35 /**
36 * ID of the server
37 */
38 identification_t *server;
39
40 /**
41 * ID of the peer
42 */
43 identification_t *peer;
44
45 /**
46 * EAP method type we are proxying
47 */
48 eap_type_t type;
49
50 /**
51 * EAP vendor, if any
52 */
53 u_int32_t vendor;
54
55 /**
56 * EAP MSK, if method established one
57 */
58 chunk_t msk;
59
60 /**
61 * RADIUS client instance
62 */
63 radius_client_t *client;
64
65 /**
66 * TRUE to use EAP-Start, FALSE to send EAP-Identity Response directly
67 */
68 bool eap_start;
69
70 /**
71 * Prefix to prepend to EAP identity
72 */
73 char *id_prefix;
74 };
75
76 /**
77 * Add EAP-Identity to RADIUS message
78 */
79 static void add_eap_identity(private_eap_radius_t *this,
80 radius_message_t *request)
81 {
82 struct {
83 /** EAP code (REQUEST/RESPONSE) */
84 u_int8_t code;
85 /** unique message identifier */
86 u_int8_t identifier;
87 /** length of whole message */
88 u_int16_t length;
89 /** EAP type */
90 u_int8_t type;
91 /** identity data */
92 u_int8_t data[];
93 } __attribute__((__packed__)) *hdr;
94 chunk_t id, prefix;
95 size_t len;
96
97 id = this->peer->get_encoding(this->peer);
98 prefix = chunk_create(this->id_prefix, strlen(this->id_prefix));
99 len = sizeof(*hdr) + prefix.len + id.len;
100
101 hdr = alloca(len);
102 hdr->code = EAP_RESPONSE;
103 hdr->identifier = 0;
104 hdr->length = htons(len);
105 hdr->type = EAP_IDENTITY;
106 memcpy(hdr->data, prefix.ptr, prefix.len);
107 memcpy(hdr->data + prefix.len, id.ptr, id.len);
108
109 request->add(request, RAT_EAP_MESSAGE, chunk_create((u_char*)hdr, len));
110 }
111
112 /**
113 * Copy EAP-Message attribute from RADIUS message to an new EAP payload
114 */
115 static bool radius2ike(private_eap_radius_t *this,
116 radius_message_t *msg, eap_payload_t **out)
117 {
118 enumerator_t *enumerator;
119 eap_payload_t *payload;
120 chunk_t data, message = chunk_empty;
121 int type;
122
123 enumerator = msg->create_enumerator(msg);
124 while (enumerator->enumerate(enumerator, &type, &data))
125 {
126 if (type == RAT_EAP_MESSAGE && data.len)
127 {
128 message = chunk_cat("mc", message, data);
129 }
130 }
131 enumerator->destroy(enumerator);
132 if (message.len)
133 {
134 *out = payload = eap_payload_create_data(message);
135 free(message.ptr);
136 /* apply EAP method selected by RADIUS server */
137 this->type = payload->get_type(payload, &this->vendor);
138 return TRUE;
139 }
140 return FALSE;
141 }
142
143 /**
144 * Implementation of eap_method_t.initiate
145 */
146 static status_t initiate(private_eap_radius_t *this, eap_payload_t **out)
147 {
148 radius_message_t *request, *response;
149 status_t status = FAILED;
150 chunk_t username;
151
152 request = radius_message_create_request();
153 username = chunk_create(this->id_prefix, strlen(this->id_prefix));
154 username = chunk_cata("cc", username, this->peer->get_encoding(this->peer));
155 request->add(request, RAT_USER_NAME, username);
156
157 if (this->eap_start)
158 {
159 request->add(request, RAT_EAP_MESSAGE, chunk_empty);
160 }
161 else
162 {
163 add_eap_identity(this, request);
164 }
165
166 response = this->client->request(this->client, request);
167 if (response)
168 {
169 if (radius2ike(this, response, out))
170 {
171 status = NEED_MORE;
172 }
173 response->destroy(response);
174 }
175 request->destroy(request);
176 return status;
177 }
178
179 /**
180 * Implementation of eap_method_t.process
181 */
182 static status_t process(private_eap_radius_t *this,
183 eap_payload_t *in, eap_payload_t **out)
184 {
185 radius_message_t *request, *response;
186 status_t status = FAILED;
187 chunk_t data;
188
189 request = radius_message_create_request();
190 request->add(request, RAT_USER_NAME, this->peer->get_encoding(this->peer));
191 data = in->get_data(in);
192 /* fragment data suitable for RADIUS (not more than 253 bytes) */
193 while (data.len > 253)
194 {
195 request->add(request, RAT_EAP_MESSAGE, chunk_create(data.ptr, 253));
196 data = chunk_skip(data, 253);
197 }
198 request->add(request, RAT_EAP_MESSAGE, data);
199
200 response = this->client->request(this->client, request);
201 if (response)
202 {
203 switch (response->get_code(response))
204 {
205 case RMC_ACCESS_CHALLENGE:
206 if (radius2ike(this, response, out))
207 {
208 status = NEED_MORE;
209 break;
210 }
211 status = FAILED;
212 break;
213 case RMC_ACCESS_ACCEPT:
214 this->msk = this->client->decrypt_msk(this->client,
215 response, request);
216 status = SUCCESS;
217 break;
218 case RMC_ACCESS_REJECT:
219 default:
220 DBG1(DBG_CFG, "received %N from RADIUS server",
221 radius_message_code_names, response->get_code(response));
222 status = FAILED;
223 break;
224 }
225 response->destroy(response);
226 }
227 request->destroy(request);
228 return status;
229 }
230
231 /**
232 * Implementation of eap_method_t.get_type.
233 */
234 static eap_type_t get_type(private_eap_radius_t *this, u_int32_t *vendor)
235 {
236 *vendor = this->vendor;
237 return this->type;
238 }
239
240 /**
241 * Implementation of eap_method_t.get_msk.
242 */
243 static status_t get_msk(private_eap_radius_t *this, chunk_t *msk)
244 {
245 if (this->msk.ptr)
246 {
247 *msk = this->msk;
248 return SUCCESS;
249 }
250 return FAILED;
251 }
252
253 /**
254 * Implementation of eap_method_t.is_mutual.
255 */
256 static bool is_mutual(private_eap_radius_t *this)
257 {
258 switch (this->type)
259 {
260 case EAP_AKA:
261 case EAP_SIM:
262 return TRUE;
263 default:
264 return FALSE;
265 }
266 }
267
268 /**
269 * Implementation of eap_method_t.destroy.
270 */
271 static void destroy(private_eap_radius_t *this)
272 {
273 this->peer->destroy(this->peer);
274 this->server->destroy(this->server);
275 this->client->destroy(this->client);
276 chunk_clear(&this->msk);
277 free(this);
278 }
279
280 /**
281 * Generic constructor
282 */
283 eap_radius_t *eap_radius_create(identification_t *server, identification_t *peer)
284 {
285 private_eap_radius_t *this = malloc_thing(private_eap_radius_t);
286
287 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
288 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
289 this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
290 this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
291 this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
292 this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
293
294 this->client = radius_client_create();
295 if (!this->client)
296 {
297 free(this);
298 return NULL;
299 }
300 this->peer = peer->clone(peer);
301 this->server = server->clone(server);
302 /* initially EAP_RADIUS, but is set to the method selected by RADIUS */
303 this->type = EAP_RADIUS;
304 this->vendor = 0;
305 this->msk = chunk_empty;
306 this->eap_start = lib->settings->get_bool(lib->settings,
307 "charon.plugins.eap-radius.eap_start", FALSE);
308 this->id_prefix = lib->settings->get_str(lib->settings,
309 "charon.plugins.eap-radius.id_prefix", "");
310 return &this->public;
311 }
312