send tunneled EAP Identity response using eap-identity plugin
[strongswan.git] / src / libcharon / plugins / eap_ttls / eap_ttls.c
1 /*
2 * Copyright (C) 2010 Martin Willi, revosec AG
3 * Copyright (C) 2010 Andreas Steffen, 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.h"
17 #include "eap_ttls_peer.h"
18
19 #include <tls.h>
20
21 #include <daemon.h>
22 #include <library.h>
23
24 typedef struct private_eap_ttls_t private_eap_ttls_t;
25
26 /**
27 * Private data of an eap_ttls_t object.
28 */
29 struct private_eap_ttls_t {
30
31 /**
32 * Public interface.
33 */
34 eap_ttls_t public;
35
36 /**
37 * Number of EAP-TLS messages processed so far
38 */
39 int processed;
40
41 /**
42 * Is this method instance acting as server?
43 */
44 bool is_server;
45
46 /**
47 * TLS layers
48 */
49 tls_t *tls;
50
51 /**
52 * Allocated input buffer
53 */
54 chunk_t input;
55
56 /**
57 * Number of bytes read in input buffer
58 */
59 size_t inpos;
60
61 /**
62 * Allocated ouput buffer
63 */
64 chunk_t output;
65
66 /**
67 * Number of bytes sent from output buffer
68 */
69 size_t outpos;
70 };
71
72 /** Size limit for a single TLS message */
73 #define MAX_TLS_MESSAGE_LEN 16384
74 /** Size of a EAP-TLS fragment */
75 #define EAP_TTLS_FRAGMENT_LEN 1014
76 /** Maximum number of EAP-TLS messages/fragments allowed */
77 #define MAX_EAP_TTLS_MESSAGE_COUNT 16
78
79 /**
80 * Flags of an EAP-TLS message
81 */
82 typedef enum {
83 EAP_TTLS_LENGTH = (1<<7),
84 EAP_TTLS_MORE_FRAGS = (1<<6),
85 EAP_TTLS_START = (1<<5),
86 EAP_TTLS_VERSION = 0x07
87 } eap_ttls_flags_t;
88
89 /**
90 * EAP-TLS packet format
91 */
92 typedef struct __attribute__((packed)) {
93 u_int8_t code;
94 u_int8_t identifier;
95 u_int16_t length;
96 u_int8_t type;
97 u_int8_t flags;
98 } eap_ttls_packet_t;
99
100 /**
101 * TLS record
102 */
103 typedef struct __attribute__((packed)) {
104 u_int8_t type;
105 u_int16_t version;
106 u_int16_t length;
107 char data[];
108 } tls_record_t;
109
110 METHOD(eap_method_t, initiate, status_t,
111 private_eap_ttls_t *this, eap_payload_t **out)
112 {
113 if (this->is_server)
114 {
115 eap_ttls_packet_t pkt = {
116 .type = EAP_TTLS,
117 .code = EAP_REQUEST,
118 .flags = EAP_TTLS_START,
119 };
120 htoun16(&pkt.length, sizeof(eap_ttls_packet_t));
121 /* start with non-zero random identifier */
122 do {
123 pkt.identifier = random();
124 } while (!pkt.identifier);
125 DBG2(DBG_IKE, "sending EAP-TLS start packet");
126
127 *out = eap_payload_create_data(chunk_from_thing(pkt));
128 return NEED_MORE;
129 }
130 return FAILED;
131 }
132
133 /**
134 * Write received TLS data to the input buffer
135 */
136 static bool write_buf(private_eap_ttls_t *this, eap_ttls_packet_t *pkt)
137 {
138 u_int32_t msg_len;
139 u_int16_t pkt_len;
140 chunk_t data;
141
142 pkt_len = untoh16(&pkt->length);
143
144 if (pkt->flags & EAP_TTLS_LENGTH)
145 {
146 if (pkt_len < sizeof(eap_ttls_packet_t) + sizeof(msg_len))
147 {
148 DBG1(DBG_IKE, "EAP-TLS packet too short");
149 return FALSE;
150 }
151 msg_len = untoh32(pkt + 1);
152 if (msg_len < pkt_len - sizeof(eap_ttls_packet_t) - sizeof(msg_len) ||
153 msg_len > MAX_TLS_MESSAGE_LEN)
154 {
155 DBG1(DBG_IKE, "invalid EAP-TLS packet length");
156 return FALSE;
157 }
158 if (this->input.ptr)
159 {
160 if (msg_len != this->input.len)
161 {
162 DBG1(DBG_IKE, "received unexpected TLS message length");
163 return FALSE;
164 }
165 }
166 else
167 {
168 this->input = chunk_alloc(msg_len);
169 this->inpos = 0;
170 }
171 data = chunk_create((char*)(pkt + 1) + sizeof(msg_len),
172 pkt_len - sizeof(eap_ttls_packet_t) - sizeof(msg_len));
173 }
174 else
175 {
176 data = chunk_create((char*)(pkt + 1),
177 pkt_len - sizeof(eap_ttls_packet_t));
178 }
179 if (data.len > this->input.len - this->inpos)
180 {
181 DBG1(DBG_IKE, "EAP-TLS fragment exceeds TLS message length");
182 return FALSE;
183 }
184 memcpy(this->input.ptr + this->inpos, data.ptr, data.len);
185 this->inpos += data.len;
186 return TRUE;
187 }
188
189 /**
190 * Send an ack to request next fragment
191 */
192 static eap_payload_t *create_ack(private_eap_ttls_t *this, u_int8_t identifier)
193 {
194 eap_ttls_packet_t pkt = {
195 .code = this->is_server ? EAP_REQUEST : EAP_RESPONSE,
196 .identifier = this->is_server ? identifier + 1 : identifier,
197 .type = EAP_TTLS,
198 };
199 htoun16(&pkt.length, sizeof(pkt));
200 DBG2(DBG_IKE, "sending EAP-TLS acknowledgement packet");
201
202 return eap_payload_create_data(chunk_from_thing(pkt));
203 }
204
205 /**
206 * Create a eap response from data in the TLS output buffer
207 */
208 static eap_payload_t *read_buf(private_eap_ttls_t *this, u_int8_t identifier)
209 {
210 char buf[EAP_TTLS_FRAGMENT_LEN + sizeof(eap_ttls_packet_t) + 4], *start;
211 eap_ttls_packet_t *pkt = (eap_ttls_packet_t*)buf;
212 u_int16_t pkt_len = sizeof(eap_ttls_packet_t);
213
214 pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
215 pkt->identifier = this->is_server ? identifier + 1 : identifier;
216 pkt->type = EAP_TTLS;
217 pkt->flags = 0;
218
219 if (this->output.len)
220 {
221 start = (char*)(pkt + 1);
222 if (this->outpos == 0)
223 { /* first fragment */
224 pkt->flags = EAP_TTLS_LENGTH;
225 pkt_len += 4;
226 start += 4;
227 htoun32(pkt + 1, this->output.len);
228 }
229
230 if (this->output.len - this->outpos > EAP_TTLS_FRAGMENT_LEN)
231 {
232 pkt->flags |= EAP_TTLS_MORE_FRAGS;
233 pkt_len += EAP_TTLS_FRAGMENT_LEN;
234 memcpy(start, this->output.ptr + this->outpos, EAP_TTLS_FRAGMENT_LEN);
235 this->outpos += EAP_TTLS_FRAGMENT_LEN;
236 DBG2(DBG_IKE, "sending EAP-TLS packet fragment");
237 }
238 else
239 {
240 pkt_len += this->output.len - this->outpos;
241 memcpy(start, this->output.ptr + this->outpos,
242 this->output.len - this->outpos);
243 chunk_free(&this->output);
244 this->outpos = 0;
245 DBG2(DBG_IKE, "sending EAP-TLS packet");
246 }
247 }
248 else
249 {
250 DBG2(DBG_IKE, "sending EAP-TLS acknowledgement packet");
251 }
252 htoun16(&pkt->length, pkt_len);
253 return eap_payload_create_data(chunk_create(buf, pkt_len));
254 }
255
256 /**
257 * Pass data in input buffer to upper layers, write result to output buffer
258 */
259 static status_t process_buf(private_eap_ttls_t *this)
260 {
261 tls_record_t *in, out;
262 chunk_t data;
263 u_int16_t len;
264 status_t status;
265
266 /* pass input buffer to upper layer, record for record */
267 data = this->input;
268 while (data.len > sizeof(tls_record_t))
269 {
270 in = (tls_record_t*)data.ptr;
271 len = untoh16(&in->length);
272 if (len > data.len - sizeof(tls_record_t))
273 {
274 DBG1(DBG_IKE, "TLS record length invalid");
275 return FAILED;
276 }
277 if (untoh16(&in->version) < TLS_1_0)
278 {
279 DBG1(DBG_IKE, "%N invalid with EAP-TLS",
280 tls_version_names, untoh16(&in->version));
281 return FAILED;
282 }
283
284 status = this->tls->process(this->tls, in->type,
285 chunk_create(in->data, len));
286 if (status != NEED_MORE)
287 {
288 return status;
289 }
290 data = chunk_skip(data, len + sizeof(tls_record_t));
291 }
292 chunk_free(&this->input);
293 this->inpos = 0;
294
295 /* read in records from upper layer, append to output buffer */
296 chunk_free(&this->output);
297 while (TRUE)
298 {
299 tls_content_type_t type;
300 chunk_t header = chunk_from_thing(out);
301
302 status = this->tls->build(this->tls, &type, &data);
303 switch (status)
304 {
305 case NEED_MORE:
306 break;
307 case INVALID_STATE:
308 /* invalid state means we need more input from peer first */
309 return NEED_MORE;
310 case SUCCESS:
311 return SUCCESS;
312 case FAILED:
313 default:
314 return FAILED;
315 }
316 out.type = type;
317 htoun16(&out.version, this->tls->get_version(this->tls));
318 htoun16(&out.length, data.len);
319 this->output = chunk_cat("mcm", this->output, header, data);
320 }
321 }
322
323 METHOD(eap_method_t, process, status_t,
324 private_eap_ttls_t *this, eap_payload_t *in, eap_payload_t **out)
325 {
326 eap_ttls_packet_t *pkt;
327 chunk_t data;
328 status_t status;
329
330 if (++this->processed > MAX_EAP_TTLS_MESSAGE_COUNT)
331 {
332 DBG1(DBG_IKE, "EAP-TTLS packet count exceeded");
333 return FAILED;
334 }
335
336 data = in->get_data(in);
337
338 pkt = (eap_ttls_packet_t*)data.ptr;
339 if (data.len < sizeof(eap_ttls_packet_t) ||
340 untoh16(&pkt->length) != data.len)
341 {
342 DBG1(DBG_IKE, "invalid EAP-TLS packet length");
343 return FAILED;
344 }
345 if (pkt->flags & EAP_TTLS_START)
346 {
347 DBG1(DBG_IKE, "EAP-TTLS version is v%u",
348 pkt->flags & EAP_TTLS_VERSION);
349 }
350 else
351 {
352 if (data.len == sizeof(eap_ttls_packet_t))
353 {
354 if (this->output.len)
355 { /* ACK to our fragment, send next */
356 *out = read_buf(this, pkt->identifier);
357 return NEED_MORE;
358 }
359 if (this->tls->is_complete(this->tls))
360 {
361 return SUCCESS;
362 }
363 return FAILED;
364 }
365 if (!write_buf(this, pkt))
366 {
367 return FAILED;
368 }
369 if (pkt->flags & EAP_TTLS_MORE_FRAGS)
370 { /* more fragments follow */
371 *out = create_ack(this, pkt->identifier);
372 return NEED_MORE;
373 }
374 else if (this->input.len != this->inpos)
375 {
376 DBG1(DBG_IKE, "defragmented TLS message has invalid length");
377 return FAILED;
378 }
379 }
380 status = process_buf(this);
381 if (status == NEED_MORE)
382 {
383 *out = read_buf(this, pkt->identifier);
384 }
385 return status;
386 }
387
388 METHOD(eap_method_t, get_type, eap_type_t,
389 private_eap_ttls_t *this, u_int32_t *vendor)
390 {
391 *vendor = 0;
392 return EAP_TTLS;
393 }
394
395 METHOD(eap_method_t, get_msk, status_t,
396 private_eap_ttls_t *this, chunk_t *msk)
397 {
398 *msk = this->tls->get_eap_msk(this->tls);
399 if (msk->len)
400 {
401 return SUCCESS;
402 }
403 return FAILED;
404 }
405
406 METHOD(eap_method_t, is_mutual, bool,
407 private_eap_ttls_t *this)
408 {
409 return TRUE;
410 }
411
412 METHOD(eap_method_t, destroy, void,
413 private_eap_ttls_t *this)
414 {
415 free(this->input.ptr);
416 free(this->output.ptr);
417
418 this->tls->destroy(this->tls);
419
420 free(this);
421 }
422
423 /**
424 * Generic private constructor
425 */
426 static eap_ttls_t *eap_ttls_create(identification_t *server,
427 identification_t *peer, bool is_server,
428 tls_application_t *application)
429 {
430 private_eap_ttls_t *this;
431
432 INIT(this,
433 .public.eap_method = {
434 .initiate = _initiate,
435 .process = _process,
436 .get_type = _get_type,
437 .is_mutual = _is_mutual,
438 .get_msk = _get_msk,
439 .destroy = _destroy,
440 },
441 .is_server = is_server,
442 );
443 /* MSK PRF ASCII constant label according to EAP-TTLS RFC 5281 */
444 this->tls = tls_create(is_server, server, peer, "ttls keying material",
445 application);
446 return &this->public;
447 }
448
449 eap_ttls_t *eap_ttls_create_server(identification_t *server,
450 identification_t *peer)
451 {
452 return eap_ttls_create(server, peer, TRUE, NULL);
453 }
454
455 eap_ttls_t *eap_ttls_create_peer(identification_t *server,
456 identification_t *peer)
457 {
458 return eap_ttls_create(server, peer, FALSE,
459 &eap_ttls_peer_create(server, peer)->application);
460 }