diff -up unzip60/crc_i386.S.exec-shield unzip60/crc_i386.S --- unzip60/crc_i386.S.exec-shield 2007-01-07 06:02:58.000000000 +0100 +++ unzip60/crc_i386.S 2009-11-18 11:16:39.630389312 +0100 @@ -302,3 +302,6 @@ _crc32: /* ulg c #endif /* i386 || _i386 || _I386 || __i386 */ #endif /* !USE_ZLIB && !CRC_TABLE_ONLY */ + +.section .note.GNU-stack, "", @progbits +.previous diff -up unzip60/extract.c.close unzip60/extract.c --- unzip60/extract.c.close 2009-03-14 02:32:52.000000000 +0100 +++ unzip60/extract.c 2009-11-19 08:17:23.481263496 +0100 @@ -1924,24 +1924,21 @@ static int extract_or_test_member(__G) #ifdef VMS /* VMS: required even for stdout! (final flush) */ if (!uO.tflag) /* don't close NULL file */ - close_outfile(__G); + error = close_outfile(__G); #else #ifdef DLL if (!uO.tflag && (!uO.cflag || G.redirect_data)) { if (G.redirect_data) FINISH_REDIRECT(); else - close_outfile(__G); + error = close_outfile(__G); } #else if (!uO.tflag && !uO.cflag) /* don't close NULL file or stdout */ - close_outfile(__G); + error = close_outfile(__G); #endif #endif /* VMS */ - /* GRR: CONVERT close_outfile() TO NON-VOID: CHECK FOR ERRORS! */ - - if (G.disk_full) { /* set by flush() */ if (G.disk_full > 1) { #if (defined(DELETE_IF_FULL) && defined(HAVE_UNLINK)) diff -up unzip60/unix/unix.c.close unzip60/unix/unix.c --- unzip60/unix/unix.c.close 2009-01-24 00:31:26.000000000 +0100 +++ unzip60/unix/unix.c 2009-11-19 08:33:25.568389171 +0100 @@ -1096,10 +1096,41 @@ static int get_extattribs(__G__ pzt, z_u #ifndef MTS /****************************/ +/* Function CloseError() */ +/***************************/ + +int CloseError(__G) + __GDEF +{ + int errval = PK_OK; + + if (fclose(G.outfile) < 0) { + switch (errno) { + case ENOSPC: + /* Do we need this on fileio.c? */ + Info(slide, 0x4a1, ((char *)slide, "%s: write error (disk full?). Continue? (y/n/^C) ", + FnFilter1(G.filename))); + fgets(G.answerbuf, 9, stdin); + if (*G.answerbuf == 'y') /* stop writing to this file */ + G.disk_full = 1; /* pass to next */ + else + G.disk_full = 2; /* no: exit program */ + + errval = PK_DISK; + break; + + default: + errval = PK_WARN; + } + } + return errval; +} /* End of CloseError() */ + +/****************************/ /* Function close_outfile() */ /****************************/ -void close_outfile(__G) /* GRR: change to return PK-style warning level */ +int close_outfile(__G) __GDEF { union { @@ -1108,6 +1139,7 @@ void close_outfile(__G) /* GRR: chang } zt; ulg z_uidgid[2]; int have_uidgid_flg; + int errval = PK_OK; have_uidgid_flg = get_extattribs(__G__ &(zt.t3), z_uidgid); @@ -1141,16 +1173,16 @@ void close_outfile(__G) /* GRR: chang Info(slide, 0x201, ((char *)slide, "warning: symbolic link (%s) failed: mem alloc overflow\n", FnFilter1(G.filename))); - fclose(G.outfile); - return; + errval = CloseError(G.outfile, G.filename); + return errval ? errval : PK_WARN; } if ((slnk_entry = (slinkentry *)malloc(slnk_entrysize)) == NULL) { Info(slide, 0x201, ((char *)slide, "warning: symbolic link (%s) failed: no mem\n", FnFilter1(G.filename))); - fclose(G.outfile); - return; + errval = CloseError(G.outfile, G.filename); + return errval ? errval : PK_WARN; } slnk_entry->next = NULL; slnk_entry->targetlen = ucsize; @@ -1174,10 +1206,10 @@ void close_outfile(__G) /* GRR: chang "warning: symbolic link (%s) failed\n", FnFilter1(G.filename))); free(slnk_entry); - fclose(G.outfile); - return; + errval = CloseError(G.outfile, G.filename); + return errval ? errval : PK_WARN; } - fclose(G.outfile); /* close "link" file for good... */ + errval = CloseError(G.outfile, G.filename); /* close "link" file for good... */ slnk_entry->target[ucsize] = '\0'; if (QCOND2) Info(slide, 0, ((char *)slide, "-> %s ", @@ -1188,7 +1220,7 @@ void close_outfile(__G) /* GRR: chang else G.slink_head = slnk_entry; G.slink_last = slnk_entry; - return; + return errval; } #endif /* SYMLINKS */ @@ -1201,7 +1233,7 @@ void close_outfile(__G) /* GRR: chang #endif #if (defined(NO_FCHOWN)) - fclose(G.outfile); + errval = CloseError(G.outfile, G.filename); #endif /* if -X option was specified and we have UID/GID info, restore it */ @@ -1227,7 +1259,7 @@ void close_outfile(__G) /* GRR: chang } #if (!defined(NO_FCHOWN) && defined(NO_FCHMOD)) - fclose(G.outfile); + errval = CloseError(G.outfile, G.filename); #endif #if (!defined(NO_FCHOWN) && !defined(NO_FCHMOD)) @@ -1239,7 +1271,7 @@ void close_outfile(__G) /* GRR: chang if (fchmod(fileno(G.outfile), filtattr(__G__ G.pInfo->file_attr))) perror("fchmod (file attributes) error"); - fclose(G.outfile); + errval = CloseError(G.outfile, G.filename); #endif /* !NO_FCHOWN && !NO_FCHMOD */ /* skip restoring time stamps on user's request */ @@ -1267,6 +1299,7 @@ void close_outfile(__G) /* GRR: chang #endif #endif /* NO_FCHOWN || NO_FCHMOD */ + return errval; } /* end function close_outfile() */ #endif /* !MTS */ diff -up unzip60/unzpriv.h.close unzip60/unzpriv.h --- unzip60/unzpriv.h.close 2009-04-20 01:59:26.000000000 +0200 +++ unzip60/unzpriv.h 2009-11-19 08:19:08.610388618 +0100 @@ -2604,7 +2604,7 @@ char *GetLoadPath OF((__GPRO)); int SetFileSize OF((FILE *file, zusz_t filesize)); /* local */ #endif #ifndef MTS /* macro in MTS */ - void close_outfile OF((__GPRO)); /* local */ + int close_outfile OF((__GPRO)); /* local */ #endif #ifdef SET_SYMLINK_ATTRIBS int set_symlnk_attribs OF((__GPRO__ slinkentry *slnk_entry)); /* local */ diff -up unzip60/zipinfo.c.attribs-overflow unzip60/zipinfo.c --- unzip60/zipinfo.c.attribs-overflow 2009-11-30 09:55:39.000000000 +0100 +++ unzip60/zipinfo.c 2009-11-30 09:56:42.844263244 +0100 @@ -1881,7 +1881,7 @@ static int zi_short(__G) /* return PK- #endif int k, error, error_in_archive=PK_COOL; unsigned hostnum, hostver, methid, methnum, xattr; - char *p, workspace[12], attribs[16]; + char *p, workspace[12], attribs[17]; char methbuf[5]; static ZCONST char dtype[5]="NXFS"; /* normal, maximum, fast, superfast */ static ZCONST char Far os[NUM_HOSTS+1][4] = { diff --git a/process.c b/process.c index 1e9a1e1..905732b 100644 --- a/process.c +++ b/process.c @@ -1751,6 +1751,12 @@ int process_cdir_file_hdr(__G) /* return PK-type error code */ = (G.crec.general_purpose_bit_flag & (1 << 11)) == (1 << 11); #endif +#ifdef SYMLINKS + /* Initialize the symlink flag, may be set by the platform-specific + mapattr function. */ + G.pInfo->symlink = 0; +#endif + return PK_COOL; } /* end function process_cdir_file_hdr() */ diff --git a/extract.c b/extract.c index eeb2f57..a0a4929 100644 --- a/extract.c +++ b/extract.c @@ -472,8 +472,8 @@ int extract_or_test_files(__G) /* return PK-type error code */ */ Info(slide, 0x401, ((char *)slide, LoadFarString(CentSigMsg), j + blknum*DIR_BLKSIZ + 1)); - Info(slide, 0x401, ((char *)slide, - LoadFarString(ReportMsg))); + Info(slide, 0x401, + ((char *)slide,"%s", LoadFarString(ReportMsg))); error_in_archive = PK_BADERR; } reached_end = TRUE; /* ...so no more left to do */ @@ -752,8 +752,8 @@ int extract_or_test_files(__G) /* return PK-type error code */ #ifndef SFX if (no_endsig_found) { /* just to make sure */ - Info(slide, 0x401, ((char *)slide, LoadFarString(EndSigMsg))); - Info(slide, 0x401, ((char *)slide, LoadFarString(ReportMsg))); + Info(slide, 0x401, ((char *)slide,"%s", LoadFarString(EndSigMsg))); + Info(slide, 0x401, ((char *)slide,"%s", LoadFarString(ReportMsg))); if (!error_in_archive) /* don't overwrite stronger error */ error_in_archive = PK_WARN; } diff --git a/list.c b/list.c index 15e0011..f7359c3 100644 --- a/list.c +++ b/list.c @@ -181,7 +181,7 @@ int list_files(__G) /* return PK-type error code */ Info(slide, 0x401, ((char *)slide, LoadFarString(CentSigMsg), j)); Info(slide, 0x401, - ((char *)slide, LoadFarString(ReportMsg))); + ((char *)slide,"%s", LoadFarString(ReportMsg))); return PK_BADERR; /* sig not found */ } } @@ -507,7 +507,8 @@ int list_files(__G) /* return PK-type error code */ && (!G.ecrec.is_zip64_archive) && (memcmp(G.sig, end_central_sig, 4) != 0) ) { /* just to make sure again */ - Info(slide, 0x401, ((char *)slide, LoadFarString(EndSigMsg))); + Info(slide, 0x401, + ((char *)slide,"%s", LoadFarString(EndSigMsg))); error_in_archive = PK_WARN; /* didn't find sig */ } @@ -591,7 +592,7 @@ int get_time_stamp(__G__ last_modtime, nmember) /* return PK-type error code */ Info(slide, 0x401, ((char *)slide, LoadFarString(CentSigMsg), j)); Info(slide, 0x401, - ((char *)slide, LoadFarString(ReportMsg))); + ((char *)slide,"%s", LoadFarString(ReportMsg))); return PK_BADERR; /* sig not found */ } } @@ -674,7 +675,7 @@ int get_time_stamp(__G__ last_modtime, nmember) /* return PK-type error code */ ---------------------------------------------------------------------------*/ if (memcmp(G.sig, end_central_sig, 4)) { /* just to make sure again */ - Info(slide, 0x401, ((char *)slide, LoadFarString(EndSigMsg))); + Info(slide, 0x401, ((char *)slide,"%s", LoadFarString(EndSigMsg))); error_in_archive = PK_WARN; } if (*nmember == 0L && error_in_archive <= PK_WARN) diff --git a/zipinfo.c b/zipinfo.c index 6e22cc8..ac5c61b 100644 --- a/zipinfo.c +++ b/zipinfo.c @@ -771,7 +771,7 @@ int zipinfo(__G) /* return PK-type error code */ Info(slide, 0x401, ((char *)slide, LoadFarString(CentSigMsg), j)); Info(slide, 0x401, - ((char *)slide, LoadFarString(ReportMsg))); + ((char *)slide,"%s", LoadFarString(ReportMsg))); error_in_archive = PK_BADERR; /* sig not found */ break; } @@ -960,7 +960,8 @@ int zipinfo(__G) /* return PK-type error code */ && (!G.ecrec.is_zip64_archive) && (memcmp(G.sig, end_central_sig, 4) != 0) ) { /* just to make sure again */ - Info(slide, 0x401, ((char *)slide, LoadFarString(EndSigMsg))); + Info(slide, 0x401, + ((char *)slide,"%s", LoadFarString(EndSigMsg))); error_in_archive = PK_WARN; /* didn't find sig */ } diff --git a/fileio.c b/fileio.c index ba0a1d0..03fc4be 100644 --- a/fileio.c +++ b/fileio.c @@ -2006,6 +2006,7 @@ int do_string(__G__ length, option) /* return PK-type error code */ unsigned comment_bytes_left; unsigned int block_len; int error=PK_OK; + unsigned int length2; #ifdef AMIGA char tmp_fnote[2 * AMIGA_FILENOTELEN]; /* extra room for squozen chars */ #endif @@ -2292,8 +2293,12 @@ int do_string(__G__ length, option) /* return PK-type error code */ seek_zipf(__G__ G.cur_zipfile_bufstart - G.extra_bytes + (G.inptr-G.inbuf) + length); } else { - if (readbuf(__G__ (char *)G.extra_field, length) == 0) + if ((length2 = readbuf(__G__ (char *)G.extra_field, length)) == 0) return PK_EOF; + if(length2 < length) { + memset (__G__ (char *)G.extra_field+length2, 0 , length-length2); + length = length2; + } /* Looks like here is where extra fields are read */ getZip64Data(__G__ G.extra_field, length); #ifdef UNICODE_SUPPORT --- ./process.c.orig 2009-03-06 02:25:10.000000000 +0100 +++ ./process.c 2013-09-12 10:51:16.000000000 +0200 @@ -2901,9 +2901,9 @@ */ #ifdef IZ_HAVE_UXUIDGID - if (eb_len >= EB_UX3_MINLEN - && z_uidgid != NULL - && (*((EB_HEADSIZE + 0) + ef_buf) == 1) + if ((eb_len >= EB_UX3_MINLEN) + && (z_uidgid != NULL) + && ((*((EB_HEADSIZE + 0) + ef_buf) == 1))) /* only know about version 1 */ { uch uid_size; @@ -2915,10 +2915,10 @@ flags &= ~0x0ff; /* ignore any previous UNIX field */ if ( read_ux3_value((EB_HEADSIZE + 2) + ef_buf, - uid_size, z_uidgid[0]) + uid_size, &z_uidgid[0]) && read_ux3_value((EB_HEADSIZE + uid_size + 3) + ef_buf, - gid_size, z_uidgid[1]) ) + gid_size, &z_uidgid[1]) ) { flags |= EB_UX2_VALID; /* signal success */ } diff --git a/extract.c b/extract.c index a0a4929..9ef80b3 100644 --- a/extract.c +++ b/extract.c @@ -2214,6 +2214,7 @@ static int test_compr_eb(__G__ eb, eb_size, compr_offset, test_uc_ebdata) ulg eb_ucsize; uch *eb_ucptr; int r; + ush method; if (compr_offset < 4) /* field is not compressed: */ return PK_OK; /* do nothing and signal OK */ @@ -2223,6 +2224,12 @@ static int test_compr_eb(__G__ eb, eb_size, compr_offset, test_uc_ebdata) eb_size <= (compr_offset + EB_CMPRHEADLEN))) return IZ_EF_TRUNC; /* no compressed data! */ + method = makeword(eb + (EB_HEADSIZE + compr_offset)); + if ((method == STORED) && (eb_size != compr_offset + EB_CMPRHEADLEN + eb_ucsize)) + return PK_ERR; /* compressed & uncompressed + * should match in STORED + * method */ + if ( #ifdef INT_16BIT (((ulg)(extent)eb_ucsize) != eb_ucsize) || diff --git a/extract.c b/extract.c index 9ef80b3..c741b5f 100644 --- a/extract.c +++ b/extract.c @@ -1,5 +1,5 @@ /* - Copyright (c) 1990-2009 Info-ZIP. All rights reserved. + Copyright (c) 1990-2014 Info-ZIP. All rights reserved. See the accompanying file LICENSE, version 2009-Jan-02 or later (the contents of which are also included in unzip.h) for terms of use. @@ -298,6 +298,8 @@ char ZCONST Far TruncNTSD[] = #ifndef SFX static ZCONST char Far InconsistEFlength[] = "bad extra-field entry:\n \ EF block length (%u bytes) exceeds remaining EF data (%u bytes)\n"; + static ZCONST char Far TooSmallEBlength[] = "bad extra-field entry:\n \ + EF block length (%u bytes) invalid (< %d)\n"; static ZCONST char Far InvalidComprDataEAs[] = " invalid compressed data for EAs\n"; # if (defined(WIN32) && defined(NTSD_EAS)) @@ -2020,7 +2022,8 @@ static int TestExtraField(__G__ ef, ef_len) ebID = makeword(ef); ebLen = (unsigned)makeword(ef+EB_LEN); - if (ebLen > (ef_len - EB_HEADSIZE)) { + if (ebLen > (ef_len - EB_HEADSIZE)) + { /* Discovered some extra field inconsistency! */ if (uO.qflag) Info(slide, 1, ((char *)slide, "%-22s ", @@ -2155,11 +2158,29 @@ static int TestExtraField(__G__ ef, ef_len) } break; case EF_PKVMS: - if (makelong(ef+EB_HEADSIZE) != - crc32(CRCVAL_INITIAL, ef+(EB_HEADSIZE+4), - (extent)(ebLen-4))) - Info(slide, 1, ((char *)slide, - LoadFarString(BadCRC_EAs))); + /* 2015-01-30 SMS. Added sufficient-bytes test/message + * here. (Removed defective ebLen test above.) + * + * If sufficient bytes (EB_PKVMS_MINLEN) are available, + * then compare the stored CRC value with the calculated + * CRC for the remainder of the data (and complain about + * a mismatch). + */ + if (ebLen < EB_PKVMS_MINLEN) + { + /* Insufficient bytes available. */ + Info( slide, 1, + ((char *)slide, LoadFarString( TooSmallEBlength), + ebLen, EB_PKVMS_MINLEN)); + } + else if (makelong(ef+ EB_HEADSIZE) != + crc32(CRCVAL_INITIAL, + (ef+ EB_HEADSIZE+ EB_PKVMS_MINLEN), + (extent)(ebLen- EB_PKVMS_MINLEN))) + { + Info(slide, 1, ((char *)slide, + LoadFarString(BadCRC_EAs))); + } break; case EF_PKW32: case EF_PKUNIX: diff --git a/unzpriv.h b/unzpriv.h index 005cee0..5c83a6e 100644 --- a/unzpriv.h +++ b/unzpriv.h @@ -1806,6 +1806,8 @@ #define EB_NTSD_VERSION 4 /* offset of NTSD version byte */ #define EB_NTSD_MAX_VER (0) /* maximum version # we know how to handle */ +#define EB_PKVMS_MINLEN 4 /* minimum data length of PKVMS extra block */ + #define EB_ASI_CRC32 0 /* offset of ASI Unix field's crc32 checksum */ #define EB_ASI_MODE 4 /* offset of ASI Unix permission mode field */ diff --git a/extract.c b/extract.c index c741b5f..e4a4c7b 100644 --- a/extract.c +++ b/extract.c @@ -2240,10 +2240,17 @@ static int test_compr_eb(__G__ eb, eb_size, compr_offset, test_uc_ebdata) if (compr_offset < 4) /* field is not compressed: */ return PK_OK; /* do nothing and signal OK */ + /* Return no/bad-data error status if any problem is found: + * 1. eb_size is too small to hold the uncompressed size + * (eb_ucsize). (Else extract eb_ucsize.) + * 2. eb_ucsize is zero (invalid). 2014-12-04 SMS. + * 3. eb_ucsize is positive, but eb_size is too small to hold + * the compressed data header. + */ if ((eb_size < (EB_UCSIZE_P + 4)) || - ((eb_ucsize = makelong(eb+(EB_HEADSIZE+EB_UCSIZE_P))) > 0L && - eb_size <= (compr_offset + EB_CMPRHEADLEN))) - return IZ_EF_TRUNC; /* no compressed data! */ + ((eb_ucsize = makelong( eb+ (EB_HEADSIZE+ EB_UCSIZE_P))) == 0L) || + ((eb_ucsize > 0L) && (eb_size <= (compr_offset + EB_CMPRHEADLEN)))) + return IZ_EF_TRUNC; /* no/bad compressed data! */ method = makeword(eb + (EB_HEADSIZE + compr_offset)); if ((method == STORED) && (eb_size != compr_offset + EB_CMPRHEADLEN + eb_ucsize)) diff --git a/fileio.c b/fileio.c index 03fc4be..2a61a30 100644 --- a/fileio.c +++ b/fileio.c @@ -176,6 +176,8 @@ static ZCONST char Far FilenameTooLongTrunc[] = #endif static ZCONST char Far ExtraFieldTooLong[] = "warning: extra field too long (%d). Ignoring...\n"; +static ZCONST char Far ExtraFieldCorrupt[] = + "warning: extra field (type: 0x%04x) corrupt. Continuing...\n"; #ifdef WINDLL static ZCONST char Far DiskFullQuery[] = @@ -2300,7 +2302,13 @@ int do_string(__G__ length, option) /* return PK-type error code */ length = length2; } /* Looks like here is where extra fields are read */ - getZip64Data(__G__ G.extra_field, length); + if (getZip64Data(__G__ G.extra_field, length) != PK_COOL) + { + Info(slide, 0x401, ((char *)slide, + LoadFarString( ExtraFieldCorrupt), EF_PKSZ64)); + error = PK_WARN; + } + #ifdef UNICODE_SUPPORT G.unipath_filename = NULL; if (G.UzO.U_flag < 2) { diff --git a/process.c b/process.c index be6e006..0d57ab4 100644 --- a/process.c +++ b/process.c @@ -1,5 +1,5 @@ /* - Copyright (c) 1990-2009 Info-ZIP. All rights reserved. + Copyright (c) 1990-2014 Info-ZIP. All rights reserved. See the accompanying file LICENSE, version 2009-Jan-02 or later (the contents of which are also included in unzip.h) for terms of use. @@ -1894,48 +1894,83 @@ int getZip64Data(__G__ ef_buf, ef_len) and a 4-byte version of disk start number. Sets both local header and central header fields. Not terribly clever, but it means that this procedure is only called in one place. + + 2014-12-05 SMS. + Added checks to ensure that enough data are available before calling + makeint64() or makelong(). Replaced various sizeof() values with + simple ("4" or "8") constants. (The Zip64 structures do not depend + on our variable sizes.) Error handling is crude, but we should now + stay within the buffer. ---------------------------------------------------------------------------*/ +#define Z64FLGS 0xffff +#define Z64FLGL 0xffffffff + if (ef_len == 0 || ef_buf == NULL) return PK_COOL; Trace((stderr,"\ngetZip64Data: scanning extra field of length %u\n", ef_len)); - while (ef_len >= EB_HEADSIZE) { + while (ef_len >= EB_HEADSIZE) + { eb_id = makeword(EB_ID + ef_buf); eb_len = makeword(EB_LEN + ef_buf); - if (eb_len > (ef_len - EB_HEADSIZE)) { - /* discovered some extra field inconsistency! */ + if (eb_len > (ef_len - EB_HEADSIZE)) + { + /* Extra block length exceeds remaining extra field length. */ Trace((stderr, "getZip64Data: block length %u > rest ef_size %u\n", eb_len, ef_len - EB_HEADSIZE)); break; } - if (eb_id == EF_PKSZ64) { + if (eb_id == EF_PKSZ64) + { int offset = EB_HEADSIZE; - if (G.crec.ucsize == 0xffffffff || G.lrec.ucsize == 0xffffffff){ - G.lrec.ucsize = G.crec.ucsize = makeint64(offset + ef_buf); - offset += sizeof(G.crec.ucsize); + if ((G.crec.ucsize == Z64FLGL) || (G.lrec.ucsize == Z64FLGL)) + { + if (offset+ 8 > ef_len) + return PK_ERR; + + G.crec.ucsize = G.lrec.ucsize = makeint64(offset + ef_buf); + offset += 8; } - if (G.crec.csize == 0xffffffff || G.lrec.csize == 0xffffffff){ - G.csize = G.lrec.csize = G.crec.csize = makeint64(offset + ef_buf); - offset += sizeof(G.crec.csize); + + if ((G.crec.csize == Z64FLGL) || (G.lrec.csize == Z64FLGL)) + { + if (offset+ 8 > ef_len) + return PK_ERR; + + G.csize = G.crec.csize = G.lrec.csize = makeint64(offset + ef_buf); + offset += 8; } - if (G.crec.relative_offset_local_header == 0xffffffff){ + + if (G.crec.relative_offset_local_header == Z64FLGL) + { + if (offset+ 8 > ef_len) + return PK_ERR; + G.crec.relative_offset_local_header = makeint64(offset + ef_buf); - offset += sizeof(G.crec.relative_offset_local_header); + offset += 8; } - if (G.crec.disk_number_start == 0xffff){ + + if (G.crec.disk_number_start == Z64FLGS) + { + if (offset+ 4 > ef_len) + return PK_ERR; + G.crec.disk_number_start = (zuvl_t)makelong(offset + ef_buf); - offset += sizeof(G.crec.disk_number_start); + offset += 4; } +#if 0 + break; /* Expect only one EF_PKSZ64 block. */ +#endif /* 0 */ } - /* Skip this extra field block */ + /* Skip this extra field block. */ ef_buf += (eb_len + EB_HEADSIZE); ef_len -= (eb_len + EB_HEADSIZE); } diff --git a/list.c b/list.c index f7359c3..4c3d703 100644 --- a/list.c +++ b/list.c @@ -97,7 +97,7 @@ int list_files(__G) /* return PK-type error code */ { int do_this_file=FALSE, cfactor, error, error_in_archive=PK_COOL; #ifndef WINDLL - char sgn, cfactorstr[10]; + char sgn, cfactorstr[13]; int longhdr=(uO.vflag>1); #endif int date_format; @@ -339,7 +339,19 @@ int list_files(__G) /* return PK-type error code */ G.crec.compression_method == ENHDEFLATED) { methbuf[5] = dtype[(G.crec.general_purpose_bit_flag>>1) & 3]; } else if (methnum >= NUM_METHODS) { - sprintf(&methbuf[4], "%03u", G.crec.compression_method); + /* 2013-02-26 SMS. + * http://sourceforge.net/tracker/?func=detail + * &aid=2861648&group_id=118012&atid=679786 + * Unexpectedly large compression methods overflow + * &methbuf[]. Use the old, three-digit decimal format + * for values which fit. Otherwise, sacrifice the + * colon, and use four-digit hexadecimal. + */ + if (G.crec.compression_method <= 999) { + sprintf( &methbuf[ 4], "%03u", G.crec.compression_method); + } else { + sprintf( &methbuf[ 3], "%04X", G.crec.compression_method); + } } #if 0 /* GRR/Euro: add this? */ From bdd4a0cecd745cb4825e4508b5bdf2579731086a Mon Sep 17 00:00:00 2001 From: Petr Stodulka Date: Mon, 14 Sep 2015 18:23:17 +0200 Subject: [PATCH 1/3] upstream fix for heap overflow https://bugzilla.redhat.com/attachment.cgi?id=1073002 --- crypt.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/crypt.c b/crypt.c index 784e411..a8975f2 100644 --- a/crypt.c +++ b/crypt.c @@ -465,7 +465,17 @@ int decrypt(__G__ passwrd) GLOBAL(pInfo->encrypted) = FALSE; defer_leftover_input(__G); for (n = 0; n < RAND_HEAD_LEN; n++) { - b = NEXTBYTE; + /* 2012-11-23 SMS. (OUSPG report.) + * Quit early if compressed size < HEAD_LEN. The resulting + * error message ("unable to get password") could be improved, + * but it's better than trying to read nonexistent data, and + * then continuing with a negative G.csize. (See + * fileio.c:readbyte()). + */ + if ((b = NEXTBYTE) == (ush)EOF) + { + return PK_ERR; + } h[n] = (uch)b; Trace((stdout, " (%02x)", h[n])); } -- 2.4.6 From 4b48844661ff9569f2ecf582a387d46a5775b5d8 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Mon, 14 Sep 2015 18:24:56 +0200 Subject: [PATCH 2/3] fix infinite loop when extracting empty bzip2 data Bug: https://sourceforge.net/p/infozip/patches/23/ --- extract.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/extract.c b/extract.c index 7134bfe..29db027 100644 --- a/extract.c +++ b/extract.c @@ -2733,6 +2733,12 @@ __GDEF int repeated_buf_err; bz_stream bstrm; + if (G.incnt <= 0 && G.csize <= 0L) { + /* avoid an infinite loop */ + Trace((stderr, "UZbunzip2() got empty input\n")); + return 2; + } + #if (defined(DLL) && !defined(NO_SLIDE_REDIR)) if (G.redirect_slide) wsize = G.redirect_size, redirSlide = G.redirect_buffer; -- 2.4.6 From bd150334fb4084f5555a6be26b015a0671cb5b74 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Tue, 22 Sep 2015 18:52:23 +0200 Subject: [PATCH 3/3] extract: prevent unsigned overflow on invalid input Suggested-by: Stefan Cornelius --- extract.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/extract.c b/extract.c index 29db027..b9ae667 100644 --- a/extract.c +++ b/extract.c @@ -1257,8 +1257,17 @@ static int extract_or_test_entrylist(__G__ numchunk, if (G.lrec.compression_method == STORED) { zusz_t csiz_decrypted = G.lrec.csize; - if (G.pInfo->encrypted) + if (G.pInfo->encrypted) { + if (csiz_decrypted < 12) { + /* handle the error now to prevent unsigned overflow */ + Info(slide, 0x401, ((char *)slide, + LoadFarStringSmall(ErrUnzipNoFile), + LoadFarString(InvalidComprData), + LoadFarStringSmall2(Inflate))); + return PK_ERR; + } csiz_decrypted -= 12; + } if (G.lrec.ucsize != csiz_decrypted) { Info(slide, 0x401, ((char *)slide, LoadFarStringSmall2(WrnStorUCSizCSizDiff), -- 2.5.2 From: Giovanni Scafora Subject: unzip files encoded with non-latin, non-unicode file names Last-Update: 2015-02-11 Updated 2015-02-11 by Marc Deslauriers to fix buffer overflow in charset_to_intern() Index: unzip-6.0/unix/unix.c =================================================================== --- unzip-6.0.orig/unix/unix.c 2015-02-11 08:46:43.675324290 -0500 +++ unzip-6.0/unix/unix.c 2015-02-11 09:18:04.902081319 -0500 @@ -30,6 +30,9 @@ #define UNZIP_INTERNAL #include "unzip.h" +#include +#include + #ifdef SCO_XENIX # define SYSNDIR #else /* SCO Unix, AIX, DNIX, TI SysV, Coherent 4.x, ... */ @@ -1874,3 +1877,102 @@ } } #endif /* QLZIP */ + + +typedef struct { + char *local_charset; + char *archive_charset; +} CHARSET_MAP; + +/* A mapping of local <-> archive charsets used by default to convert filenames + * of DOS/Windows Zip archives. Currently very basic. */ +static CHARSET_MAP dos_charset_map[] = { + { "ANSI_X3.4-1968", "CP850" }, + { "ISO-8859-1", "CP850" }, + { "CP1252", "CP850" }, + { "UTF-8", "CP866" }, + { "KOI8-R", "CP866" }, + { "KOI8-U", "CP866" }, + { "ISO-8859-5", "CP866" } +}; + +char OEM_CP[MAX_CP_NAME] = ""; +char ISO_CP[MAX_CP_NAME] = ""; + +/* Try to guess the default value of OEM_CP based on the current locale. + * ISO_CP is left alone for now. */ +void init_conversion_charsets() +{ + const char *local_charset; + int i; + + /* Make a guess only if OEM_CP not already set. */ + if(*OEM_CP == '\0') { + local_charset = nl_langinfo(CODESET); + for(i = 0; i < sizeof(dos_charset_map)/sizeof(CHARSET_MAP); i++) + if(!strcasecmp(local_charset, dos_charset_map[i].local_charset)) { + strncpy(OEM_CP, dos_charset_map[i].archive_charset, + sizeof(OEM_CP)); + break; + } + } +} + +/* Convert a string from one encoding to the current locale using iconv(). + * Be as non-intrusive as possible. If error is encountered during covertion + * just leave the string intact. */ +static void charset_to_intern(char *string, char *from_charset) +{ + iconv_t cd; + char *s,*d, *buf; + size_t slen, dlen, buflen; + const char *local_charset; + + if(*from_charset == '\0') + return; + + buf = NULL; + local_charset = nl_langinfo(CODESET); + + if((cd = iconv_open(local_charset, from_charset)) == (iconv_t)-1) + return; + + slen = strlen(string); + s = string; + + /* Make sure OUTBUFSIZ + 1 never ends up smaller than FILNAMSIZ + * as this function also gets called with G.outbuf in fileio.c + */ + buflen = FILNAMSIZ; + if (OUTBUFSIZ + 1 < FILNAMSIZ) + { + buflen = OUTBUFSIZ + 1; + } + + d = buf = malloc(buflen); + if(!d) + goto cleanup; + + bzero(buf,buflen); + dlen = buflen - 1; + + if(iconv(cd, &s, &slen, &d, &dlen) == (size_t)-1) + goto cleanup; + strncpy(string, buf, buflen); + + cleanup: + free(buf); + iconv_close(cd); +} + +/* Convert a string from OEM_CP to the current locale charset. */ +inline void oem_intern(char *string) +{ + charset_to_intern(string, OEM_CP); +} + +/* Convert a string from ISO_CP to the current locale charset. */ +inline void iso_intern(char *string) +{ + charset_to_intern(string, ISO_CP); +} Index: unzip-6.0/unix/unxcfg.h =================================================================== --- unzip-6.0.orig/unix/unxcfg.h 2015-02-11 08:46:43.675324290 -0500 +++ unzip-6.0/unix/unxcfg.h 2015-02-11 08:46:43.671324260 -0500 @@ -228,4 +228,30 @@ /* wild_dir, dirname, wildname, matchname[], dirnamelen, have_dirname, */ /* and notfirstcall are used by do_wild(). */ + +#define MAX_CP_NAME 25 + +#ifdef SETLOCALE +# undef SETLOCALE +#endif +#define SETLOCALE(category, locale) setlocale(category, locale) +#include + +#ifdef _ISO_INTERN +# undef _ISO_INTERN +#endif +#define _ISO_INTERN(str1) iso_intern(str1) + +#ifdef _OEM_INTERN +# undef _OEM_INTERN +#endif +#ifndef IZ_OEM2ISO_ARRAY +# define IZ_OEM2ISO_ARRAY +#endif +#define _OEM_INTERN(str1) oem_intern(str1) + +void iso_intern(char *); +void oem_intern(char *); +void init_conversion_charsets(void); + #endif /* !__unxcfg_h */ Index: unzip-6.0/unzip.c =================================================================== --- unzip-6.0.orig/unzip.c 2015-02-11 08:46:43.675324290 -0500 +++ unzip-6.0/unzip.c 2015-02-11 08:46:43.675324290 -0500 @@ -327,11 +327,21 @@ -2 just filenames but allow -h/-t/-z -l long Unix \"ls -l\" format\n\ -v verbose, multi-page format\n"; +#ifndef UNIX static ZCONST char Far ZipInfoUsageLine3[] = "miscellaneous options:\n\ -h print header line -t print totals for listed files or for all\n\ -z print zipfile comment -T print file times in sortable decimal format\ \n -C be case-insensitive %s\ -x exclude filenames that follow from listing\n"; +#else /* UNIX */ +static ZCONST char Far ZipInfoUsageLine3[] = "miscellaneous options:\n\ + -h print header line -t print totals for listed files or for all\n\ + -z print zipfile comment %c-T%c print file times in sortable decimal format\ +\n %c-C%c be case-insensitive %s\ + -x exclude filenames that follow from listing\n\ + -O CHARSET specify a character encoding for DOS, Windows and OS/2 archives\n\ + -I CHARSET specify a character encoding for UNIX and other archives\n"; +#endif /* !UNIX */ #ifdef MORE static ZCONST char Far ZipInfoUsageLine4[] = " -M page output through built-in \"more\"\n"; @@ -664,6 +674,17 @@ -U use escapes for all non-ASCII Unicode -UU ignore any Unicode fields\n\ -C match filenames case-insensitively -L make (some) names \ lowercase\n %-42s -V retain VMS version numbers\n%s"; +#elif (defined UNIX) +static ZCONST char Far UnzipUsageLine4[] = "\ +modifiers:\n\ + -n never overwrite existing files -q quiet mode (-qq => quieter)\n\ + -o overwrite files WITHOUT prompting -a auto-convert any text files\n\ + -j junk paths (do not make directories) -aa treat ALL files as text\n\ + -U use escapes for all non-ASCII Unicode -UU ignore any Unicode fields\n\ + -C match filenames case-insensitively -L make (some) names \ +lowercase\n %-42s -V retain VMS version numbers\n%s\ + -O CHARSET specify a character encoding for DOS, Windows and OS/2 archives\n\ + -I CHARSET specify a character encoding for UNIX and other archives\n\n"; #else /* !VMS */ static ZCONST char Far UnzipUsageLine4[] = "\ modifiers:\n\ @@ -802,6 +823,10 @@ #endif /* UNICODE_SUPPORT */ +#ifdef UNIX + init_conversion_charsets(); +#endif + #if (defined(__IBMC__) && defined(__DEBUG_ALLOC__)) extern void DebugMalloc(void); @@ -1335,6 +1360,11 @@ argc = *pargc; argv = *pargv; +#ifdef UNIX + extern char OEM_CP[MAX_CP_NAME]; + extern char ISO_CP[MAX_CP_NAME]; +#endif + while (++argv, (--argc > 0 && *argv != NULL && **argv == '-')) { s = *argv + 1; while ((c = *s++) != 0) { /* "!= 0": prevent Turbo C warning */ @@ -1516,6 +1546,35 @@ } break; #endif /* MACOS */ +#ifdef UNIX + case ('I'): + if (negative) { + Info(slide, 0x401, ((char *)slide, + "error: encodings can't be negated")); + return(PK_PARAM); + } else { + if(*s) { /* Handle the -Icharset case */ + /* Assume that charsets can't start with a dash to spot arguments misuse */ + if(*s == '-') { + Info(slide, 0x401, ((char *)slide, + "error: a valid character encoding should follow the -I argument")); + return(PK_PARAM); + } + strncpy(ISO_CP, s, sizeof(ISO_CP)); + } else { /* -I charset */ + ++argv; + if(!(--argc > 0 && *argv != NULL && **argv != '-')) { + Info(slide, 0x401, ((char *)slide, + "error: a valid character encoding should follow the -I argument")); + return(PK_PARAM); + } + s = *argv; + strncpy(ISO_CP, s, sizeof(ISO_CP)); + } + while(*(++s)); /* No params straight after charset name */ + } + break; +#endif /* ?UNIX */ case ('j'): /* junk pathnames/directory structure */ if (negative) uO.jflag = FALSE, negative = 0; @@ -1591,6 +1650,35 @@ } else ++uO.overwrite_all; break; +#ifdef UNIX + case ('O'): + if (negative) { + Info(slide, 0x401, ((char *)slide, + "error: encodings can't be negated")); + return(PK_PARAM); + } else { + if(*s) { /* Handle the -Ocharset case */ + /* Assume that charsets can't start with a dash to spot arguments misuse */ + if(*s == '-') { + Info(slide, 0x401, ((char *)slide, + "error: a valid character encoding should follow the -I argument")); + return(PK_PARAM); + } + strncpy(OEM_CP, s, sizeof(OEM_CP)); + } else { /* -O charset */ + ++argv; + if(!(--argc > 0 && *argv != NULL && **argv != '-')) { + Info(slide, 0x401, ((char *)slide, + "error: a valid character encoding should follow the -O argument")); + return(PK_PARAM); + } + s = *argv; + strncpy(OEM_CP, s, sizeof(OEM_CP)); + } + while(*(++s)); /* No params straight after charset name */ + } + break; +#endif /* ?UNIX */ case ('p'): /* pipes: extract to stdout, no messages */ if (negative) { uO.cflag = FALSE; Index: unzip-6.0/unzpriv.h =================================================================== --- unzip-6.0.orig/unzpriv.h 2015-02-11 08:46:43.675324290 -0500 +++ unzip-6.0/unzpriv.h 2015-02-11 08:46:43.675324290 -0500 @@ -3008,7 +3008,7 @@ !(((islochdr) || (isuxatt)) && \ ((hostver) == 25 || (hostver) == 26 || (hostver) == 40))) || \ (hostnum) == FS_HPFS_ || \ - ((hostnum) == FS_NTFS_ && (hostver) == 50)) { \ + ((hostnum) == FS_NTFS_ /* && (hostver) == 50 */ )) { \ _OEM_INTERN((string)); \ } else { \ _ISO_INTERN((string)); \ Index: unzip-6.0/zipinfo.c =================================================================== --- unzip-6.0.orig/zipinfo.c 2015-02-11 08:46:43.675324290 -0500 +++ unzip-6.0/zipinfo.c 2015-02-11 08:46:43.675324290 -0500 @@ -457,6 +457,10 @@ int tflag_slm=TRUE, tflag_2v=FALSE; int explicit_h=FALSE, explicit_t=FALSE; +#ifdef UNIX + extern char OEM_CP[MAX_CP_NAME]; + extern char ISO_CP[MAX_CP_NAME]; +#endif #ifdef MACOS uO.lflag = LFLAG; /* reset default on each call */ @@ -501,6 +505,35 @@ uO.lflag = 0; } break; +#ifdef UNIX + case ('I'): + if (negative) { + Info(slide, 0x401, ((char *)slide, + "error: encodings can't be negated")); + return(PK_PARAM); + } else { + if(*s) { /* Handle the -Icharset case */ + /* Assume that charsets can't start with a dash to spot arguments misuse */ + if(*s == '-') { + Info(slide, 0x401, ((char *)slide, + "error: a valid character encoding should follow the -I argument")); + return(PK_PARAM); + } + strncpy(ISO_CP, s, sizeof(ISO_CP)); + } else { /* -I charset */ + ++argv; + if(!(--argc > 0 && *argv != NULL && **argv != '-')) { + Info(slide, 0x401, ((char *)slide, + "error: a valid character encoding should follow the -I argument")); + return(PK_PARAM); + } + s = *argv; + strncpy(ISO_CP, s, sizeof(ISO_CP)); + } + while(*(++s)); /* No params straight after charset name */ + } + break; +#endif /* ?UNIX */ case 'l': /* longer form of "ls -l" type listing */ if (negative) uO.lflag = -2, negative = 0; @@ -521,6 +554,35 @@ G.M_flag = TRUE; break; #endif +#ifdef UNIX + case ('O'): + if (negative) { + Info(slide, 0x401, ((char *)slide, + "error: encodings can't be negated")); + return(PK_PARAM); + } else { + if(*s) { /* Handle the -Ocharset case */ + /* Assume that charsets can't start with a dash to spot arguments misuse */ + if(*s == '-') { + Info(slide, 0x401, ((char *)slide, + "error: a valid character encoding should follow the -I argument")); + return(PK_PARAM); + } + strncpy(OEM_CP, s, sizeof(OEM_CP)); + } else { /* -O charset */ + ++argv; + if(!(--argc > 0 && *argv != NULL && **argv != '-')) { + Info(slide, 0x401, ((char *)slide, + "error: a valid character encoding should follow the -O argument")); + return(PK_PARAM); + } + s = *argv; + strncpy(OEM_CP, s, sizeof(OEM_CP)); + } + while(*(++s)); /* No params straight after charset name */ + } + break; +#endif /* ?UNIX */ case 's': /* default: shorter "ls -l" type listing */ if (negative) uO.lflag = -2, negative = 0; From ca0212ba19b64488b9e8459a762c11ecd6e7d0bd Mon Sep 17 00:00:00 2001 From: Petr Stodulka Date: Tue, 24 Nov 2015 17:56:11 +0100 Subject: [PATCH] print correctly non-ascii filenames --- extract.c | 289 ++++++++++++++++++++++++++++++++++++++++++++++++-------------- unzpriv.h | 7 ++ 2 files changed, 233 insertions(+), 63 deletions(-) diff --git a/extract.c b/extract.c index 0ee4e93..741b7e0 100644 --- a/extract.c +++ b/extract.c @@ -2648,8 +2648,21 @@ static void set_deferred_symlink(__G__ slnk_entry) } /* end function set_deferred_symlink() */ #endif /* SYMLINKS */ +/* + * If Unicode is supported, assume we have what we need to do this + * check using wide characters, avoiding MBCS issues. + */ - +#ifndef UZ_FNFILTER_REPLACECHAR + /* A convenient choice for the replacement of unprintable char codes is + * the "single char wildcard", as this character is quite unlikely to + * appear in filenames by itself. The following default definition + * sets the replacement char to a question mark as the most common + * "single char wildcard"; this setting should be overridden in the + * appropiate system-specific configuration header when needed. + */ +# define UZ_FNFILTER_REPLACECHAR '?' +#endif /*************************/ /* Function fnfilter() */ /* here instead of in list.c for SFX */ @@ -2661,48 +2674,168 @@ char *fnfilter(raw, space, size) /* convert name to safely printable form */ extent size; { #ifndef NATIVE /* ASCII: filter ANSI escape codes, etc. */ - ZCONST uch *r=(ZCONST uch *)raw; + ZCONST uch *r; // =(ZCONST uch *)raw; uch *s=space; uch *slim=NULL; uch *se=NULL; int have_overflow = FALSE; - if (size > 0) { - slim = space + size -#ifdef _MBCS - - (MB_CUR_MAX - 1) -#endif - - 4; +# if defined( UNICODE_SUPPORT) && defined( _MBCS) +/* If Unicode support is enabled, and we have multi-byte characters, + * then do the isprint() checks by first converting to wide characters + * and checking those. This avoids our having to parse multi-byte + * characters for ourselves. After the wide-char replacements have been + * made, the wide string is converted back to the local character set. + */ + wchar_t *wstring; /* wchar_t version of raw */ + size_t wslen; /* length of wstring */ + wchar_t *wostring; /* wchar_t version of output string */ + size_t woslen; /* length of wostring */ + char *newraw; /* new raw */ + + /* 2012-11-06 SMS. + * Changed to check the value returned by mbstowcs(), and bypass the + * Unicode processing if it fails. This seems to fix a problem + * reported in the SourceForge forum, but it's not clear that we + * should be doing any Unicode processing without some evidence that + * the name actually is Unicode. (Check bit 11 in the flags before + * coming here?) + * http://sourceforge.net/p/infozip/bugs/40/ + */ + + if (MB_CUR_MAX <= 1) + { + /* There's no point to converting multi-byte chars if there are + * no multi-byte chars. + */ + wslen = (size_t)-1; } - while (*r) { - if (size > 0 && s >= slim && se == NULL) { - se = s; + else + { + /* Get Unicode wide character count (for storage allocation). */ + wslen = mbstowcs( NULL, raw, 0); + } + + if (wslen != (size_t)-1) + { + /* Apparently valid Unicode. Allocate wide-char storage. */ + wstring = (wchar_t *)malloc((wslen + 1) * sizeof(wchar_t)); + if (wstring == NULL) { + strcpy( (char *)space, raw); + return (char *)space; } -#ifdef QDOS - if (qlflag & 2) { - if (*r == '/' || *r == '.') { + wostring = (wchar_t *)malloc(2 * (wslen + 1) * sizeof(wchar_t)); + if (wostring == NULL) { + free(wstring); + strcpy( (char *)space, raw); + return (char *)space; + } + + /* Convert the multi-byte Unicode to wide chars. */ + wslen = mbstowcs(wstring, raw, wslen + 1); + + /* Filter the wide-character string. */ + fnfilterw( wstring, wostring, (2 * (wslen + 1) * sizeof(wchar_t))); + + /* Convert filtered wide chars back to multi-byte. */ + woslen = wcstombs( NULL, wostring, 0); + if ((newraw = malloc(woslen + 1)) == NULL) { + free(wstring); + free(wostring); + strcpy( (char *)space, raw); + return (char *)space; + } + woslen = wcstombs( newraw, wostring, (woslen * MB_CUR_MAX) + 1); + + if (size > 0) { + slim = space + size - 4; + } + r = (ZCONST uch *)newraw; + while (*r) { + if (size > 0 && s >= slim && se == NULL) { + se = s; + } +# ifdef QDOS + if (qlflag & 2) { + if (*r == '/' || *r == '.') { + if (se != NULL && (s > (space + (size-3)))) { + have_overflow = TRUE; + break; + } + ++r; + *s++ = '_'; + continue; + } + } else +# endif + { if (se != NULL && (s > (space + (size-3)))) { have_overflow = TRUE; break; } - ++r; - *s++ = '_'; - continue; + *s++ = *r++; } - } else + } + if (have_overflow) { + strcpy((char *)se, "..."); + } else { + *s = '\0'; + } + + free(wstring); + free(wostring); + free(newraw); + } + else +# endif /* defined( UNICODE_SUPPORT) && defined( _MBCS) */ + { + /* No Unicode support, or apparently invalid Unicode. */ + r = (ZCONST uch *)raw; + + if (size > 0) { + slim = space + size +#ifdef _MBCS + - (MB_CUR_MAX - 1) +#endif + - 4; + } + while (*r) { + if (size > 0 && s >= slim && se == NULL) { + se = s; + } +#ifdef QDOS + if (qlflag & 2) { + if (*r == '/' || *r == '.') { + if (se != NULL && (s > (space + (size-3)))) { + have_overflow = TRUE; + break; + } + ++r; + *s++ = '_'; + continue; + } + } else #endif #ifdef HAVE_WORKING_ISPRINT -# ifndef UZ_FNFILTER_REPLACECHAR - /* A convenient choice for the replacement of unprintable char codes is - * the "single char wildcard", as this character is quite unlikely to - * appear in filenames by itself. The following default definition - * sets the replacement char to a question mark as the most common - * "single char wildcard"; this setting should be overridden in the - * appropiate system-specific configuration header when needed. - */ -# define UZ_FNFILTER_REPLACECHAR '?' -# endif - if (!isprint(*r)) { + if (!isprint(*r)) { + if (*r < 32) { + /* ASCII control codes are escaped as "^{letter}". */ + if (se != NULL && (s > (space + (size-4)))) { + have_overflow = TRUE; + break; + } + *s++ = '^', *s++ = (uch)(64 + *r++); + } else { + /* Other unprintable codes are replaced by the + * placeholder character. */ + if (se != NULL && (s > (space + (size-3)))) { + have_overflow = TRUE; + break; + } + *s++ = UZ_FNFILTER_REPLACECHAR; + INCSTR(r); + } +#else /* !HAVE_WORKING_ISPRINT */ if (*r < 32) { /* ASCII control codes are escaped as "^{letter}". */ if (se != NULL && (s > (space + (size-4)))) { @@ -2710,47 +2843,30 @@ char *fnfilter(raw, space, size) /* convert name to safely printable form */ break; } *s++ = '^', *s++ = (uch)(64 + *r++); +#endif /* ?HAVE_WORKING_ISPRINT */ } else { - /* Other unprintable codes are replaced by the - * placeholder character. */ +#ifdef _MBCS + unsigned i = CLEN(r); + if (se != NULL && (s > (space + (size-i-2)))) { + have_overflow = TRUE; + break; + } + for (; i > 0; i--) + *s++ = *r++; +#else if (se != NULL && (s > (space + (size-3)))) { have_overflow = TRUE; break; } - *s++ = UZ_FNFILTER_REPLACECHAR; - INCSTR(r); - } -#else /* !HAVE_WORKING_ISPRINT */ - if (*r < 32) { - /* ASCII control codes are escaped as "^{letter}". */ - if (se != NULL && (s > (space + (size-4)))) { - have_overflow = TRUE; - break; - } - *s++ = '^', *s++ = (uch)(64 + *r++); -#endif /* ?HAVE_WORKING_ISPRINT */ - } else { -#ifdef _MBCS - unsigned i = CLEN(r); - if (se != NULL && (s > (space + (size-i-2)))) { - have_overflow = TRUE; - break; - } - for (; i > 0; i--) *s++ = *r++; -#else - if (se != NULL && (s > (space + (size-3)))) { - have_overflow = TRUE; - break; - } - *s++ = *r++; #endif - } - } - if (have_overflow) { - strcpy((char *)se, "..."); - } else { - *s = '\0'; + } + } + if (have_overflow) { + strcpy((char *)se, "..."); + } else { + *s = '\0'; + } } #ifdef WINDLL @@ -2772,6 +2888,53 @@ char *fnfilter(raw, space, size) /* convert name to safely printable form */ } /* end function fnfilter() */ +#if defined( UNICODE_SUPPORT) && defined( _MBCS) + +/****************************/ +/* Function fnfilter[w]() */ /* (Here instead of in list.c for SFX.) */ +/****************************/ + +/* fnfilterw() - Convert wide name to safely printable form. */ + +/* fnfilterw() - Convert wide-character name to safely printable form. */ + +wchar_t *fnfilterw( src, dst, siz) + ZCONST wchar_t *src; /* Pointer to source char (string). */ + wchar_t *dst; /* Pointer to destination char (string). */ + extent siz; /* Not used (!). */ +{ + wchar_t *dsx = dst; + + /* Filter the wide chars. */ + while (*src) + { + if (iswprint( *src)) + { + /* Printable code. Copy it. */ + *dst++ = *src; + } + else + { + /* Unprintable code. Substitute something printable for it. */ + if (*src < 32) + { + /* Replace ASCII control code with "^{letter}". */ + *dst++ = (wchar_t)'^'; + *dst++ = (wchar_t)(64 + *src); + } + else + { + /* Replace other unprintable code with the placeholder. */ + *dst++ = (wchar_t)UZ_FNFILTER_REPLACECHAR; + } + } + src++; + } + *dst = (wchar_t)0; /* NUL-terminate the destination string. */ + return dsx; +} /* fnfilterw(). */ + +#endif /* defined( UNICODE_SUPPORT) && defined( _MBCS) */ #ifdef SET_DIR_ATTRIB diff --git a/unzpriv.h b/unzpriv.h index 22d3923..e48a652 100644 --- a/unzpriv.h +++ b/unzpriv.h @@ -1212,6 +1212,7 @@ # ifdef UNICODE_WCHAR # if !(defined(_WIN32_WCE) || defined(POCKET_UNZIP)) # include +# include # endif # endif # ifndef _MBCS /* no need to include twice, see below */ @@ -2410,6 +2411,12 @@ int memflush OF((__GPRO__ ZCONST uch *rawbuf, ulg size)); char *fnfilter OF((ZCONST char *raw, uch *space, extent size)); +# if defined( UNICODE_SUPPORT) && defined( _MBCS) +wchar_t *fnfilterw OF((ZCONST wchar_t *src, wchar_t *dst, + extent siz)); +#endif + + /*--------------------------------------------------------------------------- Decompression functions: ---------------------------------------------------------------------------*/ -- 2.4.3 From 754137e70cf58a64ad524b704a86b651ba0cde07 Mon Sep 17 00:00:00 2001 From: Petr Stodulka Date: Wed, 14 Dec 2016 16:30:36 +0100 Subject: [PATCH] Fix CVE-2016-9844 (rhbz#1404283) Fixes buffer overflow in zipinfo in similar way like fix for CVE-2014-9913 provided by upstream. --- zipinfo.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/zipinfo.c b/zipinfo.c index c03620e..accca2a 100644 --- a/zipinfo.c +++ b/zipinfo.c @@ -1984,7 +1984,19 @@ static int zi_short(__G) /* return PK-type error code */ ush dnum=(ush)((G.crec.general_purpose_bit_flag>>1) & 3); methbuf[3] = dtype[dnum]; } else if (methnum >= NUM_METHODS) { /* unknown */ - sprintf(&methbuf[1], "%03u", G.crec.compression_method); + /* 2016-12-05 SMS. + * https://launchpad.net/bugs/1643750 + * Unexpectedly large compression methods overflow + * &methbuf[]. Use the old, three-digit decimal format + * for values which fit. Otherwise, sacrifice the "u", + * and use four-digit hexadecimal. + */ + if (G.crec.compression_method <= 999) { + sprintf( &methbuf[ 1], "%03u", G.crec.compression_method); + } else { + sprintf( &methbuf[ 0], "%04X", G.crec.compression_method); + } + } for (k = 0; k < 15; ++k) -- 2.5.5 From: "Steven M. Schweda" Subject: Do not ignore extra fields containing Unix Timestamps Bug-Debian: https://bugs.debian.org/842993 X-Debian-version: 6.0-21 --- a/process.c +++ b/process.c @@ -2914,10 +2914,13 @@ break; case EF_IZUNIX2: - if (have_new_type_eb == 0) { - flags &= ~0x0ff; /* ignore any previous IZUNIX field */ + if (have_new_type_eb == 0) { /* (< 1) */ have_new_type_eb = 1; } + if (have_new_type_eb <= 1) { + /* Ignore any prior (EF_IZUNIX/EF_PKUNIX) UID/GID. */ + flags &= 0x0ff; + } #ifdef IZ_HAVE_UXUIDGID if (have_new_type_eb > 1) break; /* IZUNIX3 overrides IZUNIX2 e.f. block ! */ @@ -2933,6 +2936,8 @@ /* new 3rd generation Unix ef */ have_new_type_eb = 2; + /* Ignore any prior EF_IZUNIX/EF_PKUNIX/EF_IZUNIX2 UID/GID. */ + flags &= 0x0ff; /* Version 1 byte version of this extra field, currently 1 UIDSize 1 byte Size of UID field @@ -2953,8 +2958,6 @@ uid_size = *((EB_HEADSIZE + 1) + ef_buf); gid_size = *((EB_HEADSIZE + uid_size + 2) + ef_buf); - flags &= ~0x0ff; /* ignore any previous UNIX field */ - if ( read_ux3_value((EB_HEADSIZE + 2) + ef_buf, uid_size, &z_uidgid[0]) && --- a/fileio.c 2014-12-05 05:06:05 -0600 +++ b/fileio.c 2017-11-14 01:06:28 -0600 @@ -1,5 +1,5 @@ /* - Copyright (c) 1990-2009 Info-ZIP. All rights reserved. + Copyright (c) 1990-2017 Info-ZIP. All rights reserved. See the accompanying file LICENSE, version 2009-Jan-02 or later (the contents of which are also included in unzip.h) for terms of use. @@ -1582,6 +1582,8 @@ int r = IZ_PW_ENTERED; char *m; char *prompt; + char *ep; + char *zp; #ifndef REENTRANT /* tell picky compilers to shut up about "unused variable" warnings */ @@ -1590,9 +1592,12 @@ if (*rcnt == 0) { /* First call for current entry */ *rcnt = 2; - if ((prompt = (char *)malloc(2*FILNAMSIZ + 15)) != (char *)NULL) { - sprintf(prompt, LoadFarString(PasswPrompt), - FnFilter1(zfn), FnFilter2(efn)); + zp = FnFilter1( zfn); + ep = FnFilter2( efn); + prompt = (char *)malloc( /* Slightly too long (2* "%s"). */ + sizeof( PasswPrompt)+ strlen( zp)+ strlen( ep)); + if (prompt != (char *)NULL) { + sprintf(prompt, LoadFarString(PasswPrompt), zp, ep); m = prompt; } else m = (char *)LoadFarString(PasswPrompt2); --- unzip60/list.c +++ unzip60/list.c @@ -97,7 +97,7 @@ int list_files(__G) /* return PK-type { int do_this_file=FALSE, cfactor, error, error_in_archive=PK_COOL; #ifndef WINDLL - char sgn, cfactorstr[13]; + char sgn, cfactorstr[1+10+1+1]; /* %NUL */ int longhdr=(uO.vflag>1); #endif int date_format; @@ -389,9 +389,9 @@ int list_files(__G) /* return PK-type } #else /* !WINDLL */ if (cfactor == 100) - sprintf(cfactorstr, LoadFarString(CompFactor100)); + snprintf(cfactorstr, sizeof(cfactorstr), LoadFarString(CompFactor100)); else - sprintf(cfactorstr, LoadFarString(CompFactorStr), sgn, cfactor); + snprintf(cfactorstr, sizeof(cfactorstr), LoadFarString(CompFactorStr), sgn, cfactor); if (longhdr) Info(slide, 0, ((char *)slide, LoadFarString(LongHdrStats), FmZofft(G.crec.ucsize, "8", "u"), methbuf, @@ -471,9 +471,9 @@ int list_files(__G) /* return PK-type #else /* !WINDLL */ if (cfactor == 100) - sprintf(cfactorstr, LoadFarString(CompFactor100)); + snprintf(cfactorstr, sizeof(cfactorstr), LoadFarString(CompFactor100)); else - sprintf(cfactorstr, LoadFarString(CompFactorStr), sgn, cfactor); + snprintf(cfactorstr, sizeof(cfactorstr), LoadFarString(CompFactorStr), sgn, cfactor); if (longhdr) { Info(slide, 0, ((char *)slide, LoadFarString(LongFileTrailer), FmZofft(tot_ucsize, "8", "u"), FmZofft(tot_csize, "8", "u"), From 06d1b08aef94984256cad3c5a54cedb10295681f Mon Sep 17 00:00:00 2001 From: Jakub Martisko Date: Thu, 8 Nov 2018 09:31:18 +0100 Subject: [PATCH] Possible unterminated string fix --- unix/unix.c | 4 +++- unix/unxcfg.h | 2 +- unzip.c | 12 ++++++++---- zipinfo.c | 12 ++++++++---- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/unix/unix.c b/unix/unix.c index 59b622d..cd57f80 100644 --- a/unix/unix.c +++ b/unix/unix.c @@ -1945,7 +1945,9 @@ void init_conversion_charsets() for(i = 0; i < sizeof(dos_charset_map)/sizeof(CHARSET_MAP); i++) if(!strcasecmp(local_charset, dos_charset_map[i].local_charset)) { strncpy(OEM_CP, dos_charset_map[i].archive_charset, - sizeof(OEM_CP)); + MAX_CP_NAME - 1); + + OEM_CP[MAX_CP_NAME - 1] = '\0'; break; } } diff --git a/unix/unxcfg.h b/unix/unxcfg.h index 8729de2..9ee8cfe 100644 --- a/unix/unxcfg.h +++ b/unix/unxcfg.h @@ -228,7 +228,7 @@ typedef struct stat z_stat; /* and notfirstcall are used by do_wild(). */ -#define MAX_CP_NAME 25 +#define MAX_CP_NAME 25 + 1 #ifdef SETLOCALE # undef SETLOCALE diff --git a/unzip.c b/unzip.c index 2d94a38..a485f2b 100644 --- a/unzip.c +++ b/unzip.c @@ -1561,7 +1561,8 @@ int uz_opts(__G__ pargc, pargv) "error: a valid character encoding should follow the -I argument")); return(PK_PARAM); } - strncpy(ISO_CP, s, sizeof(ISO_CP)); + strncpy(ISO_CP, s, MAX_CP_NAME - 1); + ISO_CP[MAX_CP_NAME - 1] = '\0'; } else { /* -I charset */ ++argv; if(!(--argc > 0 && *argv != NULL && **argv != '-')) { @@ -1570,7 +1571,8 @@ int uz_opts(__G__ pargc, pargv) return(PK_PARAM); } s = *argv; - strncpy(ISO_CP, s, sizeof(ISO_CP)); + strncpy(ISO_CP, s, MAX_CP_NAME - 1); + ISO_CP[MAX_CP_NAME - 1] = '\0'; } while(*(++s)); /* No params straight after charset name */ } @@ -1665,7 +1667,8 @@ int uz_opts(__G__ pargc, pargv) "error: a valid character encoding should follow the -I argument")); return(PK_PARAM); } - strncpy(OEM_CP, s, sizeof(OEM_CP)); + strncpy(OEM_CP, s, MAX_CP_NAME - 1); + OEM_CP[MAX_CP_NAME - 1] = '\0'; } else { /* -O charset */ ++argv; if(!(--argc > 0 && *argv != NULL && **argv != '-')) { @@ -1674,7 +1677,8 @@ int uz_opts(__G__ pargc, pargv) return(PK_PARAM); } s = *argv; - strncpy(OEM_CP, s, sizeof(OEM_CP)); + strncpy(OEM_CP, s, MAX_CP_NAME - 1); + OEM_CP[MAX_CP_NAME - 1] = '\0'; } while(*(++s)); /* No params straight after charset name */ } diff --git a/zipinfo.c b/zipinfo.c index accca2a..cb7e08d 100644 --- a/zipinfo.c +++ b/zipinfo.c @@ -519,7 +519,8 @@ int zi_opts(__G__ pargc, pargv) "error: a valid character encoding should follow the -I argument")); return(PK_PARAM); } - strncpy(ISO_CP, s, sizeof(ISO_CP)); + strncpy(ISO_CP, s, MAX_CP_NAME - 1); + ISO_CP[MAX_CP_NAME - 1] = '\0'; } else { /* -I charset */ ++argv; if(!(--argc > 0 && *argv != NULL && **argv != '-')) { @@ -528,7 +529,8 @@ int zi_opts(__G__ pargc, pargv) return(PK_PARAM); } s = *argv; - strncpy(ISO_CP, s, sizeof(ISO_CP)); + strncpy(ISO_CP, s, MAX_CP_NAME - 1); + ISO_CP[MAX_CP_NAME - 1] = '\0'; } while(*(++s)); /* No params straight after charset name */ } @@ -568,7 +570,8 @@ int zi_opts(__G__ pargc, pargv) "error: a valid character encoding should follow the -I argument")); return(PK_PARAM); } - strncpy(OEM_CP, s, sizeof(OEM_CP)); + strncpy(OEM_CP, s, MAX_CP_NAME - 1); + OEM_CP[MAX_CP_NAME - 1] = '\0'; } else { /* -O charset */ ++argv; if(!(--argc > 0 && *argv != NULL && **argv != '-')) { @@ -577,7 +580,8 @@ int zi_opts(__G__ pargc, pargv) return(PK_PARAM); } s = *argv; - strncpy(OEM_CP, s, sizeof(OEM_CP)); + strncpy(OEM_CP, s, MAX_CP_NAME - 1); + OEM_CP[MAX_CP_NAME - 1] = '\0'; } while(*(++s)); /* No params straight after charset name */ } -- 2.14.5 From 41beb477c5744bc396fa1162ee0c14218ec12213 Mon Sep 17 00:00:00 2001 From: Mark Adler Date: Mon, 27 May 2019 08:20:32 -0700 Subject: [PATCH] Fix bug in undefer_input() that misplaced the input state. --- fileio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fileio.c b/fileio.c index c042987..bc00d74 100644 --- a/fileio.c +++ b/fileio.c @@ -530,8 +530,10 @@ void undefer_input(__G) * This condition was checked when G.incnt_leftover was set > 0 in * defer_leftover_input(), and it is NOT allowed to touch G.csize * before calling undefer_input() when (G.incnt_leftover > 0) - * (single exception: see read_byte()'s "G.csize <= 0" handling) !! + * (single exception: see readbyte()'s "G.csize <= 0" handling) !! */ + if (G.csize < 0L) + G.csize = 0L; G.incnt = G.incnt_leftover + (int)G.csize; G.inptr = G.inptr_leftover - (int)G.csize; G.incnt_leftover = 0; From 47b3ceae397d21bf822bc2ac73052a4b1daf8e1c Mon Sep 17 00:00:00 2001 From: Mark Adler Date: Tue, 11 Jun 2019 22:01:18 -0700 Subject: [PATCH] Detect and reject a zip bomb using overlapped entries. This detects an invalid zip file that has at least one entry that overlaps with another entry or with the central directory to the end of the file. A Fifield zip bomb uses overlapped local entries to vastly increase the potential inflation ratio. Such an invalid zip file is rejected. See https://www.bamsoftware.com/hacks/zipbomb/ for David Fifield's analysis, construction, and examples of such zip bombs. The detection maintains a list of covered spans of the zip files so far, where the central directory to the end of the file and any bytes preceding the first entry at zip file offset zero are considered covered initially. Then as each entry is decompressed or tested, it is considered covered. When a new entry is about to be processed, its initial offset is checked to see if it is contained by a covered span. If so, the zip file is rejected as invalid. This commit depends on a preceding commit: "Fix bug in undefer_input() that misplaced the input state." --- extract.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++- globals.c | 1 + globals.h | 3 + process.c | 11 ++++ unzip.h | 1 + 5 files changed, 205 insertions(+), 1 deletion(-) diff --git a/extract.c b/extract.c index 1acd769..0973a33 100644 --- a/extract.c +++ b/extract.c @@ -319,6 +319,125 @@ static ZCONST char Far UnsupportedExtraField[] = "\nerror: unsupported extra-field compression type (%u)--skipping\n"; static ZCONST char Far BadExtraFieldCRC[] = "error [%s]: bad extra-field CRC %08lx (should be %08lx)\n"; +static ZCONST char Far NotEnoughMemCover[] = + "error: not enough memory for bomb detection\n"; +static ZCONST char Far OverlappedComponents[] = + "error: invalid zip file with overlapped components (possible zip bomb)\n"; + + + + + +/* A growable list of spans. */ +typedef zoff_t bound_t; +typedef struct { + bound_t beg; /* start of the span */ + bound_t end; /* one past the end of the span */ +} span_t; +typedef struct { + span_t *span; /* allocated, distinct, and sorted list of spans */ + size_t num; /* number of spans in the list */ + size_t max; /* allocated number of spans (num <= max) */ +} cover_t; + +/* + * Return the index of the first span in cover whose beg is greater than val. + * If there is no such span, then cover->num is returned. + */ +static size_t cover_find(cover, val) + cover_t *cover; + bound_t val; +{ + size_t lo = 0, hi = cover->num; + while (lo < hi) { + size_t mid = (lo + hi) >> 1; + if (val < cover->span[mid].beg) + hi = mid; + else + lo = mid + 1; + } + return hi; +} + +/* Return true if val lies within any one of the spans in cover. */ +static int cover_within(cover, val) + cover_t *cover; + bound_t val; +{ + size_t pos = cover_find(cover, val); + return pos > 0 && val < cover->span[pos - 1].end; +} + +/* + * Add a new span to the list, but only if the new span does not overlap any + * spans already in the list. The new span covers the values beg..end-1. beg + * must be less than end. + * + * Keep the list sorted and merge adjacent spans. Grow the allocated space for + * the list as needed. On success, 0 is returned. If the new span overlaps any + * existing spans, then 1 is returned and the new span is not added to the + * list. If the new span is invalid because beg is greater than or equal to + * end, then -1 is returned. If the list needs to be grown but the memory + * allocation fails, then -2 is returned. + */ +static int cover_add(cover, beg, end) + cover_t *cover; + bound_t beg; + bound_t end; +{ + size_t pos; + int prec, foll; + + if (beg >= end) + /* The new span is invalid. */ + return -1; + + /* Find where the new span should go, and make sure that it does not + overlap with any existing spans. */ + pos = cover_find(cover, beg); + if ((pos > 0 && beg < cover->span[pos - 1].end) || + (pos < cover->num && end > cover->span[pos].beg)) + return 1; + + /* Check for adjacencies. */ + prec = pos > 0 && beg == cover->span[pos - 1].end; + foll = pos < cover->num && end == cover->span[pos].beg; + if (prec && foll) { + /* The new span connects the preceding and following spans. Merge the + following span into the preceding span, and delete the following + span. */ + cover->span[pos - 1].end = cover->span[pos].end; + cover->num--; + memmove(cover->span + pos, cover->span + pos + 1, + (cover->num - pos) * sizeof(span_t)); + } + else if (prec) + /* The new span is adjacent only to the preceding span. Extend the end + of the preceding span. */ + cover->span[pos - 1].end = end; + else if (foll) + /* The new span is adjacent only to the following span. Extend the + beginning of the following span. */ + cover->span[pos].beg = beg; + else { + /* The new span has gaps between both the preceding and the following + spans. Assure that there is room and insert the span. */ + if (cover->num == cover->max) { + size_t max = cover->max == 0 ? 16 : cover->max << 1; + span_t *span = realloc(cover->span, max * sizeof(span_t)); + if (span == NULL) + return -2; + cover->span = span; + cover->max = max; + } + memmove(cover->span + pos + 1, cover->span + pos, + (cover->num - pos) * sizeof(span_t)); + cover->num++; + cover->span[pos].beg = beg; + cover->span[pos].end = end; + } + return 0; +} @@ -374,6 +493,29 @@ int extract_or_test_files(__G) /* return PK-type error code */ } #endif /* !SFX || SFX_EXDIR */ + /* One more: initialize cover structure for bomb detection. Start with a + span that covers the central directory though the end of the file. */ + if (G.cover == NULL) { + G.cover = malloc(sizeof(cover_t)); + if (G.cover == NULL) { + Info(slide, 0x401, ((char *)slide, + LoadFarString(NotEnoughMemCover))); + return PK_MEM; + } + ((cover_t *)G.cover)->span = NULL; + ((cover_t *)G.cover)->max = 0; + } + ((cover_t *)G.cover)->num = 0; + if ((G.extra_bytes != 0 && + cover_add((cover_t *)G.cover, 0, G.extra_bytes) != 0) || + cover_add((cover_t *)G.cover, + G.extra_bytes + G.ecrec.offset_start_central_directory, + G.ziplen) != 0) { + Info(slide, 0x401, ((char *)slide, + LoadFarString(NotEnoughMemCover))); + return PK_MEM; + } + /*--------------------------------------------------------------------------- The basic idea of this function is as follows. Since the central di- rectory lies at the end of the zipfile and the member files lie at the @@ -591,7 +733,8 @@ int extract_or_test_files(__G) /* return PK-type error code */ if (error > error_in_archive) error_in_archive = error; /* ...and keep going (unless disk full or user break) */ - if (G.disk_full > 1 || error_in_archive == IZ_CTRLC) { + if (G.disk_full > 1 || error_in_archive == IZ_CTRLC || + error == PK_BOMB) { /* clear reached_end to signal premature stop ... */ reached_end = FALSE; /* ... and cancel scanning the central directory */ @@ -1060,6 +1203,11 @@ static int extract_or_test_entrylist(__G__ numchunk, /* seek_zipf(__G__ pInfo->offset); */ request = G.pInfo->offset + G.extra_bytes; + if (cover_within((cover_t *)G.cover, request)) { + Info(slide, 0x401, ((char *)slide, + LoadFarString(OverlappedComponents))); + return PK_BOMB; + } inbuf_offset = request % INBUFSIZ; bufstart = request - inbuf_offset; @@ -1591,6 +1739,18 @@ static int extract_or_test_entrylist(__G__ numchunk, return IZ_CTRLC; /* cancel operation by user request */ } #endif + error = cover_add((cover_t *)G.cover, request, + G.cur_zipfile_bufstart + (G.inptr - G.inbuf)); + if (error < 0) { + Info(slide, 0x401, ((char *)slide, + LoadFarString(NotEnoughMemCover))); + return PK_MEM; + } + if (error != 0) { + Info(slide, 0x401, ((char *)slide, + LoadFarString(OverlappedComponents))); + return PK_BOMB; + } #ifdef MACOS /* MacOS is no preemptive OS, thus call event-handling by hand */ UserStop(); #endif @@ -1992,6 +2152,34 @@ static int extract_or_test_member(__G) /* return PK-type error code */ } undefer_input(__G); + + if ((G.lrec.general_purpose_bit_flag & 8) != 0) { + /* skip over data descriptor (harder than it sounds, due to signature + * ambiguity) + */ +# define SIG 0x08074b50 +# define LOW 0xffffffff + uch buf[12]; + unsigned shy = 12 - readbuf((char *)buf, 12); + ulg crc = shy ? 0 : makelong(buf); + ulg clen = shy ? 0 : makelong(buf + 4); + ulg ulen = shy ? 0 : makelong(buf + 8); /* or high clen if ZIP64 */ + if (crc == SIG && /* if not SIG, no signature */ + (G.lrec.crc32 != SIG || /* if not SIG, have signature */ + (clen == SIG && /* if not SIG, no signature */ + ((G.lrec.csize & LOW) != SIG || /* if not SIG, have signature */ + (ulen == SIG && /* if not SIG, no signature */ + (G.zip64 ? G.lrec.csize >> 32 : G.lrec.ucsize) != SIG + /* if not SIG, have signature */ + ))))) + /* skip four more bytes to account for signature */ + shy += 4 - readbuf((char *)buf, 4); + if (G.zip64) + shy += 8 - readbuf((char *)buf, 8); /* skip eight more for ZIP64 */ + if (shy) + error = PK_ERR; + } + return error; } /* end function extract_or_test_member() */ diff --git a/globals.c b/globals.c index fa8cca5..1e0f608 100644 --- a/globals.c +++ b/globals.c @@ -181,6 +181,7 @@ Uz_Globs *globalsCtor() # if (!defined(NO_TIMESTAMPS)) uO.D_flag=1; /* default to '-D', no restoration of dir timestamps */ # endif + G.cover = NULL; /* not allocated yet */ #endif uO.lflag=(-1); diff --git a/globals.h b/globals.h index 11b7215..2bdcdeb 100644 --- a/globals.h +++ b/globals.h @@ -260,12 +260,15 @@ typedef struct Globals { ecdir_rec ecrec; /* used in unzip.c, extract.c */ z_stat statbuf; /* used by main, mapname, check_for_newer */ + int zip64; /* true if Zip64 info in extra field */ + int mem_mode; uch *outbufptr; /* extract.c static */ ulg outsize; /* extract.c static */ int reported_backslash; /* extract.c static */ int disk_full; int newfile; + void **cover; /* used in extract.c for bomb detection */ int didCRlast; /* fileio static */ ulg numlines; /* fileio static: number of lines printed */ diff --git a/process.c b/process.c index 1e9a1e1..d2e4dc3 100644 --- a/process.c +++ b/process.c @@ -637,6 +637,13 @@ void free_G_buffers(__G) /* releases all memory allocated in global vars */ } #endif + /* Free the cover span list and the cover structure. */ + if (G.cover != NULL) { + free(*(G.cover)); + free(G.cover); + G.cover = NULL; + } + } /* end function free_G_buffers() */ @@ -1890,6 +1897,8 @@ int getZip64Data(__G__ ef_buf, ef_len) #define Z64FLGS 0xffff #define Z64FLGL 0xffffffff + G.zip64 = FALSE; + if (ef_len == 0 || ef_buf == NULL) return PK_COOL; @@ -1927,6 +1936,8 @@ int getZip64Data(__G__ ef_buf, ef_len) #if 0 break; /* Expect only one EF_PKSZ64 block. */ #endif /* 0 */ + + G.zip64 = TRUE; } /* Skip this extra field block. */ diff --git a/unzip.h b/unzip.h index 5b2a326..ed24a5b 100644 --- a/unzip.h +++ b/unzip.h @@ -645,6 +645,7 @@ typedef struct _Uzp_cdir_Rec { #define PK_NOZIP 9 /* zipfile not found */ #define PK_PARAM 10 /* bad or illegal parameters specified */ #define PK_FIND 11 /* no files found */ +#define PK_BOMB 12 /* likely zip bomb */ #define PK_DISK 50 /* disk full */ #define PK_EOF 51 /* unexpected EOF */ From 6d351831be705cc26d897db44f878a978f4138fc Mon Sep 17 00:00:00 2001 From: Mark Adler Date: Thu, 25 Jul 2019 20:43:17 -0700 Subject: [PATCH] Do not raise a zip bomb alert for a misplaced central directory. There is a zip-like file in the Firefox distribution, omni.ja, which is a zip container with the central directory placed at the start of the file instead of after the local entries as required by the zip standard. This commit marks the actual location of the central directory, as well as the end of central directory records, as disallowed locations. This now permits such containers to not raise a zip bomb alert, where in fact there are no overlaps. --- extract.c | 25 +++++++++++++++++++------ process.c | 6 ++++++ unzpriv.h | 10 ++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/extract.c b/extract.c index 0973a33..1b73cb0 100644 --- a/extract.c +++ b/extract.c @@ -493,8 +493,11 @@ int extract_or_test_files(__G) /* return PK-type error code */ } #endif /* !SFX || SFX_EXDIR */ - /* One more: initialize cover structure for bomb detection. Start with a - span that covers the central directory though the end of the file. */ + /* One more: initialize cover structure for bomb detection. Start with + spans that cover any extra bytes at the start, the central directory, + the end of central directory record (including the Zip64 end of central + directory locator, if present), and the Zip64 end of central directory + record, if present. */ if (G.cover == NULL) { G.cover = malloc(sizeof(cover_t)); if (G.cover == NULL) { @@ -506,15 +509,25 @@ int extract_or_test_files(__G) /* return PK-type error code */ ((cover_t *)G.cover)->max = 0; } ((cover_t *)G.cover)->num = 0; - if ((G.extra_bytes != 0 && - cover_add((cover_t *)G.cover, 0, G.extra_bytes) != 0) || - cover_add((cover_t *)G.cover, + if (cover_add((cover_t *)G.cover, G.extra_bytes + G.ecrec.offset_start_central_directory, - G.ziplen) != 0) { + G.extra_bytes + G.ecrec.offset_start_central_directory + + G.ecrec.size_central_directory) != 0) { Info(slide, 0x401, ((char *)slide, LoadFarString(NotEnoughMemCover))); return PK_MEM; } + if ((G.extra_bytes != 0 && + cover_add((cover_t *)G.cover, 0, G.extra_bytes) != 0) || + (G.ecrec.have_ecr64 && + cover_add((cover_t *)G.cover, G.ecrec.ec64_start, + G.ecrec.ec64_end) != 0) || + cover_add((cover_t *)G.cover, G.ecrec.ec_start, + G.ecrec.ec_end) != 0) { + Info(slide, 0x401, ((char *)slide, + LoadFarString(OverlappedComponents))); + return PK_BOMB; + } /*--------------------------------------------------------------------------- The basic idea of this function is as follows. Since the central di- diff --git a/process.c b/process.c index d2e4dc3..d75d405 100644 --- a/process.c +++ b/process.c @@ -1408,6 +1408,10 @@ static int find_ecrec64(__G__ searchlen) /* return PK-class error */ /* Now, we are (almost) sure that we have a Zip64 archive. */ G.ecrec.have_ecr64 = 1; + G.ecrec.ec_start -= ECLOC64_SIZE+4; + G.ecrec.ec64_start = ecrec64_start_offset; + G.ecrec.ec64_end = ecrec64_start_offset + + 12 + makeint64(&byterec[ECREC64_LENGTH]); /* Update the "end-of-central-dir offset" for later checks. */ G.real_ecrec_offset = ecrec64_start_offset; @@ -1542,6 +1546,8 @@ static int find_ecrec(__G__ searchlen) /* return PK-class error */ makelong(&byterec[OFFSET_START_CENTRAL_DIRECTORY]); G.ecrec.zipfile_comment_length = makeword(&byterec[ZIPFILE_COMMENT_LENGTH]); + G.ecrec.ec_start = G.real_ecrec_offset; + G.ecrec.ec_end = G.ecrec.ec_start + 22 + G.ecrec.zipfile_comment_length; /* Now, we have to read the archive comment, BEFORE the file pointer is moved away backwards to seek for a Zip64 ECLOC64 structure. diff --git a/unzpriv.h b/unzpriv.h index dc9eff5..297b3c7 100644 --- a/unzpriv.h +++ b/unzpriv.h @@ -2185,6 +2185,16 @@ typedef struct VMStimbuf { int have_ecr64; /* valid Zip64 ecdir-record exists */ int is_zip64_archive; /* Zip64 ecdir-record is mandatory */ ush zipfile_comment_length; + zusz_t ec_start, ec_end; /* offsets of start and end of the + end of central directory record, + including if present the Zip64 + end of central directory locator, + which immediately precedes the + end of central directory record */ + zusz_t ec64_start, ec64_end; /* if have_ecr64 is true, then these + are the offsets of the start and + end of the Zip64 end of central + directory record */ } ecdir_rec; From 6fe72291a5563cdbcd2bdd87e36528537b7cdcfb Mon Sep 17 00:00:00 2001 From: Jakub Martisko Date: Mon, 18 Nov 2019 14:17:46 +0100 Subject: [PATCH] update the man page --- man/unzip.1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/man/unzip.1 b/man/unzip.1 index 21816d1..4d66073 100644 --- a/man/unzip.1 +++ b/man/unzip.1 @@ -850,6 +850,8 @@ the specified zipfiles were not found. invalid options were specified on the command line. .IP 11 no matching files were found. +.IP 12 +invalid zip file with overlapped components (possible zip-bomb). The zip-bomb checks can be disabled by using the UNZIP_DISABLE_ZIPBOMB_DETECTION=TRUE environment variable. .IP 50 the disk is (or was) full during extraction. .IP 51 -- 2.23.0 From 5e2efcd633a4a1fb95a129a75508e7d769e767be Mon Sep 17 00:00:00 2001 From: Mark Adler Date: Sun, 9 Feb 2020 20:36:28 -0800 Subject: [PATCH] Fix bug in UZbunzip2() that incorrectly updated G.incnt. The update assumed a full buffer, which is not always full. This could result in a false overlapped element detection when a small bzip2-compressed file was unzipped. This commit remedies that. --- extract.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extract.c b/extract.c index d9866f9..0cb7bfc 100644 --- a/extract.c +++ b/extract.c @@ -3010,7 +3010,7 @@ __GDEF #endif G.inptr = (uch *)bstrm.next_in; - G.incnt = (G.inbuf + INBUFSIZ) - G.inptr; /* reset for other routines */ + G.incnt -= G.inptr - G.inbuf; /* reset for other routines */ uzbunzip_cleanup_exit: err = BZ2_bzDecompressEnd(&bstrm); From 5c572555cf5d80309a07c30cf7a54b2501493720 Mon Sep 17 00:00:00 2001 From: Mark Adler Date: Sun, 9 Feb 2020 21:39:09 -0800 Subject: [PATCH] Fix bug in UZinflate() that incorrectly updated G.incnt. The update assumed a full buffer, which is not always full. This could result in a false overlapped element detection when a small deflate-compressed file was unzipped using an old zlib. This commit remedies that. --- inflate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inflate.c b/inflate.c index 2f5a015..70e3cc0 100644 --- a/inflate.c +++ b/inflate.c @@ -700,7 +700,7 @@ int UZinflate(__G__ is_defl64) G.dstrm.total_out)); G.inptr = (uch *)G.dstrm.next_in; - G.incnt = (G.inbuf + INBUFSIZ) - G.inptr; /* reset for other routines */ + G.incnt -= G.inptr - G.inbuf; /* reset for other routines */ uzinflate_cleanup_exit: err = inflateReset(&G.dstrm); From 122050bac16fae82a460ff739fb1ca0f106e9d85 Mon Sep 17 00:00:00 2001 From: Mark Adler Date: Sat, 2 Jan 2021 13:09:34 -0800 Subject: [PATCH] Determine Zip64 status entry-by-entry instead of for entire file. Fixes a bug for zip files with mixed Zip64 and not Zip64 entries, which resulted in an incorrect data descriptor length. The bug is seen when a Zip64 entry precedes a non-Zip64 entry, in which case the data descriptor would have been assumed to be larger than it is, resulting in an incorrect bomb warning due to a perceived overlap with the next entry. This commit determines and saves the Zip64 status for each entry based on the central directory, and then computes the length of each data descriptor accordingly. --- extract.c | 5 +++-- globals.h | 2 -- process.c | 4 +--- unzpriv.h | 1 + 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/extract.c b/extract.c index 504afd6..878817d 100644 --- a/extract.c +++ b/extract.c @@ -658,6 +658,7 @@ int extract_or_test_files(__G) /* return PK-type error code */ break; } } + G.pInfo->zip64 = FALSE; if ((error = do_string(__G__ G.crec.extra_field_length, EXTRA_FIELD)) != 0) { @@ -2187,12 +2188,12 @@ static int extract_or_test_member(__G) /* return PK-type error code */ (clen == SIG && /* if not SIG, no signature */ ((G.lrec.csize & LOW) != SIG || /* if not SIG, have signature */ (ulen == SIG && /* if not SIG, no signature */ - (G.zip64 ? G.lrec.csize >> 32 : G.lrec.ucsize) != SIG + (G.pInfo->zip64 ? G.lrec.csize >> 32 : G.lrec.ucsize) != SIG /* if not SIG, have signature */ ))))) /* skip four more bytes to account for signature */ shy += 4 - readbuf((char *)buf, 4); - if (G.zip64) + if (G.pInfo->zip64) shy += 8 - readbuf((char *)buf, 8); /* skip eight more for ZIP64 */ if (shy) error = PK_ERR; diff --git a/globals.h b/globals.h index f9c6daf..a883c90 100644 --- a/globals.h +++ b/globals.h @@ -261,8 +261,6 @@ typedef struct Globals { ecdir_rec ecrec; /* used in unzip.c, extract.c */ z_stat statbuf; /* used by main, mapname, check_for_newer */ - int zip64; /* true if Zip64 info in extra field */ - int mem_mode; uch *outbufptr; /* extract.c static */ ulg outsize; /* extract.c static */ diff --git a/process.c b/process.c index d75d405..d643c6f 100644 --- a/process.c +++ b/process.c @@ -1903,8 +1903,6 @@ int getZip64Data(__G__ ef_buf, ef_len) #define Z64FLGS 0xffff #define Z64FLGL 0xffffffff - G.zip64 = FALSE; - if (ef_len == 0 || ef_buf == NULL) return PK_COOL; @@ -1943,7 +1941,7 @@ int getZip64Data(__G__ ef_buf, ef_len) break; /* Expect only one EF_PKSZ64 block. */ #endif /* 0 */ - G.zip64 = TRUE; + G.pInfo->zip64 = TRUE; } /* Skip this extra field block. */ diff --git a/unzpriv.h b/unzpriv.h index 09f288e..75b3359 100644 --- a/unzpriv.h +++ b/unzpriv.h @@ -2034,6 +2034,7 @@ typedef struct min_info { #ifdef UNICODE_SUPPORT unsigned GPFIsUTF8: 1; /* crec gen_purpose_flag UTF-8 bit 11 is set */ #endif + unsigned zip64: 1; /* true if entry has Zip64 extra block */ #ifndef SFX char Far *cfilname; /* central header version of filename */ #endif From 5b44c818b96193b3e240f38f61985fa2bc780eb7 Mon Sep 17 00:00:00 2001 From: Jakub Martisko Date: Tue, 30 Nov 2021 15:42:17 +0100 Subject: [PATCH] Add an option to disable the zipbomb detection This can be done by settting a newly introduced environment variable UNZIP_DISABLE_ZIPBOMB_DETECTION to {TRUE,True,true}. If the variable is unset, or set to any other value the zipbomb detection is left enabled. Example: UNZIP_DISABLE_ZIPBOMB_DETECTION=True unzip ./zbsm.zip -d ./test --- extract.c | 85 ++++++++++++++++++++++++++++++------------------------- unzip.c | 15 ++++++++-- unzip.h | 1 + 3 files changed, 60 insertions(+), 41 deletions(-) diff --git a/extract.c b/extract.c index 878817d..3e58071 100644 --- a/extract.c +++ b/extract.c @@ -322,7 +322,8 @@ static ZCONST char Far BadExtraFieldCRC[] = static ZCONST char Far NotEnoughMemCover[] = "error: not enough memory for bomb detection\n"; static ZCONST char Far OverlappedComponents[] = - "error: invalid zip file with overlapped components (possible zip bomb)\n"; + "error: invalid zip file with overlapped components (possible zip bomb)\n \ +To unzip the file anyway, rerun the command with UNZIP_DISABLE_ZIPBOMB_DETECTION=TRUE environmnent variable\n"; @@ -502,35 +503,37 @@ int extract_or_test_files(__G) /* return PK-type error code */ the end of central directory record (including the Zip64 end of central directory locator, if present), and the Zip64 end of central directory record, if present. */ - if (G.cover == NULL) { + if (uO.zipbomb == TRUE) { + if (G.cover == NULL) { G.cover = malloc(sizeof(cover_t)); if (G.cover == NULL) { - Info(slide, 0x401, ((char *)slide, - LoadFarString(NotEnoughMemCover))); - return PK_MEM; + Info(slide, 0x401, ((char *)slide, + LoadFarString(NotEnoughMemCover))); + return PK_MEM; } ((cover_t *)G.cover)->span = NULL; ((cover_t *)G.cover)->max = 0; - } - ((cover_t *)G.cover)->num = 0; - if (cover_add((cover_t *)G.cover, - G.extra_bytes + G.ecrec.offset_start_central_directory, - G.extra_bytes + G.ecrec.offset_start_central_directory + - G.ecrec.size_central_directory) != 0) { + } + ((cover_t *)G.cover)->num = 0; + if (cover_add((cover_t *)G.cover, + G.extra_bytes + G.ecrec.offset_start_central_directory, + G.extra_bytes + G.ecrec.offset_start_central_directory + + G.ecrec.size_central_directory) != 0) { Info(slide, 0x401, ((char *)slide, - LoadFarString(NotEnoughMemCover))); + LoadFarString(NotEnoughMemCover))); return PK_MEM; - } - if ((G.extra_bytes != 0 && - cover_add((cover_t *)G.cover, 0, G.extra_bytes) != 0) || - (G.ecrec.have_ecr64 && - cover_add((cover_t *)G.cover, G.ecrec.ec64_start, - G.ecrec.ec64_end) != 0) || - cover_add((cover_t *)G.cover, G.ecrec.ec_start, - G.ecrec.ec_end) != 0) { + } + if ((G.extra_bytes != 0 && + cover_add((cover_t *)G.cover, 0, G.extra_bytes) != 0) || + (G.ecrec.have_ecr64 && + cover_add((cover_t *)G.cover, G.ecrec.ec64_start, + G.ecrec.ec64_end) != 0) || + cover_add((cover_t *)G.cover, G.ecrec.ec_start, + G.ecrec.ec_end) != 0) { Info(slide, 0x401, ((char *)slide, - LoadFarString(OverlappedComponents))); + LoadFarString(OverlappedComponents))); return PK_BOMB; + } } /*--------------------------------------------------------------------------- @@ -1222,10 +1225,12 @@ static int extract_or_test_entrylist(__G__ numchunk, /* seek_zipf(__G__ pInfo->offset); */ request = G.pInfo->offset + G.extra_bytes; - if (cover_within((cover_t *)G.cover, request)) { + if (uO.zipbomb == TRUE) { + if (cover_within((cover_t *)G.cover, request)) { Info(slide, 0x401, ((char *)slide, - LoadFarString(OverlappedComponents))); + LoadFarString(OverlappedComponents))); return PK_BOMB; + } } inbuf_offset = request % INBUFSIZ; bufstart = request - inbuf_offset; @@ -1758,17 +1763,19 @@ reprompt: return IZ_CTRLC; /* cancel operation by user request */ } #endif - error = cover_add((cover_t *)G.cover, request, - G.cur_zipfile_bufstart + (G.inptr - G.inbuf)); - if (error < 0) { + if (uO.zipbomb == TRUE) { + error = cover_add((cover_t *)G.cover, request, + G.cur_zipfile_bufstart + (G.inptr - G.inbuf)); + if (error < 0) { Info(slide, 0x401, ((char *)slide, - LoadFarString(NotEnoughMemCover))); + LoadFarString(NotEnoughMemCover))); return PK_MEM; - } - if (error != 0) { + } + if (error != 0) { Info(slide, 0x401, ((char *)slide, - LoadFarString(OverlappedComponents))); + LoadFarString(OverlappedComponents))); return PK_BOMB; + } } #ifdef MACOS /* MacOS is no preemptive OS, thus call event-handling by hand */ UserStop(); @@ -2171,8 +2178,8 @@ static int extract_or_test_member(__G) /* return PK-type error code */ } undefer_input(__G); - - if ((G.lrec.general_purpose_bit_flag & 8) != 0) { + if (uO.zipbomb == TRUE) { + if ((G.lrec.general_purpose_bit_flag & 8) != 0) { /* skip over data descriptor (harder than it sounds, due to signature * ambiguity) */ @@ -2189,16 +2196,16 @@ static int extract_or_test_member(__G) /* return PK-type error code */ ((G.lrec.csize & LOW) != SIG || /* if not SIG, have signature */ (ulen == SIG && /* if not SIG, no signature */ (G.pInfo->zip64 ? G.lrec.csize >> 32 : G.lrec.ucsize) != SIG - /* if not SIG, have signature */ + /* if not SIG, have signature */ ))))) - /* skip four more bytes to account for signature */ - shy += 4 - readbuf((char *)buf, 4); + /* skip four more bytes to account for signature */ + shy += 4 - readbuf((char *)buf, 4); if (G.pInfo->zip64) - shy += 8 - readbuf((char *)buf, 8); /* skip eight more for ZIP64 */ + shy += 8 - readbuf((char *)buf, 8); /* skip eight more for ZIP64 */ if (shy) - error = PK_ERR; + error = PK_ERR; + } } - return error; } /* end function extract_or_test_member() */ diff --git a/unzip.c b/unzip.c index 8dbfc95..abb3644 100644 --- a/unzip.c +++ b/unzip.c @@ -1329,10 +1329,9 @@ int uz_opts(__G__ pargc, pargv) int *pargc; char ***pargv; { - char **argv, *s; + char **argv, *s, *zipbomb_envar; int argc, c, error=FALSE, negative=0, showhelp=0; - argc = *pargc; argv = *pargv; @@ -1923,6 +1922,18 @@ opts_done: /* yes, very ugly...but only used by UnZipSFX with -x xlist */ else G.extract_flag = TRUE; + /* 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. */ + zipbomb_envar = getenv("UNZIP_DISABLE_ZIPBOMB_DETECTION"); + uO.zipbomb = TRUE; + if (zipbomb_envar != NULL) { + /* strcasecmp might be a better approach here but it is POSIX-only */ + if ((strcmp ("TRUE", zipbomb_envar) == 0) + || (strcmp ("True", zipbomb_envar) == 0) + || (strcmp ("true",zipbomb_envar) == 0)) { + uO.zipbomb = FALSE; + } + } + *pargc = argc; *pargv = argv; return PK_OK; diff --git a/unzip.h b/unzip.h index ed24a5b..e7665e8 100644 --- a/unzip.h +++ b/unzip.h @@ -559,6 +559,7 @@ typedef struct _UzpOpts { #ifdef UNIX int cflxflag; /* -^: allow control chars in extracted filenames */ #endif + int zipbomb; #endif /* !FUNZIP */ } UzpOpts; -- 2.33.0 diff --git a/process.c b/process.c index d2a846e..cba2463 100644 --- a/process.c +++ b/process.c @@ -2064,10 +2064,14 @@ int getUnicodeData(__G__ ef_buf, ef_len) G.unipath_checksum = makelong(offset + ef_buf); offset += 4; + if (!G.filename_full) { + /* Check if we have a unicode extra section but no filename set */ + return PK_ERR; + } + /* * Compute 32-bit crc */ - chksum = crc32(chksum, (uch *)(G.filename_full), strlen(G.filename_full));