From cac2158f8038cb01eb6b48b7aa4a6f5a06401daa Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Mon, 18 Sep 2017 18:46:43 +0100 Subject: [PATCH] [Minor] Add preliminary 7z support --- src/libmime/archives.c | 133 +++++++++++++++++++++++++++++++++++++++++ src/libmime/archives.h | 1 + 2 files changed, 134 insertions(+) diff --git a/src/libmime/archives.c b/src/libmime/archives.c index 2123e1131..0d24cb28f 100644 --- a/src/libmime/archives.c +++ b/src/libmime/archives.c @@ -558,6 +558,130 @@ part->flags |= RSPAMD_MIME_PART_ARCHIVE; arch->size = part->parsed_data.len; } +static inline gint +rspamd_archive_7zip_read_vint (const guchar *start, gsize remain, guint64 *res) +{ + /* + * REAL_UINT64 means real UINT64. + * UINT64 means real UINT64 encoded with the following scheme: + * + * Size of encoding sequence depends from first byte: + * First_Byte Extra_Bytes Value + * (binary) + * 0xxxxxxx : ( xxxxxxx ) + * 10xxxxxx BYTE y[1] : ( xxxxxx << (8 * 1)) + y + * 110xxxxx BYTE y[2] : ( xxxxx << (8 * 2)) + y + * ... + * 1111110x BYTE y[6] : ( x << (8 * 6)) + y + * 11111110 BYTE y[7] : y + * 11111111 BYTE y[8] : y + */ + guchar t; + + if (remain == 0) { + return -1; + } + + t = *start; + + if (!isset (&t, 7)) { + /* Trivial case */ + *res = t; + return 1; + } + else if (t == 0xFF) { + if (remain >= sizeof (guint64) + 1) { + memcpy (res, start + 1, sizeof (guint64)); + *res = GUINT64_FROM_LE (*res); + + return sizeof (guint64) + 1; + } + } + else { + gint cur_bit = 6, intlen = 1; + const guchar bmask = 0xFF; + guint64 tgt; + + while (cur_bit > 0) { + if (!isset (&t, cur_bit)) { + if (remain >= intlen + 1) { + memcpy (&tgt, start + 1, intlen); + tgt = GUINT64_FROM_LE (tgt); + /* Shift back */ + tgt >>= sizeof (tgt) - NBBY * intlen; + /* Add masked value */ + tgt += (guint64)(t & (bmask >> (NBBY - cur_bit))) + << (NBBY * intlen); + *res = tgt; + + return intlen + 1; + } + } + cur_bit --; + intlen ++; + } + } + + return -1; +} + +#define SZ_READ_VINT_SKIP() do { \ + r = rspamd_archive_7zip_read_vint (p, end - p, &vint); \ + if (r == -1) { \ + msg_debug_task ("7z archive is invalid (bad vint)"); \ + return; \ + } \ + p += r; \ +} while (0) +#define SZ_READ_VINT(var) do { \ + r = rspamd_archive_7zip_read_vint (p, end - p, &(var)); \ + if (r == -1) { \ + msg_debug_task ("7z archive is invalid (bad vint)"); \ + return; \ + } \ + p += r; \ +} while (0) + +static void +rspamd_archive_process_7zip (struct rspamd_task *task, + struct rspamd_mime_part *part) +{ + struct rspamd_archive *arch; + const guchar *p, *end; + const guchar sz_magic[] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; + guint64 section_offset = 0, section_length = 0; + gint r; + + p = part->parsed_data.begin; + end = p + part->parsed_data.len; + + if (end - p <= sizeof (guint64) + sizeof (guint32) || + memcmp (p, sz_magic, sizeof (sz_magic)) != 0) { + msg_debug_task ("7z archive is invalid (no 7z magic)"); + + return; + } + + arch = rspamd_mempool_alloc0 (task->task_pool, sizeof (*arch)); + arch->files = g_ptr_array_new (); + arch->type = RSPAMD_ARCHIVE_7ZIP; + rspamd_mempool_add_destructor (task->task_pool, rspamd_archive_dtor, + arch); + + /* Magic (6 bytes) + version (2 bytes) + crc32 (4 bytes) */ + p += sizeof (guint64) + sizeof (guint32); + + SZ_READ_VINT(section_offset); + SZ_READ_VINT(section_length); + + part->flags |= RSPAMD_MIME_PART_ARCHIVE; + part->specific.arch = arch; + if (part->cd != NULL) { + arch->archive_name = &part->cd->filename; + } + arch->size = part->parsed_data.len; +} + static gboolean rspamd_archive_cheat_detect (struct rspamd_mime_part *part, const gchar *str, const guchar *magic_start, gsize magic_len) @@ -608,6 +732,7 @@ rspamd_archives_process (struct rspamd_task *task) struct rspamd_mime_part *part; const guchar rar_magic[] = {0x52, 0x61, 0x72, 0x21, 0x1A, 0x07}; const guchar zip_magic[] = {0x50, 0x4b, 0x03, 0x04}; + const guchar sz_magic[] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; for (i = 0; i < task->parts->len; i ++) { part = g_ptr_array_index (task->parts, i); @@ -621,6 +746,11 @@ rspamd_archives_process (struct rspamd_task *task) rar_magic, sizeof (rar_magic))) { rspamd_archive_process_rar (task, part); } + else if (rspamd_archive_cheat_detect (part, "7z", + sz_magic, sizeof (sz_magic))) { + rspamd_archive_process_7zip (task, part); + } + } } } @@ -638,6 +768,9 @@ rspamd_archive_type_str (enum rspamd_archive_type type) case RSPAMD_ARCHIVE_RAR: ret = "rar"; break; + case RSPAMD_ARCHIVE_7ZIP: + ret = "7z"; + break; } return ret; diff --git a/src/libmime/archives.h b/src/libmime/archives.h index 9b64ab130..a1c4cb29f 100644 --- a/src/libmime/archives.h +++ b/src/libmime/archives.h @@ -21,6 +21,7 @@ enum rspamd_archive_type { RSPAMD_ARCHIVE_ZIP, RSPAMD_ARCHIVE_RAR, + RSPAMD_ARCHIVE_7ZIP, }; enum rspamd_archive_flags { -- 2.39.5