9b72a6
From 5b44c818b96193b3e240f38f61985fa2bc780eb7 Mon Sep 17 00:00:00 2001
9b72a6
From: Jakub Martisko <jamartis@redhat.com>
9b72a6
Date: Tue, 30 Nov 2021 15:42:17 +0100
9b72a6
Subject: [PATCH] Add an option to disable the zipbomb detection
9b72a6
9b72a6
This can be done by settting a newly introduced environment variable
9b72a6
UNZIP_DISABLE_ZIPBOMB_DETECTION to {TRUE,True,true}. If the variable is unset, or
9b72a6
set to any other value the zipbomb detection is left enabled.
9b72a6
9b72a6
Example:
9b72a6
	UNZIP_DISABLE_ZIPBOMB_DETECTION=True unzip ./zbsm.zip -d ./test
9b72a6
---
9b72a6
 extract.c | 85 ++++++++++++++++++++++++++++++-------------------------
9b72a6
 unzip.c   | 15 ++++++++--
9b72a6
 unzip.h   |  1 +
9b72a6
 3 files changed, 60 insertions(+), 41 deletions(-)
9b72a6
9b72a6
diff --git a/extract.c b/extract.c
9b72a6
index 878817d..3e58071 100644
9b72a6
--- a/extract.c
9b72a6
+++ b/extract.c
9b72a6
@@ -322,7 +322,8 @@ static ZCONST char Far BadExtraFieldCRC[] =
9b72a6
 static ZCONST char Far NotEnoughMemCover[] =
9b72a6
   "error: not enough memory for bomb detection\n";
9b72a6
 static ZCONST char Far OverlappedComponents[] =
9b72a6
-  "error: invalid zip file with overlapped components (possible zip bomb)\n";
9b72a6
+  "error: invalid zip file with overlapped components (possible zip bomb)\n \
9b72a6
+To unzip the file anyway, rerun the command with UNZIP_DISABLE_ZIPBOMB_DETECTION=TRUE environmnent variable\n";
9b72a6
 
9b72a6
 
9b72a6
 
9b72a6
@@ -502,35 +503,37 @@ int extract_or_test_files(__G)    /* return PK-type error code */
9b72a6
        the end of central directory record (including the Zip64 end of central
9b72a6
        directory locator, if present), and the Zip64 end of central directory
9b72a6
        record, if present. */
9b72a6
-    if (G.cover == NULL) {
9b72a6
+    if (uO.zipbomb == TRUE) {
9b72a6
+      if (G.cover == NULL) {
9b72a6
         G.cover = malloc(sizeof(cover_t));
9b72a6
         if (G.cover == NULL) {
9b72a6
-            Info(slide, 0x401, ((char *)slide,
9b72a6
-              LoadFarString(NotEnoughMemCover)));
9b72a6
-            return PK_MEM;
9b72a6
+            Info(slide, 0x401, ((char *)slide,
9b72a6
+              LoadFarString(NotEnoughMemCover)));
9b72a6
+            return PK_MEM;
9b72a6
         }
9b72a6
         ((cover_t *)G.cover)->span = NULL;
9b72a6
         ((cover_t *)G.cover)->max = 0;
9b72a6
-    }
9b72a6
-    ((cover_t *)G.cover)->num = 0;
9b72a6
-    if (cover_add((cover_t *)G.cover,
9b72a6
-                  G.extra_bytes + G.ecrec.offset_start_central_directory,
9b72a6
-                  G.extra_bytes + G.ecrec.offset_start_central_directory +
9b72a6
-                  G.ecrec.size_central_directory) != 0) {
9b72a6
+    }
9b72a6
+    ((cover_t *)G.cover)->num = 0;
9b72a6
+    if (cover_add((cover_t *)G.cover,
9b72a6
+                  G.extra_bytes + G.ecrec.offset_start_central_directory,
9b72a6
+                  G.extra_bytes + G.ecrec.offset_start_central_directory +
9b72a6
+                  G.ecrec.size_central_directory) != 0) {
9b72a6
         Info(slide, 0x401, ((char *)slide,
9b72a6
-          LoadFarString(NotEnoughMemCover)));
9b72a6
+          LoadFarString(NotEnoughMemCover)));
9b72a6
         return PK_MEM;
9b72a6
-    }
9b72a6
-    if ((G.extra_bytes != 0 &&
9b72a6
-         cover_add((cover_t *)G.cover, 0, G.extra_bytes) != 0) ||
9b72a6
-        (G.ecrec.have_ecr64 &&
9b72a6
-         cover_add((cover_t *)G.cover, G.ecrec.ec64_start,
9b72a6
-                   G.ecrec.ec64_end) != 0) ||
9b72a6
-        cover_add((cover_t *)G.cover, G.ecrec.ec_start,
9b72a6
-                  G.ecrec.ec_end) != 0) {
9b72a6
+    }
9b72a6
+    if ((G.extra_bytes != 0 &&
9b72a6
+         cover_add((cover_t *)G.cover, 0, G.extra_bytes) != 0) ||
9b72a6
+        (G.ecrec.have_ecr64 &&
9b72a6
+         cover_add((cover_t *)G.cover, G.ecrec.ec64_start,
9b72a6
+                   G.ecrec.ec64_end) != 0) ||
9b72a6
+        cover_add((cover_t *)G.cover, G.ecrec.ec_start,
9b72a6
+                  G.ecrec.ec_end) != 0) {
9b72a6
         Info(slide, 0x401, ((char *)slide,
9b72a6
-          LoadFarString(OverlappedComponents)));
9b72a6
+          LoadFarString(OverlappedComponents)));
9b72a6
         return PK_BOMB;
9b72a6
+      }
9b72a6
     }
9b72a6
 
9b72a6
 /*---------------------------------------------------------------------------
9b72a6
@@ -1222,10 +1225,12 @@ static int extract_or_test_entrylist(__G__ numchunk,
9b72a6
 
9b72a6
         /* seek_zipf(__G__ pInfo->offset);  */
9b72a6
         request = G.pInfo->offset + G.extra_bytes;
9b72a6
-        if (cover_within((cover_t *)G.cover, request)) {
9b72a6
+        if (uO.zipbomb == TRUE) {
9b72a6
+          if (cover_within((cover_t *)G.cover, request)) {
9b72a6
             Info(slide, 0x401, ((char *)slide,
9b72a6
-              LoadFarString(OverlappedComponents)));
9b72a6
+              LoadFarString(OverlappedComponents)));
9b72a6
             return PK_BOMB;
9b72a6
+          }
9b72a6
         }
9b72a6
         inbuf_offset = request % INBUFSIZ;
9b72a6
         bufstart = request - inbuf_offset;
9b72a6
@@ -1758,17 +1763,19 @@ reprompt:
9b72a6
             return IZ_CTRLC;        /* cancel operation by user request */
9b72a6
         }
9b72a6
 #endif
9b72a6
-        error = cover_add((cover_t *)G.cover, request,
9b72a6
-                          G.cur_zipfile_bufstart + (G.inptr - G.inbuf));
9b72a6
-        if (error < 0) {
9b72a6
+        if (uO.zipbomb == TRUE) {
9b72a6
+          error = cover_add((cover_t *)G.cover, request,
9b72a6
+                            G.cur_zipfile_bufstart + (G.inptr - G.inbuf));
9b72a6
+          if (error < 0) {
9b72a6
             Info(slide, 0x401, ((char *)slide,
9b72a6
-              LoadFarString(NotEnoughMemCover)));
9b72a6
+                                LoadFarString(NotEnoughMemCover)));
9b72a6
             return PK_MEM;
9b72a6
-        }
9b72a6
-        if (error != 0) {
9b72a6
+          }
9b72a6
+          if (error != 0) {
9b72a6
             Info(slide, 0x401, ((char *)slide,
9b72a6
-              LoadFarString(OverlappedComponents)));
9b72a6
+                                LoadFarString(OverlappedComponents)));
9b72a6
             return PK_BOMB;
9b72a6
+          }
9b72a6
         }
9b72a6
 #ifdef MACOS  /* MacOS is no preemptive OS, thus call event-handling by hand */
9b72a6
         UserStop();
9b72a6
@@ -2171,8 +2178,8 @@ static int extract_or_test_member(__G)    /* return PK-type error code */
9b72a6
     }
9b72a6
 
9b72a6
     undefer_input(__G);
