mirror of
https://github.com/irssi/irssi.git
synced 2026-08-08 19:30:15 +02:00
otr: cap reassembled multi-fragment message size (remote DoS)
enqueue_otr_fragment() reassembles ?OTR: multi-fragment OTR messages into a per-peer heap buffer (opc->full_msg), grown by realloc on every fragment that does not end with the OTR end tag '.'. The buffer has no total size limit. A remote IRC user can open a reassembly by sending a query PRIVMSG whose body begins with ?OTR: and lacks the trailing '.', then keep sending further PRIVMSGs: each is appended to opc->full_msg (roughly one IRC-line-length worth of bytes per fragment) with no bound, growing it until the victim irssi is OOM-killed. No OTR session is required -- the peer context is created lazily by otr_find_context(..., create=1) on first contact in otr_receive(), and sig_message_private (signal_add_first on "message private") feeds every query PRIVMSG straight to otr_receive()/enqueue_otr_fragment(). Add an OTR_REASM_MAX_SIZE (256 KiB) cap on the total reassembled size. When appending a fragment would exceed it, free and reset the reassembly state and return OTR_MSG_ERROR, on both the subsequent-fragment and initial-fragment paths. 256 KiB is far above any legitimate OTR message, so there is no functional regression.
This commit is contained in:
parent
27f857e37b
commit
a5bd495733
2 changed files with 24 additions and 0 deletions
|
|
@ -615,6 +615,18 @@ static enum otr_msg_status enqueue_otr_fragment(const char *msg, struct otr_peer
|
|||
}
|
||||
|
||||
if (opc->full_msg) {
|
||||
/* Reject the reassembly if appending this fragment would exceed
|
||||
* the maximum assembled size. Without this check a peer can keep
|
||||
* the reassembly open forever (fragments never end with '.') and
|
||||
* grow opc->full_msg without bound, exhausting memory. */
|
||||
if (opc->msg_len + msg_len + 1 > OTR_REASM_MAX_SIZE) {
|
||||
free(opc->full_msg);
|
||||
opc->full_msg = NULL;
|
||||
opc->msg_size = opc->msg_len = 0;
|
||||
ret = OTR_MSG_ERROR;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Grow the buffer unless it already has room for msg_len bytes
|
||||
* *plus* the NUL terminator written below. Using '>' here (instead
|
||||
* of '>=') is an off-by-one: when msg_len exactly equals the
|
||||
|
|
@ -674,6 +686,12 @@ static enum otr_msg_status enqueue_otr_fragment(const char *msg, struct otr_peer
|
|||
*/
|
||||
pos = strstr(msg, OTR_MSG_BEGIN_TAG);
|
||||
if (pos && (pos == msg) && msg[msg_len - 1] != OTR_MSG_END_TAG) {
|
||||
/* Reject oversized initial fragment. */
|
||||
if ((msg_len * 2) + 1 > OTR_REASM_MAX_SIZE) {
|
||||
ret = OTR_MSG_ERROR;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Allocate full message buffer with an extra for NULL byte. */
|
||||
opc->full_msg = g_new0(char, (msg_len * 2) + 1);
|
||||
if (!opc->full_msg) {
|
||||
|
|
|
|||
|
|
@ -54,6 +54,12 @@
|
|||
#define OTR_MSG_BEGIN_TAG "?OTR:"
|
||||
#define OTR_MSG_END_TAG '.'
|
||||
|
||||
/* Maximum total size of a reassembled multi-fragment OTR message. Incoming
|
||||
* ?OTR: fragments are concatenated into a per-peer heap buffer until the
|
||||
* end tag is received; this caps that buffer so a peer cannot grow it
|
||||
* without bound by sending fragments that never terminate. */
|
||||
#define OTR_REASM_MAX_SIZE (256 * 1024)
|
||||
|
||||
/* IRC /me command marker and len. */
|
||||
#define OTR_IRC_MARKER_ME "/me "
|
||||
#define OTR_IRC_MARKER_ME_LEN sizeof(OTR_IRC_MARKER_ME) - 1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue