xar: add checksumming checks and fixups from code review

remotes/push_mirror/0.98.1
Steve Morgan 12 years ago
parent b09a05e4c3
commit e3f876851b
  1. 299
      libclamav/xar.c
  2. 5
      libclamav/xar.h

@ -27,9 +27,12 @@
#include "fmap.h"
#if HAVE_LIBXML2
#include <libxml/xmlreader.h>
#include "str.h"
#include "scanners.h"
#include "inflate64.h"
#include "lzma_iface.h"
#include "sha1.h"
#include "md5.h"
/*
xar_cleanup_temp_file - cleanup after cli_gentempfd
@ -62,18 +65,65 @@ static int xar_cleanup_temp_file(cli_ctx *ctx, int fd, char * tmpname)
*/
static int xar_get_numeric_from_xml_element(xmlTextReaderPtr reader, long * value)
{
const xmlChar * numstr;
if (xmlTextReaderRead(reader) == 1 && xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) {
*value = atol((const char *)xmlTextReaderConstValue(reader));
if (*value < 0) {
cli_errmsg("cli_scanxar: XML element value %li\n", *value);
return CL_EFORMAT;
numstr = xmlTextReaderConstValue(reader);
if (numstr) {
*value = atol((const char *)numstr);
if (*value < 0) {
cli_errmsg("cli_scanxar: XML element value %li\n", *value);
return CL_EFORMAT;
}
return CL_SUCCESS;
}
return CL_SUCCESS;
}
cli_errmsg("cli_scanxar: No text for XML element\n");
return CL_EFORMAT;
}
/*
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
*/
static void xar_get_checksum_values(xmlTextReaderPtr reader, char ** cksum, int * hash)
{
xmlChar * style = xmlTextReaderGetAttribute(reader, (const xmlChar *)"style");
const char * xmlval;
*hash = XAR_CKSUM_NONE;
if (style == NULL) {
cli_errmsg("cli_scaxar: xmlTextReaderGetAttribute no style attribute "
"for checksum element\n");
} else {
cli_dbgmsg("cli_scanxar: checksum algorithm is %s.\n", style);
if (xmlStrEqual(style, (const xmlChar *)"sha1")) {
*hash = XAR_CKSUM_SHA1;
} else if (xmlStrEqual(style, (const xmlChar *)"md5")) {
*hash = XAR_CKSUM_MD5;
} else {
cli_dbgmsg("cli_scanxar: checksum algorithm %s is unsupported.\n", style);
*hash = XAR_CKSUM_OTHER;
}
}
if (xmlTextReaderRead(reader) == 1 && xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) {
xmlval = (const char *)xmlTextReaderConstValue(reader);
if (xmlval) {
*cksum = xmlStrdup(xmlval);
cli_dbgmsg("cli_scanxar: checksum value is %s.\n", *cksum);
} else {
*cksum = NULL;
cli_errmsg("cli_scanxar: xmlTextReaderConstValue() returns NULL for checksum value.\n");
}
}
else
cli_errmsg("cli_scanxar: No text for XML checksum element.\n");
}
/*
xar_get_toc_data_values - return the values of a <data> or <ea> xml element that represent
an extent of data on the heap.
@ -83,14 +133,25 @@ static int xar_get_numeric_from_xml_element(xmlTextReaderPtr reader, long * valu
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.
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.
returns - CL_FORMAT, CL_SUCCESS, CL_BREAK. CL_BREAK indicates no more <data>/<ea> element.
*/
static int xar_get_toc_data_values(xmlTextReaderPtr reader, long *length, long *offset, long *size, int *encoding)
static int xar_get_toc_data_values(xmlTextReaderPtr reader, long *length, long *offset, long *size, int *encoding,
char ** a_cksum, int * a_hash, char ** e_cksum, int * e_hash)
{
const xmlChar *name;
int indata = 0, inea = 0;
int rc, gotoffset=0, gotlength=0, gotsize=0;
*a_cksum = NULL;
*a_hash = XAR_CKSUM_NONE;
*e_cksum = NULL;
*e_hash = XAR_CKSUM_NONE;
*encoding = CL_TYPE_ANY;
rc = xmlTextReaderRead(reader);
while (rc == 1) {
name = xmlTextReaderConstLocalName(reader);
@ -100,6 +161,7 @@ static int xar_get_toc_data_values(xmlTextReaderPtr reader, long *length, long *
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
if (CL_SUCCESS == xar_get_numeric_from_xml_element(reader, offset))
gotoffset=1;
} else if (xmlStrEqual(name, (const xmlChar *)"length") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
if (CL_SUCCESS == xar_get_numeric_from_xml_element(reader, length))
@ -110,10 +172,19 @@ static int xar_get_toc_data_values(xmlTextReaderPtr reader, long *length, long *
if (CL_SUCCESS == xar_get_numeric_from_xml_element(reader, size))
gotsize=1;
} 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);
} else if (xmlStrEqual(name, (const xmlChar *)"extracted-checksum") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
cli_dbgmsg("cli_scanxar: <extracted-checksum>:\n");
xar_get_checksum_values(reader, e_cksum, e_hash);
} else if (xmlStrEqual(name, (const xmlChar *)"encoding") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
xmlChar * style;
style = xmlTextReaderGetAttribute(reader, (const xmlChar *)"style");
xmlChar * style = xmlTextReaderGetAttribute(reader, (const xmlChar *)"style");
if (style == NULL) {
cli_errmsg("cli_scaxar: xmlTextReaderGetAttribute no style attribute "
"for encoding element\n");
@ -132,18 +203,22 @@ static int xar_get_toc_data_values(xmlTextReaderPtr reader, long *length, long *
*encoding = CL_TYPE_7Z;
} else if (xmlStrEqual(style, (const xmlChar *)"application/x-xz")) {
cli_dbgmsg("cli_scanxar: encoding = application/x-xz.\n");
*encoding = CL_TYPE_7Z;
cli_dbgmsg("cli_scanxar: decompression of application/x-xz not supported.\n");
*encoding = CL_TYPE_ANY;
} else {
cli_errmsg("cli_scaxar: unknown style value=%s for encoding element\n", style);
*encoding = CL_TYPE_ANY;
}
} else if (indata && xmlStrEqual(name, (const xmlChar *)"data") &&
} else if (indata && xmlStrEqual(name, (const xmlChar *)"data") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT) {
break;
} else if (inea && xmlStrEqual(name, (const xmlChar *)"ea") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT) {
break;
}
} else {
if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
if (xmlStrEqual(name, (const xmlChar *)"data")) {
@ -156,6 +231,7 @@ static int xar_get_toc_data_values(xmlTextReaderPtr reader, long *length, long *
} else if ((xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT) &&
xmlStrEqual(name, (const xmlChar *)"xar")) {
cli_dbgmsg("cli_scanxar: finished parsing xar TOC.\n");
break;
}
}
rc = xmlTextReaderRead(reader);
@ -184,7 +260,7 @@ static int xar_get_toc_data_values(xmlTextReaderPtr reader, long *length, long *
*/
static int xar_scan_subdocuments(xmlTextReaderPtr reader, cli_ctx *ctx)
{
int rc, subdoc_len, fd;
int rc = CL_SUCCESS, subdoc_len, fd;
xmlChar * subdoc;
const xmlChar *name;
char * tmpname;
@ -218,13 +294,14 @@ static int xar_scan_subdocuments(xmlTextReaderPtr reader, cli_ctx *ctx)
if(ctx->engine->keeptmp) {
if ((rc = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) {
cli_errmsg("cli_scanxar: Can't create temporary file for subdocument.\n");
} else {
cli_dbgmsg("cli_scanxar: Writing subdoc to temp file %s.\n", tmpname);
if (cli_writen(fd, subdoc, subdoc_len) < 0) {
cli_errmsg("cli_scanxar: cli_writen error writing subdoc temporary file.\n");
rc = CL_EWRITE;
}
rc = xar_cleanup_temp_file(ctx, fd, tmpname);
}
cli_dbgmsg("cli_scanxar: Writing subdoc to temp file %s.\n", tmpname);
if (cli_writen(fd, subdoc, subdoc_len) < 0) {
cli_errmsg("cli_scanxar: cli_writen error writing subdoc temporary file.\n");
rc = CL_EWRITE;
}
rc = xar_cleanup_temp_file(ctx, fd, tmpname);
}
xmlFree(subdoc);
@ -235,6 +312,86 @@ static int xar_scan_subdocuments(xmlTextReaderPtr reader, cli_ctx *ctx)
}
return rc;
}
static void * xar_hash_init(int hash, SHA1Context *sc, cli_md5_ctx *mc)
{
if (!sc && !mc)
return NULL;
switch (hash) {
case XAR_CKSUM_SHA1:
SHA1Init(sc);
return sc;
case XAR_CKSUM_MD5:
cli_md5_init(mc);
return mc;
case XAR_CKSUM_OTHER:
case XAR_CKSUM_NONE:
default:
return NULL;
}
}
static void xar_hash_update(void * hash_ctx, const void * data, unsigned long size, int hash)
{
if (!hash_ctx || !data || !size)
return;
switch (hash) {
case XAR_CKSUM_SHA1:
SHA1Update(hash_ctx, data, size);
return;
case XAR_CKSUM_MD5:
if (0 == cli_md5_update(hash_ctx, data, size)) {
cli_errmsg("cli_scanxar: cli_md5_update invalid return.\n");
return;
}
return;
case XAR_CKSUM_OTHER:
case XAR_CKSUM_NONE:
default:
return;
}
}
static void xar_hash_final(void * hash_ctx, void * result, int hash)
{
if (!hash_ctx || !result)
return;
switch (hash) {
case XAR_CKSUM_SHA1:
SHA1Final(hash_ctx, result);
return;
case XAR_CKSUM_MD5:
cli_md5_final(result, hash_ctx);
return;
case XAR_CKSUM_OTHER:
case XAR_CKSUM_NONE:
default:
return;
}
}
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);
}
#endif
/*
@ -256,6 +413,9 @@ int cli_scanxar(cli_ctx *ctx)
z_stream strm = {0};
char *toc, *tmpname;
xmlTextReaderPtr reader = NULL;
int a_hash, e_hash;
char *a_cksum = NULL, *e_cksum = NULL;
unsigned int cksum_fails = 0;
/* retrieve xar header */
if (fmap_readn(*ctx->fmap, &hdr, 0, sizeof(hdr)) != sizeof(hdr)) {
@ -363,8 +523,14 @@ int cli_scanxar(cli_ctx *ctx)
/* Walk the TOC XML and extract files */
fd = -1;
tmpname = NULL;
while (CL_SUCCESS == (rc = xar_get_toc_data_values(reader, &length, &offset, &size, &encoding))) {
while (CL_SUCCESS == (rc = xar_get_toc_data_values(reader, &length, &offset, &size, &encoding,
&a_cksum, &a_hash, &e_cksum, &e_hash))) {
char * blockp;
SHA1Context a_sc, e_sc;
cli_md5_ctx a_mc, e_mc;
void *a_hash_ctx, *e_hash_ctx;
char result[SHA1_HASH_SIZE];
char * expected;
/* clean up temp file from previous loop iteration */
if (fd > -1 && tmpname) {
@ -384,6 +550,10 @@ int cli_scanxar(cli_ctx *ctx)
"from xar heap offset %li length %li\n",
tmpname, size, offset, length);
a_hash_ctx = xar_hash_init(a_hash, &a_sc, &a_mc);
e_hash_ctx = xar_hash_init(e_hash, &e_sc, &e_mc);
switch (encoding) {
case CL_TYPE_GZ:
/* inflate gzip directly because file segments do not contain magic */
@ -404,6 +574,9 @@ int cli_scanxar(cli_ctx *ctx)
rc = CL_EREAD;
goto exit_tmpfile;
}
xar_hash_update(a_hash_ctx, strm.next_in, bytes, a_hash);
at += bytes;
strm.avail_in = bytes;
do {
@ -419,7 +592,12 @@ int cli_scanxar(cli_ctx *ctx)
rc = CL_EFORMAT;
goto exit_tmpfile;
}
if (cli_writen(fd, buff, sizeof(buff) - strm.avail_out) < 0) {
bytes = sizeof(buff) - strm.avail_out;
xar_hash_update(e_hash_ctx, buff, bytes, e_hash);
if (cli_writen(fd, buff, bytes) < 0) {
cli_errmsg("cli_scanxar: cli_writen error file %s.\n", tmpname);
inflateEnd(&strm);
rc = CL_EWRITE;
@ -466,6 +644,9 @@ int cli_scanxar(cli_ctx *ctx)
lz.next_in = blockp;
lz.avail_in = CLI_LZMA_HDR_SIZE;
xar_hash_update(a_hash_ctx, blockp, CLI_LZMA_HDR_SIZE, a_hash);
rc = cli_LzmaInit(&lz, 0);
if (rc != LZMA_RESULT_OK) {
cli_errmsg("cli_scanxar: cli_LzmaInit() fails: %i.\n", rc);
@ -475,7 +656,7 @@ int cli_scanxar(cli_ctx *ctx)
}
at += CLI_LZMA_HDR_SIZE;
in_remaining -= CLI_LZMA_HDR_SIZE;
in_remaining -= CLI_LZMA_HDR_SIZE;
while (at < map->len && at < offset+hdr.toc_length_compressed+hdr.size+length) {
SizeT avail_in;
SizeT avail_out;
@ -494,6 +675,8 @@ int cli_scanxar(cli_ctx *ctx)
goto exit_tmpfile;
}
xar_hash_update(a_hash_ctx, lz.next_in, avail_in, a_hash);
rc = cli_LzmaDecode(&lz);
if (rc != LZMA_RESULT_OK && rc != LZMA_STREAM_END) {
cli_errmsg("cli_scanxar: cli_LzmaDecode() fails: %i.\n", rc);
@ -517,9 +700,11 @@ int cli_scanxar(cli_ctx *ctx)
goto exit_tmpfile;
}
xar_hash_update(e_hash_ctx, buff, avail_out, e_hash);
/* Write a decompressed block. */
cli_dbgmsg("Writing %li bytes to LZMA decompress temp file, "
"consumed %li of %li available bytes.\n",
"consumed %li of %li available compressed bytes.\n",
avail_out, in_consumed, avail_in);
if (cli_writen(fd, buff, avail_out) < 0) {
@ -541,6 +726,7 @@ int cli_scanxar(cli_ctx *ctx)
break;
}
cli_LzmaShutdown(&lz);
__lzma_wrap_free(NULL, buff);
}
@ -548,21 +734,62 @@ int cli_scanxar(cli_ctx *ctx)
default:
case CL_TYPE_BZ:
case CL_TYPE_ANY:
/* for uncompressed, bzip2, and unknown, just pull the file, cli_magic_scandesc does the rest */
if (!(blockp = (void*)fmap_need_off_once(map, at, length))) {
cli_errmsg("cli_scanxar: Can't read %li bytes @ %li, errno:%s.\n", length, at, strerror(errno));
rc = CL_EREAD;
goto exit_tmpfile;
{
/* for uncompressed, bzip2, and unknown, just pull the file, cli_magic_scandesc does the rest */
// TODO ensure correct bounds for at/length
unsigned long write_len;
if (ctx->engine->maxfilesize)
write_len = MIN(ctx->engine->maxfilesize, length);
else
write_len = length;
if (!(blockp = (void*)fmap_need_off_once(map, at, length))) {
cli_errmsg("cli_scanxar: Can't read %li bytes @ %li, errno:%s.\n",
length, at, strerror(errno));
rc = CL_EREAD;
goto exit_tmpfile;
}
xar_hash_update(a_hash_ctx, blockp, length, a_hash);
xar_hash_update(e_hash_ctx, blockp, length, e_hash);
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;*/
}
}
xar_hash_final(a_hash_ctx, result, a_hash);
if (a_cksum != NULL) {
expected = cli_hex2str(a_cksum);
if (xar_hash_check(a_hash, result, expected) != 0) {
cli_dbgmsg("cli_scanxar: archived-checksum missing or mismatch.\n");
cksum_fails++;
} else {
cli_dbgmsg("cli_scanxar: archived-checksum matched.\n");
}
if (cli_writen(fd, blockp, length) < 0) {
cli_dbgmsg("cli_scanxar: cli_writen error %li bytes @ %li.\n", length, at);
rc = CL_EWRITE;
goto exit_tmpfile;
free(expected);
xmlFree(a_cksum);
a_cksum = NULL;
}
if (e_cksum != NULL) {
xar_hash_final(e_hash_ctx, result, e_hash);
expected = cli_hex2str(e_cksum);
if (xar_hash_check(e_hash, result, expected) != 0) {
cli_dbgmsg("cli_scanxar: extracted-checksum missing or mismatch.\n");
cksum_fails++;
} else {
cli_dbgmsg("cli_scanxar: extracted-checksum matched.\n");
}
/*break;*/
free(expected);
xmlFree(e_cksum);
e_cksum = NULL;
}
rc = cli_magic_scandesc(fd, ctx);
if (rc != CL_SUCCESS) {
if (rc == CL_VIRUS) {
@ -578,6 +805,10 @@ int cli_scanxar(cli_ctx *ctx)
exit_tmpfile:
xar_cleanup_temp_file(ctx, fd, tmpname);
if (a_cksum != NULL)
xmlFree(a_cksum);
if (e_cksum != NULL)
xmlFree(e_cksum);
exit_reader:
xmlTextReaderClose(reader);
@ -591,6 +822,8 @@ int cli_scanxar(cli_ctx *ctx)
#else
cli_dbgmsg("cli_scanxar: can't scan xar files, need libxml2.\n");
#endif
if (cksum_fails != 0)
cli_warnmsg("cli_scanxar: %u checksums missing/mismatched- use --debug for more info.\n", cksum_fails);
return rc;
}

@ -52,6 +52,11 @@ struct xar_header {
#define XAR_HEADER_MAGIC 0x78617221
#define XAR_HEADER_VERSION 0
#define XAR_CKSUM_NONE 0
#define XAR_CKSUM_SHA1 1
#define XAR_CKSUM_MD5 2
#define XAR_CKSUM_OTHER 3
#ifdef HAVE_PRAGMA_PACK
#pragma pack()
#endif

Loading…
Cancel
Save