aboutsummaryrefslogtreecommitdiffstats
path: root/src/libmime/archives.c
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2017-09-18 18:46:43 +0100
committerVsevolod Stakhov <vsevolod@highsecure.ru>2017-09-18 18:46:43 +0100
commitcac2158f8038cb01eb6b48b7aa4a6f5a06401daa (patch)
tree000d7d157016205b97cef53b070dba98a1811295 /src/libmime/archives.c
parent3e23c6b1128023a94a440b118eabf8df5b33090f (diff)
downloadrspamd-cac2158f8038cb01eb6b48b7aa4a6f5a06401daa.tar.gz
rspamd-cac2158f8038cb01eb6b48b7aa4a6f5a06401daa.zip
[Minor] Add preliminary 7z support
Diffstat (limited to 'src/libmime/archives.c')
-rw-r--r--src/libmime/archives.c133
1 files changed, 133 insertions, 0 deletions
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;