From f8be651b4329befa7824abba32559e3784900f56 Mon Sep 17 00:00:00 2001 From: aCaB Date: Tue, 1 Jan 2008 23:09:15 +0000 Subject: [PATCH] unzip/implode support git-svn: trunk@3476 --- ChangeLog | 5 + libclamav/Makefile.am | 4 +- libclamav/Makefile.in | 7 +- libclamav/explode.c | 315 ++++++++++++++++++++++++++++++++++++++++++ libclamav/explode.h | 73 ++++++++++ libclamav/unzip.c | 50 ++++++- 6 files changed, 446 insertions(+), 8 deletions(-) create mode 100644 libclamav/explode.c create mode 100644 libclamav/explode.h diff --git a/ChangeLog b/ChangeLog index 468fa9c6d..d24e0989f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Tue Jan 1 23:30:06 CET 2008 (acab) +----------------------------------- + * libclamav/unzip: add support for zip method 6 (implode) - bb#584 + fix for zip method 12 (bzip2) + Mon Dec 31 14:08:40 EET 2007 (edwin) ------------------------------------ * configure*: add support for version scripts when using Sun's ld on Solaris. diff --git a/libclamav/Makefile.am b/libclamav/Makefile.am index 8b07f7d57..0a0b4d22b 100644 --- a/libclamav/Makefile.am +++ b/libclamav/Makefile.am @@ -180,7 +180,9 @@ libclamav_la_SOURCES = \ dconf.c \ dconf.h \ lzma_iface.c \ - lzma_iface.h + lzma_iface.h \ + explode.c \ + explode.h libclamav_internal_utils_la_SOURCES=str.c \ str.h \ diff --git a/libclamav/Makefile.in b/libclamav/Makefile.in index 56de8b26f..a8a54864a 100644 --- a/libclamav/Makefile.in +++ b/libclamav/Makefile.in @@ -88,7 +88,7 @@ am_libclamav_la_OBJECTS = matcher-ac.lo matcher-bm.lo matcher.lo \ pdf.lo spin.lo yc.lo elf.lo sis.lo uuencode.lo pst.lo \ phishcheck.lo phish_domaincheck_db.lo phish_whitelist.lo \ regex_list.lo mspack.lo cab.lo entconv.lo hashtab.lo dconf.lo \ - lzma_iface.lo + lzma_iface.lo explode.lo libclamav_la_OBJECTS = $(am_libclamav_la_OBJECTS) libclamav_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ @@ -410,7 +410,9 @@ libclamav_la_SOURCES = \ dconf.c \ dconf.h \ lzma_iface.c \ - lzma_iface.h + lzma_iface.h \ + explode.c \ + explode.h libclamav_internal_utils_la_SOURCES = str.c \ str.h \ @@ -515,6 +517,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsig.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entconv.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/explode.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filetypes.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fsg.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hashtab.Plo@am__quote@ diff --git a/libclamav/explode.c b/libclamav/explode.c new file mode 100644 index 000000000..2309af00f --- /dev/null +++ b/libclamav/explode.c @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2007 Sourcefire Inc. + * Author: aCaB + * + * 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. + */ + +/* + * Written from scratch based on specs from PKWARE: + * see www.pkware.com/documents/casestudies/APPNOTE.TXT + * + * To the best of my knowledge, it's patent free: + * http://www.unisys.com/about__unisys/lzw +*/ + + +/* To Cami and Dario, the only laywers I can stand */ + + +#if HAVE_CONFIG_H +#include "clamav-config.h" +#endif + +#if HAVE_STRING_H +#include +#endif + +#include "explode.h" + +/* NOTE: sorting algo must be stable! */ +static void bs(uint8_t *k, uint8_t *v, unsigned int elements) { + uint8_t tmp; + unsigned int i=0, l=0, stop=0, r=elements; + + while(!stop) { + stop=1; + for(; iv[k[i+1]]) { + tmp=k[i]; + k[i]=k[i+1]; + k[i+1]=tmp; + stop=0; + } + } + if(stop) break; + r--; + i--; + for(; i>l; i--) { + if(v[k[i]]window; + uint8_t packsz; + unsigned int i; + uint16_t code=0, codeinc=0, lastlen=0; + + packsz=*cur++; + + for(i=0; i>4) + 1; + if(values>i) return 1; + i-=values; + while(values--) + *ttree++ = len; + } while(packsz--); + + if(i) return 1; + + bs(order, temptree, expected-1); + + i=expected-1; + do { + code=code+codeinc; + if(temptree[order[i]]!=lastlen) { + lastlen=temptree[order[i]]; + codeinc=1<<(16-lastlen); + } + tree[order[i]]=code | ((uint32_t)lastlen<<16); + } while(i--); + + return 0; +} + +/* bit lame of a lookup, but prolly not worth optimizing */ +static int lookup_tree(uint32_t *tree, unsigned int size, uint16_t code, uint8_t len) { + uint32_t lookup=((uint32_t)(len+1))<<16 | code; + unsigned int i; + for(i=0; ibits = X->cur = 0; + if(flags&2) { + X->largewin = 1; + X->mask = 0x1fff; + } else { + X->largewin = 0; + X->mask = 0xfff; + } + if(flags&4) { + X->state = GRABLITS; + X->litcodes = 1; + X->minlen=3; + } else { + X->state = GRABLENS; + X->litcodes = 0; + X->minlen=2; + } + X->got=0; + return EXPLODE_OK; +} + +#define GETBIT \ + if(X->bits) { \ + X->bits--; \ + val=X->bitmap&1; \ + X->bitmap>>=1; \ + } else { \ + if(!X->avail_in) return EXPLODE_EBUFF; \ + if(X->avail_in>=4) { \ + X->bitmap=*(uint32_t *)X->next_in; \ + X->bits=31; \ + X->next_in+=4; \ + X->avail_in-=4; \ + } else { \ + X->bitmap=*X->next_in; \ + X->bits=7; \ + X->next_in++; \ + X->avail_in--; \ + } \ + val=X->bitmap&1; \ + X->bitmap>>=1; \ + } + + +#define GETBITS(NUM) \ + if(X->bits>=(NUM)) { \ + val=X->bitmap&((1<<(NUM))-1); \ + X->bitmap>>=(NUM); \ + X->bits-=(NUM); \ + } else { \ + if(X->avail_in*8+X->bits<(NUM)) return EXPLODE_EBUFF; \ + val=X->bitmap; \ + if(X->avail_in>=4) { \ + X->bitmap=*(uint32_t *)X->next_in; \ + X->next_in+=4; \ + X->avail_in-=4; \ + val|=(X->bitmap&((1<<((NUM)-X->bits))-1))<bits; \ + X->bitmap>>=(NUM)-X->bits; \ + X->bits=32-((NUM)-X->bits); \ + } else { \ + X->bitmap=*X->next_in; \ + X->next_in++; \ + X->avail_in--; \ + val|=(X->bitmap&((1<<((NUM)-X->bits))-1))<bits; \ + X->bitmap>>=(NUM)-X->bits; \ + X->bits=8-((NUM)-X->bits); \ + } \ + } + + +#define GETCODES(CASE, WHICH, HOWMANY) \ + case CASE: { \ + if(!X->avail_in) return EXPLODE_EBUFF; \ + if(!X->got) need = *X->next_in; \ + else need = X->window[0]; \ + if(need > HOWMANY - 1) return EXPLODE_ESTREAM; /* too many codes */ \ + need = need + 2 - X->got; /* bytes remaining */ \ + if(need>X->avail_in) { /* if not enuff */ \ + /* just copy what's avail... */ \ + memcpy(&X->window[X->got], X->next_in, X->avail_in); \ + X->got += X->avail_in; \ + X->next_in += X->avail_in; \ + X->avail_in = 0; \ + return EXPLODE_EBUFF; /* ...and beg for more */ \ + } \ + /* else fetch what's needed */ \ + memcpy(&X->window[X->got], X->next_in, need); \ + X->avail_in -= need; \ + X->next_in += need; \ + if(unpack_tree(X, X->WHICH, HOWMANY )) return EXPLODE_ESTREAM; \ + /* and move on */ \ + X->got=0; \ + X->state++; \ + } + +#define SETCASE(CASE) \ + X->state = (CASE); \ + case(CASE): \ + {/* FAKE */} + +int explode(struct xplstate *X) { + unsigned int val, need; + int temp; + + switch(X->state) { + /* grab compressed coded literals, if present */ + GETCODES(GRABLITS, lit_tree, 256); + /* grab compressed coded lens */ + GETCODES(GRABLENS, len_tree, 64); + /* grab compressed coded dists */ + GETCODES(GRABDISTS, dist_tree, 64); + + case EXPLODE: + while(X->avail_in || X->bits) { + GETBIT; /* can't fail */ + if(val) { + if(X->litcodes) { + X->backsize=0; + X->state=EXPLODE_LITCODES; + for(X->got=0; X->got<=15; X->got++) { + case EXPLODE_LITCODES: + GETBIT; + X->backsize|=val<<(15-X->got); + if((temp=lookup_tree(X->lit_tree, 256, X->backsize, X->got))!=-1) break; + } + if(temp==-1) return EXPLODE_ESTREAM; + X->got=temp; + } else { + SETCASE(EXPLODE_LITS); + GETBITS(8); + X->got=val; + } + SETCASE(EXPLODE_WBYTE); + if(!X->avail_out) return EXPLODE_EBUFF; + X->avail_out--; + *X->next_out = X->window[X->cur & X->mask] = X->got; + X->cur++; + X->next_out++; + } else { + SETCASE(EXPLODE_BASEDIST); + GETBITS(6+X->largewin); + X->backbytes=val; + X->backsize=0; + X->state=EXPLODE_DECODEDISTS; + for(X->got=0; X->got<=15; X->got++) { + case EXPLODE_DECODEDISTS: + GETBIT; + X->backsize|=val<<(15-X->got); + if((temp=lookup_tree(X->dist_tree, 64, X->backsize, X->got))!=-1) break; + } + if(temp==-1) return EXPLODE_ESTREAM; + X->backbytes|=temp<<(6+X->largewin); + X->backbytes++; + X->backsize=0; + X->state=EXPLODE_DECODELENS; + for(X->got=0; X->got<=15; X->got++) { + case EXPLODE_DECODELENS: + GETBIT; + X->backsize|=val<<(15-X->got); + if((temp=lookup_tree(X->len_tree, 64, X->backsize, X->got))!=-1) break; + } + if(temp==-1) { + cli_dbgmsg("HERE3\n"); + return EXPLODE_ESTREAM; + } + if(temp==63) { + SETCASE(EXPLODE_DECODEEXTRA); + GETBITS(8); + temp=63+val; + } + X->backsize=temp+X->minlen; + X->state=EXPLODE_BACKCOPY; + while(X->backsize--) { + case EXPLODE_BACKCOPY: + if(!X->avail_out) return EXPLODE_EBUFF; + X->avail_out--; + if (X->cur>=X->backbytes) + *X->next_out = X->window[X->cur & X->mask] = X->window[(X->cur-X->backbytes) & X->mask]; + else + *X->next_out = X->window[X->cur & X->mask] = 0; + X->cur++; + X->next_out++; + } + } + X->state=EXPLODE; + } + } + return EXPLODE_EBUFF; +} + +void explode_shutdown() {}; diff --git a/libclamav/explode.h b/libclamav/explode.h new file mode 100644 index 000000000..493e268e2 --- /dev/null +++ b/libclamav/explode.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2007 Sourcefire Inc. + * Author: aCaB + * + * 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. + */ + +#ifndef __EXPLODE_H +#define __EXPLODE_H + +#include "cltypes.h" + +enum { + EXPLODE_EBUFF, + EXPLODE_ESTREAM +}; + +#define EXPLODE_OK EXPLODE_EBUFF + +enum XPL_STATE { + GRABLITS, + GRABLENS, + GRABDISTS, + EXPLODE, + EXPLODE_LITCODES, + EXPLODE_LITS, + EXPLODE_BASEDIST, + EXPLODE_DECODEDISTS, + EXPLODE_DECODELENS, + EXPLODE_DECODEEXTRA, + EXPLODE_WBYTE, + EXPLODE_BACKCOPY +}; + +struct xplstate { + uint8_t *next_in; + uint8_t *next_out; + unsigned int got; + unsigned int minlen; + unsigned int mask; + unsigned int cur; + uint32_t lit_tree[256]; + uint32_t len_tree[64]; + uint32_t dist_tree[64]; + uint32_t bitmap; + uint32_t avail_in; + uint32_t avail_out; + uint16_t backbytes; + uint16_t backsize; + uint8_t window[8192]; + uint8_t bits; + uint8_t largewin; + uint8_t litcodes; + enum XPL_STATE state; +}; + +int explode_init(struct xplstate *, uint8_t); +int explode(struct xplstate *); +void explode_shutdown(); + +#endif /* __EXPLODE_H */ diff --git a/libclamav/unzip.c b/libclamav/unzip.c index c43f97bc2..697c6165f 100644 --- a/libclamav/unzip.c +++ b/libclamav/unzip.c @@ -45,6 +45,7 @@ #include #endif +#include "explode.h" #include "others.h" #include "clamav.h" #include "scanners.h" @@ -62,7 +63,7 @@ static int wrap_inflateinit2(void *a, int b) { return inflateInit2(a, b); } -static int unz(uint8_t *src, uint32_t csize, uint32_t usize, uint16_t method, unsigned int *fu, cli_ctx *ctx, char *tmpd) { +static int unz(uint8_t *src, uint32_t csize, uint32_t usize, uint16_t method, uint16_t flags, unsigned int *fu, cli_ctx *ctx, char *tmpd) { char name[1024], obuf[BUFSIZ]; char *tempfile = name; int of, ret=CL_CLEAN; @@ -84,7 +85,7 @@ static int unz(uint8_t *src, uint32_t csize, uint32_t usize, uint16_t method, un if(csizelimits->maxfilesize); - res = Z_STREAM_END; + res = BZ_STREAM_END; break; } if(cli_writen(of, obuf, sizeof(obuf)-strm.avail_out) != (int)(sizeof(obuf)-strm.avail_out)) { @@ -216,6 +217,7 @@ static int unz(uint8_t *src, uint32_t csize, uint32_t usize, uint16_t method, un } strm.next_out = obuf; strm.avail_out = sizeof(obuf); + continue; } break; } @@ -227,6 +229,45 @@ static int unz(uint8_t *src, uint32_t csize, uint32_t usize, uint16_t method, un } #endif /* HAVE_BZLIB_H */ + + case ALG_IMPLODE: { + struct xplstate strm; + strm.next_in = (char *)src; + strm.next_out = obuf; + 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; + if(ctx->limits && ctx->limits->maxfilesize && written > ctx->limits->maxfilesize) { + if(BLOCKMAX) { + *ctx->virname = "Zip.ExceededFileSize"; + ret = CL_VIRUS; + break; + } + cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", ctx->limits->maxfilesize); + res = 0; + break; + } + if(cli_writen(of, obuf, sizeof(obuf)-strm.avail_out) != (int)(sizeof(obuf)-strm.avail_out)) { + cli_warnmsg("cli_unzip: falied to write %lu exploded bytes\n", sizeof(obuf)-strm.avail_out); + ret = CL_EIO; + res=1; + } + strm.next_out = obuf; + strm.avail_out = sizeof(obuf); + continue; + } + break; + } + break; + } + + case ALG_LZMA: /* easy but there's not a single sample in the zoo */ @@ -238,7 +279,6 @@ static int unz(uint8_t *src, uint32_t csize, uint32_t usize, uint16_t method, un case ALG_REDUCE2: case ALG_REDUCE3: case ALG_REDUCE4: - case ALG_IMPLODE: case ALG_TOKENZD: case ALG_OLDTERSE: case ALG_RSVD1: @@ -365,7 +405,7 @@ static unsigned int lhdr(uint8_t *zip, uint32_t zsize, unsigned int *fu, unsigne *ctx->virname = "Oversized.Zip"; *ret = CL_VIRUS; return 0; - } else *ret = unz(zip, csize, usize, LH_method, fu, ctx, tmpd); + } else *ret = unz(zip, csize, usize, LH_method, LH_flags, fu, ctx, tmpd); zip+=csize; zsize-=csize; }