File clamav-zipbomb.patch of Package clamav.13406

--- libclamav/unzip.c.orig
+++ libclamav/unzip.c
@@ -54,6 +54,8 @@
 #define UNZIP_PRIVATE
 #include "unzip.h"
 
+#define ZIP_MAX_NUM_OVERLAPPING_FILES 5
+
 #define ZIP_CRC32(r,c,b,l)			\
     do {					\
 	r = crc32(~c,b,l);			\
@@ -500,7 +502,7 @@ static inline int zdecrypt(const uint8_t
     return CL_SUCCESS;
 }
 
-static unsigned int lhdr(fmap_t *map, uint32_t loff,uint32_t zsize, unsigned int *fu, unsigned int fc, const uint8_t *ch, int *ret, cli_ctx *ctx, char *tmpd, int detect_encrypted, zip_cb zcb) {
+static unsigned int lhdr(fmap_t *map, uint32_t loff,uint32_t zsize, unsigned int *fu, unsigned int fc, const uint8_t *ch, int *ret, cli_ctx *ctx, char *tmpd, int detect_encrypted, zip_cb zcb, uint32_t *file_local_header_size, uint32_t* file_local_data_size) {
   const uint8_t *lh, *zip;
   char name[256];
   uint32_t csize, usize;
@@ -581,6 +583,11 @@ static unsigned int lhdr(fmap_t *map, ui
   zip+=LH_elen;
   zsize-=LH_elen;
 
+  if (NULL != file_local_header_size)
+	  *file_local_header_size = zip - lh;
+  if (NULL != file_local_data_size)
+	  *file_local_data_size = csize;
+
   if (!csize) { /* FIXME: what's used for method0 files? csize or usize? Nothing in the specs, needs testing */
       cli_dbgmsg("cli_unzip: lh - skipping empty file\n");
   } else {
@@ -624,12 +631,19 @@ static unsigned int lhdr(fmap_t *map, ui
   return zip-lh;
 }
 
-static unsigned int chdr(fmap_t *map, uint32_t coff, uint32_t zsize, unsigned int *fu, unsigned int fc, int *ret, cli_ctx *ctx, char *tmpd, struct zip_requests *requests) {
+static unsigned int chdr(fmap_t *map, uint32_t coff, uint32_t zsize, unsigned int *fu, unsigned int fc, int *ret, cli_ctx *ctx, char *tmpd, struct zip_requests *requests, uint32_t *file_local_offset, uint32_t *file_local_header_size, uint32_t *file_local_data_size) {
   char name[256];
   int last = 0;
   const uint8_t *ch;
   int virus_found = 0;
 
+  if (NULL != file_local_offset)
+	  *file_local_offset = 0;
+  if (NULL != file_local_header_size)
+	  *file_local_header_size = 0;
+  if (NULL != file_local_data_size)
+	  *file_local_data_size = 0;
+
   if(!(ch = fmap_need_off(map, coff, SIZEOF_CH)) || CH_magic != 0x02014b50) {
       if(ch) fmap_unneed_ptr(map, ch, SIZEOF_CH);
       cli_dbgmsg("cli_unzip: ch - wrkcomplete\n");
@@ -674,7 +688,9 @@ static unsigned int chdr(fmap_t *map, ui
 
   if (!requests) {
       if(CH_off<zsize-SIZEOF_LH) {
-          lhdr(map, CH_off, zsize-CH_off, fu, fc, ch, ret, ctx, tmpd, 1, zip_scan_cb);
+		  if (NULL != file_local_offset)
+			  *file_local_offset = CH_off;
+		  lhdr(map, CH_off, zsize-CH_off, fu, fc ,ch, ret, ctx, tmpd, 1, zip_scan_cb, file_local_header_size, file_local_data_size);
       } else cli_dbgmsg("cli_unzip: ch - local hdr out of file\n");
   }
   else {
@@ -712,6 +728,13 @@ int cli_unzip(cli_ctx *ctx) {
 #if HAVE_JSON
   int toval = 0;
 #endif
+  int bZipBombDetected					= 0;
+  uint32_t cur_file_local_offset		= 0;
+  uint32_t cur_file_local_header_size	= 0;
+  uint32_t cur_file_local_data_size		= 0;
+  uint32_t prev_file_local_offset		= 0;
+  uint32_t prev_file_local_header_size	= 0;
+  uint32_t prev_file_local_data_size	= 0;
 
   cli_dbgmsg("in cli_unzip\n");
   fsize = (uint32_t)map->len;
@@ -744,20 +767,47 @@ int cli_unzip(cli_ctx *ctx) {
   }
 
   if(coff) {
+	  uint32_t nOverlappingFiles = 0;
+
       cli_dbgmsg("cli_unzip: central @%x\n", coff);
-      while((coff=chdr(map, coff, fsize, &fu, fc+1, &ret, ctx, tmpd, NULL))) {
+      while((coff=chdr(map, coff, fsize, &fu, fc+1, &ret, ctx, tmpd, NULL, &cur_file_local_offset, &cur_file_local_header_size, &cur_file_local_data_size))) {
 	  fc++;
 	  if (ctx->engine->maxfiles && fu>=ctx->engine->maxfiles) {
 	      cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles);
 	      ret=CL_EMAXFILES;
 	  }
+	  /*
+	   * Detect overlapping files and zip bombs.
+	   */
+	  if ((((cur_file_local_offset > prev_file_local_offset) && (cur_file_local_offset < prev_file_local_offset + prev_file_local_header_size + prev_file_local_data_size)) ||
+		   ((prev_file_local_offset > cur_file_local_offset) && (prev_file_local_offset < cur_file_local_offset + cur_file_local_header_size + cur_file_local_data_size))) &&
+		   (cur_file_local_header_size + cur_file_local_data_size > 0)) {
+			/* Overlapping file detected */
+			nOverlappingFiles++;
+
+			cli_dbgmsg("clip_unzip: Overlapping files detected.\n");
+			cli_dbgmsg("    previous file end: %u\n", prev_file_local_offset + prev_file_local_header_size + prev_file_local_data_size);
+			cli_dbgmsg("    current file start: %u\n", cur_file_local_offset);
+			if (ZIP_MAX_NUM_OVERLAPPING_FILES < nOverlappingFiles) {
+				if (SCAN_ALGO) {
+					ret = cli_append_virus(ctx, "Heuristics.Zip.OverlappingFiles");
+					virus_found = 1;
+				} else {
+					ret = CL_EFORMAT;
+				}
+				bZipBombDetected = 1;
+			}
+	  }
+	  prev_file_local_offset		= cur_file_local_offset;
+	  prev_file_local_header_size	= cur_file_local_header_size;
+	  prev_file_local_data_size		= cur_file_local_data_size;
 #if HAVE_JSON
           if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) {
               ret=CL_ETIMEOUT;
           }
 #endif
           if (ret != CL_CLEAN) {
-              if (ret == CL_VIRUS && SCAN_ALL) {
+              if (ret == CL_VIRUS && SCAN_ALL && !bZipBombDetected) {
                   ret = CL_CLEAN;
                   virus_found = 1;
               } else
@@ -769,7 +819,7 @@ int cli_unzip(cli_ctx *ctx) {
       ret = CL_VIRUS;
   if(fu<=(fc/4)) { /* FIXME: make up a sane ratio or remove the whole logic */
     fc = 0;
-    while (ret==CL_CLEAN && lhoff<fsize && (coff=lhdr(map, lhoff, fsize-lhoff, &fu, fc+1, NULL, &ret, ctx, tmpd, 1, zip_scan_cb))) {
+    while (ret==CL_CLEAN && lhoff<fsize && (coff=lhdr(map, lhoff, fsize-lhoff, &fu, fc+1, NULL, &ret, ctx, tmpd, 1, zip_scan_cb, NULL, NULL))) {
       fc++;
       lhoff+=coff;
       if (SCAN_ALL && ret == CL_VIRUS) {
@@ -816,7 +866,7 @@ int unzip_single_internal(cli_ctx *ctx,
     return CL_CLEAN;
   }
 
-  lhdr(map, lhoffl, fsize, &fu, 0, NULL, &ret, ctx, NULL, 0, zcb);
+  lhdr(map, lhoffl, fsize, &fu, 0, NULL, &ret, ctx, NULL, 0, zcb, NULL, NULL);
 
   return ret;
 }
@@ -886,7 +936,7 @@ int unzip_search(cli_ctx *ctx, fmap_t *m
 
     if(coff) {
         cli_dbgmsg("unzip_search: central @%x\n", coff);
-        while(ret==CL_CLEAN && (coff=chdr(zmap, coff, fsize, NULL, fc+1, &ret, ctx, NULL, requests))) {
+        while(ret==CL_CLEAN && (coff=chdr(zmap, coff, fsize, NULL, fc+1, &ret, ctx, NULL, requests, NULL, NULL, NULL))) {
             if (requests->match) {
                 ret=CL_VIRUS;
             }
openSUSE Build Service is sponsored by