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