Coverage Report

Created: 2026-06-10 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}