>From 9f7f0c94b67e9506ebf8ca674dd6cbb6a7989f44 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Wed, 24 Nov 2021 16:57:00 +0100 Subject: [PATCH] CVE-2021-3657: security fixes unlike in the 1.4 branch, we use signed ints for offsets and lengths, so many of the qualifying statements from the 1.4 series don't apply. --- src/drv_imap.c | 9 +++++++++ src/drv_maildir.c | 8 +++++++- src/socket.c | 8 ++++++-- src/sync.c | 15 ++++++++++----- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/drv_imap.c b/src/drv_imap.c index 4cc3b2a..0d6c869 100644 --- a/src/drv_imap.c +++ b/src/drv_imap.c @@ -780,6 +780,11 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts ) bytes = cur->len = strtol( s + 1, &s, 10 ); if (*s != '}' || *++s) goto bail; + if ((uint)bytes >= INT_MAX) { + error( "IMAP error: excessively large literal from %s " + "- THIS MIGHT BE AN ATTEMPT TO HACK YOU!\n", ctx->conn.name ); + goto bail; + } s = cur->val = nfmalloc( cur->len + 1 ); s[cur->len] = 0; @@ -1279,6 +1284,10 @@ parse_list_rsp_p2( imap_store_t *ctx, list_t *list, char *cmd ATTR_UNUSED ) } arg = list->val; argl = list->len; + if (argl > 1000) { + warn( "IMAP warning: ignoring unreasonably long mailbox name '%.100s[...]'\n", arg ); + goto skip; + } if ((l = strlen( ctx->prefix ))) { if (!starts_with( arg, argl, ctx->prefix, l )) { if (is_inbox( ctx, arg, argl )) { diff --git a/src/drv_maildir.c b/src/drv_maildir.c index e1622d3..40a4d2a 100644 --- a/src/drv_maildir.c +++ b/src/drv_maildir.c @@ -1147,7 +1147,8 @@ maildir_scan( maildir_store_t *ctx, msg_t_array_alloc_t *msglist ) } goto retry; } - entry->size = st.st_size; + // The clipped value is good enough for MaxSize comparisons. + entry->size = st.st_size > INT_MAX ? INT_MAX : (int)st.st_size; } if (want_tuid || want_msgid) { if (!(f = fopen( buf, "r" ))) { @@ -1534,12 +1535,17 @@ maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data, } } fstat( fd, &st ); + if (st.st_size > INT_MAX) { + error( "Maildir error: %s is too big", buf ); + goto mbad; + } data->len = st.st_size; if (data->date == -1) data->date = st.st_mtime; data->data = nfmalloc( data->len ); if (read( fd, data->data, data->len ) != data->len) { sys_error( "Maildir error: cannot read %s", buf ); + mbad: close( fd ); cb( DRV_MSG_BAD, aux ); return; diff --git a/src/socket.c b/src/socket.c index 84449e7..d8548a9 100644 --- a/src/socket.c +++ b/src/socket.c @@ -898,6 +898,8 @@ do_append( conn_t *conn, buff_chunk_t *bc ) /* This is big enough to avoid excessive chunking, but is * sufficiently small to keep SSL latency low with a slow uplink. */ #define WRITE_CHUNK_SIZE 1024 +// Huge data blocks (message payloads) are forcibly chunked. +#define MAX_WRITE_CHUNK_SIZE (1 << 30) static void do_flush( conn_t *conn ) @@ -952,7 +954,8 @@ do_flush( conn_t *conn ) void socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt ) { - int i, buf_avail, len, offset = 0, total = 0; + int i, buf_avail, len, offset = 0; + uint total = 0; buff_chunk_t *bc; for (i = 0; i < iovcnt; i++) @@ -971,7 +974,8 @@ socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt ) * predict a reasonable output buffer size anyway - deflatePending() does * not account for consumed but not yet compressed input, and adding up * the deflateBound()s would be a tad *too* pessimistic. */ - buf_avail = total > WRITE_CHUNK_SIZE ? total : WRITE_CHUNK_SIZE; + buf_avail = total > MAX_WRITE_CHUNK_SIZE ? MAX_WRITE_CHUNK_SIZE : + total > WRITE_CHUNK_SIZE ? total : WRITE_CHUNK_SIZE; bc = nfmalloc( offsetof(buff_chunk_t, data) + buf_avail ); bc->len = 0; #ifndef HAVE_LIBZ diff --git a/src/sync.c b/src/sync.c index 7d3fe79..46e089c 100644 --- a/src/sync.c +++ b/src/sync.c @@ -333,7 +333,7 @@ copy_msg_bytes( char **out_ptr, const char *in_buf, int *in_idx, int in_len, int } static int -copy_msg_convert( int in_cr, int out_cr, copy_vars_t *vars ) +copy_msg_convert( int in_cr, int out_cr, copy_vars_t *vars, int t ) { char *in_buf = vars->data.data; int in_len = vars->data.len; @@ -361,7 +361,8 @@ copy_msg_convert( int in_cr, int out_cr, copy_vars_t *vars ) goto nloop; } } - /* invalid message */ + warn( "Warning: message %u from %s has incomplete header; skipping.\n", + vars->msg->uid, str_ms[1-t] ); free( in_buf ); return 0; oke: @@ -382,6 +383,12 @@ copy_msg_convert( int in_cr, int out_cr, copy_vars_t *vars ) } vars->data.len = in_len + extra; + if ((uint)vars->data.len > INT_MAX) { + warn( "Warning: message %u from %s is too big after conversion; skipping.\n", + vars->msg->uid, str_ms[1-t] ); + free( in_buf ); + return 0; + } char *out_buf = vars->data.data = nfmalloc( vars->data.len ); idx = 0; if (vars->srec) { @@ -423,9 +430,7 @@ msg_fetched( int sts, void *aux ) scr = (svars->drv[1-t]->get_caps( svars->ctx[1-t] ) / DRV_CRLF) & 1; tcr = (svars->drv[t]->get_caps( svars->ctx[t] ) / DRV_CRLF) & 1; if (vars->srec || scr != tcr) { - if (!copy_msg_convert( scr, tcr, vars )) { - warn( "Warning: message %u from %s has incomplete header.\n", - vars->msg->uid, str_ms[1-t] ); + if (!copy_msg_convert( scr, tcr, vars, t )) { vars->cb( SYNC_NOGOOD, 0, vars ); return; } -- 2.33.1.11.g2e4d00c830