added a RADIUS id_prefix option to prefix the IMSI
[strongswan.git] / src / charon / 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;
121 int type;
122
123 enumerator = msg->create_enumerator(msg);
124 while (enumerator->enumerate(enumerator, &type, &data))
125 {
126 if (type == RAT_EAP_MESSAGE)
127 {
128 *out = payload = eap_payload_create_data(data);
129 /* apply EAP method selected by RADIUS server */
130 this->type = payload->get_type(payload, &this->vendor);
131 enumerator->destroy(enumerator);
132 return TRUE;
133 }
134 }
135 enumerator->destroy(enumerator);
136 return FALSE;
137 }
138
139 /**
140 * Implementation of eap_method_t.initiate
141 */
142 static status_t initiate(private_eap_radius_t *this, eap_payload_t **out)
143 {
144 radius_message_t *request, *response;
145 status_t status = FAILED;
146 chunk_t username;
147
148 request = radius_message_create_request();
149 username = chunk_create(this->id_prefix, strlen(this->id_prefix));
150 username = chunk_cata("cc", username, this->peer->get_encoding(this->peer));
151 request->add(request, RAT_USER_NAME, username);
152
153 if (this->eap_start)
154 {
155 request->add(request, RAT_EAP_MESSAGE, chunk_empty);
156 }
157 else
158 {
159 add_eap_identity(this, request);
160 }
161
162 response = this->client->request(this->client, request);
163 if (response)
164 {
165 if (radius2ike(this, response, out))
166 {
167 status = NEED_MORE;
168 }
169 response->destroy(response);
170 }
171 request->destroy(request);
172 return status;
173 }
174
175 /**
176 * Implementation of eap_method_t.process
177 */
178 static status_t process(private_eap_radius_t *this,
179 eap_payload_t *in, eap_payload_t **out)
180 {
181 radius_message_t *request, *response;
182 status_t status = FAILED;
183
184 request = radius_message_create_request();
185 request->add(request, RAT_USER_NAME, this->peer->get_encoding(this->peer));
186 request->add(request, RAT_EAP_MESSAGE, in->get_data(in));
187
188 response = this->client->request(this->client, request);
189 if (response)
190 {
191 switch (response->get_code(response))
192 {
193 case RMC_ACCESS_CHALLENGE:
194 if (radius2ike(this, response, out))
195 {
196 status = NEED_MORE;
197 break;
198 }
199 status = FAILED;
200 break;
201 case RMC_ACCESS_ACCEPT:
202 this->msk = this->client->decrypt_msk(this->client,
203 response, request);
204 status = SUCCESS;
205 break;
206 case RMC_ACCESS_REJECT:
207 default:
208 DBG1(DBG_CFG, "received %N from RADIUS server",
209 radius_message_code_names, response->get_code(response));
210 status = FAILED;
211 break;
212 }
213 response->destroy(response);
214 }
215 request->destroy(request);
216 return status;
217 }
218
219 /**
220 * Implementation of eap_method_t.get_type.
221 */
222 static eap_type_t get_type(private_eap_radius_t *this, u_int32_t *vendor)
223 {
224 *vendor = this->vendor;
225 return this->type;
226 }
227
228 /**
229 * Implementation of eap_method_t.get_msk.
230 */
231 static status_t get_msk(private_eap_radius_t *this, chunk_t *msk)
232 {
233 if (this->msk.ptr)
234 {
235 *msk = this->msk;
236 return SUCCESS;
237 }
238 return FAILED;
239 }
240
241 /**
242 * Implementation of eap_method_t.is_mutual.
243 */
244 static bool is_mutual(private_eap_radius_t *this)
245 {
246 switch (this->type)
247 {
248 case EAP_AKA:
249 case EAP_SIM:
250 return TRUE;
251 default:
252 return FALSE;
253 }
254 }
255
256 /**
257 * Implementation of eap_method_t.destroy.
258 */
259 static void destroy(private_eap_radius_t *this)
260 {
261 this->peer->destroy(this->peer);
262 this->server->destroy(this->server);
263 this->client->destroy(this->client);
264 chunk_clear(&this->msk);
265 free(this);
266 }
267
268 /**
269 * Generic constructor
270 */
271 eap_radius_t *eap_radius_create(identification_t *server, identification_t *peer)
272 {
273 private_eap_radius_t *this = malloc_thing(private_eap_radius_t);
274
275 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
276 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
277 this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
278 this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
279 this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
280 this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
281
282 this->client = radius_client_create();
283 if (!this->client)
284 {
285 free(this);
286 return NULL;
287 }
288 this->peer = peer->clone(peer);
289 this->server = server->clone(server);
290 /* initially EAP_RADIUS, but is set to the method selected by RADIUS */
291 this->type = EAP_RADIUS;
292 this->vendor = 0;
293 this->msk = chunk_empty;
294 this->eap_start = lib->settings->get_bool(lib->settings,
295 "charon.plugins.eap_radius.eap_start", FALSE);
296 this->id_prefix = lib->settings->get_str(lib->settings,
297 "charon.plugins.eap_radius.id_prefix", "");
298 return &this->public;
299 }
300