464de17ba57184b7664f0d7d8f5313643eba87c2
[strongswan.git] / src / libcharon / plugins / eap_ttls / eap_ttls_server.c
1 /*
2 * Copyright (C) 2010 Andreas Steffen
3 * Copyright (C) 2010 HSR 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_ttls_server.h"
17 #include "eap_ttls_avp.h"
18
19 #include <utils/debug.h>
20 #include <daemon.h>
21
22 #include <sa/eap/eap_method.h>
23
24 typedef struct private_eap_ttls_server_t private_eap_ttls_server_t;
25
26 /**
27 * Private data of an eap_ttls_server_t object.
28 */
29 struct private_eap_ttls_server_t {
30
31 /**
32 * Public eap_ttls_server_t interface.
33 */
34 eap_ttls_server_t public;
35
36 /**
37 * Server identity
38 */
39 identification_t *server;
40
41 /**
42 * Peer identity
43 */
44 identification_t *peer;
45
46 /**
47 * Current EAP-TTLS phase2 state
48 */
49 bool start_phase2;
50
51 /**
52 * Current EAP-TTLS phase2 TNC state
53 */
54 bool start_phase2_tnc;
55
56 /**
57 * Current phase 2 EAP method
58 */
59 eap_method_t *method;
60
61 /**
62 * Pending outbound EAP message
63 */
64 eap_payload_t *out;
65
66 /**
67 * AVP handler
68 */
69 eap_ttls_avp_t *avp;
70 };
71
72 /**
73 * Start EAP client authentication protocol
74 */
75 static status_t start_phase2_auth(private_eap_ttls_server_t *this)
76 {
77 char *eap_type_str;
78 eap_type_t type;
79
80 eap_type_str = lib->settings->get_str(lib->settings,
81 "%s.plugins.eap-ttls.phase2_method", "md5",
82 charon->name);
83 type = eap_type_from_string(eap_type_str);
84 if (type == 0)
85 {
86 DBG1(DBG_IKE, "unrecognized phase2 method \"%s\"", eap_type_str);
87 return FAILED;
88 }
89 DBG1(DBG_IKE, "phase2 method %N selected", eap_type_names, type);
90 this->method = charon->eap->create_instance(charon->eap, type, 0,
91 EAP_SERVER, this->server, this->peer);
92 if (this->method == NULL)
93 {
94 DBG1(DBG_IKE, "%N method not available", eap_type_names, type);
95 return FAILED;
96 }
97 if (this->method->initiate(this->method, &this->out) == NEED_MORE)
98 {
99 return NEED_MORE;
100 }
101 else
102 {
103 DBG1(DBG_IKE, "%N method failed", eap_type_names, type);
104 return FAILED;
105 }
106 }
107
108 /**
109 * If configured, start EAP-TNC protocol
110 */
111 static status_t start_phase2_tnc(private_eap_ttls_server_t *this)
112 {
113 if (this->start_phase2_tnc && lib->settings->get_bool(lib->settings,
114 "%s.plugins.eap-ttls.phase2_tnc", FALSE, charon->name))
115 {
116 DBG1(DBG_IKE, "phase2 method %N selected", eap_type_names, EAP_TNC);
117 this->method = charon->eap->create_instance(charon->eap, EAP_TNC,
118 0, EAP_SERVER, this->server, this->peer);
119 if (this->method == NULL)
120 {
121 DBG1(DBG_IKE, "%N method not available", eap_type_names, EAP_TNC);
122 return FAILED;
123 }
124 this->start_phase2_tnc = FALSE;
125 if (this->method->initiate(this->method, &this->out) == NEED_MORE)
126 {
127 return NEED_MORE;
128 }
129 else
130 {
131 DBG1(DBG_IKE, "%N method failed", eap_type_names, EAP_TNC);
132 return FAILED;
133 }
134 }
135 return SUCCESS;
136 }
137
138 METHOD(tls_application_t, process, status_t,
139 private_eap_ttls_server_t *this, bio_reader_t *reader)
140 {
141 chunk_t data = chunk_empty;
142 status_t status;
143 payload_t *payload;
144 eap_payload_t *in;
145 eap_code_t code;
146 eap_type_t type = EAP_NAK, received_type;
147 u_int32_t vendor, received_vendor;
148
149 status = this->avp->process(this->avp, reader, &data);
150 switch (status)
151 {
152 case SUCCESS:
153 break;
154 case NEED_MORE:
155 return NEED_MORE;
156 case FAILED:
157 default:
158 return FAILED;
159 }
160 in = eap_payload_create_data(data);
161 chunk_free(&data);
162 payload = (payload_t*)in;
163
164 if (payload->verify(payload) != SUCCESS)
165 {
166 in->destroy(in);
167 return FAILED;
168 }
169 code = in->get_code(in);
170 received_type = in->get_type(in, &received_vendor);
171 DBG1(DBG_IKE, "received tunneled EAP-TTLS AVP [EAP/%N/%N]",
172 eap_code_short_names, code,
173 eap_type_short_names, received_type);
174 if (code != EAP_RESPONSE)
175 {
176 DBG1(DBG_IKE, "%N expected", eap_code_names, EAP_RESPONSE);
177 in->destroy(in);
178 return FAILED;
179 }
180
181 if (this->method)
182 {
183 type = this->method->get_type(this->method, &vendor);
184
185 if (type != received_type || vendor != received_vendor)
186 {
187 if (received_vendor == 0 && received_type == EAP_NAK)
188 {
189 DBG1(DBG_IKE, "peer does not support %N", eap_type_names, type);
190 }
191 else
192 {
193 DBG1(DBG_IKE, "received invalid EAP response");
194 }
195 in->destroy(in);
196 return FAILED;
197 }
198 }
199
200 if (!received_vendor && received_type == EAP_IDENTITY)
201 {
202 chunk_t eap_id;
203
204 if (this->method == NULL)
205 {
206 /* Received an EAP Identity response without a matching request */
207 this->method = charon->eap->create_instance(charon->eap,
208 EAP_IDENTITY, 0, EAP_SERVER,
209 this->server, this->peer);
210 if (this->method == NULL)
211 {
212 DBG1(DBG_IKE, "%N method not available",
213 eap_type_names, EAP_IDENTITY);
214 return FAILED;
215 }
216 }
217
218 if (this->method->process(this->method, in, &this->out) != SUCCESS)
219 {
220
221 DBG1(DBG_IKE, "%N method failed", eap_type_names, EAP_IDENTITY);
222 return FAILED;
223 }
224
225 if (this->method->get_msk(this->method, &eap_id) == SUCCESS)
226 {
227 this->peer->destroy(this->peer);
228 this->peer = identification_create_from_data(eap_id);
229 DBG1(DBG_IKE, "received EAP identity '%Y'", this->peer);
230 }
231
232 in->destroy(in);
233 this->method->destroy(this->method);
234 this->method = NULL;
235
236 /* Start Phase 2 of EAP-TTLS authentication */
237 if (lib->settings->get_bool(lib->settings,
238 "%s.plugins.eap-ttls.request_peer_auth", FALSE, charon->name))
239 {
240 return start_phase2_tnc(this);
241 }
242 else
243 {
244 return start_phase2_auth(this);
245 }
246 }
247
248 if (this->method == 0)
249 {
250 DBG1(DBG_IKE, "no %N phase2 method installed", eap_type_names, EAP_TTLS);
251 in->destroy(in);
252 return FAILED;
253 }
254
255 status = this->method->process(this->method, in, &this->out);
256 in->destroy(in);
257
258 switch (status)
259 {
260 case SUCCESS:
261 DBG1(DBG_IKE, "%N phase2 authentication of '%Y' with %N successful",
262 eap_type_names, EAP_TTLS, this->peer,
263 eap_type_names, type);
264 this->method->destroy(this->method);
265 this->method = NULL;
266
267 /* continue phase2 with EAP-TNC? */
268 return start_phase2_tnc(this);
269 case NEED_MORE:
270 break;
271 case FAILED:
272 default:
273 if (vendor)
274 {
275 DBG1(DBG_IKE, "vendor specific EAP method %d-%d failed",
276 type, vendor);
277 }
278 else
279 {
280 DBG1(DBG_IKE, "%N method failed", eap_type_names, type);
281 }
282 return FAILED;
283 }
284 return status;
285 }
286
287 METHOD(tls_application_t, build, status_t,
288 private_eap_ttls_server_t *this, bio_writer_t *writer)
289 {
290 chunk_t data;
291 eap_code_t code;
292 eap_type_t type;
293 u_int32_t vendor;
294
295 if (this->method == NULL && this->start_phase2 &&
296 lib->settings->get_bool(lib->settings,
297 "%s.plugins.eap-ttls.phase2_piggyback", FALSE, charon->name))
298 {
299 /* generate an EAP Identity request which will be piggybacked right
300 * onto the TLS Finished message thus initiating EAP-TTLS phase2
301 */
302 this->method = charon->eap->create_instance(charon->eap, EAP_IDENTITY,
303 0, EAP_SERVER, this->server, this->peer);
304 if (this->method == NULL)
305 {
306 DBG1(DBG_IKE, "%N method not available",
307 eap_type_names, EAP_IDENTITY);
308 return FAILED;
309 }
310 this->method->initiate(this->method, &this->out);
311 this->start_phase2 = FALSE;
312 }
313
314 if (this->out)
315 {
316 code = this->out->get_code(this->out);
317 type = this->out->get_type(this->out, &vendor);
318 DBG1(DBG_IKE, "sending tunneled EAP-TTLS AVP [EAP/%N/%N]",
319 eap_code_short_names, code, eap_type_short_names, type);
320
321 /* get the raw EAP message data */
322 data = this->out->get_data(this->out);
323 this->avp->build(this->avp, writer, data);
324
325 this->out->destroy(this->out);
326 this->out = NULL;
327 }
328 return INVALID_STATE;
329 }
330
331 METHOD(tls_application_t, destroy, void,
332 private_eap_ttls_server_t *this)
333 {
334 this->server->destroy(this->server);
335 this->peer->destroy(this->peer);
336 DESTROY_IF(this->method);
337 DESTROY_IF(this->out);
338 this->avp->destroy(this->avp);
339 free(this);
340 }
341
342 /**
343 * See header
344 */
345 eap_ttls_server_t *eap_ttls_server_create(identification_t *server,
346 identification_t *peer)
347 {
348 private_eap_ttls_server_t *this;
349
350 INIT(this,
351 .public = {
352 .application = {
353 .process = _process,
354 .build = _build,
355 .destroy = _destroy,
356 },
357 },
358 .server = server->clone(server),
359 .peer = peer->clone(peer),
360 .start_phase2 = TRUE,
361 .start_phase2_tnc = TRUE,
362 .avp = eap_ttls_avp_create(),
363 );
364
365 return &this->public;
366 }