50593e02 |
/* |
c442ca9c |
* Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
9eff9418 |
* Copyright (C) 2007-2013 Sourcefire, Inc. |
2023340a |
*
* Authors: Alberto Wu |
50593e02 |
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/* FIXME: get a clue about masked stuff */
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <zlib.h>
#include "inflate64.h"
#if HAVE_BZLIB_H
#include <bzlib.h>
#endif
|
f8be651b |
#include "explode.h" |
50593e02 |
#include "others.h"
#include "clamav.h"
#include "scanners.h"
#include "matcher.h" |
774898a7 |
#include "fmap.h" |
20b45621 |
#include "json_api.h" |
50593e02 |
#define UNZIP_PRIVATE
#include "unzip.h"
|
dcd26ea5 |
#define ZIP_MAX_NUM_OVERLAPPING_FILES 5
|
a60ec799 |
#define ZIP_CRC32(r,c,b,l) \
do { \
r = crc32(~c,b,l); \
r = ~r; \
} while(0)
|
50593e02 |
static int wrap_inflateinit2(void *a, int b) {
return inflateInit2(a, b);
}
|
c8c878f9 |
static int unz(const uint8_t *src, uint32_t csize, uint32_t usize, uint16_t method, uint16_t flags, unsigned int *fu, cli_ctx *ctx, char *tmpd, zip_cb zcb) { |
50593e02 |
char name[1024], obuf[BUFSIZ];
char *tempfile = name;
int of, ret=CL_CLEAN;
unsigned int res=1, written=0;
if(tmpd) { |
58481352 |
snprintf(name, sizeof(name), "%s"PATHSEP"zip.%03u", tmpd, *fu); |
50593e02 |
name[sizeof(name)-1]='\0';
} else { |
33068e09 |
if(!(tempfile = cli_gentemp(ctx->engine->tmpdir))) return CL_EMEM; |
50593e02 |
}
if((of = open(tempfile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRUSR|S_IWUSR))==-1) {
cli_warnmsg("cli_unzip: failed to create temporary file %s\n", tempfile);
if(!tmpd) free(tempfile); |
1b9b5f6d |
return CL_ETMPFILE; |
50593e02 |
}
switch (method) {
case ALG_STORED:
if(csize<usize) {
unsigned int fake = *fu + 1;
cli_dbgmsg("cli_unzip: attempting to inflate stored file with inconsistent size\n"); |
c8c878f9 |
if ((ret=unz(src, csize, usize, ALG_DEFLATE, 0, &fake, ctx, tmpd, zcb))==CL_CLEAN) { |
50593e02 |
(*fu)++;
res=fake-(*fu);
}
else break;
}
if(res==1) { |
724b2bf7 |
if(ctx->engine->maxfilesize && csize > ctx->engine->maxfilesize) { |
e78b5186 |
cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (long unsigned int) ctx->engine->maxfilesize); |
724b2bf7 |
csize = ctx->engine->maxfilesize; |
50593e02 |
} |
871177cd |
if(cli_writen(of, src, csize)!=(int)csize) ret = CL_EWRITE; |
50593e02 |
else res=0;
}
break;
case ALG_DEFLATE:
case ALG_DEFLATE64: {
union {
z_stream64 strm64;
z_stream strm;
} strm;
typedef int (*unz_init_) (void *, int);
typedef int (*unz_unz_) (void *, int);
typedef int (*unz_end_) (void *);
unz_init_ unz_init;
unz_unz_ unz_unz;
unz_end_ unz_end;
int wbits;
void **next_in;
void **next_out;
unsigned int *avail_in;
unsigned int *avail_out;
if(method == ALG_DEFLATE64) {
unz_init = (unz_init_)inflate64Init2;
unz_unz = (unz_unz_)inflate64;
unz_end = (unz_end_)inflate64End;
next_in = (void *)&strm.strm64.next_in;
next_out = (void *)&strm.strm64.next_out;
avail_in = &strm.strm64.avail_in;
avail_out = &strm.strm64.avail_out;
wbits=MAX_WBITS64;
} else {
unz_init = (unz_init_)wrap_inflateinit2;
unz_unz = (unz_unz_)inflate;
unz_end = (unz_end_)inflateEnd;
next_in = (void *)&strm.strm.next_in;
next_out = (void *)&strm.strm.next_out;
avail_in = &strm.strm.avail_in;
avail_out = &strm.strm.avail_out;
wbits=MAX_WBITS;
}
memset(&strm, 0, sizeof(strm));
|
f304dc68 |
*next_in = (void*) src; |
50593e02 |
*next_out = obuf;
*avail_in = csize;
*avail_out = sizeof(obuf);
if (unz_init(&strm, -wbits)!=Z_OK) {
cli_dbgmsg("cli_unzip: zinit failed\n");
break;
}
while(1) {
while((res = unz_unz(&strm, Z_NO_FLUSH))==Z_OK) {};
if(*avail_out!=sizeof(obuf)) {
written+=sizeof(obuf)-(*avail_out); |
724b2bf7 |
if(ctx->engine->maxfilesize && written > ctx->engine->maxfilesize) { |
e78b5186 |
cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (long unsigned int) ctx->engine->maxfilesize); |
50593e02 |
res = Z_STREAM_END;
break;
}
if(cli_writen(of, obuf, sizeof(obuf)-(*avail_out)) != (int)(sizeof(obuf)-(*avail_out))) { |
c8c878f9 |
cli_warnmsg("cli_unzip: falied to write %lu inflated bytes\n", (unsigned long int)sizeof(obuf)-(*avail_out)); |
871177cd |
ret = CL_EWRITE; |
b346e362 |
res = 100;
break; |
50593e02 |
}
*next_out = obuf;
*avail_out = sizeof(obuf);
continue;
}
break;
}
unz_end(&strm); |
f3fd2ac2 |
if ((res == Z_STREAM_END) | (res == Z_BUF_ERROR)) res=0; |
50593e02 |
break;
}
#if HAVE_BZLIB_H |
c09deba1 |
#ifdef NOBZ2PREFIX |
a622f5f3 |
#define BZ2_bzDecompress bzDecompress
#define BZ2_bzDecompressEnd bzDecompressEnd
#define BZ2_bzDecompressInit bzDecompressInit |
c09deba1 |
#endif
|
50593e02 |
case ALG_BZIP2: {
bz_stream strm;
memset(&strm, 0, sizeof(strm));
strm.next_in = (char *)src;
strm.next_out = obuf;
strm.avail_in = csize;
strm.avail_out = sizeof(obuf);
if (BZ2_bzDecompressInit(&strm, 0, 0)!=BZ_OK) {
cli_dbgmsg("cli_unzip: bzinit failed\n");
break;
}
while((res = BZ2_bzDecompress(&strm))==BZ_OK || res==BZ_STREAM_END) {
if(strm.avail_out!=sizeof(obuf)) {
written+=sizeof(obuf)-strm.avail_out; |
724b2bf7 |
if(ctx->engine->maxfilesize && written > ctx->engine->maxfilesize) { |
e78b5186 |
cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (unsigned long int) ctx->engine->maxfilesize); |
f8be651b |
res = BZ_STREAM_END; |
50593e02 |
break;
}
if(cli_writen(of, obuf, sizeof(obuf)-strm.avail_out) != (int)(sizeof(obuf)-strm.avail_out)) { |
c8c878f9 |
cli_warnmsg("cli_unzip: falied to write %lu bunzipped bytes\n", (long unsigned int)sizeof(obuf)-strm.avail_out); |
871177cd |
ret = CL_EWRITE; |
b346e362 |
res = 100;
break; |
50593e02 |
}
strm.next_out = obuf;
strm.avail_out = sizeof(obuf); |
b346e362 |
if (res == BZ_OK) continue; /* after returning BZ_STREAM_END once, decompress returns an error */ |
50593e02 |
}
break;
}
BZ2_bzDecompressEnd(&strm); |
b346e362 |
if (res == BZ_STREAM_END) res=0; |
50593e02 |
break;
}
#endif /* HAVE_BZLIB_H */
|
f8be651b |
case ALG_IMPLODE: {
struct xplstate strm; |
f304dc68 |
strm.next_in = (void*)src; |
7e05c025 |
strm.next_out = (uint8_t *)obuf; |
f8be651b |
strm.avail_in = csize;
strm.avail_out = sizeof(obuf);
if (explode_init(&strm, flags)!=EXPLODE_OK) {
cli_dbgmsg("cli_unzip: explode_init() failed\n");
break;
}
while((res = explode(&strm))==EXPLODE_OK) {
if(strm.avail_out!=sizeof(obuf)) {
written+=sizeof(obuf)-strm.avail_out; |
724b2bf7 |
if(ctx->engine->maxfilesize && written > ctx->engine->maxfilesize) { |
e78b5186 |
cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (unsigned long int) ctx->engine->maxfilesize); |
f8be651b |
res = 0;
break;
}
if(cli_writen(of, obuf, sizeof(obuf)-strm.avail_out) != (int)(sizeof(obuf)-strm.avail_out)) { |
c8c878f9 |
cli_warnmsg("cli_unzip: falied to write %lu exploded bytes\n", (unsigned long int) sizeof(obuf)-strm.avail_out); |
871177cd |
ret = CL_EWRITE; |
b346e362 |
res = 100;
break; |
f8be651b |
} |
7e05c025 |
strm.next_out = (uint8_t *)obuf; |
f8be651b |
strm.avail_out = sizeof(obuf);
continue;
}
break;
}
break;
}
|
50593e02 |
case ALG_LZMA:
/* easy but there's not a single sample in the zoo */
#if !HAVE_BZLIB_H
case ALG_BZIP2:
#endif
case ALG_SHRUNK:
case ALG_REDUCE1:
case ALG_REDUCE2:
case ALG_REDUCE3:
case ALG_REDUCE4:
case ALG_TOKENZD:
case ALG_OLDTERSE:
case ALG_RSVD1:
case ALG_RSVD2:
case ALG_RSVD3:
case ALG_RSVD4:
case ALG_RSVD5:
case ALG_NEWTERSE:
case ALG_LZ77:
case ALG_WAVPACK:
case ALG_PPMD:
cli_dbgmsg("cli_unzip: unsupported method (%d)\n", method);
break;
default:
cli_dbgmsg("cli_unzip: unknown method (%d)\n", method);
break;
}
if(!res) {
(*fu)++;
cli_dbgmsg("cli_unzip: extracted to %s\n", tempfile); |
7e40bab9 |
if (lseek(of, 0, SEEK_SET) == -1) {
cli_dbgmsg("cli_unzip: call to lseek() failed\n");
if (!(tmpd))
free(tempfile); |
9eff9418 |
close(of); |
7e40bab9 |
return CL_ESEEK;
} |
d39cb658 |
ret = zcb(of, tempfile, ctx); |
50593e02 |
close(of); |
33068e09 |
if(!ctx->engine->keeptmp) |
871177cd |
if(cli_unlink(tempfile)) ret = CL_EUNLINK; |
50593e02 |
if(!tmpd) free(tempfile);
return ret;
}
close(of); |
33068e09 |
if(!ctx->engine->keeptmp) |
871177cd |
if(cli_unlink(tempfile)) ret = CL_EUNLINK; |
50593e02 |
if(!tmpd) free(tempfile);
cli_dbgmsg("cli_unzip: extraction failed\n");
return ret;
}
|
1ac97cf0 |
/* zip update keys, taken from zip specification */
static inline void zupdatekey(uint32_t key[3], unsigned char input)
{
unsigned char tmp[1];
unsigned long crctmp;
tmp[0] = input;
ZIP_CRC32(key[0], key[0], tmp, 1);
key[1] = key[1] + (key[0] & 0xff);
key[1] = key[1] * 134775813 + 1;
tmp[0] = key[1] >> 24;
ZIP_CRC32(key[2], key[2], tmp, 1);
}
/* zip init keys */ |
038cb67a |
static inline void zinitkey(uint32_t key[3], struct cli_pwdb *password) |
1ac97cf0 |
{
int i;
|
7cd9337a |
/* initialize keys, these are specified but the zip specification */ |
1ac97cf0 |
key[0] = 305419896L;
key[1] = 591751049L;
key[2] = 878082192L;
/* update keys with password */
for (i = 0; i < password->length; i++)
zupdatekey(key, password->passwd[i]);
}
/* zip decrypt byte */
static inline unsigned char zdecryptbyte(uint32_t key[3])
{
unsigned short temp;
temp = key[2] | 2;
return ((temp * (temp ^ 1)) >> 8);
}
/* zip decrypt, CL_EPARSE = could not apply a password, csize includes the decryption header */
/* TODO - search for strong encryption header (0x0017) and handle them */
static inline int zdecrypt(const uint8_t *src, uint32_t csize, uint32_t usize, const uint8_t *lh, unsigned int *fu, cli_ctx *ctx, char *tmpd, zip_cb zcb)
{
int i, ret, v = 0;
uint32_t key[3];
uint8_t eh[12]; /* encryption header buffer */ |
038cb67a |
struct cli_pwdb *password, *pass_any, *pass_zip; |
1ac97cf0 |
if (!ctx || !ctx->engine)
return CL_ENULLARG;
|
f5f7b7a1 |
/* dconf */
if (ctx->dconf && !(ctx->dconf->archive & ARCH_CONF_PASSWD)) {
cli_dbgmsg("cli_unzip: decrypt - skipping encrypted file\n");
return CL_SUCCESS;
} |
1ac97cf0 |
|
038cb67a |
pass_any = ctx->engine->pwdbs[CLI_PWDB_ANY];
pass_zip = ctx->engine->pwdbs[CLI_PWDB_ZIP]; |
1ac97cf0 |
|
038cb67a |
while (pass_any || pass_zip) {
password = pass_zip ? pass_zip : pass_any; |
1ac97cf0 |
zinitkey(key, password);
/* decrypting the encryption header */
memcpy(eh, src, SIZEOF_EH);
for (i = 0; i < SIZEOF_EH; i++) {
eh[i] ^= zdecryptbyte(key);
zupdatekey(key, eh[i]);
}
/* verify that the password is correct */
if (LH_version > 20) { /* higher than 2.0 */
uint16_t a = eh[SIZEOF_EH-1];
if (LH_flags & F_USEDD) { |
0b119e6f |
cli_dbgmsg("cli_unzip: decrypt - (v%u) >> 0x%02x 0x%x (moddate)\n", LH_version, a, LH_mtime); |
1ac97cf0 |
if (a == ((LH_mtime >> 8) & 0xff))
v = 1;
} else { |
0b119e6f |
cli_dbgmsg("cli_unzip: decrypt - (v%u) >> 0x%02x 0x%x (crc32)\n", LH_version, a, LH_crc32); |
1ac97cf0 |
if (a == ((LH_crc32 >> 24) & 0xff))
v = 1;
}
} else {
uint16_t a = eh[SIZEOF_EH-1], b = eh[SIZEOF_EH-2];
if (LH_flags & F_USEDD) { |
0b119e6f |
cli_dbgmsg("cli_unzip: decrypt - (v%u) >> 0x0000%02x%02x 0x%x (moddate)\n", LH_version, a, b, LH_mtime); |
1ac97cf0 |
if ((b | (a << 8)) == (LH_mtime & 0xffff))
v = 1;
} else { |
0b119e6f |
cli_dbgmsg("cli_unzip: decrypt - (v%u) >> 0x0000%02x%02x 0x%x (crc32)\n", LH_version, eh[SIZEOF_EH-1], eh[SIZEOF_EH-2], LH_crc32); |
1ac97cf0 |
if ((b | (a << 8)) == ((LH_crc32 >> 16) & 0xffff))
v = 1;
}
}
if (v) {
char name[1024], obuf[BUFSIZ];
char *tempfile = name;
unsigned int written = 0, total = 0;
fmap_t *dcypt_map;
const uint8_t *dcypt_zip;
int of;
|
0b119e6f |
cli_dbgmsg("cli_unzip: decrypt - password [%s] matches\n", password->name); |
1ac97cf0 |
/* output decrypted data to tempfile */
if(tmpd) {
snprintf(name, sizeof(name), "%s"PATHSEP"zip.decrypt.%03u", tmpd, *fu);
name[sizeof(name)-1]='\0';
} else {
if(!(tempfile = cli_gentemp(ctx->engine->tmpdir))) return CL_EMEM;
}
if((of = open(tempfile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRUSR|S_IWUSR))==-1) { |
0b119e6f |
cli_warnmsg("cli_unzip: decrypt - failed to create temporary file %s\n", tempfile); |
1ac97cf0 |
if(!tmpd) free(tempfile); |
1b9b5f6d |
return CL_ETMPFILE; |
1ac97cf0 |
}
for (i = 12; i < csize; i++) {
obuf[written] = src[i] ^ zdecryptbyte(key);
zupdatekey(key, obuf[written]);
written++;
if (written >= BUFSIZ) {
if (cli_writen(of, obuf, written)!=(int)written) {
ret = CL_EWRITE;
goto zd_clean;
}
total += written;
written = 0;
}
}
if (written) {
if (cli_writen(of, obuf, written)!=(int)written) {
ret = CL_EWRITE;
goto zd_clean;
}
total += written;
written = 0;
}
|
0b119e6f |
cli_dbgmsg("cli_unzip: decrypt - decrypted %u bytes to %s\n", total, tempfile); |
1ac97cf0 |
/* decrypt data to new fmap -> buffer */
if (!(dcypt_map = fmap(of, 0, total))) { |
0b119e6f |
cli_warnmsg("cli_unzip: decrypt - failed to create fmap on decrypted file %s\n", tempfile); |
1ac97cf0 |
ret = CL_EMAP;
goto zd_clean;
}
if (!(dcypt_zip = fmap_need_off_once(dcypt_map, 0, total))) { |
0b119e6f |
cli_warnmsg("cli_unzip: decrypt - failed to acquire buffer on decrypted file %s\n", tempfile); |
1ac97cf0 |
funmap(dcypt_map);
ret = CL_EREAD;
goto zd_clean;
}
/* call unz on decrypted output */
ret = unz(dcypt_zip, csize - SIZEOF_EH, usize, LH_method, LH_flags, fu, ctx, tmpd, zcb);
/* clean-up and return */
funmap(dcypt_map);
zd_clean:
close(of);
if (!ctx->engine->keeptmp) |
202a5dae |
if (cli_unlink(tempfile)) {
if (!tmpd) free(tempfile);
return CL_EUNLINK;
} |
1ac97cf0 |
if (!tmpd) free(tempfile);
return ret;
}
|
038cb67a |
if (pass_zip)
pass_zip = pass_zip->next;
else |
dcd26ea5 |
pass_any = pass_any->next; |
1ac97cf0 |
}
|
0b119e6f |
cli_dbgmsg("cli_unzip: decrypt - skipping encrypted file, no valid passwords\n"); |
1ac97cf0 |
return CL_SUCCESS;
}
|
dcd26ea5 |
static unsigned int lhdr(fmap_t *map, uint32_t loff,uint32_t zsize, unsigned int *fu, unsigned int fc, const uint8_t *ch, int *ret, cli_ctx *ctx, char *tmpd, int detect_encrypted, zip_cb zcb, uint32_t *file_local_header_size, uint32_t* file_local_data_size) { |
f304dc68 |
const uint8_t *lh, *zip; |
50593e02 |
char name[256]; |
e5083cb5 |
uint32_t csize, usize; |
7a307529 |
int virus_found = 0; |
50593e02 |
|
774898a7 |
if(!(lh = fmap_need_off(map, loff, SIZEOF_LH))) {
cli_dbgmsg("cli_unzip: lh - out of file\n");
return 0; |
50593e02 |
}
if(LH_magic != 0x04034b50) {
if (!ch) cli_dbgmsg("cli_unzip: lh - wrkcomplete\n");
else cli_dbgmsg("cli_unzip: lh - bad magic\n"); |
774898a7 |
fmap_unneed_off(map, loff, SIZEOF_LH); |
50593e02 |
return 0;
}
|
774898a7 |
zip = lh + SIZEOF_LH; |
50593e02 |
zsize-=SIZEOF_LH;
if(zsize<=LH_flen) {
cli_dbgmsg("cli_unzip: lh - fname out of file\n"); |
b183aa78 |
fmap_unneed_off(map, loff, SIZEOF_LH); |
50593e02 |
return 0;
} |
15f413d1 |
if(ctx->engine->cdb || cli_debug_flag) { |
774898a7 |
uint32_t nsize = (LH_flen>=sizeof(name))?sizeof(name)-1:LH_flen; |
f304dc68 |
const char *src; |
774898a7 |
if(nsize && (src = fmap_need_ptr_once(map, zip, nsize))) {
memcpy(name, zip, nsize);
name[nsize]='\0';
} else
name[0] = '\0'; |
50593e02 |
}
zip+=LH_flen;
zsize-=LH_flen;
|
3145cde0 |
cli_dbgmsg("cli_unzip: lh - ZMDNAME:%d:%s:%u:%u:%x:%u:%u:%u\n", ((LH_flags & F_ENCR)!=0), name, LH_usize, LH_csize, LH_crc32, LH_method, fc, ctx->recursion); |
50593e02 |
/* ZMDfmt virname:encrypted(0-1):filename(exact|*):usize(exact|*):csize(exact|*):crc32(exact|*):method(exact|*):fileno(exact|*):maxdepth(exact|*) */
|
570b1d00 |
if(cli_matchmeta(ctx, name, LH_csize, LH_usize, (LH_flags & F_ENCR)!=0, fc, LH_crc32, NULL) == CL_VIRUS) { |
7a307529 |
*ret = CL_VIRUS; |
d7979d4f |
if (!SCAN_ALLMATCHES) |
7a307529 |
return 0;
virus_found = 1; |
50593e02 |
}
if(LH_flags & F_MSKED) {
cli_dbgmsg("cli_unzip: lh - header has got unusable masked data\n");
/* FIXME: need to find/craft a sample */ |
b183aa78 |
fmap_unneed_off(map, loff, SIZEOF_LH); |
50593e02 |
return 0;
}
|
f61e92da |
if(detect_encrypted && (LH_flags & F_ENCR) && SCAN_HEURISTIC_ENCRYPTED_ARCHIVE) { |
8201d79d |
cli_dbgmsg("cli_unzip: Encrypted files found in archive.\n"); |
cbf5017a |
*ret = cli_append_virus(ctx, "Heuristics.Encrypted.Zip"); |
d7979d4f |
if ((*ret == CL_VIRUS && !SCAN_ALLMATCHES) || *ret != CL_CLEAN) { |
7a307529 |
fmap_unneed_off(map, loff, SIZEOF_LH);
return 0;
}
virus_found = 1; |
8201d79d |
} |
dcd26ea5 |
|
50593e02 |
if(LH_flags & F_USEDD) {
cli_dbgmsg("cli_unzip: lh - has data desc\n"); |
774898a7 |
if(!ch) { |
b183aa78 |
fmap_unneed_off(map, loff, SIZEOF_LH); |
774898a7 |
return 0;
} |
e5083cb5 |
else { usize = CH_usize; csize = CH_csize; }
} else { usize = LH_usize; csize = LH_csize; } |
50593e02 |
if(zsize<=LH_elen) {
cli_dbgmsg("cli_unzip: lh - extra out of file\n"); |
b183aa78 |
fmap_unneed_off(map, loff, SIZEOF_LH); |
50593e02 |
return 0;
}
zip+=LH_elen;
zsize-=LH_elen;
|
dcd26ea5 |
if (NULL != file_local_header_size)
*file_local_header_size = zip - lh;
if (NULL != file_local_data_size)
*file_local_data_size = csize;
|
50593e02 |
if (!csize) { /* FIXME: what's used for method0 files? csize or usize? Nothing in the specs, needs testing */ |
774898a7 |
cli_dbgmsg("cli_unzip: lh - skipping empty file\n"); |
50593e02 |
} else { |
774898a7 |
if(zsize<csize) {
cli_dbgmsg("cli_unzip: lh - stream out of file\n"); |
b183aa78 |
fmap_unneed_off(map, loff, SIZEOF_LH); |
774898a7 |
return 0;
} |
dcd26ea5 |
|
774898a7 |
if(LH_flags & F_ENCR) { |
a60ec799 |
if(fmap_need_ptr_once(map, zip, csize))
*ret = zdecrypt(zip, csize, usize, lh, fu, ctx, tmpd, zcb); |
774898a7 |
} else {
if(fmap_need_ptr_once(map, zip, csize)) |
c8c878f9 |
*ret = unz(zip, csize, usize, LH_method, LH_flags, fu, ctx, tmpd, zcb); |
774898a7 |
}
zip+=csize;
zsize-=csize; |
50593e02 |
}
|
7a307529 |
if (virus_found != 0)
*ret = CL_VIRUS;
|
b183aa78 |
fmap_unneed_off(map, loff, SIZEOF_LH); /* unneed now. block is guaranteed to exists till the next need */ |
50593e02 |
if(LH_flags & F_USEDD) { |
774898a7 |
if(zsize<12) {
cli_dbgmsg("cli_unzip: lh - data desc out of file\n");
return 0; |
50593e02 |
} |
774898a7 |
zsize-=12;
if(fmap_need_ptr_once(map, zip, 4)) {
if(cli_readint32(zip)==0x08074b50) {
if(zsize<4) {
cli_dbgmsg("cli_unzip: lh - data desc out of file\n");
return 0;
}
zip+=4;
}
}
zip+=12; |
50593e02 |
}
return zip-lh;
}
|
dcd26ea5 |
static unsigned int chdr(fmap_t *map, uint32_t coff, uint32_t zsize, unsigned int *fu, unsigned int fc, int *ret, cli_ctx *ctx, char *tmpd, struct zip_requests *requests, uint32_t *file_local_offset, uint32_t *file_local_header_size, uint32_t *file_local_data_size) { |
50593e02 |
char name[256];
int last = 0; |
f304dc68 |
const uint8_t *ch; |
6146fae1 |
int virus_found = 0; |
50593e02 |
|
dcd26ea5 |
if (NULL != file_local_offset)
*file_local_offset = 0;
if (NULL != file_local_header_size)
*file_local_header_size = 0;
if (NULL != file_local_data_size)
*file_local_data_size = 0;
|
774898a7 |
if(!(ch = fmap_need_off(map, coff, SIZEOF_CH)) || CH_magic != 0x02014b50) {
if(ch) fmap_unneed_ptr(map, ch, SIZEOF_CH);
cli_dbgmsg("cli_unzip: ch - wrkcomplete\n");
return 0; |
50593e02 |
}
coff+=SIZEOF_CH;
cli_dbgmsg("cli_unzip: ch - flags %x - method %x - csize %x - usize %x - flen %x - elen %x - clen %x - disk %x - off %x\n", CH_flags, CH_method, CH_csize, CH_usize, CH_flen, CH_elen, CH_clen, CH_dsk, CH_off);
if(zsize-coff<=CH_flen) {
cli_dbgmsg("cli_unzip: ch - fname out of file\n");
last=1;
} |
c8c878f9 |
name[0]='\0'; |
6146fae1 |
if(!last) { |
774898a7 |
unsigned int size = (CH_flen>=sizeof(name))?sizeof(name)-1:CH_flen; |
f304dc68 |
const char *src = fmap_need_off_once(map, coff, size); |
774898a7 |
if(src) {
memcpy(name, src, size);
name[size]='\0';
cli_dbgmsg("cli_unzip: ch - fname: %s\n", name);
} |
50593e02 |
}
coff+=CH_flen;
|
51b8cc32 |
/* requests do not supply a ctx; also prevent multiple scans */
if(ctx && cli_matchmeta(ctx, name, CH_csize, CH_usize, (CH_flags & F_ENCR)!=0, fc, CH_crc32, NULL) == CL_VIRUS) |
6146fae1 |
virus_found = 1;
|
50593e02 |
if(zsize-coff<=CH_elen && !last) {
cli_dbgmsg("cli_unzip: ch - extra out of file\n");
last=1;
}
coff+=CH_elen;
if(zsize-coff<CH_clen && !last) {
cli_dbgmsg("cli_unzip: ch - comment out of file\n");
last = 1;
}
coff+=CH_clen;
|
c8c80ddf |
if (!requests) { |
c8c878f9 |
if(CH_off<zsize-SIZEOF_LH) { |
dcd26ea5 |
if (NULL != file_local_offset)
*file_local_offset = CH_off;
lhdr(map, CH_off, zsize-CH_off, fu, fc, ch, ret, ctx, tmpd, 1, zip_scan_cb, file_local_header_size, file_local_data_size); |
c8c878f9 |
} else cli_dbgmsg("cli_unzip: ch - local hdr out of file\n");
}
else { |
c8c80ddf |
int i;
size_t len;
if (!last) {
for (i = 0; i < requests->namecnt; ++i) {
cli_dbgmsg("checking for %i: %s\n", i, requests->names[i]);
|
dcd26ea5 |
len = MIN(sizeof(name)-1, requests->namelens[i]); |
c8c80ddf |
if (!strncmp(requests->names[i], name, len)) {
requests->match = 1;
requests->found = i;
requests->loff = CH_off;
}
} |
c8c878f9 |
}
}
|
6146fae1 |
if (virus_found == 1)
*ret = CL_VIRUS; |
774898a7 |
fmap_unneed_ptr(map, ch, SIZEOF_CH); |
871b862e |
return (last?0:coff); |
50593e02 |
}
|
bd7f7684 |
int cli_unzip(cli_ctx *ctx) { |
50593e02 |
unsigned int fc=0, fu=0;
int ret=CL_CLEAN;
uint32_t fsize, lhoff = 0, coff = 0; |
49cc1e3c |
fmap_t *map = *ctx->fmap; |
f304dc68 |
char *tmpd;
const char *ptr; |
92e0ae15 |
int virus_found = 0; |
20b45621 |
#if HAVE_JSON
int toval = 0;
#endif |
dcd26ea5 |
int bZipBombDetected = 0;
uint32_t cur_file_local_offset = 0;
uint32_t cur_file_local_header_size = 0;
uint32_t cur_file_local_data_size = 0;
uint32_t prev_file_local_offset = 0;
uint32_t prev_file_local_header_size = 0;
uint32_t prev_file_local_data_size = 0; |
50593e02 |
cli_dbgmsg("in cli_unzip\n"); |
bd7f7684 |
fsize = (uint32_t)map->len; |
cd94be7a |
if(sizeof(off_t)!=sizeof(uint32_t) && (size_t)fsize!=map->len) { |
50593e02 |
cli_dbgmsg("cli_unzip: file too big\n");
return CL_CLEAN;
}
if (fsize < SIZEOF_CH) {
cli_dbgmsg("cli_unzip: file too short\n");
return CL_CLEAN;
} |
33068e09 |
if (!(tmpd = cli_gentemp(ctx->engine->tmpdir))) { |
50593e02 |
return CL_ETMPDIR; |
0b8d9622 |
} |
50593e02 |
if (mkdir(tmpd, 0700)) {
cli_dbgmsg("cli_unzip: Can't create temporary directory %s\n", tmpd);
free(tmpd);
return CL_ETMPDIR;
}
for(coff=fsize-22 ; coff>0 ; coff--) { /* sizeof(EOC)==22 */ |
774898a7 |
if(!(ptr = fmap_need_off_once(map, coff, 20)))
continue;
if(cli_readint32(ptr)==0x06054b50) {
uint32_t chptr = cli_readint32(&ptr[16]);
if(!CLI_ISCONTAINED(0, fsize, chptr, SIZEOF_CH)) continue;
coff=chptr;
break;
} |
50593e02 |
}
if(coff) { |
dcd26ea5 |
uint32_t nOverlappingFiles = 0;
|
774898a7 |
cli_dbgmsg("cli_unzip: central @%x\n", coff); |
dcd26ea5 |
while((coff=chdr(map, coff, fsize, &fu, fc+1, &ret, ctx, tmpd, NULL, &cur_file_local_offset, &cur_file_local_header_size, &cur_file_local_data_size))) { |
774898a7 |
fc++;
if (ctx->engine->maxfiles && fu>=ctx->engine->maxfiles) {
cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles);
ret=CL_EMAXFILES;
} |
dcd26ea5 |
/*
* Detect overlapping files and zip bombs.
*/
if ((((cur_file_local_offset > prev_file_local_offset) && (cur_file_local_offset < prev_file_local_offset + prev_file_local_header_size + prev_file_local_data_size)) ||
((prev_file_local_offset > cur_file_local_offset) && (prev_file_local_offset < cur_file_local_offset + cur_file_local_header_size + cur_file_local_data_size))) &&
(cur_file_local_header_size + cur_file_local_data_size > 0)) {
/* Overlapping file detected */
nOverlappingFiles++;
cli_dbgmsg("cli_unzip: Overlapping files detected.\n");
cli_dbgmsg(" previous file end: %u\n", prev_file_local_offset + prev_file_local_header_size + prev_file_local_data_size);
cli_dbgmsg(" current file start: %u\n", cur_file_local_offset);
if (ZIP_MAX_NUM_OVERLAPPING_FILES < nOverlappingFiles) {
if (SCAN_HEURISTICS) {
ret = cli_append_virus(ctx, "Heuristics.Zip.OverlappingFiles");
virus_found = 1;
} else {
ret = CL_EFORMAT;
}
bZipBombDetected = 1;
}
}
prev_file_local_offset = cur_file_local_offset;
prev_file_local_header_size = cur_file_local_header_size;
prev_file_local_data_size = cur_file_local_data_size;
|
20b45621 |
#if HAVE_JSON
if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) { |
93a9a942 |
ret=CL_ETIMEOUT; |
20b45621 |
}
#endif |
9276cd1f |
if (ret != CL_CLEAN) { |
dcd26ea5 |
if (ret == CL_VIRUS && SCAN_ALLMATCHES && !bZipBombDetected) { |
9276cd1f |
ret = CL_CLEAN;
virus_found = 1;
} else
break;
} |
50593e02 |
}
} else cli_dbgmsg("cli_unzip: central not found, using localhdrs\n"); |
9276cd1f |
if (virus_found == 1)
ret = CL_VIRUS; |
50593e02 |
if(fu<=(fc/4)) { /* FIXME: make up a sane ratio or remove the whole logic */
fc = 0; |
dcd26ea5 |
while (ret==CL_CLEAN && lhoff<fsize && (coff=lhdr(map, lhoff, fsize-lhoff, &fu, fc+1, NULL, &ret, ctx, tmpd, 1, zip_scan_cb, NULL, NULL))) { |
50593e02 |
fc++;
lhoff+=coff; |
d7979d4f |
if (SCAN_ALLMATCHES && ret == CL_VIRUS) { |
92e0ae15 |
ret = CL_CLEAN;
virus_found = 1;
} |
724b2bf7 |
if (ctx->engine->maxfiles && fu>=ctx->engine->maxfiles) {
cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles); |
50593e02 |
ret=CL_EMAXFILES;
} |
20b45621 |
#if HAVE_JSON
if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) { |
93a9a942 |
ret=CL_ETIMEOUT; |
20b45621 |
}
#endif
|
50593e02 |
}
}
|
33068e09 |
if (!ctx->engine->keeptmp) cli_rmdirs(tmpd); |
50593e02 |
free(tmpd);
|
92e0ae15 |
if (ret == CL_CLEAN && virus_found)
ret = CL_VIRUS;
|
50593e02 |
return ret;
}
|
c8c878f9 |
int unzip_single_internal(cli_ctx *ctx, off_t lhoffl, zip_cb zcb)
{ |
50593e02 |
int ret=CL_CLEAN;
unsigned int fu=0;
uint32_t fsize; |
49cc1e3c |
fmap_t *map = *ctx->fmap; |
50593e02 |
cli_dbgmsg("in cli_unzip_single\n"); |
2d5dbc37 |
fsize = (uint32_t)(map->len - lhoffl); |
cd94be7a |
if (lhoffl<0 || (size_t)lhoffl>map->len || (sizeof(off_t)!=sizeof(uint32_t) && (size_t)fsize!=map->len - lhoffl)) { |
50593e02 |
cli_dbgmsg("cli_unzip: bad offset\n");
return CL_CLEAN;
}
if (fsize < SIZEOF_LH) {
cli_dbgmsg("cli_unzip: file too short\n");
return CL_CLEAN;
}
|
dcd26ea5 |
lhdr(map, lhoffl, fsize, &fu, 0, NULL, &ret, ctx, NULL, 0, zcb, NULL, NULL); |
50593e02 |
return ret;
} |
c8c878f9 |
int cli_unzip_single(cli_ctx *ctx, off_t lhoffl) {
return unzip_single_internal(ctx, lhoffl, zip_scan_cb);
}
|
c8c80ddf |
int unzip_search_add(struct zip_requests *requests, const char *name, size_t nlen)
{
cli_dbgmsg("in unzip_search_add\n");
if (requests->namecnt >= MAX_ZIP_REQUESTS) {
cli_dbgmsg("DEBUGGING MESSAGE GOES HERE!\n");
return CL_BREAK;
}
cli_dbgmsg("unzip_search_add: adding %s (len %llu)\n", name, (long long unsigned)nlen);
requests->names[requests->namecnt] = name;
requests->namelens[requests->namecnt] = nlen;
requests->namecnt++;
return CL_SUCCESS;
}
int unzip_search(cli_ctx *ctx, fmap_t *map, struct zip_requests *requests) |
c8c878f9 |
{
unsigned int fc = 0; |
c8c80ddf |
fmap_t *zmap = map; |
c8c878f9 |
size_t fsize;
uint32_t coff = 0;
const char *ptr;
int ret = CL_CLEAN; |
20b45621 |
#if HAVE_JSON
uint32_t toval = 0;
#endif |
c8c878f9 |
cli_dbgmsg("in unzip_search\n"); |
c8c80ddf |
if ((!ctx && !map) || !requests) { |
c8c878f9 |
return CL_ENULLARG;
}
|
c8c80ddf |
/* get priority to given map over *ctx->fmap */
if (ctx && !map)
zmap = *ctx->fmap;
fsize = zmap->len;
if(sizeof(off_t)!=sizeof(uint32_t) && fsize!=zmap->len) { |
c8c878f9 |
cli_dbgmsg("unzip_search: file too big\n");
return CL_CLEAN;
}
if (fsize < SIZEOF_CH) {
cli_dbgmsg("unzip_search: file too short\n");
return CL_CLEAN;
}
for(coff=fsize-22 ; coff>0 ; coff--) { /* sizeof(EOC)==22 */ |
c8c80ddf |
if(!(ptr = fmap_need_off_once(zmap, coff, 20))) |
c8c878f9 |
continue;
if(cli_readint32(ptr)==0x06054b50) {
uint32_t chptr = cli_readint32(&ptr[16]);
if(!CLI_ISCONTAINED(0, fsize, chptr, SIZEOF_CH)) continue;
coff=chptr;
break;
}
}
if(coff) {
cli_dbgmsg("unzip_search: central @%x\n", coff); |
dcd26ea5 |
while(ret==CL_CLEAN && (coff=chdr(zmap, coff, fsize, NULL, fc+1, &ret, ctx, NULL, requests, NULL, NULL, NULL))) { |
c8c80ddf |
if (requests->match) { |
93a9a942 |
ret=CL_VIRUS; |
c8c878f9 |
}
fc++; |
c8c80ddf |
if (ctx && ctx->engine->maxfiles && fc >= ctx->engine->maxfiles) { |
c8c878f9 |
cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles);
ret=CL_EMAXFILES;
} |
20b45621 |
#if HAVE_JSON |
c8c80ddf |
if (ctx && cli_json_timeout_cycle_check(ctx, (int *)(&toval)) != CL_SUCCESS) { |
93a9a942 |
ret=CL_ETIMEOUT; |
20b45621 |
}
#endif |
c8c878f9 |
}
} else {
cli_dbgmsg("unzip_search: cannot locate central directory\n");
}
return ret;
}
|
c8c80ddf |
int unzip_search_single(cli_ctx *ctx, const char *name, size_t nlen, uint32_t *loff)
{
struct zip_requests requests;
int ret;
cli_dbgmsg("in unzip_search_single\n");
if (!ctx) {
return CL_ENULLARG;
}
memset(&requests, 0, sizeof(struct zip_requests));
if ((ret = unzip_search_add(&requests, name, nlen)) != CL_SUCCESS) {
return ret;
}
if ((ret = unzip_search(ctx, NULL, &requests)) == CL_VIRUS) {
*loff = requests.loff;
}
return ret;
}
|