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