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