Skip to content

Commit 7667849

Browse files
committed
dtls.c: fix length checks in SKIP_VAR_FIELD.
Check for field length before reading the length to skip from the field. Signed-off-by: Achim Kraus <achim.kraus@cloudcoap.net>
1 parent 62975bf commit 7667849

1 file changed

Lines changed: 44 additions & 19 deletions

File tree

dtls.c

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -178,16 +178,39 @@ memarray_t dtlscontext_storage;
178178
#define HANDSHAKE(M) ((dtls_handshake_header_t *)((M) + DTLS_RH_LENGTH))
179179
#define CLIENTHELLO(M) ((dtls_client_hello_t *)((M) + HS_HDR_LENGTH))
180180

181-
/* The length check here should work because dtls_*_to_int() works on
182-
* unsigned char. Otherwise, broken messages could cause severe
183-
* trouble. Note that this macro jumps out of the current program flow
184-
* when the message is too short. Beware!
181+
/*
182+
* Skip variable length field.
183+
*
184+
* A variable length field is encoded with a preceding length followed by
185+
* the value. That length itself is encoded in one to three bytes using uint8,
186+
* uint16, or uint24. Decoding a variable length field requires to check first,
187+
* if the length itself is within the bounds, and if so, if the value is also
188+
* within the bounds.
189+
*
190+
* The macro "returns" the calling context with an error when the bounds are
191+
* violated.
192+
*
193+
* \param P pointer to length of the var field. Will be forwarded the end of
194+
* the var field.
195+
* \param L left overall data of P. Will be reduced by the size of the var
196+
* field.
197+
* \param T length type. e.g. uint8 or uint16
198+
* \param A alert description in case of a length violation
199+
* \param M logging message in case of a length violation
185200
*/
186-
#define SKIP_VAR_FIELD(P,L,T) { \
187-
if (L < dtls_ ## T ## _to_int(P) + sizeof(T)) \
188-
goto error; \
189-
L -= dtls_ ## T ## _to_int(P) + sizeof(T); \
190-
P += dtls_ ## T ## _to_int(P) + sizeof(T); \
201+
#define SKIP_VAR_FIELD(P, L, T, A, M) { \
202+
size_t skip_length = sizeof(T); \
203+
if (L < skip_length) { \
204+
dtls_info("%s: field length exceeds buffer", M); \
205+
return dtls_alert_fatal_create(A); \
206+
} \
207+
skip_length += dtls_ ## T ## _to_int(P); \
208+
if (L < skip_length) { \
209+
dtls_info("%s: field value exceeds buffer", M); \
210+
return dtls_alert_fatal_create(A); \
211+
} \
212+
L -= skip_length; \
213+
P += skip_length; \
191214
}
192215

193216
/*
@@ -419,16 +442,15 @@ dtls_get_cookie(uint8 *msg, size_t msglen, uint8 **cookie) {
419442
msglen -= DTLS_HS_LENGTH + DTLS_CH_LENGTH;
420443
msg += DTLS_HS_LENGTH + DTLS_CH_LENGTH;
421444

422-
SKIP_VAR_FIELD(msg, msglen, uint8); /* skip session id */
445+
/* skip session id */
446+
SKIP_VAR_FIELD(msg, msglen, uint8, DTLS_ALERT_HANDSHAKE_FAILURE,
447+
"get_cookie, session_id");
423448

424449
if (msglen < (*msg & 0xff) + sizeof(uint8))
425450
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
426451

427452
*cookie = msg + sizeof(uint8);
428453
return dtls_uint8_to_int(msg);
429-
430-
error:
431-
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
432454
}
433455

434456
static int
@@ -1306,8 +1328,12 @@ dtls_update_parameters(dtls_context_t *ctx,
13061328
data_length -= DTLS_RANDOM_LENGTH;
13071329

13081330
/* Caution: SKIP_VAR_FIELD may jump to error: */
1309-
SKIP_VAR_FIELD(data, data_length, uint8); /* skip session id */
1310-
SKIP_VAR_FIELD(data, data_length, uint8); /* skip cookie */
1331+
/* skip session_id */
1332+
SKIP_VAR_FIELD(data, data_length, uint8, DTLS_ALERT_HANDSHAKE_FAILURE,
1333+
"update_parameters, session_id");
1334+
/* skip cookie */
1335+
SKIP_VAR_FIELD(data, data_length, uint8, DTLS_ALERT_HANDSHAKE_FAILURE,
1336+
"update_parameters, cookie");
13111337

13121338
if (data_length < sizeof(uint16)) {
13131339
dtls_debug("cipher suites length exceeds record\n");
@@ -3247,7 +3273,9 @@ check_server_hello(dtls_context_t *ctx,
32473273
data += DTLS_RANDOM_LENGTH;
32483274
data_length -= DTLS_RANDOM_LENGTH;
32493275

3250-
SKIP_VAR_FIELD(data, data_length, uint8); /* skip session id */
3276+
/* skip session_id */
3277+
SKIP_VAR_FIELD(data, data_length, uint8, DTLS_ALERT_DECODE_ERROR,
3278+
"ServerHello, session_id");
32513279
/*
32523280
* Need to re-check in case session id was not empty
32533281
* 2 bytes for the selected cipher suite
@@ -3282,9 +3310,6 @@ check_server_hello(dtls_context_t *ctx,
32823310
/* Server may not support extended master secret */
32833311
handshake->extended_master_secret = 0;
32843312
return dtls_check_tls_extension(peer, data, data_length, 0);
3285-
3286-
error:
3287-
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
32883313
}
32893314

32903315
static int

0 commit comments

Comments
 (0)