faaf436a |
/* |
2023340a |
* Copyright (C) 2007-2008 Sourcefire Inc.
*
* Authors: Alberto Wu |
faaf436a |
*
* 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.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "others.h"
#include "cltypes.h"
#include "nsis_bzlib.h" |
84f4e2bb |
/* #include "zlib.h" */
#include "nsis_zlib.h" |
9f0d9b98 |
#include "lzma_iface.h" |
69c06112 |
#include "matcher.h"
#include "scanners.h" |
a993c5ad |
#include "nulsft.h" /* SHUT UP GCC -Wextra */ |
5ef1fa42 |
#include "fmap.h" |
faaf436a |
#define EC32(x) le32_to_host(x)
enum {
COMP_NOT_DETECTED,
COMP_BZIP2,
COMP_LZMA,
COMP_ZLIB,
COMP_NOCOMP
};
|
b8ad506b |
struct nsis_st { |
5ef1fa42 |
size_t curpos; |
b8ad506b |
int ofd;
off_t off; |
1cd33a34 |
off_t fullsz; |
b8ad506b |
char *dir;
uint32_t asz;
uint32_t hsz;
uint32_t fno; |
1cd33a34 |
uint8_t comp;
uint8_t solid;
uint8_t freecomp;
uint8_t eof; |
24555841 |
struct stream_state nsis; |
b8ad506b |
nsis_bzstream bz; |
87787e76 |
struct CLI_LZMA lz; |
84f4e2bb |
/* z_stream z; */
nsis_z_stream z; |
b8ad506b |
unsigned char *freeme; |
49cc1e3c |
fmap_t *map; |
b8ad506b |
char ofn[1024];
};
|
faaf436a |
#define LINESTR(x) #x
#define LINESTR2(x) LINESTR(x)
#define __AT__ " at "__FILE__":"LINESTR2(__LINE__)
static int nsis_init(struct nsis_st *n) {
switch(n->comp) {
case COMP_BZIP2: |
f377e052 |
memset(&n->bz, 0, sizeof(nsis_bzstream)); |
faaf436a |
if (nsis_BZ2_bzDecompressInit(&n->bz, 0, 0)!=BZ_OK) |
871177cd |
return CL_EUNPACK; |
faaf436a |
n->freecomp=1;
break;
case COMP_LZMA: |
5e1d2c28 |
memset(&n->lz, 0, sizeof(struct CLI_LZMA));
if(cli_LzmaInit(&n->lz, 0xffffffffffffffffULL)!=LZMA_RESULT_OK)
return CL_EUNPACK; |
faaf436a |
n->freecomp=1;
break;
case COMP_ZLIB: |
cadf86d4 |
memset(&n->z, 0, sizeof(z_stream)); |
84f4e2bb |
/* inflateInit2(&n->z, -MAX_WBITS); */
/* n->freecomp=1; */
nsis_inflateInit(&n->z);
n->freecomp=0; |
faaf436a |
}
return CL_SUCCESS;
}
static void nsis_shutdown(struct nsis_st *n) {
if(!n->freecomp)
return;
switch(n->comp) {
case COMP_BZIP2:
nsis_BZ2_bzDecompressEnd(&n->bz);
break;
case COMP_LZMA: |
f377e052 |
cli_LzmaShutdown(&n->lz); |
19da6c08 |
break; |
faaf436a |
case COMP_ZLIB: |
84f4e2bb |
/* inflateEnd(&n->z); */ |
faaf436a |
break;
}
n->freecomp=0;
}
static int nsis_decomp(struct nsis_st *n) { |
c9896758 |
int ret = CL_EFORMAT; |
faaf436a |
switch(n->comp) {
case COMP_BZIP2:
n->bz.avail_in = n->nsis.avail_in;
n->bz.next_in = n->nsis.next_in;
n->bz.avail_out = n->nsis.avail_out;
n->bz.next_out = n->nsis.next_out;
switch (nsis_BZ2_bzDecompress(&n->bz)) {
case BZ_OK:
ret = CL_SUCCESS;
break;
case BZ_STREAM_END:
ret = CL_BREAK;
}
n->nsis.avail_in = n->bz.avail_in;
n->nsis.next_in = n->bz.next_in;
n->nsis.avail_out = n->bz.avail_out;
n->nsis.next_out = n->bz.next_out;
break;
case COMP_LZMA: |
87787e76 |
n->lz.avail_in = n->nsis.avail_in;
n->lz.next_in = n->nsis.next_in;
n->lz.avail_out = n->nsis.avail_out;
n->lz.next_out = n->nsis.next_out;
switch (cli_LzmaDecode(&n->lz)) { |
f377e052 |
case LZMA_RESULT_OK: |
faaf436a |
ret = CL_SUCCESS;
break;
case LZMA_STREAM_END:
ret = CL_BREAK;
} |
87787e76 |
n->nsis.avail_in = n->lz.avail_in;
n->nsis.next_in = n->lz.next_in;
n->nsis.avail_out = n->lz.avail_out;
n->nsis.next_out = n->lz.next_out; |
faaf436a |
break;
case COMP_ZLIB:
n->z.avail_in = n->nsis.avail_in;
n->z.next_in = n->nsis.next_in;
n->z.avail_out = n->nsis.avail_out;
n->z.next_out = n->nsis.next_out; |
84f4e2bb |
/* switch (inflate(&n->z, Z_NO_FLUSH)) { */
switch (nsis_inflate(&n->z)) { |
faaf436a |
case Z_OK:
ret = CL_SUCCESS;
break;
case Z_STREAM_END:
ret = CL_BREAK;
}
n->nsis.avail_in = n->z.avail_in;
n->nsis.next_in = n->z.next_in;
n->nsis.avail_out = n->z.avail_out;
n->nsis.next_out = n->z.next_out;
break;
}
return ret;
}
static int nsis_unpack_next(struct nsis_st *n, cli_ctx *ctx) {
unsigned char *ibuf;
uint32_t size, loops; |
a87b030d |
int ret, gotsome=0; |
faaf436a |
unsigned char obuf[BUFSIZ];
if (n->eof) {
cli_dbgmsg("NSIS: extraction complete\n");
return CL_BREAK;
} |
bbd6ca3f |
|
5a0c148d |
if ((ret=cli_checklimits("NSIS", ctx, 0, 0, 0))!=CL_CLEAN) |
bbd6ca3f |
return ret; |
faaf436a |
if (n->fno) |
58481352 |
snprintf(n->ofn, 1023, "%s"PATHSEP"content.%.3u", n->dir, n->fno); |
faaf436a |
else |
58481352 |
snprintf(n->ofn, 1023, "%s"PATHSEP"headers", n->dir); |
faaf436a |
n->fno++;
if ((n->ofd=open(n->ofn, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600))==-1) {
cli_errmsg("NSIS: unable to create output file %s - aborting.", n->ofn); |
871177cd |
return CL_ECREAT; |
faaf436a |
}
if (!n->solid) { |
5ef1fa42 |
if (fmap_readn(n->map, &size, n->curpos, 4)!=4) { |
faaf436a |
cli_dbgmsg("NSIS: reached EOF - extraction complete\n");
close(n->ofd);
return CL_BREAK;
} |
5ef1fa42 |
n->curpos += 4; |
faaf436a |
if (n->asz==4) {
cli_dbgmsg("NSIS: reached CRC - extraction complete\n");
close(n->ofd);
return CL_BREAK;
}
loops = EC32(size);
if (!(size = (loops&~0x80000000))) {
cli_dbgmsg("NSIS: empty file found\n");
return CL_SUCCESS;
}
if (n->asz <4 || size > n->asz-4) {
cli_dbgmsg("NSIS: next file is outside the archive\n");
close(n->ofd);
return CL_BREAK;
}
n->asz -= size+4;
|
5a0c148d |
if ((ret=cli_checklimits("NSIS", ctx, size, 0, 0))!=CL_CLEAN) { |
faaf436a |
close(n->ofd); |
5ef1fa42 |
n->curpos += size; |
bbd6ca3f |
return ret; |
faaf436a |
} |
5ef1fa42 |
if (!(ibuf = fmap_need_off_once(n->map, n->curpos, size))) { |
faaf436a |
cli_dbgmsg("NSIS: cannot read %u bytes"__AT__"\n", size);
close(n->ofd); |
871177cd |
return CL_EREAD; |
faaf436a |
} |
5ef1fa42 |
n->curpos += size; |
faaf436a |
if (loops==size) {
if (cli_writen(n->ofd, ibuf, size) != (ssize_t) size) {
cli_dbgmsg("NSIS: cannot write output file"__AT__"\n");
close(n->ofd); |
871177cd |
return CL_EWRITE; |
faaf436a |
}
} else {
if ((ret=nsis_init(n))!=CL_SUCCESS) {
cli_dbgmsg("NSIS: decompressor init failed"__AT__"\n");
close(n->ofd);
return ret;
}
n->nsis.avail_in = size;
n->nsis.next_in = ibuf;
n->nsis.next_out = obuf;
n->nsis.avail_out = BUFSIZ;
loops=0;
while ((ret=nsis_decomp(n))==CL_SUCCESS) {
if ((size = n->nsis.next_out - obuf)) { |
a87b030d |
gotsome=1; |
faaf436a |
if (cli_writen(n->ofd, obuf, size) != (ssize_t) size) {
cli_dbgmsg("NSIS: cannot write output file"__AT__"\n");
close(n->ofd); |
a87b030d |
nsis_shutdown(n); |
871177cd |
return CL_EWRITE; |
faaf436a |
}
n->nsis.next_out = obuf;
n->nsis.avail_out = BUFSIZ;
loops=0; |
bbd6ca3f |
if ((ret=cli_checklimits("NSIS", ctx, size, 0, 0))!=CL_CLEAN) { |
faaf436a |
close(n->ofd);
nsis_shutdown(n); |
bbd6ca3f |
return ret; |
faaf436a |
} |
a87b030d |
} else if (++loops > 20) { |
faaf436a |
cli_dbgmsg("NSIS: xs looping, breaking out"__AT__"\n"); |
a87b030d |
ret = CL_EFORMAT; |
faaf436a |
break;
}
}
|
a87b030d |
nsis_shutdown(n);
if (n->nsis.next_out - obuf) {
gotsome=1;
if (cli_writen(n->ofd, obuf, n->nsis.next_out - obuf) != n->nsis.next_out - obuf) {
cli_dbgmsg("NSIS: cannot write output file"__AT__"\n");
close(n->ofd); |
871177cd |
return CL_EWRITE; |
a87b030d |
} |
faaf436a |
}
|
a87b030d |
if (ret != CL_SUCCESS && ret != CL_BREAK) {
cli_dbgmsg("NSIS: bad stream"__AT__"\n");
if (gotsome) {
ret = CL_SUCCESS;
} else { |
04468f36 |
ret = CL_EMAXSIZE; |
a87b030d |
close(n->ofd);
}
return ret; |
faaf436a |
} |
a87b030d |
|
faaf436a |
}
return CL_SUCCESS;
} else {
if (!n->freeme) {
if ((ret=nsis_init(n))!=CL_SUCCESS) {
cli_dbgmsg("NSIS: decompressor init failed\n");
close(n->ofd);
return ret;
} |
5ef1fa42 |
if(!(n->freeme = fmap_need_off_once(n->map, n->curpos, n->asz))) { |
cd6d37c1 |
cli_dbgmsg("NSIS: cannot read %u bytes"__AT__"\n", n->asz); |
faaf436a |
close(n->ofd); |
871177cd |
return CL_EREAD; |
faaf436a |
}
n->nsis.next_in = n->freeme;
n->nsis.avail_in = n->asz;
}
if (n->nsis.avail_in<=4) {
cli_dbgmsg("NSIS: extraction complete\n");
close(n->ofd);
return CL_BREAK;
}
n->nsis.next_out = obuf;
n->nsis.avail_out = 4;
loops = 0;
while ((ret=nsis_decomp(n))==CL_SUCCESS) {
if (n->nsis.next_out - obuf == 4) break;
if (++loops > 20) {
cli_dbgmsg("NSIS: xs looping, breaking out"__AT__"\n");
ret = CL_BREAK;
break;
}
}
if (ret != CL_SUCCESS) {
cli_dbgmsg("NSIS: bad stream"__AT__"\n");
close(n->ofd);
return CL_EFORMAT;
}
size=cli_readint32(obuf); |
bbd6ca3f |
if ((ret=cli_checklimits("NSIS", ctx, size, 0, 0))!=CL_CLEAN) { |
faaf436a |
close(n->ofd); |
bbd6ca3f |
return ret; |
faaf436a |
}
n->nsis.next_out = obuf;
n->nsis.avail_out = MIN(BUFSIZ,size);
loops = 0;
while (size && (ret=nsis_decomp(n))==CL_SUCCESS) {
unsigned int wsz;
if ((wsz = n->nsis.next_out - obuf)) { |
a87b030d |
gotsome=1; |
faaf436a |
if (cli_writen(n->ofd, obuf, wsz) != (ssize_t) wsz) { |
a87b030d |
cli_dbgmsg("NSIS: cannot write output file"__AT__"\n"); |
faaf436a |
close(n->ofd); |
871177cd |
return CL_EWRITE; |
faaf436a |
}
size-=wsz; |
a87b030d |
loops=0; |
faaf436a |
n->nsis.next_out = obuf;
n->nsis.avail_out = MIN(size,BUFSIZ);
} else if ( ++loops > 20 ) {
cli_dbgmsg("NSIS: xs looping, breaking out"__AT__"\n"); |
a87b030d |
ret = CL_EFORMAT; |
faaf436a |
break;
}
}
|
a87b030d |
if (n->nsis.next_out - obuf) {
gotsome=1; |
faaf436a |
if (cli_writen(n->ofd, obuf, n->nsis.next_out - obuf) != n->nsis.next_out - obuf) { |
a87b030d |
cli_dbgmsg("NSIS: cannot write output file"__AT__"\n"); |
faaf436a |
close(n->ofd); |
871177cd |
return CL_EWRITE; |
faaf436a |
} |
a87b030d |
}
if (ret == CL_EFORMAT) {
cli_dbgmsg("NSIS: bad stream"__AT__"\n");
if (!gotsome) {
close(n->ofd);
return CL_EMAXSIZE;
}
}
if (ret == CL_EFORMAT || ret == CL_BREAK) { |
faaf436a |
n->eof=1;
} else if (ret != CL_SUCCESS) {
cli_dbgmsg("NSIS: bad stream"__AT__"\n");
close(n->ofd);
return CL_EFORMAT;
}
return CL_SUCCESS;
}
}
|
b8ad506b |
static uint8_t nsis_detcomp(const char *b) { |
faaf436a |
if (*b=='1') return COMP_BZIP2;
if ((cli_readint32(b)&~0x80000000)==0x5d) return COMP_LZMA;
return COMP_ZLIB;
}
static int nsis_headers(struct nsis_st *n, cli_ctx *ctx) { |
5ef1fa42 |
const char *buf; |
faaf436a |
uint32_t pos;
int i;
uint8_t comps[] = {0, 0, 0, 0}, trunc = 0;
|
5ef1fa42 |
if (!(buf = fmap_need_off_once(n->map, n->off, 0x1c))) |
871177cd |
return CL_EREAD; |
faaf436a |
n->hsz = (uint32_t)cli_readint32(buf+0x14);
n->asz = (uint32_t)cli_readint32(buf+0x18); |
5ef1fa42 |
n->fullsz = n->map->len; |
faaf436a |
cli_dbgmsg("NSIS: Header info - Flags=%x, Header size=%x, Archive size=%x\n", cli_readint32(buf), n->hsz, n->asz);
|
5ef1fa42 |
if (n->fullsz - n->off < (off_t) n->asz) { |
faaf436a |
cli_dbgmsg("NSIS: Possibly truncated file\n"); |
5ef1fa42 |
n->asz = n->fullsz - n->off; |
faaf436a |
trunc++; |
5ef1fa42 |
} else if (n->fullsz - n->off != (off_t) n->asz) { |
faaf436a |
cli_dbgmsg("NSIS: Overlays found\n");
}
n->asz -= 0x1c; |
5ef1fa42 |
buf += 0x1c; |
faaf436a |
/* Guess if solid */
for (i=0, pos=0;pos < n->asz-4;i++) {
int32_t nextsz; |
49cc1e3c |
if (!(buf = fmap_need_ptr_once(n->map, (void *)buf, 4))) return CL_EREAD; |
5ef1fa42 |
nextsz=cli_readint32(buf);
if (!i) n->comp = nsis_detcomp(buf);
buf += 4; |
faaf436a |
if (nextsz&0x80000000) {
nextsz&=~0x80000000; |
49cc1e3c |
if (!(buf = fmap_need_ptr_once(n->map, (void *)buf, 4))) return CL_EREAD; |
5ef1fa42 |
comps[nsis_detcomp(buf)]++; |
faaf436a |
nextsz-=4;
pos+=4; |
5ef1fa42 |
buf+=4; |
faaf436a |
}
if ((pos+=4+nextsz) > n->asz) {
n->solid = 1;
break;
}
|
5ef1fa42 |
buf += nextsz; |
faaf436a |
}
if (trunc && i>=2) n->solid=0;
cli_dbgmsg("NSIS: solid compression%s detected\n", (n->solid)?"":" not");
/* Guess the compression method */
if (!n->solid) {
cli_dbgmsg("NSIS: bzip2 %u - lzma %u - zlib %u\n", comps[1], comps[2], comps[3]);
n->comp = (comps[1]<comps[2]) ? (comps[2]<comps[3] ? COMP_ZLIB : COMP_LZMA) : (comps[1]<comps[3] ? COMP_ZLIB : COMP_BZIP2);
}
|
5ef1fa42 |
n->curpos = n->off+0x1c; |
faaf436a |
return nsis_unpack_next(n, ctx);
}
|
a993c5ad |
static int cli_nsis_unpack(struct nsis_st *n, cli_ctx *ctx) { |
faaf436a |
return (n->fno) ? nsis_unpack_next(n, ctx) : nsis_headers(n, ctx);
}
|
05eff05f |
int cli_scannulsft(int desc, cli_ctx *ctx, off_t offset) {
int ret;
struct nsis_st nsist;
cli_dbgmsg("in scannulsft()\n");
memset(&nsist, 0, sizeof(struct nsis_st));
nsist.off = offset; |
33068e09 |
if (!(nsist.dir = cli_gentemp(ctx->engine->tmpdir))) |
f5354453 |
return CL_ETMPDIR; |
05eff05f |
if(mkdir(nsist.dir, 0700)) {
cli_dbgmsg("NSIS: Can't create temporary directory %s\n", nsist.dir);
free(nsist.dir);
return CL_ETMPDIR;
} |
5ef1fa42 |
if(!(nsist.map = fmap(desc, 0, 0))) {
cli_errmsg("scannulsft: fmap failed\n");
return CL_EMEM;
} |
05eff05f |
|
33068e09 |
if(ctx->engine->keeptmp) cli_dbgmsg("NSIS: Extracting files to %s\n", nsist.dir); |
b8ad506b |
|
05eff05f |
do {
ret = cli_nsis_unpack(&nsist, ctx); |
c17ed38c |
if (ret == CL_SUCCESS) {
cli_dbgmsg("NSIS: Successully extracted file #%u\n", nsist.fno);
lseek(nsist.ofd, 0, SEEK_SET);
if(nsist.fno == 1) |
ffa9b060 |
ret=cli_scandesc(nsist.ofd, ctx, 0, 0, NULL, AC_SCAN_VIR, NULL); |
c17ed38c |
else
ret=cli_magic_scandesc(nsist.ofd, ctx);
close(nsist.ofd); |
33068e09 |
if(!ctx->engine->keeptmp) |
871177cd |
if(cli_unlink(nsist.ofn)) ret = CL_EUNLINK; |
c17ed38c |
} else if(ret == CL_EMAXSIZE) {
ret = nsist.solid ? CL_BREAK : CL_SUCCESS; |
05eff05f |
}
} while(ret == CL_SUCCESS);
|
bbd6ca3f |
if(ret == CL_BREAK || ret == CL_EMAXFILES) |
05eff05f |
ret = CL_CLEAN;
|
5ef1fa42 |
nsis_shutdown(&nsist); |
49cc1e3c |
funmap(nsist.map); |
05eff05f |
|
33068e09 |
if(!ctx->engine->keeptmp) |
05eff05f |
cli_rmdirs(nsist.dir);
free(nsist.dir);
return ret;
} |
69c06112 |
|