/src/strongswan/src/libtls/tls_fragmentation.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2010 Martin Willi |
3 | | * |
4 | | * Copyright (C) secunet Security Networks AG |
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 "tls_fragmentation.h" |
18 | | |
19 | | #include <bio/bio_reader.h> |
20 | | #include <utils/debug.h> |
21 | | |
22 | | /** |
23 | | * Maximum size of a TLS handshake message we accept |
24 | | */ |
25 | 1 | #define TLS_MAX_HANDSHAKE_LEN 65536 |
26 | | |
27 | | typedef struct private_tls_fragmentation_t private_tls_fragmentation_t; |
28 | | |
29 | | /** |
30 | | * Alert state |
31 | | */ |
32 | | typedef enum { |
33 | | /* no alert received/sent */ |
34 | | ALERT_NONE, |
35 | | /* currently sending an alert */ |
36 | | ALERT_SENDING, |
37 | | /* alert sent and out */ |
38 | | ALERT_SENT, |
39 | | } alert_state_t; |
40 | | |
41 | | /** |
42 | | * Private data of an tls_fragmentation_t object. |
43 | | */ |
44 | | struct private_tls_fragmentation_t { |
45 | | |
46 | | /** |
47 | | * Public tls_fragmentation_t interface. |
48 | | */ |
49 | | tls_fragmentation_t public; |
50 | | |
51 | | /** |
52 | | * Upper layer handshake protocol |
53 | | */ |
54 | | tls_handshake_t *handshake; |
55 | | |
56 | | /** |
57 | | * TLS alert handler |
58 | | */ |
59 | | tls_alert_t *alert; |
60 | | |
61 | | /** |
62 | | * State of alert handling |
63 | | */ |
64 | | alert_state_t state; |
65 | | |
66 | | /** |
67 | | * Did the application layer complete successfully? |
68 | | */ |
69 | | bool application_finished; |
70 | | |
71 | | /** |
72 | | * Handshake input buffer |
73 | | */ |
74 | | chunk_t input; |
75 | | |
76 | | /** |
77 | | * Position in input buffer |
78 | | */ |
79 | | size_t inpos; |
80 | | |
81 | | /** |
82 | | * Currently processed handshake message type |
83 | | */ |
84 | | tls_handshake_type_t type; |
85 | | |
86 | | /** |
87 | | * Handshake output buffer |
88 | | */ |
89 | | chunk_t output; |
90 | | |
91 | | /** |
92 | | * Type of data in output buffer |
93 | | */ |
94 | | tls_content_type_t output_type; |
95 | | |
96 | | /** |
97 | | * Upper layer application data protocol |
98 | | */ |
99 | | tls_application_t *application; |
100 | | |
101 | | /** |
102 | | * Type of context this TLS instance runs in |
103 | | */ |
104 | | tls_purpose_t purpose; |
105 | | }; |
106 | | |
107 | | /** |
108 | | * Check if we should send a close notify once the application finishes |
109 | | */ |
110 | | static bool send_close_notify(private_tls_fragmentation_t *this) |
111 | 0 | { |
112 | 0 | switch (this->purpose) |
113 | 0 | { |
114 | 0 | case TLS_PURPOSE_EAP_TLS: |
115 | 0 | case TLS_PURPOSE_EAP_TTLS: |
116 | 0 | case TLS_PURPOSE_EAP_PEAP: |
117 | | /* not for TLS-in-EAP, as we indicate completion with EAP-SUCCCESS. |
118 | | * Windows does not like close notifies, and hangs/disconnects. */ |
119 | 0 | return FALSE; |
120 | 0 | default: |
121 | 0 | return TRUE; |
122 | 0 | } |
123 | 0 | } |
124 | | |
125 | | /** |
126 | | * Process a TLS alert |
127 | | */ |
128 | | static status_t process_alert(private_tls_fragmentation_t *this, |
129 | | bio_reader_t *reader) |
130 | 1 | { |
131 | 1 | uint8_t level, description; |
132 | | |
133 | 1 | if (!reader->read_uint8(reader, &level) || |
134 | 1 | !reader->read_uint8(reader, &description)) |
135 | 0 | { |
136 | 0 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); |
137 | 0 | return NEED_MORE; |
138 | 0 | } |
139 | 1 | return this->alert->process(this->alert, level, description); |
140 | 1 | } |
141 | | |
142 | | /** |
143 | | * Process TLS handshake protocol data |
144 | | */ |
145 | | static status_t process_handshake(private_tls_fragmentation_t *this, |
146 | | bio_reader_t *reader) |
147 | 2 | { |
148 | 3 | while (reader->remaining(reader)) |
149 | 1 | { |
150 | 1 | bio_reader_t *msg; |
151 | 1 | uint8_t type; |
152 | 1 | uint32_t len; |
153 | 1 | status_t status; |
154 | 1 | chunk_t data; |
155 | | |
156 | 1 | if (reader->remaining(reader) > TLS_MAX_FRAGMENT_LEN) |
157 | 0 | { |
158 | 0 | DBG1(DBG_TLS, "TLS fragment has invalid length"); |
159 | 0 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); |
160 | 0 | return NEED_MORE; |
161 | 0 | } |
162 | | |
163 | 1 | if (this->input.len == 0) |
164 | 1 | { /* new handshake message */ |
165 | 1 | if (!reader->read_uint8(reader, &type) || |
166 | 1 | !reader->read_uint24(reader, &len)) |
167 | 0 | { |
168 | 0 | DBG1(DBG_TLS, "TLS handshake header invalid"); |
169 | 0 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); |
170 | 0 | return NEED_MORE; |
171 | 0 | } |
172 | 1 | this->type = type; |
173 | 1 | if (len > TLS_MAX_HANDSHAKE_LEN) |
174 | 0 | { |
175 | 0 | DBG1(DBG_TLS, "TLS handshake exceeds maximum length"); |
176 | 0 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); |
177 | 0 | return NEED_MORE; |
178 | 0 | } |
179 | 1 | chunk_free(&this->input); |
180 | 1 | this->inpos = 0; |
181 | 1 | if (len) |
182 | 1 | { |
183 | 1 | this->input = chunk_alloc(len); |
184 | 1 | } |
185 | 1 | } |
186 | | |
187 | 1 | len = min(this->input.len - this->inpos, reader->remaining(reader)); |
188 | 1 | if (!reader->read_data(reader, len, &data)) |
189 | 0 | { |
190 | 0 | DBG1(DBG_TLS, "TLS fragment has invalid length"); |
191 | 0 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); |
192 | 0 | return NEED_MORE; |
193 | 0 | } |
194 | 1 | memcpy(this->input.ptr + this->inpos, data.ptr, len); |
195 | 1 | this->inpos += len; |
196 | | |
197 | 1 | if (this->input.len == this->inpos) |
198 | 0 | { /* message completely defragmented, process */ |
199 | 0 | msg = bio_reader_create(this->input); |
200 | 0 | DBG2(DBG_TLS, "received TLS %N handshake (%u bytes)", |
201 | 0 | tls_handshake_type_names, this->type, this->input.len); |
202 | 0 | status = this->handshake->process(this->handshake, this->type, msg); |
203 | 0 | msg->destroy(msg); |
204 | 0 | chunk_free(&this->input); |
205 | 0 | if (status != NEED_MORE) |
206 | 0 | { |
207 | 0 | return status; |
208 | 0 | } |
209 | 0 | } |
210 | 1 | if (this->alert->fatal(this->alert)) |
211 | 0 | { |
212 | 0 | break; |
213 | 0 | } |
214 | 1 | } |
215 | 2 | return NEED_MORE; |
216 | 2 | } |
217 | | |
218 | | /** |
219 | | * Process TLS application data |
220 | | */ |
221 | | static status_t process_application(private_tls_fragmentation_t *this, |
222 | | bio_reader_t *reader) |
223 | 1 | { |
224 | 1 | if (!this->handshake->finished(this->handshake)) |
225 | 1 | { |
226 | 1 | DBG1(DBG_TLS, "received TLS application data, " |
227 | 1 | "but handshake not finished"); |
228 | 1 | return FAILED; |
229 | 1 | } |
230 | 0 | while (reader->remaining(reader)) |
231 | 0 | { |
232 | 0 | status_t status; |
233 | |
|
234 | 0 | if (reader->remaining(reader) > TLS_MAX_FRAGMENT_LEN) |
235 | 0 | { |
236 | 0 | DBG1(DBG_TLS, "TLS fragment has invalid length"); |
237 | 0 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); |
238 | 0 | return NEED_MORE; |
239 | 0 | } |
240 | | #if DEBUG_LEVEL >= 3 |
241 | | chunk_t data = reader->peek(reader); |
242 | | DBG3(DBG_TLS, "%B", &data); |
243 | | #endif |
244 | 0 | status = this->application->process(this->application, reader); |
245 | 0 | switch (status) |
246 | 0 | { |
247 | 0 | case NEED_MORE: |
248 | 0 | continue; |
249 | 0 | case SUCCESS: |
250 | 0 | this->application_finished = TRUE; |
251 | 0 | if (!send_close_notify(this)) |
252 | 0 | { |
253 | 0 | return SUCCESS; |
254 | 0 | } |
255 | | /* FALL */ |
256 | 0 | case FAILED: |
257 | 0 | default: |
258 | 0 | this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY); |
259 | 0 | return NEED_MORE; |
260 | 0 | } |
261 | 0 | } |
262 | 0 | return NEED_MORE; |
263 | 0 | } |
264 | | |
265 | | METHOD(tls_fragmentation_t, process, status_t, |
266 | | private_tls_fragmentation_t *this, tls_content_type_t type, chunk_t data) |
267 | 1.50M | { |
268 | 1.50M | bio_reader_t *reader; |
269 | 1.50M | status_t status; |
270 | | |
271 | 1.50M | switch (this->state) |
272 | 1.50M | { |
273 | 0 | case ALERT_SENDING: |
274 | 0 | case ALERT_SENT: |
275 | | /* don't accept more input, fatal error occurred */ |
276 | 0 | return NEED_MORE; |
277 | 1.50M | case ALERT_NONE: |
278 | 1.50M | break; |
279 | 1.50M | } |
280 | 1.50M | reader = bio_reader_create(data); |
281 | 1.50M | switch (type) |
282 | 1.50M | { |
283 | 0 | case TLS_CHANGE_CIPHER_SPEC: |
284 | 0 | if (this->handshake->cipherspec_changed(this->handshake, TRUE)) |
285 | 0 | { |
286 | 0 | this->handshake->change_cipherspec(this->handshake, TRUE); |
287 | 0 | status = NEED_MORE; |
288 | 0 | break; |
289 | 0 | } |
290 | 0 | status = FAILED; |
291 | 0 | break; |
292 | 1 | case TLS_ALERT: |
293 | 1 | status = process_alert(this, reader); |
294 | 1 | break; |
295 | 2 | case TLS_HANDSHAKE: |
296 | 2 | status = process_handshake(this, reader); |
297 | 2 | if (!this->handshake->finished(this->handshake)) |
298 | 2 | { |
299 | 2 | break; |
300 | 2 | } |
301 | | /* fall-through */ |
302 | 1 | case TLS_APPLICATION_DATA: |
303 | 1 | status = process_application(this, reader); |
304 | 1 | break; |
305 | 1.50M | default: |
306 | 1.50M | DBG1(DBG_TLS, "received unknown TLS content type %d, ignored", type); |
307 | 1.50M | status = NEED_MORE; |
308 | 1.50M | break; |
309 | 1.50M | } |
310 | 1.50M | reader->destroy(reader); |
311 | 1.50M | return status; |
312 | 1.50M | } |
313 | | |
314 | | /** |
315 | | * Check if alerts are pending |
316 | | */ |
317 | | static bool check_alerts(private_tls_fragmentation_t *this, chunk_t *data) |
318 | 328 | { |
319 | 328 | tls_alert_level_t level; |
320 | 328 | tls_alert_desc_t desc; |
321 | 328 | bio_writer_t *writer; |
322 | | |
323 | 328 | if (this->alert->get(this->alert, &level, &desc)) |
324 | 0 | { |
325 | 0 | writer = bio_writer_create(2); |
326 | |
|
327 | 0 | writer->write_uint8(writer, level); |
328 | 0 | writer->write_uint8(writer, desc); |
329 | |
|
330 | 0 | *data = chunk_clone(writer->get_buf(writer)); |
331 | 0 | writer->destroy(writer); |
332 | 0 | return TRUE; |
333 | 0 | } |
334 | 328 | return FALSE; |
335 | 328 | } |
336 | | |
337 | | /** |
338 | | * Build handshake message |
339 | | */ |
340 | | static status_t build_handshake(private_tls_fragmentation_t *this) |
341 | 164 | { |
342 | 164 | bio_writer_t *hs, *msg; |
343 | 164 | tls_handshake_type_t type; |
344 | 164 | status_t status; |
345 | | |
346 | 164 | msg = bio_writer_create(64); |
347 | 164 | while (TRUE) |
348 | 201 | { |
349 | 201 | hs = bio_writer_create(64); |
350 | 201 | status = this->handshake->build(this->handshake, &type, hs); |
351 | 201 | switch (status) |
352 | 201 | { |
353 | 37 | case NEED_MORE: |
354 | 37 | if (this->alert->fatal(this->alert)) |
355 | 0 | { |
356 | 0 | break; |
357 | 0 | } |
358 | 37 | msg->write_uint8(msg, type); |
359 | 37 | msg->write_data24(msg, hs->get_buf(hs)); |
360 | 37 | DBG2(DBG_TLS, "sending TLS %N handshake (%u bytes)", |
361 | 37 | tls_handshake_type_names, type, hs->get_buf(hs).len); |
362 | 37 | if (type != TLS_FINISHED && |
363 | 37 | type != TLS_KEY_UPDATE && |
364 | 37 | !this->handshake->cipherspec_changed(this->handshake, FALSE)) |
365 | 37 | { |
366 | 37 | hs->destroy(hs); |
367 | 37 | continue; |
368 | 37 | } |
369 | | /* FALL */ |
370 | 164 | case INVALID_STATE: |
371 | 164 | this->output_type = TLS_HANDSHAKE; |
372 | 164 | this->output = chunk_clone(msg->get_buf(msg)); |
373 | 164 | break; |
374 | 0 | default: |
375 | 0 | break; |
376 | 201 | } |
377 | 164 | hs->destroy(hs); |
378 | 164 | break; |
379 | 201 | } |
380 | 164 | msg->destroy(msg); |
381 | 164 | return status; |
382 | 164 | } |
383 | | |
384 | | /** |
385 | | * Build TLS application data |
386 | | */ |
387 | | static status_t build_application(private_tls_fragmentation_t *this) |
388 | 0 | { |
389 | 0 | bio_writer_t *msg; |
390 | 0 | status_t status; |
391 | |
|
392 | 0 | msg = bio_writer_create(64); |
393 | 0 | while (TRUE) |
394 | 0 | { |
395 | 0 | status = this->application->build(this->application, msg); |
396 | 0 | switch (status) |
397 | 0 | { |
398 | 0 | case NEED_MORE: |
399 | 0 | continue; |
400 | 0 | case INVALID_STATE: |
401 | 0 | this->output_type = TLS_APPLICATION_DATA; |
402 | 0 | this->output = chunk_clone(msg->get_buf(msg)); |
403 | 0 | break; |
404 | 0 | case SUCCESS: |
405 | 0 | this->application_finished = TRUE; |
406 | 0 | if (!send_close_notify(this)) |
407 | 0 | { |
408 | 0 | break; |
409 | 0 | } |
410 | | /* FALL */ |
411 | 0 | case FAILED: |
412 | 0 | default: |
413 | 0 | this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY); |
414 | 0 | break; |
415 | 0 | } |
416 | 0 | break; |
417 | 0 | } |
418 | 0 | msg->destroy(msg); |
419 | 0 | return status; |
420 | 0 | } |
421 | | |
422 | | METHOD(tls_fragmentation_t, build, status_t, |
423 | | private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data) |
424 | 164 | { |
425 | 164 | status_t status = INVALID_STATE; |
426 | | |
427 | 164 | switch (this->state) |
428 | 164 | { |
429 | 0 | case ALERT_SENDING: |
430 | 0 | this->state = ALERT_SENT; |
431 | 0 | return INVALID_STATE; |
432 | 0 | case ALERT_SENT: |
433 | 0 | if (this->application_finished) |
434 | 0 | { |
435 | 0 | return SUCCESS; |
436 | 0 | } |
437 | 0 | return FAILED; |
438 | 164 | case ALERT_NONE: |
439 | 164 | break; |
440 | 164 | } |
441 | 164 | if (check_alerts(this, data)) |
442 | 0 | { |
443 | 0 | this->state = ALERT_SENDING; |
444 | 0 | *type = TLS_ALERT; |
445 | 0 | return NEED_MORE; |
446 | 0 | } |
447 | 164 | if (!this->output.len) |
448 | 164 | { |
449 | 164 | if (this->handshake->cipherspec_changed(this->handshake, FALSE)) |
450 | 0 | { |
451 | 0 | this->handshake->change_cipherspec(this->handshake, FALSE); |
452 | 0 | *type = TLS_CHANGE_CIPHER_SPEC; |
453 | 0 | *data = chunk_clone(chunk_from_chars(0x01)); |
454 | 0 | return NEED_MORE; |
455 | 0 | } |
456 | 164 | if (!this->handshake->finished(this->handshake)) |
457 | 164 | { |
458 | 164 | status = build_handshake(this); |
459 | 164 | } |
460 | 0 | else if (this->application) |
461 | 0 | { |
462 | 0 | status = build_application(this); |
463 | 0 | } |
464 | 164 | if (check_alerts(this, data)) |
465 | 0 | { |
466 | 0 | this->state = ALERT_SENDING; |
467 | 0 | *type = TLS_ALERT; |
468 | 0 | return NEED_MORE; |
469 | 0 | } |
470 | 164 | } |
471 | 164 | if (this->output.len) |
472 | 37 | { |
473 | 37 | *type = this->output_type; |
474 | 37 | if (this->output.len <= TLS_MAX_FRAGMENT_LEN) |
475 | 37 | { |
476 | 37 | *data = this->output; |
477 | 37 | this->output = chunk_empty; |
478 | 37 | return NEED_MORE; |
479 | 37 | } |
480 | 0 | *data = chunk_create(this->output.ptr, TLS_MAX_FRAGMENT_LEN); |
481 | 0 | this->output = chunk_clone(chunk_skip(this->output, TLS_MAX_FRAGMENT_LEN)); |
482 | 0 | return NEED_MORE; |
483 | 37 | } |
484 | 127 | return status; |
485 | 164 | } |
486 | | |
487 | | METHOD(tls_fragmentation_t, application_finished, bool, |
488 | | private_tls_fragmentation_t *this) |
489 | 0 | { |
490 | 0 | return this->application_finished; |
491 | 0 | } |
492 | | |
493 | | METHOD(tls_fragmentation_t, destroy, void, |
494 | | private_tls_fragmentation_t *this) |
495 | 92 | { |
496 | 92 | free(this->input.ptr); |
497 | 92 | free(this->output.ptr); |
498 | 92 | free(this); |
499 | 92 | } |
500 | | |
501 | | /** |
502 | | * See header |
503 | | */ |
504 | | tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake, |
505 | | tls_alert_t *alert, tls_application_t *application, |
506 | | tls_purpose_t purpose) |
507 | 92 | { |
508 | 92 | private_tls_fragmentation_t *this; |
509 | | |
510 | 92 | INIT(this, |
511 | 92 | .public = { |
512 | 92 | .process = _process, |
513 | 92 | .build = _build, |
514 | 92 | .application_finished = _application_finished, |
515 | 92 | .destroy = _destroy, |
516 | 92 | }, |
517 | 92 | .handshake = handshake, |
518 | 92 | .alert = alert, |
519 | 92 | .state = ALERT_NONE, |
520 | 92 | .application = application, |
521 | 92 | .purpose = purpose, |
522 | 92 | ); |
523 | | |
524 | 92 | return &this->public; |
525 | 92 | } |