ca019d6d |
/*
* Copyright (C) 2013 Sourcefire, Inc.
* |
5e56b827 |
* Authors: Steven Morgan <smorgan@sourcefire.com> |
ca019d6d |
*
* 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
|
5e56b827 |
#include <errno.h> |
ca019d6d |
#include "xar.h"
#include "fmap.h" |
5e56b827 |
#if HAVE_LIBXML2 |
b147b8d4 |
#ifdef _WIN32
#ifndef LIBXML_WRITER_ENABLED
#define LIBXML_WRITER_ENABLED 1
#endif
#endif |
5e56b827 |
#include <libxml/xmlreader.h> |
60d8d2c3 |
#include "clamav.h" |
e3f87685 |
#include "str.h" |
ca019d6d |
#include "scanners.h" |
5e56b827 |
#include "inflate64.h" |
9b20264d |
#include "lzma_iface.h" |
5e56b827 |
/*
xar_cleanup_temp_file - cleanup after cli_gentempfd
parameters:
ctx - cli_ctx context pointer
fd - fd to close
tmpname - name of file to unlink, address of storage to free
returns - CL_SUCCESS or CL_EUNLINK
*/
static int xar_cleanup_temp_file(cli_ctx *ctx, int fd, char * tmpname)
{
int rc = CL_SUCCESS; |
fe91f308 |
if (fd > -1)
close(fd);
if (tmpname != NULL) {
if (!ctx->engine->keeptmp) {
if (cli_unlink(tmpname)) { |
96de30ae |
cli_dbgmsg("cli_scanxar: error unlinking tmpfile %s\n", tmpname); |
fe91f308 |
rc = CL_EUNLINK;
} |
5e56b827 |
} |
fe91f308 |
free(tmpname); |
5e56b827 |
}
return rc;
}
/*
xar_get_numeric_from_xml_element - extract xml element value as numeric
parameters:
reader - xmlTextReaderPtr
value - pointer to long to contain the returned value
returns - CL_SUCCESS or CL_EFORMAT
*/
static int xar_get_numeric_from_xml_element(xmlTextReaderPtr reader, long * value)
{ |
e3f87685 |
const xmlChar * numstr; |
5e56b827 |
if (xmlTextReaderRead(reader) == 1 && xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) { |
e3f87685 |
numstr = xmlTextReaderConstValue(reader);
if (numstr) {
*value = atol((const char *)numstr);
if (*value < 0) { |
96de30ae |
cli_dbgmsg("cli_scanxar: XML element value %li\n", *value); |
e3f87685 |
return CL_EFORMAT;
}
return CL_SUCCESS; |
5e56b827 |
}
} |
96de30ae |
cli_dbgmsg("cli_scanxar: No text for XML element\n"); |
5e56b827 |
return CL_EFORMAT;
}
/* |
e3f87685 |
xar_get_checksum_values - extract checksum and hash algorithm from xml element
parameters:
reader - xmlTextReaderPtr
cksum - pointer to char* for returning checksum value.
hash - pointer to int for returning checksum algorithm.
returns - void
*/ |
5a5d38e4 |
static void xar_get_checksum_values(xmlTextReaderPtr reader, unsigned char ** cksum, int * hash) |
e3f87685 |
{
xmlChar * style = xmlTextReaderGetAttribute(reader, (const xmlChar *)"style"); |
5a5d38e4 |
const xmlChar * xmlval; |
e3f87685 |
*hash = XAR_CKSUM_NONE;
if (style == NULL) { |
96de30ae |
cli_dbgmsg("cli_scaxar: xmlTextReaderGetAttribute no style attribute " |
e3f87685 |
"for checksum element\n");
} else {
cli_dbgmsg("cli_scanxar: checksum algorithm is %s.\n", style); |
0c6aeb9e |
if (0 == xmlStrcasecmp(style, (const xmlChar *)"sha1")) { |
e3f87685 |
*hash = XAR_CKSUM_SHA1; |
0c6aeb9e |
} else if (0 == xmlStrcasecmp(style, (const xmlChar *)"md5")) { |
e3f87685 |
*hash = XAR_CKSUM_MD5;
} else {
cli_dbgmsg("cli_scanxar: checksum algorithm %s is unsupported.\n", style);
*hash = XAR_CKSUM_OTHER;
}
} |
374f21dd |
if (style != NULL)
xmlFree(style); |
e3f87685 |
if (xmlTextReaderRead(reader) == 1 && xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) { |
5a5d38e4 |
xmlval = xmlTextReaderConstValue(reader); |
e3f87685 |
if (xmlval) {
*cksum = xmlStrdup(xmlval);
cli_dbgmsg("cli_scanxar: checksum value is %s.\n", *cksum);
} else {
*cksum = NULL; |
96de30ae |
cli_dbgmsg("cli_scanxar: xmlTextReaderConstValue() returns NULL for checksum value.\n"); |
e3f87685 |
}
}
else |
96de30ae |
cli_dbgmsg("cli_scanxar: No text for XML checksum element.\n"); |
e3f87685 |
}
/* |
5e56b827 |
xar_get_toc_data_values - return the values of a <data> or <ea> xml element that represent
an extent of data on the heap.
parameters:
reader - xmlTextReaderPtr
length - pointer to long for returning value of the <length> element.
offset - pointer to long for returning value of the <offset> element.
size - pointer to long for returning value of the <size> element.
encoding - pointer to int for returning indication of the <encoding> style attribute. |
e3f87685 |
a_cksum - pointer to char* for return archived checksum value.
a_hash - pointer to int for returning archived checksum algorithm.
e_cksum - pointer to char* for return extracted checksum value.
e_hash - pointer to int for returning extracted checksum algorithm. |
5e56b827 |
returns - CL_FORMAT, CL_SUCCESS, CL_BREAK. CL_BREAK indicates no more <data>/<ea> element.
*/ |
e3f87685 |
static int xar_get_toc_data_values(xmlTextReaderPtr reader, long *length, long *offset, long *size, int *encoding, |
5a5d38e4 |
unsigned char ** a_cksum, int * a_hash, unsigned char ** e_cksum, int * e_hash) |
5e56b827 |
{
const xmlChar *name;
int indata = 0, inea = 0;
int rc, gotoffset=0, gotlength=0, gotsize=0;
|
e3f87685 |
*a_cksum = NULL;
*a_hash = XAR_CKSUM_NONE;
*e_cksum = NULL;
*e_hash = XAR_CKSUM_NONE;
*encoding = CL_TYPE_ANY;
|
5e56b827 |
rc = xmlTextReaderRead(reader);
while (rc == 1) {
name = xmlTextReaderConstLocalName(reader);
if (indata || inea) {
/* cli_dbgmsg("cli_scanxar: xmlTextReaderRead read %s\n", name); */
if (xmlStrEqual(name, (const xmlChar *)"offset") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
if (CL_SUCCESS == xar_get_numeric_from_xml_element(reader, offset))
gotoffset=1; |
e3f87685 |
|
5e56b827 |
} else if (xmlStrEqual(name, (const xmlChar *)"length") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
if (CL_SUCCESS == xar_get_numeric_from_xml_element(reader, length))
gotlength=1;
} else if (xmlStrEqual(name, (const xmlChar *)"size") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
if (CL_SUCCESS == xar_get_numeric_from_xml_element(reader, size))
gotsize=1;
|
e3f87685 |
} else if (xmlStrEqual(name, (const xmlChar *)"archived-checksum") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
cli_dbgmsg("cli_scanxar: <archived-checksum>:\n");
xar_get_checksum_values(reader, a_cksum, a_hash);
|
14ebd699 |
} else if ((xmlStrEqual(name, (const xmlChar *)"extracted-checksum") ||
xmlStrEqual(name, (const xmlChar *)"unarchived-checksum")) && |
e3f87685 |
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
cli_dbgmsg("cli_scanxar: <extracted-checksum>:\n");
xar_get_checksum_values(reader, e_cksum, e_hash);
|
5e56b827 |
} else if (xmlStrEqual(name, (const xmlChar *)"encoding") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) { |
e3f87685 |
xmlChar * style = xmlTextReaderGetAttribute(reader, (const xmlChar *)"style"); |
5e56b827 |
if (style == NULL) { |
96de30ae |
cli_dbgmsg("cli_scaxar: xmlTextReaderGetAttribute no style attribute " |
5e56b827 |
"for encoding element\n");
*encoding = CL_TYPE_ANY;
} else if (xmlStrEqual(style, (const xmlChar *)"application/x-gzip")) {
cli_dbgmsg("cli_scanxar: encoding = application/x-gzip.\n");
*encoding = CL_TYPE_GZ;
} else if (xmlStrEqual(style, (const xmlChar *)"application/octet-stream")) {
cli_dbgmsg("cli_scanxar: encoding = application/octet-stream.\n");
*encoding = CL_TYPE_ANY;
} else if (xmlStrEqual(style, (const xmlChar *)"application/x-bzip2")) {
cli_dbgmsg("cli_scanxar: encoding = application/x-bzip2.\n");
*encoding = CL_TYPE_BZ;
} else if (xmlStrEqual(style, (const xmlChar *)"application/x-lzma")) {
cli_dbgmsg("cli_scanxar: encoding = application/x-lzma.\n");
*encoding = CL_TYPE_7Z;
} else if (xmlStrEqual(style, (const xmlChar *)"application/x-xz")) {
cli_dbgmsg("cli_scanxar: encoding = application/x-xz.\n"); |
43d7f6f6 |
*encoding = CL_TYPE_XZ; |
5e56b827 |
} else { |
96de30ae |
cli_dbgmsg("cli_scaxar: unknown style value=%s for encoding element\n", style); |
5e56b827 |
*encoding = CL_TYPE_ANY;
} |
374f21dd |
if (style != NULL)
xmlFree(style); |
e3f87685 |
} else if (indata && xmlStrEqual(name, (const xmlChar *)"data") && |
5e56b827 |
xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT) {
break; |
e3f87685 |
|
5e56b827 |
} else if (inea && xmlStrEqual(name, (const xmlChar *)"ea") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT) {
break;
} |
e3f87685 |
|
5e56b827 |
} else {
if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
if (xmlStrEqual(name, (const xmlChar *)"data")) { |
9b20264d |
cli_dbgmsg("cli_scanxar: xmlTextReaderRead read <data>\n"); |
5e56b827 |
indata = 1;
} else if (xmlStrEqual(name, (const xmlChar *)"ea")) { |
9b20264d |
cli_dbgmsg("cli_scanxar: xmlTextReaderRead read <ea>\n"); |
5e56b827 |
inea = 1;
}
} else if ((xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT) &&
xmlStrEqual(name, (const xmlChar *)"xar")) { |
9b20264d |
cli_dbgmsg("cli_scanxar: finished parsing xar TOC.\n"); |
e3f87685 |
break; |
5e56b827 |
}
}
rc = xmlTextReaderRead(reader);
}
if (gotoffset && gotlength && gotsize) {
rc = CL_SUCCESS;
}
else if (0 == gotoffset + gotlength + gotsize)
rc = CL_BREAK;
else
rc = CL_EFORMAT;
return rc;
}
|
e2c74282 |
/*
xar_process_subdocument - check TOC for xml subdocument. If found, extract and
scan in memory.
Parameters:
reader - xmlTextReaderPtr
ctx - pointer to cli_ctx
Returns:
CL_SUCCESS - subdoc found and clean scan (or virus found and SCAN_ALL), or no subdocument
other - error return code from cli_mem_scandesc()
*/
static int xar_scan_subdocuments(xmlTextReaderPtr reader, cli_ctx *ctx)
{ |
e3f87685 |
int rc = CL_SUCCESS, subdoc_len, fd; |
e2c74282 |
xmlChar * subdoc;
const xmlChar *name;
char * tmpname;
while (xmlTextReaderRead(reader) == 1) {
name = xmlTextReaderConstLocalName(reader);
if (name == NULL) { |
96de30ae |
cli_dbgmsg("cli_scanxar: xmlTextReaderConstLocalName() no name.\n"); |
e2c74282 |
rc = CL_EFORMAT;
break;
}
if (xmlStrEqual(name, (const xmlChar *)"toc") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT)
return CL_SUCCESS;
if (xmlStrEqual(name, (const xmlChar *)"subdoc") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
subdoc = xmlTextReaderReadInnerXml(reader);
if (subdoc == NULL) { |
96de30ae |
cli_dbgmsg("cli_scanxar: no content in subdoc element.\n"); |
e2c74282 |
xmlTextReaderNext(reader);
continue;
}
subdoc_len = xmlStrlen(subdoc);
cli_dbgmsg("cli_scanxar: in-memory scan of xml subdocument, len %i.\n", subdoc_len);
rc = cli_mem_scandesc(subdoc, subdoc_len, ctx); |
5a5d38e4 |
if (rc == CL_VIRUS && SCAN_ALL) |
e2c74282 |
rc = CL_SUCCESS;
/* make a file to leave if --leave-temps in effect */
if(ctx->engine->keeptmp) {
if ((rc = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { |
7f50a91c |
cli_dbgmsg("cli_scanxar: Can't create temporary file for subdocument.\n"); |
e3f87685 |
} else {
cli_dbgmsg("cli_scanxar: Writing subdoc to temp file %s.\n", tmpname);
if (cli_writen(fd, subdoc, subdoc_len) < 0) { |
96de30ae |
cli_dbgmsg("cli_scanxar: cli_writen error writing subdoc temporary file.\n"); |
e3f87685 |
rc = CL_EWRITE;
}
rc = xar_cleanup_temp_file(ctx, fd, tmpname); |
e2c74282 |
}
}
xmlFree(subdoc);
if (rc != CL_SUCCESS)
return rc;
xmlTextReaderNext(reader);
}
}
return rc;
} |
e3f87685 |
|
da6e06dd |
static void * xar_hash_init(int hash, void **sc, void **mc) |
e3f87685 |
{
if (!sc && !mc)
return NULL;
switch (hash) {
case XAR_CKSUM_SHA1: |
da6e06dd |
*sc = cl_hash_init("sha1"); |
f077c617 |
if (!(*sc)) {
return NULL;
}
return *sc; |
e3f87685 |
case XAR_CKSUM_MD5: |
da6e06dd |
*mc = cl_hash_init("md5"); |
f077c617 |
if (!(*mc)) {
return NULL;
}
return *mc; |
e3f87685 |
case XAR_CKSUM_OTHER:
case XAR_CKSUM_NONE:
default:
return NULL;
}
}
|
b9dcb2de |
static void xar_hash_update(void * hash_ctx, void * data, unsigned long size, int hash) |
e3f87685 |
{
if (!hash_ctx || !data || !size)
return; |
b2e7c931 |
|
e3f87685 |
switch (hash) {
case XAR_CKSUM_NONE: |
b2e7c931 |
case XAR_CKSUM_OTHER: |
e3f87685 |
return;
} |
b2e7c931 |
|
da6e06dd |
cl_update_hash(hash_ctx, data, size); |
e3f87685 |
}
|
da6e06dd |
static void xar_hash_final(void * hash_ctx, void * result, int hash) |
e3f87685 |
{
if (!hash_ctx || !result)
return; |
b2e7c931 |
|
e3f87685 |
switch (hash) {
case XAR_CKSUM_OTHER:
case XAR_CKSUM_NONE:
return;
} |
b2e7c931 |
|
da6e06dd |
cl_finish_hash(hash_ctx, result); |
e3f87685 |
}
static int xar_hash_check(int hash, const void * result, const void * expected)
{
int len;
if (!result || !expected)
return 1;
switch (hash) {
case XAR_CKSUM_SHA1:
len = SHA1_HASH_SIZE;
break;
case XAR_CKSUM_MD5:
len = CLI_HASH_MD5;
break;
case XAR_CKSUM_OTHER:
case XAR_CKSUM_NONE:
default:
return 1;
}
return memcmp(result, expected, len);
}
|
5e56b827 |
#endif |
e2c74282 |
|
5e56b827 |
/*
cli_scanxar - scan an xar archive.
Parameters:
ctx - pointer to cli_ctx.
returns - CL_SUCCESS or CL_ error code.
*/ |
ca019d6d |
int cli_scanxar(cli_ctx *ctx)
{ |
5e56b827 |
int rc = CL_SUCCESS; |
326056f2 |
unsigned int cksum_fails = 0; |
7f50a91c |
unsigned int extract_errors = 0; |
5e56b827 |
#if HAVE_LIBXML2
int fd = -1; |
ca019d6d |
struct xar_header hdr; |
5e56b827 |
fmap_t *map = *ctx->fmap;
long length, offset, size, at;
int encoding; |
cd94be7a |
z_stream strm; |
5e56b827 |
char *toc, *tmpname;
xmlTextReaderPtr reader = NULL; |
e3f87685 |
int a_hash, e_hash; |
5a5d38e4 |
unsigned char *a_cksum = NULL, *e_cksum = NULL; |
a223f6c9 |
void *a_hash_ctx = NULL, *e_hash_ctx = NULL;
char result[SHA1_HASH_SIZE]; |
5e56b827 |
|
cd94be7a |
memset(&strm, 0x00, sizeof(z_stream));
|
5e56b827 |
/* retrieve xar header */
if (fmap_readn(*ctx->fmap, &hdr, 0, sizeof(hdr)) != sizeof(hdr)) { |
96de30ae |
cli_dbgmsg("cli_scanxar: Invalid header, too short.\n"); |
ca019d6d |
return CL_EFORMAT;
}
hdr.magic = be32_to_host(hdr.magic); |
5e56b827 |
if (hdr.magic == XAR_HEADER_MAGIC) { |
ca019d6d |
cli_dbgmsg("cli_scanxar: Matched magic\n");
}
else { |
96de30ae |
cli_dbgmsg("cli_scanxar: Invalid magic\n"); |
ca019d6d |
return CL_EFORMAT;
} |
5e56b827 |
hdr.size = be16_to_host(hdr.size);
hdr.version = be16_to_host(hdr.version);
hdr.toc_length_compressed = be64_to_host(hdr.toc_length_compressed);
hdr.toc_length_decompressed = be64_to_host(hdr.toc_length_decompressed);
hdr.chksum_alg = be32_to_host(hdr.chksum_alg); |
ca019d6d |
|
5e56b827 |
/* cli_dbgmsg("hdr.magic %x\n", hdr.magic); */
/* cli_dbgmsg("hdr.size %i\n", hdr.size); */
/* cli_dbgmsg("hdr.version %i\n", hdr.version); */
/* cli_dbgmsg("hdr.toc_length_compressed %lu\n", hdr.toc_length_compressed); */
/* cli_dbgmsg("hdr.toc_length_decompressed %lu\n", hdr.toc_length_decompressed); */
/* cli_dbgmsg("hdr.chksum_alg %i\n", hdr.chksum_alg); */
/* Uncompress TOC */
strm.next_in = (unsigned char *)fmap_need_off_once(*ctx->fmap, hdr.size, hdr.toc_length_compressed);
if (strm.next_in == NULL) { |
96de30ae |
cli_dbgmsg("cli_scanxar: fmap_need_off_once fails on TOC.\n");
return CL_EREAD; |
5e56b827 |
}
strm.avail_in = hdr.toc_length_compressed;
toc = cli_malloc(hdr.toc_length_decompressed+1);
if (toc == NULL) { |
96de30ae |
cli_dbgmsg("cli_scanxar: cli_malloc fails on TOC decompress buffer.\n"); |
5e56b827 |
return CL_EMEM;
}
toc[hdr.toc_length_decompressed] = '\0';
strm.avail_out = hdr.toc_length_decompressed;
strm.next_out = (unsigned char *)toc;
rc = inflateInit(&strm);
if (rc != Z_OK) { |
96de30ae |
cli_dbgmsg("cli_scanxar:inflateInit error %i \n", rc); |
5e56b827 |
rc = CL_EFORMAT;
goto exit_toc;
}
rc = inflate(&strm, Z_SYNC_FLUSH);
if (rc != Z_OK && rc != Z_STREAM_END) { |
96de30ae |
cli_dbgmsg("cli_scanxar:inflate error %i \n", rc); |
5e56b827 |
rc = CL_EFORMAT;
goto exit_toc;
}
rc = inflateEnd(&strm);
if (rc != Z_OK) { |
96de30ae |
cli_dbgmsg("cli_scanxar:inflateEnd error %i \n", rc); |
5e56b827 |
rc = CL_EFORMAT;
goto exit_toc;
} |
ca019d6d |
|
9b20264d |
/* cli_dbgmsg("cli_scanxar: TOC xml:\n%s\n", toc); */
/* printf("cli_scanxar: TOC xml:\n%s\n", toc); */
/* cli_dbgmsg("cli_scanxar: TOC end:\n"); */
/* printf("cli_scanxar: TOC end:\n"); */ |
5e56b827 |
/* scan the xml */ |
e2c74282 |
cli_dbgmsg("cli_scanxar: scanning xar TOC xml in memory.\n"); |
5e56b827 |
rc = cli_mem_scandesc(toc, hdr.toc_length_decompressed, ctx);
if (rc != CL_SUCCESS) {
if (rc != CL_VIRUS || !SCAN_ALL)
goto exit_toc;
}
/* make a file to leave if --leave-temps in effect */
if(ctx->engine->keeptmp) {
if ((rc = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { |
96de30ae |
cli_dbgmsg("cli_scanxar: Can't create temporary file for TOC.\n"); |
5e56b827 |
goto exit_toc;
}
if (cli_writen(fd, toc, hdr.toc_length_decompressed) < 0) { |
96de30ae |
cli_dbgmsg("cli_scanxar: cli_writen error writing TOC.\n"); |
5e56b827 |
rc = CL_EWRITE;
xar_cleanup_temp_file(ctx, fd, tmpname);
goto exit_toc;
}
rc = xar_cleanup_temp_file(ctx, fd, tmpname);
if (rc != CL_SUCCESS)
goto exit_toc;
}
|
fb05a793 |
reader = xmlReaderForMemory(toc, hdr.toc_length_decompressed, "noname.xml", NULL, CLAMAV_MIN_XMLREADER_FLAGS); |
5e56b827 |
if (reader == NULL) { |
96de30ae |
cli_dbgmsg("cli_scanxar: xmlReaderForMemory error for TOC\n"); |
5e56b827 |
goto exit_toc;
}
|
e2c74282 |
rc = xar_scan_subdocuments(reader, ctx);
if (rc != CL_SUCCESS) { |
96de30ae |
cli_dbgmsg("xar_scan_subdocuments returns %i.\n", rc); |
9d762a7a |
goto exit_reader; |
e2c74282 |
}
|
5e56b827 |
/* Walk the TOC XML and extract files */
fd = -1;
tmpname = NULL; |
e3f87685 |
while (CL_SUCCESS == (rc = xar_get_toc_data_values(reader, &length, &offset, &size, &encoding,
&a_cksum, &a_hash, &e_cksum, &e_hash))) { |
2599f40b |
int do_extract_cksum = 1; |
5a5d38e4 |
unsigned char * blockp; |
da6e06dd |
void *a_sc, *e_sc;
void *a_mc, *e_mc; |
e3f87685 |
char * expected; |
5e56b827 |
/* clean up temp file from previous loop iteration */
if (fd > -1 && tmpname) {
rc = xar_cleanup_temp_file(ctx, fd, tmpname);
if (rc != CL_SUCCESS)
goto exit_reader;
}
at = offset + hdr.toc_length_compressed + hdr.size;
if ((rc = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { |
96de30ae |
cli_dbgmsg("cli_scanxar: Can't generate temporary file.\n"); |
5e56b827 |
goto exit_reader;
}
|
9b20264d |
cli_dbgmsg("cli_scanxar: decompress into temp file:\n%s, size %li,\n"
"from xar heap offset %li length %li\n",
tmpname, size, offset, length); |
5e56b827 |
|
e3f87685 |
a_hash_ctx = xar_hash_init(a_hash, &a_sc, &a_mc);
e_hash_ctx = xar_hash_init(e_hash, &e_sc, &e_mc);
|
5e56b827 |
switch (encoding) {
case CL_TYPE_GZ:
/* inflate gzip directly because file segments do not contain magic */
memset(&strm, 0, sizeof(strm));
if ((rc = inflateInit(&strm)) != Z_OK) { |
96de30ae |
cli_dbgmsg("cli_scanxar: InflateInit failed: %d\n", rc); |
5e56b827 |
rc = CL_EFORMAT; |
7f50a91c |
extract_errors++;
break; |
5e56b827 |
}
|
cd94be7a |
while ((size_t)at < map->len && (unsigned long)at < offset+hdr.toc_length_compressed+hdr.size+length) { |
0c6aeb9e |
unsigned long avail_in;
void * next_in; |
5e56b827 |
unsigned int bytes = MIN(map->len - at, map->pgsz);
bytes = MIN(length, bytes); |
0c6aeb9e |
if(!(strm.next_in = next_in = (void*)fmap_need_off_once(map, at, bytes))) { |
5e56b827 |
cli_dbgmsg("cli_scanxar: Can't read %u bytes @ %lu.\n", bytes, (long unsigned)at);
inflateEnd(&strm);
rc = CL_EREAD;
goto exit_tmpfile;
}
at += bytes; |
0c6aeb9e |
strm.avail_in = avail_in = bytes; |
5e56b827 |
do {
int inf, outsize = 0;
unsigned char buff[FILEBUFF];
strm.avail_out = sizeof(buff);
strm.next_out = buff;
inf = inflate(&strm, Z_SYNC_FLUSH);
if (inf != Z_OK && inf != Z_STREAM_END && inf != Z_BUF_ERROR) { |
96de30ae |
cli_dbgmsg("cli_scanxar: inflate error %i %s.\n", inf, strm.msg?strm.msg:""); |
5e56b827 |
rc = CL_EFORMAT; |
7f50a91c |
extract_errors++;
break; |
5e56b827 |
} |
e3f87685 |
bytes = sizeof(buff) - strm.avail_out;
|
14ebd699 |
if (e_hash_ctx != NULL)
xar_hash_update(e_hash_ctx, buff, bytes, e_hash); |
e3f87685 |
if (cli_writen(fd, buff, bytes) < 0) { |
96de30ae |
cli_dbgmsg("cli_scanxar: cli_writen error file %s.\n", tmpname); |
5e56b827 |
inflateEnd(&strm);
rc = CL_EWRITE;
goto exit_tmpfile;
}
outsize += sizeof(buff) - strm.avail_out;
if (cli_checklimits("cli_scanxar", ctx, outsize, 0, 0) != CL_CLEAN) {
break;
}
if (inf == Z_STREAM_END) {
break;
}
} while (strm.avail_out == 0); |
0c6aeb9e |
|
7f50a91c |
if (rc != CL_SUCCESS)
break;
|
0c6aeb9e |
avail_in -= strm.avail_in; |
14ebd699 |
if (a_hash_ctx != NULL)
xar_hash_update(a_hash_ctx, next_in, avail_in, a_hash); |
5e56b827 |
} |
7f50a91c |
|
5e56b827 |
inflateEnd(&strm);
break;
case CL_TYPE_7Z: |
9b20264d |
#define CLI_LZMA_OBUF_SIZE 1024*1024
#define CLI_LZMA_HDR_SIZE LZMA_PROPS_SIZE+8 |
43d7f6f6 |
#define CLI_LZMA_IBUF_SIZE CLI_LZMA_OBUF_SIZE>>2 /* estimated compression ratio 25% */
{ |
5a5d38e4 |
struct CLI_LZMA lz; |
9b20264d |
unsigned long in_remaining = length;
unsigned long out_size = 0; |
5a5d38e4 |
unsigned char * buff = __lzma_wrap_alloc(NULL, CLI_LZMA_OBUF_SIZE); |
f8708da8 |
int lret; |
9b20264d |
|
5a5d38e4 |
memset(&lz, 0, sizeof(lz)); |
9b20264d |
if (buff == NULL) { |
96de30ae |
cli_dbgmsg("cli_scanxar: memory request for lzma decompression buffer fails.\n"); |
9b20264d |
rc = CL_EMEM;
goto exit_tmpfile;
}
blockp = (void*)fmap_need_off_once(map, at, CLI_LZMA_HDR_SIZE);
if (blockp == NULL) { |
d78095c2 |
char errbuff[128];
cli_strerror(errno, errbuff, sizeof(errbuff)); |
96de30ae |
cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno:%s.\n", |
f30644d1 |
length, at, errbuff); |
9b20264d |
rc = CL_EREAD;
__lzma_wrap_free(NULL, buff);
goto exit_tmpfile;
}
lz.next_in = blockp;
lz.avail_in = CLI_LZMA_HDR_SIZE;
|
14ebd699 |
if (a_hash_ctx != NULL)
xar_hash_update(a_hash_ctx, blockp, CLI_LZMA_HDR_SIZE, a_hash); |
e3f87685 |
|
f8708da8 |
lret = cli_LzmaInit(&lz, 0);
if (lret != LZMA_RESULT_OK) {
cli_dbgmsg("cli_scanxar: cli_LzmaInit() fails: %i.\n", lret); |
9b20264d |
rc = CL_EFORMAT;
__lzma_wrap_free(NULL, buff); |
7f50a91c |
extract_errors++;
break; |
9b20264d |
} |
7f50a91c |
|
9b20264d |
at += CLI_LZMA_HDR_SIZE; |
e3f87685 |
in_remaining -= CLI_LZMA_HDR_SIZE; |
cd94be7a |
while ((size_t)at < map->len && (unsigned long)at < offset+hdr.toc_length_compressed+hdr.size+length) { |
9b20264d |
SizeT avail_in;
SizeT avail_out; |
3f058bd7 |
void * next_in; |
9b20264d |
unsigned long in_consumed;
lz.next_out = buff;
lz.avail_out = CLI_LZMA_OBUF_SIZE; |
43d7f6f6 |
lz.avail_in = avail_in = MIN(CLI_LZMA_IBUF_SIZE, in_remaining); |
3f058bd7 |
lz.next_in = next_in = (void*)fmap_need_off_once(map, at, lz.avail_in); |
9b20264d |
if (lz.next_in == NULL) { |
d78095c2 |
char errbuff[128];
cli_strerror(errno, errbuff, sizeof(errbuff)); |
96de30ae |
cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno: %s.\n", |
f30644d1 |
length, at, errbuff); |
9b20264d |
rc = CL_EREAD;
__lzma_wrap_free(NULL, buff);
cli_LzmaShutdown(&lz);
goto exit_tmpfile;
}
|
f8708da8 |
lret = cli_LzmaDecode(&lz);
if (lret != LZMA_RESULT_OK && lret != LZMA_STREAM_END) {
cli_dbgmsg("cli_scanxar: cli_LzmaDecode() fails: %i.\n", lret); |
9b20264d |
rc = CL_EFORMAT; |
7f50a91c |
extract_errors++;
break; |
9b20264d |
}
in_consumed = avail_in - lz.avail_in;
in_remaining -= in_consumed;
at += in_consumed;
avail_out = CLI_LZMA_OBUF_SIZE - lz.avail_out;
|
3f058bd7 |
if (avail_out == 0)
cli_dbgmsg("cli_scanxar: cli_LzmaDecode() produces no output for " |
9b20264d |
"avail_in %lu, avail_out %lu.\n", avail_in, avail_out); |
3f058bd7 |
|
14ebd699 |
if (a_hash_ctx != NULL)
xar_hash_update(a_hash_ctx, next_in, in_consumed, a_hash);
if (e_hash_ctx != NULL)
xar_hash_update(e_hash_ctx, buff, avail_out, e_hash); |
e3f87685 |
|
9b20264d |
/* Write a decompressed block. */ |
3f058bd7 |
/* cli_dbgmsg("Writing %li bytes to LZMA decompress temp file, " */
/* "consumed %li of %li available compressed bytes.\n", */
/* avail_out, in_consumed, avail_in); */ |
7f50a91c |
|
9b20264d |
if (cli_writen(fd, buff, avail_out) < 0) {
cli_dbgmsg("cli_scanxar: cli_writen error writing lzma temp file for %li bytes.\n",
avail_out);
__lzma_wrap_free(NULL, buff);
cli_LzmaShutdown(&lz);
rc = CL_EWRITE;
goto exit_tmpfile;
} |
7f50a91c |
|
9b20264d |
/* Check file size limitation. */
out_size += avail_out;
if (cli_checklimits("cli_scanxar", ctx, out_size, 0, 0) != CL_CLEAN) {
break;
} |
7f50a91c |
|
f8708da8 |
if (lret == LZMA_STREAM_END) |
9b20264d |
break;
}
cli_LzmaShutdown(&lz);
__lzma_wrap_free(NULL, buff);
}
break; |
2599f40b |
case CL_TYPE_ANY: |
5e56b827 |
default:
case CL_TYPE_BZ: |
43d7f6f6 |
case CL_TYPE_XZ:
/* for uncompressed, bzip2, xz, and unknown, just pull the file, cli_magic_scandesc does the rest */ |
2599f40b |
do_extract_cksum = 0; |
e3f87685 |
{
unsigned long write_len;
if (ctx->engine->maxfilesize) |
cd94be7a |
write_len = MIN((size_t)(ctx->engine->maxfilesize), (size_t)length); |
e3f87685 |
else
write_len = length;
if (!(blockp = (void*)fmap_need_off_once(map, at, length))) { |
d78095c2 |
char errbuff[128];
cli_strerror(errno, errbuff, sizeof(errbuff)); |
96de30ae |
cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno:%s.\n", |
f30644d1 |
length, at, errbuff); |
e3f87685 |
rc = CL_EREAD;
goto exit_tmpfile;
}
|
14ebd699 |
if (a_hash_ctx != NULL)
xar_hash_update(a_hash_ctx, blockp, length, a_hash); |
e3f87685 |
if (cli_writen(fd, blockp, write_len) < 0) {
cli_dbgmsg("cli_scanxar: cli_writen error %li bytes @ %li.\n", length, at);
rc = CL_EWRITE;
goto exit_tmpfile;
}
/*break;*/
}
}
|
7f50a91c |
if (rc == CL_SUCCESS) { |
14ebd699 |
if (a_hash_ctx != NULL) {
xar_hash_final(a_hash_ctx, result, a_hash);
a_hash_ctx = NULL;
} else {
cli_dbgmsg("cli_scanxar: archived-checksum missing.\n");
cksum_fails++;
} |
7f50a91c |
if (a_cksum != NULL) {
expected = cli_hex2str((char *)a_cksum);
if (xar_hash_check(a_hash, result, expected) != 0) { |
14ebd699 |
cli_dbgmsg("cli_scanxar: archived-checksum mismatch.\n"); |
2599f40b |
cksum_fails++;
} else { |
7f50a91c |
cli_dbgmsg("cli_scanxar: archived-checksum matched.\n"); |
2599f40b |
}
free(expected); |
5e56b827 |
} |
14ebd699 |
if (e_hash_ctx != NULL) {
xar_hash_final(e_hash_ctx, result, e_hash);
e_hash_ctx = NULL;
} else {
cli_dbgmsg("cli_scanxar: extracted-checksum(unarchived-checksum) missing.\n");
cksum_fails++;
} |
7f50a91c |
if (e_cksum != NULL) {
if (do_extract_cksum) {
expected = cli_hex2str((char *)e_cksum);
if (xar_hash_check(e_hash, result, expected) != 0) { |
14ebd699 |
cli_dbgmsg("cli_scanxar: extracted-checksum mismatch.\n"); |
7f50a91c |
cksum_fails++;
} else {
cli_dbgmsg("cli_scanxar: extracted-checksum matched.\n");
}
free(expected);
}
} |
e3f87685 |
|
7f50a91c |
rc = cli_magic_scandesc(fd, ctx);
if (rc != CL_SUCCESS) {
if (rc == CL_VIRUS) {
cli_dbgmsg("cli_scanxar: Infected with %s\n", cli_get_last_virus(ctx));
if (!SCAN_ALL)
goto exit_tmpfile;
} else if (rc != CL_BREAK) {
cli_dbgmsg("cli_scanxar: cli_magic_scandesc error %i\n", rc); |
5e56b827 |
goto exit_tmpfile; |
7f50a91c |
} |
5e56b827 |
}
} |
7f50a91c |
if (a_cksum != NULL) {
xmlFree(a_cksum);
a_cksum = NULL;
}
if (e_cksum != NULL) {
xmlFree(e_cksum);
e_cksum = NULL;
}
} |
5e56b827 |
exit_tmpfile:
xar_cleanup_temp_file(ctx, fd, tmpname); |
a223f6c9 |
if (a_hash_ctx != NULL)
xar_hash_final(a_hash_ctx, result, a_hash);
if (e_hash_ctx != NULL)
xar_hash_final(e_hash_ctx, result, e_hash);
|
fe91f308 |
exit_reader: |
e3f87685 |
if (a_cksum != NULL)
xmlFree(a_cksum);
if (e_cksum != NULL)
xmlFree(e_cksum); |
e2c74282 |
xmlTextReaderClose(reader); |
5e56b827 |
xmlFreeTextReader(reader);
exit_toc:
free(toc);
if (rc == CL_BREAK)
rc = CL_SUCCESS;
#else |
9eb30a47 |
cli_dbgmsg("cli_scanxar: can't scan xar files, need libxml2.\n"); |
5e56b827 |
#endif |
7f50a91c |
if (cksum_fails + extract_errors != 0) {
cli_warnmsg("cli_scanxar: %u checksum errors and %u extraction errors, use --debug for more info.\n",
cksum_fails, extract_errors);
} |
5e56b827 |
return rc;
} |