]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Minor] Add preliminary 7z support
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 18 Sep 2017 17:46:43 +0000 (18:46 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 18 Sep 2017 17:46:43 +0000 (18:46 +0100)
src/libmime/archives.c
src/libmime/archives.h

index 2123e1131e6c63abf37c96d0f352f73adab57480..0d24cb28fd328ea61a95d5cbd0c382d5e7cb7ce7 100644 (file)
@@ -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;
index 9b64ab130b332519780e6a314774766853219849..a1c4cb29f97c4703764da77f67676c35701f8175 100644 (file)
@@ -21,6 +21,7 @@
 enum rspamd_archive_type {
        RSPAMD_ARCHIVE_ZIP,
        RSPAMD_ARCHIVE_RAR,
+       RSPAMD_ARCHIVE_7ZIP,
 };
 
 enum rspamd_archive_flags {