]>
Commit | Line | Data |
---|---|---|
79f2102c | 1 | /* |
8d590903 | 2 | * Copyright (C) 2010-2014 Andreas Steffen |
19ef2aec TB |
3 | * |
4 | * Copyright (C) secunet Security Networks AG | |
79f2102c AS |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or (at your | |
9 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | * for more details. | |
15 | */ | |
16 | ||
17 | #include "eap_ttls_server.h" | |
18 | #include "eap_ttls_avp.h" | |
19 | ||
f05b4272 | 20 | #include <utils/debug.h> |
79f2102c AS |
21 | #include <daemon.h> |
22 | ||
326a9423 | 23 | #include <sa/eap/eap_method.h> |
2a421163 | 24 | #include <sa/eap/eap_inner_method.h> |
79f2102c AS |
25 | |
26 | typedef struct private_eap_ttls_server_t private_eap_ttls_server_t; | |
27 | ||
28 | /** | |
29 | * Private data of an eap_ttls_server_t object. | |
30 | */ | |
31 | struct private_eap_ttls_server_t { | |
32 | ||
33 | /** | |
34 | * Public eap_ttls_server_t interface. | |
35 | */ | |
36 | eap_ttls_server_t public; | |
37 | ||
38 | /** | |
39 | * Server identity | |
40 | */ | |
41 | identification_t *server; | |
42 | ||
43 | /** | |
44 | * Peer identity | |
45 | */ | |
46 | identification_t *peer; | |
47 | ||
48 | /** | |
88845558 | 49 | * Current EAP-TTLS phase2 state |
79f2102c AS |
50 | */ |
51 | bool start_phase2; | |
52 | ||
88845558 AS |
53 | /** |
54 | * Current EAP-TTLS phase2 TNC state | |
55 | */ | |
56 | bool start_phase2_tnc; | |
57 | ||
79f2102c | 58 | /** |
5b0bcfb1 | 59 | * Current phase 2 EAP method |
79f2102c AS |
60 | */ |
61 | eap_method_t *method; | |
62 | ||
63 | /** | |
5b0bcfb1 | 64 | * Pending outbound EAP message |
79f2102c AS |
65 | */ |
66 | eap_payload_t *out; | |
67 | ||
68 | /** | |
69 | * AVP handler | |
70 | */ | |
71 | eap_ttls_avp_t *avp; | |
72 | }; | |
73 | ||
260d0561 AS |
74 | /** |
75 | * Start EAP client authentication protocol | |
76 | */ | |
77 | static status_t start_phase2_auth(private_eap_ttls_server_t *this) | |
78 | { | |
79 | char *eap_type_str; | |
80 | eap_type_t type; | |
81 | ||
5b0bcfb1 | 82 | eap_type_str = lib->settings->get_str(lib->settings, |
42500c27 | 83 | "%s.plugins.eap-ttls.phase2_method", "md5", |
d223fe80 | 84 | lib->ns); |
260d0561 AS |
85 | type = eap_type_from_string(eap_type_str); |
86 | if (type == 0) | |
87 | { | |
88 | DBG1(DBG_IKE, "unrecognized phase2 method \"%s\"", eap_type_str); | |
89 | return FAILED; | |
90 | } | |
91 | DBG1(DBG_IKE, "phase2 method %N selected", eap_type_names, type); | |
92 | this->method = charon->eap->create_instance(charon->eap, type, 0, | |
93 | EAP_SERVER, this->server, this->peer); | |
94 | if (this->method == NULL) | |
95 | { | |
96 | DBG1(DBG_IKE, "%N method not available", eap_type_names, type); | |
97 | return FAILED; | |
98 | } | |
99 | if (this->method->initiate(this->method, &this->out) == NEED_MORE) | |
100 | { | |
101 | return NEED_MORE; | |
102 | } | |
103 | else | |
104 | { | |
105 | DBG1(DBG_IKE, "%N method failed", eap_type_names, type); | |
106 | return FAILED; | |
107 | } | |
108 | } | |
109 | ||
110 | /** | |
8d590903 | 111 | * If configured, start PT-EAP or legacy EAP-TNC protocol |
260d0561 | 112 | */ |
2a421163 AS |
113 | static status_t start_phase2_tnc(private_eap_ttls_server_t *this, |
114 | eap_type_t auth_type) | |
260d0561 | 115 | { |
2a421163 | 116 | eap_inner_method_t *inner_method; |
8d590903 AS |
117 | eap_type_t type; |
118 | char *eap_type_str; | |
2a421163 | 119 | |
260d0561 | 120 | if (this->start_phase2_tnc && lib->settings->get_bool(lib->settings, |
d223fe80 | 121 | "%s.plugins.eap-ttls.phase2_tnc", FALSE, lib->ns)) |
260d0561 | 122 | { |
8d590903 AS |
123 | eap_type_str = lib->settings->get_str(lib->settings, |
124 | "%s.plugins.eap-ttls.phase2_tnc_method", "pt", | |
125 | lib->ns); | |
126 | type = eap_type_from_string(eap_type_str); | |
127 | if (type == 0) | |
128 | { | |
129 | DBG1(DBG_IKE, "unrecognized phase2 EAP TNC method \"%s\"", | |
130 | eap_type_str); | |
131 | return FAILED; | |
132 | } | |
133 | DBG1(DBG_IKE, "phase2 method %N selected", eap_type_names, type); | |
134 | this->method = charon->eap->create_instance(charon->eap, type, | |
260d0561 AS |
135 | 0, EAP_SERVER, this->server, this->peer); |
136 | if (this->method == NULL) | |
137 | { | |
8d590903 | 138 | DBG1(DBG_IKE, "%N method not available", eap_type_names, type); |
260d0561 AS |
139 | return FAILED; |
140 | } | |
2a421163 AS |
141 | inner_method = (eap_inner_method_t *)this->method; |
142 | inner_method->set_auth_type(inner_method, auth_type); | |
143 | ||
260d0561 AS |
144 | this->start_phase2_tnc = FALSE; |
145 | if (this->method->initiate(this->method, &this->out) == NEED_MORE) | |
146 | { | |
147 | return NEED_MORE; | |
148 | } | |
149 | else | |
150 | { | |
8d590903 | 151 | DBG1(DBG_IKE, "%N method failed", eap_type_names, type); |
260d0561 AS |
152 | return FAILED; |
153 | } | |
154 | } | |
155 | return SUCCESS; | |
156 | } | |
157 | ||
79f2102c | 158 | METHOD(tls_application_t, process, status_t, |
7e432eff | 159 | private_eap_ttls_server_t *this, bio_reader_t *reader) |
79f2102c | 160 | { |
79a5e391 | 161 | chunk_t data = chunk_empty; |
79f2102c AS |
162 | status_t status; |
163 | payload_t *payload; | |
164 | eap_payload_t *in; | |
165 | eap_code_t code; | |
6291fbed | 166 | eap_type_t type = EAP_NAK, received_type; |
8d590903 | 167 | uint32_t vendor, received_vendor; |
79f2102c AS |
168 | |
169 | status = this->avp->process(this->avp, reader, &data); | |
79a5e391 | 170 | switch (status) |
79f2102c | 171 | { |
79a5e391 AS |
172 | case SUCCESS: |
173 | break; | |
174 | case NEED_MORE: | |
175 | return NEED_MORE; | |
176 | case FAILED: | |
177 | default: | |
178 | return FAILED; | |
79f2102c AS |
179 | } |
180 | in = eap_payload_create_data(data); | |
79a5e391 | 181 | chunk_free(&data); |
79f2102c AS |
182 | payload = (payload_t*)in; |
183 | ||
184 | if (payload->verify(payload) != SUCCESS) | |
185 | { | |
186 | in->destroy(in); | |
187 | return FAILED; | |
188 | } | |
189 | code = in->get_code(in); | |
190 | received_type = in->get_type(in, &received_vendor); | |
191 | DBG1(DBG_IKE, "received tunneled EAP-TTLS AVP [EAP/%N/%N]", | |
42500c27 | 192 | eap_code_short_names, code, |
79f2102c AS |
193 | eap_type_short_names, received_type); |
194 | if (code != EAP_RESPONSE) | |
195 | { | |
196 | DBG1(DBG_IKE, "%N expected", eap_code_names, EAP_RESPONSE); | |
197 | in->destroy(in); | |
198 | return FAILED; | |
199 | } | |
200 | ||
201 | if (this->method) | |
202 | { | |
203 | type = this->method->get_type(this->method, &vendor); | |
204 | ||
205 | if (type != received_type || vendor != received_vendor) | |
206 | { | |
207 | if (received_vendor == 0 && received_type == EAP_NAK) | |
208 | { | |
209 | DBG1(DBG_IKE, "peer does not support %N", eap_type_names, type); | |
210 | } | |
211 | else | |
212 | { | |
213 | DBG1(DBG_IKE, "received invalid EAP response"); | |
214 | } | |
215 | in->destroy(in); | |
216 | return FAILED; | |
217 | } | |
218 | } | |
ba31fe1f | 219 | |
79f2102c AS |
220 | if (!received_vendor && received_type == EAP_IDENTITY) |
221 | { | |
222 | chunk_t eap_id; | |
79f2102c AS |
223 | |
224 | if (this->method == NULL) | |
225 | { | |
226 | /* Received an EAP Identity response without a matching request */ | |
227 | this->method = charon->eap->create_instance(charon->eap, | |
228 | EAP_IDENTITY, 0, EAP_SERVER, | |
229 | this->server, this->peer); | |
230 | if (this->method == NULL) | |
231 | { | |
232 | DBG1(DBG_IKE, "%N method not available", | |
233 | eap_type_names, EAP_IDENTITY); | |
234 | return FAILED; | |
235 | } | |
236 | } | |
237 | ||
238 | if (this->method->process(this->method, in, &this->out) != SUCCESS) | |
239 | { | |
240 | ||
241 | DBG1(DBG_IKE, "%N method failed", eap_type_names, EAP_IDENTITY); | |
242 | return FAILED; | |
243 | } | |
244 | ||
245 | if (this->method->get_msk(this->method, &eap_id) == SUCCESS) | |
246 | { | |
247 | this->peer->destroy(this->peer); | |
248 | this->peer = identification_create_from_data(eap_id); | |
249 | DBG1(DBG_IKE, "received EAP identity '%Y'", this->peer); | |
250 | } | |
251 | ||
252 | in->destroy(in); | |
253 | this->method->destroy(this->method); | |
254 | this->method = NULL; | |
255 | ||
256 | /* Start Phase 2 of EAP-TTLS authentication */ | |
260d0561 | 257 | if (lib->settings->get_bool(lib->settings, |
d223fe80 | 258 | "%s.plugins.eap-ttls.request_peer_auth", FALSE, lib->ns)) |
79f2102c | 259 | { |
2a421163 | 260 | return start_phase2_tnc(this, EAP_TLS); |
79f2102c AS |
261 | } |
262 | else | |
263 | { | |
260d0561 | 264 | return start_phase2_auth(this); |
79f2102c AS |
265 | } |
266 | } | |
ba31fe1f | 267 | |
79f2102c AS |
268 | if (this->method == 0) |
269 | { | |
270 | DBG1(DBG_IKE, "no %N phase2 method installed", eap_type_names, EAP_TTLS); | |
271 | in->destroy(in); | |
272 | return FAILED; | |
273 | } | |
274 | ||
275 | status = this->method->process(this->method, in, &this->out); | |
276 | in->destroy(in); | |
ba31fe1f | 277 | |
79f2102c AS |
278 | switch (status) |
279 | { | |
280 | case SUCCESS: | |
281 | DBG1(DBG_IKE, "%N phase2 authentication of '%Y' with %N successful", | |
282 | eap_type_names, EAP_TTLS, this->peer, | |
283 | eap_type_names, type); | |
9ba53310 AS |
284 | this->method->destroy(this->method); |
285 | this->method = NULL; | |
88845558 AS |
286 | |
287 | /* continue phase2 with EAP-TNC? */ | |
2a421163 | 288 | return start_phase2_tnc(this, type); |
79f2102c AS |
289 | case NEED_MORE: |
290 | break; | |
291 | case FAILED: | |
292 | default: | |
9ba53310 AS |
293 | if (vendor) |
294 | { | |
295 | DBG1(DBG_IKE, "vendor specific EAP method %d-%d failed", | |
296 | type, vendor); | |
297 | } | |
298 | else | |
299 | { | |
300 | DBG1(DBG_IKE, "%N method failed", eap_type_names, type); | |
301 | } | |
302 | return FAILED; | |
42500c27 | 303 | } |
79f2102c AS |
304 | return status; |
305 | } | |
306 | ||
307 | METHOD(tls_application_t, build, status_t, | |
7e432eff | 308 | private_eap_ttls_server_t *this, bio_writer_t *writer) |
79f2102c AS |
309 | { |
310 | chunk_t data; | |
311 | eap_code_t code; | |
312 | eap_type_t type; | |
8d590903 | 313 | uint32_t vendor; |
79f2102c | 314 | |
9ba53310 AS |
315 | if (this->method == NULL && this->start_phase2 && |
316 | lib->settings->get_bool(lib->settings, | |
d223fe80 | 317 | "%s.plugins.eap-ttls.phase2_piggyback", FALSE, lib->ns)) |
9ba53310 AS |
318 | { |
319 | /* generate an EAP Identity request which will be piggybacked right | |
320 | * onto the TLS Finished message thus initiating EAP-TTLS phase2 | |
321 | */ | |
322 | this->method = charon->eap->create_instance(charon->eap, EAP_IDENTITY, | |
323 | 0, EAP_SERVER, this->server, this->peer); | |
324 | if (this->method == NULL) | |
325 | { | |
88845558 AS |
326 | DBG1(DBG_IKE, "%N method not available", |
327 | eap_type_names, EAP_IDENTITY); | |
9ba53310 AS |
328 | return FAILED; |
329 | } | |
330 | this->method->initiate(this->method, &this->out); | |
331 | this->start_phase2 = FALSE; | |
332 | } | |
333 | ||
79f2102c AS |
334 | if (this->out) |
335 | { | |
336 | code = this->out->get_code(this->out); | |
337 | type = this->out->get_type(this->out, &vendor); | |
338 | DBG1(DBG_IKE, "sending tunneled EAP-TTLS AVP [EAP/%N/%N]", | |
339 | eap_code_short_names, code, eap_type_short_names, type); | |
340 | ||
341 | /* get the raw EAP message data */ | |
342 | data = this->out->get_data(this->out); | |
343 | this->avp->build(this->avp, writer, data); | |
344 | ||
345 | this->out->destroy(this->out); | |
346 | this->out = NULL; | |
347 | } | |
348 | return INVALID_STATE; | |
349 | } | |
350 | ||
351 | METHOD(tls_application_t, destroy, void, | |
352 | private_eap_ttls_server_t *this) | |
353 | { | |
354 | this->server->destroy(this->server); | |
355 | this->peer->destroy(this->peer); | |
356 | DESTROY_IF(this->method); | |
357 | DESTROY_IF(this->out); | |
358 | this->avp->destroy(this->avp); | |
359 | free(this); | |
360 | } | |
361 | ||
362 | /** | |
363 | * See header | |
364 | */ | |
365 | eap_ttls_server_t *eap_ttls_server_create(identification_t *server, | |
366 | identification_t *peer) | |
367 | { | |
368 | private_eap_ttls_server_t *this; | |
369 | ||
370 | INIT(this, | |
ba31fe1f MW |
371 | .public = { |
372 | .application = { | |
373 | .process = _process, | |
374 | .build = _build, | |
375 | .destroy = _destroy, | |
376 | }, | |
79f2102c AS |
377 | }, |
378 | .server = server->clone(server), | |
379 | .peer = peer->clone(peer), | |
380 | .start_phase2 = TRUE, | |
88845558 | 381 | .start_phase2_tnc = TRUE, |
79f2102c AS |
382 | .avp = eap_ttls_avp_create(), |
383 | ); | |
384 | ||
385 | return &this->public; | |
386 | } |