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