From 88151d3eb78e04d4d4f9a778569a5d2339e6280e Mon Sep 17 00:00:00 2001 From: Jon Shallow Date: Mon, 15 Jun 2020 10:25:43 +0100 Subject: [PATCH] TCP: Prevent incorrectly formed PDUs causing RAM exhaustion. src/net.c: Add in a sanity check that the PDU length is not more that the negotiated CSM size for incoming data. Track CSM remote supports block transfer. Do not allow Block options if CSM has not negotiated block transfer. src/coap_session.c: Include block-wise-transfer is supported in the CSM include/coap2/coap_session.h: Make MTU size bigger than 32 bits as CSM mtu size can be greater than 32 bits. Track CSM remote supports block transfer. include/coap2/pdu.h: Make COAP_DEFAULT_MAX_PDU_RX_SIZE a unsigned long to be bigger than 32 bits. --- include/coap2/coap_session.h | 3 ++- include/coap2/pdu.h | 4 ++-- src/coap_session.c | 5 ++++- src/net.c | 29 ++++++++++++++++++++++++++++- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/include/coap2/coap_session.h b/include/coap2/coap_session.h index 039965e3..afb37bef 100644 --- a/include/coap2/coap_session.h +++ b/include/coap2/coap_session.h @@ -63,7 +63,7 @@ typedef struct coap_session_t { coap_session_state_t state; /**< current state of relationaship with peer */ unsigned ref; /**< reference count from queues */ unsigned tls_overhead; /**< overhead of TLS layer */ - unsigned mtu; /**< path or CSM mtu */ + uint64_t mtu; /**< path or CSM mtu */ coap_address_t local_if; /**< optional local interface address */ UT_hash_handle hh; coap_addr_tuple_t addr_info; /**< key: remote/local address info */ @@ -74,6 +74,7 @@ typedef struct coap_session_t { void *tls; /**< security parameters */ uint16_t tx_mid; /**< the last message id that was used in this session */ uint8_t con_active; /**< Active CON request sent */ + uint8_t csm_block_supported; /**< CSM TCP blocks supported */ coap_tid_t last_ping_mid; /**< the last keepalive message id that was used in this session */ struct coap_queue_t *delayqueue; /**< list of delayed messages waiting to be sent */ size_t partial_write; /**< if > 0 indicates number of bytes already written from the pdu at the head of sendqueue */ diff --git a/include/coap2/pdu.h b/include/coap2/pdu.h index 6b4d9aef..2487bc31 100644 --- a/include/coap2/pdu.h +++ b/include/coap2/pdu.h @@ -45,10 +45,10 @@ struct coap_session_t; #ifndef COAP_DEFAULT_MAX_PDU_RX_SIZE #if defined(WITH_CONTIKI) || defined(WITH_LWIP) -#define COAP_DEFAULT_MAX_PDU_RX_SIZE (COAP_MAX_MESSAGE_SIZE_TCP16+4) +#define COAP_DEFAULT_MAX_PDU_RX_SIZE (COAP_MAX_MESSAGE_SIZE_TCP16+4UL) #else /* 8 MiB max-message-size plus some space for options */ -#define COAP_DEFAULT_MAX_PDU_RX_SIZE (8*1024*1024+256) +#define COAP_DEFAULT_MAX_PDU_RX_SIZE (8UL*1024*1024+256) #endif #endif /* COAP_DEFAULT_MAX_PDU_RX_SIZE */ diff --git a/src/coap_session.c b/src/coap_session.c index 17f12613..cc8ecc50 100644 --- a/src/coap_session.c +++ b/src/coap_session.c @@ -303,11 +303,14 @@ void coap_session_send_csm(coap_session_t *session) { session->partial_write = 0; if (session->mtu == 0) session->mtu = COAP_DEFAULT_MTU; /* base value */ - pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_SIGNALING_CSM, 0, 16); + pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_SIGNALING_CSM, 0, 20); if ( pdu == NULL || coap_add_option(pdu, COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE, coap_encode_var_safe(buf, sizeof(buf), COAP_DEFAULT_MAX_PDU_RX_SIZE), buf) == 0 + || coap_add_option(pdu, COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, + coap_encode_var_safe(buf, sizeof(buf), + 0), buf) == 0 || coap_pdu_encode_header(pdu, session->proto) == 0 ) { coap_session_disconnected(session, COAP_NACK_NOT_DELIVERABLE); diff --git a/src/net.c b/src/net.c index acfc93d4..74e233da 100644 --- a/src/net.c +++ b/src/net.c @@ -997,6 +997,25 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { goto error; } +#if !COAP_DISABLE_TCP + if (COAP_PROTO_RELIABLE(session->proto) && !session->csm_block_supported) { + /* + * Need to check that this instance is not sending any block options as the + * remote end via CSM has not informed us that there is support + * https://tools.ietf.org/html/rfc8323#section-5.3.2 + * Note that this also includes BERT which is application specific. + */ + coap_opt_iterator_t opt_iter; + + if (coap_check_option(pdu, COAP_OPTION_BLOCK1, &opt_iter) != NULL) { + goto error; + } + if (coap_check_option(pdu, COAP_OPTION_BLOCK2, &opt_iter) != NULL) { + goto error; + } + } +#endif /* !COAP_DISABLE_TCP */ + bytes_written = coap_send_pdu( session, pdu, NULL ); if (bytes_written == COAP_PDU_DELAYED) { @@ -1331,6 +1350,14 @@ coap_read_session(coap_context_t *ctx, coap_session_t *session, coap_tick_t now) if (n == len) { size_t size = coap_pdu_parse_size(session->proto, session->read_header, hdr_size); + if (size > COAP_DEFAULT_MAX_PDU_RX_SIZE) { + coap_log(LOG_WARNING, + "** %s: incoming PDU length too large (%zu > %lu)\n", + coap_session_str(session), + size, COAP_DEFAULT_MAX_PDU_RX_SIZE); + bytes_read = -1; + break; + } session->partial_pdu = coap_pdu_init(0, 0, 0, size); if (session->partial_pdu == NULL) { bytes_read = -1; @@ -2386,7 +2413,7 @@ handle_signaling(coap_context_t *context, coap_session_t *session, coap_session_set_mtu(session, coap_decode_var_bytes(coap_opt_value(option), coap_opt_length(option))); } else if (opt_iter.type == COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER) { - /* ... */ + session->csm_block_supported = 1; } } if (session->state == COAP_SESSION_STATE_CSM)