]> source.dussan.org Git - rspamd.git/commitdiff
[Feature] Core: Add support for gzip archives
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Thu, 27 Dec 2018 15:41:44 +0000 (15:41 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Thu, 27 Dec 2018 15:41:44 +0000 (15:41 +0000)
src/libmime/archives.c
src/libmime/archives.h

index 1f9a5c6345ddc44baf631882d4aad4ff00ba3153..183232e6f6d957e1750467d03e8d384ffc3a6950 100644 (file)
@@ -652,7 +652,7 @@ rspamd_archive_7zip_read_vint (const guchar *start, gsize remain, guint64 *res)
                msg_debug_task ("7zip archive is invalid (bad vint): %s", G_STRLOC); \
                return; \
        } \
-       n = *(guint64 *)p; \
+       memcpy (&(n), p, sizeof (guint64)); \
        n = GUINT64_FROM_LE(n); \
        p += sizeof (guint64); \
 } while (0)
@@ -1454,6 +1454,147 @@ rspamd_archive_process_7zip (struct rspamd_task *task,
        arch->size = part->parsed_data.len;
 }
 
+static void
+rspamd_archive_process_gzip (struct rspamd_task *task,
+                                                        struct rspamd_mime_part *part) {
+       struct rspamd_archive *arch;
+       const guchar *start, *p, *end;
+       const guchar gz_magic[] = {0x1F, 0x8B};
+       guchar flags;
+
+       start = part->parsed_data.begin;
+       p = start;
+       end = p + part->parsed_data.len;
+
+       if (end - p <= 10 || memcmp (p, gz_magic, sizeof (gz_magic)) != 0) {
+               msg_debug_task ("gzip archive is invalid (no gzip magic)");
+
+               return;
+       }
+
+       arch = rspamd_mempool_alloc0 (task->task_pool, sizeof (*arch));
+       arch->files = g_ptr_array_sized_new (1);
+       arch->type = RSPAMD_ARCHIVE_GZIP;
+       rspamd_mempool_add_destructor (task->task_pool, rspamd_archive_dtor,
+                       arch);
+
+       flags = p[3];
+
+       if (flags & (1u << 5)) {
+               arch->flags |= RSPAMD_ARCHIVE_ENCRYPTED;
+       }
+
+       if (flags & (1u << 3)) {
+               /* We have file name presented in archive, try to use it */
+               if (flags & (1u << 1)) {
+                       /* Multipart */
+                       p += 12;
+               }
+               else {
+                       p += 10;
+               }
+
+               if (flags & (1u << 2)) {
+                       /* Optional section */
+                       guint16 optlen = 0;
+
+                       RAR_READ_UINT16 (optlen);
+
+                       if (end <= p + optlen) {
+                               msg_debug_task ("gzip archive is invalid, bad extra length: %d",
+                                               (int)optlen);
+
+                               return;
+                       }
+
+                       p += optlen;
+               }
+
+               /* Read file name */
+               const guchar *fname_start = p;
+
+               while (p < end) {
+                       if (*p == '\0') {
+                               if (p > fname_start) {
+                                       struct rspamd_archive_file *f;
+
+                                       f = g_malloc0 (sizeof (*f));
+                                       f->fname = g_string_new (fname_start);
+
+                                       g_ptr_array_add (arch->files, f);
+
+                                       goto set;
+                               }
+                       }
+                       else if (!g_ascii_isgraph (*p)) {
+                               msg_debug_task ("gzip archive is invalid, bad filename at pos %d",
+                                               (int)(p - start));
+
+                               return;
+                       }
+
+                       p ++;
+               }
+
+               /* Wrong filename, not zero terminated */
+               msg_debug_task ("gzip archive is invalid, bad filename at pos %d",
+                               (int)(p - start));
+
+               return;
+       }
+
+       /* Fallback, we need to extract file name from archive name if possible */
+       if (part->cd->filename.len > 0) {
+               const gchar *dot_pos, *slash_pos;
+
+               dot_pos = rspamd_memrchr (part->cd->filename.begin, '.',
+                               part->cd->filename.len);
+
+               if (dot_pos) {
+                       struct rspamd_archive_file *f;
+
+                       slash_pos = rspamd_memrchr (part->cd->filename.begin, '/',
+                                       part->cd->filename.len);
+
+                       if (slash_pos && slash_pos < dot_pos) {
+                               f = g_malloc0 (sizeof (*f));
+                               f->fname = g_string_sized_new (dot_pos - slash_pos);
+                               g_string_append_len (f->fname, slash_pos + 1,
+                                               dot_pos - slash_pos - 1);
+
+                               g_ptr_array_add (arch->files, f);
+
+                               goto set;
+                       }
+                       else {
+                               const gchar *fname_start = part->cd->filename.begin;
+
+                               f = g_malloc0 (sizeof (*f));
+                               f->fname = g_string_sized_new (dot_pos - slash_pos);
+                               g_string_append_len (f->fname, fname_start,
+                                               dot_pos - fname_start);
+
+                               g_ptr_array_add (arch->files, f);
+
+                               goto set;
+                       }
+               }
+       }
+
+       return;
+
+set:
+       /* Set archive data */
+       part->flags |= RSPAMD_MIME_PART_ARCHIVE;
+       part->specific.arch = arch;
+
+       if (part->cd) {
+               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)
@@ -1527,6 +1668,7 @@ rspamd_archives_process (struct rspamd_task *task)
        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};
+       const guchar gz_magic[] = {0x1F, 0x8B};
 
        for (i = 0; i < task->parts->len; i ++) {
                part = g_ptr_array_index (task->parts, i);
@@ -1536,13 +1678,19 @@ rspamd_archives_process (struct rspamd_task *task)
                                if (rspamd_archive_cheat_detect (part, "zip",
                                                zip_magic, sizeof (zip_magic))) {
                                        rspamd_archive_process_zip (task, part);
-                               } else if (rspamd_archive_cheat_detect (part, "rar",
+                               }
+                               else if (rspamd_archive_cheat_detect (part, "rar",
                                                rar_magic, sizeof (rar_magic))) {
                                        rspamd_archive_process_rar (task, part);
-                               } else if (rspamd_archive_cheat_detect (part, "7z",
+                               }
+                               else if (rspamd_archive_cheat_detect (part, "7z",
                                                sz_magic, sizeof (sz_magic))) {
                                        rspamd_archive_process_7zip (task, part);
                                }
+                               else if (rspamd_archive_cheat_detect (part, "gz",
+                                               gz_magic, sizeof (gz_magic))) {
+                                       rspamd_archive_process_gzip (task, part);
+                               }
 
                                if (IS_CT_TEXT (part->ct) &&
                                                (part->flags & RSPAMD_MIME_PART_ARCHIVE)) {
@@ -1571,6 +1719,9 @@ rspamd_archive_type_str (enum rspamd_archive_type type)
        case RSPAMD_ARCHIVE_7ZIP:
                ret = "7z";
                break;
+       case RSPAMD_ARCHIVE_GZIP:
+               ret = "gz";
+               break;
        }
 
        return ret;
index a1c4cb29f97c4703764da77f67676c35701f8175..9ea1b28e1b88bf7c8b4bcc0e92add588db7a3e36 100644 (file)
@@ -22,6 +22,7 @@ enum rspamd_archive_type {
        RSPAMD_ARCHIVE_ZIP,
        RSPAMD_ARCHIVE_RAR,
        RSPAMD_ARCHIVE_7ZIP,
+       RSPAMD_ARCHIVE_GZIP,
 };
 
 enum rspamd_archive_flags {