dtls.c: fix length checks in check_certificate_request.

Add GET_VAR_FIELD.

Signed-off-by: Achim Kraus <achim.kraus@cloudcoap.net>
This commit is contained in:
Achim Kraus
2023-04-28 17:00:17 +02:00
parent 3159ca6065
commit 62975bfe65

74
dtls.c
View File

@@ -190,6 +190,41 @@ memarray_t dtlscontext_storage;
P += dtls_ ## T ## _to_int(P) + sizeof(T); \
}
/*
* Get variable length field.
*
* A variable length field is encoded with a preceding length followed by
* the value. That length itself is encoded in one to three bytes using uint8,
* uint16, or uint24. Decoding a variable length field requires to check first,
* if the length itself is within the bounds, and if so, if the value is also
* within the bounds.
*
* The macro "returns" the calling context with an error when the bounds are
* violated.
*
* \param VL value length, variable to assign the length of the field value.
* \param P pointer to length of the var field. Will be forwarded to the
* value of the field.
* \param L left overall data of P. Will be reduced by the size of the field
* length type
* \param T field length type. e.g. uint8 or uint16
* \param A alert description in case of a length violation
* \param M logging message in case of a length violation
*/
#define GET_VAR_FIELD(VL, P, L, T, A, M) { \
if (L < sizeof(T)) { \
dtls_info("%s: field length exceeds buffer", M); \
return dtls_alert_fatal_create(A); \
} \
VL = dtls_ ## T ## _to_int(P); \
L -= sizeof(T); \
P += sizeof(T); \
if (L < VL) { \
dtls_info("%s: field value exceeds buffer", M); \
return dtls_alert_fatal_create(A); \
} \
}
/* some constants for the PRF */
#define PRF_LABEL(Label) prf_label_##Label
#define PRF_LABEL_SIZE(Label) (sizeof(PRF_LABEL(Label)) - 1)
@@ -3470,39 +3505,37 @@ check_certificate_request(dtls_context_t *ctx,
assert(is_key_exchange_ecdhe_ecdsa(peer->handshake_params->cipher_index));
data += DTLS_HS_LENGTH;
if (data_length < DTLS_HS_LENGTH + 5) {
dtls_alert("the packet length does not match the expected\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
i = dtls_uint8_to_int(data);
data += sizeof(uint8);
if (i + 1 > data_length) {
dtls_alert("the certificate types are too long\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
data += DTLS_HS_LENGTH;
data_length -= DTLS_HS_LENGTH;
GET_VAR_FIELD(i, data, data_length, uint8, DTLS_ALERT_DECODE_ERROR,
"CertificateRequest, certificate_types");
auth_alg = 0;
for (; i > 0 ; i -= sizeof(uint8)) {
if (dtls_uint8_to_int(data) == TLS_CLIENT_CERTIFICATE_TYPE_ECDSA_SIGN
&& auth_alg == 0)
auth_alg = dtls_uint8_to_int(data);
if (dtls_uint8_to_int(data) == TLS_CLIENT_CERTIFICATE_TYPE_ECDSA_SIGN) {
auth_alg = TLS_CLIENT_CERTIFICATE_TYPE_ECDSA_SIGN;
/* skip the rest of the field value */
data += i;
data_length -=i;
break;
}
data += sizeof(uint8);
data_length -= sizeof(uint8);
}
if (auth_alg != TLS_CLIENT_CERTIFICATE_TYPE_ECDSA_SIGN) {
dtls_alert("the request authentication algorithm is not supproted\n");
dtls_alert("the request authentication algorithm is not supported\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
i = dtls_uint16_to_int(data);
data += sizeof(uint16);
if (i + 1 > data_length) {
dtls_alert("the signature and hash algorithm list is too long\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
GET_VAR_FIELD(i, data, data_length, uint16, DTLS_ALERT_DECODE_ERROR,
"CertificateRequest, signature_algorithms");
hash_alg = 0;
sig_alg = 0;
@@ -3513,7 +3546,7 @@ check_certificate_request(dtls_context_t *ctx,
dtls_alert("illegal certificate request\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
for (; i >= sizeof(uint16); i -= sizeof(uint16)) {
int current_hash_alg;
int current_sig_alg;
@@ -3523,10 +3556,13 @@ check_certificate_request(dtls_context_t *ctx,
current_sig_alg = dtls_uint8_to_int(data);
data += sizeof(uint8);
data_length -= sizeof(uint16);
if (current_hash_alg == TLS_EXT_SIG_HASH_ALGO_SHA256 && hash_alg == 0 &&
current_sig_alg == TLS_EXT_SIG_HASH_ALGO_ECDSA && sig_alg == 0) {
hash_alg = current_hash_alg;
sig_alg = current_sig_alg;
break;
}
}