9b72a6
-
9b72a6
-    if ((G.lrec.general_purpose_bit_flag & 8) != 0) {
9b72a6
+    if (uO.zipbomb == TRUE) {
9b72a6
+      if ((G.lrec.general_purpose_bit_flag & 8) != 0) {
9b72a6
         /* skip over data descriptor (harder than it sounds, due to signature
9b72a6
          * ambiguity)
9b72a6
          */
9b72a6
@@ -2189,16 +2196,16 @@ static int extract_or_test_member(__G)    /* return PK-type error code */
9b72a6
               ((G.lrec.csize & LOW) != SIG ||   /* if not SIG, have signature */
9b72a6
                (ulen == SIG &&                  /* if not SIG, no signature */
9b72a6
                 (G.pInfo->zip64 ? G.lrec.csize >> 32 : G.lrec.ucsize) != SIG
9b72a6
-                                                /* if not SIG, have signature */
9b72a6
+                /* if not SIG, have signature */
9b72a6
                 )))))
9b72a6
-                   /* skip four more bytes to account for signature */
9b72a6
-                   shy += 4 - readbuf((char *)buf, 4);
9b72a6
+          /* skip four more bytes to account for signature */
9b72a6
+          shy += 4 - readbuf((char *)buf, 4);
9b72a6
         if (G.pInfo->zip64)
9b72a6
-            shy += 8 - readbuf((char *)buf, 8); /* skip eight more for ZIP64 */
9b72a6
+          shy += 8 - readbuf((char *)buf, 8); /* skip eight more for ZIP64 */
9b72a6
         if (shy)
9b72a6
-            error = PK_ERR;
9b72a6
+          error = PK_ERR;
9b72a6
+      }
9b72a6
     }
9b72a6
-
9b72a6
     return error;
9b72a6
 
9b72a6
 } /* end function extract_or_test_member() */
9b72a6
diff --git a/unzip.c b/unzip.c
9b72a6
index 8dbfc95..abb3644 100644
9b72a6
--- a/unzip.c
9b72a6
+++ b/unzip.c
9b72a6
@@ -1329,10 +1329,9 @@ int uz_opts(__G__ pargc, pargv)
9b72a6
     int *pargc;
9b72a6
     char ***pargv;
9b72a6
 {
9b72a6
-    char **argv, *s;
9b72a6
+    char **argv, *s, *zipbomb_envar;
9b72a6
     int argc, c, error=FALSE, negative=0, showhelp=0;
9b72a6
 
9b72a6
-
9b72a6
     argc = *pargc;
9b72a6
     argv = *pargv;
9b72a6
 
9b72a6
@@ -1923,6 +1922,18 @@ opts_done:  /* yes, very ugly...but only used by UnZipSFX with -x xlist */
9b72a6
     else
9b72a6
         G.extract_flag = TRUE;
9b72a6
 
9b72a6
+    /* Disable the zipbomb detection, this is the only option set only via the shell variables but it should at least not clash with something in the future. */
9b72a6
+    zipbomb_envar = getenv("UNZIP_DISABLE_ZIPBOMB_DETECTION");
9b72a6
+    uO.zipbomb = TRUE;
9b72a6
+    if (zipbomb_envar != NULL) {
9b72a6
+      /* strcasecmp might be a better approach here but it is POSIX-only */
9b72a6
+      if ((strcmp ("TRUE", zipbomb_envar) == 0)
9b72a6
+       || (strcmp ("True", zipbomb_envar) == 0)
9b72a6
+       || (strcmp ("true",zipbomb_envar) == 0)) {
9b72a6
+        uO.zipbomb = FALSE;
9b72a6
+      }
9b72a6
+    }
9b72a6
+
9b72a6
     *pargc = argc;
9b72a6
     *pargv = argv;
9b72a6
     return PK_OK;
9b72a6
diff --git a/unzip.h b/unzip.h
9b72a6
index ed24a5b..e7665e8 100644
9b72a6
--- a/unzip.h
9b72a6
+++ b/unzip.h
9b72a6
@@ -559,6 +559,7 @@ typedef struct _UzpOpts {
9b72a6
 #ifdef UNIX
9b72a6
     int cflxflag;       /* -^: allow control chars in extracted filenames */
9b72a6
 #endif
9b72a6
+  int zipbomb;
9b72a6
 #endif /* !FUNZIP */
9b72a6
 } UzpOpts;
9b72a6
 
9b72a6
-- 
9b72a6
2.33.0
9b72a6